summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorKarl Shaffer <karlshaffer@google.com>2023-08-11 00:04:18 +0000
committerAutomerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>2023-08-11 00:04:18 +0000
commitd3fac44428dd0296a04a50c6827e3205b8dbea8a (patch)
treeace24ba4307d4978ee3134f7da671a77ad172da0
parent5df6e262b13a4e2a008638ceea2b1f99db0d2331 (diff)
parent029d049e490dcd5fa609bb7632b0262d95f1bcce (diff)
downloadapache-commons-math-main.tar.gz
Original change: https://android-review.googlesource.com/c/platform/external/apache-commons-math/+/2702413 Change-Id: I6451550459c6d42417e3214f1db820289d799bc7 Signed-off-by: Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
-rw-r--r--Android.bp27
-rw-r--r--src/main/java/org/apache/commons/math3/Field.java58
-rw-r--r--src/main/java/org/apache/commons/math3/FieldElement.java87
-rw-r--r--src/main/java/org/apache/commons/math3/RealFieldElement.java402
-rw-r--r--src/main/java/org/apache/commons/math3/analysis/BivariateFunction.java34
-rw-r--r--src/main/java/org/apache/commons/math3/analysis/DifferentiableMultivariateFunction.java53
-rw-r--r--src/main/java/org/apache/commons/math3/analysis/DifferentiableMultivariateVectorFunction.java37
-rw-r--r--src/main/java/org/apache/commons/math3/analysis/DifferentiableUnivariateFunction.java34
-rw-r--r--src/main/java/org/apache/commons/math3/analysis/DifferentiableUnivariateMatrixFunction.java36
-rw-r--r--src/main/java/org/apache/commons/math3/analysis/DifferentiableUnivariateVectorFunction.java36
-rw-r--r--src/main/java/org/apache/commons/math3/analysis/FunctionUtils.java823
-rw-r--r--src/main/java/org/apache/commons/math3/analysis/MultivariateFunction.java41
-rw-r--r--src/main/java/org/apache/commons/math3/analysis/MultivariateMatrixFunction.java35
-rw-r--r--src/main/java/org/apache/commons/math3/analysis/MultivariateVectorFunction.java35
-rw-r--r--src/main/java/org/apache/commons/math3/analysis/ParametricUnivariateFunction.java44
-rw-r--r--src/main/java/org/apache/commons/math3/analysis/RealFieldUnivariateFunction.java82
-rw-r--r--src/main/java/org/apache/commons/math3/analysis/TrivariateFunction.java35
-rw-r--r--src/main/java/org/apache/commons/math3/analysis/UnivariateFunction.java76
-rw-r--r--src/main/java/org/apache/commons/math3/analysis/UnivariateMatrixFunction.java33
-rw-r--r--src/main/java/org/apache/commons/math3/analysis/UnivariateVectorFunction.java33
-rw-r--r--src/main/java/org/apache/commons/math3/analysis/differentiation/DSCompiler.java1820
-rw-r--r--src/main/java/org/apache/commons/math3/analysis/differentiation/DerivativeStructure.java1195
-rw-r--r--src/main/java/org/apache/commons/math3/analysis/differentiation/FiniteDifferencesDifferentiator.java384
-rw-r--r--src/main/java/org/apache/commons/math3/analysis/differentiation/GradientFunction.java65
-rw-r--r--src/main/java/org/apache/commons/math3/analysis/differentiation/JacobianFunction.java69
-rw-r--r--src/main/java/org/apache/commons/math3/analysis/differentiation/MultivariateDifferentiableFunction.java42
-rw-r--r--src/main/java/org/apache/commons/math3/analysis/differentiation/MultivariateDifferentiableVectorFunction.java43
-rw-r--r--src/main/java/org/apache/commons/math3/analysis/differentiation/SparseGradient.java877
-rw-r--r--src/main/java/org/apache/commons/math3/analysis/differentiation/UnivariateDifferentiableFunction.java43
-rw-r--r--src/main/java/org/apache/commons/math3/analysis/differentiation/UnivariateDifferentiableMatrixFunction.java40
-rw-r--r--src/main/java/org/apache/commons/math3/analysis/differentiation/UnivariateDifferentiableVectorFunction.java40
-rw-r--r--src/main/java/org/apache/commons/math3/analysis/differentiation/UnivariateFunctionDifferentiator.java33
-rw-r--r--src/main/java/org/apache/commons/math3/analysis/differentiation/UnivariateMatrixFunctionDifferentiator.java33
-rw-r--r--src/main/java/org/apache/commons/math3/analysis/differentiation/UnivariateVectorFunctionDifferentiator.java33
-rw-r--r--src/main/java/org/apache/commons/math3/analysis/differentiation/package-info.java42
-rw-r--r--src/main/java/org/apache/commons/math3/analysis/function/Abs.java33
-rw-r--r--src/main/java/org/apache/commons/math3/analysis/function/Acos.java53
-rw-r--r--src/main/java/org/apache/commons/math3/analysis/function/Acosh.java53
-rw-r--r--src/main/java/org/apache/commons/math3/analysis/function/Add.java32
-rw-r--r--src/main/java/org/apache/commons/math3/analysis/function/Asin.java53
-rw-r--r--src/main/java/org/apache/commons/math3/analysis/function/Asinh.java53
-rw-r--r--src/main/java/org/apache/commons/math3/analysis/function/Atan.java53
-rw-r--r--src/main/java/org/apache/commons/math3/analysis/function/Atan2.java33
-rw-r--r--src/main/java/org/apache/commons/math3/analysis/function/Atanh.java53
-rw-r--r--src/main/java/org/apache/commons/math3/analysis/function/Cbrt.java53
-rw-r--r--src/main/java/org/apache/commons/math3/analysis/function/Ceil.java33
-rw-r--r--src/main/java/org/apache/commons/math3/analysis/function/Constant.java60
-rw-r--r--src/main/java/org/apache/commons/math3/analysis/function/Cos.java53
-rw-r--r--src/main/java/org/apache/commons/math3/analysis/function/Cosh.java51
-rw-r--r--src/main/java/org/apache/commons/math3/analysis/function/Divide.java32
-rw-r--r--src/main/java/org/apache/commons/math3/analysis/function/Exp.java53
-rw-r--r--src/main/java/org/apache/commons/math3/analysis/function/Expm1.java53
-rw-r--r--src/main/java/org/apache/commons/math3/analysis/function/Floor.java33
-rw-r--r--src/main/java/org/apache/commons/math3/analysis/function/Gaussian.java259
-rw-r--r--src/main/java/org/apache/commons/math3/analysis/function/HarmonicOscillator.java183
-rw-r--r--src/main/java/org/apache/commons/math3/analysis/function/Identity.java50
-rw-r--r--src/main/java/org/apache/commons/math3/analysis/function/Inverse.java52
-rw-r--r--src/main/java/org/apache/commons/math3/analysis/function/Log.java53
-rw-r--r--src/main/java/org/apache/commons/math3/analysis/function/Log10.java54
-rw-r--r--src/main/java/org/apache/commons/math3/analysis/function/Log1p.java53
-rw-r--r--src/main/java/org/apache/commons/math3/analysis/function/Logistic.java228
-rw-r--r--src/main/java/org/apache/commons/math3/analysis/function/Logit.java212
-rw-r--r--src/main/java/org/apache/commons/math3/analysis/function/Max.java33
-rw-r--r--src/main/java/org/apache/commons/math3/analysis/function/Min.java33
-rw-r--r--src/main/java/org/apache/commons/math3/analysis/function/Minus.java50
-rw-r--r--src/main/java/org/apache/commons/math3/analysis/function/Multiply.java32
-rw-r--r--src/main/java/org/apache/commons/math3/analysis/function/Pow.java33
-rw-r--r--src/main/java/org/apache/commons/math3/analysis/function/Power.java63
-rw-r--r--src/main/java/org/apache/commons/math3/analysis/function/Rint.java33
-rw-r--r--src/main/java/org/apache/commons/math3/analysis/function/Sigmoid.java218
-rw-r--r--src/main/java/org/apache/commons/math3/analysis/function/Signum.java33
-rw-r--r--src/main/java/org/apache/commons/math3/analysis/function/Sin.java51
-rw-r--r--src/main/java/org/apache/commons/math3/analysis/function/Sinc.java205
-rw-r--r--src/main/java/org/apache/commons/math3/analysis/function/Sinh.java51
-rw-r--r--src/main/java/org/apache/commons/math3/analysis/function/Sqrt.java53
-rw-r--r--src/main/java/org/apache/commons/math3/analysis/function/StepFunction.java101
-rw-r--r--src/main/java/org/apache/commons/math3/analysis/function/Subtract.java32
-rw-r--r--src/main/java/org/apache/commons/math3/analysis/function/Tan.java53
-rw-r--r--src/main/java/org/apache/commons/math3/analysis/function/Tanh.java53
-rw-r--r--src/main/java/org/apache/commons/math3/analysis/function/Ulp.java33
-rw-r--r--src/main/java/org/apache/commons/math3/analysis/function/package-info.java26
-rw-r--r--src/main/java/org/apache/commons/math3/analysis/integration/BaseAbstractUnivariateIntegrator.java297
-rw-r--r--src/main/java/org/apache/commons/math3/analysis/integration/IterativeLegendreGaussIntegrator.java183
-rw-r--r--src/main/java/org/apache/commons/math3/analysis/integration/LegendreGaussIntegrator.java265
-rw-r--r--src/main/java/org/apache/commons/math3/analysis/integration/MidPointIntegrator.java169
-rw-r--r--src/main/java/org/apache/commons/math3/analysis/integration/RombergIntegrator.java142
-rw-r--r--src/main/java/org/apache/commons/math3/analysis/integration/SimpsonIntegrator.java129
-rw-r--r--src/main/java/org/apache/commons/math3/analysis/integration/TrapezoidIntegrator.java168
-rw-r--r--src/main/java/org/apache/commons/math3/analysis/integration/UnivariateIntegrator.java95
-rw-r--r--src/main/java/org/apache/commons/math3/analysis/integration/gauss/BaseRuleFactory.java153
-rw-r--r--src/main/java/org/apache/commons/math3/analysis/integration/gauss/GaussIntegrator.java129
-rw-r--r--src/main/java/org/apache/commons/math3/analysis/integration/gauss/GaussIntegratorFactory.java167
-rw-r--r--src/main/java/org/apache/commons/math3/analysis/integration/gauss/HermiteRuleFactory.java177
-rw-r--r--src/main/java/org/apache/commons/math3/analysis/integration/gauss/LegendreHighPrecisionRuleFactory.java215
-rw-r--r--src/main/java/org/apache/commons/math3/analysis/integration/gauss/LegendreRuleFactory.java140
-rw-r--r--src/main/java/org/apache/commons/math3/analysis/integration/gauss/SymmetricGaussIntegrator.java103
-rw-r--r--src/main/java/org/apache/commons/math3/analysis/integration/gauss/package-info.java22
-rw-r--r--src/main/java/org/apache/commons/math3/analysis/integration/package-info.java22
-rw-r--r--src/main/java/org/apache/commons/math3/analysis/interpolation/AkimaSplineInterpolator.java215
-rw-r--r--src/main/java/org/apache/commons/math3/analysis/interpolation/BicubicInterpolatingFunction.java325
-rw-r--r--src/main/java/org/apache/commons/math3/analysis/interpolation/BicubicInterpolator.java113
-rw-r--r--src/main/java/org/apache/commons/math3/analysis/interpolation/BicubicSplineInterpolatingFunction.java641
-rw-r--r--src/main/java/org/apache/commons/math3/analysis/interpolation/BicubicSplineInterpolator.java176
-rw-r--r--src/main/java/org/apache/commons/math3/analysis/interpolation/BivariateGridInterpolator.java51
-rw-r--r--src/main/java/org/apache/commons/math3/analysis/interpolation/DividedDifferenceInterpolator.java120
-rw-r--r--src/main/java/org/apache/commons/math3/analysis/interpolation/FieldHermiteInterpolator.java209
-rw-r--r--src/main/java/org/apache/commons/math3/analysis/interpolation/HermiteInterpolator.java239
-rw-r--r--src/main/java/org/apache/commons/math3/analysis/interpolation/InterpolatingMicrosphere.java385
-rw-r--r--src/main/java/org/apache/commons/math3/analysis/interpolation/InterpolatingMicrosphere2D.java87
-rw-r--r--src/main/java/org/apache/commons/math3/analysis/interpolation/LinearInterpolator.java79
-rw-r--r--src/main/java/org/apache/commons/math3/analysis/interpolation/LoessInterpolator.java473
-rw-r--r--src/main/java/org/apache/commons/math3/analysis/interpolation/MicrosphereInterpolatingFunction.java253
-rw-r--r--src/main/java/org/apache/commons/math3/analysis/interpolation/MicrosphereInterpolator.java105
-rw-r--r--src/main/java/org/apache/commons/math3/analysis/interpolation/MicrosphereProjectionInterpolator.java164
-rw-r--r--src/main/java/org/apache/commons/math3/analysis/interpolation/MultivariateInterpolator.java51
-rw-r--r--src/main/java/org/apache/commons/math3/analysis/interpolation/NevilleInterpolator.java60
-rw-r--r--src/main/java/org/apache/commons/math3/analysis/interpolation/PiecewiseBicubicSplineInterpolatingFunction.java210
-rw-r--r--src/main/java/org/apache/commons/math3/analysis/interpolation/PiecewiseBicubicSplineInterpolator.java61
-rw-r--r--src/main/java/org/apache/commons/math3/analysis/interpolation/SmoothingPolynomialBicubicSplineInterpolator.java171
-rw-r--r--src/main/java/org/apache/commons/math3/analysis/interpolation/SplineInterpolator.java127
-rw-r--r--src/main/java/org/apache/commons/math3/analysis/interpolation/TricubicInterpolatingFunction.java508
-rw-r--r--src/main/java/org/apache/commons/math3/analysis/interpolation/TricubicInterpolator.java143
-rw-r--r--src/main/java/org/apache/commons/math3/analysis/interpolation/TricubicSplineInterpolatingFunction.java482
-rw-r--r--src/main/java/org/apache/commons/math3/analysis/interpolation/TricubicSplineInterpolator.java201
-rw-r--r--src/main/java/org/apache/commons/math3/analysis/interpolation/TrivariateGridInterpolator.java54
-rw-r--r--src/main/java/org/apache/commons/math3/analysis/interpolation/UnivariateInterpolator.java41
-rw-r--r--src/main/java/org/apache/commons/math3/analysis/interpolation/UnivariatePeriodicInterpolator.java124
-rw-r--r--src/main/java/org/apache/commons/math3/analysis/interpolation/package-info.java22
-rw-r--r--src/main/java/org/apache/commons/math3/analysis/package-info.java27
-rw-r--r--src/main/java/org/apache/commons/math3/analysis/polynomials/PolynomialFunction.java412
-rw-r--r--src/main/java/org/apache/commons/math3/analysis/polynomials/PolynomialFunctionLagrangeForm.java326
-rw-r--r--src/main/java/org/apache/commons/math3/analysis/polynomials/PolynomialFunctionNewtonForm.java245
-rw-r--r--src/main/java/org/apache/commons/math3/analysis/polynomials/PolynomialSplineFunction.java246
-rw-r--r--src/main/java/org/apache/commons/math3/analysis/polynomials/PolynomialsUtils.java444
-rw-r--r--src/main/java/org/apache/commons/math3/analysis/polynomials/package-info.java23
-rw-r--r--src/main/java/org/apache/commons/math3/analysis/solvers/AbstractDifferentiableUnivariateSolver.java82
-rw-r--r--src/main/java/org/apache/commons/math3/analysis/solvers/AbstractPolynomialSolver.java80
-rw-r--r--src/main/java/org/apache/commons/math3/analysis/solvers/AbstractUnivariateDifferentiableSolver.java82
-rw-r--r--src/main/java/org/apache/commons/math3/analysis/solvers/AbstractUnivariateSolver.java60
-rw-r--r--src/main/java/org/apache/commons/math3/analysis/solvers/AllowedSolution.java75
-rw-r--r--src/main/java/org/apache/commons/math3/analysis/solvers/BaseAbstractUnivariateSolver.java318
-rw-r--r--src/main/java/org/apache/commons/math3/analysis/solvers/BaseSecantSolver.java278
-rw-r--r--src/main/java/org/apache/commons/math3/analysis/solvers/BaseUnivariateSolver.java142
-rw-r--r--src/main/java/org/apache/commons/math3/analysis/solvers/BisectionSolver.java91
-rw-r--r--src/main/java/org/apache/commons/math3/analysis/solvers/BracketedRealFieldUnivariateSolver.java142
-rw-r--r--src/main/java/org/apache/commons/math3/analysis/solvers/BracketedUnivariateSolver.java92
-rw-r--r--src/main/java/org/apache/commons/math3/analysis/solvers/BracketingNthOrderBrentSolver.java411
-rw-r--r--src/main/java/org/apache/commons/math3/analysis/solvers/BrentSolver.java243
-rw-r--r--src/main/java/org/apache/commons/math3/analysis/solvers/DifferentiableUnivariateSolver.java30
-rw-r--r--src/main/java/org/apache/commons/math3/analysis/solvers/FieldBracketingNthOrderBrentSolver.java446
-rw-r--r--src/main/java/org/apache/commons/math3/analysis/solvers/IllinoisSolver.java82
-rw-r--r--src/main/java/org/apache/commons/math3/analysis/solvers/LaguerreSolver.java440
-rw-r--r--src/main/java/org/apache/commons/math3/analysis/solvers/MullerSolver.java202
-rw-r--r--src/main/java/org/apache/commons/math3/analysis/solvers/MullerSolver2.java168
-rw-r--r--src/main/java/org/apache/commons/math3/analysis/solvers/NewtonRaphsonSolver.java92
-rw-r--r--src/main/java/org/apache/commons/math3/analysis/solvers/NewtonSolver.java92
-rw-r--r--src/main/java/org/apache/commons/math3/analysis/solvers/PegasusSolver.java84
-rw-r--r--src/main/java/org/apache/commons/math3/analysis/solvers/PolynomialSolver.java28
-rw-r--r--src/main/java/org/apache/commons/math3/analysis/solvers/RegulaFalsiSolver.java94
-rw-r--r--src/main/java/org/apache/commons/math3/analysis/solvers/RiddersSolver.java142
-rw-r--r--src/main/java/org/apache/commons/math3/analysis/solvers/SecantSolver.java135
-rw-r--r--src/main/java/org/apache/commons/math3/analysis/solvers/UnivariateDifferentiableSolver.java29
-rw-r--r--src/main/java/org/apache/commons/math3/analysis/solvers/UnivariateSolver.java28
-rw-r--r--src/main/java/org/apache/commons/math3/analysis/solvers/UnivariateSolverUtils.java467
-rw-r--r--src/main/java/org/apache/commons/math3/analysis/solvers/package-info.java22
-rw-r--r--src/main/java/org/apache/commons/math3/complex/Complex.java1219
-rw-r--r--src/main/java/org/apache/commons/math3/complex/ComplexField.java87
-rw-r--r--src/main/java/org/apache/commons/math3/complex/ComplexFormat.java420
-rw-r--r--src/main/java/org/apache/commons/math3/complex/ComplexUtils.java82
-rw-r--r--src/main/java/org/apache/commons/math3/complex/Quaternion.java434
-rw-r--r--src/main/java/org/apache/commons/math3/complex/RootsOfUnity.java198
-rw-r--r--src/main/java/org/apache/commons/math3/complex/package-info.java18
-rw-r--r--src/main/java/org/apache/commons/math3/dfp/BracketingNthOrderBrentSolverDFP.java161
-rw-r--r--src/main/java/org/apache/commons/math3/dfp/Dfp.java3082
-rw-r--r--src/main/java/org/apache/commons/math3/dfp/DfpDec.java388
-rw-r--r--src/main/java/org/apache/commons/math3/dfp/DfpField.java826
-rw-r--r--src/main/java/org/apache/commons/math3/dfp/DfpMath.java970
-rw-r--r--src/main/java/org/apache/commons/math3/dfp/UnivariateDfpFunction.java40
-rw-r--r--src/main/java/org/apache/commons/math3/dfp/package-info.java80
-rw-r--r--src/main/java/org/apache/commons/math3/distribution/AbstractIntegerDistribution.java250
-rw-r--r--src/main/java/org/apache/commons/math3/distribution/AbstractMultivariateRealDistribution.java68
-rw-r--r--src/main/java/org/apache/commons/math3/distribution/AbstractRealDistribution.java306
-rw-r--r--src/main/java/org/apache/commons/math3/distribution/BetaDistribution.java417
-rw-r--r--src/main/java/org/apache/commons/math3/distribution/BinomialDistribution.java195
-rw-r--r--src/main/java/org/apache/commons/math3/distribution/CauchyDistribution.java251
-rw-r--r--src/main/java/org/apache/commons/math3/distribution/ChiSquaredDistribution.java196
-rw-r--r--src/main/java/org/apache/commons/math3/distribution/ConstantRealDistribution.java112
-rw-r--r--src/main/java/org/apache/commons/math3/distribution/EnumeratedDistribution.java283
-rw-r--r--src/main/java/org/apache/commons/math3/distribution/EnumeratedIntegerDistribution.java274
-rw-r--r--src/main/java/org/apache/commons/math3/distribution/EnumeratedRealDistribution.java336
-rw-r--r--src/main/java/org/apache/commons/math3/distribution/ExponentialDistribution.java342
-rw-r--r--src/main/java/org/apache/commons/math3/distribution/FDistribution.java341
-rw-r--r--src/main/java/org/apache/commons/math3/distribution/GammaDistribution.java505
-rw-r--r--src/main/java/org/apache/commons/math3/distribution/GeometricDistribution.java186
-rw-r--r--src/main/java/org/apache/commons/math3/distribution/GumbelDistribution.java167
-rw-r--r--src/main/java/org/apache/commons/math3/distribution/HypergeometricDistribution.java347
-rw-r--r--src/main/java/org/apache/commons/math3/distribution/IntegerDistribution.java145
-rw-r--r--src/main/java/org/apache/commons/math3/distribution/KolmogorovSmirnovDistribution.java338
-rw-r--r--src/main/java/org/apache/commons/math3/distribution/LaplaceDistribution.java160
-rw-r--r--src/main/java/org/apache/commons/math3/distribution/LevyDistribution.java197
-rw-r--r--src/main/java/org/apache/commons/math3/distribution/LogNormalDistribution.java349
-rw-r--r--src/main/java/org/apache/commons/math3/distribution/LogisticDistribution.java161
-rw-r--r--src/main/java/org/apache/commons/math3/distribution/MixtureMultivariateNormalDistribution.java104
-rw-r--r--src/main/java/org/apache/commons/math3/distribution/MixtureMultivariateRealDistribution.java167
-rw-r--r--src/main/java/org/apache/commons/math3/distribution/MultivariateNormalDistribution.java237
-rw-r--r--src/main/java/org/apache/commons/math3/distribution/MultivariateRealDistribution.java75
-rw-r--r--src/main/java/org/apache/commons/math3/distribution/NakagamiDistribution.java192
-rw-r--r--src/main/java/org/apache/commons/math3/distribution/NormalDistribution.java308
-rw-r--r--src/main/java/org/apache/commons/math3/distribution/ParetoDistribution.java315
-rw-r--r--src/main/java/org/apache/commons/math3/distribution/PascalDistribution.java240
-rw-r--r--src/main/java/org/apache/commons/math3/distribution/PoissonDistribution.java394
-rw-r--r--src/main/java/org/apache/commons/math3/distribution/RealDistribution.java182
-rw-r--r--src/main/java/org/apache/commons/math3/distribution/SaddlePointExpansion.java199
-rw-r--r--src/main/java/org/apache/commons/math3/distribution/TDistribution.java270
-rw-r--r--src/main/java/org/apache/commons/math3/distribution/TriangularDistribution.java274
-rw-r--r--src/main/java/org/apache/commons/math3/distribution/UniformIntegerDistribution.java174
-rw-r--r--src/main/java/org/apache/commons/math3/distribution/UniformRealDistribution.java234
-rw-r--r--src/main/java/org/apache/commons/math3/distribution/WeibullDistribution.java346
-rw-r--r--src/main/java/org/apache/commons/math3/distribution/ZipfDistribution.java502
-rw-r--r--src/main/java/org/apache/commons/math3/distribution/fitting/MultivariateNormalMixtureExpectationMaximization.java454
-rw-r--r--src/main/java/org/apache/commons/math3/distribution/fitting/package-info.java20
-rw-r--r--src/main/java/org/apache/commons/math3/distribution/package-info.java18
-rw-r--r--src/main/java/org/apache/commons/math3/exception/ConvergenceException.java46
-rw-r--r--src/main/java/org/apache/commons/math3/exception/DimensionMismatchException.java62
-rw-r--r--src/main/java/org/apache/commons/math3/exception/InsufficientDataException.java46
-rw-r--r--src/main/java/org/apache/commons/math3/exception/MathArithmeticException.java71
-rw-r--r--src/main/java/org/apache/commons/math3/exception/MathIllegalArgumentException.java63
-rw-r--r--src/main/java/org/apache/commons/math3/exception/MathIllegalNumberException.java57
-rw-r--r--src/main/java/org/apache/commons/math3/exception/MathIllegalStateException.java83
-rw-r--r--src/main/java/org/apache/commons/math3/exception/MathInternalError.java57
-rw-r--r--src/main/java/org/apache/commons/math3/exception/MathParseException.java53
-rw-r--r--src/main/java/org/apache/commons/math3/exception/MathRuntimeException.java63
-rw-r--r--src/main/java/org/apache/commons/math3/exception/MathUnsupportedOperationException.java69
-rw-r--r--src/main/java/org/apache/commons/math3/exception/MaxCountExceededException.java61
-rw-r--r--src/main/java/org/apache/commons/math3/exception/MultiDimensionMismatchException.java90
-rw-r--r--src/main/java/org/apache/commons/math3/exception/NoBracketingException.java115
-rw-r--r--src/main/java/org/apache/commons/math3/exception/NoDataException.java45
-rw-r--r--src/main/java/org/apache/commons/math3/exception/NonMonotonicSequenceException.java120
-rw-r--r--src/main/java/org/apache/commons/math3/exception/NotANumberException.java34
-rw-r--r--src/main/java/org/apache/commons/math3/exception/NotFiniteNumberException.java51
-rw-r--r--src/main/java/org/apache/commons/math3/exception/NotPositiveException.java48
-rw-r--r--src/main/java/org/apache/commons/math3/exception/NotStrictlyPositiveException.java49
-rw-r--r--src/main/java/org/apache/commons/math3/exception/NullArgumentException.java46
-rw-r--r--src/main/java/org/apache/commons/math3/exception/NumberIsTooLargeException.java83
-rw-r--r--src/main/java/org/apache/commons/math3/exception/NumberIsTooSmallException.java83
-rw-r--r--src/main/java/org/apache/commons/math3/exception/OutOfRangeException.java75
-rw-r--r--src/main/java/org/apache/commons/math3/exception/TooManyEvaluationsException.java39
-rw-r--r--src/main/java/org/apache/commons/math3/exception/TooManyIterationsException.java39
-rw-r--r--src/main/java/org/apache/commons/math3/exception/ZeroException.java46
-rw-r--r--src/main/java/org/apache/commons/math3/exception/package-info.java21
-rw-r--r--src/main/java/org/apache/commons/math3/exception/util/ArgUtils.java55
-rw-r--r--src/main/java/org/apache/commons/math3/exception/util/DummyLocalizable.java57
-rw-r--r--src/main/java/org/apache/commons/math3/exception/util/ExceptionContext.java334
-rw-r--r--src/main/java/org/apache/commons/math3/exception/util/ExceptionContextProvider.java33
-rw-r--r--src/main/java/org/apache/commons/math3/exception/util/Localizable.java43
-rw-r--r--src/main/java/org/apache/commons/math3/exception/util/LocalizedFormats.java414
-rw-r--r--src/main/java/org/apache/commons/math3/exception/util/package-info.java22
-rw-r--r--src/main/java/org/apache/commons/math3/filter/DefaultMeasurementModel.java77
-rw-r--r--src/main/java/org/apache/commons/math3/filter/DefaultProcessModel.java151
-rw-r--r--src/main/java/org/apache/commons/math3/filter/KalmanFilter.java385
-rw-r--r--src/main/java/org/apache/commons/math3/filter/MeasurementModel.java44
-rw-r--r--src/main/java/org/apache/commons/math3/filter/ProcessModel.java73
-rw-r--r--src/main/java/org/apache/commons/math3/filter/package-info.java18
-rw-r--r--src/main/java/org/apache/commons/math3/fitting/AbstractCurveFitter.java141
-rw-r--r--src/main/java/org/apache/commons/math3/fitting/CurveFitter.java235
-rw-r--r--src/main/java/org/apache/commons/math3/fitting/GaussianCurveFitter.java425
-rw-r--r--src/main/java/org/apache/commons/math3/fitting/GaussianFitter.java362
-rw-r--r--src/main/java/org/apache/commons/math3/fitting/HarmonicCurveFitter.java410
-rw-r--r--src/main/java/org/apache/commons/math3/fitting/HarmonicFitter.java386
-rw-r--r--src/main/java/org/apache/commons/math3/fitting/PolynomialCurveFitter.java127
-rw-r--r--src/main/java/org/apache/commons/math3/fitting/PolynomialFitter.java71
-rw-r--r--src/main/java/org/apache/commons/math3/fitting/SimpleCurveFitter.java119
-rw-r--r--src/main/java/org/apache/commons/math3/fitting/WeightedObservedPoint.java81
-rw-r--r--src/main/java/org/apache/commons/math3/fitting/WeightedObservedPoints.java100
-rw-r--r--src/main/java/org/apache/commons/math3/fitting/leastsquares/AbstractEvaluation.java87
-rw-r--r--src/main/java/org/apache/commons/math3/fitting/leastsquares/DenseWeightedEvaluation.java68
-rw-r--r--src/main/java/org/apache/commons/math3/fitting/leastsquares/EvaluationRmsChecker.java75
-rw-r--r--src/main/java/org/apache/commons/math3/fitting/leastsquares/GaussNewtonOptimizer.java299
-rw-r--r--src/main/java/org/apache/commons/math3/fitting/leastsquares/LeastSquaresAdapter.java77
-rw-r--r--src/main/java/org/apache/commons/math3/fitting/leastsquares/LeastSquaresBuilder.java226
-rw-r--r--src/main/java/org/apache/commons/math3/fitting/leastsquares/LeastSquaresFactory.java532
-rw-r--r--src/main/java/org/apache/commons/math3/fitting/leastsquares/LeastSquaresOptimizer.java62
-rw-r--r--src/main/java/org/apache/commons/math3/fitting/leastsquares/LeastSquaresProblem.java156
-rw-r--r--src/main/java/org/apache/commons/math3/fitting/leastsquares/LevenbergMarquardtOptimizer.java1042
-rw-r--r--src/main/java/org/apache/commons/math3/fitting/leastsquares/MultivariateJacobianFunction.java39
-rw-r--r--src/main/java/org/apache/commons/math3/fitting/leastsquares/OptimumImpl.java97
-rw-r--r--src/main/java/org/apache/commons/math3/fitting/leastsquares/ParameterValidator.java34
-rw-r--r--src/main/java/org/apache/commons/math3/fitting/leastsquares/ValueAndJacobianFunction.java44
-rw-r--r--src/main/java/org/apache/commons/math3/fitting/leastsquares/package-info.java39
-rw-r--r--src/main/java/org/apache/commons/math3/fitting/package-info.java25
-rw-r--r--src/main/java/org/apache/commons/math3/fraction/AbstractFormat.java216
-rw-r--r--src/main/java/org/apache/commons/math3/fraction/BigFraction.java1065
-rw-r--r--src/main/java/org/apache/commons/math3/fraction/BigFractionField.java87
-rw-r--r--src/main/java/org/apache/commons/math3/fraction/BigFractionFormat.java288
-rw-r--r--src/main/java/org/apache/commons/math3/fraction/Fraction.java669
-rw-r--r--src/main/java/org/apache/commons/math3/fraction/FractionConversionException.java56
-rw-r--r--src/main/java/org/apache/commons/math3/fraction/FractionField.java87
-rw-r--r--src/main/java/org/apache/commons/math3/fraction/FractionFormat.java270
-rw-r--r--src/main/java/org/apache/commons/math3/fraction/ProperBigFractionFormat.java239
-rw-r--r--src/main/java/org/apache/commons/math3/fraction/ProperFractionFormat.java231
-rw-r--r--src/main/java/org/apache/commons/math3/fraction/package-info.java18
-rw-r--r--src/main/java/org/apache/commons/math3/genetics/AbstractListChromosome.java119
-rw-r--r--src/main/java/org/apache/commons/math3/genetics/BinaryChromosome.java100
-rw-r--r--src/main/java/org/apache/commons/math3/genetics/BinaryMutation.java56
-rw-r--r--src/main/java/org/apache/commons/math3/genetics/Chromosome.java108
-rw-r--r--src/main/java/org/apache/commons/math3/genetics/ChromosomePair.java66
-rw-r--r--src/main/java/org/apache/commons/math3/genetics/CrossoverPolicy.java40
-rw-r--r--src/main/java/org/apache/commons/math3/genetics/CycleCrossover.java185
-rw-r--r--src/main/java/org/apache/commons/math3/genetics/ElitisticListPopulation.java125
-rw-r--r--src/main/java/org/apache/commons/math3/genetics/Fitness.java32
-rw-r--r--src/main/java/org/apache/commons/math3/genetics/FixedElapsedTime.java78
-rw-r--r--src/main/java/org/apache/commons/math3/genetics/FixedGenerationCount.java71
-rw-r--r--src/main/java/org/apache/commons/math3/genetics/GeneticAlgorithm.java240
-rw-r--r--src/main/java/org/apache/commons/math3/genetics/InvalidRepresentationException.java41
-rw-r--r--src/main/java/org/apache/commons/math3/genetics/ListPopulation.java245
-rw-r--r--src/main/java/org/apache/commons/math3/genetics/MutationPolicy.java37
-rw-r--r--src/main/java/org/apache/commons/math3/genetics/NPointCrossover.java187
-rw-r--r--src/main/java/org/apache/commons/math3/genetics/OnePointCrossover.java128
-rw-r--r--src/main/java/org/apache/commons/math3/genetics/OrderedCrossover.java154
-rw-r--r--src/main/java/org/apache/commons/math3/genetics/PermutationChromosome.java38
-rw-r--r--src/main/java/org/apache/commons/math3/genetics/Population.java63
-rw-r--r--src/main/java/org/apache/commons/math3/genetics/RandomKey.java296
-rw-r--r--src/main/java/org/apache/commons/math3/genetics/RandomKeyMutation.java55
-rw-r--r--src/main/java/org/apache/commons/math3/genetics/SelectionPolicy.java36
-rw-r--r--src/main/java/org/apache/commons/math3/genetics/StoppingCondition.java33
-rw-r--r--src/main/java/org/apache/commons/math3/genetics/TournamentSelection.java119
-rw-r--r--src/main/java/org/apache/commons/math3/genetics/UniformCrossover.java140
-rw-r--r--src/main/java/org/apache/commons/math3/genetics/package-info.java18
-rw-r--r--src/main/java/org/apache/commons/math3/geometry/Point.java52
-rw-r--r--src/main/java/org/apache/commons/math3/geometry/Space.java47
-rw-r--r--src/main/java/org/apache/commons/math3/geometry/Vector.java194
-rw-r--r--src/main/java/org/apache/commons/math3/geometry/VectorFormat.java307
-rw-r--r--src/main/java/org/apache/commons/math3/geometry/enclosing/Encloser.java36
-rw-r--r--src/main/java/org/apache/commons/math3/geometry/enclosing/EnclosingBall.java103
-rw-r--r--src/main/java/org/apache/commons/math3/geometry/enclosing/SupportBallGenerator.java42
-rw-r--r--src/main/java/org/apache/commons/math3/geometry/enclosing/WelzlEncloser.java181
-rw-r--r--src/main/java/org/apache/commons/math3/geometry/enclosing/package-info.java24
-rw-r--r--src/main/java/org/apache/commons/math3/geometry/euclidean/oned/Euclidean1D.java100
-rw-r--r--src/main/java/org/apache/commons/math3/geometry/euclidean/oned/Interval.java135
-rw-r--r--src/main/java/org/apache/commons/math3/geometry/euclidean/oned/IntervalsSet.java686
-rw-r--r--src/main/java/org/apache/commons/math3/geometry/euclidean/oned/OrientedPoint.java153
-rw-r--r--src/main/java/org/apache/commons/math3/geometry/euclidean/oned/SubOrientedPoint.java72
-rw-r--r--src/main/java/org/apache/commons/math3/geometry/euclidean/oned/Vector1D.java356
-rw-r--r--src/main/java/org/apache/commons/math3/geometry/euclidean/oned/Vector1DFormat.java135
-rw-r--r--src/main/java/org/apache/commons/math3/geometry/euclidean/oned/package-info.java24
-rw-r--r--src/main/java/org/apache/commons/math3/geometry/euclidean/threed/CardanEulerSingularityException.java44
-rw-r--r--src/main/java/org/apache/commons/math3/geometry/euclidean/threed/Euclidean3D.java74
-rw-r--r--src/main/java/org/apache/commons/math3/geometry/euclidean/threed/FieldRotation.java1663
-rw-r--r--src/main/java/org/apache/commons/math3/geometry/euclidean/threed/FieldVector3D.java1185
-rw-r--r--src/main/java/org/apache/commons/math3/geometry/euclidean/threed/Line.java275
-rw-r--r--src/main/java/org/apache/commons/math3/geometry/euclidean/threed/NotARotationMatrixException.java47
-rw-r--r--src/main/java/org/apache/commons/math3/geometry/euclidean/threed/OutlineExtractor.java263
-rw-r--r--src/main/java/org/apache/commons/math3/geometry/euclidean/threed/Plane.java527
-rw-r--r--src/main/java/org/apache/commons/math3/geometry/euclidean/threed/PolyhedronsSet.java739
-rw-r--r--src/main/java/org/apache/commons/math3/geometry/euclidean/threed/Rotation.java1424
-rw-r--r--src/main/java/org/apache/commons/math3/geometry/euclidean/threed/RotationConvention.java79
-rw-r--r--src/main/java/org/apache/commons/math3/geometry/euclidean/threed/RotationOrder.java174
-rw-r--r--src/main/java/org/apache/commons/math3/geometry/euclidean/threed/Segment.java66
-rw-r--r--src/main/java/org/apache/commons/math3/geometry/euclidean/threed/SphereGenerator.java152
-rw-r--r--src/main/java/org/apache/commons/math3/geometry/euclidean/threed/SphericalCoordinates.java395
-rw-r--r--src/main/java/org/apache/commons/math3/geometry/euclidean/threed/SubLine.java165
-rw-r--r--src/main/java/org/apache/commons/math3/geometry/euclidean/threed/SubPlane.java108
-rw-r--r--src/main/java/org/apache/commons/math3/geometry/euclidean/threed/Vector3D.java588
-rw-r--r--src/main/java/org/apache/commons/math3/geometry/euclidean/threed/Vector3DFormat.java155
-rw-r--r--src/main/java/org/apache/commons/math3/geometry/euclidean/threed/package-info.java24
-rw-r--r--src/main/java/org/apache/commons/math3/geometry/euclidean/twod/DiskGenerator.java108
-rw-r--r--src/main/java/org/apache/commons/math3/geometry/euclidean/twod/Euclidean2D.java74
-rw-r--r--src/main/java/org/apache/commons/math3/geometry/euclidean/twod/Line.java587
-rw-r--r--src/main/java/org/apache/commons/math3/geometry/euclidean/twod/NestedLoops.java201
-rw-r--r--src/main/java/org/apache/commons/math3/geometry/euclidean/twod/PolygonsSet.java1160
-rw-r--r--src/main/java/org/apache/commons/math3/geometry/euclidean/twod/Segment.java112
-rw-r--r--src/main/java/org/apache/commons/math3/geometry/euclidean/twod/SubLine.java214
-rw-r--r--src/main/java/org/apache/commons/math3/geometry/euclidean/twod/Vector2D.java460
-rw-r--r--src/main/java/org/apache/commons/math3/geometry/euclidean/twod/Vector2DFormat.java138
-rw-r--r--src/main/java/org/apache/commons/math3/geometry/euclidean/twod/hull/AbstractConvexHullGenerator2D.java116
-rw-r--r--src/main/java/org/apache/commons/math3/geometry/euclidean/twod/hull/AklToussaintHeuristic.java153
-rw-r--r--src/main/java/org/apache/commons/math3/geometry/euclidean/twod/hull/ConvexHull2D.java172
-rw-r--r--src/main/java/org/apache/commons/math3/geometry/euclidean/twod/hull/ConvexHullGenerator2D.java37
-rw-r--r--src/main/java/org/apache/commons/math3/geometry/euclidean/twod/hull/MonotoneChain.java181
-rw-r--r--src/main/java/org/apache/commons/math3/geometry/euclidean/twod/hull/package-info.java25
-rw-r--r--src/main/java/org/apache/commons/math3/geometry/euclidean/twod/package-info.java24
-rw-r--r--src/main/java/org/apache/commons/math3/geometry/hull/ConvexHull.java48
-rw-r--r--src/main/java/org/apache/commons/math3/geometry/hull/ConvexHullGenerator.java49
-rw-r--r--src/main/java/org/apache/commons/math3/geometry/hull/package-info.java24
-rw-r--r--src/main/java/org/apache/commons/math3/geometry/package-info.java21
-rw-r--r--src/main/java/org/apache/commons/math3/geometry/partitioning/AbstractRegion.java540
-rw-r--r--src/main/java/org/apache/commons/math3/geometry/partitioning/AbstractSubHyperplane.java191
-rw-r--r--src/main/java/org/apache/commons/math3/geometry/partitioning/BSPTree.java821
-rw-r--r--src/main/java/org/apache/commons/math3/geometry/partitioning/BSPTreeVisitor.java114
-rw-r--r--src/main/java/org/apache/commons/math3/geometry/partitioning/BoundaryAttribute.java116
-rw-r--r--src/main/java/org/apache/commons/math3/geometry/partitioning/BoundaryBuilder.java95
-rw-r--r--src/main/java/org/apache/commons/math3/geometry/partitioning/BoundaryProjection.java83
-rw-r--r--src/main/java/org/apache/commons/math3/geometry/partitioning/BoundaryProjector.java200
-rw-r--r--src/main/java/org/apache/commons/math3/geometry/partitioning/BoundarySizeVisitor.java65
-rw-r--r--src/main/java/org/apache/commons/math3/geometry/partitioning/Characterization.java190
-rw-r--r--src/main/java/org/apache/commons/math3/geometry/partitioning/Embedding.java68
-rw-r--r--src/main/java/org/apache/commons/math3/geometry/partitioning/Hyperplane.java98
-rw-r--r--src/main/java/org/apache/commons/math3/geometry/partitioning/InsideFinder.java150
-rw-r--r--src/main/java/org/apache/commons/math3/geometry/partitioning/NodesSet.java72
-rw-r--r--src/main/java/org/apache/commons/math3/geometry/partitioning/Region.java221
-rw-r--r--src/main/java/org/apache/commons/math3/geometry/partitioning/RegionFactory.java378
-rw-r--r--src/main/java/org/apache/commons/math3/geometry/partitioning/Side.java37
-rw-r--r--src/main/java/org/apache/commons/math3/geometry/partitioning/SubHyperplane.java155
-rw-r--r--src/main/java/org/apache/commons/math3/geometry/partitioning/Transform.java80
-rw-r--r--src/main/java/org/apache/commons/math3/geometry/partitioning/package-info.java114
-rw-r--r--src/main/java/org/apache/commons/math3/geometry/partitioning/utilities/AVLTree.java634
-rw-r--r--src/main/java/org/apache/commons/math3/geometry/partitioning/utilities/OrderedTuple.java431
-rw-r--r--src/main/java/org/apache/commons/math3/geometry/partitioning/utilities/doc-files/OrderedTuple.pngbin0 -> 28882 bytes
-rw-r--r--src/main/java/org/apache/commons/math3/geometry/partitioning/utilities/package-info.java24
-rw-r--r--src/main/java/org/apache/commons/math3/geometry/spherical/oned/Arc.java132
-rw-r--r--src/main/java/org/apache/commons/math3/geometry/spherical/oned/ArcsSet.java955
-rw-r--r--src/main/java/org/apache/commons/math3/geometry/spherical/oned/LimitAngle.java127
-rw-r--r--src/main/java/org/apache/commons/math3/geometry/spherical/oned/S1Point.java157
-rw-r--r--src/main/java/org/apache/commons/math3/geometry/spherical/oned/Sphere1D.java106
-rw-r--r--src/main/java/org/apache/commons/math3/geometry/spherical/oned/SubLimitAngle.java66
-rw-r--r--src/main/java/org/apache/commons/math3/geometry/spherical/oned/package-info.java30
-rw-r--r--src/main/java/org/apache/commons/math3/geometry/spherical/twod/Circle.java326
-rw-r--r--src/main/java/org/apache/commons/math3/geometry/spherical/twod/Edge.java222
-rw-r--r--src/main/java/org/apache/commons/math3/geometry/spherical/twod/EdgesBuilder.java169
-rw-r--r--src/main/java/org/apache/commons/math3/geometry/spherical/twod/PropertiesComputer.java173
-rw-r--r--src/main/java/org/apache/commons/math3/geometry/spherical/twod/S2Point.java237
-rw-r--r--src/main/java/org/apache/commons/math3/geometry/spherical/twod/Sphere2D.java80
-rw-r--r--src/main/java/org/apache/commons/math3/geometry/spherical/twod/SphericalPolygonsSet.java565
-rw-r--r--src/main/java/org/apache/commons/math3/geometry/spherical/twod/SubCircle.java72
-rw-r--r--src/main/java/org/apache/commons/math3/geometry/spherical/twod/Vertex.java124
-rw-r--r--src/main/java/org/apache/commons/math3/geometry/spherical/twod/package-info.java30
-rw-r--r--src/main/java/org/apache/commons/math3/linear/AbstractFieldMatrix.java1155
-rw-r--r--src/main/java/org/apache/commons/math3/linear/AbstractRealMatrix.java1010
-rw-r--r--src/main/java/org/apache/commons/math3/linear/AnyMatrix.java47
-rw-r--r--src/main/java/org/apache/commons/math3/linear/Array2DRowFieldMatrix.java612
-rw-r--r--src/main/java/org/apache/commons/math3/linear/Array2DRowRealMatrix.java540
-rw-r--r--src/main/java/org/apache/commons/math3/linear/ArrayFieldVector.java1091
-rw-r--r--src/main/java/org/apache/commons/math3/linear/ArrayRealVector.java937
-rw-r--r--src/main/java/org/apache/commons/math3/linear/BiDiagonalTransformer.java388
-rw-r--r--src/main/java/org/apache/commons/math3/linear/BlockFieldMatrix.java1664
-rw-r--r--src/main/java/org/apache/commons/math3/linear/BlockRealMatrix.java1653
-rw-r--r--src/main/java/org/apache/commons/math3/linear/CholeskyDecomposition.java319
-rw-r--r--src/main/java/org/apache/commons/math3/linear/ConjugateGradient.java231
-rw-r--r--src/main/java/org/apache/commons/math3/linear/DecompositionSolver.java97
-rw-r--r--src/main/java/org/apache/commons/math3/linear/DefaultFieldMatrixChangingVisitor.java58
-rw-r--r--src/main/java/org/apache/commons/math3/linear/DefaultFieldMatrixPreservingVisitor.java56
-rw-r--r--src/main/java/org/apache/commons/math3/linear/DefaultIterativeLinearSolverEvent.java142
-rw-r--r--src/main/java/org/apache/commons/math3/linear/DefaultRealMatrixChangingVisitor.java42
-rw-r--r--src/main/java/org/apache/commons/math3/linear/DefaultRealMatrixPreservingVisitor.java40
-rw-r--r--src/main/java/org/apache/commons/math3/linear/DiagonalMatrix.java353
-rw-r--r--src/main/java/org/apache/commons/math3/linear/EigenDecomposition.java968
-rw-r--r--src/main/java/org/apache/commons/math3/linear/FieldDecompositionSolver.java79
-rw-r--r--src/main/java/org/apache/commons/math3/linear/FieldLUDecomposition.java461
-rw-r--r--src/main/java/org/apache/commons/math3/linear/FieldMatrix.java818
-rw-r--r--src/main/java/org/apache/commons/math3/linear/FieldMatrixChangingVisitor.java61
-rw-r--r--src/main/java/org/apache/commons/math3/linear/FieldMatrixPreservingVisitor.java60
-rw-r--r--src/main/java/org/apache/commons/math3/linear/FieldVector.java324
-rw-r--r--src/main/java/org/apache/commons/math3/linear/FieldVectorChangingVisitor.java55
-rw-r--r--src/main/java/org/apache/commons/math3/linear/FieldVectorPreservingVisitor.java54
-rw-r--r--src/main/java/org/apache/commons/math3/linear/HessenbergTransformer.java243
-rw-r--r--src/main/java/org/apache/commons/math3/linear/IllConditionedOperatorException.java39
-rw-r--r--src/main/java/org/apache/commons/math3/linear/IterativeLinearSolver.java166
-rw-r--r--src/main/java/org/apache/commons/math3/linear/IterativeLinearSolverEvent.java104
-rw-r--r--src/main/java/org/apache/commons/math3/linear/JacobiPreconditioner.java127
-rw-r--r--src/main/java/org/apache/commons/math3/linear/LUDecomposition.java409
-rw-r--r--src/main/java/org/apache/commons/math3/linear/MatrixDimensionMismatchException.java75
-rw-r--r--src/main/java/org/apache/commons/math3/linear/MatrixUtils.java1080
-rw-r--r--src/main/java/org/apache/commons/math3/linear/NonPositiveDefiniteMatrixException.java75
-rw-r--r--src/main/java/org/apache/commons/math3/linear/NonPositiveDefiniteOperatorException.java40
-rw-r--r--src/main/java/org/apache/commons/math3/linear/NonSelfAdjointOperatorException.java41
-rw-r--r--src/main/java/org/apache/commons/math3/linear/NonSquareMatrixException.java40
-rw-r--r--src/main/java/org/apache/commons/math3/linear/NonSquareOperatorException.java40
-rw-r--r--src/main/java/org/apache/commons/math3/linear/NonSymmetricMatrixException.java74
-rw-r--r--src/main/java/org/apache/commons/math3/linear/OpenMapRealMatrix.java301
-rw-r--r--src/main/java/org/apache/commons/math3/linear/OpenMapRealVector.java799
-rw-r--r--src/main/java/org/apache/commons/math3/linear/PreconditionedIterativeLinearSolver.java205
-rw-r--r--src/main/java/org/apache/commons/math3/linear/QRDecomposition.java492
-rw-r--r--src/main/java/org/apache/commons/math3/linear/RRQRDecomposition.java244
-rw-r--r--src/main/java/org/apache/commons/math3/linear/RealLinearOperator.java103
-rw-r--r--src/main/java/org/apache/commons/math3/linear/RealMatrix.java827
-rw-r--r--src/main/java/org/apache/commons/math3/linear/RealMatrixChangingVisitor.java59
-rw-r--r--src/main/java/org/apache/commons/math3/linear/RealMatrixFormat.java442
-rw-r--r--src/main/java/org/apache/commons/math3/linear/RealMatrixPreservingVisitor.java58
-rw-r--r--src/main/java/org/apache/commons/math3/linear/RealVector.java1509
-rw-r--r--src/main/java/org/apache/commons/math3/linear/RealVectorChangingVisitor.java56
-rw-r--r--src/main/java/org/apache/commons/math3/linear/RealVectorFormat.java310
-rw-r--r--src/main/java/org/apache/commons/math3/linear/RealVectorPreservingVisitor.java55
-rw-r--r--src/main/java/org/apache/commons/math3/linear/RectangularCholeskyDecomposition.java191
-rw-r--r--src/main/java/org/apache/commons/math3/linear/SchurTransformer.java474
-rw-r--r--src/main/java/org/apache/commons/math3/linear/SingularMatrixException.java35
-rw-r--r--src/main/java/org/apache/commons/math3/linear/SingularOperatorException.java35
-rw-r--r--src/main/java/org/apache/commons/math3/linear/SingularValueDecomposition.java778
-rw-r--r--src/main/java/org/apache/commons/math3/linear/SparseFieldMatrix.java184
-rw-r--r--src/main/java/org/apache/commons/math3/linear/SparseFieldVector.java771
-rw-r--r--src/main/java/org/apache/commons/math3/linear/SparseRealMatrix.java30
-rw-r--r--src/main/java/org/apache/commons/math3/linear/SparseRealVector.java29
-rw-r--r--src/main/java/org/apache/commons/math3/linear/SymmLQ.java1182
-rw-r--r--src/main/java/org/apache/commons/math3/linear/TriDiagonalTransformer.java274
-rw-r--r--src/main/java/org/apache/commons/math3/linear/package-info.java18
-rw-r--r--src/main/java/org/apache/commons/math3/ml/clustering/CentroidCluster.java53
-rw-r--r--src/main/java/org/apache/commons/math3/ml/clustering/Cluster.java60
-rw-r--r--src/main/java/org/apache/commons/math3/ml/clustering/Clusterable.java32
-rw-r--r--src/main/java/org/apache/commons/math3/ml/clustering/Clusterer.java80
-rw-r--r--src/main/java/org/apache/commons/math3/ml/clustering/DBSCANClusterer.java233
-rw-r--r--src/main/java/org/apache/commons/math3/ml/clustering/DoublePoint.java86
-rw-r--r--src/main/java/org/apache/commons/math3/ml/clustering/FuzzyKMeansClusterer.java426
-rw-r--r--src/main/java/org/apache/commons/math3/ml/clustering/KMeansPlusPlusClusterer.java565
-rw-r--r--src/main/java/org/apache/commons/math3/ml/clustering/MultiKMeansPlusPlusClusterer.java135
-rw-r--r--src/main/java/org/apache/commons/math3/ml/clustering/evaluation/ClusterEvaluator.java122
-rw-r--r--src/main/java/org/apache/commons/math3/ml/clustering/evaluation/SumOfClusterVariances.java69
-rw-r--r--src/main/java/org/apache/commons/math3/ml/clustering/evaluation/package-info.java20
-rw-r--r--src/main/java/org/apache/commons/math3/ml/clustering/package-info.java20
-rw-r--r--src/main/java/org/apache/commons/math3/ml/distance/CanberraDistance.java46
-rw-r--r--src/main/java/org/apache/commons/math3/ml/distance/ChebyshevDistance.java38
-rw-r--r--src/main/java/org/apache/commons/math3/ml/distance/DistanceMeasure.java41
-rw-r--r--src/main/java/org/apache/commons/math3/ml/distance/EarthMoversDistance.java48
-rw-r--r--src/main/java/org/apache/commons/math3/ml/distance/EuclideanDistance.java38
-rw-r--r--src/main/java/org/apache/commons/math3/ml/distance/ManhattanDistance.java38
-rw-r--r--src/main/java/org/apache/commons/math3/ml/distance/package-info.java20
-rw-r--r--src/main/java/org/apache/commons/math3/ml/neuralnet/FeatureInitializer.java32
-rw-r--r--src/main/java/org/apache/commons/math3/ml/neuralnet/FeatureInitializerFactory.java114
-rw-r--r--src/main/java/org/apache/commons/math3/ml/neuralnet/MapUtils.java326
-rw-r--r--src/main/java/org/apache/commons/math3/ml/neuralnet/Network.java499
-rw-r--r--src/main/java/org/apache/commons/math3/ml/neuralnet/Neuron.java272
-rw-r--r--src/main/java/org/apache/commons/math3/ml/neuralnet/SquareNeighbourhood.java38
-rw-r--r--src/main/java/org/apache/commons/math3/ml/neuralnet/UpdateAction.java34
-rw-r--r--src/main/java/org/apache/commons/math3/ml/neuralnet/oned/NeuronString.java238
-rw-r--r--src/main/java/org/apache/commons/math3/ml/neuralnet/oned/package-info.java22
-rw-r--r--src/main/java/org/apache/commons/math3/ml/neuralnet/package-info.java22
-rw-r--r--src/main/java/org/apache/commons/math3/ml/neuralnet/sofm/KohonenTrainingTask.java59
-rw-r--r--src/main/java/org/apache/commons/math3/ml/neuralnet/sofm/KohonenUpdateAction.java225
-rw-r--r--src/main/java/org/apache/commons/math3/ml/neuralnet/sofm/LearningFactorFunction.java34
-rw-r--r--src/main/java/org/apache/commons/math3/ml/neuralnet/sofm/LearningFactorFunctionFactory.java117
-rw-r--r--src/main/java/org/apache/commons/math3/ml/neuralnet/sofm/NeighbourhoodSizeFunction.java37
-rw-r--r--src/main/java/org/apache/commons/math3/ml/neuralnet/sofm/NeighbourhoodSizeFunctionFactory.java107
-rw-r--r--src/main/java/org/apache/commons/math3/ml/neuralnet/sofm/package-info.java22
-rw-r--r--src/main/java/org/apache/commons/math3/ml/neuralnet/sofm/util/ExponentialDecayFunction.java83
-rw-r--r--src/main/java/org/apache/commons/math3/ml/neuralnet/sofm/util/QuasiSigmoidDecayFunction.java87
-rw-r--r--src/main/java/org/apache/commons/math3/ml/neuralnet/sofm/util/package-info.java22
-rw-r--r--src/main/java/org/apache/commons/math3/ml/neuralnet/twod/NeuronSquareMesh2D.java628
-rw-r--r--src/main/java/org/apache/commons/math3/ml/neuralnet/twod/package-info.java22
-rw-r--r--src/main/java/org/apache/commons/math3/ml/neuralnet/twod/util/HitHistogram.java83
-rw-r--r--src/main/java/org/apache/commons/math3/ml/neuralnet/twod/util/LocationFinder.java105
-rw-r--r--src/main/java/org/apache/commons/math3/ml/neuralnet/twod/util/MapDataVisualization.java38
-rw-r--r--src/main/java/org/apache/commons/math3/ml/neuralnet/twod/util/MapVisualization.java34
-rw-r--r--src/main/java/org/apache/commons/math3/ml/neuralnet/twod/util/QuantizationError.java76
-rw-r--r--src/main/java/org/apache/commons/math3/ml/neuralnet/twod/util/SmoothedDataHistogram.java97
-rw-r--r--src/main/java/org/apache/commons/math3/ml/neuralnet/twod/util/TopographicErrorHistogram.java91
-rw-r--r--src/main/java/org/apache/commons/math3/ml/neuralnet/twod/util/UnifiedDistanceMatrix.java209
-rw-r--r--src/main/java/org/apache/commons/math3/ml/neuralnet/twod/util/package-info.java22
-rw-r--r--src/main/java/org/apache/commons/math3/ml/package-info.java18
-rw-r--r--src/main/java/org/apache/commons/math3/ode/AbstractFieldIntegrator.java510
-rw-r--r--src/main/java/org/apache/commons/math3/ode/AbstractIntegrator.java509
-rw-r--r--src/main/java/org/apache/commons/math3/ode/AbstractParameterizable.java81
-rw-r--r--src/main/java/org/apache/commons/math3/ode/ContinuousOutputFieldModel.java360
-rw-r--r--src/main/java/org/apache/commons/math3/ode/ContinuousOutputModel.java435
-rw-r--r--src/main/java/org/apache/commons/math3/ode/EquationsMapper.java106
-rw-r--r--src/main/java/org/apache/commons/math3/ode/ExpandableStatefulODE.java349
-rw-r--r--src/main/java/org/apache/commons/math3/ode/FieldEquationsMapper.java220
-rw-r--r--src/main/java/org/apache/commons/math3/ode/FieldExpandableODE.java148
-rw-r--r--src/main/java/org/apache/commons/math3/ode/FieldODEState.java155
-rw-r--r--src/main/java/org/apache/commons/math3/ode/FieldODEStateAndDerivative.java91
-rw-r--r--src/main/java/org/apache/commons/math3/ode/FieldSecondaryEquations.java77
-rw-r--r--src/main/java/org/apache/commons/math3/ode/FirstOrderConverter.java109
-rw-r--r--src/main/java/org/apache/commons/math3/ode/FirstOrderDifferentialEquations.java63
-rw-r--r--src/main/java/org/apache/commons/math3/ode/FirstOrderFieldDifferentialEquations.java70
-rw-r--r--src/main/java/org/apache/commons/math3/ode/FirstOrderFieldIntegrator.java217
-rw-r--r--src/main/java/org/apache/commons/math3/ode/FirstOrderIntegrator.java69
-rw-r--r--src/main/java/org/apache/commons/math3/ode/JacobianMatrices.java510
-rw-r--r--src/main/java/org/apache/commons/math3/ode/MainStateJacobianProvider.java45
-rw-r--r--src/main/java/org/apache/commons/math3/ode/MultistepFieldIntegrator.java482
-rw-r--r--src/main/java/org/apache/commons/math3/ode/MultistepIntegrator.java496
-rw-r--r--src/main/java/org/apache/commons/math3/ode/ODEIntegrator.java185
-rw-r--r--src/main/java/org/apache/commons/math3/ode/ParameterConfiguration.java75
-rw-r--r--src/main/java/org/apache/commons/math3/ode/ParameterJacobianProvider.java50
-rw-r--r--src/main/java/org/apache/commons/math3/ode/ParameterJacobianWrapper.java103
-rw-r--r--src/main/java/org/apache/commons/math3/ode/Parameterizable.java46
-rw-r--r--src/main/java/org/apache/commons/math3/ode/ParameterizedODE.java44
-rw-r--r--src/main/java/org/apache/commons/math3/ode/ParameterizedWrapper.java88
-rw-r--r--src/main/java/org/apache/commons/math3/ode/SecondOrderDifferentialEquations.java59
-rw-r--r--src/main/java/org/apache/commons/math3/ode/SecondOrderIntegrator.java61
-rw-r--r--src/main/java/org/apache/commons/math3/ode/SecondaryEquations.java66
-rw-r--r--src/main/java/org/apache/commons/math3/ode/UnknownParameterException.java51
-rw-r--r--src/main/java/org/apache/commons/math3/ode/events/Action.java56
-rw-r--r--src/main/java/org/apache/commons/math3/ode/events/EventFilter.java204
-rw-r--r--src/main/java/org/apache/commons/math3/ode/events/EventHandler.java219
-rw-r--r--src/main/java/org/apache/commons/math3/ode/events/EventState.java431
-rw-r--r--src/main/java/org/apache/commons/math3/ode/events/FieldEventHandler.java180
-rw-r--r--src/main/java/org/apache/commons/math3/ode/events/FieldEventState.java344
-rw-r--r--src/main/java/org/apache/commons/math3/ode/events/FilterType.java400
-rw-r--r--src/main/java/org/apache/commons/math3/ode/events/Transformer.java107
-rw-r--r--src/main/java/org/apache/commons/math3/ode/events/package-info.java96
-rw-r--r--src/main/java/org/apache/commons/math3/ode/nonstiff/AdamsBashforthFieldIntegrator.java354
-rw-r--r--src/main/java/org/apache/commons/math3/ode/nonstiff/AdamsBashforthIntegrator.java362
-rw-r--r--src/main/java/org/apache/commons/math3/ode/nonstiff/AdamsFieldIntegrator.java145
-rw-r--r--src/main/java/org/apache/commons/math3/ode/nonstiff/AdamsFieldStepInterpolator.java189
-rw-r--r--src/main/java/org/apache/commons/math3/ode/nonstiff/AdamsIntegrator.java136
-rw-r--r--src/main/java/org/apache/commons/math3/ode/nonstiff/AdamsMoultonFieldIntegrator.java416
-rw-r--r--src/main/java/org/apache/commons/math3/ode/nonstiff/AdamsMoultonIntegrator.java421
-rw-r--r--src/main/java/org/apache/commons/math3/ode/nonstiff/AdamsNordsieckFieldTransformer.java363
-rw-r--r--src/main/java/org/apache/commons/math3/ode/nonstiff/AdamsNordsieckTransformer.java361
-rw-r--r--src/main/java/org/apache/commons/math3/ode/nonstiff/AdaptiveStepsizeFieldIntegrator.java366
-rw-r--r--src/main/java/org/apache/commons/math3/ode/nonstiff/AdaptiveStepsizeIntegrator.java375
-rw-r--r--src/main/java/org/apache/commons/math3/ode/nonstiff/ClassicalRungeKuttaFieldIntegrator.java111
-rw-r--r--src/main/java/org/apache/commons/math3/ode/nonstiff/ClassicalRungeKuttaFieldStepInterpolator.java136
-rw-r--r--src/main/java/org/apache/commons/math3/ode/nonstiff/ClassicalRungeKuttaIntegrator.java75
-rw-r--r--src/main/java/org/apache/commons/math3/ode/nonstiff/ClassicalRungeKuttaStepInterpolator.java136
-rw-r--r--src/main/java/org/apache/commons/math3/ode/nonstiff/DormandPrince54FieldIntegrator.java232
-rw-r--r--src/main/java/org/apache/commons/math3/ode/nonstiff/DormandPrince54FieldStepInterpolator.java249
-rw-r--r--src/main/java/org/apache/commons/math3/ode/nonstiff/DormandPrince54Integrator.java161
-rw-r--r--src/main/java/org/apache/commons/math3/ode/nonstiff/DormandPrince54StepInterpolator.java225
-rw-r--r--src/main/java/org/apache/commons/math3/ode/nonstiff/DormandPrince853FieldIntegrator.java454
-rw-r--r--src/main/java/org/apache/commons/math3/ode/nonstiff/DormandPrince853FieldStepInterpolator.java302
-rw-r--r--src/main/java/org/apache/commons/math3/ode/nonstiff/DormandPrince853Integrator.java286
-rw-r--r--src/main/java/org/apache/commons/math3/ode/nonstiff/DormandPrince853StepInterpolator.java501
-rw-r--r--src/main/java/org/apache/commons/math3/ode/nonstiff/EmbeddedRungeKuttaFieldIntegrator.java385
-rw-r--r--src/main/java/org/apache/commons/math3/ode/nonstiff/EmbeddedRungeKuttaIntegrator.java380
-rw-r--r--src/main/java/org/apache/commons/math3/ode/nonstiff/EulerFieldIntegrator.java96
-rw-r--r--src/main/java/org/apache/commons/math3/ode/nonstiff/EulerFieldStepInterpolator.java108
-rw-r--r--src/main/java/org/apache/commons/math3/ode/nonstiff/EulerIntegrator.java72
-rw-r--r--src/main/java/org/apache/commons/math3/ode/nonstiff/EulerStepInterpolator.java102
-rw-r--r--src/main/java/org/apache/commons/math3/ode/nonstiff/FieldButcherArrayProvider.java46
-rw-r--r--src/main/java/org/apache/commons/math3/ode/nonstiff/GillFieldIntegrator.java121
-rw-r--r--src/main/java/org/apache/commons/math3/ode/nonstiff/GillFieldStepInterpolator.java148
-rw-r--r--src/main/java/org/apache/commons/math3/ode/nonstiff/GillIntegrator.java74
-rw-r--r--src/main/java/org/apache/commons/math3/ode/nonstiff/GillStepInterpolator.java151
-rw-r--r--src/main/java/org/apache/commons/math3/ode/nonstiff/GraggBulirschStoerIntegrator.java949
-rw-r--r--src/main/java/org/apache/commons/math3/ode/nonstiff/GraggBulirschStoerStepInterpolator.java407
-rw-r--r--src/main/java/org/apache/commons/math3/ode/nonstiff/HighamHall54FieldIntegrator.java205
-rw-r--r--src/main/java/org/apache/commons/math3/ode/nonstiff/HighamHall54FieldStepInterpolator.java116
-rw-r--r--src/main/java/org/apache/commons/math3/ode/nonstiff/HighamHall54Integrator.java135
-rw-r--r--src/main/java/org/apache/commons/math3/ode/nonstiff/HighamHall54StepInterpolator.java122
-rw-r--r--src/main/java/org/apache/commons/math3/ode/nonstiff/LutherFieldIntegrator.java146
-rw-r--r--src/main/java/org/apache/commons/math3/ode/nonstiff/LutherFieldStepInterpolator.java224
-rw-r--r--src/main/java/org/apache/commons/math3/ode/nonstiff/LutherIntegrator.java89
-rw-r--r--src/main/java/org/apache/commons/math3/ode/nonstiff/LutherStepInterpolator.java182
-rw-r--r--src/main/java/org/apache/commons/math3/ode/nonstiff/MidpointFieldIntegrator.java96
-rw-r--r--src/main/java/org/apache/commons/math3/ode/nonstiff/MidpointFieldStepInterpolator.java118
-rw-r--r--src/main/java/org/apache/commons/math3/ode/nonstiff/MidpointIntegrator.java69
-rw-r--r--src/main/java/org/apache/commons/math3/ode/nonstiff/MidpointStepInterpolator.java116
-rw-r--r--src/main/java/org/apache/commons/math3/ode/nonstiff/RungeKuttaFieldIntegrator.java273
-rw-r--r--src/main/java/org/apache/commons/math3/ode/nonstiff/RungeKuttaFieldStepInterpolator.java143
-rw-r--r--src/main/java/org/apache/commons/math3/ode/nonstiff/RungeKuttaIntegrator.java269
-rw-r--r--src/main/java/org/apache/commons/math3/ode/nonstiff/RungeKuttaStepInterpolator.java211
-rw-r--r--src/main/java/org/apache/commons/math3/ode/nonstiff/ThreeEighthesFieldIntegrator.java110
-rw-r--r--src/main/java/org/apache/commons/math3/ode/nonstiff/ThreeEighthesFieldStepInterpolator.java139
-rw-r--r--src/main/java/org/apache/commons/math3/ode/nonstiff/ThreeEighthesIntegrator.java72
-rw-r--r--src/main/java/org/apache/commons/math3/ode/nonstiff/ThreeEighthesStepInterpolator.java146
-rw-r--r--src/main/java/org/apache/commons/math3/ode/nonstiff/package-info.java25
-rw-r--r--src/main/java/org/apache/commons/math3/ode/package-info.java120
-rw-r--r--src/main/java/org/apache/commons/math3/ode/sampling/AbstractFieldStepInterpolator.java171
-rw-r--r--src/main/java/org/apache/commons/math3/ode/sampling/AbstractStepInterpolator.java605
-rw-r--r--src/main/java/org/apache/commons/math3/ode/sampling/DummyStepHandler.java89
-rw-r--r--src/main/java/org/apache/commons/math3/ode/sampling/FieldFixedStepHandler.java69
-rw-r--r--src/main/java/org/apache/commons/math3/ode/sampling/FieldStepHandler.java75
-rw-r--r--src/main/java/org/apache/commons/math3/ode/sampling/FieldStepInterpolator.java76
-rw-r--r--src/main/java/org/apache/commons/math3/ode/sampling/FieldStepNormalizer.java273
-rw-r--r--src/main/java/org/apache/commons/math3/ode/sampling/FixedStepHandler.java71
-rw-r--r--src/main/java/org/apache/commons/math3/ode/sampling/NordsieckStepInterpolator.java293
-rw-r--r--src/main/java/org/apache/commons/math3/ode/sampling/StepHandler.java75
-rw-r--r--src/main/java/org/apache/commons/math3/ode/sampling/StepInterpolator.java181
-rw-r--r--src/main/java/org/apache/commons/math3/ode/sampling/StepNormalizer.java300
-rw-r--r--src/main/java/org/apache/commons/math3/ode/sampling/StepNormalizerBounds.java84
-rw-r--r--src/main/java/org/apache/commons/math3/ode/sampling/StepNormalizerMode.java70
-rw-r--r--src/main/java/org/apache/commons/math3/ode/sampling/package-info.java60
-rw-r--r--src/main/java/org/apache/commons/math3/optim/AbstractConvergenceChecker.java60
-rw-r--r--src/main/java/org/apache/commons/math3/optim/AbstractOptimizationProblem.java102
-rw-r--r--src/main/java/org/apache/commons/math3/optim/BaseMultiStartMultivariateOptimizer.java221
-rw-r--r--src/main/java/org/apache/commons/math3/optim/BaseMultivariateOptimizer.java152
-rw-r--r--src/main/java/org/apache/commons/math3/optim/BaseOptimizer.java233
-rw-r--r--src/main/java/org/apache/commons/math3/optim/ConvergenceChecker.java48
-rw-r--r--src/main/java/org/apache/commons/math3/optim/InitialGuess.java45
-rw-r--r--src/main/java/org/apache/commons/math3/optim/MaxEval.java60
-rw-r--r--src/main/java/org/apache/commons/math3/optim/MaxIter.java60
-rw-r--r--src/main/java/org/apache/commons/math3/optim/OptimizationData.java26
-rw-r--r--src/main/java/org/apache/commons/math3/optim/OptimizationProblem.java52
-rw-r--r--src/main/java/org/apache/commons/math3/optim/PointValuePair.java116
-rw-r--r--src/main/java/org/apache/commons/math3/optim/PointVectorValuePair.java139
-rw-r--r--src/main/java/org/apache/commons/math3/optim/SimpleBounds.java77
-rw-r--r--src/main/java/org/apache/commons/math3/optim/SimplePointChecker.java117
-rw-r--r--src/main/java/org/apache/commons/math3/optim/SimpleValueChecker.java113
-rw-r--r--src/main/java/org/apache/commons/math3/optim/SimpleVectorValueChecker.java125
-rw-r--r--src/main/java/org/apache/commons/math3/optim/linear/LinearConstraint.java230
-rw-r--r--src/main/java/org/apache/commons/math3/optim/linear/LinearConstraintSet.java63
-rw-r--r--src/main/java/org/apache/commons/math3/optim/linear/LinearObjectiveFunction.java150
-rw-r--r--src/main/java/org/apache/commons/math3/optim/linear/LinearOptimizer.java131
-rw-r--r--src/main/java/org/apache/commons/math3/optim/linear/NoFeasibleSolutionException.java37
-rw-r--r--src/main/java/org/apache/commons/math3/optim/linear/NonNegativeConstraint.java47
-rw-r--r--src/main/java/org/apache/commons/math3/optim/linear/PivotSelectionRule.java38
-rw-r--r--src/main/java/org/apache/commons/math3/optim/linear/Relationship.java65
-rw-r--r--src/main/java/org/apache/commons/math3/optim/linear/SimplexSolver.java407
-rw-r--r--src/main/java/org/apache/commons/math3/optim/linear/SimplexTableau.java713
-rw-r--r--src/main/java/org/apache/commons/math3/optim/linear/SolutionCallback.java62
-rw-r--r--src/main/java/org/apache/commons/math3/optim/linear/UnboundedSolutionException.java37
-rw-r--r--src/main/java/org/apache/commons/math3/optim/linear/package-info.java21
-rw-r--r--src/main/java/org/apache/commons/math3/optim/nonlinear/scalar/GoalType.java32
-rw-r--r--src/main/java/org/apache/commons/math3/optim/nonlinear/scalar/GradientMultivariateOptimizer.java102
-rw-r--r--src/main/java/org/apache/commons/math3/optim/nonlinear/scalar/LeastSquaresConverter.java186
-rw-r--r--src/main/java/org/apache/commons/math3/optim/nonlinear/scalar/LineSearch.java138
-rw-r--r--src/main/java/org/apache/commons/math3/optim/nonlinear/scalar/MultiStartMultivariateOptimizer.java111
-rw-r--r--src/main/java/org/apache/commons/math3/optim/nonlinear/scalar/MultivariateFunctionMappingAdapter.java294
-rw-r--r--src/main/java/org/apache/commons/math3/optim/nonlinear/scalar/MultivariateFunctionPenaltyAdapter.java186
-rw-r--r--src/main/java/org/apache/commons/math3/optim/nonlinear/scalar/MultivariateOptimizer.java117
-rw-r--r--src/main/java/org/apache/commons/math3/optim/nonlinear/scalar/ObjectiveFunction.java46
-rw-r--r--src/main/java/org/apache/commons/math3/optim/nonlinear/scalar/ObjectiveFunctionGradient.java46
-rw-r--r--src/main/java/org/apache/commons/math3/optim/nonlinear/scalar/gradient/NonLinearConjugateGradientOptimizer.java415
-rw-r--r--src/main/java/org/apache/commons/math3/optim/nonlinear/scalar/gradient/Preconditioner.java44
-rw-r--r--src/main/java/org/apache/commons/math3/optim/nonlinear/scalar/gradient/package-info.java21
-rw-r--r--src/main/java/org/apache/commons/math3/optim/nonlinear/scalar/noderiv/AbstractSimplex.java345
-rw-r--r--src/main/java/org/apache/commons/math3/optim/nonlinear/scalar/noderiv/BOBYQAOptimizer.java2475
-rw-r--r--src/main/java/org/apache/commons/math3/optim/nonlinear/scalar/noderiv/CMAESOptimizer.java1354
-rw-r--r--src/main/java/org/apache/commons/math3/optim/nonlinear/scalar/noderiv/MultiDirectionalSimplex.java215
-rw-r--r--src/main/java/org/apache/commons/math3/optim/nonlinear/scalar/noderiv/NelderMeadSimplex.java280
-rw-r--r--src/main/java/org/apache/commons/math3/optim/nonlinear/scalar/noderiv/PowellOptimizer.java299
-rw-r--r--src/main/java/org/apache/commons/math3/optim/nonlinear/scalar/noderiv/SimplexOptimizer.java222
-rw-r--r--src/main/java/org/apache/commons/math3/optim/nonlinear/scalar/noderiv/package-info.java21
-rw-r--r--src/main/java/org/apache/commons/math3/optim/nonlinear/scalar/package-info.java21
-rw-r--r--src/main/java/org/apache/commons/math3/optim/nonlinear/vector/JacobianMultivariateVectorOptimizer.java116
-rw-r--r--src/main/java/org/apache/commons/math3/optim/nonlinear/vector/ModelFunction.java51
-rw-r--r--src/main/java/org/apache/commons/math3/optim/nonlinear/vector/ModelFunctionJacobian.java51
-rw-r--r--src/main/java/org/apache/commons/math3/optim/nonlinear/vector/MultiStartMultivariateVectorOptimizer.java124
-rw-r--r--src/main/java/org/apache/commons/math3/optim/nonlinear/vector/MultivariateVectorOptimizer.java167
-rw-r--r--src/main/java/org/apache/commons/math3/optim/nonlinear/vector/Target.java54
-rw-r--r--src/main/java/org/apache/commons/math3/optim/nonlinear/vector/Weight.java71
-rw-r--r--src/main/java/org/apache/commons/math3/optim/nonlinear/vector/jacobian/AbstractLeastSquaresOptimizer.java281
-rw-r--r--src/main/java/org/apache/commons/math3/optim/nonlinear/vector/jacobian/GaussNewtonOptimizer.java183
-rw-r--r--src/main/java/org/apache/commons/math3/optim/nonlinear/vector/jacobian/LevenbergMarquardtOptimizer.java961
-rw-r--r--src/main/java/org/apache/commons/math3/optim/nonlinear/vector/jacobian/package-info.java26
-rw-r--r--src/main/java/org/apache/commons/math3/optim/nonlinear/vector/package-info.java26
-rw-r--r--src/main/java/org/apache/commons/math3/optim/package-info.java60
-rw-r--r--src/main/java/org/apache/commons/math3/optim/univariate/BracketFinder.java290
-rw-r--r--src/main/java/org/apache/commons/math3/optim/univariate/BrentOptimizer.java314
-rw-r--r--src/main/java/org/apache/commons/math3/optim/univariate/MultiStartUnivariateOptimizer.java228
-rw-r--r--src/main/java/org/apache/commons/math3/optim/univariate/SearchInterval.java95
-rw-r--r--src/main/java/org/apache/commons/math3/optim/univariate/SimpleUnivariateValueChecker.java127
-rw-r--r--src/main/java/org/apache/commons/math3/optim/univariate/UnivariateObjectiveFunction.java46
-rw-r--r--src/main/java/org/apache/commons/math3/optim/univariate/UnivariateOptimizer.java151
-rw-r--r--src/main/java/org/apache/commons/math3/optim/univariate/UnivariatePointValuePair.java66
-rw-r--r--src/main/java/org/apache/commons/math3/optim/univariate/package-info.java21
-rw-r--r--src/main/java/org/apache/commons/math3/optimization/AbstractConvergenceChecker.java95
-rw-r--r--src/main/java/org/apache/commons/math3/optimization/BaseMultivariateMultiStartOptimizer.java192
-rw-r--r--src/main/java/org/apache/commons/math3/optimization/BaseMultivariateOptimizer.java60
-rw-r--r--src/main/java/org/apache/commons/math3/optimization/BaseMultivariateSimpleBoundsOptimizer.java67
-rw-r--r--src/main/java/org/apache/commons/math3/optimization/BaseMultivariateVectorMultiStartOptimizer.java207
-rw-r--r--src/main/java/org/apache/commons/math3/optimization/BaseMultivariateVectorOptimizer.java62
-rw-r--r--src/main/java/org/apache/commons/math3/optimization/BaseOptimizer.java59
-rw-r--r--src/main/java/org/apache/commons/math3/optimization/ConvergenceChecker.java50
-rw-r--r--src/main/java/org/apache/commons/math3/optimization/DifferentiableMultivariateMultiStartOptimizer.java51
-rw-r--r--src/main/java/org/apache/commons/math3/optimization/DifferentiableMultivariateOptimizer.java34
-rw-r--r--src/main/java/org/apache/commons/math3/optimization/DifferentiableMultivariateVectorMultiStartOptimizer.java51
-rw-r--r--src/main/java/org/apache/commons/math3/optimization/DifferentiableMultivariateVectorOptimizer.java31
-rw-r--r--src/main/java/org/apache/commons/math3/optimization/GoalType.java36
-rw-r--r--src/main/java/org/apache/commons/math3/optimization/InitialGuess.java47
-rw-r--r--src/main/java/org/apache/commons/math3/optimization/LeastSquaresConverter.java181
-rw-r--r--src/main/java/org/apache/commons/math3/optimization/MultivariateDifferentiableMultiStartOptimizer.java51
-rw-r--r--src/main/java/org/apache/commons/math3/optimization/MultivariateDifferentiableOptimizer.java34
-rw-r--r--src/main/java/org/apache/commons/math3/optimization/MultivariateDifferentiableVectorMultiStartOptimizer.java51
-rw-r--r--src/main/java/org/apache/commons/math3/optimization/MultivariateDifferentiableVectorOptimizer.java31
-rw-r--r--src/main/java/org/apache/commons/math3/optimization/MultivariateMultiStartOptimizer.java51
-rw-r--r--src/main/java/org/apache/commons/math3/optimization/MultivariateOptimizer.java35
-rw-r--r--src/main/java/org/apache/commons/math3/optimization/OptimizationData.java28
-rw-r--r--src/main/java/org/apache/commons/math3/optimization/PointValuePair.java120
-rw-r--r--src/main/java/org/apache/commons/math3/optimization/PointVectorValuePair.java143
-rw-r--r--src/main/java/org/apache/commons/math3/optimization/SimpleBounds.java62
-rw-r--r--src/main/java/org/apache/commons/math3/optimization/SimplePointChecker.java130
-rw-r--r--src/main/java/org/apache/commons/math3/optimization/SimpleValueChecker.java125
-rw-r--r--src/main/java/org/apache/commons/math3/optimization/SimpleVectorValueChecker.java137
-rw-r--r--src/main/java/org/apache/commons/math3/optimization/Target.java48
-rw-r--r--src/main/java/org/apache/commons/math3/optimization/Weight.java66
-rw-r--r--src/main/java/org/apache/commons/math3/optimization/direct/AbstractSimplex.java347
-rw-r--r--src/main/java/org/apache/commons/math3/optimization/direct/BOBYQAOptimizer.java2480
-rw-r--r--src/main/java/org/apache/commons/math3/optimization/direct/BaseAbstractMultivariateOptimizer.java318
-rw-r--r--src/main/java/org/apache/commons/math3/optimization/direct/BaseAbstractMultivariateSimpleBoundsOptimizer.java82
-rw-r--r--src/main/java/org/apache/commons/math3/optimization/direct/BaseAbstractMultivariateVectorOptimizer.java370
-rw-r--r--src/main/java/org/apache/commons/math3/optimization/direct/CMAESOptimizer.java1441
-rw-r--r--src/main/java/org/apache/commons/math3/optimization/direct/MultiDirectionalSimplex.java218
-rw-r--r--src/main/java/org/apache/commons/math3/optimization/direct/MultivariateFunctionMappingAdapter.java301
-rw-r--r--src/main/java/org/apache/commons/math3/optimization/direct/MultivariateFunctionPenaltyAdapter.java190
-rw-r--r--src/main/java/org/apache/commons/math3/optimization/direct/NelderMeadSimplex.java283
-rw-r--r--src/main/java/org/apache/commons/math3/optimization/direct/PowellOptimizer.java353
-rw-r--r--src/main/java/org/apache/commons/math3/optimization/direct/SimplexOptimizer.java235
-rw-r--r--src/main/java/org/apache/commons/math3/optimization/direct/package-info.java24
-rw-r--r--src/main/java/org/apache/commons/math3/optimization/fitting/CurveFitter.java299
-rw-r--r--src/main/java/org/apache/commons/math3/optimization/fitting/GaussianFitter.java371
-rw-r--r--src/main/java/org/apache/commons/math3/optimization/fitting/HarmonicFitter.java384
-rw-r--r--src/main/java/org/apache/commons/math3/optimization/fitting/PolynomialFitter.java111
-rw-r--r--src/main/java/org/apache/commons/math3/optimization/fitting/WeightedObservedPoint.java76
-rw-r--r--src/main/java/org/apache/commons/math3/optimization/fitting/package-info.java30
-rw-r--r--src/main/java/org/apache/commons/math3/optimization/general/AbstractDifferentiableOptimizer.java90
-rw-r--r--src/main/java/org/apache/commons/math3/optimization/general/AbstractLeastSquaresOptimizer.java577
-rw-r--r--src/main/java/org/apache/commons/math3/optimization/general/AbstractScalarDifferentiableOptimizer.java114
-rw-r--r--src/main/java/org/apache/commons/math3/optimization/general/ConjugateGradientFormula.java50
-rw-r--r--src/main/java/org/apache/commons/math3/optimization/general/GaussNewtonOptimizer.java194
-rw-r--r--src/main/java/org/apache/commons/math3/optimization/general/LevenbergMarquardtOptimizer.java943
-rw-r--r--src/main/java/org/apache/commons/math3/optimization/general/NonLinearConjugateGradientOptimizer.java311
-rw-r--r--src/main/java/org/apache/commons/math3/optimization/general/Preconditioner.java46
-rw-r--r--src/main/java/org/apache/commons/math3/optimization/general/package-info.java22
-rw-r--r--src/main/java/org/apache/commons/math3/optimization/linear/AbstractLinearOptimizer.java162
-rw-r--r--src/main/java/org/apache/commons/math3/optimization/linear/LinearConstraint.java236
-rw-r--r--src/main/java/org/apache/commons/math3/optimization/linear/LinearObjectiveFunction.java150
-rw-r--r--src/main/java/org/apache/commons/math3/optimization/linear/LinearOptimizer.java92
-rw-r--r--src/main/java/org/apache/commons/math3/optimization/linear/NoFeasibleSolutionException.java42
-rw-r--r--src/main/java/org/apache/commons/math3/optimization/linear/Relationship.java68
-rw-r--r--src/main/java/org/apache/commons/math3/optimization/linear/SimplexSolver.java238
-rw-r--r--src/main/java/org/apache/commons/math3/optimization/linear/SimplexTableau.java637
-rw-r--r--src/main/java/org/apache/commons/math3/optimization/linear/UnboundedSolutionException.java42
-rw-r--r--src/main/java/org/apache/commons/math3/optimization/linear/package-info.java22
-rw-r--r--src/main/java/org/apache/commons/math3/optimization/package-info.java74
-rw-r--r--src/main/java/org/apache/commons/math3/optimization/univariate/BaseAbstractUnivariateOptimizer.java162
-rw-r--r--src/main/java/org/apache/commons/math3/optimization/univariate/BaseUnivariateOptimizer.java86
-rw-r--r--src/main/java/org/apache/commons/math3/optimization/univariate/BracketFinder.java289
-rw-r--r--src/main/java/org/apache/commons/math3/optimization/univariate/BrentOptimizer.java316
-rw-r--r--src/main/java/org/apache/commons/math3/optimization/univariate/SimpleUnivariateValueChecker.java139
-rw-r--r--src/main/java/org/apache/commons/math3/optimization/univariate/UnivariateMultiStartOptimizer.java203
-rw-r--r--src/main/java/org/apache/commons/math3/optimization/univariate/UnivariateOptimizer.java29
-rw-r--r--src/main/java/org/apache/commons/math3/optimization/univariate/UnivariatePointValuePair.java68
-rw-r--r--src/main/java/org/apache/commons/math3/optimization/univariate/package-info.java22
-rw-r--r--src/main/java/org/apache/commons/math3/package-info.java20
-rw-r--r--src/main/java/org/apache/commons/math3/primes/PollardRho.java165
-rw-r--r--src/main/java/org/apache/commons/math3/primes/Primes.java124
-rw-r--r--src/main/java/org/apache/commons/math3/primes/SmallPrimes.java197
-rw-r--r--src/main/java/org/apache/commons/math3/primes/package-info.java18
-rw-r--r--src/main/java/org/apache/commons/math3/random/AbstractRandomGenerator.java250
-rw-r--r--src/main/java/org/apache/commons/math3/random/AbstractWell.java216
-rw-r--r--src/main/java/org/apache/commons/math3/random/BitsStreamGenerator.java249
-rw-r--r--src/main/java/org/apache/commons/math3/random/CorrelatedRandomVectorGenerator.java178
-rw-r--r--src/main/java/org/apache/commons/math3/random/EmpiricalDistribution.java866
-rw-r--r--src/main/java/org/apache/commons/math3/random/GaussianRandomGenerator.java49
-rw-r--r--src/main/java/org/apache/commons/math3/random/HaltonSequenceGenerator.java195
-rw-r--r--src/main/java/org/apache/commons/math3/random/ISAACRandom.java279
-rw-r--r--src/main/java/org/apache/commons/math3/random/JDKRandomGenerator.java55
-rw-r--r--src/main/java/org/apache/commons/math3/random/MersenneTwister.java275
-rw-r--r--src/main/java/org/apache/commons/math3/random/NormalizedRandomGenerator.java38
-rw-r--r--src/main/java/org/apache/commons/math3/random/RandomAdaptor.java182
-rw-r--r--src/main/java/org/apache/commons/math3/random/RandomData.java245
-rw-r--r--src/main/java/org/apache/commons/math3/random/RandomDataGenerator.java780
-rw-r--r--src/main/java/org/apache/commons/math3/random/RandomDataImpl.java544
-rw-r--r--src/main/java/org/apache/commons/math3/random/RandomGenerator.java130
-rw-r--r--src/main/java/org/apache/commons/math3/random/RandomGeneratorFactory.java118
-rw-r--r--src/main/java/org/apache/commons/math3/random/RandomVectorGenerator.java33
-rw-r--r--src/main/java/org/apache/commons/math3/random/SobolSequenceGenerator.java329
-rw-r--r--src/main/java/org/apache/commons/math3/random/StableRandomGenerator.java143
-rw-r--r--src/main/java/org/apache/commons/math3/random/SynchronizedRandomGenerator.java95
-rw-r--r--src/main/java/org/apache/commons/math3/random/UncorrelatedRandomVectorGenerator.java91
-rw-r--r--src/main/java/org/apache/commons/math3/random/UniformRandomGenerator.java58
-rw-r--r--src/main/java/org/apache/commons/math3/random/UnitSphereRandomVectorGenerator.java74
-rw-r--r--src/main/java/org/apache/commons/math3/random/ValueServer.java465
-rw-r--r--src/main/java/org/apache/commons/math3/random/Well1024a.java110
-rw-r--r--src/main/java/org/apache/commons/math3/random/Well19937a.java112
-rw-r--r--src/main/java/org/apache/commons/math3/random/Well19937c.java117
-rw-r--r--src/main/java/org/apache/commons/math3/random/Well44497a.java115
-rw-r--r--src/main/java/org/apache/commons/math3/random/Well44497b.java122
-rw-r--r--src/main/java/org/apache/commons/math3/random/Well512a.java111
-rw-r--r--src/main/java/org/apache/commons/math3/random/package-info.java115
-rw-r--r--src/main/java/org/apache/commons/math3/special/BesselJ.java661
-rw-r--r--src/main/java/org/apache/commons/math3/special/Beta.java478
-rw-r--r--src/main/java/org/apache/commons/math3/special/Erf.java227
-rw-r--r--src/main/java/org/apache/commons/math3/special/Gamma.java692
-rw-r--r--src/main/java/org/apache/commons/math3/special/package-info.java18
-rw-r--r--src/main/java/org/apache/commons/math3/stat/Frequency.java664
-rw-r--r--src/main/java/org/apache/commons/math3/stat/StatUtils.java852
-rw-r--r--src/main/java/org/apache/commons/math3/stat/clustering/Cluster.java76
-rw-r--r--src/main/java/org/apache/commons/math3/stat/clustering/Clusterable.java48
-rw-r--r--src/main/java/org/apache/commons/math3/stat/clustering/DBSCANClusterer.java226
-rw-r--r--src/main/java/org/apache/commons/math3/stat/clustering/EuclideanDoublePoint.java100
-rw-r--r--src/main/java/org/apache/commons/math3/stat/clustering/EuclideanIntegerPoint.java101
-rw-r--r--src/main/java/org/apache/commons/math3/stat/clustering/KMeansPlusPlusClusterer.java514
-rw-r--r--src/main/java/org/apache/commons/math3/stat/clustering/package-info.java29
-rw-r--r--src/main/java/org/apache/commons/math3/stat/correlation/Covariance.java295
-rw-r--r--src/main/java/org/apache/commons/math3/stat/correlation/KendallsCorrelation.java272
-rw-r--r--src/main/java/org/apache/commons/math3/stat/correlation/PearsonsCorrelation.java330
-rw-r--r--src/main/java/org/apache/commons/math3/stat/correlation/SpearmansCorrelation.java262
-rw-r--r--src/main/java/org/apache/commons/math3/stat/correlation/StorelessBivariateCovariance.java138
-rw-r--r--src/main/java/org/apache/commons/math3/stat/correlation/StorelessCovariance.java229
-rw-r--r--src/main/java/org/apache/commons/math3/stat/correlation/package-info.java22
-rw-r--r--src/main/java/org/apache/commons/math3/stat/descriptive/AbstractStorelessUnivariateStatistic.java187
-rw-r--r--src/main/java/org/apache/commons/math3/stat/descriptive/AbstractUnivariateStatistic.java263
-rw-r--r--src/main/java/org/apache/commons/math3/stat/descriptive/AggregateSummaryStatistics.java422
-rw-r--r--src/main/java/org/apache/commons/math3/stat/descriptive/DescriptiveStatistics.java777
-rw-r--r--src/main/java/org/apache/commons/math3/stat/descriptive/MultivariateSummaryStatistics.java635
-rw-r--r--src/main/java/org/apache/commons/math3/stat/descriptive/StatisticalMultivariateSummary.java119
-rw-r--r--src/main/java/org/apache/commons/math3/stat/descriptive/StatisticalSummary.java64
-rw-r--r--src/main/java/org/apache/commons/math3/stat/descriptive/StatisticalSummaryValues.java186
-rw-r--r--src/main/java/org/apache/commons/math3/stat/descriptive/StorelessUnivariateStatistic.java87
-rw-r--r--src/main/java/org/apache/commons/math3/stat/descriptive/SummaryStatistics.java765
-rw-r--r--src/main/java/org/apache/commons/math3/stat/descriptive/SynchronizedDescriptiveStatistics.java192
-rw-r--r--src/main/java/org/apache/commons/math3/stat/descriptive/SynchronizedMultivariateSummaryStatistics.java297
-rw-r--r--src/main/java/org/apache/commons/math3/stat/descriptive/SynchronizedSummaryStatistics.java366
-rw-r--r--src/main/java/org/apache/commons/math3/stat/descriptive/UnivariateStatistic.java55
-rw-r--r--src/main/java/org/apache/commons/math3/stat/descriptive/WeightedEvaluation.java57
-rw-r--r--src/main/java/org/apache/commons/math3/stat/descriptive/moment/FirstMoment.java169
-rw-r--r--src/main/java/org/apache/commons/math3/stat/descriptive/moment/FourthMoment.java151
-rw-r--r--src/main/java/org/apache/commons/math3/stat/descriptive/moment/GeometricMean.java214
-rw-r--r--src/main/java/org/apache/commons/math3/stat/descriptive/moment/Kurtosis.java226
-rw-r--r--src/main/java/org/apache/commons/math3/stat/descriptive/moment/Mean.java286
-rw-r--r--src/main/java/org/apache/commons/math3/stat/descriptive/moment/SecondMoment.java134
-rw-r--r--src/main/java/org/apache/commons/math3/stat/descriptive/moment/SemiVariance.java369
-rw-r--r--src/main/java/org/apache/commons/math3/stat/descriptive/moment/Skewness.java228
-rw-r--r--src/main/java/org/apache/commons/math3/stat/descriptive/moment/StandardDeviation.java280
-rw-r--r--src/main/java/org/apache/commons/math3/stat/descriptive/moment/ThirdMoment.java148
-rw-r--r--src/main/java/org/apache/commons/math3/stat/descriptive/moment/Variance.java627
-rw-r--r--src/main/java/org/apache/commons/math3/stat/descriptive/moment/VectorialCovariance.java157
-rw-r--r--src/main/java/org/apache/commons/math3/stat/descriptive/moment/VectorialMean.java105
-rw-r--r--src/main/java/org/apache/commons/math3/stat/descriptive/moment/package-info.java20
-rw-r--r--src/main/java/org/apache/commons/math3/stat/descriptive/package-info.java44
-rw-r--r--src/main/java/org/apache/commons/math3/stat/descriptive/rank/Max.java171
-rw-r--r--src/main/java/org/apache/commons/math3/stat/descriptive/rank/Median.java97
-rw-r--r--src/main/java/org/apache/commons/math3/stat/descriptive/rank/Min.java171
-rw-r--r--src/main/java/org/apache/commons/math3/stat/descriptive/rank/PSquarePercentile.java997
-rw-r--r--src/main/java/org/apache/commons/math3/stat/descriptive/rank/Percentile.java1072
-rw-r--r--src/main/java/org/apache/commons/math3/stat/descriptive/rank/package-info.java20
-rw-r--r--src/main/java/org/apache/commons/math3/stat/descriptive/summary/Product.java230
-rw-r--r--src/main/java/org/apache/commons/math3/stat/descriptive/summary/Sum.java226
-rw-r--r--src/main/java/org/apache/commons/math3/stat/descriptive/summary/SumOfLogs.java170
-rw-r--r--src/main/java/org/apache/commons/math3/stat/descriptive/summary/SumOfSquares.java159
-rw-r--r--src/main/java/org/apache/commons/math3/stat/descriptive/summary/package-info.java20
-rw-r--r--src/main/java/org/apache/commons/math3/stat/inference/AlternativeHypothesis.java40
-rw-r--r--src/main/java/org/apache/commons/math3/stat/inference/BinomialTest.java160
-rw-r--r--src/main/java/org/apache/commons/math3/stat/inference/ChiSquareTest.java602
-rw-r--r--src/main/java/org/apache/commons/math3/stat/inference/GTest.java538
-rw-r--r--src/main/java/org/apache/commons/math3/stat/inference/KolmogorovSmirnovTest.java1270
-rw-r--r--src/main/java/org/apache/commons/math3/stat/inference/MannWhitneyUTest.java238
-rw-r--r--src/main/java/org/apache/commons/math3/stat/inference/OneWayAnova.java355
-rw-r--r--src/main/java/org/apache/commons/math3/stat/inference/TTest.java1184
-rw-r--r--src/main/java/org/apache/commons/math3/stat/inference/TestUtils.java547
-rw-r--r--src/main/java/org/apache/commons/math3/stat/inference/WilcoxonSignedRankTest.java325
-rw-r--r--src/main/java/org/apache/commons/math3/stat/inference/package-info.java22
-rw-r--r--src/main/java/org/apache/commons/math3/stat/interval/AgrestiCoullInterval.java48
-rw-r--r--src/main/java/org/apache/commons/math3/stat/interval/BinomialConfidenceInterval.java62
-rw-r--r--src/main/java/org/apache/commons/math3/stat/interval/ClopperPearsonInterval.java58
-rw-r--r--src/main/java/org/apache/commons/math3/stat/interval/ConfidenceInterval.java109
-rw-r--r--src/main/java/org/apache/commons/math3/stat/interval/IntervalUtils.java174
-rw-r--r--src/main/java/org/apache/commons/math3/stat/interval/NormalApproximationInterval.java44
-rw-r--r--src/main/java/org/apache/commons/math3/stat/interval/WilsonScoreInterval.java52
-rw-r--r--src/main/java/org/apache/commons/math3/stat/interval/package-info.java22
-rw-r--r--src/main/java/org/apache/commons/math3/stat/package-info.java18
-rw-r--r--src/main/java/org/apache/commons/math3/stat/ranking/NaNStrategy.java55
-rw-r--r--src/main/java/org/apache/commons/math3/stat/ranking/NaturalRanking.java474
-rw-r--r--src/main/java/org/apache/commons/math3/stat/ranking/RankingAlgorithm.java40
-rw-r--r--src/main/java/org/apache/commons/math3/stat/ranking/TiesStrategy.java54
-rw-r--r--src/main/java/org/apache/commons/math3/stat/ranking/package-info.java22
-rw-r--r--src/main/java/org/apache/commons/math3/stat/regression/AbstractMultipleLinearRegression.java383
-rw-r--r--src/main/java/org/apache/commons/math3/stat/regression/GLSMultipleLinearRegression.java135
-rw-r--r--src/main/java/org/apache/commons/math3/stat/regression/MillerUpdatingRegression.java1101
-rw-r--r--src/main/java/org/apache/commons/math3/stat/regression/ModelSpecificationException.java40
-rw-r--r--src/main/java/org/apache/commons/math3/stat/regression/MultipleLinearRegression.java69
-rw-r--r--src/main/java/org/apache/commons/math3/stat/regression/OLSMultipleLinearRegression.java285
-rw-r--r--src/main/java/org/apache/commons/math3/stat/regression/RegressionResults.java421
-rw-r--r--src/main/java/org/apache/commons/math3/stat/regression/SimpleRegression.java881
-rw-r--r--src/main/java/org/apache/commons/math3/stat/regression/UpdatingMultipleLinearRegression.java93
-rw-r--r--src/main/java/org/apache/commons/math3/stat/regression/package-info.java22
-rw-r--r--src/main/java/org/apache/commons/math3/transform/DctNormalization.java62
-rw-r--r--src/main/java/org/apache/commons/math3/transform/DftNormalization.java58
-rw-r--r--src/main/java/org/apache/commons/math3/transform/DstNormalization.java59
-rw-r--r--src/main/java/org/apache/commons/math3/transform/FastCosineTransformer.java177
-rw-r--r--src/main/java/org/apache/commons/math3/transform/FastFourierTransformer.java749
-rw-r--r--src/main/java/org/apache/commons/math3/transform/FastHadamardTransformer.java316
-rw-r--r--src/main/java/org/apache/commons/math3/transform/FastSineTransformer.java175
-rw-r--r--src/main/java/org/apache/commons/math3/transform/RealTransformer.java68
-rw-r--r--src/main/java/org/apache/commons/math3/transform/TransformType.java30
-rw-r--r--src/main/java/org/apache/commons/math3/transform/TransformUtils.java160
-rw-r--r--src/main/java/org/apache/commons/math3/transform/package-info.java18
-rw-r--r--src/main/java/org/apache/commons/math3/util/ArithmeticUtils.java845
-rw-r--r--src/main/java/org/apache/commons/math3/util/BigReal.java362
-rw-r--r--src/main/java/org/apache/commons/math3/util/BigRealField.java87
-rw-r--r--src/main/java/org/apache/commons/math3/util/CentralPivotingStrategy.java45
-rw-r--r--src/main/java/org/apache/commons/math3/util/Combinations.java384
-rw-r--r--src/main/java/org/apache/commons/math3/util/CombinatoricsUtils.java460
-rw-r--r--src/main/java/org/apache/commons/math3/util/CompositeFormat.java215
-rw-r--r--src/main/java/org/apache/commons/math3/util/ContinuedFraction.java181
-rw-r--r--src/main/java/org/apache/commons/math3/util/Decimal64.java806
-rw-r--r--src/main/java/org/apache/commons/math3/util/Decimal64Field.java61
-rw-r--r--src/main/java/org/apache/commons/math3/util/DefaultTransformer.java79
-rw-r--r--src/main/java/org/apache/commons/math3/util/DoubleArray.java96
-rw-r--r--src/main/java/org/apache/commons/math3/util/FastMath.java4508
-rw-r--r--src/main/java/org/apache/commons/math3/util/FastMathCalc.java678
-rw-r--r--src/main/java/org/apache/commons/math3/util/FastMathLiteralArrays.java8228
-rw-r--r--src/main/java/org/apache/commons/math3/util/Incrementor.java218
-rw-r--r--src/main/java/org/apache/commons/math3/util/IntegerSequence.java328
-rw-r--r--src/main/java/org/apache/commons/math3/util/IterationEvent.java52
-rw-r--r--src/main/java/org/apache/commons/math3/util/IterationListener.java52
-rw-r--r--src/main/java/org/apache/commons/math3/util/IterationManager.java191
-rw-r--r--src/main/java/org/apache/commons/math3/util/KthSelector.java154
-rw-r--r--src/main/java/org/apache/commons/math3/util/MathArrays.java1912
-rw-r--r--src/main/java/org/apache/commons/math3/util/MathUtils.java289
-rw-r--r--src/main/java/org/apache/commons/math3/util/MedianOf3PivotingStrategy.java63
-rw-r--r--src/main/java/org/apache/commons/math3/util/MultidimensionalCounter.java283
-rw-r--r--src/main/java/org/apache/commons/math3/util/NumberTransformer.java36
-rw-r--r--src/main/java/org/apache/commons/math3/util/OpenIntToDoubleHashMap.java604
-rw-r--r--src/main/java/org/apache/commons/math3/util/OpenIntToFieldHashMap.java632
-rw-r--r--src/main/java/org/apache/commons/math3/util/Pair.java148
-rw-r--r--src/main/java/org/apache/commons/math3/util/PivotingStrategyInterface.java43
-rw-r--r--src/main/java/org/apache/commons/math3/util/Precision.java613
-rw-r--r--src/main/java/org/apache/commons/math3/util/RandomPivotingStrategy.java58
-rw-r--r--src/main/java/org/apache/commons/math3/util/ResizableDoubleArray.java1130
-rw-r--r--src/main/java/org/apache/commons/math3/util/TransformerMap.java182
-rw-r--r--src/main/java/org/apache/commons/math3/util/package-info.java18
992 files changed, 225936 insertions, 1 deletions
diff --git a/Android.bp b/Android.bp
index ac41b9c..3c2fb11 100644
--- a/Android.bp
+++ b/Android.bp
@@ -50,12 +50,30 @@ license {
],
}
+// NOTE: New users of this library should prefer the
+// commons-math3 library over the commons-math library.
+
java_library {
name: "apache-commons-math",
host_supported: true,
java_version: "1.7",
- srcs: ["src/main/java/**/*.java"],
+ srcs: ["src/main/java/org/apache/commons/math/**/*.java"],
+ sdk_version: "current",
+ errorprone: {
+ javacflags: [
+ "-Xep:MissingOverride:OFF",
+ "-Xep:IdentityBinaryExpression:OFF",
+ "-Xep:BoxedPrimitiveEquality:OFF",
+ ],
+ },
+}
+
+java_library {
+ name: "apache-commons-math3",
+ host_supported: true,
+ java_version: "1.7",
+ srcs: ["src/main/java/org/apache/commons/math3/**/*.java"],
sdk_version: "current",
errorprone: {
javacflags: [
@@ -64,6 +82,13 @@ java_library {
"-Xep:BoxedPrimitiveEquality:OFF",
],
},
+ target: {
+ android: {
+ // Exclude the geometry module from Android variant due to missing dependency
+ // on java.awt.geom.AffineTransform.
+ exclude_srcs: ["src/main/java/org/apache/commons/math3/geometry/**/*.java"]
+ }
+ }
}
java_library_host {
diff --git a/src/main/java/org/apache/commons/math3/Field.java b/src/main/java/org/apache/commons/math3/Field.java
new file mode 100644
index 0000000..618536c
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/Field.java
@@ -0,0 +1,58 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math3;
+
+/**
+ * Interface representing a <a href="http://mathworld.wolfram.com/Field.html">field</a>.
+ * <p>
+ * Classes implementing this interface will often be singletons.
+ * </p>
+ * @param <T> the type of the field elements
+ * @see FieldElement
+ * @since 2.0
+ */
+public interface Field<T> {
+
+ /** Get the additive identity of the field.
+ * <p>
+ * The additive identity is the element e<sub>0</sub> of the field such that
+ * for all elements a of the field, the equalities a + e<sub>0</sub> =
+ * e<sub>0</sub> + a = a hold.
+ * </p>
+ * @return additive identity of the field
+ */
+ T getZero();
+
+ /** Get the multiplicative identity of the field.
+ * <p>
+ * The multiplicative identity is the element e<sub>1</sub> of the field such that
+ * for all elements a of the field, the equalities a &times; e<sub>1</sub> =
+ * e<sub>1</sub> &times; a = a hold.
+ * </p>
+ * @return multiplicative identity of the field
+ */
+ T getOne();
+
+ /**
+ * Returns the runtime class of the FieldElement.
+ *
+ * @return The {@code Class} object that represents the runtime
+ * class of this object.
+ */
+ Class<? extends FieldElement<T>> getRuntimeClass();
+
+}
diff --git a/src/main/java/org/apache/commons/math3/FieldElement.java b/src/main/java/org/apache/commons/math3/FieldElement.java
new file mode 100644
index 0000000..bd286ef
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/FieldElement.java
@@ -0,0 +1,87 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math3;
+
+import org.apache.commons.math3.exception.MathArithmeticException;
+import org.apache.commons.math3.exception.NullArgumentException;
+
+
+/**
+ * Interface representing <a href="http://mathworld.wolfram.com/Field.html">field</a> elements.
+ * @param <T> the type of the field elements
+ * @see Field
+ * @since 2.0
+ */
+public interface FieldElement<T> {
+
+ /** Compute this + a.
+ * @param a element to add
+ * @return a new element representing this + a
+ * @throws NullArgumentException if {@code a} is {@code null}.
+ */
+ T add(T a) throws NullArgumentException;
+
+ /** Compute this - a.
+ * @param a element to subtract
+ * @return a new element representing this - a
+ * @throws NullArgumentException if {@code a} is {@code null}.
+ */
+ T subtract(T a) throws NullArgumentException;
+
+ /**
+ * Returns the additive inverse of {@code this} element.
+ * @return the opposite of {@code this}.
+ */
+ T negate();
+
+ /** Compute n &times; this. Multiplication by an integer number is defined
+ * as the following sum
+ * <center>
+ * n &times; this = &sum;<sub>i=1</sub><sup>n</sup> this.
+ * </center>
+ * @param n Number of times {@code this} must be added to itself.
+ * @return A new element representing n &times; this.
+ */
+ T multiply(int n);
+
+ /** Compute this &times; a.
+ * @param a element to multiply
+ * @return a new element representing this &times; a
+ * @throws NullArgumentException if {@code a} is {@code null}.
+ */
+ T multiply(T a) throws NullArgumentException;
+
+ /** Compute this &divide; a.
+ * @param a element to divide by
+ * @return a new element representing this &divide; a
+ * @throws NullArgumentException if {@code a} is {@code null}.
+ * @throws MathArithmeticException if {@code a} is zero
+ */
+ T divide(T a) throws NullArgumentException, MathArithmeticException;
+
+ /**
+ * Returns the multiplicative inverse of {@code this} element.
+ * @return the inverse of {@code this}.
+ * @throws MathArithmeticException if {@code this} is zero
+ */
+ T reciprocal() throws MathArithmeticException;
+
+ /** Get the {@link Field} to which the instance belongs.
+ * @return {@link Field} to which the instance belongs
+ */
+ Field<T> getField();
+}
diff --git a/src/main/java/org/apache/commons/math3/RealFieldElement.java b/src/main/java/org/apache/commons/math3/RealFieldElement.java
new file mode 100644
index 0000000..e3656d4
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/RealFieldElement.java
@@ -0,0 +1,402 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math3;
+
+import org.apache.commons.math3.exception.DimensionMismatchException;
+
+/**
+ * Interface representing a <a href="http://mathworld.wolfram.com/RealNumber.html">real</a>
+ * <a href="http://mathworld.wolfram.com/Field.html">field</a>.
+ * @param <T> the type of the field elements
+ * @see FieldElement
+ * @since 3.2
+ */
+public interface RealFieldElement<T> extends FieldElement<T> {
+
+ /** Get the real value of the number.
+ * @return real value
+ */
+ double getReal();
+
+ /** '+' operator.
+ * @param a right hand side parameter of the operator
+ * @return this+a
+ */
+ T add(double a);
+
+ /** '-' operator.
+ * @param a right hand side parameter of the operator
+ * @return this-a
+ */
+ T subtract(double a);
+
+ /** '&times;' operator.
+ * @param a right hand side parameter of the operator
+ * @return this&times;a
+ */
+ T multiply(double a);
+
+ /** '&divide;' operator.
+ * @param a right hand side parameter of the operator
+ * @return this&divide;a
+ */
+ T divide(double a);
+
+ /** IEEE remainder operator.
+ * @param a right hand side parameter of the operator
+ * @return this - n &times; a where n is the closest integer to this/a
+ * (the even integer is chosen for n if this/a is halfway between two integers)
+ */
+ T remainder(double a);
+
+ /** IEEE remainder operator.
+ * @param a right hand side parameter of the operator
+ * @return this - n &times; a where n is the closest integer to this/a
+ * (the even integer is chosen for n if this/a is halfway between two integers)
+ * @exception DimensionMismatchException if number of free parameters or orders are inconsistent
+ */
+ T remainder(T a)
+ throws DimensionMismatchException;
+
+ /** absolute value.
+ * @return abs(this)
+ */
+ T abs();
+
+ /** Get the smallest whole number larger than instance.
+ * @return ceil(this)
+ */
+ T ceil();
+
+ /** Get the largest whole number smaller than instance.
+ * @return floor(this)
+ */
+ T floor();
+
+ /** Get the whole number that is the nearest to the instance, or the even one if x is exactly half way between two integers.
+ * @return a double number r such that r is an integer r - 0.5 &le; this &le; r + 0.5
+ */
+ T rint();
+
+ /** Get the closest long to instance value.
+ * @return closest long to {@link #getReal()}
+ */
+ long round();
+
+ /** Compute the signum of the instance.
+ * The signum is -1 for negative numbers, +1 for positive numbers and 0 otherwise
+ * @return -1.0, -0.0, +0.0, +1.0 or NaN depending on sign of a
+ */
+ T signum();
+
+ /**
+ * Returns the instance with the sign of the argument.
+ * A NaN {@code sign} argument is treated as positive.
+ *
+ * @param sign the sign for the returned value
+ * @return the instance with the same sign as the {@code sign} argument
+ */
+ T copySign(T sign);
+
+ /**
+ * Returns the instance with the sign of the argument.
+ * A NaN {@code sign} argument is treated as positive.
+ *
+ * @param sign the sign for the returned value
+ * @return the instance with the same sign as the {@code sign} argument
+ */
+ T copySign(double sign);
+
+ /**
+ * Multiply the instance by a power of 2.
+ * @param n power of 2
+ * @return this &times; 2<sup>n</sup>
+ */
+ T scalb(int n);
+
+ /**
+ * Returns the hypotenuse of a triangle with sides {@code this} and {@code y}
+ * - sqrt(<i>this</i><sup>2</sup>&nbsp;+<i>y</i><sup>2</sup>)
+ * avoiding intermediate overflow or underflow.
+ *
+ * <ul>
+ * <li> If either argument is infinite, then the result is positive infinity.</li>
+ * <li> else, if either argument is NaN then the result is NaN.</li>
+ * </ul>
+ *
+ * @param y a value
+ * @return sqrt(<i>this</i><sup>2</sup>&nbsp;+<i>y</i><sup>2</sup>)
+ * @exception DimensionMismatchException if number of free parameters or orders are inconsistent
+ */
+ T hypot(T y)
+ throws DimensionMismatchException;
+
+ /** {@inheritDoc} */
+ T reciprocal();
+
+ /** Square root.
+ * @return square root of the instance
+ */
+ T sqrt();
+
+ /** Cubic root.
+ * @return cubic root of the instance
+ */
+ T cbrt();
+
+ /** N<sup>th</sup> root.
+ * @param n order of the root
+ * @return n<sup>th</sup> root of the instance
+ */
+ T rootN(int n);
+
+ /** Power operation.
+ * @param p power to apply
+ * @return this<sup>p</sup>
+ */
+ T pow(double p);
+
+ /** Integer power operation.
+ * @param n power to apply
+ * @return this<sup>n</sup>
+ */
+ T pow(int n);
+
+ /** Power operation.
+ * @param e exponent
+ * @return this<sup>e</sup>
+ * @exception DimensionMismatchException if number of free parameters or orders are inconsistent
+ */
+ T pow(T e)
+ throws DimensionMismatchException;
+
+ /** Exponential.
+ * @return exponential of the instance
+ */
+ T exp();
+
+ /** Exponential minus 1.
+ * @return exponential minus one of the instance
+ */
+ T expm1();
+
+ /** Natural logarithm.
+ * @return logarithm of the instance
+ */
+ T log();
+
+ /** Shifted natural logarithm.
+ * @return logarithm of one plus the instance
+ */
+ T log1p();
+
+// TODO: add this method in 4.0, as it is not possible to do it in 3.2
+// due to incompatibility of the return type in the Dfp class
+// /** Base 10 logarithm.
+// * @return base 10 logarithm of the instance
+// */
+// T log10();
+
+ /** Cosine operation.
+ * @return cos(this)
+ */
+ T cos();
+
+ /** Sine operation.
+ * @return sin(this)
+ */
+ T sin();
+
+ /** Tangent operation.
+ * @return tan(this)
+ */
+ T tan();
+
+ /** Arc cosine operation.
+ * @return acos(this)
+ */
+ T acos();
+
+ /** Arc sine operation.
+ * @return asin(this)
+ */
+ T asin();
+
+ /** Arc tangent operation.
+ * @return atan(this)
+ */
+ T atan();
+
+ /** Two arguments arc tangent operation.
+ * @param x second argument of the arc tangent
+ * @return atan2(this, x)
+ * @exception DimensionMismatchException if number of free parameters or orders are inconsistent
+ */
+ T atan2(T x)
+ throws DimensionMismatchException;
+
+ /** Hyperbolic cosine operation.
+ * @return cosh(this)
+ */
+ T cosh();
+
+ /** Hyperbolic sine operation.
+ * @return sinh(this)
+ */
+ T sinh();
+
+ /** Hyperbolic tangent operation.
+ * @return tanh(this)
+ */
+ T tanh();
+
+ /** Inverse hyperbolic cosine operation.
+ * @return acosh(this)
+ */
+ T acosh();
+
+ /** Inverse hyperbolic sine operation.
+ * @return asin(this)
+ */
+ T asinh();
+
+ /** Inverse hyperbolic tangent operation.
+ * @return atanh(this)
+ */
+ T atanh();
+
+ /**
+ * Compute a linear combination.
+ * @param a Factors.
+ * @param b Factors.
+ * @return <code>&Sigma;<sub>i</sub> a<sub>i</sub> b<sub>i</sub></code>.
+ * @throws DimensionMismatchException if arrays dimensions don't match
+ * @since 3.2
+ */
+ T linearCombination(T[] a, T[] b)
+ throws DimensionMismatchException;
+
+ /**
+ * Compute a linear combination.
+ * @param a Factors.
+ * @param b Factors.
+ * @return <code>&Sigma;<sub>i</sub> a<sub>i</sub> b<sub>i</sub></code>.
+ * @throws DimensionMismatchException if arrays dimensions don't match
+ * @since 3.2
+ */
+ T linearCombination(double[] a, T[] b)
+ throws DimensionMismatchException;
+
+ /**
+ * Compute a linear combination.
+ * @param a1 first factor of the first term
+ * @param b1 second factor of the first term
+ * @param a2 first factor of the second term
+ * @param b2 second factor of the second term
+ * @return a<sub>1</sub>&times;b<sub>1</sub> +
+ * a<sub>2</sub>&times;b<sub>2</sub>
+ * @see #linearCombination(Object, Object, Object, Object, Object, Object)
+ * @see #linearCombination(Object, Object, Object, Object, Object, Object, Object, Object)
+ * @since 3.2
+ */
+ T linearCombination(T a1, T b1, T a2, T b2);
+
+ /**
+ * Compute a linear combination.
+ * @param a1 first factor of the first term
+ * @param b1 second factor of the first term
+ * @param a2 first factor of the second term
+ * @param b2 second factor of the second term
+ * @return a<sub>1</sub>&times;b<sub>1</sub> +
+ * a<sub>2</sub>&times;b<sub>2</sub>
+ * @see #linearCombination(double, Object, double, Object, double, Object)
+ * @see #linearCombination(double, Object, double, Object, double, Object, double, Object)
+ * @since 3.2
+ */
+ T linearCombination(double a1, T b1, double a2, T b2);
+
+ /**
+ * Compute a linear combination.
+ * @param a1 first factor of the first term
+ * @param b1 second factor of the first term
+ * @param a2 first factor of the second term
+ * @param b2 second factor of the second term
+ * @param a3 first factor of the third term
+ * @param b3 second factor of the third term
+ * @return a<sub>1</sub>&times;b<sub>1</sub> +
+ * a<sub>2</sub>&times;b<sub>2</sub> + a<sub>3</sub>&times;b<sub>3</sub>
+ * @see #linearCombination(Object, Object, Object, Object)
+ * @see #linearCombination(Object, Object, Object, Object, Object, Object, Object, Object)
+ * @since 3.2
+ */
+ T linearCombination(T a1, T b1, T a2, T b2, T a3, T b3);
+
+ /**
+ * Compute a linear combination.
+ * @param a1 first factor of the first term
+ * @param b1 second factor of the first term
+ * @param a2 first factor of the second term
+ * @param b2 second factor of the second term
+ * @param a3 first factor of the third term
+ * @param b3 second factor of the third term
+ * @return a<sub>1</sub>&times;b<sub>1</sub> +
+ * a<sub>2</sub>&times;b<sub>2</sub> + a<sub>3</sub>&times;b<sub>3</sub>
+ * @see #linearCombination(double, Object, double, Object)
+ * @see #linearCombination(double, Object, double, Object, double, Object, double, Object)
+ * @since 3.2
+ */
+ T linearCombination(double a1, T b1, double a2, T b2, double a3, T b3);
+
+ /**
+ * Compute a linear combination.
+ * @param a1 first factor of the first term
+ * @param b1 second factor of the first term
+ * @param a2 first factor of the second term
+ * @param b2 second factor of the second term
+ * @param a3 first factor of the third term
+ * @param b3 second factor of the third term
+ * @param a4 first factor of the third term
+ * @param b4 second factor of the third term
+ * @return a<sub>1</sub>&times;b<sub>1</sub> +
+ * a<sub>2</sub>&times;b<sub>2</sub> + a<sub>3</sub>&times;b<sub>3</sub> +
+ * a<sub>4</sub>&times;b<sub>4</sub>
+ * @see #linearCombination(Object, Object, Object, Object)
+ * @see #linearCombination(Object, Object, Object, Object, Object, Object)
+ * @since 3.2
+ */
+ T linearCombination(T a1, T b1, T a2, T b2, T a3, T b3, T a4, T b4);
+
+ /**
+ * Compute a linear combination.
+ * @param a1 first factor of the first term
+ * @param b1 second factor of the first term
+ * @param a2 first factor of the second term
+ * @param b2 second factor of the second term
+ * @param a3 first factor of the third term
+ * @param b3 second factor of the third term
+ * @param a4 first factor of the third term
+ * @param b4 second factor of the third term
+ * @return a<sub>1</sub>&times;b<sub>1</sub> +
+ * a<sub>2</sub>&times;b<sub>2</sub> + a<sub>3</sub>&times;b<sub>3</sub> +
+ * a<sub>4</sub>&times;b<sub>4</sub>
+ * @see #linearCombination(double, Object, double, Object)
+ * @see #linearCombination(double, Object, double, Object, double, Object)
+ * @since 3.2
+ */
+ T linearCombination(double a1, T b1, double a2, T b2, double a3, T b3, double a4, T b4);
+
+}
diff --git a/src/main/java/org/apache/commons/math3/analysis/BivariateFunction.java b/src/main/java/org/apache/commons/math3/analysis/BivariateFunction.java
new file mode 100644
index 0000000..a595753
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/analysis/BivariateFunction.java
@@ -0,0 +1,34 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math3.analysis;
+
+/**
+ * An interface representing a bivariate real function.
+ *
+ * @since 2.1
+ */
+public interface BivariateFunction {
+ /**
+ * Compute the value for the function.
+ *
+ * @param x Abscissa for which the function value should be computed.
+ * @param y Ordinate for which the function value should be computed.
+ * @return the value.
+ */
+ double value(double x, double y);
+}
diff --git a/src/main/java/org/apache/commons/math3/analysis/DifferentiableMultivariateFunction.java b/src/main/java/org/apache/commons/math3/analysis/DifferentiableMultivariateFunction.java
new file mode 100644
index 0000000..c5561a5
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/analysis/DifferentiableMultivariateFunction.java
@@ -0,0 +1,53 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math3.analysis;
+
+/**
+ * Extension of {@link MultivariateFunction} representing a differentiable multivariate real
+ * function.
+ *
+ * @since 2.0
+ * @deprecated as of 3.1 replaced by {@link
+ * org.apache.commons.math3.analysis.differentiation.MultivariateDifferentiableFunction}
+ */
+@Deprecated
+public interface DifferentiableMultivariateFunction extends MultivariateFunction {
+
+ /**
+ * Returns the partial derivative of the function with respect to a point coordinate.
+ *
+ * <p>The partial derivative is defined with respect to point coordinate x<sub>k</sub>. If the
+ * partial derivatives with respect to all coordinates are needed, it may be more efficient to
+ * use the {@link #gradient()} method which will compute them all at once.
+ *
+ * @param k index of the coordinate with respect to which the partial derivative is computed
+ * @return the partial derivative function with respect to k<sup>th</sup> point coordinate
+ */
+ MultivariateFunction partialDerivative(int k);
+
+ /**
+ * Returns the gradient function.
+ *
+ * <p>If only one partial derivative with respect to a specific coordinate is needed, it may be
+ * more efficient to use the {@link #partialDerivative(int)} method which will compute only the
+ * specified component.
+ *
+ * @return the gradient function
+ */
+ MultivariateVectorFunction gradient();
+}
diff --git a/src/main/java/org/apache/commons/math3/analysis/DifferentiableMultivariateVectorFunction.java b/src/main/java/org/apache/commons/math3/analysis/DifferentiableMultivariateVectorFunction.java
new file mode 100644
index 0000000..a6a4907
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/analysis/DifferentiableMultivariateVectorFunction.java
@@ -0,0 +1,37 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math3.analysis;
+
+/**
+ * Extension of {@link MultivariateVectorFunction} representing a differentiable multivariate
+ * vectorial function.
+ *
+ * @since 2.0
+ * @deprecated as of 3.1 replaced by {@link
+ * org.apache.commons.math3.analysis.differentiation.MultivariateDifferentiableVectorFunction}
+ */
+@Deprecated
+public interface DifferentiableMultivariateVectorFunction extends MultivariateVectorFunction {
+
+ /**
+ * Returns the jacobian function.
+ *
+ * @return the jacobian function
+ */
+ MultivariateMatrixFunction jacobian();
+}
diff --git a/src/main/java/org/apache/commons/math3/analysis/DifferentiableUnivariateFunction.java b/src/main/java/org/apache/commons/math3/analysis/DifferentiableUnivariateFunction.java
new file mode 100644
index 0000000..577e7d7
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/analysis/DifferentiableUnivariateFunction.java
@@ -0,0 +1,34 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math3.analysis;
+
+/**
+ * Extension of {@link UnivariateFunction} representing a differentiable univariate real function.
+ *
+ * @deprecated as of 3.1 replaced by {@link
+ * org.apache.commons.math3.analysis.differentiation.UnivariateDifferentiableFunction}
+ */
+@Deprecated
+public interface DifferentiableUnivariateFunction extends UnivariateFunction {
+
+ /**
+ * Returns the derivative of the function
+ *
+ * @return the derivative function
+ */
+ UnivariateFunction derivative();
+}
diff --git a/src/main/java/org/apache/commons/math3/analysis/DifferentiableUnivariateMatrixFunction.java b/src/main/java/org/apache/commons/math3/analysis/DifferentiableUnivariateMatrixFunction.java
new file mode 100644
index 0000000..24ba488
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/analysis/DifferentiableUnivariateMatrixFunction.java
@@ -0,0 +1,36 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math3.analysis;
+
+/**
+ * Extension of {@link UnivariateMatrixFunction} representing a differentiable univariate matrix
+ * function.
+ *
+ * @since 2.0
+ * @deprecated as of 3.1 replaced by {@link
+ * org.apache.commons.math3.analysis.differentiation.UnivariateDifferentiableMatrixFunction}
+ */
+@Deprecated
+public interface DifferentiableUnivariateMatrixFunction extends UnivariateMatrixFunction {
+
+ /**
+ * Returns the derivative of the function
+ *
+ * @return the derivative function
+ */
+ UnivariateMatrixFunction derivative();
+}
diff --git a/src/main/java/org/apache/commons/math3/analysis/DifferentiableUnivariateVectorFunction.java b/src/main/java/org/apache/commons/math3/analysis/DifferentiableUnivariateVectorFunction.java
new file mode 100644
index 0000000..6028bf6
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/analysis/DifferentiableUnivariateVectorFunction.java
@@ -0,0 +1,36 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math3.analysis;
+
+/**
+ * Extension of {@link UnivariateVectorFunction} representing a differentiable univariate vectorial
+ * function.
+ *
+ * @since 2.0
+ * @deprecated as of 3.1 replaced by {@link
+ * org.apache.commons.math3.analysis.differentiation.UnivariateDifferentiableVectorFunction}
+ */
+@Deprecated
+public interface DifferentiableUnivariateVectorFunction extends UnivariateVectorFunction {
+
+ /**
+ * Returns the derivative of the function
+ *
+ * @return the derivative function
+ */
+ UnivariateVectorFunction derivative();
+}
diff --git a/src/main/java/org/apache/commons/math3/analysis/FunctionUtils.java b/src/main/java/org/apache/commons/math3/analysis/FunctionUtils.java
new file mode 100644
index 0000000..b3cdb9e
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/analysis/FunctionUtils.java
@@ -0,0 +1,823 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math3.analysis;
+
+import org.apache.commons.math3.analysis.differentiation.DerivativeStructure;
+import org.apache.commons.math3.analysis.differentiation.MultivariateDifferentiableFunction;
+import org.apache.commons.math3.analysis.differentiation.MultivariateDifferentiableVectorFunction;
+import org.apache.commons.math3.analysis.differentiation.UnivariateDifferentiableFunction;
+import org.apache.commons.math3.analysis.function.Identity;
+import org.apache.commons.math3.exception.DimensionMismatchException;
+import org.apache.commons.math3.exception.NotStrictlyPositiveException;
+import org.apache.commons.math3.exception.NumberIsTooLargeException;
+import org.apache.commons.math3.exception.util.LocalizedFormats;
+
+/**
+ * Utilities for manipulating function objects.
+ *
+ * @since 3.0
+ */
+public class FunctionUtils {
+ /** Class only contains static methods. */
+ private FunctionUtils() {}
+
+ /**
+ * Composes functions.
+ *
+ * <p>The functions in the argument list are composed sequentially, in the given order. For
+ * example, compose(f1,f2,f3) acts like f1(f2(f3(x))).
+ *
+ * @param f List of functions.
+ * @return the composite function.
+ */
+ public static UnivariateFunction compose(final UnivariateFunction... f) {
+ return new UnivariateFunction() {
+ /** {@inheritDoc} */
+ public double value(double x) {
+ double r = x;
+ for (int i = f.length - 1; i >= 0; i--) {
+ r = f[i].value(r);
+ }
+ return r;
+ }
+ };
+ }
+
+ /**
+ * Composes functions.
+ *
+ * <p>The functions in the argument list are composed sequentially, in the given order. For
+ * example, compose(f1,f2,f3) acts like f1(f2(f3(x))).
+ *
+ * @param f List of functions.
+ * @return the composite function.
+ * @since 3.1
+ */
+ public static UnivariateDifferentiableFunction compose(
+ final UnivariateDifferentiableFunction... f) {
+ return new UnivariateDifferentiableFunction() {
+
+ /** {@inheritDoc} */
+ public double value(final double t) {
+ double r = t;
+ for (int i = f.length - 1; i >= 0; i--) {
+ r = f[i].value(r);
+ }
+ return r;
+ }
+
+ /** {@inheritDoc} */
+ public DerivativeStructure value(final DerivativeStructure t) {
+ DerivativeStructure r = t;
+ for (int i = f.length - 1; i >= 0; i--) {
+ r = f[i].value(r);
+ }
+ return r;
+ }
+ };
+ }
+
+ /**
+ * Composes functions.
+ *
+ * <p>The functions in the argument list are composed sequentially, in the given order. For
+ * example, compose(f1,f2,f3) acts like f1(f2(f3(x))).
+ *
+ * @param f List of functions.
+ * @return the composite function.
+ * @deprecated as of 3.1 replaced by {@link #compose(UnivariateDifferentiableFunction...)}
+ */
+ @Deprecated
+ public static DifferentiableUnivariateFunction compose(
+ final DifferentiableUnivariateFunction... f) {
+ return new DifferentiableUnivariateFunction() {
+ /** {@inheritDoc} */
+ public double value(double x) {
+ double r = x;
+ for (int i = f.length - 1; i >= 0; i--) {
+ r = f[i].value(r);
+ }
+ return r;
+ }
+
+ /** {@inheritDoc} */
+ public UnivariateFunction derivative() {
+ return new UnivariateFunction() {
+ /** {@inheritDoc} */
+ public double value(double x) {
+ double p = 1;
+ double r = x;
+ for (int i = f.length - 1; i >= 0; i--) {
+ p *= f[i].derivative().value(r);
+ r = f[i].value(r);
+ }
+ return p;
+ }
+ };
+ }
+ };
+ }
+
+ /**
+ * Adds functions.
+ *
+ * @param f List of functions.
+ * @return a function that computes the sum of the functions.
+ */
+ public static UnivariateFunction add(final UnivariateFunction... f) {
+ return new UnivariateFunction() {
+ /** {@inheritDoc} */
+ public double value(double x) {
+ double r = f[0].value(x);
+ for (int i = 1; i < f.length; i++) {
+ r += f[i].value(x);
+ }
+ return r;
+ }
+ };
+ }
+
+ /**
+ * Adds functions.
+ *
+ * @param f List of functions.
+ * @return a function that computes the sum of the functions.
+ * @since 3.1
+ */
+ public static UnivariateDifferentiableFunction add(
+ final UnivariateDifferentiableFunction... f) {
+ return new UnivariateDifferentiableFunction() {
+
+ /** {@inheritDoc} */
+ public double value(final double t) {
+ double r = f[0].value(t);
+ for (int i = 1; i < f.length; i++) {
+ r += f[i].value(t);
+ }
+ return r;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @throws DimensionMismatchException if functions are not consistent with each other
+ */
+ public DerivativeStructure value(final DerivativeStructure t)
+ throws DimensionMismatchException {
+ DerivativeStructure r = f[0].value(t);
+ for (int i = 1; i < f.length; i++) {
+ r = r.add(f[i].value(t));
+ }
+ return r;
+ }
+ };
+ }
+
+ /**
+ * Adds functions.
+ *
+ * @param f List of functions.
+ * @return a function that computes the sum of the functions.
+ * @deprecated as of 3.1 replaced by {@link #add(UnivariateDifferentiableFunction...)}
+ */
+ @Deprecated
+ public static DifferentiableUnivariateFunction add(
+ final DifferentiableUnivariateFunction... f) {
+ return new DifferentiableUnivariateFunction() {
+ /** {@inheritDoc} */
+ public double value(double x) {
+ double r = f[0].value(x);
+ for (int i = 1; i < f.length; i++) {
+ r += f[i].value(x);
+ }
+ return r;
+ }
+
+ /** {@inheritDoc} */
+ public UnivariateFunction derivative() {
+ return new UnivariateFunction() {
+ /** {@inheritDoc} */
+ public double value(double x) {
+ double r = f[0].derivative().value(x);
+ for (int i = 1; i < f.length; i++) {
+ r += f[i].derivative().value(x);
+ }
+ return r;
+ }
+ };
+ }
+ };
+ }
+
+ /**
+ * Multiplies functions.
+ *
+ * @param f List of functions.
+ * @return a function that computes the product of the functions.
+ */
+ public static UnivariateFunction multiply(final UnivariateFunction... f) {
+ return new UnivariateFunction() {
+ /** {@inheritDoc} */
+ public double value(double x) {
+ double r = f[0].value(x);
+ for (int i = 1; i < f.length; i++) {
+ r *= f[i].value(x);
+ }
+ return r;
+ }
+ };
+ }
+
+ /**
+ * Multiplies functions.
+ *
+ * @param f List of functions.
+ * @return a function that computes the product of the functions.
+ * @since 3.1
+ */
+ public static UnivariateDifferentiableFunction multiply(
+ final UnivariateDifferentiableFunction... f) {
+ return new UnivariateDifferentiableFunction() {
+
+ /** {@inheritDoc} */
+ public double value(final double t) {
+ double r = f[0].value(t);
+ for (int i = 1; i < f.length; i++) {
+ r *= f[i].value(t);
+ }
+ return r;
+ }
+
+ /** {@inheritDoc} */
+ public DerivativeStructure value(final DerivativeStructure t) {
+ DerivativeStructure r = f[0].value(t);
+ for (int i = 1; i < f.length; i++) {
+ r = r.multiply(f[i].value(t));
+ }
+ return r;
+ }
+ };
+ }
+
+ /**
+ * Multiplies functions.
+ *
+ * @param f List of functions.
+ * @return a function that computes the product of the functions.
+ * @deprecated as of 3.1 replaced by {@link #multiply(UnivariateDifferentiableFunction...)}
+ */
+ @Deprecated
+ public static DifferentiableUnivariateFunction multiply(
+ final DifferentiableUnivariateFunction... f) {
+ return new DifferentiableUnivariateFunction() {
+ /** {@inheritDoc} */
+ public double value(double x) {
+ double r = f[0].value(x);
+ for (int i = 1; i < f.length; i++) {
+ r *= f[i].value(x);
+ }
+ return r;
+ }
+
+ /** {@inheritDoc} */
+ public UnivariateFunction derivative() {
+ return new UnivariateFunction() {
+ /** {@inheritDoc} */
+ public double value(double x) {
+ double sum = 0;
+ for (int i = 0; i < f.length; i++) {
+ double prod = f[i].derivative().value(x);
+ for (int j = 0; j < f.length; j++) {
+ if (i != j) {
+ prod *= f[j].value(x);
+ }
+ }
+ sum += prod;
+ }
+ return sum;
+ }
+ };
+ }
+ };
+ }
+
+ /**
+ * Returns the univariate function {@code h(x) = combiner(f(x), g(x)).}
+ *
+ * @param combiner Combiner function.
+ * @param f Function.
+ * @param g Function.
+ * @return the composite function.
+ */
+ public static UnivariateFunction combine(
+ final BivariateFunction combiner,
+ final UnivariateFunction f,
+ final UnivariateFunction g) {
+ return new UnivariateFunction() {
+ /** {@inheritDoc} */
+ public double value(double x) {
+ return combiner.value(f.value(x), g.value(x));
+ }
+ };
+ }
+
+ /**
+ * Returns a MultivariateFunction h(x[]) defined by
+ *
+ * <pre> <code>
+ * h(x[]) = combiner(...combiner(combiner(initialValue,f(x[0])),f(x[1]))...),f(x[x.length-1]))
+ * </code></pre>
+ *
+ * @param combiner Combiner function.
+ * @param f Function.
+ * @param initialValue Initial value.
+ * @return a collector function.
+ */
+ public static MultivariateFunction collector(
+ final BivariateFunction combiner,
+ final UnivariateFunction f,
+ final double initialValue) {
+ return new MultivariateFunction() {
+ /** {@inheritDoc} */
+ public double value(double[] point) {
+ double result = combiner.value(initialValue, f.value(point[0]));
+ for (int i = 1; i < point.length; i++) {
+ result = combiner.value(result, f.value(point[i]));
+ }
+ return result;
+ }
+ };
+ }
+
+ /**
+ * Returns a MultivariateFunction h(x[]) defined by
+ *
+ * <pre> <code>
+ * h(x[]) = combiner(...combiner(combiner(initialValue,x[0]),x[1])...),x[x.length-1])
+ * </code></pre>
+ *
+ * @param combiner Combiner function.
+ * @param initialValue Initial value.
+ * @return a collector function.
+ */
+ public static MultivariateFunction collector(
+ final BivariateFunction combiner, final double initialValue) {
+ return collector(combiner, new Identity(), initialValue);
+ }
+
+ /**
+ * Creates a unary function by fixing the first argument of a binary function.
+ *
+ * @param f Binary function.
+ * @param fixed value to which the first argument of {@code f} is set.
+ * @return the unary function h(x) = f(fixed, x)
+ */
+ public static UnivariateFunction fix1stArgument(final BivariateFunction f, final double fixed) {
+ return new UnivariateFunction() {
+ /** {@inheritDoc} */
+ public double value(double x) {
+ return f.value(fixed, x);
+ }
+ };
+ }
+
+ /**
+ * Creates a unary function by fixing the second argument of a binary function.
+ *
+ * @param f Binary function.
+ * @param fixed value to which the second argument of {@code f} is set.
+ * @return the unary function h(x) = f(x, fixed)
+ */
+ public static UnivariateFunction fix2ndArgument(final BivariateFunction f, final double fixed) {
+ return new UnivariateFunction() {
+ /** {@inheritDoc} */
+ public double value(double x) {
+ return f.value(x, fixed);
+ }
+ };
+ }
+
+ /**
+ * Samples the specified univariate real function on the specified interval.
+ *
+ * <p>The interval is divided equally into {@code n} sections and sample points are taken from
+ * {@code min} to {@code max - (max - min) / n}; therefore {@code f} is not sampled at the upper
+ * bound {@code max}.
+ *
+ * @param f Function to be sampled
+ * @param min Lower bound of the interval (included).
+ * @param max Upper bound of the interval (excluded).
+ * @param n Number of sample points.
+ * @return the array of samples.
+ * @throws NumberIsTooLargeException if the lower bound {@code min} is greater than, or equal to
+ * the upper bound {@code max}.
+ * @throws NotStrictlyPositiveException if the number of sample points {@code n} is negative.
+ */
+ public static double[] sample(UnivariateFunction f, double min, double max, int n)
+ throws NumberIsTooLargeException, NotStrictlyPositiveException {
+
+ if (n <= 0) {
+ throw new NotStrictlyPositiveException(
+ LocalizedFormats.NOT_POSITIVE_NUMBER_OF_SAMPLES, Integer.valueOf(n));
+ }
+ if (min >= max) {
+ throw new NumberIsTooLargeException(min, max, false);
+ }
+
+ final double[] s = new double[n];
+ final double h = (max - min) / n;
+ for (int i = 0; i < n; i++) {
+ s[i] = f.value(min + i * h);
+ }
+ return s;
+ }
+
+ /**
+ * Convert a {@link UnivariateDifferentiableFunction} into a {@link
+ * DifferentiableUnivariateFunction}.
+ *
+ * @param f function to convert
+ * @return converted function
+ * @deprecated this conversion method is temporary in version 3.1, as the {@link
+ * DifferentiableUnivariateFunction} interface itself is deprecated
+ */
+ @Deprecated
+ public static DifferentiableUnivariateFunction toDifferentiableUnivariateFunction(
+ final UnivariateDifferentiableFunction f) {
+ return new DifferentiableUnivariateFunction() {
+
+ /** {@inheritDoc} */
+ public double value(final double x) {
+ return f.value(x);
+ }
+
+ /** {@inheritDoc} */
+ public UnivariateFunction derivative() {
+ return new UnivariateFunction() {
+ /** {@inheritDoc} */
+ public double value(final double x) {
+ return f.value(new DerivativeStructure(1, 1, 0, x)).getPartialDerivative(1);
+ }
+ };
+ }
+ };
+ }
+
+ /**
+ * Convert a {@link DifferentiableUnivariateFunction} into a {@link
+ * UnivariateDifferentiableFunction}.
+ *
+ * <p>Note that the converted function is able to handle {@link DerivativeStructure} up to order
+ * one. If the function is called with higher order, a {@link NumberIsTooLargeException} is
+ * thrown.
+ *
+ * @param f function to convert
+ * @return converted function
+ * @deprecated this conversion method is temporary in version 3.1, as the {@link
+ * DifferentiableUnivariateFunction} interface itself is deprecated
+ */
+ @Deprecated
+ public static UnivariateDifferentiableFunction toUnivariateDifferential(
+ final DifferentiableUnivariateFunction f) {
+ return new UnivariateDifferentiableFunction() {
+
+ /** {@inheritDoc} */
+ public double value(final double x) {
+ return f.value(x);
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @exception NumberIsTooLargeException if derivation order is greater than 1
+ */
+ public DerivativeStructure value(final DerivativeStructure t)
+ throws NumberIsTooLargeException {
+ switch (t.getOrder()) {
+ case 0:
+ return new DerivativeStructure(
+ t.getFreeParameters(), 0, f.value(t.getValue()));
+ case 1:
+ {
+ final int parameters = t.getFreeParameters();
+ final double[] derivatives = new double[parameters + 1];
+ derivatives[0] = f.value(t.getValue());
+ final double fPrime = f.derivative().value(t.getValue());
+ int[] orders = new int[parameters];
+ for (int i = 0; i < parameters; ++i) {
+ orders[i] = 1;
+ derivatives[i + 1] = fPrime * t.getPartialDerivative(orders);
+ orders[i] = 0;
+ }
+ return new DerivativeStructure(parameters, 1, derivatives);
+ }
+ default:
+ throw new NumberIsTooLargeException(t.getOrder(), 1, true);
+ }
+ }
+ };
+ }
+
+ /**
+ * Convert a {@link MultivariateDifferentiableFunction} into a {@link
+ * DifferentiableMultivariateFunction}.
+ *
+ * @param f function to convert
+ * @return converted function
+ * @deprecated this conversion method is temporary in version 3.1, as the {@link
+ * DifferentiableMultivariateFunction} interface itself is deprecated
+ */
+ @Deprecated
+ public static DifferentiableMultivariateFunction toDifferentiableMultivariateFunction(
+ final MultivariateDifferentiableFunction f) {
+ return new DifferentiableMultivariateFunction() {
+
+ /** {@inheritDoc} */
+ public double value(final double[] x) {
+ return f.value(x);
+ }
+
+ /** {@inheritDoc} */
+ public MultivariateFunction partialDerivative(final int k) {
+ return new MultivariateFunction() {
+ /** {@inheritDoc} */
+ public double value(final double[] x) {
+
+ final int n = x.length;
+
+ // delegate computation to underlying function
+ final DerivativeStructure[] dsX = new DerivativeStructure[n];
+ for (int i = 0; i < n; ++i) {
+ if (i == k) {
+ dsX[i] = new DerivativeStructure(1, 1, 0, x[i]);
+ } else {
+ dsX[i] = new DerivativeStructure(1, 1, x[i]);
+ }
+ }
+ final DerivativeStructure y = f.value(dsX);
+
+ // extract partial derivative
+ return y.getPartialDerivative(1);
+ }
+ };
+ }
+
+ /** {@inheritDoc} */
+ public MultivariateVectorFunction gradient() {
+ return new MultivariateVectorFunction() {
+ /** {@inheritDoc} */
+ public double[] value(final double[] x) {
+
+ final int n = x.length;
+
+ // delegate computation to underlying function
+ final DerivativeStructure[] dsX = new DerivativeStructure[n];
+ for (int i = 0; i < n; ++i) {
+ dsX[i] = new DerivativeStructure(n, 1, i, x[i]);
+ }
+ final DerivativeStructure y = f.value(dsX);
+
+ // extract gradient
+ final double[] gradient = new double[n];
+ final int[] orders = new int[n];
+ for (int i = 0; i < n; ++i) {
+ orders[i] = 1;
+ gradient[i] = y.getPartialDerivative(orders);
+ orders[i] = 0;
+ }
+
+ return gradient;
+ }
+ };
+ }
+ };
+ }
+
+ /**
+ * Convert a {@link DifferentiableMultivariateFunction} into a {@link
+ * MultivariateDifferentiableFunction}.
+ *
+ * <p>Note that the converted function is able to handle {@link DerivativeStructure} elements
+ * that all have the same number of free parameters and order, and with order at most 1. If the
+ * function is called with inconsistent numbers of free parameters or higher order, a {@link
+ * DimensionMismatchException} or a {@link NumberIsTooLargeException} will be thrown.
+ *
+ * @param f function to convert
+ * @return converted function
+ * @deprecated this conversion method is temporary in version 3.1, as the {@link
+ * DifferentiableMultivariateFunction} interface itself is deprecated
+ */
+ @Deprecated
+ public static MultivariateDifferentiableFunction toMultivariateDifferentiableFunction(
+ final DifferentiableMultivariateFunction f) {
+ return new MultivariateDifferentiableFunction() {
+
+ /** {@inheritDoc} */
+ public double value(final double[] x) {
+ return f.value(x);
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @exception NumberIsTooLargeException if derivation order is higher than 1
+ * @exception DimensionMismatchException if numbers of free parameters are inconsistent
+ */
+ public DerivativeStructure value(final DerivativeStructure[] t)
+ throws DimensionMismatchException, NumberIsTooLargeException {
+
+ // check parameters and orders limits
+ final int parameters = t[0].getFreeParameters();
+ final int order = t[0].getOrder();
+ final int n = t.length;
+ if (order > 1) {
+ throw new NumberIsTooLargeException(order, 1, true);
+ }
+
+ // check all elements in the array are consistent
+ for (int i = 0; i < n; ++i) {
+ if (t[i].getFreeParameters() != parameters) {
+ throw new DimensionMismatchException(t[i].getFreeParameters(), parameters);
+ }
+
+ if (t[i].getOrder() != order) {
+ throw new DimensionMismatchException(t[i].getOrder(), order);
+ }
+ }
+
+ // delegate computation to underlying function
+ final double[] point = new double[n];
+ for (int i = 0; i < n; ++i) {
+ point[i] = t[i].getValue();
+ }
+ final double value = f.value(point);
+ final double[] gradient = f.gradient().value(point);
+
+ // merge value and gradient into one DerivativeStructure
+ final double[] derivatives = new double[parameters + 1];
+ derivatives[0] = value;
+ final int[] orders = new int[parameters];
+ for (int i = 0; i < parameters; ++i) {
+ orders[i] = 1;
+ for (int j = 0; j < n; ++j) {
+ derivatives[i + 1] += gradient[j] * t[j].getPartialDerivative(orders);
+ }
+ orders[i] = 0;
+ }
+
+ return new DerivativeStructure(parameters, order, derivatives);
+ }
+ };
+ }
+
+ /**
+ * Convert a {@link MultivariateDifferentiableVectorFunction} into a {@link
+ * DifferentiableMultivariateVectorFunction}.
+ *
+ * @param f function to convert
+ * @return converted function
+ * @deprecated this conversion method is temporary in version 3.1, as the {@link
+ * DifferentiableMultivariateVectorFunction} interface itself is deprecated
+ */
+ @Deprecated
+ public static DifferentiableMultivariateVectorFunction
+ toDifferentiableMultivariateVectorFunction(
+ final MultivariateDifferentiableVectorFunction f) {
+ return new DifferentiableMultivariateVectorFunction() {
+
+ /** {@inheritDoc} */
+ public double[] value(final double[] x) {
+ return f.value(x);
+ }
+
+ /** {@inheritDoc} */
+ public MultivariateMatrixFunction jacobian() {
+ return new MultivariateMatrixFunction() {
+ /** {@inheritDoc} */
+ public double[][] value(final double[] x) {
+
+ final int n = x.length;
+
+ // delegate computation to underlying function
+ final DerivativeStructure[] dsX = new DerivativeStructure[n];
+ for (int i = 0; i < n; ++i) {
+ dsX[i] = new DerivativeStructure(n, 1, i, x[i]);
+ }
+ final DerivativeStructure[] y = f.value(dsX);
+
+ // extract Jacobian
+ final double[][] jacobian = new double[y.length][n];
+ final int[] orders = new int[n];
+ for (int i = 0; i < y.length; ++i) {
+ for (int j = 0; j < n; ++j) {
+ orders[j] = 1;
+ jacobian[i][j] = y[i].getPartialDerivative(orders);
+ orders[j] = 0;
+ }
+ }
+
+ return jacobian;
+ }
+ };
+ }
+ };
+ }
+
+ /**
+ * Convert a {@link DifferentiableMultivariateVectorFunction} into a {@link
+ * MultivariateDifferentiableVectorFunction}.
+ *
+ * <p>Note that the converted function is able to handle {@link DerivativeStructure} elements
+ * that all have the same number of free parameters and order, and with order at most 1. If the
+ * function is called with inconsistent numbers of free parameters or higher order, a {@link
+ * DimensionMismatchException} or a {@link NumberIsTooLargeException} will be thrown.
+ *
+ * @param f function to convert
+ * @return converted function
+ * @deprecated this conversion method is temporary in version 3.1, as the {@link
+ * DifferentiableMultivariateFunction} interface itself is deprecated
+ */
+ @Deprecated
+ public static MultivariateDifferentiableVectorFunction
+ toMultivariateDifferentiableVectorFunction(
+ final DifferentiableMultivariateVectorFunction f) {
+ return new MultivariateDifferentiableVectorFunction() {
+
+ /** {@inheritDoc} */
+ public double[] value(final double[] x) {
+ return f.value(x);
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @exception NumberIsTooLargeException if derivation order is higher than 1
+ * @exception DimensionMismatchException if numbers of free parameters are inconsistent
+ */
+ public DerivativeStructure[] value(final DerivativeStructure[] t)
+ throws DimensionMismatchException, NumberIsTooLargeException {
+
+ // check parameters and orders limits
+ final int parameters = t[0].getFreeParameters();
+ final int order = t[0].getOrder();
+ final int n = t.length;
+ if (order > 1) {
+ throw new NumberIsTooLargeException(order, 1, true);
+ }
+
+ // check all elements in the array are consistent
+ for (int i = 0; i < n; ++i) {
+ if (t[i].getFreeParameters() != parameters) {
+ throw new DimensionMismatchException(t[i].getFreeParameters(), parameters);
+ }
+
+ if (t[i].getOrder() != order) {
+ throw new DimensionMismatchException(t[i].getOrder(), order);
+ }
+ }
+
+ // delegate computation to underlying function
+ final double[] point = new double[n];
+ for (int i = 0; i < n; ++i) {
+ point[i] = t[i].getValue();
+ }
+ final double[] value = f.value(point);
+ final double[][] jacobian = f.jacobian().value(point);
+
+ // merge value and Jacobian into a DerivativeStructure array
+ final DerivativeStructure[] merged = new DerivativeStructure[value.length];
+ for (int k = 0; k < merged.length; ++k) {
+ final double[] derivatives = new double[parameters + 1];
+ derivatives[0] = value[k];
+ final int[] orders = new int[parameters];
+ for (int i = 0; i < parameters; ++i) {
+ orders[i] = 1;
+ for (int j = 0; j < n; ++j) {
+ derivatives[i + 1] +=
+ jacobian[k][j] * t[j].getPartialDerivative(orders);
+ }
+ orders[i] = 0;
+ }
+ merged[k] = new DerivativeStructure(parameters, order, derivatives);
+ }
+
+ return merged;
+ }
+ };
+ }
+}
diff --git a/src/main/java/org/apache/commons/math3/analysis/MultivariateFunction.java b/src/main/java/org/apache/commons/math3/analysis/MultivariateFunction.java
new file mode 100644
index 0000000..cb6546f
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/analysis/MultivariateFunction.java
@@ -0,0 +1,41 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math3.analysis;
+
+/**
+ * An interface representing a multivariate real function.
+ *
+ * @since 2.0
+ */
+public interface MultivariateFunction {
+
+ /**
+ * Compute the value for the function at the given point.
+ *
+ * @param point Point at which the function must be evaluated.
+ * @return the function value for the given point.
+ * @throws org.apache.commons.math3.exception.DimensionMismatchException if the parameter's
+ * dimension is wrong for the function being evaluated.
+ * @throws org.apache.commons.math3.exception.MathIllegalArgumentException when the activated
+ * method itself can ascertain that preconditions, specified in the API expressed at the
+ * level of the activated method, have been violated. In the vast majority of cases where
+ * Commons Math throws this exception, it is the result of argument checking of actual
+ * parameters immediately passed to a method.
+ */
+ double value(double[] point);
+}
diff --git a/src/main/java/org/apache/commons/math3/analysis/MultivariateMatrixFunction.java b/src/main/java/org/apache/commons/math3/analysis/MultivariateMatrixFunction.java
new file mode 100644
index 0000000..8f7aaf0
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/analysis/MultivariateMatrixFunction.java
@@ -0,0 +1,35 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math3.analysis;
+
+/**
+ * An interface representing a multivariate matrix function.
+ *
+ * @since 2.0
+ */
+public interface MultivariateMatrixFunction {
+
+ /**
+ * Compute the value for the function at the given point.
+ *
+ * @param point point at which the function must be evaluated
+ * @return function value for the given point
+ * @exception IllegalArgumentException if point's dimension is wrong
+ */
+ double[][] value(double[] point) throws IllegalArgumentException;
+}
diff --git a/src/main/java/org/apache/commons/math3/analysis/MultivariateVectorFunction.java b/src/main/java/org/apache/commons/math3/analysis/MultivariateVectorFunction.java
new file mode 100644
index 0000000..e26d9a6
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/analysis/MultivariateVectorFunction.java
@@ -0,0 +1,35 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math3.analysis;
+
+/**
+ * An interface representing a multivariate vectorial function.
+ *
+ * @since 2.0
+ */
+public interface MultivariateVectorFunction {
+
+ /**
+ * Compute the value for the function at the given point.
+ *
+ * @param point point at which the function must be evaluated
+ * @return function value for the given point
+ * @exception IllegalArgumentException if point's dimension is wrong
+ */
+ double[] value(double[] point) throws IllegalArgumentException;
+}
diff --git a/src/main/java/org/apache/commons/math3/analysis/ParametricUnivariateFunction.java b/src/main/java/org/apache/commons/math3/analysis/ParametricUnivariateFunction.java
new file mode 100644
index 0000000..beee82f
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/analysis/ParametricUnivariateFunction.java
@@ -0,0 +1,44 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math3.analysis;
+
+/**
+ * An interface representing a real function that depends on one independent variable plus some
+ * extra parameters.
+ *
+ * @since 3.0
+ */
+public interface ParametricUnivariateFunction {
+ /**
+ * Compute the value of the function.
+ *
+ * @param x Point for which the function value should be computed.
+ * @param parameters Function parameters.
+ * @return the value.
+ */
+ double value(double x, double... parameters);
+
+ /**
+ * Compute the gradient of the function with respect to its parameters.
+ *
+ * @param x Point for which the function value should be computed.
+ * @param parameters Function parameters.
+ * @return the value.
+ */
+ double[] gradient(double x, double... parameters);
+}
diff --git a/src/main/java/org/apache/commons/math3/analysis/RealFieldUnivariateFunction.java b/src/main/java/org/apache/commons/math3/analysis/RealFieldUnivariateFunction.java
new file mode 100644
index 0000000..431c867
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/analysis/RealFieldUnivariateFunction.java
@@ -0,0 +1,82 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math3.analysis;
+
+import org.apache.commons.math3.RealFieldElement;
+
+/**
+ * An interface representing a univariate real function.
+ *
+ * <p>When a <em>user-defined</em> function encounters an error during evaluation, the {@link
+ * #value(RealFieldElement) value} method should throw a <em>user-defined</em> unchecked exception.
+ *
+ * <p>The following code excerpt shows the recommended way to do that using a root solver as an
+ * example, but the same construct is applicable to ODE integrators or optimizers.
+ *
+ * <pre>
+ * private static class LocalException extends RuntimeException {
+ * // The x value that caused the problem.
+ * private final SomeFieldType x;
+ *
+ * public LocalException(SomeFieldType x) {
+ * this.x = x;
+ * }
+ *
+ * public double getX() {
+ * return x;
+ * }
+ * }
+ *
+ * private static class MyFunction implements FieldUnivariateFunction&lt;SomeFieldType&gt; {
+ * public SomeFieldType value(SomeFieldType x) {
+ * SomeFieldType y = hugeFormula(x);
+ * if (somethingBadHappens) {
+ * throw new LocalException(x);
+ * }
+ * return y;
+ * }
+ * }
+ *
+ * public void compute() {
+ * try {
+ * solver.solve(maxEval, new MyFunction(a, b, c), min, max);
+ * } catch (LocalException le) {
+ * // Retrieve the x value.
+ * }
+ * }
+ * </pre>
+ *
+ * As shown, the exception is local to the user's code and it is guaranteed that Apache Commons Math
+ * will not catch it.
+ *
+ * @param <T> the type of the field elements
+ * @since 3.6
+ * @see UnivariateFunction
+ */
+public interface RealFieldUnivariateFunction<T extends RealFieldElement<T>> {
+ /**
+ * Compute the value of the function.
+ *
+ * @param x Point at which the function value should be computed.
+ * @return the value of the function.
+ * @throws IllegalArgumentException when the activated method itself can ascertain that a
+ * precondition, specified in the API expressed at the level of the activated method, has
+ * been violated. When Commons Math throws an {@code IllegalArgumentException}, it is
+ * usually the consequence of checking the actual parameters passed to the method.
+ */
+ T value(T x);
+}
diff --git a/src/main/java/org/apache/commons/math3/analysis/TrivariateFunction.java b/src/main/java/org/apache/commons/math3/analysis/TrivariateFunction.java
new file mode 100644
index 0000000..30ba767
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/analysis/TrivariateFunction.java
@@ -0,0 +1,35 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math3.analysis;
+
+/**
+ * An interface representing a trivariate real function.
+ *
+ * @since 2.2
+ */
+public interface TrivariateFunction {
+ /**
+ * Compute the value for the function.
+ *
+ * @param x x-coordinate for which the function value should be computed.
+ * @param y y-coordinate for which the function value should be computed.
+ * @param z z-coordinate for which the function value should be computed.
+ * @return the value.
+ */
+ double value(double x, double y, double z);
+}
diff --git a/src/main/java/org/apache/commons/math3/analysis/UnivariateFunction.java b/src/main/java/org/apache/commons/math3/analysis/UnivariateFunction.java
new file mode 100644
index 0000000..54b0d0f
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/analysis/UnivariateFunction.java
@@ -0,0 +1,76 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math3.analysis;
+
+/**
+ * An interface representing a univariate real function.
+ *
+ * <p>When a <em>user-defined</em> function encounters an error during evaluation, the {@link
+ * #value(double) value} method should throw a <em>user-defined</em> unchecked exception.
+ *
+ * <p>The following code excerpt shows the recommended way to do that using a root solver as an
+ * example, but the same construct is applicable to ODE integrators or optimizers.
+ *
+ * <pre>
+ * private static class LocalException extends RuntimeException {
+ * // The x value that caused the problem.
+ * private final double x;
+ *
+ * public LocalException(double x) {
+ * this.x = x;
+ * }
+ *
+ * public double getX() {
+ * return x;
+ * }
+ * }
+ *
+ * private static class MyFunction implements UnivariateFunction {
+ * public double value(double x) {
+ * double y = hugeFormula(x);
+ * if (somethingBadHappens) {
+ * throw new LocalException(x);
+ * }
+ * return y;
+ * }
+ * }
+ *
+ * public void compute() {
+ * try {
+ * solver.solve(maxEval, new MyFunction(a, b, c), min, max);
+ * } catch (LocalException le) {
+ * // Retrieve the x value.
+ * }
+ * }
+ * </pre>
+ *
+ * As shown, the exception is local to the user's code and it is guaranteed that Apache Commons Math
+ * will not catch it.
+ */
+public interface UnivariateFunction {
+ /**
+ * Compute the value of the function.
+ *
+ * @param x Point at which the function value should be computed.
+ * @return the value of the function.
+ * @throws IllegalArgumentException when the activated method itself can ascertain that a
+ * precondition, specified in the API expressed at the level of the activated method, has
+ * been violated. When Commons Math throws an {@code IllegalArgumentException}, it is
+ * usually the consequence of checking the actual parameters passed to the method.
+ */
+ double value(double x);
+}
diff --git a/src/main/java/org/apache/commons/math3/analysis/UnivariateMatrixFunction.java b/src/main/java/org/apache/commons/math3/analysis/UnivariateMatrixFunction.java
new file mode 100644
index 0000000..8348056
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/analysis/UnivariateMatrixFunction.java
@@ -0,0 +1,33 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math3.analysis;
+
+/**
+ * An interface representing a univariate matrix function.
+ *
+ * @since 2.0
+ */
+public interface UnivariateMatrixFunction {
+
+ /**
+ * Compute the value for the function.
+ *
+ * @param x the point for which the function value should be computed
+ * @return the value
+ */
+ double[][] value(double x);
+}
diff --git a/src/main/java/org/apache/commons/math3/analysis/UnivariateVectorFunction.java b/src/main/java/org/apache/commons/math3/analysis/UnivariateVectorFunction.java
new file mode 100644
index 0000000..2caeb36
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/analysis/UnivariateVectorFunction.java
@@ -0,0 +1,33 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math3.analysis;
+
+/**
+ * An interface representing a univariate vectorial function.
+ *
+ * @since 2.0
+ */
+public interface UnivariateVectorFunction {
+
+ /**
+ * Compute the value for the function.
+ *
+ * @param x the point for which the function value should be computed
+ * @return the value
+ */
+ double[] value(double x);
+}
diff --git a/src/main/java/org/apache/commons/math3/analysis/differentiation/DSCompiler.java b/src/main/java/org/apache/commons/math3/analysis/differentiation/DSCompiler.java
new file mode 100644
index 0000000..15fa499
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/analysis/differentiation/DSCompiler.java
@@ -0,0 +1,1820 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math3.analysis.differentiation;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.concurrent.atomic.AtomicReference;
+
+import org.apache.commons.math3.exception.DimensionMismatchException;
+import org.apache.commons.math3.exception.MathArithmeticException;
+import org.apache.commons.math3.exception.MathInternalError;
+import org.apache.commons.math3.exception.NotPositiveException;
+import org.apache.commons.math3.exception.NumberIsTooLargeException;
+import org.apache.commons.math3.util.CombinatoricsUtils;
+import org.apache.commons.math3.util.FastMath;
+import org.apache.commons.math3.util.MathArrays;
+
+/** Class holding "compiled" computation rules for derivative structures.
+ * <p>This class implements the computation rules described in Dan Kalman's paper <a
+ * href="http://www1.american.edu/cas/mathstat/People/kalman/pdffiles/mmgautodiff.pdf">Doubly
+ * Recursive Multivariate Automatic Differentiation</a>, Mathematics Magazine, vol. 75,
+ * no. 3, June 2002. However, in order to avoid performances bottlenecks, the recursive
+ * rules are "compiled" once in an unfold form. This class does this recursion unrolling
+ * and stores the computation rules as simple loops with pre-computed indirection arrays.</p>
+ * <p>
+ * This class maps all derivative computation into single dimension arrays that hold the
+ * value and partial derivatives. The class does not hold these arrays, which remains under
+ * the responsibility of the caller. For each combination of number of free parameters and
+ * derivation order, only one compiler is necessary, and this compiler will be used to
+ * perform computations on all arrays provided to it, which can represent hundreds or
+ * thousands of different parameters kept together with all theur partial derivatives.
+ * </p>
+ * <p>
+ * The arrays on which compilers operate contain only the partial derivatives together
+ * with the 0<sup>th</sup> derivative, i.e. the value. The partial derivatives are stored in
+ * a compiler-specific order, which can be retrieved using methods {@link
+ * #getPartialDerivativeIndex(int...) getPartialDerivativeIndex} and {@link
+ * #getPartialDerivativeOrders(int)}. The value is guaranteed to be stored as the first element
+ * (i.e. the {@link #getPartialDerivativeIndex(int...) getPartialDerivativeIndex} method returns
+ * 0 when called with 0 for all derivation orders and {@link #getPartialDerivativeOrders(int)
+ * getPartialDerivativeOrders} returns an array filled with 0 when called with 0 as the index).
+ * </p>
+ * <p>
+ * Note that the ordering changes with number of parameters and derivation order. For example
+ * given 2 parameters x and y, df/dy is stored at index 2 when derivation order is set to 1 (in
+ * this case the array has three elements: f, df/dx and df/dy). If derivation order is set to
+ * 2, then df/dy will be stored at index 3 (in this case the array has six elements: f, df/dx,
+ * df/dxdx, df/dy, df/dxdy and df/dydy).
+ * </p>
+ * <p>
+ * Given this structure, users can perform some simple operations like adding, subtracting
+ * or multiplying constants and negating the elements by themselves, knowing if they want to
+ * mutate their array or create a new array. These simple operations are not provided by
+ * the compiler. The compiler provides only the more complex operations between several arrays.
+ * </p>
+ * <p>This class is mainly used as the engine for scalar variable {@link DerivativeStructure}.
+ * It can also be used directly to hold several variables in arrays for more complex data
+ * structures. User can for example store a vector of n variables depending on three x, y
+ * and z free parameters in one array as follows:</p> <pre>
+ * // parameter 0 is x, parameter 1 is y, parameter 2 is z
+ * int parameters = 3;
+ * DSCompiler compiler = DSCompiler.getCompiler(parameters, order);
+ * int size = compiler.getSize();
+ *
+ * // pack all elements in a single array
+ * double[] array = new double[n * size];
+ * for (int i = 0; i &lt; n; ++i) {
+ *
+ * // we know value is guaranteed to be the first element
+ * array[i * size] = v[i];
+ *
+ * // we don't know where first derivatives are stored, so we ask the compiler
+ * array[i * size + compiler.getPartialDerivativeIndex(1, 0, 0) = dvOnDx[i][0];
+ * array[i * size + compiler.getPartialDerivativeIndex(0, 1, 0) = dvOnDy[i][0];
+ * array[i * size + compiler.getPartialDerivativeIndex(0, 0, 1) = dvOnDz[i][0];
+ *
+ * // we let all higher order derivatives set to 0
+ *
+ * }
+ * </pre>
+ * <p>Then in another function, user can perform some operations on all elements stored
+ * in the single array, such as a simple product of all variables:</p> <pre>
+ * // compute the product of all elements
+ * double[] product = new double[size];
+ * prod[0] = 1.0;
+ * for (int i = 0; i &lt; n; ++i) {
+ * double[] tmp = product.clone();
+ * compiler.multiply(tmp, 0, array, i * size, product, 0);
+ * }
+ *
+ * // value
+ * double p = product[0];
+ *
+ * // first derivatives
+ * double dPdX = product[compiler.getPartialDerivativeIndex(1, 0, 0)];
+ * double dPdY = product[compiler.getPartialDerivativeIndex(0, 1, 0)];
+ * double dPdZ = product[compiler.getPartialDerivativeIndex(0, 0, 1)];
+ *
+ * // cross derivatives (assuming order was at least 2)
+ * double dPdXdX = product[compiler.getPartialDerivativeIndex(2, 0, 0)];
+ * double dPdXdY = product[compiler.getPartialDerivativeIndex(1, 1, 0)];
+ * double dPdXdZ = product[compiler.getPartialDerivativeIndex(1, 0, 1)];
+ * double dPdYdY = product[compiler.getPartialDerivativeIndex(0, 2, 0)];
+ * double dPdYdZ = product[compiler.getPartialDerivativeIndex(0, 1, 1)];
+ * double dPdZdZ = product[compiler.getPartialDerivativeIndex(0, 0, 2)];
+ * </pre>
+ * @see DerivativeStructure
+ * @since 3.1
+ */
+public class DSCompiler {
+
+ /** Array of all compilers created so far. */
+ private static AtomicReference<DSCompiler[][]> compilers =
+ new AtomicReference<DSCompiler[][]>(null);
+
+ /** Number of free parameters. */
+ private final int parameters;
+
+ /** Derivation order. */
+ private final int order;
+
+ /** Number of partial derivatives (including the single 0 order derivative element). */
+ private final int[][] sizes;
+
+ /** Indirection array for partial derivatives. */
+ private final int[][] derivativesIndirection;
+
+ /** Indirection array of the lower derivative elements. */
+ private final int[] lowerIndirection;
+
+ /** Indirection arrays for multiplication. */
+ private final int[][][] multIndirection;
+
+ /** Indirection arrays for function composition. */
+ private final int[][][] compIndirection;
+
+ /** Private constructor, reserved for the factory method {@link #getCompiler(int, int)}.
+ * @param parameters number of free parameters
+ * @param order derivation order
+ * @param valueCompiler compiler for the value part
+ * @param derivativeCompiler compiler for the derivative part
+ * @throws NumberIsTooLargeException if order is too large
+ */
+ private DSCompiler(final int parameters, final int order,
+ final DSCompiler valueCompiler, final DSCompiler derivativeCompiler)
+ throws NumberIsTooLargeException {
+
+ this.parameters = parameters;
+ this.order = order;
+ this.sizes = compileSizes(parameters, order, valueCompiler);
+ this.derivativesIndirection =
+ compileDerivativesIndirection(parameters, order,
+ valueCompiler, derivativeCompiler);
+ this.lowerIndirection =
+ compileLowerIndirection(parameters, order,
+ valueCompiler, derivativeCompiler);
+ this.multIndirection =
+ compileMultiplicationIndirection(parameters, order,
+ valueCompiler, derivativeCompiler, lowerIndirection);
+ this.compIndirection =
+ compileCompositionIndirection(parameters, order,
+ valueCompiler, derivativeCompiler,
+ sizes, derivativesIndirection);
+
+ }
+
+ /** Get the compiler for number of free parameters and order.
+ * @param parameters number of free parameters
+ * @param order derivation order
+ * @return cached rules set
+ * @throws NumberIsTooLargeException if order is too large
+ */
+ public static DSCompiler getCompiler(int parameters, int order)
+ throws NumberIsTooLargeException {
+
+ // get the cached compilers
+ final DSCompiler[][] cache = compilers.get();
+ if (cache != null && cache.length > parameters &&
+ cache[parameters].length > order && cache[parameters][order] != null) {
+ // the compiler has already been created
+ return cache[parameters][order];
+ }
+
+ // we need to create more compilers
+ final int maxParameters = FastMath.max(parameters, cache == null ? 0 : cache.length);
+ final int maxOrder = FastMath.max(order, cache == null ? 0 : cache[0].length);
+ final DSCompiler[][] newCache = new DSCompiler[maxParameters + 1][maxOrder + 1];
+
+ if (cache != null) {
+ // preserve the already created compilers
+ for (int i = 0; i < cache.length; ++i) {
+ System.arraycopy(cache[i], 0, newCache[i], 0, cache[i].length);
+ }
+ }
+
+ // create the array in increasing diagonal order
+ for (int diag = 0; diag <= parameters + order; ++diag) {
+ for (int o = FastMath.max(0, diag - parameters); o <= FastMath.min(order, diag); ++o) {
+ final int p = diag - o;
+ if (newCache[p][o] == null) {
+ final DSCompiler valueCompiler = (p == 0) ? null : newCache[p - 1][o];
+ final DSCompiler derivativeCompiler = (o == 0) ? null : newCache[p][o - 1];
+ newCache[p][o] = new DSCompiler(p, o, valueCompiler, derivativeCompiler);
+ }
+ }
+ }
+
+ // atomically reset the cached compilers array
+ compilers.compareAndSet(cache, newCache);
+
+ return newCache[parameters][order];
+
+ }
+
+ /** Compile the sizes array.
+ * @param parameters number of free parameters
+ * @param order derivation order
+ * @param valueCompiler compiler for the value part
+ * @return sizes array
+ */
+ private static int[][] compileSizes(final int parameters, final int order,
+ final DSCompiler valueCompiler) {
+
+ final int[][] sizes = new int[parameters + 1][order + 1];
+ if (parameters == 0) {
+ Arrays.fill(sizes[0], 1);
+ } else {
+ System.arraycopy(valueCompiler.sizes, 0, sizes, 0, parameters);
+ sizes[parameters][0] = 1;
+ for (int i = 0; i < order; ++i) {
+ sizes[parameters][i + 1] = sizes[parameters][i] + sizes[parameters - 1][i + 1];
+ }
+ }
+
+ return sizes;
+
+ }
+
+ /** Compile the derivatives indirection array.
+ * @param parameters number of free parameters
+ * @param order derivation order
+ * @param valueCompiler compiler for the value part
+ * @param derivativeCompiler compiler for the derivative part
+ * @return derivatives indirection array
+ */
+ private static int[][] compileDerivativesIndirection(final int parameters, final int order,
+ final DSCompiler valueCompiler,
+ final DSCompiler derivativeCompiler) {
+
+ if (parameters == 0 || order == 0) {
+ return new int[1][parameters];
+ }
+
+ final int vSize = valueCompiler.derivativesIndirection.length;
+ final int dSize = derivativeCompiler.derivativesIndirection.length;
+ final int[][] derivativesIndirection = new int[vSize + dSize][parameters];
+
+ // set up the indices for the value part
+ for (int i = 0; i < vSize; ++i) {
+ // copy the first indices, the last one remaining set to 0
+ System.arraycopy(valueCompiler.derivativesIndirection[i], 0,
+ derivativesIndirection[i], 0,
+ parameters - 1);
+ }
+
+ // set up the indices for the derivative part
+ for (int i = 0; i < dSize; ++i) {
+
+ // copy the indices
+ System.arraycopy(derivativeCompiler.derivativesIndirection[i], 0,
+ derivativesIndirection[vSize + i], 0,
+ parameters);
+
+ // increment the derivation order for the last parameter
+ derivativesIndirection[vSize + i][parameters - 1]++;
+
+ }
+
+ return derivativesIndirection;
+
+ }
+
+ /** Compile the lower derivatives indirection array.
+ * <p>
+ * This indirection array contains the indices of all elements
+ * except derivatives for last derivation order.
+ * </p>
+ * @param parameters number of free parameters
+ * @param order derivation order
+ * @param valueCompiler compiler for the value part
+ * @param derivativeCompiler compiler for the derivative part
+ * @return lower derivatives indirection array
+ */
+ private static int[] compileLowerIndirection(final int parameters, final int order,
+ final DSCompiler valueCompiler,
+ final DSCompiler derivativeCompiler) {
+
+ if (parameters == 0 || order <= 1) {
+ return new int[] { 0 };
+ }
+
+ // this is an implementation of definition 6 in Dan Kalman's paper.
+ final int vSize = valueCompiler.lowerIndirection.length;
+ final int dSize = derivativeCompiler.lowerIndirection.length;
+ final int[] lowerIndirection = new int[vSize + dSize];
+ System.arraycopy(valueCompiler.lowerIndirection, 0, lowerIndirection, 0, vSize);
+ for (int i = 0; i < dSize; ++i) {
+ lowerIndirection[vSize + i] = valueCompiler.getSize() + derivativeCompiler.lowerIndirection[i];
+ }
+
+ return lowerIndirection;
+
+ }
+
+ /** Compile the multiplication indirection array.
+ * <p>
+ * This indirection array contains the indices of all pairs of elements
+ * involved when computing a multiplication. This allows a straightforward
+ * loop-based multiplication (see {@link #multiply(double[], int, double[], int, double[], int)}).
+ * </p>
+ * @param parameters number of free parameters
+ * @param order derivation order
+ * @param valueCompiler compiler for the value part
+ * @param derivativeCompiler compiler for the derivative part
+ * @param lowerIndirection lower derivatives indirection array
+ * @return multiplication indirection array
+ */
+ private static int[][][] compileMultiplicationIndirection(final int parameters, final int order,
+ final DSCompiler valueCompiler,
+ final DSCompiler derivativeCompiler,
+ final int[] lowerIndirection) {
+
+ if ((parameters == 0) || (order == 0)) {
+ return new int[][][] { { { 1, 0, 0 } } };
+ }
+
+ // this is an implementation of definition 3 in Dan Kalman's paper.
+ final int vSize = valueCompiler.multIndirection.length;
+ final int dSize = derivativeCompiler.multIndirection.length;
+ final int[][][] multIndirection = new int[vSize + dSize][][];
+
+ System.arraycopy(valueCompiler.multIndirection, 0, multIndirection, 0, vSize);
+
+ for (int i = 0; i < dSize; ++i) {
+ final int[][] dRow = derivativeCompiler.multIndirection[i];
+ List<int[]> row = new ArrayList<int[]>(dRow.length * 2);
+ for (int j = 0; j < dRow.length; ++j) {
+ row.add(new int[] { dRow[j][0], lowerIndirection[dRow[j][1]], vSize + dRow[j][2] });
+ row.add(new int[] { dRow[j][0], vSize + dRow[j][1], lowerIndirection[dRow[j][2]] });
+ }
+
+ // combine terms with similar derivation orders
+ final List<int[]> combined = new ArrayList<int[]>(row.size());
+ for (int j = 0; j < row.size(); ++j) {
+ final int[] termJ = row.get(j);
+ if (termJ[0] > 0) {
+ for (int k = j + 1; k < row.size(); ++k) {
+ final int[] termK = row.get(k);
+ if (termJ[1] == termK[1] && termJ[2] == termK[2]) {
+ // combine termJ and termK
+ termJ[0] += termK[0];
+ // make sure we will skip termK later on in the outer loop
+ termK[0] = 0;
+ }
+ }
+ combined.add(termJ);
+ }
+ }
+
+ multIndirection[vSize + i] = combined.toArray(new int[combined.size()][]);
+
+ }
+
+ return multIndirection;
+
+ }
+
+ /** Compile the function composition indirection array.
+ * <p>
+ * This indirection array contains the indices of all sets of elements
+ * involved when computing a composition. This allows a straightforward
+ * loop-based composition (see {@link #compose(double[], int, double[], double[], int)}).
+ * </p>
+ * @param parameters number of free parameters
+ * @param order derivation order
+ * @param valueCompiler compiler for the value part
+ * @param derivativeCompiler compiler for the derivative part
+ * @param sizes sizes array
+ * @param derivativesIndirection derivatives indirection array
+ * @return multiplication indirection array
+ * @throws NumberIsTooLargeException if order is too large
+ */
+ private static int[][][] compileCompositionIndirection(final int parameters, final int order,
+ final DSCompiler valueCompiler,
+ final DSCompiler derivativeCompiler,
+ final int[][] sizes,
+ final int[][] derivativesIndirection)
+ throws NumberIsTooLargeException {
+
+ if ((parameters == 0) || (order == 0)) {
+ return new int[][][] { { { 1, 0 } } };
+ }
+
+ final int vSize = valueCompiler.compIndirection.length;
+ final int dSize = derivativeCompiler.compIndirection.length;
+ final int[][][] compIndirection = new int[vSize + dSize][][];
+
+ // the composition rules from the value part can be reused as is
+ System.arraycopy(valueCompiler.compIndirection, 0, compIndirection, 0, vSize);
+
+ // the composition rules for the derivative part are deduced by
+ // differentiation the rules from the underlying compiler once
+ // with respect to the parameter this compiler handles and the
+ // underlying one did not handle
+ for (int i = 0; i < dSize; ++i) {
+ List<int[]> row = new ArrayList<int[]>();
+ for (int[] term : derivativeCompiler.compIndirection[i]) {
+
+ // handle term p * f_k(g(x)) * g_l1(x) * g_l2(x) * ... * g_lp(x)
+
+ // derive the first factor in the term: f_k with respect to new parameter
+ int[] derivedTermF = new int[term.length + 1];
+ derivedTermF[0] = term[0]; // p
+ derivedTermF[1] = term[1] + 1; // f_(k+1)
+ int[] orders = new int[parameters];
+ orders[parameters - 1] = 1;
+ derivedTermF[term.length] = getPartialDerivativeIndex(parameters, order, sizes, orders); // g_1
+ for (int j = 2; j < term.length; ++j) {
+ // convert the indices as the mapping for the current order
+ // is different from the mapping with one less order
+ derivedTermF[j] = convertIndex(term[j], parameters,
+ derivativeCompiler.derivativesIndirection,
+ parameters, order, sizes);
+ }
+ Arrays.sort(derivedTermF, 2, derivedTermF.length);
+ row.add(derivedTermF);
+
+ // derive the various g_l
+ for (int l = 2; l < term.length; ++l) {
+ int[] derivedTermG = new int[term.length];
+ derivedTermG[0] = term[0];
+ derivedTermG[1] = term[1];
+ for (int j = 2; j < term.length; ++j) {
+ // convert the indices as the mapping for the current order
+ // is different from the mapping with one less order
+ derivedTermG[j] = convertIndex(term[j], parameters,
+ derivativeCompiler.derivativesIndirection,
+ parameters, order, sizes);
+ if (j == l) {
+ // derive this term
+ System.arraycopy(derivativesIndirection[derivedTermG[j]], 0, orders, 0, parameters);
+ orders[parameters - 1]++;
+ derivedTermG[j] = getPartialDerivativeIndex(parameters, order, sizes, orders);
+ }
+ }
+ Arrays.sort(derivedTermG, 2, derivedTermG.length);
+ row.add(derivedTermG);
+ }
+
+ }
+
+ // combine terms with similar derivation orders
+ final List<int[]> combined = new ArrayList<int[]>(row.size());
+ for (int j = 0; j < row.size(); ++j) {
+ final int[] termJ = row.get(j);
+ if (termJ[0] > 0) {
+ for (int k = j + 1; k < row.size(); ++k) {
+ final int[] termK = row.get(k);
+ boolean equals = termJ.length == termK.length;
+ for (int l = 1; equals && l < termJ.length; ++l) {
+ equals &= termJ[l] == termK[l];
+ }
+ if (equals) {
+ // combine termJ and termK
+ termJ[0] += termK[0];
+ // make sure we will skip termK later on in the outer loop
+ termK[0] = 0;
+ }
+ }
+ combined.add(termJ);
+ }
+ }
+
+ compIndirection[vSize + i] = combined.toArray(new int[combined.size()][]);
+
+ }
+
+ return compIndirection;
+
+ }
+
+ /** Get the index of a partial derivative in the array.
+ * <p>
+ * If all orders are set to 0, then the 0<sup>th</sup> order derivative
+ * is returned, which is the value of the function.
+ * </p>
+ * <p>The indices of derivatives are between 0 and {@link #getSize() getSize()} - 1.
+ * Their specific order is fixed for a given compiler, but otherwise not
+ * publicly specified. There are however some simple cases which have guaranteed
+ * indices:
+ * </p>
+ * <ul>
+ * <li>the index of 0<sup>th</sup> order derivative is always 0</li>
+ * <li>if there is only 1 {@link #getFreeParameters() free parameter}, then the
+ * derivatives are sorted in increasing derivation order (i.e. f at index 0, df/dp
+ * at index 1, d<sup>2</sup>f/dp<sup>2</sup> at index 2 ...
+ * d<sup>k</sup>f/dp<sup>k</sup> at index k),</li>
+ * <li>if the {@link #getOrder() derivation order} is 1, then the derivatives
+ * are sorted in increasing free parameter order (i.e. f at index 0, df/dx<sub>1</sub>
+ * at index 1, df/dx<sub>2</sub> at index 2 ... df/dx<sub>k</sub> at index k),</li>
+ * <li>all other cases are not publicly specified</li>
+ * </ul>
+ * <p>
+ * This method is the inverse of method {@link #getPartialDerivativeOrders(int)}
+ * </p>
+ * @param orders derivation orders with respect to each parameter
+ * @return index of the partial derivative
+ * @exception DimensionMismatchException if the numbers of parameters does not
+ * match the instance
+ * @exception NumberIsTooLargeException if sum of derivation orders is larger
+ * than the instance limits
+ * @see #getPartialDerivativeOrders(int)
+ */
+ public int getPartialDerivativeIndex(final int ... orders)
+ throws DimensionMismatchException, NumberIsTooLargeException {
+
+ // safety check
+ if (orders.length != getFreeParameters()) {
+ throw new DimensionMismatchException(orders.length, getFreeParameters());
+ }
+
+ return getPartialDerivativeIndex(parameters, order, sizes, orders);
+
+ }
+
+ /** Get the index of a partial derivative in an array.
+ * @param parameters number of free parameters
+ * @param order derivation order
+ * @param sizes sizes array
+ * @param orders derivation orders with respect to each parameter
+ * (the lenght of this array must match the number of parameters)
+ * @return index of the partial derivative
+ * @exception NumberIsTooLargeException if sum of derivation orders is larger
+ * than the instance limits
+ */
+ private static int getPartialDerivativeIndex(final int parameters, final int order,
+ final int[][] sizes, final int ... orders)
+ throws NumberIsTooLargeException {
+
+ // the value is obtained by diving into the recursive Dan Kalman's structure
+ // this is theorem 2 of his paper, with recursion replaced by iteration
+ int index = 0;
+ int m = order;
+ int ordersSum = 0;
+ for (int i = parameters - 1; i >= 0; --i) {
+
+ // derivative order for current free parameter
+ int derivativeOrder = orders[i];
+
+ // safety check
+ ordersSum += derivativeOrder;
+ if (ordersSum > order) {
+ throw new NumberIsTooLargeException(ordersSum, order, true);
+ }
+
+ while (derivativeOrder-- > 0) {
+ // as long as we differentiate according to current free parameter,
+ // we have to skip the value part and dive into the derivative part
+ // so we add the size of the value part to the base index
+ index += sizes[i][m--];
+ }
+
+ }
+
+ return index;
+
+ }
+
+ /** Convert an index from one (parameters, order) structure to another.
+ * @param index index of a partial derivative in source derivative structure
+ * @param srcP number of free parameters in source derivative structure
+ * @param srcDerivativesIndirection derivatives indirection array for the source
+ * derivative structure
+ * @param destP number of free parameters in destination derivative structure
+ * @param destO derivation order in destination derivative structure
+ * @param destSizes sizes array for the destination derivative structure
+ * @return index of the partial derivative with the <em>same</em> characteristics
+ * in destination derivative structure
+ * @throws NumberIsTooLargeException if order is too large
+ */
+ private static int convertIndex(final int index,
+ final int srcP, final int[][] srcDerivativesIndirection,
+ final int destP, final int destO, final int[][] destSizes)
+ throws NumberIsTooLargeException {
+ int[] orders = new int[destP];
+ System.arraycopy(srcDerivativesIndirection[index], 0, orders, 0, FastMath.min(srcP, destP));
+ return getPartialDerivativeIndex(destP, destO, destSizes, orders);
+ }
+
+ /** Get the derivation orders for a specific index in the array.
+ * <p>
+ * This method is the inverse of {@link #getPartialDerivativeIndex(int...)}.
+ * </p>
+ * @param index of the partial derivative
+ * @return orders derivation orders with respect to each parameter
+ * @see #getPartialDerivativeIndex(int...)
+ */
+ public int[] getPartialDerivativeOrders(final int index) {
+ return derivativesIndirection[index];
+ }
+
+ /** Get the number of free parameters.
+ * @return number of free parameters
+ */
+ public int getFreeParameters() {
+ return parameters;
+ }
+
+ /** Get the derivation order.
+ * @return derivation order
+ */
+ public int getOrder() {
+ return order;
+ }
+
+ /** Get the array size required for holding partial derivatives data.
+ * <p>
+ * This number includes the single 0 order derivative element, which is
+ * guaranteed to be stored in the first element of the array.
+ * </p>
+ * @return array size required for holding partial derivatives data
+ */
+ public int getSize() {
+ return sizes[parameters][order];
+ }
+
+ /** Compute linear combination.
+ * The derivative structure built will be a1 * ds1 + a2 * ds2
+ * @param a1 first scale factor
+ * @param c1 first base (unscaled) component
+ * @param offset1 offset of first operand in its array
+ * @param a2 second scale factor
+ * @param c2 second base (unscaled) component
+ * @param offset2 offset of second operand in its array
+ * @param result array where result must be stored (it may be
+ * one of the input arrays)
+ * @param resultOffset offset of the result in its array
+ */
+ public void linearCombination(final double a1, final double[] c1, final int offset1,
+ final double a2, final double[] c2, final int offset2,
+ final double[] result, final int resultOffset) {
+ for (int i = 0; i < getSize(); ++i) {
+ result[resultOffset + i] =
+ MathArrays.linearCombination(a1, c1[offset1 + i], a2, c2[offset2 + i]);
+ }
+ }
+
+ /** Compute linear combination.
+ * The derivative structure built will be a1 * ds1 + a2 * ds2 + a3 * ds3 + a4 * ds4
+ * @param a1 first scale factor
+ * @param c1 first base (unscaled) component
+ * @param offset1 offset of first operand in its array
+ * @param a2 second scale factor
+ * @param c2 second base (unscaled) component
+ * @param offset2 offset of second operand in its array
+ * @param a3 third scale factor
+ * @param c3 third base (unscaled) component
+ * @param offset3 offset of third operand in its array
+ * @param result array where result must be stored (it may be
+ * one of the input arrays)
+ * @param resultOffset offset of the result in its array
+ */
+ public void linearCombination(final double a1, final double[] c1, final int offset1,
+ final double a2, final double[] c2, final int offset2,
+ final double a3, final double[] c3, final int offset3,
+ final double[] result, final int resultOffset) {
+ for (int i = 0; i < getSize(); ++i) {
+ result[resultOffset + i] =
+ MathArrays.linearCombination(a1, c1[offset1 + i],
+ a2, c2[offset2 + i],
+ a3, c3[offset3 + i]);
+ }
+ }
+
+ /** Compute linear combination.
+ * The derivative structure built will be a1 * ds1 + a2 * ds2 + a3 * ds3 + a4 * ds4
+ * @param a1 first scale factor
+ * @param c1 first base (unscaled) component
+ * @param offset1 offset of first operand in its array
+ * @param a2 second scale factor
+ * @param c2 second base (unscaled) component
+ * @param offset2 offset of second operand in its array
+ * @param a3 third scale factor
+ * @param c3 third base (unscaled) component
+ * @param offset3 offset of third operand in its array
+ * @param a4 fourth scale factor
+ * @param c4 fourth base (unscaled) component
+ * @param offset4 offset of fourth operand in its array
+ * @param result array where result must be stored (it may be
+ * one of the input arrays)
+ * @param resultOffset offset of the result in its array
+ */
+ public void linearCombination(final double a1, final double[] c1, final int offset1,
+ final double a2, final double[] c2, final int offset2,
+ final double a3, final double[] c3, final int offset3,
+ final double a4, final double[] c4, final int offset4,
+ final double[] result, final int resultOffset) {
+ for (int i = 0; i < getSize(); ++i) {
+ result[resultOffset + i] =
+ MathArrays.linearCombination(a1, c1[offset1 + i],
+ a2, c2[offset2 + i],
+ a3, c3[offset3 + i],
+ a4, c4[offset4 + i]);
+ }
+ }
+
+ /** Perform addition of two derivative structures.
+ * @param lhs array holding left hand side of addition
+ * @param lhsOffset offset of the left hand side in its array
+ * @param rhs array right hand side of addition
+ * @param rhsOffset offset of the right hand side in its array
+ * @param result array where result must be stored (it may be
+ * one of the input arrays)
+ * @param resultOffset offset of the result in its array
+ */
+ public void add(final double[] lhs, final int lhsOffset,
+ final double[] rhs, final int rhsOffset,
+ final double[] result, final int resultOffset) {
+ for (int i = 0; i < getSize(); ++i) {
+ result[resultOffset + i] = lhs[lhsOffset + i] + rhs[rhsOffset + i];
+ }
+ }
+ /** Perform subtraction of two derivative structures.
+ * @param lhs array holding left hand side of subtraction
+ * @param lhsOffset offset of the left hand side in its array
+ * @param rhs array right hand side of subtraction
+ * @param rhsOffset offset of the right hand side in its array
+ * @param result array where result must be stored (it may be
+ * one of the input arrays)
+ * @param resultOffset offset of the result in its array
+ */
+ public void subtract(final double[] lhs, final int lhsOffset,
+ final double[] rhs, final int rhsOffset,
+ final double[] result, final int resultOffset) {
+ for (int i = 0; i < getSize(); ++i) {
+ result[resultOffset + i] = lhs[lhsOffset + i] - rhs[rhsOffset + i];
+ }
+ }
+
+ /** Perform multiplication of two derivative structures.
+ * @param lhs array holding left hand side of multiplication
+ * @param lhsOffset offset of the left hand side in its array
+ * @param rhs array right hand side of multiplication
+ * @param rhsOffset offset of the right hand side in its array
+ * @param result array where result must be stored (for
+ * multiplication the result array <em>cannot</em> be one of
+ * the input arrays)
+ * @param resultOffset offset of the result in its array
+ */
+ public void multiply(final double[] lhs, final int lhsOffset,
+ final double[] rhs, final int rhsOffset,
+ final double[] result, final int resultOffset) {
+ for (int i = 0; i < multIndirection.length; ++i) {
+ final int[][] mappingI = multIndirection[i];
+ double r = 0;
+ for (int j = 0; j < mappingI.length; ++j) {
+ r += mappingI[j][0] *
+ lhs[lhsOffset + mappingI[j][1]] *
+ rhs[rhsOffset + mappingI[j][2]];
+ }
+ result[resultOffset + i] = r;
+ }
+ }
+
+ /** Perform division of two derivative structures.
+ * @param lhs array holding left hand side of division
+ * @param lhsOffset offset of the left hand side in its array
+ * @param rhs array right hand side of division
+ * @param rhsOffset offset of the right hand side in its array
+ * @param result array where result must be stored (for
+ * division the result array <em>cannot</em> be one of
+ * the input arrays)
+ * @param resultOffset offset of the result in its array
+ */
+ public void divide(final double[] lhs, final int lhsOffset,
+ final double[] rhs, final int rhsOffset,
+ final double[] result, final int resultOffset) {
+ final double[] reciprocal = new double[getSize()];
+ pow(rhs, lhsOffset, -1, reciprocal, 0);
+ multiply(lhs, lhsOffset, reciprocal, 0, result, resultOffset);
+ }
+
+ /** Perform remainder of two derivative structures.
+ * @param lhs array holding left hand side of remainder
+ * @param lhsOffset offset of the left hand side in its array
+ * @param rhs array right hand side of remainder
+ * @param rhsOffset offset of the right hand side in its array
+ * @param result array where result must be stored (it may be
+ * one of the input arrays)
+ * @param resultOffset offset of the result in its array
+ */
+ public void remainder(final double[] lhs, final int lhsOffset,
+ final double[] rhs, final int rhsOffset,
+ final double[] result, final int resultOffset) {
+
+ // compute k such that lhs % rhs = lhs - k rhs
+ final double rem = FastMath.IEEEremainder(lhs[lhsOffset], rhs[rhsOffset]);
+ final double k = FastMath.rint((lhs[lhsOffset] - rem) / rhs[rhsOffset]);
+
+ // set up value
+ result[resultOffset] = rem;
+
+ // set up partial derivatives
+ for (int i = 1; i < getSize(); ++i) {
+ result[resultOffset + i] = lhs[lhsOffset + i] - k * rhs[rhsOffset + i];
+ }
+
+ }
+
+ /** Compute power of a double to a derivative structure.
+ * @param a number to exponentiate
+ * @param operand array holding the power
+ * @param operandOffset offset of the power in its array
+ * @param result array where result must be stored (for
+ * power the result array <em>cannot</em> be the input
+ * array)
+ * @param resultOffset offset of the result in its array
+ * @since 3.3
+ */
+ public void pow(final double a,
+ final double[] operand, final int operandOffset,
+ final double[] result, final int resultOffset) {
+
+ // create the function value and derivatives
+ // [a^x, ln(a) a^x, ln(a)^2 a^x,, ln(a)^3 a^x, ... ]
+ final double[] function = new double[1 + order];
+ if (a == 0) {
+ if (operand[operandOffset] == 0) {
+ function[0] = 1;
+ double infinity = Double.POSITIVE_INFINITY;
+ for (int i = 1; i < function.length; ++i) {
+ infinity = -infinity;
+ function[i] = infinity;
+ }
+ } else if (operand[operandOffset] < 0) {
+ Arrays.fill(function, Double.NaN);
+ }
+ } else {
+ function[0] = FastMath.pow(a, operand[operandOffset]);
+ final double lnA = FastMath.log(a);
+ for (int i = 1; i < function.length; ++i) {
+ function[i] = lnA * function[i - 1];
+ }
+ }
+
+
+ // apply function composition
+ compose(operand, operandOffset, function, result, resultOffset);
+
+ }
+
+ /** Compute power of a derivative structure.
+ * @param operand array holding the operand
+ * @param operandOffset offset of the operand in its array
+ * @param p power to apply
+ * @param result array where result must be stored (for
+ * power the result array <em>cannot</em> be the input
+ * array)
+ * @param resultOffset offset of the result in its array
+ */
+ public void pow(final double[] operand, final int operandOffset, final double p,
+ final double[] result, final int resultOffset) {
+
+ // create the function value and derivatives
+ // [x^p, px^(p-1), p(p-1)x^(p-2), ... ]
+ double[] function = new double[1 + order];
+ double xk = FastMath.pow(operand[operandOffset], p - order);
+ for (int i = order; i > 0; --i) {
+ function[i] = xk;
+ xk *= operand[operandOffset];
+ }
+ function[0] = xk;
+ double coefficient = p;
+ for (int i = 1; i <= order; ++i) {
+ function[i] *= coefficient;
+ coefficient *= p - i;
+ }
+
+ // apply function composition
+ compose(operand, operandOffset, function, result, resultOffset);
+
+ }
+
+ /** Compute integer power of a derivative structure.
+ * @param operand array holding the operand
+ * @param operandOffset offset of the operand in its array
+ * @param n power to apply
+ * @param result array where result must be stored (for
+ * power the result array <em>cannot</em> be the input
+ * array)
+ * @param resultOffset offset of the result in its array
+ */
+ public void pow(final double[] operand, final int operandOffset, final int n,
+ final double[] result, final int resultOffset) {
+
+ if (n == 0) {
+ // special case, x^0 = 1 for all x
+ result[resultOffset] = 1.0;
+ Arrays.fill(result, resultOffset + 1, resultOffset + getSize(), 0);
+ return;
+ }
+
+ // create the power function value and derivatives
+ // [x^n, nx^(n-1), n(n-1)x^(n-2), ... ]
+ double[] function = new double[1 + order];
+
+ if (n > 0) {
+ // strictly positive power
+ final int maxOrder = FastMath.min(order, n);
+ double xk = FastMath.pow(operand[operandOffset], n - maxOrder);
+ for (int i = maxOrder; i > 0; --i) {
+ function[i] = xk;
+ xk *= operand[operandOffset];
+ }
+ function[0] = xk;
+ } else {
+ // strictly negative power
+ final double inv = 1.0 / operand[operandOffset];
+ double xk = FastMath.pow(inv, -n);
+ for (int i = 0; i <= order; ++i) {
+ function[i] = xk;
+ xk *= inv;
+ }
+ }
+
+ double coefficient = n;
+ for (int i = 1; i <= order; ++i) {
+ function[i] *= coefficient;
+ coefficient *= n - i;
+ }
+
+ // apply function composition
+ compose(operand, operandOffset, function, result, resultOffset);
+
+ }
+
+ /** Compute power of a derivative structure.
+ * @param x array holding the base
+ * @param xOffset offset of the base in its array
+ * @param y array holding the exponent
+ * @param yOffset offset of the exponent in its array
+ * @param result array where result must be stored (for
+ * power the result array <em>cannot</em> be the input
+ * array)
+ * @param resultOffset offset of the result in its array
+ */
+ public void pow(final double[] x, final int xOffset,
+ final double[] y, final int yOffset,
+ final double[] result, final int resultOffset) {
+ final double[] logX = new double[getSize()];
+ log(x, xOffset, logX, 0);
+ final double[] yLogX = new double[getSize()];
+ multiply(logX, 0, y, yOffset, yLogX, 0);
+ exp(yLogX, 0, result, resultOffset);
+ }
+
+ /** Compute n<sup>th</sup> root of a derivative structure.
+ * @param operand array holding the operand
+ * @param operandOffset offset of the operand in its array
+ * @param n order of the root
+ * @param result array where result must be stored (for
+ * n<sup>th</sup> root the result array <em>cannot</em> be the input
+ * array)
+ * @param resultOffset offset of the result in its array
+ */
+ public void rootN(final double[] operand, final int operandOffset, final int n,
+ final double[] result, final int resultOffset) {
+
+ // create the function value and derivatives
+ // [x^(1/n), (1/n)x^((1/n)-1), (1-n)/n^2x^((1/n)-2), ... ]
+ double[] function = new double[1 + order];
+ double xk;
+ if (n == 2) {
+ function[0] = FastMath.sqrt(operand[operandOffset]);
+ xk = 0.5 / function[0];
+ } else if (n == 3) {
+ function[0] = FastMath.cbrt(operand[operandOffset]);
+ xk = 1.0 / (3.0 * function[0] * function[0]);
+ } else {
+ function[0] = FastMath.pow(operand[operandOffset], 1.0 / n);
+ xk = 1.0 / (n * FastMath.pow(function[0], n - 1));
+ }
+ final double nReciprocal = 1.0 / n;
+ final double xReciprocal = 1.0 / operand[operandOffset];
+ for (int i = 1; i <= order; ++i) {
+ function[i] = xk;
+ xk *= xReciprocal * (nReciprocal - i);
+ }
+
+ // apply function composition
+ compose(operand, operandOffset, function, result, resultOffset);
+
+ }
+
+ /** Compute exponential of a derivative structure.
+ * @param operand array holding the operand
+ * @param operandOffset offset of the operand in its array
+ * @param result array where result must be stored (for
+ * exponential the result array <em>cannot</em> be the input
+ * array)
+ * @param resultOffset offset of the result in its array
+ */
+ public void exp(final double[] operand, final int operandOffset,
+ final double[] result, final int resultOffset) {
+
+ // create the function value and derivatives
+ double[] function = new double[1 + order];
+ Arrays.fill(function, FastMath.exp(operand[operandOffset]));
+
+ // apply function composition
+ compose(operand, operandOffset, function, result, resultOffset);
+
+ }
+
+ /** Compute exp(x) - 1 of a derivative structure.
+ * @param operand array holding the operand
+ * @param operandOffset offset of the operand in its array
+ * @param result array where result must be stored (for
+ * exponential the result array <em>cannot</em> be the input
+ * array)
+ * @param resultOffset offset of the result in its array
+ */
+ public void expm1(final double[] operand, final int operandOffset,
+ final double[] result, final int resultOffset) {
+
+ // create the function value and derivatives
+ double[] function = new double[1 + order];
+ function[0] = FastMath.expm1(operand[operandOffset]);
+ Arrays.fill(function, 1, 1 + order, FastMath.exp(operand[operandOffset]));
+
+ // apply function composition
+ compose(operand, operandOffset, function, result, resultOffset);
+
+ }
+
+ /** Compute natural logarithm of a derivative structure.
+ * @param operand array holding the operand
+ * @param operandOffset offset of the operand in its array
+ * @param result array where result must be stored (for
+ * logarithm the result array <em>cannot</em> be the input
+ * array)
+ * @param resultOffset offset of the result in its array
+ */
+ public void log(final double[] operand, final int operandOffset,
+ final double[] result, final int resultOffset) {
+
+ // create the function value and derivatives
+ double[] function = new double[1 + order];
+ function[0] = FastMath.log(operand[operandOffset]);
+ if (order > 0) {
+ double inv = 1.0 / operand[operandOffset];
+ double xk = inv;
+ for (int i = 1; i <= order; ++i) {
+ function[i] = xk;
+ xk *= -i * inv;
+ }
+ }
+
+ // apply function composition
+ compose(operand, operandOffset, function, result, resultOffset);
+
+ }
+
+ /** Computes shifted logarithm of a derivative structure.
+ * @param operand array holding the operand
+ * @param operandOffset offset of the operand in its array
+ * @param result array where result must be stored (for
+ * shifted logarithm the result array <em>cannot</em> be the input array)
+ * @param resultOffset offset of the result in its array
+ */
+ public void log1p(final double[] operand, final int operandOffset,
+ final double[] result, final int resultOffset) {
+
+ // create the function value and derivatives
+ double[] function = new double[1 + order];
+ function[0] = FastMath.log1p(operand[operandOffset]);
+ if (order > 0) {
+ double inv = 1.0 / (1.0 + operand[operandOffset]);
+ double xk = inv;
+ for (int i = 1; i <= order; ++i) {
+ function[i] = xk;
+ xk *= -i * inv;
+ }
+ }
+
+ // apply function composition
+ compose(operand, operandOffset, function, result, resultOffset);
+
+ }
+
+ /** Computes base 10 logarithm of a derivative structure.
+ * @param operand array holding the operand
+ * @param operandOffset offset of the operand in its array
+ * @param result array where result must be stored (for
+ * base 10 logarithm the result array <em>cannot</em> be the input array)
+ * @param resultOffset offset of the result in its array
+ */
+ public void log10(final double[] operand, final int operandOffset,
+ final double[] result, final int resultOffset) {
+
+ // create the function value and derivatives
+ double[] function = new double[1 + order];
+ function[0] = FastMath.log10(operand[operandOffset]);
+ if (order > 0) {
+ double inv = 1.0 / operand[operandOffset];
+ double xk = inv / FastMath.log(10.0);
+ for (int i = 1; i <= order; ++i) {
+ function[i] = xk;
+ xk *= -i * inv;
+ }
+ }
+
+ // apply function composition
+ compose(operand, operandOffset, function, result, resultOffset);
+
+ }
+
+ /** Compute cosine of a derivative structure.
+ * @param operand array holding the operand
+ * @param operandOffset offset of the operand in its array
+ * @param result array where result must be stored (for
+ * cosine the result array <em>cannot</em> be the input
+ * array)
+ * @param resultOffset offset of the result in its array
+ */
+ public void cos(final double[] operand, final int operandOffset,
+ final double[] result, final int resultOffset) {
+
+ // create the function value and derivatives
+ double[] function = new double[1 + order];
+ function[0] = FastMath.cos(operand[operandOffset]);
+ if (order > 0) {
+ function[1] = -FastMath.sin(operand[operandOffset]);
+ for (int i = 2; i <= order; ++i) {
+ function[i] = -function[i - 2];
+ }
+ }
+
+ // apply function composition
+ compose(operand, operandOffset, function, result, resultOffset);
+
+ }
+
+ /** Compute sine of a derivative structure.
+ * @param operand array holding the operand
+ * @param operandOffset offset of the operand in its array
+ * @param result array where result must be stored (for
+ * sine the result array <em>cannot</em> be the input
+ * array)
+ * @param resultOffset offset of the result in its array
+ */
+ public void sin(final double[] operand, final int operandOffset,
+ final double[] result, final int resultOffset) {
+
+ // create the function value and derivatives
+ double[] function = new double[1 + order];
+ function[0] = FastMath.sin(operand[operandOffset]);
+ if (order > 0) {
+ function[1] = FastMath.cos(operand[operandOffset]);
+ for (int i = 2; i <= order; ++i) {
+ function[i] = -function[i - 2];
+ }
+ }
+
+ // apply function composition
+ compose(operand, operandOffset, function, result, resultOffset);
+
+ }
+
+ /** Compute tangent of a derivative structure.
+ * @param operand array holding the operand
+ * @param operandOffset offset of the operand in its array
+ * @param result array where result must be stored (for
+ * tangent the result array <em>cannot</em> be the input
+ * array)
+ * @param resultOffset offset of the result in its array
+ */
+ public void tan(final double[] operand, final int operandOffset,
+ final double[] result, final int resultOffset) {
+
+ // create the function value and derivatives
+ final double[] function = new double[1 + order];
+ final double t = FastMath.tan(operand[operandOffset]);
+ function[0] = t;
+
+ if (order > 0) {
+
+ // the nth order derivative of tan has the form:
+ // dn(tan(x)/dxn = P_n(tan(x))
+ // where P_n(t) is a degree n+1 polynomial with same parity as n+1
+ // P_0(t) = t, P_1(t) = 1 + t^2, P_2(t) = 2 t (1 + t^2) ...
+ // the general recurrence relation for P_n is:
+ // P_n(x) = (1+t^2) P_(n-1)'(t)
+ // as per polynomial parity, we can store coefficients of both P_(n-1) and P_n in the same array
+ final double[] p = new double[order + 2];
+ p[1] = 1;
+ final double t2 = t * t;
+ for (int n = 1; n <= order; ++n) {
+
+ // update and evaluate polynomial P_n(t)
+ double v = 0;
+ p[n + 1] = n * p[n];
+ for (int k = n + 1; k >= 0; k -= 2) {
+ v = v * t2 + p[k];
+ if (k > 2) {
+ p[k - 2] = (k - 1) * p[k - 1] + (k - 3) * p[k - 3];
+ } else if (k == 2) {
+ p[0] = p[1];
+ }
+ }
+ if ((n & 0x1) == 0) {
+ v *= t;
+ }
+
+ function[n] = v;
+
+ }
+ }
+
+ // apply function composition
+ compose(operand, operandOffset, function, result, resultOffset);
+
+ }
+
+ /** Compute arc cosine of a derivative structure.
+ * @param operand array holding the operand
+ * @param operandOffset offset of the operand in its array
+ * @param result array where result must be stored (for
+ * arc cosine the result array <em>cannot</em> be the input
+ * array)
+ * @param resultOffset offset of the result in its array
+ */
+ public void acos(final double[] operand, final int operandOffset,
+ final double[] result, final int resultOffset) {
+
+ // create the function value and derivatives
+ double[] function = new double[1 + order];
+ final double x = operand[operandOffset];
+ function[0] = FastMath.acos(x);
+ if (order > 0) {
+ // the nth order derivative of acos has the form:
+ // dn(acos(x)/dxn = P_n(x) / [1 - x^2]^((2n-1)/2)
+ // where P_n(x) is a degree n-1 polynomial with same parity as n-1
+ // P_1(x) = -1, P_2(x) = -x, P_3(x) = -2x^2 - 1 ...
+ // the general recurrence relation for P_n is:
+ // P_n(x) = (1-x^2) P_(n-1)'(x) + (2n-3) x P_(n-1)(x)
+ // as per polynomial parity, we can store coefficients of both P_(n-1) and P_n in the same array
+ final double[] p = new double[order];
+ p[0] = -1;
+ final double x2 = x * x;
+ final double f = 1.0 / (1 - x2);
+ double coeff = FastMath.sqrt(f);
+ function[1] = coeff * p[0];
+ for (int n = 2; n <= order; ++n) {
+
+ // update and evaluate polynomial P_n(x)
+ double v = 0;
+ p[n - 1] = (n - 1) * p[n - 2];
+ for (int k = n - 1; k >= 0; k -= 2) {
+ v = v * x2 + p[k];
+ if (k > 2) {
+ p[k - 2] = (k - 1) * p[k - 1] + (2 * n - k) * p[k - 3];
+ } else if (k == 2) {
+ p[0] = p[1];
+ }
+ }
+ if ((n & 0x1) == 0) {
+ v *= x;
+ }
+
+ coeff *= f;
+ function[n] = coeff * v;
+
+ }
+ }
+
+ // apply function composition
+ compose(operand, operandOffset, function, result, resultOffset);
+
+ }
+
+ /** Compute arc sine of a derivative structure.
+ * @param operand array holding the operand
+ * @param operandOffset offset of the operand in its array
+ * @param result array where result must be stored (for
+ * arc sine the result array <em>cannot</em> be the input
+ * array)
+ * @param resultOffset offset of the result in its array
+ */
+ public void asin(final double[] operand, final int operandOffset,
+ final double[] result, final int resultOffset) {
+
+ // create the function value and derivatives
+ double[] function = new double[1 + order];
+ final double x = operand[operandOffset];
+ function[0] = FastMath.asin(x);
+ if (order > 0) {
+ // the nth order derivative of asin has the form:
+ // dn(asin(x)/dxn = P_n(x) / [1 - x^2]^((2n-1)/2)
+ // where P_n(x) is a degree n-1 polynomial with same parity as n-1
+ // P_1(x) = 1, P_2(x) = x, P_3(x) = 2x^2 + 1 ...
+ // the general recurrence relation for P_n is:
+ // P_n(x) = (1-x^2) P_(n-1)'(x) + (2n-3) x P_(n-1)(x)
+ // as per polynomial parity, we can store coefficients of both P_(n-1) and P_n in the same array
+ final double[] p = new double[order];
+ p[0] = 1;
+ final double x2 = x * x;
+ final double f = 1.0 / (1 - x2);
+ double coeff = FastMath.sqrt(f);
+ function[1] = coeff * p[0];
+ for (int n = 2; n <= order; ++n) {
+
+ // update and evaluate polynomial P_n(x)
+ double v = 0;
+ p[n - 1] = (n - 1) * p[n - 2];
+ for (int k = n - 1; k >= 0; k -= 2) {
+ v = v * x2 + p[k];
+ if (k > 2) {
+ p[k - 2] = (k - 1) * p[k - 1] + (2 * n - k) * p[k - 3];
+ } else if (k == 2) {
+ p[0] = p[1];
+ }
+ }
+ if ((n & 0x1) == 0) {
+ v *= x;
+ }
+
+ coeff *= f;
+ function[n] = coeff * v;
+
+ }
+ }
+
+ // apply function composition
+ compose(operand, operandOffset, function, result, resultOffset);
+
+ }
+
+ /** Compute arc tangent of a derivative structure.
+ * @param operand array holding the operand
+ * @param operandOffset offset of the operand in its array
+ * @param result array where result must be stored (for
+ * arc tangent the result array <em>cannot</em> be the input
+ * array)
+ * @param resultOffset offset of the result in its array
+ */
+ public void atan(final double[] operand, final int operandOffset,
+ final double[] result, final int resultOffset) {
+
+ // create the function value and derivatives
+ double[] function = new double[1 + order];
+ final double x = operand[operandOffset];
+ function[0] = FastMath.atan(x);
+ if (order > 0) {
+ // the nth order derivative of atan has the form:
+ // dn(atan(x)/dxn = Q_n(x) / (1 + x^2)^n
+ // where Q_n(x) is a degree n-1 polynomial with same parity as n-1
+ // Q_1(x) = 1, Q_2(x) = -2x, Q_3(x) = 6x^2 - 2 ...
+ // the general recurrence relation for Q_n is:
+ // Q_n(x) = (1+x^2) Q_(n-1)'(x) - 2(n-1) x Q_(n-1)(x)
+ // as per polynomial parity, we can store coefficients of both Q_(n-1) and Q_n in the same array
+ final double[] q = new double[order];
+ q[0] = 1;
+ final double x2 = x * x;
+ final double f = 1.0 / (1 + x2);
+ double coeff = f;
+ function[1] = coeff * q[0];
+ for (int n = 2; n <= order; ++n) {
+
+ // update and evaluate polynomial Q_n(x)
+ double v = 0;
+ q[n - 1] = -n * q[n - 2];
+ for (int k = n - 1; k >= 0; k -= 2) {
+ v = v * x2 + q[k];
+ if (k > 2) {
+ q[k - 2] = (k - 1) * q[k - 1] + (k - 1 - 2 * n) * q[k - 3];
+ } else if (k == 2) {
+ q[0] = q[1];
+ }
+ }
+ if ((n & 0x1) == 0) {
+ v *= x;
+ }
+
+ coeff *= f;
+ function[n] = coeff * v;
+
+ }
+ }
+
+ // apply function composition
+ compose(operand, operandOffset, function, result, resultOffset);
+
+ }
+
+ /** Compute two arguments arc tangent of a derivative structure.
+ * @param y array holding the first operand
+ * @param yOffset offset of the first operand in its array
+ * @param x array holding the second operand
+ * @param xOffset offset of the second operand in its array
+ * @param result array where result must be stored (for
+ * two arguments arc tangent the result array <em>cannot</em>
+ * be the input array)
+ * @param resultOffset offset of the result in its array
+ */
+ public void atan2(final double[] y, final int yOffset,
+ final double[] x, final int xOffset,
+ final double[] result, final int resultOffset) {
+
+ // compute r = sqrt(x^2+y^2)
+ double[] tmp1 = new double[getSize()];
+ multiply(x, xOffset, x, xOffset, tmp1, 0); // x^2
+ double[] tmp2 = new double[getSize()];
+ multiply(y, yOffset, y, yOffset, tmp2, 0); // y^2
+ add(tmp1, 0, tmp2, 0, tmp2, 0); // x^2 + y^2
+ rootN(tmp2, 0, 2, tmp1, 0); // r = sqrt(x^2 + y^2)
+
+ if (x[xOffset] >= 0) {
+
+ // compute atan2(y, x) = 2 atan(y / (r + x))
+ add(tmp1, 0, x, xOffset, tmp2, 0); // r + x
+ divide(y, yOffset, tmp2, 0, tmp1, 0); // y /(r + x)
+ atan(tmp1, 0, tmp2, 0); // atan(y / (r + x))
+ for (int i = 0; i < tmp2.length; ++i) {
+ result[resultOffset + i] = 2 * tmp2[i]; // 2 * atan(y / (r + x))
+ }
+
+ } else {
+
+ // compute atan2(y, x) = +/- pi - 2 atan(y / (r - x))
+ subtract(tmp1, 0, x, xOffset, tmp2, 0); // r - x
+ divide(y, yOffset, tmp2, 0, tmp1, 0); // y /(r - x)
+ atan(tmp1, 0, tmp2, 0); // atan(y / (r - x))
+ result[resultOffset] =
+ ((tmp2[0] <= 0) ? -FastMath.PI : FastMath.PI) - 2 * tmp2[0]; // +/-pi - 2 * atan(y / (r - x))
+ for (int i = 1; i < tmp2.length; ++i) {
+ result[resultOffset + i] = -2 * tmp2[i]; // +/-pi - 2 * atan(y / (r - x))
+ }
+
+ }
+
+ // fix value to take special cases (+0/+0, +0/-0, -0/+0, -0/-0, +/-infinity) correctly
+ result[resultOffset] = FastMath.atan2(y[yOffset], x[xOffset]);
+
+ }
+
+ /** Compute hyperbolic cosine of a derivative structure.
+ * @param operand array holding the operand
+ * @param operandOffset offset of the operand in its array
+ * @param result array where result must be stored (for
+ * hyperbolic cosine the result array <em>cannot</em> be the input
+ * array)
+ * @param resultOffset offset of the result in its array
+ */
+ public void cosh(final double[] operand, final int operandOffset,
+ final double[] result, final int resultOffset) {
+
+ // create the function value and derivatives
+ double[] function = new double[1 + order];
+ function[0] = FastMath.cosh(operand[operandOffset]);
+ if (order > 0) {
+ function[1] = FastMath.sinh(operand[operandOffset]);
+ for (int i = 2; i <= order; ++i) {
+ function[i] = function[i - 2];
+ }
+ }
+
+ // apply function composition
+ compose(operand, operandOffset, function, result, resultOffset);
+
+ }
+
+ /** Compute hyperbolic sine of a derivative structure.
+ * @param operand array holding the operand
+ * @param operandOffset offset of the operand in its array
+ * @param result array where result must be stored (for
+ * hyperbolic sine the result array <em>cannot</em> be the input
+ * array)
+ * @param resultOffset offset of the result in its array
+ */
+ public void sinh(final double[] operand, final int operandOffset,
+ final double[] result, final int resultOffset) {
+
+ // create the function value and derivatives
+ double[] function = new double[1 + order];
+ function[0] = FastMath.sinh(operand[operandOffset]);
+ if (order > 0) {
+ function[1] = FastMath.cosh(operand[operandOffset]);
+ for (int i = 2; i <= order; ++i) {
+ function[i] = function[i - 2];
+ }
+ }
+
+ // apply function composition
+ compose(operand, operandOffset, function, result, resultOffset);
+
+ }
+
+ /** Compute hyperbolic tangent of a derivative structure.
+ * @param operand array holding the operand
+ * @param operandOffset offset of the operand in its array
+ * @param result array where result must be stored (for
+ * hyperbolic tangent the result array <em>cannot</em> be the input
+ * array)
+ * @param resultOffset offset of the result in its array
+ */
+ public void tanh(final double[] operand, final int operandOffset,
+ final double[] result, final int resultOffset) {
+
+ // create the function value and derivatives
+ final double[] function = new double[1 + order];
+ final double t = FastMath.tanh(operand[operandOffset]);
+ function[0] = t;
+
+ if (order > 0) {
+
+ // the nth order derivative of tanh has the form:
+ // dn(tanh(x)/dxn = P_n(tanh(x))
+ // where P_n(t) is a degree n+1 polynomial with same parity as n+1
+ // P_0(t) = t, P_1(t) = 1 - t^2, P_2(t) = -2 t (1 - t^2) ...
+ // the general recurrence relation for P_n is:
+ // P_n(x) = (1-t^2) P_(n-1)'(t)
+ // as per polynomial parity, we can store coefficients of both P_(n-1) and P_n in the same array
+ final double[] p = new double[order + 2];
+ p[1] = 1;
+ final double t2 = t * t;
+ for (int n = 1; n <= order; ++n) {
+
+ // update and evaluate polynomial P_n(t)
+ double v = 0;
+ p[n + 1] = -n * p[n];
+ for (int k = n + 1; k >= 0; k -= 2) {
+ v = v * t2 + p[k];
+ if (k > 2) {
+ p[k - 2] = (k - 1) * p[k - 1] - (k - 3) * p[k - 3];
+ } else if (k == 2) {
+ p[0] = p[1];
+ }
+ }
+ if ((n & 0x1) == 0) {
+ v *= t;
+ }
+
+ function[n] = v;
+
+ }
+ }
+
+ // apply function composition
+ compose(operand, operandOffset, function, result, resultOffset);
+
+ }
+
+ /** Compute inverse hyperbolic cosine of a derivative structure.
+ * @param operand array holding the operand
+ * @param operandOffset offset of the operand in its array
+ * @param result array where result must be stored (for
+ * inverse hyperbolic cosine the result array <em>cannot</em> be the input
+ * array)
+ * @param resultOffset offset of the result in its array
+ */
+ public void acosh(final double[] operand, final int operandOffset,
+ final double[] result, final int resultOffset) {
+
+ // create the function value and derivatives
+ double[] function = new double[1 + order];
+ final double x = operand[operandOffset];
+ function[0] = FastMath.acosh(x);
+ if (order > 0) {
+ // the nth order derivative of acosh has the form:
+ // dn(acosh(x)/dxn = P_n(x) / [x^2 - 1]^((2n-1)/2)
+ // where P_n(x) is a degree n-1 polynomial with same parity as n-1
+ // P_1(x) = 1, P_2(x) = -x, P_3(x) = 2x^2 + 1 ...
+ // the general recurrence relation for P_n is:
+ // P_n(x) = (x^2-1) P_(n-1)'(x) - (2n-3) x P_(n-1)(x)
+ // as per polynomial parity, we can store coefficients of both P_(n-1) and P_n in the same array
+ final double[] p = new double[order];
+ p[0] = 1;
+ final double x2 = x * x;
+ final double f = 1.0 / (x2 - 1);
+ double coeff = FastMath.sqrt(f);
+ function[1] = coeff * p[0];
+ for (int n = 2; n <= order; ++n) {
+
+ // update and evaluate polynomial P_n(x)
+ double v = 0;
+ p[n - 1] = (1 - n) * p[n - 2];
+ for (int k = n - 1; k >= 0; k -= 2) {
+ v = v * x2 + p[k];
+ if (k > 2) {
+ p[k - 2] = (1 - k) * p[k - 1] + (k - 2 * n) * p[k - 3];
+ } else if (k == 2) {
+ p[0] = -p[1];
+ }
+ }
+ if ((n & 0x1) == 0) {
+ v *= x;
+ }
+
+ coeff *= f;
+ function[n] = coeff * v;
+
+ }
+ }
+
+ // apply function composition
+ compose(operand, operandOffset, function, result, resultOffset);
+
+ }
+
+ /** Compute inverse hyperbolic sine of a derivative structure.
+ * @param operand array holding the operand
+ * @param operandOffset offset of the operand in its array
+ * @param result array where result must be stored (for
+ * inverse hyperbolic sine the result array <em>cannot</em> be the input
+ * array)
+ * @param resultOffset offset of the result in its array
+ */
+ public void asinh(final double[] operand, final int operandOffset,
+ final double[] result, final int resultOffset) {
+
+ // create the function value and derivatives
+ double[] function = new double[1 + order];
+ final double x = operand[operandOffset];
+ function[0] = FastMath.asinh(x);
+ if (order > 0) {
+ // the nth order derivative of asinh has the form:
+ // dn(asinh(x)/dxn = P_n(x) / [x^2 + 1]^((2n-1)/2)
+ // where P_n(x) is a degree n-1 polynomial with same parity as n-1
+ // P_1(x) = 1, P_2(x) = -x, P_3(x) = 2x^2 - 1 ...
+ // the general recurrence relation for P_n is:
+ // P_n(x) = (x^2+1) P_(n-1)'(x) - (2n-3) x P_(n-1)(x)
+ // as per polynomial parity, we can store coefficients of both P_(n-1) and P_n in the same array
+ final double[] p = new double[order];
+ p[0] = 1;
+ final double x2 = x * x;
+ final double f = 1.0 / (1 + x2);
+ double coeff = FastMath.sqrt(f);
+ function[1] = coeff * p[0];
+ for (int n = 2; n <= order; ++n) {
+
+ // update and evaluate polynomial P_n(x)
+ double v = 0;
+ p[n - 1] = (1 - n) * p[n - 2];
+ for (int k = n - 1; k >= 0; k -= 2) {
+ v = v * x2 + p[k];
+ if (k > 2) {
+ p[k - 2] = (k - 1) * p[k - 1] + (k - 2 * n) * p[k - 3];
+ } else if (k == 2) {
+ p[0] = p[1];
+ }
+ }
+ if ((n & 0x1) == 0) {
+ v *= x;
+ }
+
+ coeff *= f;
+ function[n] = coeff * v;
+
+ }
+ }
+
+ // apply function composition
+ compose(operand, operandOffset, function, result, resultOffset);
+
+ }
+
+ /** Compute inverse hyperbolic tangent of a derivative structure.
+ * @param operand array holding the operand
+ * @param operandOffset offset of the operand in its array
+ * @param result array where result must be stored (for
+ * inverse hyperbolic tangent the result array <em>cannot</em> be the input
+ * array)
+ * @param resultOffset offset of the result in its array
+ */
+ public void atanh(final double[] operand, final int operandOffset,
+ final double[] result, final int resultOffset) {
+
+ // create the function value and derivatives
+ double[] function = new double[1 + order];
+ final double x = operand[operandOffset];
+ function[0] = FastMath.atanh(x);
+ if (order > 0) {
+ // the nth order derivative of atanh has the form:
+ // dn(atanh(x)/dxn = Q_n(x) / (1 - x^2)^n
+ // where Q_n(x) is a degree n-1 polynomial with same parity as n-1
+ // Q_1(x) = 1, Q_2(x) = 2x, Q_3(x) = 6x^2 + 2 ...
+ // the general recurrence relation for Q_n is:
+ // Q_n(x) = (1-x^2) Q_(n-1)'(x) + 2(n-1) x Q_(n-1)(x)
+ // as per polynomial parity, we can store coefficients of both Q_(n-1) and Q_n in the same array
+ final double[] q = new double[order];
+ q[0] = 1;
+ final double x2 = x * x;
+ final double f = 1.0 / (1 - x2);
+ double coeff = f;
+ function[1] = coeff * q[0];
+ for (int n = 2; n <= order; ++n) {
+
+ // update and evaluate polynomial Q_n(x)
+ double v = 0;
+ q[n - 1] = n * q[n - 2];
+ for (int k = n - 1; k >= 0; k -= 2) {
+ v = v * x2 + q[k];
+ if (k > 2) {
+ q[k - 2] = (k - 1) * q[k - 1] + (2 * n - k + 1) * q[k - 3];
+ } else if (k == 2) {
+ q[0] = q[1];
+ }
+ }
+ if ((n & 0x1) == 0) {
+ v *= x;
+ }
+
+ coeff *= f;
+ function[n] = coeff * v;
+
+ }
+ }
+
+ // apply function composition
+ compose(operand, operandOffset, function, result, resultOffset);
+
+ }
+
+ /** Compute composition of a derivative structure by a function.
+ * @param operand array holding the operand
+ * @param operandOffset offset of the operand in its array
+ * @param f array of value and derivatives of the function at
+ * the current point (i.e. at {@code operand[operandOffset]}).
+ * @param result array where result must be stored (for
+ * composition the result array <em>cannot</em> be the input
+ * array)
+ * @param resultOffset offset of the result in its array
+ */
+ public void compose(final double[] operand, final int operandOffset, final double[] f,
+ final double[] result, final int resultOffset) {
+ for (int i = 0; i < compIndirection.length; ++i) {
+ final int[][] mappingI = compIndirection[i];
+ double r = 0;
+ for (int j = 0; j < mappingI.length; ++j) {
+ final int[] mappingIJ = mappingI[j];
+ double product = mappingIJ[0] * f[mappingIJ[1]];
+ for (int k = 2; k < mappingIJ.length; ++k) {
+ product *= operand[operandOffset + mappingIJ[k]];
+ }
+ r += product;
+ }
+ result[resultOffset + i] = r;
+ }
+ }
+
+ /** Evaluate Taylor expansion of a derivative structure.
+ * @param ds array holding the derivative structure
+ * @param dsOffset offset of the derivative structure in its array
+ * @param delta parameters offsets (&Delta;x, &Delta;y, ...)
+ * @return value of the Taylor expansion at x + &Delta;x, y + &Delta;y, ...
+ * @throws MathArithmeticException if factorials becomes too large
+ */
+ public double taylor(final double[] ds, final int dsOffset, final double ... delta)
+ throws MathArithmeticException {
+ double value = 0;
+ for (int i = getSize() - 1; i >= 0; --i) {
+ final int[] orders = getPartialDerivativeOrders(i);
+ double term = ds[dsOffset + i];
+ for (int k = 0; k < orders.length; ++k) {
+ if (orders[k] > 0) {
+ try {
+ term *= FastMath.pow(delta[k], orders[k]) /
+ CombinatoricsUtils.factorial(orders[k]);
+ } catch (NotPositiveException e) {
+ // this cannot happen
+ throw new MathInternalError(e);
+ }
+ }
+ }
+ value += term;
+ }
+ return value;
+ }
+
+ /** Check rules set compatibility.
+ * @param compiler other compiler to check against instance
+ * @exception DimensionMismatchException if number of free parameters or orders are inconsistent
+ */
+ public void checkCompatibility(final DSCompiler compiler)
+ throws DimensionMismatchException {
+ if (parameters != compiler.parameters) {
+ throw new DimensionMismatchException(parameters, compiler.parameters);
+ }
+ if (order != compiler.order) {
+ throw new DimensionMismatchException(order, compiler.order);
+ }
+ }
+
+}
diff --git a/src/main/java/org/apache/commons/math3/analysis/differentiation/DerivativeStructure.java b/src/main/java/org/apache/commons/math3/analysis/differentiation/DerivativeStructure.java
new file mode 100644
index 0000000..da976fc
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/analysis/differentiation/DerivativeStructure.java
@@ -0,0 +1,1195 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math3.analysis.differentiation;
+
+import java.io.Serializable;
+
+import org.apache.commons.math3.Field;
+import org.apache.commons.math3.FieldElement;
+import org.apache.commons.math3.RealFieldElement;
+import org.apache.commons.math3.exception.DimensionMismatchException;
+import org.apache.commons.math3.exception.MathArithmeticException;
+import org.apache.commons.math3.exception.NumberIsTooLargeException;
+import org.apache.commons.math3.util.FastMath;
+import org.apache.commons.math3.util.MathArrays;
+import org.apache.commons.math3.util.MathUtils;
+
+/** Class representing both the value and the differentials of a function.
+ * <p>This class is the workhorse of the differentiation package.</p>
+ * <p>This class is an implementation of the extension to Rall's
+ * numbers described in Dan Kalman's paper <a
+ * href="http://www1.american.edu/cas/mathstat/People/kalman/pdffiles/mmgautodiff.pdf">Doubly
+ * Recursive Multivariate Automatic Differentiation</a>, Mathematics Magazine, vol. 75,
+ * no. 3, June 2002. Rall's numbers are an extension to the real numbers used
+ * throughout mathematical expressions; they hold the derivative together with the
+ * value of a function. Dan Kalman's derivative structures hold all partial derivatives
+ * up to any specified order, with respect to any number of free parameters. Rall's
+ * numbers therefore can be seen as derivative structures for order one derivative and
+ * one free parameter, and real numbers can be seen as derivative structures with zero
+ * order derivative and no free parameters.</p>
+ * <p>{@link DerivativeStructure} instances can be used directly thanks to
+ * the arithmetic operators to the mathematical functions provided as
+ * methods by this class (+, -, *, /, %, sin, cos ...).</p>
+ * <p>Implementing complex expressions by hand using these classes is
+ * a tedious and error-prone task but has the advantage of having no limitation
+ * on the derivation order despite no requiring users to compute the derivatives by
+ * themselves. Implementing complex expression can also be done by developing computation
+ * code using standard primitive double values and to use {@link
+ * UnivariateFunctionDifferentiator differentiators} to create the {@link
+ * DerivativeStructure}-based instances. This method is simpler but may be limited in
+ * the accuracy and derivation orders and may be computationally intensive (this is
+ * typically the case for {@link FiniteDifferencesDifferentiator finite differences
+ * differentiator}.</p>
+ * <p>Instances of this class are guaranteed to be immutable.</p>
+ * @see DSCompiler
+ * @since 3.1
+ */
+public class DerivativeStructure implements RealFieldElement<DerivativeStructure>, Serializable {
+
+ /** Serializable UID. */
+ private static final long serialVersionUID = 20120730L;
+
+ /** Compiler for the current dimensions. */
+ private transient DSCompiler compiler;
+
+ /** Combined array holding all values. */
+ private final double[] data;
+
+ /** Build an instance with all values and derivatives set to 0.
+ * @param compiler compiler to use for computation
+ */
+ private DerivativeStructure(final DSCompiler compiler) {
+ this.compiler = compiler;
+ this.data = new double[compiler.getSize()];
+ }
+
+ /** Build an instance with all values and derivatives set to 0.
+ * @param parameters number of free parameters
+ * @param order derivation order
+ * @throws NumberIsTooLargeException if order is too large
+ */
+ public DerivativeStructure(final int parameters, final int order)
+ throws NumberIsTooLargeException {
+ this(DSCompiler.getCompiler(parameters, order));
+ }
+
+ /** Build an instance representing a constant value.
+ * @param parameters number of free parameters
+ * @param order derivation order
+ * @param value value of the constant
+ * @throws NumberIsTooLargeException if order is too large
+ * @see #DerivativeStructure(int, int, int, double)
+ */
+ public DerivativeStructure(final int parameters, final int order, final double value)
+ throws NumberIsTooLargeException {
+ this(parameters, order);
+ this.data[0] = value;
+ }
+
+ /** Build an instance representing a variable.
+ * <p>Instances built using this constructor are considered
+ * to be the free variables with respect to which differentials
+ * are computed. As such, their differential with respect to
+ * themselves is +1.</p>
+ * @param parameters number of free parameters
+ * @param order derivation order
+ * @param index index of the variable (from 0 to {@code parameters - 1})
+ * @param value value of the variable
+ * @exception NumberIsTooLargeException if {@code index >= parameters}.
+ * @see #DerivativeStructure(int, int, double)
+ */
+ public DerivativeStructure(final int parameters, final int order,
+ final int index, final double value)
+ throws NumberIsTooLargeException {
+ this(parameters, order, value);
+
+ if (index >= parameters) {
+ throw new NumberIsTooLargeException(index, parameters, false);
+ }
+
+ if (order > 0) {
+ // the derivative of the variable with respect to itself is 1.
+ data[DSCompiler.getCompiler(index, order).getSize()] = 1.0;
+ }
+
+ }
+
+ /** Linear combination constructor.
+ * The derivative structure built will be a1 * ds1 + a2 * ds2
+ * @param a1 first scale factor
+ * @param ds1 first base (unscaled) derivative structure
+ * @param a2 second scale factor
+ * @param ds2 second base (unscaled) derivative structure
+ * @exception DimensionMismatchException if number of free parameters or orders are inconsistent
+ */
+ public DerivativeStructure(final double a1, final DerivativeStructure ds1,
+ final double a2, final DerivativeStructure ds2)
+ throws DimensionMismatchException {
+ this(ds1.compiler);
+ compiler.checkCompatibility(ds2.compiler);
+ compiler.linearCombination(a1, ds1.data, 0, a2, ds2.data, 0, data, 0);
+ }
+
+ /** Linear combination constructor.
+ * The derivative structure built will be a1 * ds1 + a2 * ds2 + a3 * ds3
+ * @param a1 first scale factor
+ * @param ds1 first base (unscaled) derivative structure
+ * @param a2 second scale factor
+ * @param ds2 second base (unscaled) derivative structure
+ * @param a3 third scale factor
+ * @param ds3 third base (unscaled) derivative structure
+ * @exception DimensionMismatchException if number of free parameters or orders are inconsistent
+ */
+ public DerivativeStructure(final double a1, final DerivativeStructure ds1,
+ final double a2, final DerivativeStructure ds2,
+ final double a3, final DerivativeStructure ds3)
+ throws DimensionMismatchException {
+ this(ds1.compiler);
+ compiler.checkCompatibility(ds2.compiler);
+ compiler.checkCompatibility(ds3.compiler);
+ compiler.linearCombination(a1, ds1.data, 0, a2, ds2.data, 0, a3, ds3.data, 0, data, 0);
+ }
+
+ /** Linear combination constructor.
+ * The derivative structure built will be a1 * ds1 + a2 * ds2 + a3 * ds3 + a4 * ds4
+ * @param a1 first scale factor
+ * @param ds1 first base (unscaled) derivative structure
+ * @param a2 second scale factor
+ * @param ds2 second base (unscaled) derivative structure
+ * @param a3 third scale factor
+ * @param ds3 third base (unscaled) derivative structure
+ * @param a4 fourth scale factor
+ * @param ds4 fourth base (unscaled) derivative structure
+ * @exception DimensionMismatchException if number of free parameters or orders are inconsistent
+ */
+ public DerivativeStructure(final double a1, final DerivativeStructure ds1,
+ final double a2, final DerivativeStructure ds2,
+ final double a3, final DerivativeStructure ds3,
+ final double a4, final DerivativeStructure ds4)
+ throws DimensionMismatchException {
+ this(ds1.compiler);
+ compiler.checkCompatibility(ds2.compiler);
+ compiler.checkCompatibility(ds3.compiler);
+ compiler.checkCompatibility(ds4.compiler);
+ compiler.linearCombination(a1, ds1.data, 0, a2, ds2.data, 0,
+ a3, ds3.data, 0, a4, ds4.data, 0,
+ data, 0);
+ }
+
+ /** Build an instance from all its derivatives.
+ * @param parameters number of free parameters
+ * @param order derivation order
+ * @param derivatives derivatives sorted according to
+ * {@link DSCompiler#getPartialDerivativeIndex(int...)}
+ * @exception DimensionMismatchException if derivatives array does not match the
+ * {@link DSCompiler#getSize() size} expected by the compiler
+ * @throws NumberIsTooLargeException if order is too large
+ * @see #getAllDerivatives()
+ */
+ public DerivativeStructure(final int parameters, final int order, final double ... derivatives)
+ throws DimensionMismatchException, NumberIsTooLargeException {
+ this(parameters, order);
+ if (derivatives.length != data.length) {
+ throw new DimensionMismatchException(derivatives.length, data.length);
+ }
+ System.arraycopy(derivatives, 0, data, 0, data.length);
+ }
+
+ /** Copy constructor.
+ * @param ds instance to copy
+ */
+ private DerivativeStructure(final DerivativeStructure ds) {
+ this.compiler = ds.compiler;
+ this.data = ds.data.clone();
+ }
+
+ /** Get the number of free parameters.
+ * @return number of free parameters
+ */
+ public int getFreeParameters() {
+ return compiler.getFreeParameters();
+ }
+
+ /** Get the derivation order.
+ * @return derivation order
+ */
+ public int getOrder() {
+ return compiler.getOrder();
+ }
+
+ /** Create a constant compatible with instance order and number of parameters.
+ * <p>
+ * This method is a convenience factory method, it simply calls
+ * {@code new DerivativeStructure(getFreeParameters(), getOrder(), c)}
+ * </p>
+ * @param c value of the constant
+ * @return a constant compatible with instance order and number of parameters
+ * @see #DerivativeStructure(int, int, double)
+ * @since 3.3
+ */
+ public DerivativeStructure createConstant(final double c) {
+ return new DerivativeStructure(getFreeParameters(), getOrder(), c);
+ }
+
+ /** {@inheritDoc}
+ * @since 3.2
+ */
+ public double getReal() {
+ return data[0];
+ }
+
+ /** Get the value part of the derivative structure.
+ * @return value part of the derivative structure
+ * @see #getPartialDerivative(int...)
+ */
+ public double getValue() {
+ return data[0];
+ }
+
+ /** Get a partial derivative.
+ * @param orders derivation orders with respect to each variable (if all orders are 0,
+ * the value is returned)
+ * @return partial derivative
+ * @see #getValue()
+ * @exception DimensionMismatchException if the numbers of variables does not
+ * match the instance
+ * @exception NumberIsTooLargeException if sum of derivation orders is larger
+ * than the instance limits
+ */
+ public double getPartialDerivative(final int ... orders)
+ throws DimensionMismatchException, NumberIsTooLargeException {
+ return data[compiler.getPartialDerivativeIndex(orders)];
+ }
+
+ /** Get all partial derivatives.
+ * @return a fresh copy of partial derivatives, in an array sorted according to
+ * {@link DSCompiler#getPartialDerivativeIndex(int...)}
+ */
+ public double[] getAllDerivatives() {
+ return data.clone();
+ }
+
+ /** {@inheritDoc}
+ * @since 3.2
+ */
+ public DerivativeStructure add(final double a) {
+ final DerivativeStructure ds = new DerivativeStructure(this);
+ ds.data[0] += a;
+ return ds;
+ }
+
+ /** {@inheritDoc}
+ * @exception DimensionMismatchException if number of free parameters
+ * or orders do not match
+ */
+ public DerivativeStructure add(final DerivativeStructure a)
+ throws DimensionMismatchException {
+ compiler.checkCompatibility(a.compiler);
+ final DerivativeStructure ds = new DerivativeStructure(this);
+ compiler.add(data, 0, a.data, 0, ds.data, 0);
+ return ds;
+ }
+
+ /** {@inheritDoc}
+ * @since 3.2
+ */
+ public DerivativeStructure subtract(final double a) {
+ return add(-a);
+ }
+
+ /** {@inheritDoc}
+ * @exception DimensionMismatchException if number of free parameters
+ * or orders do not match
+ */
+ public DerivativeStructure subtract(final DerivativeStructure a)
+ throws DimensionMismatchException {
+ compiler.checkCompatibility(a.compiler);
+ final DerivativeStructure ds = new DerivativeStructure(this);
+ compiler.subtract(data, 0, a.data, 0, ds.data, 0);
+ return ds;
+ }
+
+ /** {@inheritDoc} */
+ public DerivativeStructure multiply(final int n) {
+ return multiply((double) n);
+ }
+
+ /** {@inheritDoc}
+ * @since 3.2
+ */
+ public DerivativeStructure multiply(final double a) {
+ final DerivativeStructure ds = new DerivativeStructure(this);
+ for (int i = 0; i < ds.data.length; ++i) {
+ ds.data[i] *= a;
+ }
+ return ds;
+ }
+
+ /** {@inheritDoc}
+ * @exception DimensionMismatchException if number of free parameters
+ * or orders do not match
+ */
+ public DerivativeStructure multiply(final DerivativeStructure a)
+ throws DimensionMismatchException {
+ compiler.checkCompatibility(a.compiler);
+ final DerivativeStructure result = new DerivativeStructure(compiler);
+ compiler.multiply(data, 0, a.data, 0, result.data, 0);
+ return result;
+ }
+
+ /** {@inheritDoc}
+ * @since 3.2
+ */
+ public DerivativeStructure divide(final double a) {
+ final DerivativeStructure ds = new DerivativeStructure(this);
+ for (int i = 0; i < ds.data.length; ++i) {
+ ds.data[i] /= a;
+ }
+ return ds;
+ }
+
+ /** {@inheritDoc}
+ * @exception DimensionMismatchException if number of free parameters
+ * or orders do not match
+ */
+ public DerivativeStructure divide(final DerivativeStructure a)
+ throws DimensionMismatchException {
+ compiler.checkCompatibility(a.compiler);
+ final DerivativeStructure result = new DerivativeStructure(compiler);
+ compiler.divide(data, 0, a.data, 0, result.data, 0);
+ return result;
+ }
+
+ /** {@inheritDoc} */
+ public DerivativeStructure remainder(final double a) {
+ final DerivativeStructure ds = new DerivativeStructure(this);
+ ds.data[0] = FastMath.IEEEremainder(ds.data[0], a);
+ return ds;
+ }
+
+ /** {@inheritDoc}
+ * @exception DimensionMismatchException if number of free parameters
+ * or orders do not match
+ * @since 3.2
+ */
+ public DerivativeStructure remainder(final DerivativeStructure a)
+ throws DimensionMismatchException {
+ compiler.checkCompatibility(a.compiler);
+ final DerivativeStructure result = new DerivativeStructure(compiler);
+ compiler.remainder(data, 0, a.data, 0, result.data, 0);
+ return result;
+ }
+
+ /** {@inheritDoc} */
+ public DerivativeStructure negate() {
+ final DerivativeStructure ds = new DerivativeStructure(compiler);
+ for (int i = 0; i < ds.data.length; ++i) {
+ ds.data[i] = -data[i];
+ }
+ return ds;
+ }
+
+ /** {@inheritDoc}
+ * @since 3.2
+ */
+ public DerivativeStructure abs() {
+ if (Double.doubleToLongBits(data[0]) < 0) {
+ // we use the bits representation to also handle -0.0
+ return negate();
+ } else {
+ return this;
+ }
+ }
+
+ /** {@inheritDoc}
+ * @since 3.2
+ */
+ public DerivativeStructure ceil() {
+ return new DerivativeStructure(compiler.getFreeParameters(),
+ compiler.getOrder(),
+ FastMath.ceil(data[0]));
+ }
+
+ /** {@inheritDoc}
+ * @since 3.2
+ */
+ public DerivativeStructure floor() {
+ return new DerivativeStructure(compiler.getFreeParameters(),
+ compiler.getOrder(),
+ FastMath.floor(data[0]));
+ }
+
+ /** {@inheritDoc}
+ * @since 3.2
+ */
+ public DerivativeStructure rint() {
+ return new DerivativeStructure(compiler.getFreeParameters(),
+ compiler.getOrder(),
+ FastMath.rint(data[0]));
+ }
+
+ /** {@inheritDoc} */
+ public long round() {
+ return FastMath.round(data[0]);
+ }
+
+ /** {@inheritDoc}
+ * @since 3.2
+ */
+ public DerivativeStructure signum() {
+ return new DerivativeStructure(compiler.getFreeParameters(),
+ compiler.getOrder(),
+ FastMath.signum(data[0]));
+ }
+
+ /** {@inheritDoc}
+ * @since 3.2
+ */
+ public DerivativeStructure copySign(final DerivativeStructure sign){
+ long m = Double.doubleToLongBits(data[0]);
+ long s = Double.doubleToLongBits(sign.data[0]);
+ if ((m >= 0 && s >= 0) || (m < 0 && s < 0)) { // Sign is currently OK
+ return this;
+ }
+ return negate(); // flip sign
+ }
+
+ /** {@inheritDoc}
+ * @since 3.2
+ */
+ public DerivativeStructure copySign(final double sign) {
+ long m = Double.doubleToLongBits(data[0]);
+ long s = Double.doubleToLongBits(sign);
+ if ((m >= 0 && s >= 0) || (m < 0 && s < 0)) { // Sign is currently OK
+ return this;
+ }
+ return negate(); // flip sign
+ }
+
+ /**
+ * Return the exponent of the instance value, removing the bias.
+ * <p>
+ * For double numbers of the form 2<sup>x</sup>, the unbiased
+ * exponent is exactly x.
+ * </p>
+ * @return exponent for instance in IEEE754 representation, without bias
+ */
+ public int getExponent() {
+ return FastMath.getExponent(data[0]);
+ }
+
+ /** {@inheritDoc}
+ * @since 3.2
+ */
+ public DerivativeStructure scalb(final int n) {
+ final DerivativeStructure ds = new DerivativeStructure(compiler);
+ for (int i = 0; i < ds.data.length; ++i) {
+ ds.data[i] = FastMath.scalb(data[i], n);
+ }
+ return ds;
+ }
+
+ /** {@inheritDoc}
+ * @exception DimensionMismatchException if number of free parameters
+ * or orders do not match
+ * @since 3.2
+ */
+ public DerivativeStructure hypot(final DerivativeStructure y)
+ throws DimensionMismatchException {
+
+ compiler.checkCompatibility(y.compiler);
+
+ if (Double.isInfinite(data[0]) || Double.isInfinite(y.data[0])) {
+ return new DerivativeStructure(compiler.getFreeParameters(),
+ compiler.getFreeParameters(),
+ Double.POSITIVE_INFINITY);
+ } else if (Double.isNaN(data[0]) || Double.isNaN(y.data[0])) {
+ return new DerivativeStructure(compiler.getFreeParameters(),
+ compiler.getFreeParameters(),
+ Double.NaN);
+ } else {
+
+ final int expX = getExponent();
+ final int expY = y.getExponent();
+ if (expX > expY + 27) {
+ // y is neglectible with respect to x
+ return abs();
+ } else if (expY > expX + 27) {
+ // x is neglectible with respect to y
+ return y.abs();
+ } else {
+
+ // find an intermediate scale to avoid both overflow and underflow
+ final int middleExp = (expX + expY) / 2;
+
+ // scale parameters without losing precision
+ final DerivativeStructure scaledX = scalb(-middleExp);
+ final DerivativeStructure scaledY = y.scalb(-middleExp);
+
+ // compute scaled hypotenuse
+ final DerivativeStructure scaledH =
+ scaledX.multiply(scaledX).add(scaledY.multiply(scaledY)).sqrt();
+
+ // remove scaling
+ return scaledH.scalb(middleExp);
+
+ }
+
+ }
+ }
+
+ /**
+ * Returns the hypotenuse of a triangle with sides {@code x} and {@code y}
+ * - sqrt(<i>x</i><sup>2</sup>&nbsp;+<i>y</i><sup>2</sup>)
+ * avoiding intermediate overflow or underflow.
+ *
+ * <ul>
+ * <li> If either argument is infinite, then the result is positive infinity.</li>
+ * <li> else, if either argument is NaN then the result is NaN.</li>
+ * </ul>
+ *
+ * @param x a value
+ * @param y a value
+ * @return sqrt(<i>x</i><sup>2</sup>&nbsp;+<i>y</i><sup>2</sup>)
+ * @exception DimensionMismatchException if number of free parameters
+ * or orders do not match
+ * @since 3.2
+ */
+ public static DerivativeStructure hypot(final DerivativeStructure x, final DerivativeStructure y)
+ throws DimensionMismatchException {
+ return x.hypot(y);
+ }
+
+ /** Compute composition of the instance by a univariate function.
+ * @param f array of value and derivatives of the function at
+ * the current point (i.e. [f({@link #getValue()}),
+ * f'({@link #getValue()}), f''({@link #getValue()})...]).
+ * @return f(this)
+ * @exception DimensionMismatchException if the number of derivatives
+ * in the array is not equal to {@link #getOrder() order} + 1
+ */
+ public DerivativeStructure compose(final double ... f)
+ throws DimensionMismatchException {
+ if (f.length != getOrder() + 1) {
+ throw new DimensionMismatchException(f.length, getOrder() + 1);
+ }
+ final DerivativeStructure result = new DerivativeStructure(compiler);
+ compiler.compose(data, 0, f, result.data, 0);
+ return result;
+ }
+
+ /** {@inheritDoc} */
+ public DerivativeStructure reciprocal() {
+ final DerivativeStructure result = new DerivativeStructure(compiler);
+ compiler.pow(data, 0, -1, result.data, 0);
+ return result;
+ }
+
+ /** {@inheritDoc}
+ * @since 3.2
+ */
+ public DerivativeStructure sqrt() {
+ return rootN(2);
+ }
+
+ /** {@inheritDoc}
+ * @since 3.2
+ */
+ public DerivativeStructure cbrt() {
+ return rootN(3);
+ }
+
+ /** {@inheritDoc}
+ * @since 3.2
+ */
+ public DerivativeStructure rootN(final int n) {
+ final DerivativeStructure result = new DerivativeStructure(compiler);
+ compiler.rootN(data, 0, n, result.data, 0);
+ return result;
+ }
+
+ /** {@inheritDoc} */
+ public Field<DerivativeStructure> getField() {
+ return new Field<DerivativeStructure>() {
+
+ /** {@inheritDoc} */
+ public DerivativeStructure getZero() {
+ return new DerivativeStructure(compiler.getFreeParameters(), compiler.getOrder(), 0.0);
+ }
+
+ /** {@inheritDoc} */
+ public DerivativeStructure getOne() {
+ return new DerivativeStructure(compiler.getFreeParameters(), compiler.getOrder(), 1.0);
+ }
+
+ /** {@inheritDoc} */
+ public Class<? extends FieldElement<DerivativeStructure>> getRuntimeClass() {
+ return DerivativeStructure.class;
+ }
+
+ };
+ }
+
+ /** Compute a<sup>x</sup> where a is a double and x a {@link DerivativeStructure}
+ * @param a number to exponentiate
+ * @param x power to apply
+ * @return a<sup>x</sup>
+ * @since 3.3
+ */
+ public static DerivativeStructure pow(final double a, final DerivativeStructure x) {
+ final DerivativeStructure result = new DerivativeStructure(x.compiler);
+ x.compiler.pow(a, x.data, 0, result.data, 0);
+ return result;
+ }
+
+ /** {@inheritDoc}
+ * @since 3.2
+ */
+ public DerivativeStructure pow(final double p) {
+ final DerivativeStructure result = new DerivativeStructure(compiler);
+ compiler.pow(data, 0, p, result.data, 0);
+ return result;
+ }
+
+ /** {@inheritDoc}
+ * @since 3.2
+ */
+ public DerivativeStructure pow(final int n) {
+ final DerivativeStructure result = new DerivativeStructure(compiler);
+ compiler.pow(data, 0, n, result.data, 0);
+ return result;
+ }
+
+ /** {@inheritDoc}
+ * @exception DimensionMismatchException if number of free parameters
+ * or orders do not match
+ * @since 3.2
+ */
+ public DerivativeStructure pow(final DerivativeStructure e)
+ throws DimensionMismatchException {
+ compiler.checkCompatibility(e.compiler);
+ final DerivativeStructure result = new DerivativeStructure(compiler);
+ compiler.pow(data, 0, e.data, 0, result.data, 0);
+ return result;
+ }
+
+ /** {@inheritDoc}
+ * @since 3.2
+ */
+ public DerivativeStructure exp() {
+ final DerivativeStructure result = new DerivativeStructure(compiler);
+ compiler.exp(data, 0, result.data, 0);
+ return result;
+ }
+
+ /** {@inheritDoc}
+ * @since 3.2
+ */
+ public DerivativeStructure expm1() {
+ final DerivativeStructure result = new DerivativeStructure(compiler);
+ compiler.expm1(data, 0, result.data, 0);
+ return result;
+ }
+
+ /** {@inheritDoc}
+ * @since 3.2
+ */
+ public DerivativeStructure log() {
+ final DerivativeStructure result = new DerivativeStructure(compiler);
+ compiler.log(data, 0, result.data, 0);
+ return result;
+ }
+
+ /** {@inheritDoc}
+ * @since 3.2
+ */
+ public DerivativeStructure log1p() {
+ final DerivativeStructure result = new DerivativeStructure(compiler);
+ compiler.log1p(data, 0, result.data, 0);
+ return result;
+ }
+
+ /** Base 10 logarithm.
+ * @return base 10 logarithm of the instance
+ */
+ public DerivativeStructure log10() {
+ final DerivativeStructure result = new DerivativeStructure(compiler);
+ compiler.log10(data, 0, result.data, 0);
+ return result;
+ }
+
+ /** {@inheritDoc}
+ * @since 3.2
+ */
+ public DerivativeStructure cos() {
+ final DerivativeStructure result = new DerivativeStructure(compiler);
+ compiler.cos(data, 0, result.data, 0);
+ return result;
+ }
+
+ /** {@inheritDoc}
+ * @since 3.2
+ */
+ public DerivativeStructure sin() {
+ final DerivativeStructure result = new DerivativeStructure(compiler);
+ compiler.sin(data, 0, result.data, 0);
+ return result;
+ }
+
+ /** {@inheritDoc}
+ * @since 3.2
+ */
+ public DerivativeStructure tan() {
+ final DerivativeStructure result = new DerivativeStructure(compiler);
+ compiler.tan(data, 0, result.data, 0);
+ return result;
+ }
+
+ /** {@inheritDoc}
+ * @since 3.2
+ */
+ public DerivativeStructure acos() {
+ final DerivativeStructure result = new DerivativeStructure(compiler);
+ compiler.acos(data, 0, result.data, 0);
+ return result;
+ }
+
+ /** {@inheritDoc}
+ * @since 3.2
+ */
+ public DerivativeStructure asin() {
+ final DerivativeStructure result = new DerivativeStructure(compiler);
+ compiler.asin(data, 0, result.data, 0);
+ return result;
+ }
+
+ /** {@inheritDoc}
+ * @since 3.2
+ */
+ public DerivativeStructure atan() {
+ final DerivativeStructure result = new DerivativeStructure(compiler);
+ compiler.atan(data, 0, result.data, 0);
+ return result;
+ }
+
+ /** {@inheritDoc}
+ * @since 3.2
+ */
+ public DerivativeStructure atan2(final DerivativeStructure x)
+ throws DimensionMismatchException {
+ compiler.checkCompatibility(x.compiler);
+ final DerivativeStructure result = new DerivativeStructure(compiler);
+ compiler.atan2(data, 0, x.data, 0, result.data, 0);
+ return result;
+ }
+
+ /** Two arguments arc tangent operation.
+ * @param y first argument of the arc tangent
+ * @param x second argument of the arc tangent
+ * @return atan2(y, x)
+ * @exception DimensionMismatchException if number of free parameters
+ * or orders do not match
+ * @since 3.2
+ */
+ public static DerivativeStructure atan2(final DerivativeStructure y, final DerivativeStructure x)
+ throws DimensionMismatchException {
+ return y.atan2(x);
+ }
+
+ /** {@inheritDoc}
+ * @since 3.2
+ */
+ public DerivativeStructure cosh() {
+ final DerivativeStructure result = new DerivativeStructure(compiler);
+ compiler.cosh(data, 0, result.data, 0);
+ return result;
+ }
+
+ /** {@inheritDoc}
+ * @since 3.2
+ */
+ public DerivativeStructure sinh() {
+ final DerivativeStructure result = new DerivativeStructure(compiler);
+ compiler.sinh(data, 0, result.data, 0);
+ return result;
+ }
+
+ /** {@inheritDoc}
+ * @since 3.2
+ */
+ public DerivativeStructure tanh() {
+ final DerivativeStructure result = new DerivativeStructure(compiler);
+ compiler.tanh(data, 0, result.data, 0);
+ return result;
+ }
+
+ /** {@inheritDoc}
+ * @since 3.2
+ */
+ public DerivativeStructure acosh() {
+ final DerivativeStructure result = new DerivativeStructure(compiler);
+ compiler.acosh(data, 0, result.data, 0);
+ return result;
+ }
+
+ /** {@inheritDoc}
+ * @since 3.2
+ */
+ public DerivativeStructure asinh() {
+ final DerivativeStructure result = new DerivativeStructure(compiler);
+ compiler.asinh(data, 0, result.data, 0);
+ return result;
+ }
+
+ /** {@inheritDoc}
+ * @since 3.2
+ */
+ public DerivativeStructure atanh() {
+ final DerivativeStructure result = new DerivativeStructure(compiler);
+ compiler.atanh(data, 0, result.data, 0);
+ return result;
+ }
+
+ /** Convert radians to degrees, with error of less than 0.5 ULP
+ * @return instance converted into degrees
+ */
+ public DerivativeStructure toDegrees() {
+ final DerivativeStructure ds = new DerivativeStructure(compiler);
+ for (int i = 0; i < ds.data.length; ++i) {
+ ds.data[i] = FastMath.toDegrees(data[i]);
+ }
+ return ds;
+ }
+
+ /** Convert degrees to radians, with error of less than 0.5 ULP
+ * @return instance converted into radians
+ */
+ public DerivativeStructure toRadians() {
+ final DerivativeStructure ds = new DerivativeStructure(compiler);
+ for (int i = 0; i < ds.data.length; ++i) {
+ ds.data[i] = FastMath.toRadians(data[i]);
+ }
+ return ds;
+ }
+
+ /** Evaluate Taylor expansion a derivative structure.
+ * @param delta parameters offsets (&Delta;x, &Delta;y, ...)
+ * @return value of the Taylor expansion at x + &Delta;x, y + &Delta;y, ...
+ * @throws MathArithmeticException if factorials becomes too large
+ */
+ public double taylor(final double ... delta) throws MathArithmeticException {
+ return compiler.taylor(data, 0, delta);
+ }
+
+ /** {@inheritDoc}
+ * @exception DimensionMismatchException if number of free parameters
+ * or orders do not match
+ * @since 3.2
+ */
+ public DerivativeStructure linearCombination(final DerivativeStructure[] a, final DerivativeStructure[] b)
+ throws DimensionMismatchException {
+
+ // compute an accurate value, taking care of cancellations
+ final double[] aDouble = new double[a.length];
+ for (int i = 0; i < a.length; ++i) {
+ aDouble[i] = a[i].getValue();
+ }
+ final double[] bDouble = new double[b.length];
+ for (int i = 0; i < b.length; ++i) {
+ bDouble[i] = b[i].getValue();
+ }
+ final double accurateValue = MathArrays.linearCombination(aDouble, bDouble);
+
+ // compute a simple value, with all partial derivatives
+ DerivativeStructure simpleValue = a[0].getField().getZero();
+ for (int i = 0; i < a.length; ++i) {
+ simpleValue = simpleValue.add(a[i].multiply(b[i]));
+ }
+
+ // create a result with accurate value and all derivatives (not necessarily as accurate as the value)
+ final double[] all = simpleValue.getAllDerivatives();
+ all[0] = accurateValue;
+ return new DerivativeStructure(simpleValue.getFreeParameters(), simpleValue.getOrder(), all);
+
+ }
+
+ /** {@inheritDoc}
+ * @exception DimensionMismatchException if number of free parameters
+ * or orders do not match
+ * @since 3.2
+ */
+ public DerivativeStructure linearCombination(final double[] a, final DerivativeStructure[] b)
+ throws DimensionMismatchException {
+
+ // compute an accurate value, taking care of cancellations
+ final double[] bDouble = new double[b.length];
+ for (int i = 0; i < b.length; ++i) {
+ bDouble[i] = b[i].getValue();
+ }
+ final double accurateValue = MathArrays.linearCombination(a, bDouble);
+
+ // compute a simple value, with all partial derivatives
+ DerivativeStructure simpleValue = b[0].getField().getZero();
+ for (int i = 0; i < a.length; ++i) {
+ simpleValue = simpleValue.add(b[i].multiply(a[i]));
+ }
+
+ // create a result with accurate value and all derivatives (not necessarily as accurate as the value)
+ final double[] all = simpleValue.getAllDerivatives();
+ all[0] = accurateValue;
+ return new DerivativeStructure(simpleValue.getFreeParameters(), simpleValue.getOrder(), all);
+
+ }
+
+ /** {@inheritDoc}
+ * @exception DimensionMismatchException if number of free parameters
+ * or orders do not match
+ * @since 3.2
+ */
+ public DerivativeStructure linearCombination(final DerivativeStructure a1, final DerivativeStructure b1,
+ final DerivativeStructure a2, final DerivativeStructure b2)
+ throws DimensionMismatchException {
+
+ // compute an accurate value, taking care of cancellations
+ final double accurateValue = MathArrays.linearCombination(a1.getValue(), b1.getValue(),
+ a2.getValue(), b2.getValue());
+
+ // compute a simple value, with all partial derivatives
+ final DerivativeStructure simpleValue = a1.multiply(b1).add(a2.multiply(b2));
+
+ // create a result with accurate value and all derivatives (not necessarily as accurate as the value)
+ final double[] all = simpleValue.getAllDerivatives();
+ all[0] = accurateValue;
+ return new DerivativeStructure(getFreeParameters(), getOrder(), all);
+
+ }
+
+ /** {@inheritDoc}
+ * @exception DimensionMismatchException if number of free parameters
+ * or orders do not match
+ * @since 3.2
+ */
+ public DerivativeStructure linearCombination(final double a1, final DerivativeStructure b1,
+ final double a2, final DerivativeStructure b2)
+ throws DimensionMismatchException {
+
+ // compute an accurate value, taking care of cancellations
+ final double accurateValue = MathArrays.linearCombination(a1, b1.getValue(),
+ a2, b2.getValue());
+
+ // compute a simple value, with all partial derivatives
+ final DerivativeStructure simpleValue = b1.multiply(a1).add(b2.multiply(a2));
+
+ // create a result with accurate value and all derivatives (not necessarily as accurate as the value)
+ final double[] all = simpleValue.getAllDerivatives();
+ all[0] = accurateValue;
+ return new DerivativeStructure(getFreeParameters(), getOrder(), all);
+
+ }
+
+ /** {@inheritDoc}
+ * @exception DimensionMismatchException if number of free parameters
+ * or orders do not match
+ * @since 3.2
+ */
+ public DerivativeStructure linearCombination(final DerivativeStructure a1, final DerivativeStructure b1,
+ final DerivativeStructure a2, final DerivativeStructure b2,
+ final DerivativeStructure a3, final DerivativeStructure b3)
+ throws DimensionMismatchException {
+
+ // compute an accurate value, taking care of cancellations
+ final double accurateValue = MathArrays.linearCombination(a1.getValue(), b1.getValue(),
+ a2.getValue(), b2.getValue(),
+ a3.getValue(), b3.getValue());
+
+ // compute a simple value, with all partial derivatives
+ final DerivativeStructure simpleValue = a1.multiply(b1).add(a2.multiply(b2)).add(a3.multiply(b3));
+
+ // create a result with accurate value and all derivatives (not necessarily as accurate as the value)
+ final double[] all = simpleValue.getAllDerivatives();
+ all[0] = accurateValue;
+ return new DerivativeStructure(getFreeParameters(), getOrder(), all);
+
+ }
+
+ /** {@inheritDoc}
+ * @exception DimensionMismatchException if number of free parameters
+ * or orders do not match
+ * @since 3.2
+ */
+ public DerivativeStructure linearCombination(final double a1, final DerivativeStructure b1,
+ final double a2, final DerivativeStructure b2,
+ final double a3, final DerivativeStructure b3)
+ throws DimensionMismatchException {
+
+ // compute an accurate value, taking care of cancellations
+ final double accurateValue = MathArrays.linearCombination(a1, b1.getValue(),
+ a2, b2.getValue(),
+ a3, b3.getValue());
+
+ // compute a simple value, with all partial derivatives
+ final DerivativeStructure simpleValue = b1.multiply(a1).add(b2.multiply(a2)).add(b3.multiply(a3));
+
+ // create a result with accurate value and all derivatives (not necessarily as accurate as the value)
+ final double[] all = simpleValue.getAllDerivatives();
+ all[0] = accurateValue;
+ return new DerivativeStructure(getFreeParameters(), getOrder(), all);
+
+ }
+
+ /** {@inheritDoc}
+ * @exception DimensionMismatchException if number of free parameters
+ * or orders do not match
+ * @since 3.2
+ */
+ public DerivativeStructure linearCombination(final DerivativeStructure a1, final DerivativeStructure b1,
+ final DerivativeStructure a2, final DerivativeStructure b2,
+ final DerivativeStructure a3, final DerivativeStructure b3,
+ final DerivativeStructure a4, final DerivativeStructure b4)
+ throws DimensionMismatchException {
+
+ // compute an accurate value, taking care of cancellations
+ final double accurateValue = MathArrays.linearCombination(a1.getValue(), b1.getValue(),
+ a2.getValue(), b2.getValue(),
+ a3.getValue(), b3.getValue(),
+ a4.getValue(), b4.getValue());
+
+ // compute a simple value, with all partial derivatives
+ final DerivativeStructure simpleValue = a1.multiply(b1).add(a2.multiply(b2)).add(a3.multiply(b3)).add(a4.multiply(b4));
+
+ // create a result with accurate value and all derivatives (not necessarily as accurate as the value)
+ final double[] all = simpleValue.getAllDerivatives();
+ all[0] = accurateValue;
+ return new DerivativeStructure(getFreeParameters(), getOrder(), all);
+
+ }
+
+ /** {@inheritDoc}
+ * @exception DimensionMismatchException if number of free parameters
+ * or orders do not match
+ * @since 3.2
+ */
+ public DerivativeStructure linearCombination(final double a1, final DerivativeStructure b1,
+ final double a2, final DerivativeStructure b2,
+ final double a3, final DerivativeStructure b3,
+ final double a4, final DerivativeStructure b4)
+ throws DimensionMismatchException {
+
+ // compute an accurate value, taking care of cancellations
+ final double accurateValue = MathArrays.linearCombination(a1, b1.getValue(),
+ a2, b2.getValue(),
+ a3, b3.getValue(),
+ a4, b4.getValue());
+
+ // compute a simple value, with all partial derivatives
+ final DerivativeStructure simpleValue = b1.multiply(a1).add(b2.multiply(a2)).add(b3.multiply(a3)).add(b4.multiply(a4));
+
+ // create a result with accurate value and all derivatives (not necessarily as accurate as the value)
+ final double[] all = simpleValue.getAllDerivatives();
+ all[0] = accurateValue;
+ return new DerivativeStructure(getFreeParameters(), getOrder(), all);
+
+ }
+
+ /**
+ * Test for the equality of two derivative structures.
+ * <p>
+ * Derivative structures are considered equal if they have the same number
+ * of free parameters, the same derivation order, and the same derivatives.
+ * </p>
+ * @param other Object to test for equality to this
+ * @return true if two derivative structures are equal
+ * @since 3.2
+ */
+ @Override
+ public boolean equals(Object other) {
+
+ if (this == other) {
+ return true;
+ }
+
+ if (other instanceof DerivativeStructure) {
+ final DerivativeStructure rhs = (DerivativeStructure)other;
+ return (getFreeParameters() == rhs.getFreeParameters()) &&
+ (getOrder() == rhs.getOrder()) &&
+ MathArrays.equals(data, rhs.data);
+ }
+
+ return false;
+
+ }
+
+ /**
+ * Get a hashCode for the derivative structure.
+ * @return a hash code value for this object
+ * @since 3.2
+ */
+ @Override
+ public int hashCode() {
+ return 227 + 229 * getFreeParameters() + 233 * getOrder() + 239 * MathUtils.hash(data);
+ }
+
+ /**
+ * Replace the instance with a data transfer object for serialization.
+ * @return data transfer object that will be serialized
+ */
+ private Object writeReplace() {
+ return new DataTransferObject(compiler.getFreeParameters(), compiler.getOrder(), data);
+ }
+
+ /** Internal class used only for serialization. */
+ private static class DataTransferObject implements Serializable {
+
+ /** Serializable UID. */
+ private static final long serialVersionUID = 20120730L;
+
+ /** Number of variables.
+ * @serial
+ */
+ private final int variables;
+
+ /** Derivation order.
+ * @serial
+ */
+ private final int order;
+
+ /** Partial derivatives.
+ * @serial
+ */
+ private final double[] data;
+
+ /** Simple constructor.
+ * @param variables number of variables
+ * @param order derivation order
+ * @param data partial derivatives
+ */
+ DataTransferObject(final int variables, final int order, final double[] data) {
+ this.variables = variables;
+ this.order = order;
+ this.data = data;
+ }
+
+ /** Replace the deserialized data transfer object with a {@link DerivativeStructure}.
+ * @return replacement {@link DerivativeStructure}
+ */
+ private Object readResolve() {
+ return new DerivativeStructure(variables, order, data);
+ }
+
+ }
+
+}
diff --git a/src/main/java/org/apache/commons/math3/analysis/differentiation/FiniteDifferencesDifferentiator.java b/src/main/java/org/apache/commons/math3/analysis/differentiation/FiniteDifferencesDifferentiator.java
new file mode 100644
index 0000000..c2f1002
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/analysis/differentiation/FiniteDifferencesDifferentiator.java
@@ -0,0 +1,384 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math3.analysis.differentiation;
+
+import java.io.Serializable;
+
+import org.apache.commons.math3.analysis.UnivariateFunction;
+import org.apache.commons.math3.analysis.UnivariateMatrixFunction;
+import org.apache.commons.math3.analysis.UnivariateVectorFunction;
+import org.apache.commons.math3.exception.MathIllegalArgumentException;
+import org.apache.commons.math3.exception.NotPositiveException;
+import org.apache.commons.math3.exception.NumberIsTooLargeException;
+import org.apache.commons.math3.exception.NumberIsTooSmallException;
+import org.apache.commons.math3.util.FastMath;
+
+/** Univariate functions differentiator using finite differences.
+ * <p>
+ * This class creates some wrapper objects around regular
+ * {@link UnivariateFunction univariate functions} (or {@link
+ * UnivariateVectorFunction univariate vector functions} or {@link
+ * UnivariateMatrixFunction univariate matrix functions}). These
+ * wrapper objects compute derivatives in addition to function
+ * values.
+ * </p>
+ * <p>
+ * The wrapper objects work by calling the underlying function on
+ * a sampling grid around the current point and performing polynomial
+ * interpolation. A finite differences scheme with n points is
+ * theoretically able to compute derivatives up to order n-1, but
+ * it is generally better to have a slight margin. The step size must
+ * also be small enough in order for the polynomial approximation to
+ * be good in the current point neighborhood, but it should not be too
+ * small because numerical instability appears quickly (there are several
+ * differences of close points). Choosing the number of points and
+ * the step size is highly problem dependent.
+ * </p>
+ * <p>
+ * As an example of good and bad settings, lets consider the quintic
+ * polynomial function {@code f(x) = (x-1)*(x-0.5)*x*(x+0.5)*(x+1)}.
+ * Since it is a polynomial, finite differences with at least 6 points
+ * should theoretically recover the exact same polynomial and hence
+ * compute accurate derivatives for any order. However, due to numerical
+ * errors, we get the following results for a 7 points finite differences
+ * for abscissae in the [-10, 10] range:
+ * <ul>
+ * <li>step size = 0.25, second order derivative error about 9.97e-10</li>
+ * <li>step size = 0.25, fourth order derivative error about 5.43e-8</li>
+ * <li>step size = 1.0e-6, second order derivative error about 148</li>
+ * <li>step size = 1.0e-6, fourth order derivative error about 6.35e+14</li>
+ * </ul>
+ * <p>
+ * This example shows that the small step size is really bad, even simply
+ * for second order derivative!</p>
+ *
+ * @since 3.1
+ */
+public class FiniteDifferencesDifferentiator
+ implements UnivariateFunctionDifferentiator, UnivariateVectorFunctionDifferentiator,
+ UnivariateMatrixFunctionDifferentiator, Serializable {
+
+ /** Serializable UID. */
+ private static final long serialVersionUID = 20120917L;
+
+ /** Number of points to use. */
+ private final int nbPoints;
+
+ /** Step size. */
+ private final double stepSize;
+
+ /** Half sample span. */
+ private final double halfSampleSpan;
+
+ /** Lower bound for independent variable. */
+ private final double tMin;
+
+ /** Upper bound for independent variable. */
+ private final double tMax;
+
+ /**
+ * Build a differentiator with number of points and step size when independent variable is unbounded.
+ * <p>
+ * Beware that wrong settings for the finite differences differentiator
+ * can lead to highly unstable and inaccurate results, especially for
+ * high derivation orders. Using very small step sizes is often a
+ * <em>bad</em> idea.
+ * </p>
+ * @param nbPoints number of points to use
+ * @param stepSize step size (gap between each point)
+ * @exception NotPositiveException if {@code stepsize <= 0} (note that
+ * {@link NotPositiveException} extends {@link NumberIsTooSmallException})
+ * @exception NumberIsTooSmallException {@code nbPoint <= 1}
+ */
+ public FiniteDifferencesDifferentiator(final int nbPoints, final double stepSize)
+ throws NotPositiveException, NumberIsTooSmallException {
+ this(nbPoints, stepSize, Double.NEGATIVE_INFINITY, Double.POSITIVE_INFINITY);
+ }
+
+ /**
+ * Build a differentiator with number of points and step size when independent variable is bounded.
+ * <p>
+ * When the independent variable is bounded (tLower &lt; t &lt; tUpper), the sampling
+ * points used for differentiation will be adapted to ensure the constraint holds
+ * even near the boundaries. This means the sample will not be centered anymore in
+ * these cases. At an extreme case, computing derivatives exactly at the lower bound
+ * will lead the sample to be entirely on the right side of the derivation point.
+ * </p>
+ * <p>
+ * Note that the boundaries are considered to be excluded for function evaluation.
+ * </p>
+ * <p>
+ * Beware that wrong settings for the finite differences differentiator
+ * can lead to highly unstable and inaccurate results, especially for
+ * high derivation orders. Using very small step sizes is often a
+ * <em>bad</em> idea.
+ * </p>
+ * @param nbPoints number of points to use
+ * @param stepSize step size (gap between each point)
+ * @param tLower lower bound for independent variable (may be {@code Double.NEGATIVE_INFINITY}
+ * if there are no lower bounds)
+ * @param tUpper upper bound for independent variable (may be {@code Double.POSITIVE_INFINITY}
+ * if there are no upper bounds)
+ * @exception NotPositiveException if {@code stepsize <= 0} (note that
+ * {@link NotPositiveException} extends {@link NumberIsTooSmallException})
+ * @exception NumberIsTooSmallException {@code nbPoint <= 1}
+ * @exception NumberIsTooLargeException {@code stepSize * (nbPoints - 1) >= tUpper - tLower}
+ */
+ public FiniteDifferencesDifferentiator(final int nbPoints, final double stepSize,
+ final double tLower, final double tUpper)
+ throws NotPositiveException, NumberIsTooSmallException, NumberIsTooLargeException {
+
+ if (nbPoints <= 1) {
+ throw new NumberIsTooSmallException(stepSize, 1, false);
+ }
+ this.nbPoints = nbPoints;
+
+ if (stepSize <= 0) {
+ throw new NotPositiveException(stepSize);
+ }
+ this.stepSize = stepSize;
+
+ halfSampleSpan = 0.5 * stepSize * (nbPoints - 1);
+ if (2 * halfSampleSpan >= tUpper - tLower) {
+ throw new NumberIsTooLargeException(2 * halfSampleSpan, tUpper - tLower, false);
+ }
+ final double safety = FastMath.ulp(halfSampleSpan);
+ this.tMin = tLower + halfSampleSpan + safety;
+ this.tMax = tUpper - halfSampleSpan - safety;
+
+ }
+
+ /**
+ * Get the number of points to use.
+ * @return number of points to use
+ */
+ public int getNbPoints() {
+ return nbPoints;
+ }
+
+ /**
+ * Get the step size.
+ * @return step size
+ */
+ public double getStepSize() {
+ return stepSize;
+ }
+
+ /**
+ * Evaluate derivatives from a sample.
+ * <p>
+ * Evaluation is done using divided differences.
+ * </p>
+ * @param t evaluation abscissa value and derivatives
+ * @param t0 first sample point abscissa
+ * @param y function values sample {@code y[i] = f(t[i]) = f(t0 + i * stepSize)}
+ * @return value and derivatives at {@code t}
+ * @exception NumberIsTooLargeException if the requested derivation order
+ * is larger or equal to the number of points
+ */
+ private DerivativeStructure evaluate(final DerivativeStructure t, final double t0,
+ final double[] y)
+ throws NumberIsTooLargeException {
+
+ // create divided differences diagonal arrays
+ final double[] top = new double[nbPoints];
+ final double[] bottom = new double[nbPoints];
+
+ for (int i = 0; i < nbPoints; ++i) {
+
+ // update the bottom diagonal of the divided differences array
+ bottom[i] = y[i];
+ for (int j = 1; j <= i; ++j) {
+ bottom[i - j] = (bottom[i - j + 1] - bottom[i - j]) / (j * stepSize);
+ }
+
+ // update the top diagonal of the divided differences array
+ top[i] = bottom[0];
+
+ }
+
+ // evaluate interpolation polynomial (represented by top diagonal) at t
+ final int order = t.getOrder();
+ final int parameters = t.getFreeParameters();
+ final double[] derivatives = t.getAllDerivatives();
+ final double dt0 = t.getValue() - t0;
+ DerivativeStructure interpolation = new DerivativeStructure(parameters, order, 0.0);
+ DerivativeStructure monomial = null;
+ for (int i = 0; i < nbPoints; ++i) {
+ if (i == 0) {
+ // start with monomial(t) = 1
+ monomial = new DerivativeStructure(parameters, order, 1.0);
+ } else {
+ // monomial(t) = (t - t0) * (t - t1) * ... * (t - t(i-1))
+ derivatives[0] = dt0 - (i - 1) * stepSize;
+ final DerivativeStructure deltaX = new DerivativeStructure(parameters, order, derivatives);
+ monomial = monomial.multiply(deltaX);
+ }
+ interpolation = interpolation.add(monomial.multiply(top[i]));
+ }
+
+ return interpolation;
+
+ }
+
+ /** {@inheritDoc}
+ * <p>The returned object cannot compute derivatives to arbitrary orders. The
+ * value function will throw a {@link NumberIsTooLargeException} if the requested
+ * derivation order is larger or equal to the number of points.
+ * </p>
+ */
+ public UnivariateDifferentiableFunction differentiate(final UnivariateFunction function) {
+ return new UnivariateDifferentiableFunction() {
+
+ /** {@inheritDoc} */
+ public double value(final double x) throws MathIllegalArgumentException {
+ return function.value(x);
+ }
+
+ /** {@inheritDoc} */
+ public DerivativeStructure value(final DerivativeStructure t)
+ throws MathIllegalArgumentException {
+
+ // check we can achieve the requested derivation order with the sample
+ if (t.getOrder() >= nbPoints) {
+ throw new NumberIsTooLargeException(t.getOrder(), nbPoints, false);
+ }
+
+ // compute sample position, trying to be centered if possible
+ final double t0 = FastMath.max(FastMath.min(t.getValue(), tMax), tMin) - halfSampleSpan;
+
+ // compute sample points
+ final double[] y = new double[nbPoints];
+ for (int i = 0; i < nbPoints; ++i) {
+ y[i] = function.value(t0 + i * stepSize);
+ }
+
+ // evaluate derivatives
+ return evaluate(t, t0, y);
+
+ }
+
+ };
+ }
+
+ /** {@inheritDoc}
+ * <p>The returned object cannot compute derivatives to arbitrary orders. The
+ * value function will throw a {@link NumberIsTooLargeException} if the requested
+ * derivation order is larger or equal to the number of points.
+ * </p>
+ */
+ public UnivariateDifferentiableVectorFunction differentiate(final UnivariateVectorFunction function) {
+ return new UnivariateDifferentiableVectorFunction() {
+
+ /** {@inheritDoc} */
+ public double[]value(final double x) throws MathIllegalArgumentException {
+ return function.value(x);
+ }
+
+ /** {@inheritDoc} */
+ public DerivativeStructure[] value(final DerivativeStructure t)
+ throws MathIllegalArgumentException {
+
+ // check we can achieve the requested derivation order with the sample
+ if (t.getOrder() >= nbPoints) {
+ throw new NumberIsTooLargeException(t.getOrder(), nbPoints, false);
+ }
+
+ // compute sample position, trying to be centered if possible
+ final double t0 = FastMath.max(FastMath.min(t.getValue(), tMax), tMin) - halfSampleSpan;
+
+ // compute sample points
+ double[][] y = null;
+ for (int i = 0; i < nbPoints; ++i) {
+ final double[] v = function.value(t0 + i * stepSize);
+ if (i == 0) {
+ y = new double[v.length][nbPoints];
+ }
+ for (int j = 0; j < v.length; ++j) {
+ y[j][i] = v[j];
+ }
+ }
+
+ // evaluate derivatives
+ final DerivativeStructure[] value = new DerivativeStructure[y.length];
+ for (int j = 0; j < value.length; ++j) {
+ value[j] = evaluate(t, t0, y[j]);
+ }
+
+ return value;
+
+ }
+
+ };
+ }
+
+ /** {@inheritDoc}
+ * <p>The returned object cannot compute derivatives to arbitrary orders. The
+ * value function will throw a {@link NumberIsTooLargeException} if the requested
+ * derivation order is larger or equal to the number of points.
+ * </p>
+ */
+ public UnivariateDifferentiableMatrixFunction differentiate(final UnivariateMatrixFunction function) {
+ return new UnivariateDifferentiableMatrixFunction() {
+
+ /** {@inheritDoc} */
+ public double[][] value(final double x) throws MathIllegalArgumentException {
+ return function.value(x);
+ }
+
+ /** {@inheritDoc} */
+ public DerivativeStructure[][] value(final DerivativeStructure t)
+ throws MathIllegalArgumentException {
+
+ // check we can achieve the requested derivation order with the sample
+ if (t.getOrder() >= nbPoints) {
+ throw new NumberIsTooLargeException(t.getOrder(), nbPoints, false);
+ }
+
+ // compute sample position, trying to be centered if possible
+ final double t0 = FastMath.max(FastMath.min(t.getValue(), tMax), tMin) - halfSampleSpan;
+
+ // compute sample points
+ double[][][] y = null;
+ for (int i = 0; i < nbPoints; ++i) {
+ final double[][] v = function.value(t0 + i * stepSize);
+ if (i == 0) {
+ y = new double[v.length][v[0].length][nbPoints];
+ }
+ for (int j = 0; j < v.length; ++j) {
+ for (int k = 0; k < v[j].length; ++k) {
+ y[j][k][i] = v[j][k];
+ }
+ }
+ }
+
+ // evaluate derivatives
+ final DerivativeStructure[][] value = new DerivativeStructure[y.length][y[0].length];
+ for (int j = 0; j < value.length; ++j) {
+ for (int k = 0; k < y[j].length; ++k) {
+ value[j][k] = evaluate(t, t0, y[j][k]);
+ }
+ }
+
+ return value;
+
+ }
+
+ };
+ }
+
+}
diff --git a/src/main/java/org/apache/commons/math3/analysis/differentiation/GradientFunction.java b/src/main/java/org/apache/commons/math3/analysis/differentiation/GradientFunction.java
new file mode 100644
index 0000000..25aa7c7
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/analysis/differentiation/GradientFunction.java
@@ -0,0 +1,65 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math3.analysis.differentiation;
+
+import org.apache.commons.math3.analysis.MultivariateVectorFunction;
+
+/** Class representing the gradient of a multivariate function.
+ * <p>
+ * The vectorial components of the function represent the derivatives
+ * with respect to each function parameters.
+ * </p>
+ * @since 3.1
+ */
+public class GradientFunction implements MultivariateVectorFunction {
+
+ /** Underlying real-valued function. */
+ private final MultivariateDifferentiableFunction f;
+
+ /** Simple constructor.
+ * @param f underlying real-valued function
+ */
+ public GradientFunction(final MultivariateDifferentiableFunction f) {
+ this.f = f;
+ }
+
+ /** {@inheritDoc} */
+ public double[] value(double[] point) {
+
+ // set up parameters
+ final DerivativeStructure[] dsX = new DerivativeStructure[point.length];
+ for (int i = 0; i < point.length; ++i) {
+ dsX[i] = new DerivativeStructure(point.length, 1, i, point[i]);
+ }
+
+ // compute the derivatives
+ final DerivativeStructure dsY = f.value(dsX);
+
+ // extract the gradient
+ final double[] y = new double[point.length];
+ final int[] orders = new int[point.length];
+ for (int i = 0; i < point.length; ++i) {
+ orders[i] = 1;
+ y[i] = dsY.getPartialDerivative(orders);
+ orders[i] = 0;
+ }
+
+ return y;
+
+ }
+
+}
diff --git a/src/main/java/org/apache/commons/math3/analysis/differentiation/JacobianFunction.java b/src/main/java/org/apache/commons/math3/analysis/differentiation/JacobianFunction.java
new file mode 100644
index 0000000..0de47db
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/analysis/differentiation/JacobianFunction.java
@@ -0,0 +1,69 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math3.analysis.differentiation;
+
+import org.apache.commons.math3.analysis.MultivariateMatrixFunction;
+
+/** Class representing the Jacobian of a multivariate vector function.
+ * <p>
+ * The rows iterate on the model functions while the columns iterate on the parameters; thus,
+ * the numbers of rows is equal to the dimension of the underlying function vector
+ * value and the number of columns is equal to the number of free parameters of
+ * the underlying function.
+ * </p>
+ * @since 3.1
+ */
+public class JacobianFunction implements MultivariateMatrixFunction {
+
+ /** Underlying vector-valued function. */
+ private final MultivariateDifferentiableVectorFunction f;
+
+ /** Simple constructor.
+ * @param f underlying vector-valued function
+ */
+ public JacobianFunction(final MultivariateDifferentiableVectorFunction f) {
+ this.f = f;
+ }
+
+ /** {@inheritDoc} */
+ public double[][] value(double[] point) {
+
+ // set up parameters
+ final DerivativeStructure[] dsX = new DerivativeStructure[point.length];
+ for (int i = 0; i < point.length; ++i) {
+ dsX[i] = new DerivativeStructure(point.length, 1, i, point[i]);
+ }
+
+ // compute the derivatives
+ final DerivativeStructure[] dsY = f.value(dsX);
+
+ // extract the Jacobian
+ final double[][] y = new double[dsY.length][point.length];
+ final int[] orders = new int[point.length];
+ for (int i = 0; i < dsY.length; ++i) {
+ for (int j = 0; j < point.length; ++j) {
+ orders[j] = 1;
+ y[i][j] = dsY[i].getPartialDerivative(orders);
+ orders[j] = 0;
+ }
+ }
+
+ return y;
+
+ }
+
+}
diff --git a/src/main/java/org/apache/commons/math3/analysis/differentiation/MultivariateDifferentiableFunction.java b/src/main/java/org/apache/commons/math3/analysis/differentiation/MultivariateDifferentiableFunction.java
new file mode 100644
index 0000000..443671e
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/analysis/differentiation/MultivariateDifferentiableFunction.java
@@ -0,0 +1,42 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math3.analysis.differentiation;
+
+import org.apache.commons.math3.analysis.MultivariateFunction;
+import org.apache.commons.math3.exception.MathIllegalArgumentException;
+
+/**
+ * Extension of {@link MultivariateFunction} representing a
+ * multivariate differentiable real function.
+ * @since 3.1
+ */
+public interface MultivariateDifferentiableFunction extends MultivariateFunction {
+
+ /**
+ * Compute the value for the function at the given point.
+ *
+ * @param point Point at which the function must be evaluated.
+ * @return the function value for the given point.
+ * @exception MathIllegalArgumentException if {@code point} does not
+ * satisfy the function's constraints (wrong dimension, argument out of bound,
+ * or unsupported derivative order for example)
+ */
+ DerivativeStructure value(DerivativeStructure[] point)
+ throws MathIllegalArgumentException;
+
+}
diff --git a/src/main/java/org/apache/commons/math3/analysis/differentiation/MultivariateDifferentiableVectorFunction.java b/src/main/java/org/apache/commons/math3/analysis/differentiation/MultivariateDifferentiableVectorFunction.java
new file mode 100644
index 0000000..a5987ae
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/analysis/differentiation/MultivariateDifferentiableVectorFunction.java
@@ -0,0 +1,43 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math3.analysis.differentiation;
+
+import org.apache.commons.math3.analysis.MultivariateVectorFunction;
+import org.apache.commons.math3.exception.MathIllegalArgumentException;
+
+
+/**
+ * Extension of {@link MultivariateVectorFunction} representing a
+ * multivariate differentiable vectorial function.
+ * @since 3.1
+ */
+public interface MultivariateDifferentiableVectorFunction
+ extends MultivariateVectorFunction {
+
+ /**
+ * Compute the value for the function at the given point.
+ * @param point point at which the function must be evaluated
+ * @return function value for the given point
+ * @exception MathIllegalArgumentException if {@code point} does not
+ * satisfy the function's constraints (wrong dimension, argument out of bound,
+ * or unsupported derivative order for example)
+ */
+ DerivativeStructure[] value(DerivativeStructure[] point)
+ throws MathIllegalArgumentException;
+
+}
diff --git a/src/main/java/org/apache/commons/math3/analysis/differentiation/SparseGradient.java b/src/main/java/org/apache/commons/math3/analysis/differentiation/SparseGradient.java
new file mode 100644
index 0000000..8a8d8ae
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/analysis/differentiation/SparseGradient.java
@@ -0,0 +1,877 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math3.analysis.differentiation;
+
+import java.io.Serializable;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+
+import org.apache.commons.math3.Field;
+import org.apache.commons.math3.FieldElement;
+import org.apache.commons.math3.RealFieldElement;
+import org.apache.commons.math3.exception.DimensionMismatchException;
+import org.apache.commons.math3.util.FastMath;
+import org.apache.commons.math3.util.MathArrays;
+import org.apache.commons.math3.util.MathUtils;
+import org.apache.commons.math3.util.Precision;
+
+/**
+ * First derivative computation with large number of variables.
+ * <p>
+ * This class plays a similar role to {@link DerivativeStructure}, with
+ * a focus on efficiency when dealing with large number of independent variables
+ * and most computation depend only on a few of them, and when only first derivative
+ * is desired. When these conditions are met, this class should be much faster than
+ * {@link DerivativeStructure} and use less memory.
+ * </p>
+ *
+ * @since 3.3
+ */
+public class SparseGradient implements RealFieldElement<SparseGradient>, Serializable {
+
+ /** Serializable UID. */
+ private static final long serialVersionUID = 20131025L;
+
+ /** Value of the calculation. */
+ private double value;
+
+ /** Stored derivative, each key representing a different independent variable. */
+ private final Map<Integer, Double> derivatives;
+
+ /** Internal constructor.
+ * @param value value of the function
+ * @param derivatives derivatives map, a deep copy will be performed,
+ * so the map given here will remain safe from changes in the new instance,
+ * may be null to create an empty derivatives map, i.e. a constant value
+ */
+ private SparseGradient(final double value, final Map<Integer, Double> derivatives) {
+ this.value = value;
+ this.derivatives = new HashMap<Integer, Double>();
+ if (derivatives != null) {
+ this.derivatives.putAll(derivatives);
+ }
+ }
+
+ /** Internal constructor.
+ * @param value value of the function
+ * @param scale scaling factor to apply to all derivatives
+ * @param derivatives derivatives map, a deep copy will be performed,
+ * so the map given here will remain safe from changes in the new instance,
+ * may be null to create an empty derivatives map, i.e. a constant value
+ */
+ private SparseGradient(final double value, final double scale,
+ final Map<Integer, Double> derivatives) {
+ this.value = value;
+ this.derivatives = new HashMap<Integer, Double>();
+ if (derivatives != null) {
+ for (final Map.Entry<Integer, Double> entry : derivatives.entrySet()) {
+ this.derivatives.put(entry.getKey(), scale * entry.getValue());
+ }
+ }
+ }
+
+ /** Factory method creating a constant.
+ * @param value value of the constant
+ * @return a new instance
+ */
+ public static SparseGradient createConstant(final double value) {
+ return new SparseGradient(value, Collections.<Integer, Double> emptyMap());
+ }
+
+ /** Factory method creating an independent variable.
+ * @param idx index of the variable
+ * @param value value of the variable
+ * @return a new instance
+ */
+ public static SparseGradient createVariable(final int idx, final double value) {
+ return new SparseGradient(value, Collections.singletonMap(idx, 1.0));
+ }
+
+ /**
+ * Find the number of variables.
+ * @return number of variables
+ */
+ public int numVars() {
+ return derivatives.size();
+ }
+
+ /**
+ * Get the derivative with respect to a particular index variable.
+ *
+ * @param index index to differentiate with.
+ * @return derivative with respect to a particular index variable
+ */
+ public double getDerivative(final int index) {
+ final Double out = derivatives.get(index);
+ return (out == null) ? 0.0 : out;
+ }
+
+ /**
+ * Get the value of the function.
+ * @return value of the function.
+ */
+ public double getValue() {
+ return value;
+ }
+
+ /** {@inheritDoc} */
+ public double getReal() {
+ return value;
+ }
+
+ /** {@inheritDoc} */
+ public SparseGradient add(final SparseGradient a) {
+ final SparseGradient out = new SparseGradient(value + a.value, derivatives);
+ for (Map.Entry<Integer, Double> entry : a.derivatives.entrySet()) {
+ final int id = entry.getKey();
+ final Double old = out.derivatives.get(id);
+ if (old == null) {
+ out.derivatives.put(id, entry.getValue());
+ } else {
+ out.derivatives.put(id, old + entry.getValue());
+ }
+ }
+
+ return out;
+ }
+
+ /**
+ * Add in place.
+ * <p>
+ * This method is designed to be faster when used multiple times in a loop.
+ * </p>
+ * <p>
+ * The instance is changed here, in order to not change the
+ * instance the {@link #add(SparseGradient)} method should
+ * be used.
+ * </p>
+ * @param a instance to add
+ */
+ public void addInPlace(final SparseGradient a) {
+ value += a.value;
+ for (final Map.Entry<Integer, Double> entry : a.derivatives.entrySet()) {
+ final int id = entry.getKey();
+ final Double old = derivatives.get(id);
+ if (old == null) {
+ derivatives.put(id, entry.getValue());
+ } else {
+ derivatives.put(id, old + entry.getValue());
+ }
+ }
+ }
+
+ /** {@inheritDoc} */
+ public SparseGradient add(final double c) {
+ final SparseGradient out = new SparseGradient(value + c, derivatives);
+ return out;
+ }
+
+ /** {@inheritDoc} */
+ public SparseGradient subtract(final SparseGradient a) {
+ final SparseGradient out = new SparseGradient(value - a.value, derivatives);
+ for (Map.Entry<Integer, Double> entry : a.derivatives.entrySet()) {
+ final int id = entry.getKey();
+ final Double old = out.derivatives.get(id);
+ if (old == null) {
+ out.derivatives.put(id, -entry.getValue());
+ } else {
+ out.derivatives.put(id, old - entry.getValue());
+ }
+ }
+ return out;
+ }
+
+ /** {@inheritDoc} */
+ public SparseGradient subtract(double c) {
+ return new SparseGradient(value - c, derivatives);
+ }
+
+ /** {@inheritDoc} */
+ public SparseGradient multiply(final SparseGradient a) {
+ final SparseGradient out =
+ new SparseGradient(value * a.value, Collections.<Integer, Double> emptyMap());
+
+ // Derivatives.
+ for (Map.Entry<Integer, Double> entry : derivatives.entrySet()) {
+ out.derivatives.put(entry.getKey(), a.value * entry.getValue());
+ }
+ for (Map.Entry<Integer, Double> entry : a.derivatives.entrySet()) {
+ final int id = entry.getKey();
+ final Double old = out.derivatives.get(id);
+ if (old == null) {
+ out.derivatives.put(id, value * entry.getValue());
+ } else {
+ out.derivatives.put(id, old + value * entry.getValue());
+ }
+ }
+ return out;
+ }
+
+ /**
+ * Multiply in place.
+ * <p>
+ * This method is designed to be faster when used multiple times in a loop.
+ * </p>
+ * <p>
+ * The instance is changed here, in order to not change the
+ * instance the {@link #add(SparseGradient)} method should
+ * be used.
+ * </p>
+ * @param a instance to multiply
+ */
+ public void multiplyInPlace(final SparseGradient a) {
+ // Derivatives.
+ for (Map.Entry<Integer, Double> entry : derivatives.entrySet()) {
+ derivatives.put(entry.getKey(), a.value * entry.getValue());
+ }
+ for (Map.Entry<Integer, Double> entry : a.derivatives.entrySet()) {
+ final int id = entry.getKey();
+ final Double old = derivatives.get(id);
+ if (old == null) {
+ derivatives.put(id, value * entry.getValue());
+ } else {
+ derivatives.put(id, old + value * entry.getValue());
+ }
+ }
+ value *= a.value;
+ }
+
+ /** {@inheritDoc} */
+ public SparseGradient multiply(final double c) {
+ return new SparseGradient(value * c, c, derivatives);
+ }
+
+ /** {@inheritDoc} */
+ public SparseGradient multiply(final int n) {
+ return new SparseGradient(value * n, n, derivatives);
+ }
+
+ /** {@inheritDoc} */
+ public SparseGradient divide(final SparseGradient a) {
+ final SparseGradient out = new SparseGradient(value / a.value, Collections.<Integer, Double> emptyMap());
+
+ // Derivatives.
+ for (Map.Entry<Integer, Double> entry : derivatives.entrySet()) {
+ out.derivatives.put(entry.getKey(), entry.getValue() / a.value);
+ }
+ for (Map.Entry<Integer, Double> entry : a.derivatives.entrySet()) {
+ final int id = entry.getKey();
+ final Double old = out.derivatives.get(id);
+ if (old == null) {
+ out.derivatives.put(id, -out.value / a.value * entry.getValue());
+ } else {
+ out.derivatives.put(id, old - out.value / a.value * entry.getValue());
+ }
+ }
+ return out;
+ }
+
+ /** {@inheritDoc} */
+ public SparseGradient divide(final double c) {
+ return new SparseGradient(value / c, 1.0 / c, derivatives);
+ }
+
+ /** {@inheritDoc} */
+ public SparseGradient negate() {
+ return new SparseGradient(-value, -1.0, derivatives);
+ }
+
+ /** {@inheritDoc} */
+ public Field<SparseGradient> getField() {
+ return new Field<SparseGradient>() {
+
+ /** {@inheritDoc} */
+ public SparseGradient getZero() {
+ return createConstant(0);
+ }
+
+ /** {@inheritDoc} */
+ public SparseGradient getOne() {
+ return createConstant(1);
+ }
+
+ /** {@inheritDoc} */
+ public Class<? extends FieldElement<SparseGradient>> getRuntimeClass() {
+ return SparseGradient.class;
+ }
+
+ };
+ }
+
+ /** {@inheritDoc} */
+ public SparseGradient remainder(final double a) {
+ return new SparseGradient(FastMath.IEEEremainder(value, a), derivatives);
+ }
+
+ /** {@inheritDoc} */
+ public SparseGradient remainder(final SparseGradient a) {
+
+ // compute k such that lhs % rhs = lhs - k rhs
+ final double rem = FastMath.IEEEremainder(value, a.value);
+ final double k = FastMath.rint((value - rem) / a.value);
+
+ return subtract(a.multiply(k));
+
+ }
+
+ /** {@inheritDoc} */
+ public SparseGradient abs() {
+ if (Double.doubleToLongBits(value) < 0) {
+ // we use the bits representation to also handle -0.0
+ return negate();
+ } else {
+ return this;
+ }
+ }
+
+ /** {@inheritDoc} */
+ public SparseGradient ceil() {
+ return createConstant(FastMath.ceil(value));
+ }
+
+ /** {@inheritDoc} */
+ public SparseGradient floor() {
+ return createConstant(FastMath.floor(value));
+ }
+
+ /** {@inheritDoc} */
+ public SparseGradient rint() {
+ return createConstant(FastMath.rint(value));
+ }
+
+ /** {@inheritDoc} */
+ public long round() {
+ return FastMath.round(value);
+ }
+
+ /** {@inheritDoc} */
+ public SparseGradient signum() {
+ return createConstant(FastMath.signum(value));
+ }
+
+ /** {@inheritDoc} */
+ public SparseGradient copySign(final SparseGradient sign) {
+ final long m = Double.doubleToLongBits(value);
+ final long s = Double.doubleToLongBits(sign.value);
+ if ((m >= 0 && s >= 0) || (m < 0 && s < 0)) { // Sign is currently OK
+ return this;
+ }
+ return negate(); // flip sign
+ }
+
+ /** {@inheritDoc} */
+ public SparseGradient copySign(final double sign) {
+ final long m = Double.doubleToLongBits(value);
+ final long s = Double.doubleToLongBits(sign);
+ if ((m >= 0 && s >= 0) || (m < 0 && s < 0)) { // Sign is currently OK
+ return this;
+ }
+ return negate(); // flip sign
+ }
+
+ /** {@inheritDoc} */
+ public SparseGradient scalb(final int n) {
+ final SparseGradient out = new SparseGradient(FastMath.scalb(value, n), Collections.<Integer, Double> emptyMap());
+ for (Map.Entry<Integer, Double> entry : derivatives.entrySet()) {
+ out.derivatives.put(entry.getKey(), FastMath.scalb(entry.getValue(), n));
+ }
+ return out;
+ }
+
+ /** {@inheritDoc} */
+ public SparseGradient hypot(final SparseGradient y) {
+ if (Double.isInfinite(value) || Double.isInfinite(y.value)) {
+ return createConstant(Double.POSITIVE_INFINITY);
+ } else if (Double.isNaN(value) || Double.isNaN(y.value)) {
+ return createConstant(Double.NaN);
+ } else {
+
+ final int expX = FastMath.getExponent(value);
+ final int expY = FastMath.getExponent(y.value);
+ if (expX > expY + 27) {
+ // y is negligible with respect to x
+ return abs();
+ } else if (expY > expX + 27) {
+ // x is negligible with respect to y
+ return y.abs();
+ } else {
+
+ // find an intermediate scale to avoid both overflow and underflow
+ final int middleExp = (expX + expY) / 2;
+
+ // scale parameters without losing precision
+ final SparseGradient scaledX = scalb(-middleExp);
+ final SparseGradient scaledY = y.scalb(-middleExp);
+
+ // compute scaled hypotenuse
+ final SparseGradient scaledH =
+ scaledX.multiply(scaledX).add(scaledY.multiply(scaledY)).sqrt();
+
+ // remove scaling
+ return scaledH.scalb(middleExp);
+
+ }
+
+ }
+ }
+
+ /**
+ * Returns the hypotenuse of a triangle with sides {@code x} and {@code y}
+ * - sqrt(<i>x</i><sup>2</sup>&nbsp;+<i>y</i><sup>2</sup>)
+ * avoiding intermediate overflow or underflow.
+ *
+ * <ul>
+ * <li> If either argument is infinite, then the result is positive infinity.</li>
+ * <li> else, if either argument is NaN then the result is NaN.</li>
+ * </ul>
+ *
+ * @param x a value
+ * @param y a value
+ * @return sqrt(<i>x</i><sup>2</sup>&nbsp;+<i>y</i><sup>2</sup>)
+ */
+ public static SparseGradient hypot(final SparseGradient x, final SparseGradient y) {
+ return x.hypot(y);
+ }
+
+ /** {@inheritDoc} */
+ public SparseGradient reciprocal() {
+ return new SparseGradient(1.0 / value, -1.0 / (value * value), derivatives);
+ }
+
+ /** {@inheritDoc} */
+ public SparseGradient sqrt() {
+ final double sqrt = FastMath.sqrt(value);
+ return new SparseGradient(sqrt, 0.5 / sqrt, derivatives);
+ }
+
+ /** {@inheritDoc} */
+ public SparseGradient cbrt() {
+ final double cbrt = FastMath.cbrt(value);
+ return new SparseGradient(cbrt, 1.0 / (3 * cbrt * cbrt), derivatives);
+ }
+
+ /** {@inheritDoc} */
+ public SparseGradient rootN(final int n) {
+ if (n == 2) {
+ return sqrt();
+ } else if (n == 3) {
+ return cbrt();
+ } else {
+ final double root = FastMath.pow(value, 1.0 / n);
+ return new SparseGradient(root, 1.0 / (n * FastMath.pow(root, n - 1)), derivatives);
+ }
+ }
+
+ /** {@inheritDoc} */
+ public SparseGradient pow(final double p) {
+ return new SparseGradient(FastMath.pow(value, p), p * FastMath.pow(value, p - 1), derivatives);
+ }
+
+ /** {@inheritDoc} */
+ public SparseGradient pow(final int n) {
+ if (n == 0) {
+ return getField().getOne();
+ } else {
+ final double valueNm1 = FastMath.pow(value, n - 1);
+ return new SparseGradient(value * valueNm1, n * valueNm1, derivatives);
+ }
+ }
+
+ /** {@inheritDoc} */
+ public SparseGradient pow(final SparseGradient e) {
+ return log().multiply(e).exp();
+ }
+
+ /** Compute a<sup>x</sup> where a is a double and x a {@link SparseGradient}
+ * @param a number to exponentiate
+ * @param x power to apply
+ * @return a<sup>x</sup>
+ */
+ public static SparseGradient pow(final double a, final SparseGradient x) {
+ if (a == 0) {
+ if (x.value == 0) {
+ return x.compose(1.0, Double.NEGATIVE_INFINITY);
+ } else if (x.value < 0) {
+ return x.compose(Double.NaN, Double.NaN);
+ } else {
+ return x.getField().getZero();
+ }
+ } else {
+ final double ax = FastMath.pow(a, x.value);
+ return new SparseGradient(ax, ax * FastMath.log(a), x.derivatives);
+ }
+ }
+
+ /** {@inheritDoc} */
+ public SparseGradient exp() {
+ final double e = FastMath.exp(value);
+ return new SparseGradient(e, e, derivatives);
+ }
+
+ /** {@inheritDoc} */
+ public SparseGradient expm1() {
+ return new SparseGradient(FastMath.expm1(value), FastMath.exp(value), derivatives);
+ }
+
+ /** {@inheritDoc} */
+ public SparseGradient log() {
+ return new SparseGradient(FastMath.log(value), 1.0 / value, derivatives);
+ }
+
+ /** Base 10 logarithm.
+ * @return base 10 logarithm of the instance
+ */
+ public SparseGradient log10() {
+ return new SparseGradient(FastMath.log10(value), 1.0 / (FastMath.log(10.0) * value), derivatives);
+ }
+
+ /** {@inheritDoc} */
+ public SparseGradient log1p() {
+ return new SparseGradient(FastMath.log1p(value), 1.0 / (1.0 + value), derivatives);
+ }
+
+ /** {@inheritDoc} */
+ public SparseGradient cos() {
+ return new SparseGradient(FastMath.cos(value), -FastMath.sin(value), derivatives);
+ }
+
+ /** {@inheritDoc} */
+ public SparseGradient sin() {
+ return new SparseGradient(FastMath.sin(value), FastMath.cos(value), derivatives);
+ }
+
+ /** {@inheritDoc} */
+ public SparseGradient tan() {
+ final double t = FastMath.tan(value);
+ return new SparseGradient(t, 1 + t * t, derivatives);
+ }
+
+ /** {@inheritDoc} */
+ public SparseGradient acos() {
+ return new SparseGradient(FastMath.acos(value), -1.0 / FastMath.sqrt(1 - value * value), derivatives);
+ }
+
+ /** {@inheritDoc} */
+ public SparseGradient asin() {
+ return new SparseGradient(FastMath.asin(value), 1.0 / FastMath.sqrt(1 - value * value), derivatives);
+ }
+
+ /** {@inheritDoc} */
+ public SparseGradient atan() {
+ return new SparseGradient(FastMath.atan(value), 1.0 / (1 + value * value), derivatives);
+ }
+
+ /** {@inheritDoc} */
+ public SparseGradient atan2(final SparseGradient x) {
+
+ // compute r = sqrt(x^2+y^2)
+ final SparseGradient r = multiply(this).add(x.multiply(x)).sqrt();
+
+ final SparseGradient a;
+ if (x.value >= 0) {
+
+ // compute atan2(y, x) = 2 atan(y / (r + x))
+ a = divide(r.add(x)).atan().multiply(2);
+
+ } else {
+
+ // compute atan2(y, x) = +/- pi - 2 atan(y / (r - x))
+ final SparseGradient tmp = divide(r.subtract(x)).atan().multiply(-2);
+ a = tmp.add(tmp.value <= 0 ? -FastMath.PI : FastMath.PI);
+
+ }
+
+ // fix value to take special cases (+0/+0, +0/-0, -0/+0, -0/-0, +/-infinity) correctly
+ a.value = FastMath.atan2(value, x.value);
+
+ return a;
+
+ }
+
+ /** Two arguments arc tangent operation.
+ * @param y first argument of the arc tangent
+ * @param x second argument of the arc tangent
+ * @return atan2(y, x)
+ */
+ public static SparseGradient atan2(final SparseGradient y, final SparseGradient x) {
+ return y.atan2(x);
+ }
+
+ /** {@inheritDoc} */
+ public SparseGradient cosh() {
+ return new SparseGradient(FastMath.cosh(value), FastMath.sinh(value), derivatives);
+ }
+
+ /** {@inheritDoc} */
+ public SparseGradient sinh() {
+ return new SparseGradient(FastMath.sinh(value), FastMath.cosh(value), derivatives);
+ }
+
+ /** {@inheritDoc} */
+ public SparseGradient tanh() {
+ final double t = FastMath.tanh(value);
+ return new SparseGradient(t, 1 - t * t, derivatives);
+ }
+
+ /** {@inheritDoc} */
+ public SparseGradient acosh() {
+ return new SparseGradient(FastMath.acosh(value), 1.0 / FastMath.sqrt(value * value - 1.0), derivatives);
+ }
+
+ /** {@inheritDoc} */
+ public SparseGradient asinh() {
+ return new SparseGradient(FastMath.asinh(value), 1.0 / FastMath.sqrt(value * value + 1.0), derivatives);
+ }
+
+ /** {@inheritDoc} */
+ public SparseGradient atanh() {
+ return new SparseGradient(FastMath.atanh(value), 1.0 / (1.0 - value * value), derivatives);
+ }
+
+ /** Convert radians to degrees, with error of less than 0.5 ULP
+ * @return instance converted into degrees
+ */
+ public SparseGradient toDegrees() {
+ return new SparseGradient(FastMath.toDegrees(value), FastMath.toDegrees(1.0), derivatives);
+ }
+
+ /** Convert degrees to radians, with error of less than 0.5 ULP
+ * @return instance converted into radians
+ */
+ public SparseGradient toRadians() {
+ return new SparseGradient(FastMath.toRadians(value), FastMath.toRadians(1.0), derivatives);
+ }
+
+ /** Evaluate Taylor expansion of a sparse gradient.
+ * @param delta parameters offsets (&Delta;x, &Delta;y, ...)
+ * @return value of the Taylor expansion at x + &Delta;x, y + &Delta;y, ...
+ */
+ public double taylor(final double ... delta) {
+ double y = value;
+ for (int i = 0; i < delta.length; ++i) {
+ y += delta[i] * getDerivative(i);
+ }
+ return y;
+ }
+
+ /** Compute composition of the instance by a univariate function.
+ * @param f0 value of the function at (i.e. f({@link #getValue()}))
+ * @param f1 first derivative of the function at
+ * the current point (i.e. f'({@link #getValue()}))
+ * @return f(this)
+ */
+ public SparseGradient compose(final double f0, final double f1) {
+ return new SparseGradient(f0, f1, derivatives);
+ }
+
+ /** {@inheritDoc} */
+ public SparseGradient linearCombination(final SparseGradient[] a,
+ final SparseGradient[] b)
+ throws DimensionMismatchException {
+
+ // compute a simple value, with all partial derivatives
+ SparseGradient out = a[0].getField().getZero();
+ for (int i = 0; i < a.length; ++i) {
+ out = out.add(a[i].multiply(b[i]));
+ }
+
+ // recompute an accurate value, taking care of cancellations
+ final double[] aDouble = new double[a.length];
+ for (int i = 0; i < a.length; ++i) {
+ aDouble[i] = a[i].getValue();
+ }
+ final double[] bDouble = new double[b.length];
+ for (int i = 0; i < b.length; ++i) {
+ bDouble[i] = b[i].getValue();
+ }
+ out.value = MathArrays.linearCombination(aDouble, bDouble);
+
+ return out;
+
+ }
+
+ /** {@inheritDoc} */
+ public SparseGradient linearCombination(final double[] a, final SparseGradient[] b) {
+
+ // compute a simple value, with all partial derivatives
+ SparseGradient out = b[0].getField().getZero();
+ for (int i = 0; i < a.length; ++i) {
+ out = out.add(b[i].multiply(a[i]));
+ }
+
+ // recompute an accurate value, taking care of cancellations
+ final double[] bDouble = new double[b.length];
+ for (int i = 0; i < b.length; ++i) {
+ bDouble[i] = b[i].getValue();
+ }
+ out.value = MathArrays.linearCombination(a, bDouble);
+
+ return out;
+
+ }
+
+ /** {@inheritDoc} */
+ public SparseGradient linearCombination(final SparseGradient a1, final SparseGradient b1,
+ final SparseGradient a2, final SparseGradient b2) {
+
+ // compute a simple value, with all partial derivatives
+ SparseGradient out = a1.multiply(b1).add(a2.multiply(b2));
+
+ // recompute an accurate value, taking care of cancellations
+ out.value = MathArrays.linearCombination(a1.value, b1.value, a2.value, b2.value);
+
+ return out;
+
+ }
+
+ /** {@inheritDoc} */
+ public SparseGradient linearCombination(final double a1, final SparseGradient b1,
+ final double a2, final SparseGradient b2) {
+
+ // compute a simple value, with all partial derivatives
+ SparseGradient out = b1.multiply(a1).add(b2.multiply(a2));
+
+ // recompute an accurate value, taking care of cancellations
+ out.value = MathArrays.linearCombination(a1, b1.value, a2, b2.value);
+
+ return out;
+
+ }
+
+ /** {@inheritDoc} */
+ public SparseGradient linearCombination(final SparseGradient a1, final SparseGradient b1,
+ final SparseGradient a2, final SparseGradient b2,
+ final SparseGradient a3, final SparseGradient b3) {
+
+ // compute a simple value, with all partial derivatives
+ SparseGradient out = a1.multiply(b1).add(a2.multiply(b2)).add(a3.multiply(b3));
+
+ // recompute an accurate value, taking care of cancellations
+ out.value = MathArrays.linearCombination(a1.value, b1.value,
+ a2.value, b2.value,
+ a3.value, b3.value);
+
+ return out;
+
+ }
+
+ /** {@inheritDoc} */
+ public SparseGradient linearCombination(final double a1, final SparseGradient b1,
+ final double a2, final SparseGradient b2,
+ final double a3, final SparseGradient b3) {
+
+ // compute a simple value, with all partial derivatives
+ SparseGradient out = b1.multiply(a1).add(b2.multiply(a2)).add(b3.multiply(a3));
+
+ // recompute an accurate value, taking care of cancellations
+ out.value = MathArrays.linearCombination(a1, b1.value,
+ a2, b2.value,
+ a3, b3.value);
+
+ return out;
+
+ }
+
+ /** {@inheritDoc} */
+ public SparseGradient linearCombination(final SparseGradient a1, final SparseGradient b1,
+ final SparseGradient a2, final SparseGradient b2,
+ final SparseGradient a3, final SparseGradient b3,
+ final SparseGradient a4, final SparseGradient b4) {
+
+ // compute a simple value, with all partial derivatives
+ SparseGradient out = a1.multiply(b1).add(a2.multiply(b2)).add(a3.multiply(b3)).add(a4.multiply(b4));
+
+ // recompute an accurate value, taking care of cancellations
+ out.value = MathArrays.linearCombination(a1.value, b1.value,
+ a2.value, b2.value,
+ a3.value, b3.value,
+ a4.value, b4.value);
+
+ return out;
+
+ }
+
+ /** {@inheritDoc} */
+ public SparseGradient linearCombination(final double a1, final SparseGradient b1,
+ final double a2, final SparseGradient b2,
+ final double a3, final SparseGradient b3,
+ final double a4, final SparseGradient b4) {
+
+ // compute a simple value, with all partial derivatives
+ SparseGradient out = b1.multiply(a1).add(b2.multiply(a2)).add(b3.multiply(a3)).add(b4.multiply(a4));
+
+ // recompute an accurate value, taking care of cancellations
+ out.value = MathArrays.linearCombination(a1, b1.value,
+ a2, b2.value,
+ a3, b3.value,
+ a4, b4.value);
+
+ return out;
+
+ }
+
+ /**
+ * Test for the equality of two sparse gradients.
+ * <p>
+ * Sparse gradients are considered equal if they have the same value
+ * and the same derivatives.
+ * </p>
+ * @param other Object to test for equality to this
+ * @return true if two sparse gradients are equal
+ */
+ @Override
+ public boolean equals(Object other) {
+
+ if (this == other) {
+ return true;
+ }
+
+ if (other instanceof SparseGradient) {
+ final SparseGradient rhs = (SparseGradient)other;
+ if (!Precision.equals(value, rhs.value, 1)) {
+ return false;
+ }
+ if (derivatives.size() != rhs.derivatives.size()) {
+ return false;
+ }
+ for (final Map.Entry<Integer, Double> entry : derivatives.entrySet()) {
+ if (!rhs.derivatives.containsKey(entry.getKey())) {
+ return false;
+ }
+ if (!Precision.equals(entry.getValue(), rhs.derivatives.get(entry.getKey()), 1)) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ return false;
+
+ }
+
+ /**
+ * Get a hashCode for the derivative structure.
+ * @return a hash code value for this object
+ * @since 3.2
+ */
+ @Override
+ public int hashCode() {
+ return 743 + 809 * MathUtils.hash(value) + 167 * derivatives.hashCode();
+ }
+
+}
diff --git a/src/main/java/org/apache/commons/math3/analysis/differentiation/UnivariateDifferentiableFunction.java b/src/main/java/org/apache/commons/math3/analysis/differentiation/UnivariateDifferentiableFunction.java
new file mode 100644
index 0000000..097b4e0
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/analysis/differentiation/UnivariateDifferentiableFunction.java
@@ -0,0 +1,43 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math3.analysis.differentiation;
+
+import org.apache.commons.math3.analysis.UnivariateFunction;
+import org.apache.commons.math3.exception.DimensionMismatchException;
+
+/** Interface for univariate functions derivatives.
+ * <p>This interface represents a simple function which computes
+ * both the value and the first derivative of a mathematical function.
+ * The derivative is computed with respect to the input variable.</p>
+ * @see UnivariateDifferentiableFunction
+ * @see UnivariateFunctionDifferentiator
+ * @since 3.1
+ */
+public interface UnivariateDifferentiableFunction extends UnivariateFunction {
+
+ /** Simple mathematical function.
+ * <p>{@link UnivariateDifferentiableFunction} classes compute both the
+ * value and the first derivative of the function.</p>
+ * @param t function input value
+ * @return function result
+ * @exception DimensionMismatchException if t is inconsistent with the
+ * function's free parameters or order
+ */
+ DerivativeStructure value(DerivativeStructure t)
+ throws DimensionMismatchException;
+
+}
diff --git a/src/main/java/org/apache/commons/math3/analysis/differentiation/UnivariateDifferentiableMatrixFunction.java b/src/main/java/org/apache/commons/math3/analysis/differentiation/UnivariateDifferentiableMatrixFunction.java
new file mode 100644
index 0000000..b31771b
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/analysis/differentiation/UnivariateDifferentiableMatrixFunction.java
@@ -0,0 +1,40 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math3.analysis.differentiation;
+
+import org.apache.commons.math3.analysis.UnivariateMatrixFunction;
+import org.apache.commons.math3.exception.MathIllegalArgumentException;
+
+/**
+ * Extension of {@link UnivariateMatrixFunction} representing a univariate differentiable matrix function.
+ *
+ * @since 3.1
+ */
+public interface UnivariateDifferentiableMatrixFunction
+ extends UnivariateMatrixFunction {
+
+ /**
+ * Compute the value for the function.
+ * @param x the point for which the function value should be computed
+ * @return the value
+ * @exception MathIllegalArgumentException if {@code x} does not
+ * satisfy the function's constraints (argument out of bound, or unsupported
+ * derivative order for example)
+ */
+ DerivativeStructure[][] value(DerivativeStructure x) throws MathIllegalArgumentException;
+
+}
diff --git a/src/main/java/org/apache/commons/math3/analysis/differentiation/UnivariateDifferentiableVectorFunction.java b/src/main/java/org/apache/commons/math3/analysis/differentiation/UnivariateDifferentiableVectorFunction.java
new file mode 100644
index 0000000..7e79eef
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/analysis/differentiation/UnivariateDifferentiableVectorFunction.java
@@ -0,0 +1,40 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math3.analysis.differentiation;
+
+import org.apache.commons.math3.analysis.UnivariateVectorFunction;
+import org.apache.commons.math3.exception.MathIllegalArgumentException;
+
+/**
+ * Extension of {@link UnivariateVectorFunction} representing a univariate differentiable vectorial function.
+ *
+ * @since 3.1
+ */
+public interface UnivariateDifferentiableVectorFunction
+ extends UnivariateVectorFunction {
+
+ /**
+ * Compute the value for the function.
+ * @param x the point for which the function value should be computed
+ * @return the value
+ * @exception MathIllegalArgumentException if {@code x} does not
+ * satisfy the function's constraints (argument out of bound, or unsupported
+ * derivative order for example)
+ */
+ DerivativeStructure[] value(DerivativeStructure x) throws MathIllegalArgumentException;
+
+}
diff --git a/src/main/java/org/apache/commons/math3/analysis/differentiation/UnivariateFunctionDifferentiator.java b/src/main/java/org/apache/commons/math3/analysis/differentiation/UnivariateFunctionDifferentiator.java
new file mode 100644
index 0000000..f19ce20
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/analysis/differentiation/UnivariateFunctionDifferentiator.java
@@ -0,0 +1,33 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math3.analysis.differentiation;
+
+import org.apache.commons.math3.analysis.UnivariateFunction;
+
+/** Interface defining the function differentiation operation.
+ * @since 3.1
+ */
+public interface UnivariateFunctionDifferentiator {
+
+ /** Create an implementation of a {@link UnivariateDifferentiableFunction
+ * differential} from a regular {@link UnivariateFunction function}.
+ * @param function function to differentiate
+ * @return differential function
+ */
+ UnivariateDifferentiableFunction differentiate(UnivariateFunction function);
+
+}
diff --git a/src/main/java/org/apache/commons/math3/analysis/differentiation/UnivariateMatrixFunctionDifferentiator.java b/src/main/java/org/apache/commons/math3/analysis/differentiation/UnivariateMatrixFunctionDifferentiator.java
new file mode 100644
index 0000000..bc0ccf3
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/analysis/differentiation/UnivariateMatrixFunctionDifferentiator.java
@@ -0,0 +1,33 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math3.analysis.differentiation;
+
+import org.apache.commons.math3.analysis.UnivariateMatrixFunction;
+
+/** Interface defining the function differentiation operation.
+ * @since 3.1
+ */
+public interface UnivariateMatrixFunctionDifferentiator {
+
+ /** Create an implementation of a {@link UnivariateDifferentiableMatrixFunction
+ * differential} from a regular {@link UnivariateMatrixFunction matrix function}.
+ * @param function function to differentiate
+ * @return differential function
+ */
+ UnivariateDifferentiableMatrixFunction differentiate(UnivariateMatrixFunction function);
+
+}
diff --git a/src/main/java/org/apache/commons/math3/analysis/differentiation/UnivariateVectorFunctionDifferentiator.java b/src/main/java/org/apache/commons/math3/analysis/differentiation/UnivariateVectorFunctionDifferentiator.java
new file mode 100644
index 0000000..5500c50
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/analysis/differentiation/UnivariateVectorFunctionDifferentiator.java
@@ -0,0 +1,33 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math3.analysis.differentiation;
+
+import org.apache.commons.math3.analysis.UnivariateVectorFunction;
+
+/** Interface defining the function differentiation operation.
+ * @since 3.1
+ */
+public interface UnivariateVectorFunctionDifferentiator {
+
+ /** Create an implementation of a {@link UnivariateDifferentiableVectorFunction
+ * differential} from a regular {@link UnivariateVectorFunction vector function}.
+ * @param function function to differentiate
+ * @return differential function
+ */
+ UnivariateDifferentiableVectorFunction differentiate(UnivariateVectorFunction function);
+
+}
diff --git a/src/main/java/org/apache/commons/math3/analysis/differentiation/package-info.java b/src/main/java/org/apache/commons/math3/analysis/differentiation/package-info.java
new file mode 100644
index 0000000..b828981
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/analysis/differentiation/package-info.java
@@ -0,0 +1,42 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/**
+ *
+ * <p>
+ * This package holds the main interfaces and basic building block classes
+ * dealing with differentiation.
+ * The core class is {@link org.apache.commons.math3.analysis.differentiation.DerivativeStructure
+ * DerivativeStructure} which holds the value and the differentials of a function. This class
+ * handles some arbitrary number of free parameters and arbitrary differentiation order. It is used
+ * both as the input and the output type for the {@link
+ * org.apache.commons.math3.analysis.differentiation.UnivariateDifferentiableFunction
+ * UnivariateDifferentiableFunction} interface. Any differentiable function should implement this
+ * interface.
+ * </p>
+ * <p>
+ * The {@link org.apache.commons.math3.analysis.differentiation.UnivariateFunctionDifferentiator
+ * UnivariateFunctionDifferentiator} interface defines a way to differentiate a simple {@link
+ * org.apache.commons.math3.analysis.UnivariateFunction UnivariateFunction} and get a {@link
+ * org.apache.commons.math3.analysis.differentiation.UnivariateDifferentiableFunction
+ * UnivariateDifferentiableFunction}.
+ * </p>
+ * <p>
+ * Similar interfaces also exist for multivariate functions and for vector or matrix valued functions.
+ * </p>
+ *
+ */
+package org.apache.commons.math3.analysis.differentiation;
diff --git a/src/main/java/org/apache/commons/math3/analysis/function/Abs.java b/src/main/java/org/apache/commons/math3/analysis/function/Abs.java
new file mode 100644
index 0000000..9db01fd
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/analysis/function/Abs.java
@@ -0,0 +1,33 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math3.analysis.function;
+
+import org.apache.commons.math3.analysis.UnivariateFunction;
+import org.apache.commons.math3.util.FastMath;
+
+/**
+ * Absolute value function.
+ *
+ * @since 3.0
+ */
+public class Abs implements UnivariateFunction {
+ /** {@inheritDoc} */
+ public double value(double x) {
+ return FastMath.abs(x);
+ }
+}
diff --git a/src/main/java/org/apache/commons/math3/analysis/function/Acos.java b/src/main/java/org/apache/commons/math3/analysis/function/Acos.java
new file mode 100644
index 0000000..a9df4b7
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/analysis/function/Acos.java
@@ -0,0 +1,53 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math3.analysis.function;
+
+import org.apache.commons.math3.analysis.FunctionUtils;
+import org.apache.commons.math3.analysis.UnivariateFunction;
+import org.apache.commons.math3.analysis.DifferentiableUnivariateFunction;
+import org.apache.commons.math3.analysis.differentiation.DerivativeStructure;
+import org.apache.commons.math3.analysis.differentiation.UnivariateDifferentiableFunction;
+import org.apache.commons.math3.util.FastMath;
+
+/**
+ * Arc-cosine function.
+ *
+ * @since 3.0
+ */
+public class Acos implements UnivariateDifferentiableFunction, DifferentiableUnivariateFunction {
+ /** {@inheritDoc} */
+ public double value(double x) {
+ return FastMath.acos(x);
+ }
+
+ /** {@inheritDoc}
+ * @deprecated as of 3.1, replaced by {@link #value(DerivativeStructure)}
+ */
+ @Deprecated
+ public UnivariateFunction derivative() {
+ return FunctionUtils.toDifferentiableUnivariateFunction(this).derivative();
+ }
+
+ /** {@inheritDoc}
+ * @since 3.1
+ */
+ public DerivativeStructure value(final DerivativeStructure t) {
+ return t.acos();
+ }
+
+}
diff --git a/src/main/java/org/apache/commons/math3/analysis/function/Acosh.java b/src/main/java/org/apache/commons/math3/analysis/function/Acosh.java
new file mode 100644
index 0000000..58fb19f
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/analysis/function/Acosh.java
@@ -0,0 +1,53 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math3.analysis.function;
+
+import org.apache.commons.math3.analysis.FunctionUtils;
+import org.apache.commons.math3.analysis.UnivariateFunction;
+import org.apache.commons.math3.analysis.DifferentiableUnivariateFunction;
+import org.apache.commons.math3.analysis.differentiation.DerivativeStructure;
+import org.apache.commons.math3.analysis.differentiation.UnivariateDifferentiableFunction;
+import org.apache.commons.math3.util.FastMath;
+
+/**
+ * Hyperbolic arc-cosine function.
+ *
+ * @since 3.0
+ */
+public class Acosh implements UnivariateDifferentiableFunction, DifferentiableUnivariateFunction {
+ /** {@inheritDoc} */
+ public double value(double x) {
+ return FastMath.acosh(x);
+ }
+
+ /** {@inheritDoc}
+ * @deprecated as of 3.1, replaced by {@link #value(DerivativeStructure)}
+ */
+ @Deprecated
+ public UnivariateFunction derivative() {
+ return FunctionUtils.toDifferentiableUnivariateFunction(this).derivative();
+ }
+
+ /** {@inheritDoc}
+ * @since 3.1
+ */
+ public DerivativeStructure value(final DerivativeStructure t) {
+ return t.acosh();
+ }
+
+}
diff --git a/src/main/java/org/apache/commons/math3/analysis/function/Add.java b/src/main/java/org/apache/commons/math3/analysis/function/Add.java
new file mode 100644
index 0000000..366a303
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/analysis/function/Add.java
@@ -0,0 +1,32 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math3.analysis.function;
+
+import org.apache.commons.math3.analysis.BivariateFunction;
+
+/**
+ * Add the two operands.
+ *
+ * @since 3.0
+ */
+public class Add implements BivariateFunction {
+ /** {@inheritDoc} */
+ public double value(double x, double y) {
+ return x + y;
+ }
+}
diff --git a/src/main/java/org/apache/commons/math3/analysis/function/Asin.java b/src/main/java/org/apache/commons/math3/analysis/function/Asin.java
new file mode 100644
index 0000000..8fa9bdf
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/analysis/function/Asin.java
@@ -0,0 +1,53 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math3.analysis.function;
+
+import org.apache.commons.math3.analysis.FunctionUtils;
+import org.apache.commons.math3.analysis.UnivariateFunction;
+import org.apache.commons.math3.analysis.DifferentiableUnivariateFunction;
+import org.apache.commons.math3.analysis.differentiation.DerivativeStructure;
+import org.apache.commons.math3.analysis.differentiation.UnivariateDifferentiableFunction;
+import org.apache.commons.math3.util.FastMath;
+
+/**
+ * Arc-sine function.
+ *
+ * @since 3.0
+ */
+public class Asin implements UnivariateDifferentiableFunction, DifferentiableUnivariateFunction {
+ /** {@inheritDoc} */
+ public double value(double x) {
+ return FastMath.asin(x);
+ }
+
+ /** {@inheritDoc}
+ * @deprecated as of 3.1, replaced by {@link #value(DerivativeStructure)}
+ */
+ @Deprecated
+ public UnivariateFunction derivative() {
+ return FunctionUtils.toDifferentiableUnivariateFunction(this).derivative();
+ }
+
+ /** {@inheritDoc}
+ * @since 3.1
+ */
+ public DerivativeStructure value(final DerivativeStructure t) {
+ return t.asin();
+ }
+
+}
diff --git a/src/main/java/org/apache/commons/math3/analysis/function/Asinh.java b/src/main/java/org/apache/commons/math3/analysis/function/Asinh.java
new file mode 100644
index 0000000..b5b9fd2
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/analysis/function/Asinh.java
@@ -0,0 +1,53 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math3.analysis.function;
+
+import org.apache.commons.math3.analysis.FunctionUtils;
+import org.apache.commons.math3.analysis.UnivariateFunction;
+import org.apache.commons.math3.analysis.DifferentiableUnivariateFunction;
+import org.apache.commons.math3.analysis.differentiation.DerivativeStructure;
+import org.apache.commons.math3.analysis.differentiation.UnivariateDifferentiableFunction;
+import org.apache.commons.math3.util.FastMath;
+
+/**
+ * Hyperbolic arc-sine function.
+ *
+ * @since 3.0
+ */
+public class Asinh implements UnivariateDifferentiableFunction, DifferentiableUnivariateFunction {
+ /** {@inheritDoc} */
+ public double value(double x) {
+ return FastMath.asinh(x);
+ }
+
+ /** {@inheritDoc}
+ * @deprecated as of 3.1, replaced by {@link #value(DerivativeStructure)}
+ */
+ @Deprecated
+ public UnivariateFunction derivative() {
+ return FunctionUtils.toDifferentiableUnivariateFunction(this).derivative();
+ }
+
+ /** {@inheritDoc}
+ * @since 3.1
+ */
+ public DerivativeStructure value(final DerivativeStructure t) {
+ return t.asinh();
+ }
+
+}
diff --git a/src/main/java/org/apache/commons/math3/analysis/function/Atan.java b/src/main/java/org/apache/commons/math3/analysis/function/Atan.java
new file mode 100644
index 0000000..36b1265
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/analysis/function/Atan.java
@@ -0,0 +1,53 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math3.analysis.function;
+
+import org.apache.commons.math3.analysis.FunctionUtils;
+import org.apache.commons.math3.analysis.UnivariateFunction;
+import org.apache.commons.math3.analysis.DifferentiableUnivariateFunction;
+import org.apache.commons.math3.analysis.differentiation.DerivativeStructure;
+import org.apache.commons.math3.analysis.differentiation.UnivariateDifferentiableFunction;
+import org.apache.commons.math3.util.FastMath;
+
+/**
+ * Arc-tangent function.
+ *
+ * @since 3.0
+ */
+public class Atan implements UnivariateDifferentiableFunction, DifferentiableUnivariateFunction {
+ /** {@inheritDoc} */
+ public double value(double x) {
+ return FastMath.atan(x);
+ }
+
+ /** {@inheritDoc}
+ * @deprecated as of 3.1, replaced by {@link #value(DerivativeStructure)}
+ */
+ @Deprecated
+ public UnivariateFunction derivative() {
+ return FunctionUtils.toDifferentiableUnivariateFunction(this).derivative();
+ }
+
+ /** {@inheritDoc}
+ * @since 3.1
+ */
+ public DerivativeStructure value(final DerivativeStructure t) {
+ return t.atan();
+ }
+
+}
diff --git a/src/main/java/org/apache/commons/math3/analysis/function/Atan2.java b/src/main/java/org/apache/commons/math3/analysis/function/Atan2.java
new file mode 100644
index 0000000..d5f385f
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/analysis/function/Atan2.java
@@ -0,0 +1,33 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math3.analysis.function;
+
+import org.apache.commons.math3.analysis.BivariateFunction;
+import org.apache.commons.math3.util.FastMath;
+
+/**
+ * Arc-tangent function.
+ *
+ * @since 3.0
+ */
+public class Atan2 implements BivariateFunction {
+ /** {@inheritDoc} */
+ public double value(double x, double y) {
+ return FastMath.atan2(x, y);
+ }
+}
diff --git a/src/main/java/org/apache/commons/math3/analysis/function/Atanh.java b/src/main/java/org/apache/commons/math3/analysis/function/Atanh.java
new file mode 100644
index 0000000..5c04599
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/analysis/function/Atanh.java
@@ -0,0 +1,53 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math3.analysis.function;
+
+import org.apache.commons.math3.analysis.FunctionUtils;
+import org.apache.commons.math3.analysis.UnivariateFunction;
+import org.apache.commons.math3.analysis.DifferentiableUnivariateFunction;
+import org.apache.commons.math3.analysis.differentiation.DerivativeStructure;
+import org.apache.commons.math3.analysis.differentiation.UnivariateDifferentiableFunction;
+import org.apache.commons.math3.util.FastMath;
+
+/**
+ * Hyperbolic arc-tangent function.
+ *
+ * @since 3.0
+ */
+public class Atanh implements UnivariateDifferentiableFunction, DifferentiableUnivariateFunction {
+ /** {@inheritDoc} */
+ public double value(double x) {
+ return FastMath.atanh(x);
+ }
+
+ /** {@inheritDoc}
+ * @deprecated as of 3.1, replaced by {@link #value(DerivativeStructure)}
+ */
+ @Deprecated
+ public UnivariateFunction derivative() {
+ return FunctionUtils.toDifferentiableUnivariateFunction(this).derivative();
+ }
+
+ /** {@inheritDoc}
+ * @since 3.1
+ */
+ public DerivativeStructure value(final DerivativeStructure t) {
+ return t.atanh();
+ }
+
+}
diff --git a/src/main/java/org/apache/commons/math3/analysis/function/Cbrt.java b/src/main/java/org/apache/commons/math3/analysis/function/Cbrt.java
new file mode 100644
index 0000000..f26ef71
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/analysis/function/Cbrt.java
@@ -0,0 +1,53 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math3.analysis.function;
+
+import org.apache.commons.math3.analysis.FunctionUtils;
+import org.apache.commons.math3.analysis.UnivariateFunction;
+import org.apache.commons.math3.analysis.DifferentiableUnivariateFunction;
+import org.apache.commons.math3.analysis.differentiation.DerivativeStructure;
+import org.apache.commons.math3.analysis.differentiation.UnivariateDifferentiableFunction;
+import org.apache.commons.math3.util.FastMath;
+
+/**
+ * Cube root function.
+ *
+ * @since 3.0
+ */
+public class Cbrt implements UnivariateDifferentiableFunction, DifferentiableUnivariateFunction {
+ /** {@inheritDoc} */
+ public double value(double x) {
+ return FastMath.cbrt(x);
+ }
+
+ /** {@inheritDoc}
+ * @deprecated as of 3.1, replaced by {@link #value(DerivativeStructure)}
+ */
+ @Deprecated
+ public UnivariateFunction derivative() {
+ return FunctionUtils.toDifferentiableUnivariateFunction(this).derivative();
+ }
+
+ /** {@inheritDoc}
+ * @since 3.1
+ */
+ public DerivativeStructure value(final DerivativeStructure t) {
+ return t.cbrt();
+ }
+
+}
diff --git a/src/main/java/org/apache/commons/math3/analysis/function/Ceil.java b/src/main/java/org/apache/commons/math3/analysis/function/Ceil.java
new file mode 100644
index 0000000..2b9867e
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/analysis/function/Ceil.java
@@ -0,0 +1,33 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math3.analysis.function;
+
+import org.apache.commons.math3.analysis.UnivariateFunction;
+import org.apache.commons.math3.util.FastMath;
+
+/**
+ * {@code ceil} function.
+ *
+ * @since 3.0
+ */
+public class Ceil implements UnivariateFunction {
+ /** {@inheritDoc} */
+ public double value(double x) {
+ return FastMath.ceil(x);
+ }
+}
diff --git a/src/main/java/org/apache/commons/math3/analysis/function/Constant.java b/src/main/java/org/apache/commons/math3/analysis/function/Constant.java
new file mode 100644
index 0000000..4027e59
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/analysis/function/Constant.java
@@ -0,0 +1,60 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math3.analysis.function;
+
+import org.apache.commons.math3.analysis.DifferentiableUnivariateFunction;
+import org.apache.commons.math3.analysis.differentiation.DerivativeStructure;
+import org.apache.commons.math3.analysis.differentiation.UnivariateDifferentiableFunction;
+
+/**
+ * Constant function.
+ *
+ * @since 3.0
+ */
+public class Constant implements UnivariateDifferentiableFunction, DifferentiableUnivariateFunction {
+ /** Constant. */
+ private final double c;
+
+ /**
+ * @param c Constant.
+ */
+ public Constant(double c) {
+ this.c = c;
+ }
+
+ /** {@inheritDoc} */
+ public double value(double x) {
+ return c;
+ }
+
+ /** {@inheritDoc}
+ * @deprecated as of 3.1, replaced by {@link #value(DerivativeStructure)}
+ */
+ @Deprecated
+ public DifferentiableUnivariateFunction derivative() {
+ return new Constant(0);
+ }
+
+ /** {@inheritDoc}
+ * @since 3.1
+ */
+ public DerivativeStructure value(final DerivativeStructure t) {
+ return new DerivativeStructure(t.getFreeParameters(), t.getOrder(), c);
+ }
+
+}
diff --git a/src/main/java/org/apache/commons/math3/analysis/function/Cos.java b/src/main/java/org/apache/commons/math3/analysis/function/Cos.java
new file mode 100644
index 0000000..73a5e6e
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/analysis/function/Cos.java
@@ -0,0 +1,53 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math3.analysis.function;
+
+import org.apache.commons.math3.analysis.FunctionUtils;
+import org.apache.commons.math3.analysis.UnivariateFunction;
+import org.apache.commons.math3.analysis.DifferentiableUnivariateFunction;
+import org.apache.commons.math3.analysis.differentiation.DerivativeStructure;
+import org.apache.commons.math3.analysis.differentiation.UnivariateDifferentiableFunction;
+import org.apache.commons.math3.util.FastMath;
+
+/**
+ * Cosine function.
+ *
+ * @since 3.0
+ */
+public class Cos implements UnivariateDifferentiableFunction, DifferentiableUnivariateFunction {
+ /** {@inheritDoc} */
+ public double value(double x) {
+ return FastMath.cos(x);
+ }
+
+ /** {@inheritDoc}
+ * @deprecated as of 3.1, replaced by {@link #value(DerivativeStructure)}
+ */
+ @Deprecated
+ public UnivariateFunction derivative() {
+ return FunctionUtils.toDifferentiableUnivariateFunction(this).derivative();
+ }
+
+ /** {@inheritDoc}
+ * @since 3.1
+ */
+ public DerivativeStructure value(final DerivativeStructure t) {
+ return t.cos();
+ }
+
+}
diff --git a/src/main/java/org/apache/commons/math3/analysis/function/Cosh.java b/src/main/java/org/apache/commons/math3/analysis/function/Cosh.java
new file mode 100644
index 0000000..185698b
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/analysis/function/Cosh.java
@@ -0,0 +1,51 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math3.analysis.function;
+
+import org.apache.commons.math3.analysis.DifferentiableUnivariateFunction;
+import org.apache.commons.math3.analysis.differentiation.DerivativeStructure;
+import org.apache.commons.math3.analysis.differentiation.UnivariateDifferentiableFunction;
+import org.apache.commons.math3.util.FastMath;
+
+/**
+ * Hyperbolic cosine function.
+ *
+ * @since 3.0
+ */
+public class Cosh implements UnivariateDifferentiableFunction, DifferentiableUnivariateFunction {
+ /** {@inheritDoc} */
+ public double value(double x) {
+ return FastMath.cosh(x);
+ }
+
+ /** {@inheritDoc}
+ * @deprecated as of 3.1, replaced by {@link #value(DerivativeStructure)}
+ */
+ @Deprecated
+ public DifferentiableUnivariateFunction derivative() {
+ return new Sinh();
+ }
+
+ /** {@inheritDoc}
+ * @since 3.1
+ */
+ public DerivativeStructure value(final DerivativeStructure t) {
+ return t.cosh();
+ }
+
+}
diff --git a/src/main/java/org/apache/commons/math3/analysis/function/Divide.java b/src/main/java/org/apache/commons/math3/analysis/function/Divide.java
new file mode 100644
index 0000000..73413a2
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/analysis/function/Divide.java
@@ -0,0 +1,32 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math3.analysis.function;
+
+import org.apache.commons.math3.analysis.BivariateFunction;
+
+/**
+ * Divide the first operand by the second.
+ *
+ * @since 3.0
+ */
+public class Divide implements BivariateFunction {
+ /** {@inheritDoc} */
+ public double value(double x, double y) {
+ return x / y;
+ }
+}
diff --git a/src/main/java/org/apache/commons/math3/analysis/function/Exp.java b/src/main/java/org/apache/commons/math3/analysis/function/Exp.java
new file mode 100644
index 0000000..f656712
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/analysis/function/Exp.java
@@ -0,0 +1,53 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math3.analysis.function;
+
+import org.apache.commons.math3.analysis.FunctionUtils;
+import org.apache.commons.math3.analysis.UnivariateFunction;
+import org.apache.commons.math3.analysis.DifferentiableUnivariateFunction;
+import org.apache.commons.math3.analysis.differentiation.DerivativeStructure;
+import org.apache.commons.math3.analysis.differentiation.UnivariateDifferentiableFunction;
+import org.apache.commons.math3.util.FastMath;
+
+/**
+ * Exponential function.
+ *
+ * @since 3.0
+ */
+public class Exp implements UnivariateDifferentiableFunction, DifferentiableUnivariateFunction {
+ /** {@inheritDoc} */
+ public double value(double x) {
+ return FastMath.exp(x);
+ }
+
+ /** {@inheritDoc}
+ * @deprecated as of 3.1, replaced by {@link #value(DerivativeStructure)}
+ */
+ @Deprecated
+ public UnivariateFunction derivative() {
+ return FunctionUtils.toDifferentiableUnivariateFunction(this).derivative();
+ }
+
+ /** {@inheritDoc}
+ * @since 3.1
+ */
+ public DerivativeStructure value(final DerivativeStructure t) {
+ return t.exp();
+ }
+
+}
diff --git a/src/main/java/org/apache/commons/math3/analysis/function/Expm1.java b/src/main/java/org/apache/commons/math3/analysis/function/Expm1.java
new file mode 100644
index 0000000..46b0b2f
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/analysis/function/Expm1.java
@@ -0,0 +1,53 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math3.analysis.function;
+
+import org.apache.commons.math3.analysis.FunctionUtils;
+import org.apache.commons.math3.analysis.UnivariateFunction;
+import org.apache.commons.math3.analysis.DifferentiableUnivariateFunction;
+import org.apache.commons.math3.analysis.differentiation.DerivativeStructure;
+import org.apache.commons.math3.analysis.differentiation.UnivariateDifferentiableFunction;
+import org.apache.commons.math3.util.FastMath;
+
+/**
+ * <code>e<sup>x</sup>-1</code> function.
+ *
+ * @since 3.0
+ */
+public class Expm1 implements UnivariateDifferentiableFunction, DifferentiableUnivariateFunction {
+ /** {@inheritDoc} */
+ public double value(double x) {
+ return FastMath.expm1(x);
+ }
+
+ /** {@inheritDoc}
+ * @deprecated as of 3.1, replaced by {@link #value(DerivativeStructure)}
+ */
+ @Deprecated
+ public UnivariateFunction derivative() {
+ return FunctionUtils.toDifferentiableUnivariateFunction(this).derivative();
+ }
+
+ /** {@inheritDoc}
+ * @since 3.1
+ */
+ public DerivativeStructure value(final DerivativeStructure t) {
+ return t.expm1();
+ }
+
+}
diff --git a/src/main/java/org/apache/commons/math3/analysis/function/Floor.java b/src/main/java/org/apache/commons/math3/analysis/function/Floor.java
new file mode 100644
index 0000000..8d70627
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/analysis/function/Floor.java
@@ -0,0 +1,33 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math3.analysis.function;
+
+import org.apache.commons.math3.analysis.UnivariateFunction;
+import org.apache.commons.math3.util.FastMath;
+
+/**
+ * {@code floor} function.
+ *
+ * @since 3.0
+ */
+public class Floor implements UnivariateFunction {
+ /** {@inheritDoc} */
+ public double value(double x) {
+ return FastMath.floor(x);
+ }
+}
diff --git a/src/main/java/org/apache/commons/math3/analysis/function/Gaussian.java b/src/main/java/org/apache/commons/math3/analysis/function/Gaussian.java
new file mode 100644
index 0000000..8c64c8b
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/analysis/function/Gaussian.java
@@ -0,0 +1,259 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math3.analysis.function;
+
+import java.util.Arrays;
+
+import org.apache.commons.math3.analysis.FunctionUtils;
+import org.apache.commons.math3.analysis.UnivariateFunction;
+import org.apache.commons.math3.analysis.DifferentiableUnivariateFunction;
+import org.apache.commons.math3.analysis.ParametricUnivariateFunction;
+import org.apache.commons.math3.analysis.differentiation.DerivativeStructure;
+import org.apache.commons.math3.analysis.differentiation.UnivariateDifferentiableFunction;
+import org.apache.commons.math3.exception.NotStrictlyPositiveException;
+import org.apache.commons.math3.exception.NullArgumentException;
+import org.apache.commons.math3.exception.DimensionMismatchException;
+import org.apache.commons.math3.util.FastMath;
+import org.apache.commons.math3.util.Precision;
+
+/**
+ * <a href="http://en.wikipedia.org/wiki/Gaussian_function">
+ * Gaussian</a> function.
+ *
+ * @since 3.0
+ */
+public class Gaussian implements UnivariateDifferentiableFunction, DifferentiableUnivariateFunction {
+ /** Mean. */
+ private final double mean;
+ /** Inverse of the standard deviation. */
+ private final double is;
+ /** Inverse of twice the square of the standard deviation. */
+ private final double i2s2;
+ /** Normalization factor. */
+ private final double norm;
+
+ /**
+ * Gaussian with given normalization factor, mean and standard deviation.
+ *
+ * @param norm Normalization factor.
+ * @param mean Mean.
+ * @param sigma Standard deviation.
+ * @throws NotStrictlyPositiveException if {@code sigma <= 0}.
+ */
+ public Gaussian(double norm,
+ double mean,
+ double sigma)
+ throws NotStrictlyPositiveException {
+ if (sigma <= 0) {
+ throw new NotStrictlyPositiveException(sigma);
+ }
+
+ this.norm = norm;
+ this.mean = mean;
+ this.is = 1 / sigma;
+ this.i2s2 = 0.5 * is * is;
+ }
+
+ /**
+ * Normalized gaussian with given mean and standard deviation.
+ *
+ * @param mean Mean.
+ * @param sigma Standard deviation.
+ * @throws NotStrictlyPositiveException if {@code sigma <= 0}.
+ */
+ public Gaussian(double mean,
+ double sigma)
+ throws NotStrictlyPositiveException {
+ this(1 / (sigma * FastMath.sqrt(2 * Math.PI)), mean, sigma);
+ }
+
+ /**
+ * Normalized gaussian with zero mean and unit standard deviation.
+ */
+ public Gaussian() {
+ this(0, 1);
+ }
+
+ /** {@inheritDoc} */
+ public double value(double x) {
+ return value(x - mean, norm, i2s2);
+ }
+
+ /** {@inheritDoc}
+ * @deprecated as of 3.1, replaced by {@link #value(DerivativeStructure)}
+ */
+ @Deprecated
+ public UnivariateFunction derivative() {
+ return FunctionUtils.toDifferentiableUnivariateFunction(this).derivative();
+ }
+
+ /**
+ * Parametric function where the input array contains the parameters of
+ * the Gaussian, ordered as follows:
+ * <ul>
+ * <li>Norm</li>
+ * <li>Mean</li>
+ * <li>Standard deviation</li>
+ * </ul>
+ */
+ public static class Parametric implements ParametricUnivariateFunction {
+ /**
+ * Computes the value of the Gaussian at {@code x}.
+ *
+ * @param x Value for which the function must be computed.
+ * @param param Values of norm, mean and standard deviation.
+ * @return the value of the function.
+ * @throws NullArgumentException if {@code param} is {@code null}.
+ * @throws DimensionMismatchException if the size of {@code param} is
+ * not 3.
+ * @throws NotStrictlyPositiveException if {@code param[2]} is negative.
+ */
+ public double value(double x, double ... param)
+ throws NullArgumentException,
+ DimensionMismatchException,
+ NotStrictlyPositiveException {
+ validateParameters(param);
+
+ final double diff = x - param[1];
+ final double i2s2 = 1 / (2 * param[2] * param[2]);
+ return Gaussian.value(diff, param[0], i2s2);
+ }
+
+ /**
+ * Computes the value of the gradient at {@code x}.
+ * The components of the gradient vector are the partial
+ * derivatives of the function with respect to each of the
+ * <em>parameters</em> (norm, mean and standard deviation).
+ *
+ * @param x Value at which the gradient must be computed.
+ * @param param Values of norm, mean and standard deviation.
+ * @return the gradient vector at {@code x}.
+ * @throws NullArgumentException if {@code param} is {@code null}.
+ * @throws DimensionMismatchException if the size of {@code param} is
+ * not 3.
+ * @throws NotStrictlyPositiveException if {@code param[2]} is negative.
+ */
+ public double[] gradient(double x, double ... param)
+ throws NullArgumentException,
+ DimensionMismatchException,
+ NotStrictlyPositiveException {
+ validateParameters(param);
+
+ final double norm = param[0];
+ final double diff = x - param[1];
+ final double sigma = param[2];
+ final double i2s2 = 1 / (2 * sigma * sigma);
+
+ final double n = Gaussian.value(diff, 1, i2s2);
+ final double m = norm * n * 2 * i2s2 * diff;
+ final double s = m * diff / sigma;
+
+ return new double[] { n, m, s };
+ }
+
+ /**
+ * Validates parameters to ensure they are appropriate for the evaluation of
+ * the {@link #value(double,double[])} and {@link #gradient(double,double[])}
+ * methods.
+ *
+ * @param param Values of norm, mean and standard deviation.
+ * @throws NullArgumentException if {@code param} is {@code null}.
+ * @throws DimensionMismatchException if the size of {@code param} is
+ * not 3.
+ * @throws NotStrictlyPositiveException if {@code param[2]} is negative.
+ */
+ private void validateParameters(double[] param)
+ throws NullArgumentException,
+ DimensionMismatchException,
+ NotStrictlyPositiveException {
+ if (param == null) {
+ throw new NullArgumentException();
+ }
+ if (param.length != 3) {
+ throw new DimensionMismatchException(param.length, 3);
+ }
+ if (param[2] <= 0) {
+ throw new NotStrictlyPositiveException(param[2]);
+ }
+ }
+ }
+
+ /**
+ * @param xMinusMean {@code x - mean}.
+ * @param norm Normalization factor.
+ * @param i2s2 Inverse of twice the square of the standard deviation.
+ * @return the value of the Gaussian at {@code x}.
+ */
+ private static double value(double xMinusMean,
+ double norm,
+ double i2s2) {
+ return norm * FastMath.exp(-xMinusMean * xMinusMean * i2s2);
+ }
+
+ /** {@inheritDoc}
+ * @since 3.1
+ */
+ public DerivativeStructure value(final DerivativeStructure t)
+ throws DimensionMismatchException {
+
+ final double u = is * (t.getValue() - mean);
+ double[] f = new double[t.getOrder() + 1];
+
+ // the nth order derivative of the Gaussian has the form:
+ // dn(g(x)/dxn = (norm / s^n) P_n(u) exp(-u^2/2) with u=(x-m)/s
+ // where P_n(u) is a degree n polynomial with same parity as n
+ // P_0(u) = 1, P_1(u) = -u, P_2(u) = u^2 - 1, P_3(u) = -u^3 + 3 u...
+ // the general recurrence relation for P_n is:
+ // P_n(u) = P_(n-1)'(u) - u P_(n-1)(u)
+ // as per polynomial parity, we can store coefficients of both P_(n-1) and P_n in the same array
+ final double[] p = new double[f.length];
+ p[0] = 1;
+ final double u2 = u * u;
+ double coeff = norm * FastMath.exp(-0.5 * u2);
+ if (coeff <= Precision.SAFE_MIN) {
+ Arrays.fill(f, 0.0);
+ } else {
+ f[0] = coeff;
+ for (int n = 1; n < f.length; ++n) {
+
+ // update and evaluate polynomial P_n(x)
+ double v = 0;
+ p[n] = -p[n - 1];
+ for (int k = n; k >= 0; k -= 2) {
+ v = v * u2 + p[k];
+ if (k > 2) {
+ p[k - 2] = (k - 1) * p[k - 1] - p[k - 3];
+ } else if (k == 2) {
+ p[0] = p[1];
+ }
+ }
+ if ((n & 0x1) == 1) {
+ v *= u;
+ }
+
+ coeff *= is;
+ f[n] = coeff * v;
+
+ }
+ }
+
+ return t.compose(f);
+
+ }
+
+}
diff --git a/src/main/java/org/apache/commons/math3/analysis/function/HarmonicOscillator.java b/src/main/java/org/apache/commons/math3/analysis/function/HarmonicOscillator.java
new file mode 100644
index 0000000..0fbad9c
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/analysis/function/HarmonicOscillator.java
@@ -0,0 +1,183 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math3.analysis.function;
+
+import org.apache.commons.math3.analysis.DifferentiableUnivariateFunction;
+import org.apache.commons.math3.analysis.FunctionUtils;
+import org.apache.commons.math3.analysis.ParametricUnivariateFunction;
+import org.apache.commons.math3.analysis.UnivariateFunction;
+import org.apache.commons.math3.analysis.differentiation.DerivativeStructure;
+import org.apache.commons.math3.analysis.differentiation.UnivariateDifferentiableFunction;
+import org.apache.commons.math3.exception.DimensionMismatchException;
+import org.apache.commons.math3.exception.NullArgumentException;
+import org.apache.commons.math3.util.FastMath;
+
+/**
+ * <a href="http://en.wikipedia.org/wiki/Harmonic_oscillator">
+ * simple harmonic oscillator</a> function.
+ *
+ * @since 3.0
+ */
+public class HarmonicOscillator implements UnivariateDifferentiableFunction, DifferentiableUnivariateFunction {
+ /** Amplitude. */
+ private final double amplitude;
+ /** Angular frequency. */
+ private final double omega;
+ /** Phase. */
+ private final double phase;
+
+ /**
+ * Harmonic oscillator function.
+ *
+ * @param amplitude Amplitude.
+ * @param omega Angular frequency.
+ * @param phase Phase.
+ */
+ public HarmonicOscillator(double amplitude,
+ double omega,
+ double phase) {
+ this.amplitude = amplitude;
+ this.omega = omega;
+ this.phase = phase;
+ }
+
+ /** {@inheritDoc} */
+ public double value(double x) {
+ return value(omega * x + phase, amplitude);
+ }
+
+ /** {@inheritDoc}
+ * @deprecated as of 3.1, replaced by {@link #value(DerivativeStructure)}
+ */
+ @Deprecated
+ public UnivariateFunction derivative() {
+ return FunctionUtils.toDifferentiableUnivariateFunction(this).derivative();
+ }
+
+ /**
+ * Parametric function where the input array contains the parameters of
+ * the harmonic oscillator function, ordered as follows:
+ * <ul>
+ * <li>Amplitude</li>
+ * <li>Angular frequency</li>
+ * <li>Phase</li>
+ * </ul>
+ */
+ public static class Parametric implements ParametricUnivariateFunction {
+ /**
+ * Computes the value of the harmonic oscillator at {@code x}.
+ *
+ * @param x Value for which the function must be computed.
+ * @param param Values of norm, mean and standard deviation.
+ * @return the value of the function.
+ * @throws NullArgumentException if {@code param} is {@code null}.
+ * @throws DimensionMismatchException if the size of {@code param} is
+ * not 3.
+ */
+ public double value(double x, double ... param)
+ throws NullArgumentException,
+ DimensionMismatchException {
+ validateParameters(param);
+ return HarmonicOscillator.value(x * param[1] + param[2], param[0]);
+ }
+
+ /**
+ * Computes the value of the gradient at {@code x}.
+ * The components of the gradient vector are the partial
+ * derivatives of the function with respect to each of the
+ * <em>parameters</em> (amplitude, angular frequency and phase).
+ *
+ * @param x Value at which the gradient must be computed.
+ * @param param Values of amplitude, angular frequency and phase.
+ * @return the gradient vector at {@code x}.
+ * @throws NullArgumentException if {@code param} is {@code null}.
+ * @throws DimensionMismatchException if the size of {@code param} is
+ * not 3.
+ */
+ public double[] gradient(double x, double ... param)
+ throws NullArgumentException,
+ DimensionMismatchException {
+ validateParameters(param);
+
+ final double amplitude = param[0];
+ final double omega = param[1];
+ final double phase = param[2];
+
+ final double xTimesOmegaPlusPhase = omega * x + phase;
+ final double a = HarmonicOscillator.value(xTimesOmegaPlusPhase, 1);
+ final double p = -amplitude * FastMath.sin(xTimesOmegaPlusPhase);
+ final double w = p * x;
+
+ return new double[] { a, w, p };
+ }
+
+ /**
+ * Validates parameters to ensure they are appropriate for the evaluation of
+ * the {@link #value(double,double[])} and {@link #gradient(double,double[])}
+ * methods.
+ *
+ * @param param Values of norm, mean and standard deviation.
+ * @throws NullArgumentException if {@code param} is {@code null}.
+ * @throws DimensionMismatchException if the size of {@code param} is
+ * not 3.
+ */
+ private void validateParameters(double[] param)
+ throws NullArgumentException,
+ DimensionMismatchException {
+ if (param == null) {
+ throw new NullArgumentException();
+ }
+ if (param.length != 3) {
+ throw new DimensionMismatchException(param.length, 3);
+ }
+ }
+ }
+
+ /**
+ * @param xTimesOmegaPlusPhase {@code omega * x + phase}.
+ * @param amplitude Amplitude.
+ * @return the value of the harmonic oscillator function at {@code x}.
+ */
+ private static double value(double xTimesOmegaPlusPhase,
+ double amplitude) {
+ return amplitude * FastMath.cos(xTimesOmegaPlusPhase);
+ }
+
+ /** {@inheritDoc}
+ * @since 3.1
+ */
+ public DerivativeStructure value(final DerivativeStructure t)
+ throws DimensionMismatchException {
+ final double x = t.getValue();
+ double[] f = new double[t.getOrder() + 1];
+
+ final double alpha = omega * x + phase;
+ f[0] = amplitude * FastMath.cos(alpha);
+ if (f.length > 1) {
+ f[1] = -amplitude * omega * FastMath.sin(alpha);
+ final double mo2 = - omega * omega;
+ for (int i = 2; i < f.length; ++i) {
+ f[i] = mo2 * f[i - 2];
+ }
+ }
+
+ return t.compose(f);
+
+ }
+
+}
diff --git a/src/main/java/org/apache/commons/math3/analysis/function/Identity.java b/src/main/java/org/apache/commons/math3/analysis/function/Identity.java
new file mode 100644
index 0000000..d21f7e0
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/analysis/function/Identity.java
@@ -0,0 +1,50 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math3.analysis.function;
+
+import org.apache.commons.math3.analysis.DifferentiableUnivariateFunction;
+import org.apache.commons.math3.analysis.differentiation.DerivativeStructure;
+import org.apache.commons.math3.analysis.differentiation.UnivariateDifferentiableFunction;
+
+/**
+ * Identity function.
+ *
+ * @since 3.0
+ */
+public class Identity implements UnivariateDifferentiableFunction, DifferentiableUnivariateFunction {
+ /** {@inheritDoc} */
+ public double value(double x) {
+ return x;
+ }
+
+ /** {@inheritDoc}
+ * @deprecated as of 3.1, replaced by {@link #value(DerivativeStructure)}
+ */
+ @Deprecated
+ public DifferentiableUnivariateFunction derivative() {
+ return new Constant(1);
+ }
+
+ /** {@inheritDoc}
+ * @since 3.1
+ */
+ public DerivativeStructure value(final DerivativeStructure t) {
+ return t;
+ }
+
+}
diff --git a/src/main/java/org/apache/commons/math3/analysis/function/Inverse.java b/src/main/java/org/apache/commons/math3/analysis/function/Inverse.java
new file mode 100644
index 0000000..e38f689
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/analysis/function/Inverse.java
@@ -0,0 +1,52 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math3.analysis.function;
+
+import org.apache.commons.math3.analysis.FunctionUtils;
+import org.apache.commons.math3.analysis.UnivariateFunction;
+import org.apache.commons.math3.analysis.DifferentiableUnivariateFunction;
+import org.apache.commons.math3.analysis.differentiation.DerivativeStructure;
+import org.apache.commons.math3.analysis.differentiation.UnivariateDifferentiableFunction;
+
+/**
+ * Inverse function.
+ *
+ * @since 3.0
+ */
+public class Inverse implements UnivariateDifferentiableFunction, DifferentiableUnivariateFunction {
+ /** {@inheritDoc} */
+ public double value(double x) {
+ return 1 / x;
+ }
+
+ /** {@inheritDoc}
+ * @deprecated as of 3.1, replaced by {@link #value(DerivativeStructure)}
+ */
+ @Deprecated
+ public UnivariateFunction derivative() {
+ return FunctionUtils.toDifferentiableUnivariateFunction(this).derivative();
+ }
+
+ /** {@inheritDoc}
+ * @since 3.1
+ */
+ public DerivativeStructure value(final DerivativeStructure t) {
+ return t.reciprocal();
+ }
+
+}
diff --git a/src/main/java/org/apache/commons/math3/analysis/function/Log.java b/src/main/java/org/apache/commons/math3/analysis/function/Log.java
new file mode 100644
index 0000000..a1e12dc
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/analysis/function/Log.java
@@ -0,0 +1,53 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math3.analysis.function;
+
+import org.apache.commons.math3.analysis.FunctionUtils;
+import org.apache.commons.math3.analysis.UnivariateFunction;
+import org.apache.commons.math3.analysis.DifferentiableUnivariateFunction;
+import org.apache.commons.math3.analysis.differentiation.DerivativeStructure;
+import org.apache.commons.math3.analysis.differentiation.UnivariateDifferentiableFunction;
+import org.apache.commons.math3.util.FastMath;
+
+/**
+ * Natural logarithm function.
+ *
+ * @since 3.0
+ */
+public class Log implements UnivariateDifferentiableFunction, DifferentiableUnivariateFunction {
+ /** {@inheritDoc} */
+ public double value(double x) {
+ return FastMath.log(x);
+ }
+
+ /** {@inheritDoc}
+ * @deprecated as of 3.1, replaced by {@link #value(DerivativeStructure)}
+ */
+ @Deprecated
+ public UnivariateFunction derivative() {
+ return FunctionUtils.toDifferentiableUnivariateFunction(this).derivative();
+ }
+
+ /** {@inheritDoc}
+ * @since 3.1
+ */
+ public DerivativeStructure value(final DerivativeStructure t) {
+ return t.log();
+ }
+
+}
diff --git a/src/main/java/org/apache/commons/math3/analysis/function/Log10.java b/src/main/java/org/apache/commons/math3/analysis/function/Log10.java
new file mode 100644
index 0000000..66c03e1
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/analysis/function/Log10.java
@@ -0,0 +1,54 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math3.analysis.function;
+
+import org.apache.commons.math3.analysis.FunctionUtils;
+import org.apache.commons.math3.analysis.UnivariateFunction;
+import org.apache.commons.math3.analysis.DifferentiableUnivariateFunction;
+import org.apache.commons.math3.analysis.differentiation.DerivativeStructure;
+import org.apache.commons.math3.analysis.differentiation.UnivariateDifferentiableFunction;
+import org.apache.commons.math3.util.FastMath;
+
+/**
+ * Base 10 logarithm function.
+ *
+ * @since 3.0
+ */
+public class Log10 implements UnivariateDifferentiableFunction, DifferentiableUnivariateFunction {
+
+ /** {@inheritDoc} */
+ public double value(double x) {
+ return FastMath.log10(x);
+ }
+
+ /** {@inheritDoc}
+ * @deprecated as of 3.1, replaced by {@link #value(DerivativeStructure)}
+ */
+ @Deprecated
+ public UnivariateFunction derivative() {
+ return FunctionUtils.toDifferentiableUnivariateFunction(this).derivative();
+ }
+
+ /** {@inheritDoc}
+ * @since 3.1
+ */
+ public DerivativeStructure value(final DerivativeStructure t) {
+ return t.log10();
+ }
+
+}
diff --git a/src/main/java/org/apache/commons/math3/analysis/function/Log1p.java b/src/main/java/org/apache/commons/math3/analysis/function/Log1p.java
new file mode 100644
index 0000000..4966318
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/analysis/function/Log1p.java
@@ -0,0 +1,53 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math3.analysis.function;
+
+import org.apache.commons.math3.analysis.FunctionUtils;
+import org.apache.commons.math3.analysis.UnivariateFunction;
+import org.apache.commons.math3.analysis.DifferentiableUnivariateFunction;
+import org.apache.commons.math3.analysis.differentiation.DerivativeStructure;
+import org.apache.commons.math3.analysis.differentiation.UnivariateDifferentiableFunction;
+import org.apache.commons.math3.util.FastMath;
+
+/**
+ * <code>log(1 + p)</code> function.
+ *
+ * @since 3.0
+ */
+public class Log1p implements UnivariateDifferentiableFunction, DifferentiableUnivariateFunction {
+ /** {@inheritDoc} */
+ public double value(double x) {
+ return FastMath.log1p(x);
+ }
+
+ /** {@inheritDoc}
+ * @deprecated as of 3.1, replaced by {@link #value(DerivativeStructure)}
+ */
+ @Deprecated
+ public UnivariateFunction derivative() {
+ return FunctionUtils.toDifferentiableUnivariateFunction(this).derivative();
+ }
+
+ /** {@inheritDoc}
+ * @since 3.1
+ */
+ public DerivativeStructure value(final DerivativeStructure t) {
+ return t.log1p();
+ }
+
+}
diff --git a/src/main/java/org/apache/commons/math3/analysis/function/Logistic.java b/src/main/java/org/apache/commons/math3/analysis/function/Logistic.java
new file mode 100644
index 0000000..c90203c
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/analysis/function/Logistic.java
@@ -0,0 +1,228 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math3.analysis.function;
+
+import org.apache.commons.math3.analysis.FunctionUtils;
+import org.apache.commons.math3.analysis.UnivariateFunction;
+import org.apache.commons.math3.analysis.DifferentiableUnivariateFunction;
+import org.apache.commons.math3.analysis.ParametricUnivariateFunction;
+import org.apache.commons.math3.analysis.differentiation.DerivativeStructure;
+import org.apache.commons.math3.analysis.differentiation.UnivariateDifferentiableFunction;
+import org.apache.commons.math3.exception.NotStrictlyPositiveException;
+import org.apache.commons.math3.exception.NullArgumentException;
+import org.apache.commons.math3.exception.DimensionMismatchException;
+import org.apache.commons.math3.util.FastMath;
+
+/**
+ * <a href="http://en.wikipedia.org/wiki/Generalised_logistic_function">
+ * Generalised logistic</a> function.
+ *
+ * @since 3.0
+ */
+public class Logistic implements UnivariateDifferentiableFunction, DifferentiableUnivariateFunction {
+ /** Lower asymptote. */
+ private final double a;
+ /** Upper asymptote. */
+ private final double k;
+ /** Growth rate. */
+ private final double b;
+ /** Parameter that affects near which asymptote maximum growth occurs. */
+ private final double oneOverN;
+ /** Parameter that affects the position of the curve along the ordinate axis. */
+ private final double q;
+ /** Abscissa of maximum growth. */
+ private final double m;
+
+ /**
+ * @param k If {@code b > 0}, value of the function for x going towards +&infin;.
+ * If {@code b < 0}, value of the function for x going towards -&infin;.
+ * @param m Abscissa of maximum growth.
+ * @param b Growth rate.
+ * @param q Parameter that affects the position of the curve along the
+ * ordinate axis.
+ * @param a If {@code b > 0}, value of the function for x going towards -&infin;.
+ * If {@code b < 0}, value of the function for x going towards +&infin;.
+ * @param n Parameter that affects near which asymptote the maximum
+ * growth occurs.
+ * @throws NotStrictlyPositiveException if {@code n <= 0}.
+ */
+ public Logistic(double k,
+ double m,
+ double b,
+ double q,
+ double a,
+ double n)
+ throws NotStrictlyPositiveException {
+ if (n <= 0) {
+ throw new NotStrictlyPositiveException(n);
+ }
+
+ this.k = k;
+ this.m = m;
+ this.b = b;
+ this.q = q;
+ this.a = a;
+ oneOverN = 1 / n;
+ }
+
+ /** {@inheritDoc} */
+ public double value(double x) {
+ return value(m - x, k, b, q, a, oneOverN);
+ }
+
+ /** {@inheritDoc}
+ * @deprecated as of 3.1, replaced by {@link #value(DerivativeStructure)}
+ */
+ @Deprecated
+ public UnivariateFunction derivative() {
+ return FunctionUtils.toDifferentiableUnivariateFunction(this).derivative();
+ }
+
+ /**
+ * Parametric function where the input array contains the parameters of
+ * the {@link Logistic#Logistic(double,double,double,double,double,double)
+ * logistic function}, ordered as follows:
+ * <ul>
+ * <li>k</li>
+ * <li>m</li>
+ * <li>b</li>
+ * <li>q</li>
+ * <li>a</li>
+ * <li>n</li>
+ * </ul>
+ */
+ public static class Parametric implements ParametricUnivariateFunction {
+ /**
+ * Computes the value of the sigmoid at {@code x}.
+ *
+ * @param x Value for which the function must be computed.
+ * @param param Values for {@code k}, {@code m}, {@code b}, {@code q},
+ * {@code a} and {@code n}.
+ * @return the value of the function.
+ * @throws NullArgumentException if {@code param} is {@code null}.
+ * @throws DimensionMismatchException if the size of {@code param} is
+ * not 6.
+ * @throws NotStrictlyPositiveException if {@code param[5] <= 0}.
+ */
+ public double value(double x, double ... param)
+ throws NullArgumentException,
+ DimensionMismatchException,
+ NotStrictlyPositiveException {
+ validateParameters(param);
+ return Logistic.value(param[1] - x, param[0],
+ param[2], param[3],
+ param[4], 1 / param[5]);
+ }
+
+ /**
+ * Computes the value of the gradient at {@code x}.
+ * The components of the gradient vector are the partial
+ * derivatives of the function with respect to each of the
+ * <em>parameters</em>.
+ *
+ * @param x Value at which the gradient must be computed.
+ * @param param Values for {@code k}, {@code m}, {@code b}, {@code q},
+ * {@code a} and {@code n}.
+ * @return the gradient vector at {@code x}.
+ * @throws NullArgumentException if {@code param} is {@code null}.
+ * @throws DimensionMismatchException if the size of {@code param} is
+ * not 6.
+ * @throws NotStrictlyPositiveException if {@code param[5] <= 0}.
+ */
+ public double[] gradient(double x, double ... param)
+ throws NullArgumentException,
+ DimensionMismatchException,
+ NotStrictlyPositiveException {
+ validateParameters(param);
+
+ final double b = param[2];
+ final double q = param[3];
+
+ final double mMinusX = param[1] - x;
+ final double oneOverN = 1 / param[5];
+ final double exp = FastMath.exp(b * mMinusX);
+ final double qExp = q * exp;
+ final double qExp1 = qExp + 1;
+ final double factor1 = (param[0] - param[4]) * oneOverN / FastMath.pow(qExp1, oneOverN);
+ final double factor2 = -factor1 / qExp1;
+
+ // Components of the gradient.
+ final double gk = Logistic.value(mMinusX, 1, b, q, 0, oneOverN);
+ final double gm = factor2 * b * qExp;
+ final double gb = factor2 * mMinusX * qExp;
+ final double gq = factor2 * exp;
+ final double ga = Logistic.value(mMinusX, 0, b, q, 1, oneOverN);
+ final double gn = factor1 * FastMath.log(qExp1) * oneOverN;
+
+ return new double[] { gk, gm, gb, gq, ga, gn };
+ }
+
+ /**
+ * Validates parameters to ensure they are appropriate for the evaluation of
+ * the {@link #value(double,double[])} and {@link #gradient(double,double[])}
+ * methods.
+ *
+ * @param param Values for {@code k}, {@code m}, {@code b}, {@code q},
+ * {@code a} and {@code n}.
+ * @throws NullArgumentException if {@code param} is {@code null}.
+ * @throws DimensionMismatchException if the size of {@code param} is
+ * not 6.
+ * @throws NotStrictlyPositiveException if {@code param[5] <= 0}.
+ */
+ private void validateParameters(double[] param)
+ throws NullArgumentException,
+ DimensionMismatchException,
+ NotStrictlyPositiveException {
+ if (param == null) {
+ throw new NullArgumentException();
+ }
+ if (param.length != 6) {
+ throw new DimensionMismatchException(param.length, 6);
+ }
+ if (param[5] <= 0) {
+ throw new NotStrictlyPositiveException(param[5]);
+ }
+ }
+ }
+
+ /**
+ * @param mMinusX {@code m - x}.
+ * @param k {@code k}.
+ * @param b {@code b}.
+ * @param q {@code q}.
+ * @param a {@code a}.
+ * @param oneOverN {@code 1 / n}.
+ * @return the value of the function.
+ */
+ private static double value(double mMinusX,
+ double k,
+ double b,
+ double q,
+ double a,
+ double oneOverN) {
+ return a + (k - a) / FastMath.pow(1 + q * FastMath.exp(b * mMinusX), oneOverN);
+ }
+
+ /** {@inheritDoc}
+ * @since 3.1
+ */
+ public DerivativeStructure value(final DerivativeStructure t) {
+ return t.negate().add(m).multiply(b).exp().multiply(q).add(1).pow(oneOverN).reciprocal().multiply(k - a).add(a);
+ }
+
+}
diff --git a/src/main/java/org/apache/commons/math3/analysis/function/Logit.java b/src/main/java/org/apache/commons/math3/analysis/function/Logit.java
new file mode 100644
index 0000000..39abe4d
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/analysis/function/Logit.java
@@ -0,0 +1,212 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math3.analysis.function;
+
+import org.apache.commons.math3.analysis.DifferentiableUnivariateFunction;
+import org.apache.commons.math3.analysis.FunctionUtils;
+import org.apache.commons.math3.analysis.ParametricUnivariateFunction;
+import org.apache.commons.math3.analysis.UnivariateFunction;
+import org.apache.commons.math3.analysis.differentiation.DerivativeStructure;
+import org.apache.commons.math3.analysis.differentiation.UnivariateDifferentiableFunction;
+import org.apache.commons.math3.exception.DimensionMismatchException;
+import org.apache.commons.math3.exception.NullArgumentException;
+import org.apache.commons.math3.exception.OutOfRangeException;
+import org.apache.commons.math3.util.FastMath;
+
+/**
+ * <a href="http://en.wikipedia.org/wiki/Logit">
+ * Logit</a> function.
+ * It is the inverse of the {@link Sigmoid sigmoid} function.
+ *
+ * @since 3.0
+ */
+public class Logit implements UnivariateDifferentiableFunction, DifferentiableUnivariateFunction {
+ /** Lower bound. */
+ private final double lo;
+ /** Higher bound. */
+ private final double hi;
+
+ /**
+ * Usual logit function, where the lower bound is 0 and the higher
+ * bound is 1.
+ */
+ public Logit() {
+ this(0, 1);
+ }
+
+ /**
+ * Logit function.
+ *
+ * @param lo Lower bound of the function domain.
+ * @param hi Higher bound of the function domain.
+ */
+ public Logit(double lo,
+ double hi) {
+ this.lo = lo;
+ this.hi = hi;
+ }
+
+ /** {@inheritDoc} */
+ public double value(double x)
+ throws OutOfRangeException {
+ return value(x, lo, hi);
+ }
+
+ /** {@inheritDoc}
+ * @deprecated as of 3.1, replaced by {@link #value(DerivativeStructure)}
+ */
+ @Deprecated
+ public UnivariateFunction derivative() {
+ return FunctionUtils.toDifferentiableUnivariateFunction(this).derivative();
+ }
+
+ /**
+ * Parametric function where the input array contains the parameters of
+ * the logit function, ordered as follows:
+ * <ul>
+ * <li>Lower bound</li>
+ * <li>Higher bound</li>
+ * </ul>
+ */
+ public static class Parametric implements ParametricUnivariateFunction {
+ /**
+ * Computes the value of the logit at {@code x}.
+ *
+ * @param x Value for which the function must be computed.
+ * @param param Values of lower bound and higher bounds.
+ * @return the value of the function.
+ * @throws NullArgumentException if {@code param} is {@code null}.
+ * @throws DimensionMismatchException if the size of {@code param} is
+ * not 2.
+ */
+ public double value(double x, double ... param)
+ throws NullArgumentException,
+ DimensionMismatchException {
+ validateParameters(param);
+ return Logit.value(x, param[0], param[1]);
+ }
+
+ /**
+ * Computes the value of the gradient at {@code x}.
+ * The components of the gradient vector are the partial
+ * derivatives of the function with respect to each of the
+ * <em>parameters</em> (lower bound and higher bound).
+ *
+ * @param x Value at which the gradient must be computed.
+ * @param param Values for lower and higher bounds.
+ * @return the gradient vector at {@code x}.
+ * @throws NullArgumentException if {@code param} is {@code null}.
+ * @throws DimensionMismatchException if the size of {@code param} is
+ * not 2.
+ */
+ public double[] gradient(double x, double ... param)
+ throws NullArgumentException,
+ DimensionMismatchException {
+ validateParameters(param);
+
+ final double lo = param[0];
+ final double hi = param[1];
+
+ return new double[] { 1 / (lo - x), 1 / (hi - x) };
+ }
+
+ /**
+ * Validates parameters to ensure they are appropriate for the evaluation of
+ * the {@link #value(double,double[])} and {@link #gradient(double,double[])}
+ * methods.
+ *
+ * @param param Values for lower and higher bounds.
+ * @throws NullArgumentException if {@code param} is {@code null}.
+ * @throws DimensionMismatchException if the size of {@code param} is
+ * not 2.
+ */
+ private void validateParameters(double[] param)
+ throws NullArgumentException,
+ DimensionMismatchException {
+ if (param == null) {
+ throw new NullArgumentException();
+ }
+ if (param.length != 2) {
+ throw new DimensionMismatchException(param.length, 2);
+ }
+ }
+ }
+
+ /**
+ * @param x Value at which to compute the logit.
+ * @param lo Lower bound.
+ * @param hi Higher bound.
+ * @return the value of the logit function at {@code x}.
+ * @throws OutOfRangeException if {@code x < lo} or {@code x > hi}.
+ */
+ private static double value(double x,
+ double lo,
+ double hi)
+ throws OutOfRangeException {
+ if (x < lo || x > hi) {
+ throw new OutOfRangeException(x, lo, hi);
+ }
+ return FastMath.log((x - lo) / (hi - x));
+ }
+
+ /** {@inheritDoc}
+ * @since 3.1
+ * @exception OutOfRangeException if parameter is outside of function domain
+ */
+ public DerivativeStructure value(final DerivativeStructure t)
+ throws OutOfRangeException {
+ final double x = t.getValue();
+ if (x < lo || x > hi) {
+ throw new OutOfRangeException(x, lo, hi);
+ }
+ double[] f = new double[t.getOrder() + 1];
+
+ // function value
+ f[0] = FastMath.log((x - lo) / (hi - x));
+
+ if (Double.isInfinite(f[0])) {
+
+ if (f.length > 1) {
+ f[1] = Double.POSITIVE_INFINITY;
+ }
+ // fill the array with infinities
+ // (for x close to lo the signs will flip between -inf and +inf,
+ // for x close to hi the signs will always be +inf)
+ // this is probably overkill, since the call to compose at the end
+ // of the method will transform most infinities into NaN ...
+ for (int i = 2; i < f.length; ++i) {
+ f[i] = f[i - 2];
+ }
+
+ } else {
+
+ // function derivatives
+ final double invL = 1.0 / (x - lo);
+ double xL = invL;
+ final double invH = 1.0 / (hi - x);
+ double xH = invH;
+ for (int i = 1; i < f.length; ++i) {
+ f[i] = xL + xH;
+ xL *= -i * invL;
+ xH *= i * invH;
+ }
+ }
+
+ return t.compose(f);
+ }
+}
diff --git a/src/main/java/org/apache/commons/math3/analysis/function/Max.java b/src/main/java/org/apache/commons/math3/analysis/function/Max.java
new file mode 100644
index 0000000..591ac55
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/analysis/function/Max.java
@@ -0,0 +1,33 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math3.analysis.function;
+
+import org.apache.commons.math3.analysis.BivariateFunction;
+import org.apache.commons.math3.util.FastMath;
+
+/**
+ * Maximum function.
+ *
+ * @since 3.0
+ */
+public class Max implements BivariateFunction {
+ /** {@inheritDoc} */
+ public double value(double x, double y) {
+ return FastMath.max(x, y);
+ }
+}
diff --git a/src/main/java/org/apache/commons/math3/analysis/function/Min.java b/src/main/java/org/apache/commons/math3/analysis/function/Min.java
new file mode 100644
index 0000000..a776b79
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/analysis/function/Min.java
@@ -0,0 +1,33 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math3.analysis.function;
+
+import org.apache.commons.math3.analysis.BivariateFunction;
+import org.apache.commons.math3.util.FastMath;
+
+/**
+ * Minimum function.
+ *
+ * @since 3.0
+ */
+public class Min implements BivariateFunction {
+ /** {@inheritDoc} */
+ public double value(double x, double y) {
+ return FastMath.min(x, y);
+ }
+}
diff --git a/src/main/java/org/apache/commons/math3/analysis/function/Minus.java b/src/main/java/org/apache/commons/math3/analysis/function/Minus.java
new file mode 100644
index 0000000..e532779
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/analysis/function/Minus.java
@@ -0,0 +1,50 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math3.analysis.function;
+
+import org.apache.commons.math3.analysis.DifferentiableUnivariateFunction;
+import org.apache.commons.math3.analysis.differentiation.DerivativeStructure;
+import org.apache.commons.math3.analysis.differentiation.UnivariateDifferentiableFunction;
+
+/**
+ * Minus function.
+ *
+ * @since 3.0
+ */
+public class Minus implements UnivariateDifferentiableFunction, DifferentiableUnivariateFunction {
+ /** {@inheritDoc} */
+ public double value(double x) {
+ return -x;
+ }
+
+ /** {@inheritDoc}
+ * @deprecated as of 3.1, replaced by {@link #value(DerivativeStructure)}
+ */
+ @Deprecated
+ public DifferentiableUnivariateFunction derivative() {
+ return new Constant(-1);
+ }
+
+ /** {@inheritDoc}
+ * @since 3.1
+ */
+ public DerivativeStructure value(final DerivativeStructure t) {
+ return t.negate();
+ }
+
+}
diff --git a/src/main/java/org/apache/commons/math3/analysis/function/Multiply.java b/src/main/java/org/apache/commons/math3/analysis/function/Multiply.java
new file mode 100644
index 0000000..b7e771b
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/analysis/function/Multiply.java
@@ -0,0 +1,32 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math3.analysis.function;
+
+import org.apache.commons.math3.analysis.BivariateFunction;
+
+/**
+ * Multiply the two operands.
+ *
+ * @since 3.0
+ */
+public class Multiply implements BivariateFunction {
+ /** {@inheritDoc} */
+ public double value(double x, double y) {
+ return x * y;
+ }
+}
diff --git a/src/main/java/org/apache/commons/math3/analysis/function/Pow.java b/src/main/java/org/apache/commons/math3/analysis/function/Pow.java
new file mode 100644
index 0000000..756dc42
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/analysis/function/Pow.java
@@ -0,0 +1,33 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math3.analysis.function;
+
+import org.apache.commons.math3.analysis.BivariateFunction;
+import org.apache.commons.math3.util.FastMath;
+
+/**
+ * Power function.
+ *
+ * @since 3.0
+ */
+public class Pow implements BivariateFunction {
+ /** {@inheritDoc} */
+ public double value(double x, double y) {
+ return FastMath.pow(x, y);
+ }
+}
diff --git a/src/main/java/org/apache/commons/math3/analysis/function/Power.java b/src/main/java/org/apache/commons/math3/analysis/function/Power.java
new file mode 100644
index 0000000..953bcab
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/analysis/function/Power.java
@@ -0,0 +1,63 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math3.analysis.function;
+
+import org.apache.commons.math3.analysis.FunctionUtils;
+import org.apache.commons.math3.analysis.UnivariateFunction;
+import org.apache.commons.math3.analysis.DifferentiableUnivariateFunction;
+import org.apache.commons.math3.analysis.differentiation.DerivativeStructure;
+import org.apache.commons.math3.analysis.differentiation.UnivariateDifferentiableFunction;
+import org.apache.commons.math3.util.FastMath;
+
+/**
+ * Power function.
+ *
+ * @since 3.0
+ */
+public class Power implements UnivariateDifferentiableFunction, DifferentiableUnivariateFunction {
+ /** Power. */
+ private final double p;
+
+ /**
+ * @param p Power.
+ */
+ public Power(double p) {
+ this.p = p;
+ }
+
+ /** {@inheritDoc} */
+ public double value(double x) {
+ return FastMath.pow(x, p);
+ }
+
+ /** {@inheritDoc}
+ * @deprecated as of 3.1, replaced by {@link #value(DerivativeStructure)}
+ */
+ @Deprecated
+ public UnivariateFunction derivative() {
+ return FunctionUtils.toDifferentiableUnivariateFunction(this).derivative();
+ }
+
+ /** {@inheritDoc}
+ * @since 3.1
+ */
+ public DerivativeStructure value(final DerivativeStructure t) {
+ return t.pow(p);
+ }
+
+}
diff --git a/src/main/java/org/apache/commons/math3/analysis/function/Rint.java b/src/main/java/org/apache/commons/math3/analysis/function/Rint.java
new file mode 100644
index 0000000..4edde58
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/analysis/function/Rint.java
@@ -0,0 +1,33 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math3.analysis.function;
+
+import org.apache.commons.math3.analysis.UnivariateFunction;
+import org.apache.commons.math3.util.FastMath;
+
+/**
+ * {@code rint} function.
+ *
+ * @since 3.0
+ */
+public class Rint implements UnivariateFunction {
+ /** {@inheritDoc} */
+ public double value(double x) {
+ return FastMath.rint(x);
+ }
+}
diff --git a/src/main/java/org/apache/commons/math3/analysis/function/Sigmoid.java b/src/main/java/org/apache/commons/math3/analysis/function/Sigmoid.java
new file mode 100644
index 0000000..54639f9
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/analysis/function/Sigmoid.java
@@ -0,0 +1,218 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math3.analysis.function;
+
+import java.util.Arrays;
+
+import org.apache.commons.math3.analysis.FunctionUtils;
+import org.apache.commons.math3.analysis.UnivariateFunction;
+import org.apache.commons.math3.analysis.DifferentiableUnivariateFunction;
+import org.apache.commons.math3.analysis.ParametricUnivariateFunction;
+import org.apache.commons.math3.analysis.differentiation.DerivativeStructure;
+import org.apache.commons.math3.analysis.differentiation.UnivariateDifferentiableFunction;
+import org.apache.commons.math3.exception.NullArgumentException;
+import org.apache.commons.math3.exception.DimensionMismatchException;
+import org.apache.commons.math3.util.FastMath;
+
+/**
+ * <a href="http://en.wikipedia.org/wiki/Sigmoid_function">
+ * Sigmoid</a> function.
+ * It is the inverse of the {@link Logit logit} function.
+ * A more flexible version, the generalised logistic, is implemented
+ * by the {@link Logistic} class.
+ *
+ * @since 3.0
+ */
+public class Sigmoid implements UnivariateDifferentiableFunction, DifferentiableUnivariateFunction {
+ /** Lower asymptote. */
+ private final double lo;
+ /** Higher asymptote. */
+ private final double hi;
+
+ /**
+ * Usual sigmoid function, where the lower asymptote is 0 and the higher
+ * asymptote is 1.
+ */
+ public Sigmoid() {
+ this(0, 1);
+ }
+
+ /**
+ * Sigmoid function.
+ *
+ * @param lo Lower asymptote.
+ * @param hi Higher asymptote.
+ */
+ public Sigmoid(double lo,
+ double hi) {
+ this.lo = lo;
+ this.hi = hi;
+ }
+
+ /** {@inheritDoc}
+ * @deprecated as of 3.1, replaced by {@link #value(DerivativeStructure)}
+ */
+ @Deprecated
+ public UnivariateFunction derivative() {
+ return FunctionUtils.toDifferentiableUnivariateFunction(this).derivative();
+ }
+
+ /** {@inheritDoc} */
+ public double value(double x) {
+ return value(x, lo, hi);
+ }
+
+ /**
+ * Parametric function where the input array contains the parameters of
+ * the {@link Sigmoid#Sigmoid(double,double) sigmoid function}, ordered
+ * as follows:
+ * <ul>
+ * <li>Lower asymptote</li>
+ * <li>Higher asymptote</li>
+ * </ul>
+ */
+ public static class Parametric implements ParametricUnivariateFunction {
+ /**
+ * Computes the value of the sigmoid at {@code x}.
+ *
+ * @param x Value for which the function must be computed.
+ * @param param Values of lower asymptote and higher asymptote.
+ * @return the value of the function.
+ * @throws NullArgumentException if {@code param} is {@code null}.
+ * @throws DimensionMismatchException if the size of {@code param} is
+ * not 2.
+ */
+ public double value(double x, double ... param)
+ throws NullArgumentException,
+ DimensionMismatchException {
+ validateParameters(param);
+ return Sigmoid.value(x, param[0], param[1]);
+ }
+
+ /**
+ * Computes the value of the gradient at {@code x}.
+ * The components of the gradient vector are the partial
+ * derivatives of the function with respect to each of the
+ * <em>parameters</em> (lower asymptote and higher asymptote).
+ *
+ * @param x Value at which the gradient must be computed.
+ * @param param Values for lower asymptote and higher asymptote.
+ * @return the gradient vector at {@code x}.
+ * @throws NullArgumentException if {@code param} is {@code null}.
+ * @throws DimensionMismatchException if the size of {@code param} is
+ * not 2.
+ */
+ public double[] gradient(double x, double ... param)
+ throws NullArgumentException,
+ DimensionMismatchException {
+ validateParameters(param);
+
+ final double invExp1 = 1 / (1 + FastMath.exp(-x));
+
+ return new double[] { 1 - invExp1, invExp1 };
+ }
+
+ /**
+ * Validates parameters to ensure they are appropriate for the evaluation of
+ * the {@link #value(double,double[])} and {@link #gradient(double,double[])}
+ * methods.
+ *
+ * @param param Values for lower and higher asymptotes.
+ * @throws NullArgumentException if {@code param} is {@code null}.
+ * @throws DimensionMismatchException if the size of {@code param} is
+ * not 2.
+ */
+ private void validateParameters(double[] param)
+ throws NullArgumentException,
+ DimensionMismatchException {
+ if (param == null) {
+ throw new NullArgumentException();
+ }
+ if (param.length != 2) {
+ throw new DimensionMismatchException(param.length, 2);
+ }
+ }
+ }
+
+ /**
+ * @param x Value at which to compute the sigmoid.
+ * @param lo Lower asymptote.
+ * @param hi Higher asymptote.
+ * @return the value of the sigmoid function at {@code x}.
+ */
+ private static double value(double x,
+ double lo,
+ double hi) {
+ return lo + (hi - lo) / (1 + FastMath.exp(-x));
+ }
+
+ /** {@inheritDoc}
+ * @since 3.1
+ */
+ public DerivativeStructure value(final DerivativeStructure t)
+ throws DimensionMismatchException {
+
+ double[] f = new double[t.getOrder() + 1];
+ final double exp = FastMath.exp(-t.getValue());
+ if (Double.isInfinite(exp)) {
+
+ // special handling near lower boundary, to avoid NaN
+ f[0] = lo;
+ Arrays.fill(f, 1, f.length, 0.0);
+
+ } else {
+
+ // the nth order derivative of sigmoid has the form:
+ // dn(sigmoid(x)/dxn = P_n(exp(-x)) / (1+exp(-x))^(n+1)
+ // where P_n(t) is a degree n polynomial with normalized higher term
+ // P_0(t) = 1, P_1(t) = t, P_2(t) = t^2 - t, P_3(t) = t^3 - 4 t^2 + t...
+ // the general recurrence relation for P_n is:
+ // P_n(x) = n t P_(n-1)(t) - t (1 + t) P_(n-1)'(t)
+ final double[] p = new double[f.length];
+
+ final double inv = 1 / (1 + exp);
+ double coeff = hi - lo;
+ for (int n = 0; n < f.length; ++n) {
+
+ // update and evaluate polynomial P_n(t)
+ double v = 0;
+ p[n] = 1;
+ for (int k = n; k >= 0; --k) {
+ v = v * exp + p[k];
+ if (k > 1) {
+ p[k - 1] = (n - k + 2) * p[k - 2] - (k - 1) * p[k - 1];
+ } else {
+ p[0] = 0;
+ }
+ }
+
+ coeff *= inv;
+ f[n] = coeff * v;
+
+ }
+
+ // fix function value
+ f[0] += lo;
+
+ }
+
+ return t.compose(f);
+
+ }
+
+}
diff --git a/src/main/java/org/apache/commons/math3/analysis/function/Signum.java b/src/main/java/org/apache/commons/math3/analysis/function/Signum.java
new file mode 100644
index 0000000..ddde66e
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/analysis/function/Signum.java
@@ -0,0 +1,33 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math3.analysis.function;
+
+import org.apache.commons.math3.analysis.UnivariateFunction;
+import org.apache.commons.math3.util.FastMath;
+
+/**
+ * {@code signum} function.
+ *
+ * @since 3.0
+ */
+public class Signum implements UnivariateFunction {
+ /** {@inheritDoc} */
+ public double value(double x) {
+ return FastMath.signum(x);
+ }
+}
diff --git a/src/main/java/org/apache/commons/math3/analysis/function/Sin.java b/src/main/java/org/apache/commons/math3/analysis/function/Sin.java
new file mode 100644
index 0000000..71c91e7
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/analysis/function/Sin.java
@@ -0,0 +1,51 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math3.analysis.function;
+
+import org.apache.commons.math3.analysis.DifferentiableUnivariateFunction;
+import org.apache.commons.math3.analysis.differentiation.DerivativeStructure;
+import org.apache.commons.math3.analysis.differentiation.UnivariateDifferentiableFunction;
+import org.apache.commons.math3.util.FastMath;
+
+/**
+ * Sine function.
+ *
+ * @since 3.0
+ */
+public class Sin implements UnivariateDifferentiableFunction, DifferentiableUnivariateFunction {
+ /** {@inheritDoc} */
+ public double value(double x) {
+ return FastMath.sin(x);
+ }
+
+ /** {@inheritDoc}
+ * @deprecated as of 3.1, replaced by {@link #value(DerivativeStructure)}
+ */
+ @Deprecated
+ public DifferentiableUnivariateFunction derivative() {
+ return new Cos();
+ }
+
+ /** {@inheritDoc}
+ * @since 3.1
+ */
+ public DerivativeStructure value(final DerivativeStructure t) {
+ return t.sin();
+ }
+
+}
diff --git a/src/main/java/org/apache/commons/math3/analysis/function/Sinc.java b/src/main/java/org/apache/commons/math3/analysis/function/Sinc.java
new file mode 100644
index 0000000..553cfff
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/analysis/function/Sinc.java
@@ -0,0 +1,205 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math3.analysis.function;
+
+import org.apache.commons.math3.analysis.DifferentiableUnivariateFunction;
+import org.apache.commons.math3.analysis.FunctionUtils;
+import org.apache.commons.math3.analysis.UnivariateFunction;
+import org.apache.commons.math3.analysis.differentiation.DerivativeStructure;
+import org.apache.commons.math3.analysis.differentiation.UnivariateDifferentiableFunction;
+import org.apache.commons.math3.exception.DimensionMismatchException;
+import org.apache.commons.math3.util.FastMath;
+
+/**
+ * <a href="http://en.wikipedia.org/wiki/Sinc_function">Sinc</a> function,
+ * defined by
+ * <pre><code>
+ * sinc(x) = 1 if x = 0,
+ * sin(x) / x otherwise.
+ * </code></pre>
+ *
+ * @since 3.0
+ */
+public class Sinc implements UnivariateDifferentiableFunction, DifferentiableUnivariateFunction {
+ /**
+ * Value below which the computations are done using Taylor series.
+ * <p>
+ * The Taylor series for sinc even order derivatives are:
+ * <pre>
+ * d^(2n)sinc/dx^(2n) = Sum_(k>=0) (-1)^(n+k) / ((2k)!(2n+2k+1)) x^(2k)
+ * = (-1)^n [ 1/(2n+1) - x^2/(4n+6) + x^4/(48n+120) - x^6/(1440n+5040) + O(x^8) ]
+ * </pre>
+ * </p>
+ * <p>
+ * The Taylor series for sinc odd order derivatives are:
+ * <pre>
+ * d^(2n+1)sinc/dx^(2n+1) = Sum_(k>=0) (-1)^(n+k+1) / ((2k+1)!(2n+2k+3)) x^(2k+1)
+ * = (-1)^(n+1) [ x/(2n+3) - x^3/(12n+30) + x^5/(240n+840) - x^7/(10080n+45360) + O(x^9) ]
+ * </pre>
+ * </p>
+ * <p>
+ * So the ratio of the fourth term with respect to the first term
+ * is always smaller than x^6/720, for all derivative orders.
+ * This implies that neglecting this term and using only the first three terms induces
+ * a relative error bounded by x^6/720. The SHORTCUT value is chosen such that this
+ * relative error is below double precision accuracy when |x| <= SHORTCUT.
+ * </p>
+ */
+ private static final double SHORTCUT = 6.0e-3;
+ /** For normalized sinc function. */
+ private final boolean normalized;
+
+ /**
+ * The sinc function, {@code sin(x) / x}.
+ */
+ public Sinc() {
+ this(false);
+ }
+
+ /**
+ * Instantiates the sinc function.
+ *
+ * @param normalized If {@code true}, the function is
+ * <code> sin(&pi;x) / &pi;x</code>, otherwise {@code sin(x) / x}.
+ */
+ public Sinc(boolean normalized) {
+ this.normalized = normalized;
+ }
+
+ /** {@inheritDoc} */
+ public double value(final double x) {
+ final double scaledX = normalized ? FastMath.PI * x : x;
+ if (FastMath.abs(scaledX) <= SHORTCUT) {
+ // use Taylor series
+ final double scaledX2 = scaledX * scaledX;
+ return ((scaledX2 - 20) * scaledX2 + 120) / 120;
+ } else {
+ // use definition expression
+ return FastMath.sin(scaledX) / scaledX;
+ }
+ }
+
+ /** {@inheritDoc}
+ * @deprecated as of 3.1, replaced by {@link #value(DerivativeStructure)}
+ */
+ @Deprecated
+ public UnivariateFunction derivative() {
+ return FunctionUtils.toDifferentiableUnivariateFunction(this).derivative();
+ }
+
+ /** {@inheritDoc}
+ * @since 3.1
+ */
+ public DerivativeStructure value(final DerivativeStructure t)
+ throws DimensionMismatchException {
+
+ final double scaledX = (normalized ? FastMath.PI : 1) * t.getValue();
+ final double scaledX2 = scaledX * scaledX;
+
+ double[] f = new double[t.getOrder() + 1];
+
+ if (FastMath.abs(scaledX) <= SHORTCUT) {
+
+ for (int i = 0; i < f.length; ++i) {
+ final int k = i / 2;
+ if ((i & 0x1) == 0) {
+ // even derivation order
+ f[i] = (((k & 0x1) == 0) ? 1 : -1) *
+ (1.0 / (i + 1) - scaledX2 * (1.0 / (2 * i + 6) - scaledX2 / (24 * i + 120)));
+ } else {
+ // odd derivation order
+ f[i] = (((k & 0x1) == 0) ? -scaledX : scaledX) *
+ (1.0 / (i + 2) - scaledX2 * (1.0 / (6 * i + 24) - scaledX2 / (120 * i + 720)));
+ }
+ }
+
+ } else {
+
+ final double inv = 1 / scaledX;
+ final double cos = FastMath.cos(scaledX);
+ final double sin = FastMath.sin(scaledX);
+
+ f[0] = inv * sin;
+
+ // the nth order derivative of sinc has the form:
+ // dn(sinc(x)/dxn = [S_n(x) sin(x) + C_n(x) cos(x)] / x^(n+1)
+ // where S_n(x) is an even polynomial with degree n-1 or n (depending on parity)
+ // and C_n(x) is an odd polynomial with degree n-1 or n (depending on parity)
+ // S_0(x) = 1, S_1(x) = -1, S_2(x) = -x^2 + 2, S_3(x) = 3x^2 - 6...
+ // C_0(x) = 0, C_1(x) = x, C_2(x) = -2x, C_3(x) = -x^3 + 6x...
+ // the general recurrence relations for S_n and C_n are:
+ // S_n(x) = x S_(n-1)'(x) - n S_(n-1)(x) - x C_(n-1)(x)
+ // C_n(x) = x C_(n-1)'(x) - n C_(n-1)(x) + x S_(n-1)(x)
+ // as per polynomials parity, we can store both S_n and C_n in the same array
+ final double[] sc = new double[f.length];
+ sc[0] = 1;
+
+ double coeff = inv;
+ for (int n = 1; n < f.length; ++n) {
+
+ double s = 0;
+ double c = 0;
+
+ // update and evaluate polynomials S_n(x) and C_n(x)
+ final int kStart;
+ if ((n & 0x1) == 0) {
+ // even derivation order, S_n is degree n and C_n is degree n-1
+ sc[n] = 0;
+ kStart = n;
+ } else {
+ // odd derivation order, S_n is degree n-1 and C_n is degree n
+ sc[n] = sc[n - 1];
+ c = sc[n];
+ kStart = n - 1;
+ }
+
+ // in this loop, k is always even
+ for (int k = kStart; k > 1; k -= 2) {
+
+ // sine part
+ sc[k] = (k - n) * sc[k] - sc[k - 1];
+ s = s * scaledX2 + sc[k];
+
+ // cosine part
+ sc[k - 1] = (k - 1 - n) * sc[k - 1] + sc[k -2];
+ c = c * scaledX2 + sc[k - 1];
+
+ }
+ sc[0] *= -n;
+ s = s * scaledX2 + sc[0];
+
+ coeff *= inv;
+ f[n] = coeff * (s * sin + c * scaledX * cos);
+
+ }
+
+ }
+
+ if (normalized) {
+ double scale = FastMath.PI;
+ for (int i = 1; i < f.length; ++i) {
+ f[i] *= scale;
+ scale *= FastMath.PI;
+ }
+ }
+
+ return t.compose(f);
+
+ }
+
+}
diff --git a/src/main/java/org/apache/commons/math3/analysis/function/Sinh.java b/src/main/java/org/apache/commons/math3/analysis/function/Sinh.java
new file mode 100644
index 0000000..1eac044
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/analysis/function/Sinh.java
@@ -0,0 +1,51 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math3.analysis.function;
+
+import org.apache.commons.math3.analysis.DifferentiableUnivariateFunction;
+import org.apache.commons.math3.analysis.differentiation.DerivativeStructure;
+import org.apache.commons.math3.analysis.differentiation.UnivariateDifferentiableFunction;
+import org.apache.commons.math3.util.FastMath;
+
+/**
+ * Hyperbolic sine function.
+ *
+ * @since 3.0
+ */
+public class Sinh implements UnivariateDifferentiableFunction, DifferentiableUnivariateFunction {
+ /** {@inheritDoc} */
+ public double value(double x) {
+ return FastMath.sinh(x);
+ }
+
+ /** {@inheritDoc}
+ * @deprecated as of 3.1, replaced by {@link #value(DerivativeStructure)}
+ */
+ @Deprecated
+ public DifferentiableUnivariateFunction derivative() {
+ return new Cosh();
+ }
+
+ /** {@inheritDoc}
+ * @since 3.1
+ */
+ public DerivativeStructure value(final DerivativeStructure t) {
+ return t.sinh();
+ }
+
+}
diff --git a/src/main/java/org/apache/commons/math3/analysis/function/Sqrt.java b/src/main/java/org/apache/commons/math3/analysis/function/Sqrt.java
new file mode 100644
index 0000000..720d44d
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/analysis/function/Sqrt.java
@@ -0,0 +1,53 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math3.analysis.function;
+
+import org.apache.commons.math3.analysis.FunctionUtils;
+import org.apache.commons.math3.analysis.UnivariateFunction;
+import org.apache.commons.math3.analysis.DifferentiableUnivariateFunction;
+import org.apache.commons.math3.analysis.differentiation.DerivativeStructure;
+import org.apache.commons.math3.analysis.differentiation.UnivariateDifferentiableFunction;
+import org.apache.commons.math3.util.FastMath;
+
+/**
+ * Square-root function.
+ *
+ * @since 3.0
+ */
+public class Sqrt implements UnivariateDifferentiableFunction, DifferentiableUnivariateFunction {
+ /** {@inheritDoc} */
+ public double value(double x) {
+ return FastMath.sqrt(x);
+ }
+
+ /** {@inheritDoc}
+ * @deprecated as of 3.1, replaced by {@link #value(DerivativeStructure)}
+ */
+ @Deprecated
+ public UnivariateFunction derivative() {
+ return FunctionUtils.toDifferentiableUnivariateFunction(this).derivative();
+ }
+
+ /** {@inheritDoc}
+ * @since 3.1
+ */
+ public DerivativeStructure value(final DerivativeStructure t) {
+ return t.sqrt();
+ }
+
+}
diff --git a/src/main/java/org/apache/commons/math3/analysis/function/StepFunction.java b/src/main/java/org/apache/commons/math3/analysis/function/StepFunction.java
new file mode 100644
index 0000000..e3d16be
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/analysis/function/StepFunction.java
@@ -0,0 +1,101 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math3.analysis.function;
+
+import java.util.Arrays;
+
+import org.apache.commons.math3.analysis.UnivariateFunction;
+import org.apache.commons.math3.exception.DimensionMismatchException;
+import org.apache.commons.math3.exception.NoDataException;
+import org.apache.commons.math3.exception.NonMonotonicSequenceException;
+import org.apache.commons.math3.exception.NullArgumentException;
+import org.apache.commons.math3.util.MathArrays;
+
+/**
+ * <a href="http://en.wikipedia.org/wiki/Step_function">
+ * Step function</a>.
+ *
+ * @since 3.0
+ */
+public class StepFunction implements UnivariateFunction {
+ /** Abscissae. */
+ private final double[] abscissa;
+ /** Ordinates. */
+ private final double[] ordinate;
+
+ /**
+ * Builds a step function from a list of arguments and the corresponding
+ * values. Specifically, returns the function h(x) defined by <pre><code>
+ * h(x) = y[0] for all x &lt; x[1]
+ * y[1] for x[1] &le; x &lt; x[2]
+ * ...
+ * y[y.length - 1] for x &ge; x[x.length - 1]
+ * </code></pre>
+ * The value of {@code x[0]} is ignored, but it must be strictly less than
+ * {@code x[1]}.
+ *
+ * @param x Domain values where the function changes value.
+ * @param y Values of the function.
+ * @throws NonMonotonicSequenceException
+ * if the {@code x} array is not sorted in strictly increasing order.
+ * @throws NullArgumentException if {@code x} or {@code y} are {@code null}.
+ * @throws NoDataException if {@code x} or {@code y} are zero-length.
+ * @throws DimensionMismatchException if {@code x} and {@code y} do not
+ * have the same length.
+ */
+ public StepFunction(double[] x,
+ double[] y)
+ throws NullArgumentException, NoDataException,
+ DimensionMismatchException, NonMonotonicSequenceException {
+ if (x == null ||
+ y == null) {
+ throw new NullArgumentException();
+ }
+ if (x.length == 0 ||
+ y.length == 0) {
+ throw new NoDataException();
+ }
+ if (y.length != x.length) {
+ throw new DimensionMismatchException(y.length, x.length);
+ }
+ MathArrays.checkOrder(x);
+
+ abscissa = MathArrays.copyOf(x);
+ ordinate = MathArrays.copyOf(y);
+ }
+
+ /** {@inheritDoc} */
+ public double value(double x) {
+ int index = Arrays.binarySearch(abscissa, x);
+ double fx = 0;
+
+ if (index < -1) {
+ // "x" is between "abscissa[-index-2]" and "abscissa[-index-1]".
+ fx = ordinate[-index-2];
+ } else if (index >= 0) {
+ // "x" is exactly "abscissa[index]".
+ fx = ordinate[index];
+ } else {
+ // Otherwise, "x" is smaller than the first value in "abscissa"
+ // (hence the returned value should be "ordinate[0]").
+ fx = ordinate[0];
+ }
+
+ return fx;
+ }
+}
diff --git a/src/main/java/org/apache/commons/math3/analysis/function/Subtract.java b/src/main/java/org/apache/commons/math3/analysis/function/Subtract.java
new file mode 100644
index 0000000..7b87dd6
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/analysis/function/Subtract.java
@@ -0,0 +1,32 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math3.analysis.function;
+
+import org.apache.commons.math3.analysis.BivariateFunction;
+
+/**
+ * Subtract the second operand from the first.
+ *
+ * @since 3.0
+ */
+public class Subtract implements BivariateFunction {
+ /** {@inheritDoc} */
+ public double value(double x, double y) {
+ return x - y;
+ }
+}
diff --git a/src/main/java/org/apache/commons/math3/analysis/function/Tan.java b/src/main/java/org/apache/commons/math3/analysis/function/Tan.java
new file mode 100644
index 0000000..03304b4
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/analysis/function/Tan.java
@@ -0,0 +1,53 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math3.analysis.function;
+
+import org.apache.commons.math3.analysis.FunctionUtils;
+import org.apache.commons.math3.analysis.UnivariateFunction;
+import org.apache.commons.math3.analysis.DifferentiableUnivariateFunction;
+import org.apache.commons.math3.analysis.differentiation.DerivativeStructure;
+import org.apache.commons.math3.analysis.differentiation.UnivariateDifferentiableFunction;
+import org.apache.commons.math3.util.FastMath;
+
+/**
+ * Tangent function.
+ *
+ * @since 3.0
+ */
+public class Tan implements UnivariateDifferentiableFunction, DifferentiableUnivariateFunction {
+ /** {@inheritDoc} */
+ public double value(double x) {
+ return FastMath.tan(x);
+ }
+
+ /** {@inheritDoc}
+ * @deprecated as of 3.1, replaced by {@link #value(DerivativeStructure)}
+ */
+ @Deprecated
+ public UnivariateFunction derivative() {
+ return FunctionUtils.toDifferentiableUnivariateFunction(this).derivative();
+ }
+
+ /** {@inheritDoc}
+ * @since 3.1
+ */
+ public DerivativeStructure value(final DerivativeStructure t) {
+ return t.tan();
+ }
+
+}
diff --git a/src/main/java/org/apache/commons/math3/analysis/function/Tanh.java b/src/main/java/org/apache/commons/math3/analysis/function/Tanh.java
new file mode 100644
index 0000000..6c7ef0d
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/analysis/function/Tanh.java
@@ -0,0 +1,53 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math3.analysis.function;
+
+import org.apache.commons.math3.analysis.FunctionUtils;
+import org.apache.commons.math3.analysis.UnivariateFunction;
+import org.apache.commons.math3.analysis.DifferentiableUnivariateFunction;
+import org.apache.commons.math3.analysis.differentiation.DerivativeStructure;
+import org.apache.commons.math3.analysis.differentiation.UnivariateDifferentiableFunction;
+import org.apache.commons.math3.util.FastMath;
+
+/**
+ * Hyperbolic tangent function.
+ *
+ * @since 3.0
+ */
+public class Tanh implements UnivariateDifferentiableFunction, DifferentiableUnivariateFunction {
+ /** {@inheritDoc} */
+ public double value(double x) {
+ return FastMath.tanh(x);
+ }
+
+ /** {@inheritDoc}
+ * @deprecated as of 3.1, replaced by {@link #value(DerivativeStructure)}
+ */
+ @Deprecated
+ public UnivariateFunction derivative() {
+ return FunctionUtils.toDifferentiableUnivariateFunction(this).derivative();
+ }
+
+ /** {@inheritDoc}
+ * @since 3.1
+ */
+ public DerivativeStructure value(final DerivativeStructure t) {
+ return t.tanh();
+ }
+
+}
diff --git a/src/main/java/org/apache/commons/math3/analysis/function/Ulp.java b/src/main/java/org/apache/commons/math3/analysis/function/Ulp.java
new file mode 100644
index 0000000..d075a73
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/analysis/function/Ulp.java
@@ -0,0 +1,33 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math3.analysis.function;
+
+import org.apache.commons.math3.analysis.UnivariateFunction;
+import org.apache.commons.math3.util.FastMath;
+
+/**
+ * {@code ulp} function.
+ *
+ * @since 3.0
+ */
+public class Ulp implements UnivariateFunction {
+ /** {@inheritDoc} */
+ public double value(double x) {
+ return FastMath.ulp(x);
+ }
+}
diff --git a/src/main/java/org/apache/commons/math3/analysis/function/package-info.java b/src/main/java/org/apache/commons/math3/analysis/function/package-info.java
new file mode 100644
index 0000000..cb24544
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/analysis/function/package-info.java
@@ -0,0 +1,26 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/**
+ *
+ * <p>
+ * The {@code function} package contains function objects that wrap the
+ * methods contained in {@link java.lang.Math}, as well as common
+ * mathematical functions such as the gaussian and sinc functions.
+ * </p>
+ *
+ */
+package org.apache.commons.math3.analysis.function;
diff --git a/src/main/java/org/apache/commons/math3/analysis/integration/BaseAbstractUnivariateIntegrator.java b/src/main/java/org/apache/commons/math3/analysis/integration/BaseAbstractUnivariateIntegrator.java
new file mode 100644
index 0000000..74b959b
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/analysis/integration/BaseAbstractUnivariateIntegrator.java
@@ -0,0 +1,297 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math3.analysis.integration;
+
+import org.apache.commons.math3.analysis.UnivariateFunction;
+import org.apache.commons.math3.analysis.solvers.UnivariateSolverUtils;
+import org.apache.commons.math3.exception.MathIllegalArgumentException;
+import org.apache.commons.math3.exception.MaxCountExceededException;
+import org.apache.commons.math3.exception.NotStrictlyPositiveException;
+import org.apache.commons.math3.exception.NullArgumentException;
+import org.apache.commons.math3.exception.NumberIsTooSmallException;
+import org.apache.commons.math3.exception.TooManyEvaluationsException;
+import org.apache.commons.math3.util.IntegerSequence;
+import org.apache.commons.math3.util.MathUtils;
+
+/**
+ * Provide a default implementation for several generic functions.
+ *
+ * @since 1.2
+ */
+public abstract class BaseAbstractUnivariateIntegrator implements UnivariateIntegrator {
+
+ /** Default absolute accuracy. */
+ public static final double DEFAULT_ABSOLUTE_ACCURACY = 1.0e-15;
+
+ /** Default relative accuracy. */
+ public static final double DEFAULT_RELATIVE_ACCURACY = 1.0e-6;
+
+ /** Default minimal iteration count. */
+ public static final int DEFAULT_MIN_ITERATIONS_COUNT = 3;
+
+ /** Default maximal iteration count. */
+ public static final int DEFAULT_MAX_ITERATIONS_COUNT = Integer.MAX_VALUE;
+
+ /** The iteration count.
+ * @deprecated as of 3.6, this field has been replaced with {@link #incrementCount()}
+ */
+ @Deprecated
+ protected org.apache.commons.math3.util.Incrementor iterations;
+
+ /** The iteration count. */
+ private IntegerSequence.Incrementor count;
+
+ /** Maximum absolute error. */
+ private final double absoluteAccuracy;
+
+ /** Maximum relative error. */
+ private final double relativeAccuracy;
+
+ /** minimum number of iterations */
+ private final int minimalIterationCount;
+
+ /** The functions evaluation count. */
+ private IntegerSequence.Incrementor evaluations;
+
+ /** Function to integrate. */
+ private UnivariateFunction function;
+
+ /** Lower bound for the interval. */
+ private double min;
+
+ /** Upper bound for the interval. */
+ private double max;
+
+ /**
+ * Construct an integrator with given accuracies and iteration counts.
+ * <p>
+ * The meanings of the various parameters are:
+ * <ul>
+ * <li>relative accuracy:
+ * this is used to stop iterations if the absolute accuracy can't be
+ * achieved due to large values or short mantissa length. If this
+ * should be the primary criterion for convergence rather then a
+ * safety measure, set the absolute accuracy to a ridiculously small value,
+ * like {@link org.apache.commons.math3.util.Precision#SAFE_MIN Precision.SAFE_MIN}.</li>
+ * <li>absolute accuracy:
+ * The default is usually chosen so that results in the interval
+ * -10..-0.1 and +0.1..+10 can be found with a reasonable accuracy. If the
+ * expected absolute value of your results is of much smaller magnitude, set
+ * this to a smaller value.</li>
+ * <li>minimum number of iterations:
+ * minimal iteration is needed to avoid false early convergence, e.g.
+ * the sample points happen to be zeroes of the function. Users can
+ * use the default value or choose one that they see as appropriate.</li>
+ * <li>maximum number of iterations:
+ * usually a high iteration count indicates convergence problems. However,
+ * the "reasonable value" varies widely for different algorithms. Users are
+ * advised to use the default value supplied by the algorithm.</li>
+ * </ul>
+ *
+ * @param relativeAccuracy relative accuracy of the result
+ * @param absoluteAccuracy absolute accuracy of the result
+ * @param minimalIterationCount minimum number of iterations
+ * @param maximalIterationCount maximum number of iterations
+ * @exception NotStrictlyPositiveException if minimal number of iterations
+ * is not strictly positive
+ * @exception NumberIsTooSmallException if maximal number of iterations
+ * is lesser than or equal to the minimal number of iterations
+ */
+ protected BaseAbstractUnivariateIntegrator(final double relativeAccuracy,
+ final double absoluteAccuracy,
+ final int minimalIterationCount,
+ final int maximalIterationCount)
+ throws NotStrictlyPositiveException, NumberIsTooSmallException {
+
+ // accuracy settings
+ this.relativeAccuracy = relativeAccuracy;
+ this.absoluteAccuracy = absoluteAccuracy;
+
+ // iterations count settings
+ if (minimalIterationCount <= 0) {
+ throw new NotStrictlyPositiveException(minimalIterationCount);
+ }
+ if (maximalIterationCount <= minimalIterationCount) {
+ throw new NumberIsTooSmallException(maximalIterationCount, minimalIterationCount, false);
+ }
+ this.minimalIterationCount = minimalIterationCount;
+ this.count = IntegerSequence.Incrementor.create().withMaximalCount(maximalIterationCount);
+
+ @SuppressWarnings("deprecation")
+ org.apache.commons.math3.util.Incrementor wrapped =
+ org.apache.commons.math3.util.Incrementor.wrap(count);
+ this.iterations = wrapped;
+
+ // prepare evaluations counter, but do not set it yet
+ evaluations = IntegerSequence.Incrementor.create();
+
+ }
+
+ /**
+ * Construct an integrator with given accuracies.
+ * @param relativeAccuracy relative accuracy of the result
+ * @param absoluteAccuracy absolute accuracy of the result
+ */
+ protected BaseAbstractUnivariateIntegrator(final double relativeAccuracy,
+ final double absoluteAccuracy) {
+ this(relativeAccuracy, absoluteAccuracy,
+ DEFAULT_MIN_ITERATIONS_COUNT, DEFAULT_MAX_ITERATIONS_COUNT);
+ }
+
+ /**
+ * Construct an integrator with given iteration counts.
+ * @param minimalIterationCount minimum number of iterations
+ * @param maximalIterationCount maximum number of iterations
+ * @exception NotStrictlyPositiveException if minimal number of iterations
+ * is not strictly positive
+ * @exception NumberIsTooSmallException if maximal number of iterations
+ * is lesser than or equal to the minimal number of iterations
+ */
+ protected BaseAbstractUnivariateIntegrator(final int minimalIterationCount,
+ final int maximalIterationCount)
+ throws NotStrictlyPositiveException, NumberIsTooSmallException {
+ this(DEFAULT_RELATIVE_ACCURACY, DEFAULT_ABSOLUTE_ACCURACY,
+ minimalIterationCount, maximalIterationCount);
+ }
+
+ /** {@inheritDoc} */
+ public double getRelativeAccuracy() {
+ return relativeAccuracy;
+ }
+
+ /** {@inheritDoc} */
+ public double getAbsoluteAccuracy() {
+ return absoluteAccuracy;
+ }
+
+ /** {@inheritDoc} */
+ public int getMinimalIterationCount() {
+ return minimalIterationCount;
+ }
+
+ /** {@inheritDoc} */
+ public int getMaximalIterationCount() {
+ return count.getMaximalCount();
+ }
+
+ /** {@inheritDoc} */
+ public int getEvaluations() {
+ return evaluations.getCount();
+ }
+
+ /** {@inheritDoc} */
+ public int getIterations() {
+ return count.getCount();
+ }
+
+ /** Increment the number of iterations.
+ * @exception MaxCountExceededException if the number of iterations
+ * exceeds the allowed maximum number
+ */
+ protected void incrementCount() throws MaxCountExceededException {
+ count.increment();
+ }
+
+ /**
+ * @return the lower bound.
+ */
+ protected double getMin() {
+ return min;
+ }
+ /**
+ * @return the upper bound.
+ */
+ protected double getMax() {
+ return max;
+ }
+
+ /**
+ * Compute the objective function value.
+ *
+ * @param point Point at which the objective function must be evaluated.
+ * @return the objective function value at specified point.
+ * @throws TooManyEvaluationsException if the maximal number of function
+ * evaluations is exceeded.
+ */
+ protected double computeObjectiveValue(final double point)
+ throws TooManyEvaluationsException {
+ try {
+ evaluations.increment();
+ } catch (MaxCountExceededException e) {
+ throw new TooManyEvaluationsException(e.getMax());
+ }
+ return function.value(point);
+ }
+
+ /**
+ * Prepare for computation.
+ * Subclasses must call this method if they override any of the
+ * {@code solve} methods.
+ *
+ * @param maxEval Maximum number of evaluations.
+ * @param f the integrand function
+ * @param lower the min bound for the interval
+ * @param upper the upper bound for the interval
+ * @throws NullArgumentException if {@code f} is {@code null}.
+ * @throws MathIllegalArgumentException if {@code min >= max}.
+ */
+ protected void setup(final int maxEval,
+ final UnivariateFunction f,
+ final double lower, final double upper)
+ throws NullArgumentException, MathIllegalArgumentException {
+
+ // Checks.
+ MathUtils.checkNotNull(f);
+ UnivariateSolverUtils.verifyInterval(lower, upper);
+
+ // Reset.
+ min = lower;
+ max = upper;
+ function = f;
+ evaluations = evaluations.withMaximalCount(maxEval).withStart(0);
+ count = count.withStart(0);
+
+ }
+
+ /** {@inheritDoc} */
+ public double integrate(final int maxEval, final UnivariateFunction f,
+ final double lower, final double upper)
+ throws TooManyEvaluationsException, MaxCountExceededException,
+ MathIllegalArgumentException, NullArgumentException {
+
+ // Initialization.
+ setup(maxEval, f, lower, upper);
+
+ // Perform computation.
+ return doIntegrate();
+
+ }
+
+ /**
+ * Method for implementing actual integration algorithms in derived
+ * classes.
+ *
+ * @return the root.
+ * @throws TooManyEvaluationsException if the maximal number of evaluations
+ * is exceeded.
+ * @throws MaxCountExceededException if the maximum iteration count is exceeded
+ * or the integrator detects convergence problems otherwise
+ */
+ protected abstract double doIntegrate()
+ throws TooManyEvaluationsException, MaxCountExceededException;
+
+}
diff --git a/src/main/java/org/apache/commons/math3/analysis/integration/IterativeLegendreGaussIntegrator.java b/src/main/java/org/apache/commons/math3/analysis/integration/IterativeLegendreGaussIntegrator.java
new file mode 100644
index 0000000..20700dd
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/analysis/integration/IterativeLegendreGaussIntegrator.java
@@ -0,0 +1,183 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math3.analysis.integration;
+
+import org.apache.commons.math3.analysis.UnivariateFunction;
+import org.apache.commons.math3.analysis.integration.gauss.GaussIntegratorFactory;
+import org.apache.commons.math3.analysis.integration.gauss.GaussIntegrator;
+import org.apache.commons.math3.exception.MathIllegalArgumentException;
+import org.apache.commons.math3.exception.MaxCountExceededException;
+import org.apache.commons.math3.exception.NotStrictlyPositiveException;
+import org.apache.commons.math3.exception.NumberIsTooSmallException;
+import org.apache.commons.math3.exception.TooManyEvaluationsException;
+import org.apache.commons.math3.exception.util.LocalizedFormats;
+import org.apache.commons.math3.util.FastMath;
+
+/**
+ * This algorithm divides the integration interval into equally-sized
+ * sub-interval and on each of them performs a
+ * <a href="http://mathworld.wolfram.com/Legendre-GaussQuadrature.html">
+ * Legendre-Gauss</a> quadrature.
+ * Because of its <em>non-adaptive</em> nature, this algorithm can
+ * converge to a wrong value for the integral (for example, if the
+ * function is significantly different from zero toward the ends of the
+ * integration interval).
+ * In particular, a change of variables aimed at estimating integrals
+ * over infinite intervals as proposed
+ * <a href="http://en.wikipedia.org/w/index.php?title=Numerical_integration#Integrals_over_infinite_intervals">
+ * here</a> should be avoided when using this class.
+ *
+ * @since 3.1
+ */
+
+public class IterativeLegendreGaussIntegrator
+ extends BaseAbstractUnivariateIntegrator {
+ /** Factory that computes the points and weights. */
+ private static final GaussIntegratorFactory FACTORY
+ = new GaussIntegratorFactory();
+ /** Number of integration points (per interval). */
+ private final int numberOfPoints;
+
+ /**
+ * Builds an integrator with given accuracies and iterations counts.
+ *
+ * @param n Number of integration points.
+ * @param relativeAccuracy Relative accuracy of the result.
+ * @param absoluteAccuracy Absolute accuracy of the result.
+ * @param minimalIterationCount Minimum number of iterations.
+ * @param maximalIterationCount Maximum number of iterations.
+ * @throws NotStrictlyPositiveException if minimal number of iterations
+ * or number of points are not strictly positive.
+ * @throws NumberIsTooSmallException if maximal number of iterations
+ * is smaller than or equal to the minimal number of iterations.
+ */
+ public IterativeLegendreGaussIntegrator(final int n,
+ final double relativeAccuracy,
+ final double absoluteAccuracy,
+ final int minimalIterationCount,
+ final int maximalIterationCount)
+ throws NotStrictlyPositiveException, NumberIsTooSmallException {
+ super(relativeAccuracy, absoluteAccuracy, minimalIterationCount, maximalIterationCount);
+ if (n <= 0) {
+ throw new NotStrictlyPositiveException(LocalizedFormats.NUMBER_OF_POINTS, n);
+ }
+ numberOfPoints = n;
+ }
+
+ /**
+ * Builds an integrator with given accuracies.
+ *
+ * @param n Number of integration points.
+ * @param relativeAccuracy Relative accuracy of the result.
+ * @param absoluteAccuracy Absolute accuracy of the result.
+ * @throws NotStrictlyPositiveException if {@code n < 1}.
+ */
+ public IterativeLegendreGaussIntegrator(final int n,
+ final double relativeAccuracy,
+ final double absoluteAccuracy)
+ throws NotStrictlyPositiveException {
+ this(n, relativeAccuracy, absoluteAccuracy,
+ DEFAULT_MIN_ITERATIONS_COUNT, DEFAULT_MAX_ITERATIONS_COUNT);
+ }
+
+ /**
+ * Builds an integrator with given iteration counts.
+ *
+ * @param n Number of integration points.
+ * @param minimalIterationCount Minimum number of iterations.
+ * @param maximalIterationCount Maximum number of iterations.
+ * @throws NotStrictlyPositiveException if minimal number of iterations
+ * is not strictly positive.
+ * @throws NumberIsTooSmallException if maximal number of iterations
+ * is smaller than or equal to the minimal number of iterations.
+ * @throws NotStrictlyPositiveException if {@code n < 1}.
+ */
+ public IterativeLegendreGaussIntegrator(final int n,
+ final int minimalIterationCount,
+ final int maximalIterationCount)
+ throws NotStrictlyPositiveException, NumberIsTooSmallException {
+ this(n, DEFAULT_RELATIVE_ACCURACY, DEFAULT_ABSOLUTE_ACCURACY,
+ minimalIterationCount, maximalIterationCount);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ protected double doIntegrate()
+ throws MathIllegalArgumentException, TooManyEvaluationsException, MaxCountExceededException {
+ // Compute first estimate with a single step.
+ double oldt = stage(1);
+
+ int n = 2;
+ while (true) {
+ // Improve integral with a larger number of steps.
+ final double t = stage(n);
+
+ // Estimate the error.
+ final double delta = FastMath.abs(t - oldt);
+ final double limit =
+ FastMath.max(getAbsoluteAccuracy(),
+ getRelativeAccuracy() * (FastMath.abs(oldt) + FastMath.abs(t)) * 0.5);
+
+ // check convergence
+ if (getIterations() + 1 >= getMinimalIterationCount() &&
+ delta <= limit) {
+ return t;
+ }
+
+ // Prepare next iteration.
+ final double ratio = FastMath.min(4, FastMath.pow(delta / limit, 0.5 / numberOfPoints));
+ n = FastMath.max((int) (ratio * n), n + 1);
+ oldt = t;
+ incrementCount();
+ }
+ }
+
+ /**
+ * Compute the n-th stage integral.
+ *
+ * @param n Number of steps.
+ * @return the value of n-th stage integral.
+ * @throws TooManyEvaluationsException if the maximum number of evaluations
+ * is exceeded.
+ */
+ private double stage(final int n)
+ throws TooManyEvaluationsException {
+ // Function to be integrated is stored in the base class.
+ final UnivariateFunction f = new UnivariateFunction() {
+ /** {@inheritDoc} */
+ public double value(double x)
+ throws MathIllegalArgumentException, TooManyEvaluationsException {
+ return computeObjectiveValue(x);
+ }
+ };
+
+ final double min = getMin();
+ final double max = getMax();
+ final double step = (max - min) / n;
+
+ double sum = 0;
+ for (int i = 0; i < n; i++) {
+ // Integrate over each sub-interval [a, b].
+ final double a = min + i * step;
+ final double b = a + step;
+ final GaussIntegrator g = FACTORY.legendreHighPrecision(numberOfPoints, a, b);
+ sum += g.integrate(f);
+ }
+
+ return sum;
+ }
+}
diff --git a/src/main/java/org/apache/commons/math3/analysis/integration/LegendreGaussIntegrator.java b/src/main/java/org/apache/commons/math3/analysis/integration/LegendreGaussIntegrator.java
new file mode 100644
index 0000000..bfb24a1
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/analysis/integration/LegendreGaussIntegrator.java
@@ -0,0 +1,265 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math3.analysis.integration;
+
+import org.apache.commons.math3.exception.MathIllegalArgumentException;
+import org.apache.commons.math3.exception.MaxCountExceededException;
+import org.apache.commons.math3.exception.NotStrictlyPositiveException;
+import org.apache.commons.math3.exception.NumberIsTooSmallException;
+import org.apache.commons.math3.exception.TooManyEvaluationsException;
+import org.apache.commons.math3.exception.util.LocalizedFormats;
+import org.apache.commons.math3.util.FastMath;
+
+/**
+ * Implements the <a href="http://mathworld.wolfram.com/Legendre-GaussQuadrature.html">
+ * Legendre-Gauss</a> quadrature formula.
+ * <p>
+ * Legendre-Gauss integrators are efficient integrators that can
+ * accurately integrate functions with few function evaluations. A
+ * Legendre-Gauss integrator using an n-points quadrature formula can
+ * integrate 2n-1 degree polynomials exactly.
+ * </p>
+ * <p>
+ * These integrators evaluate the function on n carefully chosen
+ * abscissas in each step interval (mapped to the canonical [-1,1] interval).
+ * The evaluation abscissas are not evenly spaced and none of them are
+ * at the interval endpoints. This implies the function integrated can be
+ * undefined at integration interval endpoints.
+ * </p>
+ * <p>
+ * The evaluation abscissas x<sub>i</sub> are the roots of the degree n
+ * Legendre polynomial. The weights a<sub>i</sub> of the quadrature formula
+ * integrals from -1 to +1 &int; Li<sup>2</sup> where Li (x) =
+ * &prod; (x-x<sub>k</sub>)/(x<sub>i</sub>-x<sub>k</sub>) for k != i.
+ * </p>
+ * <p>
+ * @since 1.2
+ * @deprecated As of 3.1 (to be removed in 4.0). Please use
+ * {@link IterativeLegendreGaussIntegrator} instead.
+ */
+@Deprecated
+public class LegendreGaussIntegrator extends BaseAbstractUnivariateIntegrator {
+
+ /** Abscissas for the 2 points method. */
+ private static final double[] ABSCISSAS_2 = {
+ -1.0 / FastMath.sqrt(3.0),
+ 1.0 / FastMath.sqrt(3.0)
+ };
+
+ /** Weights for the 2 points method. */
+ private static final double[] WEIGHTS_2 = {
+ 1.0,
+ 1.0
+ };
+
+ /** Abscissas for the 3 points method. */
+ private static final double[] ABSCISSAS_3 = {
+ -FastMath.sqrt(0.6),
+ 0.0,
+ FastMath.sqrt(0.6)
+ };
+
+ /** Weights for the 3 points method. */
+ private static final double[] WEIGHTS_3 = {
+ 5.0 / 9.0,
+ 8.0 / 9.0,
+ 5.0 / 9.0
+ };
+
+ /** Abscissas for the 4 points method. */
+ private static final double[] ABSCISSAS_4 = {
+ -FastMath.sqrt((15.0 + 2.0 * FastMath.sqrt(30.0)) / 35.0),
+ -FastMath.sqrt((15.0 - 2.0 * FastMath.sqrt(30.0)) / 35.0),
+ FastMath.sqrt((15.0 - 2.0 * FastMath.sqrt(30.0)) / 35.0),
+ FastMath.sqrt((15.0 + 2.0 * FastMath.sqrt(30.0)) / 35.0)
+ };
+
+ /** Weights for the 4 points method. */
+ private static final double[] WEIGHTS_4 = {
+ (90.0 - 5.0 * FastMath.sqrt(30.0)) / 180.0,
+ (90.0 + 5.0 * FastMath.sqrt(30.0)) / 180.0,
+ (90.0 + 5.0 * FastMath.sqrt(30.0)) / 180.0,
+ (90.0 - 5.0 * FastMath.sqrt(30.0)) / 180.0
+ };
+
+ /** Abscissas for the 5 points method. */
+ private static final double[] ABSCISSAS_5 = {
+ -FastMath.sqrt((35.0 + 2.0 * FastMath.sqrt(70.0)) / 63.0),
+ -FastMath.sqrt((35.0 - 2.0 * FastMath.sqrt(70.0)) / 63.0),
+ 0.0,
+ FastMath.sqrt((35.0 - 2.0 * FastMath.sqrt(70.0)) / 63.0),
+ FastMath.sqrt((35.0 + 2.0 * FastMath.sqrt(70.0)) / 63.0)
+ };
+
+ /** Weights for the 5 points method. */
+ private static final double[] WEIGHTS_5 = {
+ (322.0 - 13.0 * FastMath.sqrt(70.0)) / 900.0,
+ (322.0 + 13.0 * FastMath.sqrt(70.0)) / 900.0,
+ 128.0 / 225.0,
+ (322.0 + 13.0 * FastMath.sqrt(70.0)) / 900.0,
+ (322.0 - 13.0 * FastMath.sqrt(70.0)) / 900.0
+ };
+
+ /** Abscissas for the current method. */
+ private final double[] abscissas;
+
+ /** Weights for the current method. */
+ private final double[] weights;
+
+ /**
+ * Build a Legendre-Gauss integrator with given accuracies and iterations counts.
+ * @param n number of points desired (must be between 2 and 5 inclusive)
+ * @param relativeAccuracy relative accuracy of the result
+ * @param absoluteAccuracy absolute accuracy of the result
+ * @param minimalIterationCount minimum number of iterations
+ * @param maximalIterationCount maximum number of iterations
+ * @exception MathIllegalArgumentException if number of points is out of [2; 5]
+ * @exception NotStrictlyPositiveException if minimal number of iterations
+ * is not strictly positive
+ * @exception NumberIsTooSmallException if maximal number of iterations
+ * is lesser than or equal to the minimal number of iterations
+ */
+ public LegendreGaussIntegrator(final int n,
+ final double relativeAccuracy,
+ final double absoluteAccuracy,
+ final int minimalIterationCount,
+ final int maximalIterationCount)
+ throws MathIllegalArgumentException, NotStrictlyPositiveException, NumberIsTooSmallException {
+ super(relativeAccuracy, absoluteAccuracy, minimalIterationCount, maximalIterationCount);
+ switch(n) {
+ case 2 :
+ abscissas = ABSCISSAS_2;
+ weights = WEIGHTS_2;
+ break;
+ case 3 :
+ abscissas = ABSCISSAS_3;
+ weights = WEIGHTS_3;
+ break;
+ case 4 :
+ abscissas = ABSCISSAS_4;
+ weights = WEIGHTS_4;
+ break;
+ case 5 :
+ abscissas = ABSCISSAS_5;
+ weights = WEIGHTS_5;
+ break;
+ default :
+ throw new MathIllegalArgumentException(
+ LocalizedFormats.N_POINTS_GAUSS_LEGENDRE_INTEGRATOR_NOT_SUPPORTED,
+ n, 2, 5);
+ }
+
+ }
+
+ /**
+ * Build a Legendre-Gauss integrator with given accuracies.
+ * @param n number of points desired (must be between 2 and 5 inclusive)
+ * @param relativeAccuracy relative accuracy of the result
+ * @param absoluteAccuracy absolute accuracy of the result
+ * @exception MathIllegalArgumentException if number of points is out of [2; 5]
+ */
+ public LegendreGaussIntegrator(final int n,
+ final double relativeAccuracy,
+ final double absoluteAccuracy)
+ throws MathIllegalArgumentException {
+ this(n, relativeAccuracy, absoluteAccuracy,
+ DEFAULT_MIN_ITERATIONS_COUNT, DEFAULT_MAX_ITERATIONS_COUNT);
+ }
+
+ /**
+ * Build a Legendre-Gauss integrator with given iteration counts.
+ * @param n number of points desired (must be between 2 and 5 inclusive)
+ * @param minimalIterationCount minimum number of iterations
+ * @param maximalIterationCount maximum number of iterations
+ * @exception MathIllegalArgumentException if number of points is out of [2; 5]
+ * @exception NotStrictlyPositiveException if minimal number of iterations
+ * is not strictly positive
+ * @exception NumberIsTooSmallException if maximal number of iterations
+ * is lesser than or equal to the minimal number of iterations
+ */
+ public LegendreGaussIntegrator(final int n,
+ final int minimalIterationCount,
+ final int maximalIterationCount)
+ throws MathIllegalArgumentException {
+ this(n, DEFAULT_RELATIVE_ACCURACY, DEFAULT_ABSOLUTE_ACCURACY,
+ minimalIterationCount, maximalIterationCount);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ protected double doIntegrate()
+ throws MathIllegalArgumentException, TooManyEvaluationsException, MaxCountExceededException {
+
+ // compute first estimate with a single step
+ double oldt = stage(1);
+
+ int n = 2;
+ while (true) {
+
+ // improve integral with a larger number of steps
+ final double t = stage(n);
+
+ // estimate error
+ final double delta = FastMath.abs(t - oldt);
+ final double limit =
+ FastMath.max(getAbsoluteAccuracy(),
+ getRelativeAccuracy() * (FastMath.abs(oldt) + FastMath.abs(t)) * 0.5);
+
+ // check convergence
+ if ((getIterations() + 1 >= getMinimalIterationCount()) && (delta <= limit)) {
+ return t;
+ }
+
+ // prepare next iteration
+ double ratio = FastMath.min(4, FastMath.pow(delta / limit, 0.5 / abscissas.length));
+ n = FastMath.max((int) (ratio * n), n + 1);
+ oldt = t;
+ incrementCount();
+
+ }
+
+ }
+
+ /**
+ * Compute the n-th stage integral.
+ * @param n number of steps
+ * @return the value of n-th stage integral
+ * @throws TooManyEvaluationsException if the maximum number of evaluations
+ * is exceeded.
+ */
+ private double stage(final int n)
+ throws TooManyEvaluationsException {
+
+ // set up the step for the current stage
+ final double step = (getMax() - getMin()) / n;
+ final double halfStep = step / 2.0;
+
+ // integrate over all elementary steps
+ double midPoint = getMin() + halfStep;
+ double sum = 0.0;
+ for (int i = 0; i < n; ++i) {
+ for (int j = 0; j < abscissas.length; ++j) {
+ sum += weights[j] * computeObjectiveValue(midPoint + halfStep * abscissas[j]);
+ }
+ midPoint += step;
+ }
+
+ return halfStep * sum;
+
+ }
+
+}
diff --git a/src/main/java/org/apache/commons/math3/analysis/integration/MidPointIntegrator.java b/src/main/java/org/apache/commons/math3/analysis/integration/MidPointIntegrator.java
new file mode 100644
index 0000000..766a917
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/analysis/integration/MidPointIntegrator.java
@@ -0,0 +1,169 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math3.analysis.integration;
+
+import org.apache.commons.math3.exception.MathIllegalArgumentException;
+import org.apache.commons.math3.exception.MaxCountExceededException;
+import org.apache.commons.math3.exception.NotStrictlyPositiveException;
+import org.apache.commons.math3.exception.NumberIsTooLargeException;
+import org.apache.commons.math3.exception.NumberIsTooSmallException;
+import org.apache.commons.math3.exception.TooManyEvaluationsException;
+import org.apache.commons.math3.util.FastMath;
+
+/**
+ * Implements the <a href="http://en.wikipedia.org/wiki/Midpoint_method">
+ * Midpoint Rule</a> for integration of real univariate functions. For
+ * reference, see <b>Numerical Mathematics</b>, ISBN 0387989595,
+ * chapter 9.2.
+ * <p>
+ * The function should be integrable.</p>
+ *
+ * @since 3.3
+ */
+public class MidPointIntegrator extends BaseAbstractUnivariateIntegrator {
+
+ /** Maximum number of iterations for midpoint. */
+ public static final int MIDPOINT_MAX_ITERATIONS_COUNT = 64;
+
+ /**
+ * Build a midpoint integrator with given accuracies and iterations counts.
+ * @param relativeAccuracy relative accuracy of the result
+ * @param absoluteAccuracy absolute accuracy of the result
+ * @param minimalIterationCount minimum number of iterations
+ * @param maximalIterationCount maximum number of iterations
+ * (must be less than or equal to {@link #MIDPOINT_MAX_ITERATIONS_COUNT}
+ * @exception NotStrictlyPositiveException if minimal number of iterations
+ * is not strictly positive
+ * @exception NumberIsTooSmallException if maximal number of iterations
+ * is lesser than or equal to the minimal number of iterations
+ * @exception NumberIsTooLargeException if maximal number of iterations
+ * is greater than {@link #MIDPOINT_MAX_ITERATIONS_COUNT}
+ */
+ public MidPointIntegrator(final double relativeAccuracy,
+ final double absoluteAccuracy,
+ final int minimalIterationCount,
+ final int maximalIterationCount)
+ throws NotStrictlyPositiveException, NumberIsTooSmallException, NumberIsTooLargeException {
+ super(relativeAccuracy, absoluteAccuracy, minimalIterationCount, maximalIterationCount);
+ if (maximalIterationCount > MIDPOINT_MAX_ITERATIONS_COUNT) {
+ throw new NumberIsTooLargeException(maximalIterationCount,
+ MIDPOINT_MAX_ITERATIONS_COUNT, false);
+ }
+ }
+
+ /**
+ * Build a midpoint integrator with given iteration counts.
+ * @param minimalIterationCount minimum number of iterations
+ * @param maximalIterationCount maximum number of iterations
+ * (must be less than or equal to {@link #MIDPOINT_MAX_ITERATIONS_COUNT}
+ * @exception NotStrictlyPositiveException if minimal number of iterations
+ * is not strictly positive
+ * @exception NumberIsTooSmallException if maximal number of iterations
+ * is lesser than or equal to the minimal number of iterations
+ * @exception NumberIsTooLargeException if maximal number of iterations
+ * is greater than {@link #MIDPOINT_MAX_ITERATIONS_COUNT}
+ */
+ public MidPointIntegrator(final int minimalIterationCount,
+ final int maximalIterationCount)
+ throws NotStrictlyPositiveException, NumberIsTooSmallException, NumberIsTooLargeException {
+ super(minimalIterationCount, maximalIterationCount);
+ if (maximalIterationCount > MIDPOINT_MAX_ITERATIONS_COUNT) {
+ throw new NumberIsTooLargeException(maximalIterationCount,
+ MIDPOINT_MAX_ITERATIONS_COUNT, false);
+ }
+ }
+
+ /**
+ * Construct a midpoint integrator with default settings.
+ * (max iteration count set to {@link #MIDPOINT_MAX_ITERATIONS_COUNT})
+ */
+ public MidPointIntegrator() {
+ super(DEFAULT_MIN_ITERATIONS_COUNT, MIDPOINT_MAX_ITERATIONS_COUNT);
+ }
+
+ /**
+ * Compute the n-th stage integral of midpoint rule.
+ * This function should only be called by API <code>integrate()</code> in the package.
+ * To save time it does not verify arguments - caller does.
+ * <p>
+ * The interval is divided equally into 2^n sections rather than an
+ * arbitrary m sections because this configuration can best utilize the
+ * already computed values.</p>
+ *
+ * @param n the stage of 1/2 refinement. Must be larger than 0.
+ * @param previousStageResult Result from the previous call to the
+ * {@code stage} method.
+ * @param min Lower bound of the integration interval.
+ * @param diffMaxMin Difference between the lower bound and upper bound
+ * of the integration interval.
+ * @return the value of n-th stage integral
+ * @throws TooManyEvaluationsException if the maximal number of evaluations
+ * is exceeded.
+ */
+ private double stage(final int n,
+ double previousStageResult,
+ double min,
+ double diffMaxMin)
+ throws TooManyEvaluationsException {
+
+ // number of new points in this stage
+ final long np = 1L << (n - 1);
+ double sum = 0;
+
+ // spacing between adjacent new points
+ final double spacing = diffMaxMin / np;
+
+ // the first new point
+ double x = min + 0.5 * spacing;
+ for (long i = 0; i < np; i++) {
+ sum += computeObjectiveValue(x);
+ x += spacing;
+ }
+ // add the new sum to previously calculated result
+ return 0.5 * (previousStageResult + sum * spacing);
+ }
+
+
+ /** {@inheritDoc} */
+ @Override
+ protected double doIntegrate()
+ throws MathIllegalArgumentException, TooManyEvaluationsException, MaxCountExceededException {
+
+ final double min = getMin();
+ final double diff = getMax() - min;
+ final double midPoint = min + 0.5 * diff;
+
+ double oldt = diff * computeObjectiveValue(midPoint);
+
+ while (true) {
+ incrementCount();
+ final int i = getIterations();
+ final double t = stage(i, oldt, min, diff);
+ if (i >= getMinimalIterationCount()) {
+ final double delta = FastMath.abs(t - oldt);
+ final double rLimit =
+ getRelativeAccuracy() * (FastMath.abs(oldt) + FastMath.abs(t)) * 0.5;
+ if ((delta <= rLimit) || (delta <= getAbsoluteAccuracy())) {
+ return t;
+ }
+ }
+ oldt = t;
+ }
+
+ }
+
+}
diff --git a/src/main/java/org/apache/commons/math3/analysis/integration/RombergIntegrator.java b/src/main/java/org/apache/commons/math3/analysis/integration/RombergIntegrator.java
new file mode 100644
index 0000000..125d251
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/analysis/integration/RombergIntegrator.java
@@ -0,0 +1,142 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math3.analysis.integration;
+
+import org.apache.commons.math3.exception.MaxCountExceededException;
+import org.apache.commons.math3.exception.NotStrictlyPositiveException;
+import org.apache.commons.math3.exception.NumberIsTooLargeException;
+import org.apache.commons.math3.exception.NumberIsTooSmallException;
+import org.apache.commons.math3.exception.TooManyEvaluationsException;
+import org.apache.commons.math3.util.FastMath;
+
+/**
+ * Implements the <a href="http://mathworld.wolfram.com/RombergIntegration.html">
+ * Romberg Algorithm</a> for integration of real univariate functions. For
+ * reference, see <b>Introduction to Numerical Analysis</b>, ISBN 038795452X,
+ * chapter 3.
+ * <p>
+ * Romberg integration employs k successive refinements of the trapezoid
+ * rule to remove error terms less than order O(N^(-2k)). Simpson's rule
+ * is a special case of k = 2.</p>
+ *
+ * @since 1.2
+ */
+public class RombergIntegrator extends BaseAbstractUnivariateIntegrator {
+
+ /** Maximal number of iterations for Romberg. */
+ public static final int ROMBERG_MAX_ITERATIONS_COUNT = 32;
+
+ /**
+ * Build a Romberg integrator with given accuracies and iterations counts.
+ * @param relativeAccuracy relative accuracy of the result
+ * @param absoluteAccuracy absolute accuracy of the result
+ * @param minimalIterationCount minimum number of iterations
+ * @param maximalIterationCount maximum number of iterations
+ * (must be less than or equal to {@link #ROMBERG_MAX_ITERATIONS_COUNT})
+ * @exception NotStrictlyPositiveException if minimal number of iterations
+ * is not strictly positive
+ * @exception NumberIsTooSmallException if maximal number of iterations
+ * is lesser than or equal to the minimal number of iterations
+ * @exception NumberIsTooLargeException if maximal number of iterations
+ * is greater than {@link #ROMBERG_MAX_ITERATIONS_COUNT}
+ */
+ public RombergIntegrator(final double relativeAccuracy,
+ final double absoluteAccuracy,
+ final int minimalIterationCount,
+ final int maximalIterationCount)
+ throws NotStrictlyPositiveException, NumberIsTooSmallException, NumberIsTooLargeException {
+ super(relativeAccuracy, absoluteAccuracy, minimalIterationCount, maximalIterationCount);
+ if (maximalIterationCount > ROMBERG_MAX_ITERATIONS_COUNT) {
+ throw new NumberIsTooLargeException(maximalIterationCount,
+ ROMBERG_MAX_ITERATIONS_COUNT, false);
+ }
+ }
+
+ /**
+ * Build a Romberg integrator with given iteration counts.
+ * @param minimalIterationCount minimum number of iterations
+ * @param maximalIterationCount maximum number of iterations
+ * (must be less than or equal to {@link #ROMBERG_MAX_ITERATIONS_COUNT})
+ * @exception NotStrictlyPositiveException if minimal number of iterations
+ * is not strictly positive
+ * @exception NumberIsTooSmallException if maximal number of iterations
+ * is lesser than or equal to the minimal number of iterations
+ * @exception NumberIsTooLargeException if maximal number of iterations
+ * is greater than {@link #ROMBERG_MAX_ITERATIONS_COUNT}
+ */
+ public RombergIntegrator(final int minimalIterationCount,
+ final int maximalIterationCount)
+ throws NotStrictlyPositiveException, NumberIsTooSmallException, NumberIsTooLargeException {
+ super(minimalIterationCount, maximalIterationCount);
+ if (maximalIterationCount > ROMBERG_MAX_ITERATIONS_COUNT) {
+ throw new NumberIsTooLargeException(maximalIterationCount,
+ ROMBERG_MAX_ITERATIONS_COUNT, false);
+ }
+ }
+
+ /**
+ * Construct a Romberg integrator with default settings
+ * (max iteration count set to {@link #ROMBERG_MAX_ITERATIONS_COUNT})
+ */
+ public RombergIntegrator() {
+ super(DEFAULT_MIN_ITERATIONS_COUNT, ROMBERG_MAX_ITERATIONS_COUNT);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ protected double doIntegrate()
+ throws TooManyEvaluationsException, MaxCountExceededException {
+
+ final int m = getMaximalIterationCount() + 1;
+ double previousRow[] = new double[m];
+ double currentRow[] = new double[m];
+
+ TrapezoidIntegrator qtrap = new TrapezoidIntegrator();
+ currentRow[0] = qtrap.stage(this, 0);
+ incrementCount();
+ double olds = currentRow[0];
+ while (true) {
+
+ final int i = getIterations();
+
+ // switch rows
+ final double[] tmpRow = previousRow;
+ previousRow = currentRow;
+ currentRow = tmpRow;
+
+ currentRow[0] = qtrap.stage(this, i);
+ incrementCount();
+ for (int j = 1; j <= i; j++) {
+ // Richardson extrapolation coefficient
+ final double r = (1L << (2 * j)) - 1;
+ final double tIJm1 = currentRow[j - 1];
+ currentRow[j] = tIJm1 + (tIJm1 - previousRow[j - 1]) / r;
+ }
+ final double s = currentRow[i];
+ if (i >= getMinimalIterationCount()) {
+ final double delta = FastMath.abs(s - olds);
+ final double rLimit = getRelativeAccuracy() * (FastMath.abs(olds) + FastMath.abs(s)) * 0.5;
+ if ((delta <= rLimit) || (delta <= getAbsoluteAccuracy())) {
+ return s;
+ }
+ }
+ olds = s;
+ }
+
+ }
+
+}
diff --git a/src/main/java/org/apache/commons/math3/analysis/integration/SimpsonIntegrator.java b/src/main/java/org/apache/commons/math3/analysis/integration/SimpsonIntegrator.java
new file mode 100644
index 0000000..527bb82
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/analysis/integration/SimpsonIntegrator.java
@@ -0,0 +1,129 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math3.analysis.integration;
+
+import org.apache.commons.math3.exception.MaxCountExceededException;
+import org.apache.commons.math3.exception.NotStrictlyPositiveException;
+import org.apache.commons.math3.exception.NumberIsTooLargeException;
+import org.apache.commons.math3.exception.NumberIsTooSmallException;
+import org.apache.commons.math3.exception.TooManyEvaluationsException;
+import org.apache.commons.math3.util.FastMath;
+
+/**
+ * Implements <a href="http://mathworld.wolfram.com/SimpsonsRule.html">
+ * Simpson's Rule</a> for integration of real univariate functions. For
+ * reference, see <b>Introduction to Numerical Analysis</b>, ISBN 038795452X,
+ * chapter 3.
+ * <p>
+ * This implementation employs the basic trapezoid rule to calculate Simpson's
+ * rule.</p>
+ *
+ * @since 1.2
+ */
+public class SimpsonIntegrator extends BaseAbstractUnivariateIntegrator {
+
+ /** Maximal number of iterations for Simpson. */
+ public static final int SIMPSON_MAX_ITERATIONS_COUNT = 64;
+
+ /**
+ * Build a Simpson integrator with given accuracies and iterations counts.
+ * @param relativeAccuracy relative accuracy of the result
+ * @param absoluteAccuracy absolute accuracy of the result
+ * @param minimalIterationCount minimum number of iterations
+ * @param maximalIterationCount maximum number of iterations
+ * (must be less than or equal to {@link #SIMPSON_MAX_ITERATIONS_COUNT})
+ * @exception NotStrictlyPositiveException if minimal number of iterations
+ * is not strictly positive
+ * @exception NumberIsTooSmallException if maximal number of iterations
+ * is lesser than or equal to the minimal number of iterations
+ * @exception NumberIsTooLargeException if maximal number of iterations
+ * is greater than {@link #SIMPSON_MAX_ITERATIONS_COUNT}
+ */
+ public SimpsonIntegrator(final double relativeAccuracy,
+ final double absoluteAccuracy,
+ final int minimalIterationCount,
+ final int maximalIterationCount)
+ throws NotStrictlyPositiveException, NumberIsTooSmallException, NumberIsTooLargeException {
+ super(relativeAccuracy, absoluteAccuracy, minimalIterationCount, maximalIterationCount);
+ if (maximalIterationCount > SIMPSON_MAX_ITERATIONS_COUNT) {
+ throw new NumberIsTooLargeException(maximalIterationCount,
+ SIMPSON_MAX_ITERATIONS_COUNT, false);
+ }
+ }
+
+ /**
+ * Build a Simpson integrator with given iteration counts.
+ * @param minimalIterationCount minimum number of iterations
+ * @param maximalIterationCount maximum number of iterations
+ * (must be less than or equal to {@link #SIMPSON_MAX_ITERATIONS_COUNT})
+ * @exception NotStrictlyPositiveException if minimal number of iterations
+ * is not strictly positive
+ * @exception NumberIsTooSmallException if maximal number of iterations
+ * is lesser than or equal to the minimal number of iterations
+ * @exception NumberIsTooLargeException if maximal number of iterations
+ * is greater than {@link #SIMPSON_MAX_ITERATIONS_COUNT}
+ */
+ public SimpsonIntegrator(final int minimalIterationCount,
+ final int maximalIterationCount)
+ throws NotStrictlyPositiveException, NumberIsTooSmallException, NumberIsTooLargeException {
+ super(minimalIterationCount, maximalIterationCount);
+ if (maximalIterationCount > SIMPSON_MAX_ITERATIONS_COUNT) {
+ throw new NumberIsTooLargeException(maximalIterationCount,
+ SIMPSON_MAX_ITERATIONS_COUNT, false);
+ }
+ }
+
+ /**
+ * Construct an integrator with default settings.
+ * (max iteration count set to {@link #SIMPSON_MAX_ITERATIONS_COUNT})
+ */
+ public SimpsonIntegrator() {
+ super(DEFAULT_MIN_ITERATIONS_COUNT, SIMPSON_MAX_ITERATIONS_COUNT);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ protected double doIntegrate()
+ throws TooManyEvaluationsException, MaxCountExceededException {
+
+ TrapezoidIntegrator qtrap = new TrapezoidIntegrator();
+ if (getMinimalIterationCount() == 1) {
+ return (4 * qtrap.stage(this, 1) - qtrap.stage(this, 0)) / 3.0;
+ }
+
+ // Simpson's rule requires at least two trapezoid stages.
+ double olds = 0;
+ double oldt = qtrap.stage(this, 0);
+ while (true) {
+ final double t = qtrap.stage(this, getIterations());
+ incrementCount();
+ final double s = (4 * t - oldt) / 3.0;
+ if (getIterations() >= getMinimalIterationCount()) {
+ final double delta = FastMath.abs(s - olds);
+ final double rLimit =
+ getRelativeAccuracy() * (FastMath.abs(olds) + FastMath.abs(s)) * 0.5;
+ if ((delta <= rLimit) || (delta <= getAbsoluteAccuracy())) {
+ return s;
+ }
+ }
+ olds = s;
+ oldt = t;
+ }
+
+ }
+
+}
diff --git a/src/main/java/org/apache/commons/math3/analysis/integration/TrapezoidIntegrator.java b/src/main/java/org/apache/commons/math3/analysis/integration/TrapezoidIntegrator.java
new file mode 100644
index 0000000..8a737f1
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/analysis/integration/TrapezoidIntegrator.java
@@ -0,0 +1,168 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math3.analysis.integration;
+
+import org.apache.commons.math3.exception.MathIllegalArgumentException;
+import org.apache.commons.math3.exception.MaxCountExceededException;
+import org.apache.commons.math3.exception.NotStrictlyPositiveException;
+import org.apache.commons.math3.exception.NumberIsTooLargeException;
+import org.apache.commons.math3.exception.NumberIsTooSmallException;
+import org.apache.commons.math3.exception.TooManyEvaluationsException;
+import org.apache.commons.math3.util.FastMath;
+
+/**
+ * Implements the <a href="http://mathworld.wolfram.com/TrapezoidalRule.html">
+ * Trapezoid Rule</a> for integration of real univariate functions. For
+ * reference, see <b>Introduction to Numerical Analysis</b>, ISBN 038795452X,
+ * chapter 3.
+ * <p>
+ * The function should be integrable.</p>
+ *
+ * @since 1.2
+ */
+public class TrapezoidIntegrator extends BaseAbstractUnivariateIntegrator {
+
+ /** Maximum number of iterations for trapezoid. */
+ public static final int TRAPEZOID_MAX_ITERATIONS_COUNT = 64;
+
+ /** Intermediate result. */
+ private double s;
+
+ /**
+ * Build a trapezoid integrator with given accuracies and iterations counts.
+ * @param relativeAccuracy relative accuracy of the result
+ * @param absoluteAccuracy absolute accuracy of the result
+ * @param minimalIterationCount minimum number of iterations
+ * @param maximalIterationCount maximum number of iterations
+ * (must be less than or equal to {@link #TRAPEZOID_MAX_ITERATIONS_COUNT}
+ * @exception NotStrictlyPositiveException if minimal number of iterations
+ * is not strictly positive
+ * @exception NumberIsTooSmallException if maximal number of iterations
+ * is lesser than or equal to the minimal number of iterations
+ * @exception NumberIsTooLargeException if maximal number of iterations
+ * is greater than {@link #TRAPEZOID_MAX_ITERATIONS_COUNT}
+ */
+ public TrapezoidIntegrator(final double relativeAccuracy,
+ final double absoluteAccuracy,
+ final int minimalIterationCount,
+ final int maximalIterationCount)
+ throws NotStrictlyPositiveException, NumberIsTooSmallException, NumberIsTooLargeException {
+ super(relativeAccuracy, absoluteAccuracy, minimalIterationCount, maximalIterationCount);
+ if (maximalIterationCount > TRAPEZOID_MAX_ITERATIONS_COUNT) {
+ throw new NumberIsTooLargeException(maximalIterationCount,
+ TRAPEZOID_MAX_ITERATIONS_COUNT, false);
+ }
+ }
+
+ /**
+ * Build a trapezoid integrator with given iteration counts.
+ * @param minimalIterationCount minimum number of iterations
+ * @param maximalIterationCount maximum number of iterations
+ * (must be less than or equal to {@link #TRAPEZOID_MAX_ITERATIONS_COUNT}
+ * @exception NotStrictlyPositiveException if minimal number of iterations
+ * is not strictly positive
+ * @exception NumberIsTooSmallException if maximal number of iterations
+ * is lesser than or equal to the minimal number of iterations
+ * @exception NumberIsTooLargeException if maximal number of iterations
+ * is greater than {@link #TRAPEZOID_MAX_ITERATIONS_COUNT}
+ */
+ public TrapezoidIntegrator(final int minimalIterationCount,
+ final int maximalIterationCount)
+ throws NotStrictlyPositiveException, NumberIsTooSmallException, NumberIsTooLargeException {
+ super(minimalIterationCount, maximalIterationCount);
+ if (maximalIterationCount > TRAPEZOID_MAX_ITERATIONS_COUNT) {
+ throw new NumberIsTooLargeException(maximalIterationCount,
+ TRAPEZOID_MAX_ITERATIONS_COUNT, false);
+ }
+ }
+
+ /**
+ * Construct a trapezoid integrator with default settings.
+ * (max iteration count set to {@link #TRAPEZOID_MAX_ITERATIONS_COUNT})
+ */
+ public TrapezoidIntegrator() {
+ super(DEFAULT_MIN_ITERATIONS_COUNT, TRAPEZOID_MAX_ITERATIONS_COUNT);
+ }
+
+ /**
+ * Compute the n-th stage integral of trapezoid rule. This function
+ * should only be called by API <code>integrate()</code> in the package.
+ * To save time it does not verify arguments - caller does.
+ * <p>
+ * The interval is divided equally into 2^n sections rather than an
+ * arbitrary m sections because this configuration can best utilize the
+ * already computed values.</p>
+ *
+ * @param baseIntegrator integrator holding integration parameters
+ * @param n the stage of 1/2 refinement, n = 0 is no refinement
+ * @return the value of n-th stage integral
+ * @throws TooManyEvaluationsException if the maximal number of evaluations
+ * is exceeded.
+ */
+ double stage(final BaseAbstractUnivariateIntegrator baseIntegrator, final int n)
+ throws TooManyEvaluationsException {
+
+ if (n == 0) {
+ final double max = baseIntegrator.getMax();
+ final double min = baseIntegrator.getMin();
+ s = 0.5 * (max - min) *
+ (baseIntegrator.computeObjectiveValue(min) +
+ baseIntegrator.computeObjectiveValue(max));
+ return s;
+ } else {
+ final long np = 1L << (n-1); // number of new points in this stage
+ double sum = 0;
+ final double max = baseIntegrator.getMax();
+ final double min = baseIntegrator.getMin();
+ // spacing between adjacent new points
+ final double spacing = (max - min) / np;
+ double x = min + 0.5 * spacing; // the first new point
+ for (long i = 0; i < np; i++) {
+ sum += baseIntegrator.computeObjectiveValue(x);
+ x += spacing;
+ }
+ // add the new sum to previously calculated result
+ s = 0.5 * (s + sum * spacing);
+ return s;
+ }
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ protected double doIntegrate()
+ throws MathIllegalArgumentException, TooManyEvaluationsException, MaxCountExceededException {
+
+ double oldt = stage(this, 0);
+ incrementCount();
+ while (true) {
+ final int i = getIterations();
+ final double t = stage(this, i);
+ if (i >= getMinimalIterationCount()) {
+ final double delta = FastMath.abs(t - oldt);
+ final double rLimit =
+ getRelativeAccuracy() * (FastMath.abs(oldt) + FastMath.abs(t)) * 0.5;
+ if ((delta <= rLimit) || (delta <= getAbsoluteAccuracy())) {
+ return t;
+ }
+ }
+ oldt = t;
+ incrementCount();
+ }
+
+ }
+
+}
diff --git a/src/main/java/org/apache/commons/math3/analysis/integration/UnivariateIntegrator.java b/src/main/java/org/apache/commons/math3/analysis/integration/UnivariateIntegrator.java
new file mode 100644
index 0000000..6453eba
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/analysis/integration/UnivariateIntegrator.java
@@ -0,0 +1,95 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math3.analysis.integration;
+
+import org.apache.commons.math3.analysis.UnivariateFunction;
+import org.apache.commons.math3.exception.MathIllegalArgumentException;
+import org.apache.commons.math3.exception.MaxCountExceededException;
+import org.apache.commons.math3.exception.NullArgumentException;
+import org.apache.commons.math3.exception.TooManyEvaluationsException;
+
+/**
+ * Interface for univariate real integration algorithms.
+ *
+ * @since 1.2
+ */
+public interface UnivariateIntegrator {
+
+ /**
+ * Get the relative accuracy.
+ *
+ * @return the accuracy
+ */
+ double getRelativeAccuracy();
+
+ /**
+ * Get the absolute accuracy.
+ *
+ * @return the accuracy
+ */
+ double getAbsoluteAccuracy();
+
+ /**
+ * Get the min limit for the number of iterations.
+ *
+ * @return the actual min limit
+ */
+ int getMinimalIterationCount();
+
+ /**
+ * Get the upper limit for the number of iterations.
+ *
+ * @return the actual upper limit
+ */
+ int getMaximalIterationCount();
+
+ /**
+ * Integrate the function in the given interval.
+ *
+ * @param maxEval Maximum number of evaluations.
+ * @param f the integrand function
+ * @param min the lower bound for the interval
+ * @param max the upper bound for the interval
+ * @return the value of integral
+ * @throws TooManyEvaluationsException if the maximum number of function
+ * evaluations is exceeded
+ * @throws MaxCountExceededException if the maximum iteration count is exceeded
+ * or the integrator detects convergence problems otherwise
+ * @throws MathIllegalArgumentException if {@code min > max} or the endpoints do not
+ * satisfy the requirements specified by the integrator
+ * @throws NullArgumentException if {@code f} is {@code null}.
+ */
+ double integrate(int maxEval, UnivariateFunction f, double min,
+ double max)
+ throws TooManyEvaluationsException, MaxCountExceededException,
+ MathIllegalArgumentException, NullArgumentException;
+
+ /**
+ * Get the number of function evaluations of the last run of the integrator.
+ *
+ * @return number of function evaluations
+ */
+ int getEvaluations();
+
+ /**
+ * Get the number of iterations of the last run of the integrator.
+ *
+ * @return number of iterations
+ */
+ int getIterations();
+
+}
diff --git a/src/main/java/org/apache/commons/math3/analysis/integration/gauss/BaseRuleFactory.java b/src/main/java/org/apache/commons/math3/analysis/integration/gauss/BaseRuleFactory.java
new file mode 100644
index 0000000..556fa0c
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/analysis/integration/gauss/BaseRuleFactory.java
@@ -0,0 +1,153 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math3.analysis.integration.gauss;
+
+import java.util.Map;
+import java.util.TreeMap;
+import org.apache.commons.math3.util.Pair;
+import org.apache.commons.math3.exception.DimensionMismatchException;
+import org.apache.commons.math3.exception.NotStrictlyPositiveException;
+import org.apache.commons.math3.exception.util.LocalizedFormats;
+
+/**
+ * Base class for rules that determines the integration nodes and their
+ * weights.
+ * Subclasses must implement the {@link #computeRule(int) computeRule} method.
+ *
+ * @param <T> Type of the number used to represent the points and weights of
+ * the quadrature rules.
+ *
+ * @since 3.1
+ */
+public abstract class BaseRuleFactory<T extends Number> {
+ /** List of points and weights, indexed by the order of the rule. */
+ private final Map<Integer, Pair<T[], T[]>> pointsAndWeights
+ = new TreeMap<Integer, Pair<T[], T[]>>();
+ /** Cache for double-precision rules. */
+ private final Map<Integer, Pair<double[], double[]>> pointsAndWeightsDouble
+ = new TreeMap<Integer, Pair<double[], double[]>>();
+
+ /**
+ * Gets a copy of the quadrature rule with the given number of integration
+ * points.
+ *
+ * @param numberOfPoints Number of integration points.
+ * @return a copy of the integration rule.
+ * @throws NotStrictlyPositiveException if {@code numberOfPoints < 1}.
+ * @throws DimensionMismatchException if the elements of the rule pair do not
+ * have the same length.
+ */
+ public Pair<double[], double[]> getRule(int numberOfPoints)
+ throws NotStrictlyPositiveException, DimensionMismatchException {
+
+ if (numberOfPoints <= 0) {
+ throw new NotStrictlyPositiveException(LocalizedFormats.NUMBER_OF_POINTS,
+ numberOfPoints);
+ }
+
+ // Try to obtain the rule from the cache.
+ Pair<double[], double[]> cached = pointsAndWeightsDouble.get(numberOfPoints);
+
+ if (cached == null) {
+ // Rule not computed yet.
+
+ // Compute the rule.
+ final Pair<T[], T[]> rule = getRuleInternal(numberOfPoints);
+ cached = convertToDouble(rule);
+
+ // Cache it.
+ pointsAndWeightsDouble.put(numberOfPoints, cached);
+ }
+
+ // Return a copy.
+ return new Pair<double[], double[]>(cached.getFirst().clone(),
+ cached.getSecond().clone());
+ }
+
+ /**
+ * Gets a rule.
+ * Synchronization ensures that rules will be computed and added to the
+ * cache at most once.
+ * The returned rule is a reference into the cache.
+ *
+ * @param numberOfPoints Order of the rule to be retrieved.
+ * @return the points and weights corresponding to the given order.
+ * @throws DimensionMismatchException if the elements of the rule pair do not
+ * have the same length.
+ */
+ protected synchronized Pair<T[], T[]> getRuleInternal(int numberOfPoints)
+ throws DimensionMismatchException {
+ final Pair<T[], T[]> rule = pointsAndWeights.get(numberOfPoints);
+ if (rule == null) {
+ addRule(computeRule(numberOfPoints));
+ // The rule should be available now.
+ return getRuleInternal(numberOfPoints);
+ }
+ return rule;
+ }
+
+ /**
+ * Stores a rule.
+ *
+ * @param rule Rule to be stored.
+ * @throws DimensionMismatchException if the elements of the pair do not
+ * have the same length.
+ */
+ protected void addRule(Pair<T[], T[]> rule) throws DimensionMismatchException {
+ if (rule.getFirst().length != rule.getSecond().length) {
+ throw new DimensionMismatchException(rule.getFirst().length,
+ rule.getSecond().length);
+ }
+
+ pointsAndWeights.put(rule.getFirst().length, rule);
+ }
+
+ /**
+ * Computes the rule for the given order.
+ *
+ * @param numberOfPoints Order of the rule to be computed.
+ * @return the computed rule.
+ * @throws DimensionMismatchException if the elements of the pair do not
+ * have the same length.
+ */
+ protected abstract Pair<T[], T[]> computeRule(int numberOfPoints)
+ throws DimensionMismatchException;
+
+ /**
+ * Converts the from the actual {@code Number} type to {@code double}
+ *
+ * @param <T> Type of the number used to represent the points and
+ * weights of the quadrature rules.
+ * @param rule Points and weights.
+ * @return points and weights as {@code double}s.
+ */
+ private static <T extends Number> Pair<double[], double[]> convertToDouble(Pair<T[], T[]> rule) {
+ final T[] pT = rule.getFirst();
+ final T[] wT = rule.getSecond();
+
+ final int len = pT.length;
+ final double[] pD = new double[len];
+ final double[] wD = new double[len];
+
+ for (int i = 0; i < len; i++) {
+ pD[i] = pT[i].doubleValue();
+ wD[i] = wT[i].doubleValue();
+ }
+
+ return new Pair<double[], double[]>(pD, wD);
+ }
+}
diff --git a/src/main/java/org/apache/commons/math3/analysis/integration/gauss/GaussIntegrator.java b/src/main/java/org/apache/commons/math3/analysis/integration/gauss/GaussIntegrator.java
new file mode 100644
index 0000000..5c7b37f
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/analysis/integration/gauss/GaussIntegrator.java
@@ -0,0 +1,129 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math3.analysis.integration.gauss;
+
+import org.apache.commons.math3.analysis.UnivariateFunction;
+import org.apache.commons.math3.exception.DimensionMismatchException;
+import org.apache.commons.math3.exception.NonMonotonicSequenceException;
+import org.apache.commons.math3.util.MathArrays;
+import org.apache.commons.math3.util.Pair;
+
+/**
+ * Class that implements the Gaussian rule for
+ * {@link #integrate(UnivariateFunction) integrating} a weighted
+ * function.
+ *
+ * @since 3.1
+ */
+public class GaussIntegrator {
+ /** Nodes. */
+ private final double[] points;
+ /** Nodes weights. */
+ private final double[] weights;
+
+ /**
+ * Creates an integrator from the given {@code points} and {@code weights}.
+ * The integration interval is defined by the first and last value of
+ * {@code points} which must be sorted in increasing order.
+ *
+ * @param points Integration points.
+ * @param weights Weights of the corresponding integration nodes.
+ * @throws NonMonotonicSequenceException if the {@code points} are not
+ * sorted in increasing order.
+ * @throws DimensionMismatchException if points and weights don't have the same length
+ */
+ public GaussIntegrator(double[] points,
+ double[] weights)
+ throws NonMonotonicSequenceException, DimensionMismatchException {
+ if (points.length != weights.length) {
+ throw new DimensionMismatchException(points.length,
+ weights.length);
+ }
+
+ MathArrays.checkOrder(points, MathArrays.OrderDirection.INCREASING, true, true);
+
+ this.points = points.clone();
+ this.weights = weights.clone();
+ }
+
+ /**
+ * Creates an integrator from the given pair of points (first element of
+ * the pair) and weights (second element of the pair.
+ *
+ * @param pointsAndWeights Integration points and corresponding weights.
+ * @throws NonMonotonicSequenceException if the {@code points} are not
+ * sorted in increasing order.
+ *
+ * @see #GaussIntegrator(double[], double[])
+ */
+ public GaussIntegrator(Pair<double[], double[]> pointsAndWeights)
+ throws NonMonotonicSequenceException {
+ this(pointsAndWeights.getFirst(), pointsAndWeights.getSecond());
+ }
+
+ /**
+ * Returns an estimate of the integral of {@code f(x) * w(x)},
+ * where {@code w} is a weight function that depends on the actual
+ * flavor of the Gauss integration scheme.
+ * The algorithm uses the points and associated weights, as passed
+ * to the {@link #GaussIntegrator(double[],double[]) constructor}.
+ *
+ * @param f Function to integrate.
+ * @return the integral of the weighted function.
+ */
+ public double integrate(UnivariateFunction f) {
+ double s = 0;
+ double c = 0;
+ for (int i = 0; i < points.length; i++) {
+ final double x = points[i];
+ final double w = weights[i];
+ final double y = w * f.value(x) - c;
+ final double t = s + y;
+ c = (t - s) - y;
+ s = t;
+ }
+ return s;
+ }
+
+ /**
+ * @return the order of the integration rule (the number of integration
+ * points).
+ */
+ public int getNumberOfPoints() {
+ return points.length;
+ }
+
+ /**
+ * Gets the integration point at the given index.
+ * The index must be in the valid range but no check is performed.
+ * @param index index of the integration point
+ * @return the integration point.
+ */
+ public double getPoint(int index) {
+ return points[index];
+ }
+
+ /**
+ * Gets the weight of the integration point at the given index.
+ * The index must be in the valid range but no check is performed.
+ * @param index index of the integration point
+ * @return the weight.
+ */
+ public double getWeight(int index) {
+ return weights[index];
+ }
+}
diff --git a/src/main/java/org/apache/commons/math3/analysis/integration/gauss/GaussIntegratorFactory.java b/src/main/java/org/apache/commons/math3/analysis/integration/gauss/GaussIntegratorFactory.java
new file mode 100644
index 0000000..570a7ba
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/analysis/integration/gauss/GaussIntegratorFactory.java
@@ -0,0 +1,167 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math3.analysis.integration.gauss;
+
+import java.math.BigDecimal;
+
+import org.apache.commons.math3.exception.DimensionMismatchException;
+import org.apache.commons.math3.exception.NotStrictlyPositiveException;
+import org.apache.commons.math3.util.Pair;
+
+/**
+ * Class that provides different ways to compute the nodes and weights to be
+ * used by the {@link GaussIntegrator Gaussian integration rule}.
+ *
+ * @since 3.1
+ */
+public class GaussIntegratorFactory {
+ /** Generator of Gauss-Legendre integrators. */
+ private final BaseRuleFactory<Double> legendre = new LegendreRuleFactory();
+ /** Generator of Gauss-Legendre integrators. */
+ private final BaseRuleFactory<BigDecimal> legendreHighPrecision = new LegendreHighPrecisionRuleFactory();
+ /** Generator of Gauss-Hermite integrators. */
+ private final BaseRuleFactory<Double> hermite = new HermiteRuleFactory();
+
+ /**
+ * Creates a Gauss-Legendre integrator of the given order.
+ * The call to the
+ * {@link GaussIntegrator#integrate(org.apache.commons.math3.analysis.UnivariateFunction)
+ * integrate} method will perform an integration on the natural interval
+ * {@code [-1 , 1]}.
+ *
+ * @param numberOfPoints Order of the integration rule.
+ * @return a Gauss-Legendre integrator.
+ */
+ public GaussIntegrator legendre(int numberOfPoints) {
+ return new GaussIntegrator(getRule(legendre, numberOfPoints));
+ }
+
+ /**
+ * Creates a Gauss-Legendre integrator of the given order.
+ * The call to the
+ * {@link GaussIntegrator#integrate(org.apache.commons.math3.analysis.UnivariateFunction)
+ * integrate} method will perform an integration on the given interval.
+ *
+ * @param numberOfPoints Order of the integration rule.
+ * @param lowerBound Lower bound of the integration interval.
+ * @param upperBound Upper bound of the integration interval.
+ * @return a Gauss-Legendre integrator.
+ * @throws NotStrictlyPositiveException if number of points is not positive
+ */
+ public GaussIntegrator legendre(int numberOfPoints,
+ double lowerBound,
+ double upperBound)
+ throws NotStrictlyPositiveException {
+ return new GaussIntegrator(transform(getRule(legendre, numberOfPoints),
+ lowerBound, upperBound));
+ }
+
+ /**
+ * Creates a Gauss-Legendre integrator of the given order.
+ * The call to the
+ * {@link GaussIntegrator#integrate(org.apache.commons.math3.analysis.UnivariateFunction)
+ * integrate} method will perform an integration on the natural interval
+ * {@code [-1 , 1]}.
+ *
+ * @param numberOfPoints Order of the integration rule.
+ * @return a Gauss-Legendre integrator.
+ * @throws NotStrictlyPositiveException if number of points is not positive
+ */
+ public GaussIntegrator legendreHighPrecision(int numberOfPoints)
+ throws NotStrictlyPositiveException {
+ return new GaussIntegrator(getRule(legendreHighPrecision, numberOfPoints));
+ }
+
+ /**
+ * Creates an integrator of the given order, and whose call to the
+ * {@link GaussIntegrator#integrate(org.apache.commons.math3.analysis.UnivariateFunction)
+ * integrate} method will perform an integration on the given interval.
+ *
+ * @param numberOfPoints Order of the integration rule.
+ * @param lowerBound Lower bound of the integration interval.
+ * @param upperBound Upper bound of the integration interval.
+ * @return a Gauss-Legendre integrator.
+ * @throws NotStrictlyPositiveException if number of points is not positive
+ */
+ public GaussIntegrator legendreHighPrecision(int numberOfPoints,
+ double lowerBound,
+ double upperBound)
+ throws NotStrictlyPositiveException {
+ return new GaussIntegrator(transform(getRule(legendreHighPrecision, numberOfPoints),
+ lowerBound, upperBound));
+ }
+
+ /**
+ * Creates a Gauss-Hermite integrator of the given order.
+ * The call to the
+ * {@link SymmetricGaussIntegrator#integrate(org.apache.commons.math3.analysis.UnivariateFunction)
+ * integrate} method will perform a weighted integration on the interval
+ * \([-\infty, +\infty]\): the computed value is the improper integral of
+ * \(e^{-x^2}f(x)\)
+ * where \(f(x)\) is the function passed to the
+ * {@link SymmetricGaussIntegrator#integrate(org.apache.commons.math3.analysis.UnivariateFunction)
+ * integrate} method.
+ *
+ * @param numberOfPoints Order of the integration rule.
+ * @return a Gauss-Hermite integrator.
+ */
+ public SymmetricGaussIntegrator hermite(int numberOfPoints) {
+ return new SymmetricGaussIntegrator(getRule(hermite, numberOfPoints));
+ }
+
+ /**
+ * @param factory Integration rule factory.
+ * @param numberOfPoints Order of the integration rule.
+ * @return the integration nodes and weights.
+ * @throws NotStrictlyPositiveException if number of points is not positive
+ * @throws DimensionMismatchException if the elements of the rule pair do not
+ * have the same length.
+ */
+ private static Pair<double[], double[]> getRule(BaseRuleFactory<? extends Number> factory,
+ int numberOfPoints)
+ throws NotStrictlyPositiveException, DimensionMismatchException {
+ return factory.getRule(numberOfPoints);
+ }
+
+ /**
+ * Performs a change of variable so that the integration can be performed
+ * on an arbitrary interval {@code [a, b]}.
+ * It is assumed that the natural interval is {@code [-1, 1]}.
+ *
+ * @param rule Original points and weights.
+ * @param a Lower bound of the integration interval.
+ * @param b Lower bound of the integration interval.
+ * @return the points and weights adapted to the new interval.
+ */
+ private static Pair<double[], double[]> transform(Pair<double[], double[]> rule,
+ double a,
+ double b) {
+ final double[] points = rule.getFirst();
+ final double[] weights = rule.getSecond();
+
+ // Scaling
+ final double scale = (b - a) / 2;
+ final double shift = a + scale;
+
+ for (int i = 0; i < points.length; i++) {
+ points[i] = points[i] * scale + shift;
+ weights[i] *= scale;
+ }
+
+ return new Pair<double[], double[]>(points, weights);
+ }
+}
diff --git a/src/main/java/org/apache/commons/math3/analysis/integration/gauss/HermiteRuleFactory.java b/src/main/java/org/apache/commons/math3/analysis/integration/gauss/HermiteRuleFactory.java
new file mode 100644
index 0000000..abe3fbe
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/analysis/integration/gauss/HermiteRuleFactory.java
@@ -0,0 +1,177 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math3.analysis.integration.gauss;
+
+import org.apache.commons.math3.exception.DimensionMismatchException;
+import org.apache.commons.math3.util.Pair;
+import org.apache.commons.math3.util.FastMath;
+
+/**
+ * Factory that creates a
+ * <a href="http://en.wikipedia.org/wiki/Gauss-Hermite_quadrature">
+ * Gauss-type quadrature rule using Hermite polynomials</a>
+ * of the first kind.
+ * Such a quadrature rule allows the calculation of improper integrals
+ * of a function
+ * <p>
+ * \(f(x) e^{-x^2}\)
+ * </p><p>
+ * Recurrence relation and weights computation follow
+ * <a href="http://en.wikipedia.org/wiki/Abramowitz_and_Stegun">
+ * Abramowitz and Stegun, 1964</a>.
+ * </p><p>
+ * The coefficients of the standard Hermite polynomials grow very rapidly.
+ * In order to avoid overflows, each Hermite polynomial is normalized with
+ * respect to the underlying scalar product.
+ * The initial interval for the application of the bisection method is
+ * based on the roots of the previous Hermite polynomial (interlacing).
+ * Upper and lower bounds of these roots are provided by </p>
+ * <blockquote>
+ * I. Krasikov,
+ * <em>Nonnegative quadratic forms and bounds on orthogonal polynomials</em>,
+ * Journal of Approximation theory <b>111</b>, 31-49
+ * </blockquote>
+ *
+ * @since 3.3
+ */
+public class HermiteRuleFactory extends BaseRuleFactory<Double> {
+ /** &pi;<sup>1/2</sup> */
+ private static final double SQRT_PI = 1.77245385090551602729;
+ /** &pi;<sup>-1/4</sup> */
+ private static final double H0 = 7.5112554446494248286e-1;
+ /** &pi;<sup>-1/4</sup> &radic;2 */
+ private static final double H1 = 1.0622519320271969145;
+
+ /** {@inheritDoc} */
+ @Override
+ protected Pair<Double[], Double[]> computeRule(int numberOfPoints)
+ throws DimensionMismatchException {
+
+ if (numberOfPoints == 1) {
+ // Break recursion.
+ return new Pair<Double[], Double[]>(new Double[] { 0d },
+ new Double[] { SQRT_PI });
+ }
+
+ // Get previous rule.
+ // If it has not been computed yet it will trigger a recursive call
+ // to this method.
+ final int lastNumPoints = numberOfPoints - 1;
+ final Double[] previousPoints = getRuleInternal(lastNumPoints).getFirst();
+
+ // Compute next rule.
+ final Double[] points = new Double[numberOfPoints];
+ final Double[] weights = new Double[numberOfPoints];
+
+ final double sqrtTwoTimesLastNumPoints = FastMath.sqrt(2 * lastNumPoints);
+ final double sqrtTwoTimesNumPoints = FastMath.sqrt(2 * numberOfPoints);
+
+ // Find i-th root of H[n+1] by bracketing.
+ final int iMax = numberOfPoints / 2;
+ for (int i = 0; i < iMax; i++) {
+ // Lower-bound of the interval.
+ double a = (i == 0) ? -sqrtTwoTimesLastNumPoints : previousPoints[i - 1].doubleValue();
+ // Upper-bound of the interval.
+ double b = (iMax == 1) ? -0.5 : previousPoints[i].doubleValue();
+
+ // H[j-1](a)
+ double hma = H0;
+ // H[j](a)
+ double ha = H1 * a;
+ // H[j-1](b)
+ double hmb = H0;
+ // H[j](b)
+ double hb = H1 * b;
+ for (int j = 1; j < numberOfPoints; j++) {
+ // Compute H[j+1](a) and H[j+1](b)
+ final double jp1 = j + 1;
+ final double s = FastMath.sqrt(2 / jp1);
+ final double sm = FastMath.sqrt(j / jp1);
+ final double hpa = s * a * ha - sm * hma;
+ final double hpb = s * b * hb - sm * hmb;
+ hma = ha;
+ ha = hpa;
+ hmb = hb;
+ hb = hpb;
+ }
+
+ // Now ha = H[n+1](a), and hma = H[n](a) (same holds for b).
+ // Middle of the interval.
+ double c = 0.5 * (a + b);
+ // P[j-1](c)
+ double hmc = H0;
+ // P[j](c)
+ double hc = H1 * c;
+ boolean done = false;
+ while (!done) {
+ done = b - a <= Math.ulp(c);
+ hmc = H0;
+ hc = H1 * c;
+ for (int j = 1; j < numberOfPoints; j++) {
+ // Compute H[j+1](c)
+ final double jp1 = j + 1;
+ final double s = FastMath.sqrt(2 / jp1);
+ final double sm = FastMath.sqrt(j / jp1);
+ final double hpc = s * c * hc - sm * hmc;
+ hmc = hc;
+ hc = hpc;
+ }
+ // Now h = H[n+1](c) and hm = H[n](c).
+ if (!done) {
+ if (ha * hc < 0) {
+ b = c;
+ hmb = hmc;
+ hb = hc;
+ } else {
+ a = c;
+ hma = hmc;
+ ha = hc;
+ }
+ c = 0.5 * (a + b);
+ }
+ }
+ final double d = sqrtTwoTimesNumPoints * hmc;
+ final double w = 2 / (d * d);
+
+ points[i] = c;
+ weights[i] = w;
+
+ final int idx = lastNumPoints - i;
+ points[idx] = -c;
+ weights[idx] = w;
+ }
+
+ // If "numberOfPoints" is odd, 0 is a root.
+ // Note: as written, the test for oddness will work for negative
+ // integers too (although it is not necessary here), preventing
+ // a FindBugs warning.
+ if (numberOfPoints % 2 != 0) {
+ double hm = H0;
+ for (int j = 1; j < numberOfPoints; j += 2) {
+ final double jp1 = j + 1;
+ hm = -FastMath.sqrt(j / jp1) * hm;
+ }
+ final double d = sqrtTwoTimesNumPoints * hm;
+ final double w = 2 / (d * d);
+
+ points[iMax] = 0d;
+ weights[iMax] = w;
+ }
+
+ return new Pair<Double[], Double[]>(points, weights);
+ }
+}
diff --git a/src/main/java/org/apache/commons/math3/analysis/integration/gauss/LegendreHighPrecisionRuleFactory.java b/src/main/java/org/apache/commons/math3/analysis/integration/gauss/LegendreHighPrecisionRuleFactory.java
new file mode 100644
index 0000000..e07cb16
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/analysis/integration/gauss/LegendreHighPrecisionRuleFactory.java
@@ -0,0 +1,215 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math3.analysis.integration.gauss;
+
+import java.math.BigDecimal;
+import java.math.MathContext;
+
+import org.apache.commons.math3.exception.DimensionMismatchException;
+import org.apache.commons.math3.util.Pair;
+
+/**
+ * Factory that creates Gauss-type quadrature rule using Legendre polynomials.
+ * In this implementation, the lower and upper bounds of the natural interval
+ * of integration are -1 and 1, respectively.
+ * The Legendre polynomials are evaluated using the recurrence relation
+ * presented in <a href="http://en.wikipedia.org/wiki/Abramowitz_and_Stegun">
+ * Abramowitz and Stegun, 1964</a>.
+ *
+ * @since 3.1
+ */
+public class LegendreHighPrecisionRuleFactory extends BaseRuleFactory<BigDecimal> {
+ /** Settings for enhanced precision computations. */
+ private final MathContext mContext;
+ /** The number {@code 2}. */
+ private final BigDecimal two;
+ /** The number {@code -1}. */
+ private final BigDecimal minusOne;
+ /** The number {@code 0.5}. */
+ private final BigDecimal oneHalf;
+
+ /**
+ * Default precision is {@link MathContext#DECIMAL128 DECIMAL128}.
+ */
+ public LegendreHighPrecisionRuleFactory() {
+ this(MathContext.DECIMAL128);
+ }
+
+ /**
+ * @param mContext Precision setting for computing the quadrature rules.
+ */
+ public LegendreHighPrecisionRuleFactory(MathContext mContext) {
+ this.mContext = mContext;
+ two = new BigDecimal("2", mContext);
+ minusOne = new BigDecimal("-1", mContext);
+ oneHalf = new BigDecimal("0.5", mContext);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ protected Pair<BigDecimal[], BigDecimal[]> computeRule(int numberOfPoints)
+ throws DimensionMismatchException {
+
+ if (numberOfPoints == 1) {
+ // Break recursion.
+ return new Pair<BigDecimal[], BigDecimal[]>(new BigDecimal[] { BigDecimal.ZERO },
+ new BigDecimal[] { two });
+ }
+
+ // Get previous rule.
+ // If it has not been computed yet it will trigger a recursive call
+ // to this method.
+ final BigDecimal[] previousPoints = getRuleInternal(numberOfPoints - 1).getFirst();
+
+ // Compute next rule.
+ final BigDecimal[] points = new BigDecimal[numberOfPoints];
+ final BigDecimal[] weights = new BigDecimal[numberOfPoints];
+
+ // Find i-th root of P[n+1] by bracketing.
+ final int iMax = numberOfPoints / 2;
+ for (int i = 0; i < iMax; i++) {
+ // Lower-bound of the interval.
+ BigDecimal a = (i == 0) ? minusOne : previousPoints[i - 1];
+ // Upper-bound of the interval.
+ BigDecimal b = (iMax == 1) ? BigDecimal.ONE : previousPoints[i];
+ // P[j-1](a)
+ BigDecimal pma = BigDecimal.ONE;
+ // P[j](a)
+ BigDecimal pa = a;
+ // P[j-1](b)
+ BigDecimal pmb = BigDecimal.ONE;
+ // P[j](b)
+ BigDecimal pb = b;
+ for (int j = 1; j < numberOfPoints; j++) {
+ final BigDecimal b_two_j_p_1 = new BigDecimal(2 * j + 1, mContext);
+ final BigDecimal b_j = new BigDecimal(j, mContext);
+ final BigDecimal b_j_p_1 = new BigDecimal(j + 1, mContext);
+
+ // Compute P[j+1](a)
+ // ppa = ((2 * j + 1) * a * pa - j * pma) / (j + 1);
+
+ BigDecimal tmp1 = a.multiply(b_two_j_p_1, mContext);
+ tmp1 = pa.multiply(tmp1, mContext);
+ BigDecimal tmp2 = pma.multiply(b_j, mContext);
+ // P[j+1](a)
+ BigDecimal ppa = tmp1.subtract(tmp2, mContext);
+ ppa = ppa.divide(b_j_p_1, mContext);
+
+ // Compute P[j+1](b)
+ // ppb = ((2 * j + 1) * b * pb - j * pmb) / (j + 1);
+
+ tmp1 = b.multiply(b_two_j_p_1, mContext);
+ tmp1 = pb.multiply(tmp1, mContext);
+ tmp2 = pmb.multiply(b_j, mContext);
+ // P[j+1](b)
+ BigDecimal ppb = tmp1.subtract(tmp2, mContext);
+ ppb = ppb.divide(b_j_p_1, mContext);
+
+ pma = pa;
+ pa = ppa;
+ pmb = pb;
+ pb = ppb;
+ }
+ // Now pa = P[n+1](a), and pma = P[n](a). Same holds for b.
+ // Middle of the interval.
+ BigDecimal c = a.add(b, mContext).multiply(oneHalf, mContext);
+ // P[j-1](c)
+ BigDecimal pmc = BigDecimal.ONE;
+ // P[j](c)
+ BigDecimal pc = c;
+ boolean done = false;
+ while (!done) {
+ BigDecimal tmp1 = b.subtract(a, mContext);
+ BigDecimal tmp2 = c.ulp().multiply(BigDecimal.TEN, mContext);
+ done = tmp1.compareTo(tmp2) <= 0;
+ pmc = BigDecimal.ONE;
+ pc = c;
+ for (int j = 1; j < numberOfPoints; j++) {
+ final BigDecimal b_two_j_p_1 = new BigDecimal(2 * j + 1, mContext);
+ final BigDecimal b_j = new BigDecimal(j, mContext);
+ final BigDecimal b_j_p_1 = new BigDecimal(j + 1, mContext);
+
+ // Compute P[j+1](c)
+ tmp1 = c.multiply(b_two_j_p_1, mContext);
+ tmp1 = pc.multiply(tmp1, mContext);
+ tmp2 = pmc.multiply(b_j, mContext);
+ // P[j+1](c)
+ BigDecimal ppc = tmp1.subtract(tmp2, mContext);
+ ppc = ppc.divide(b_j_p_1, mContext);
+
+ pmc = pc;
+ pc = ppc;
+ }
+ // Now pc = P[n+1](c) and pmc = P[n](c).
+ if (!done) {
+ if (pa.signum() * pc.signum() <= 0) {
+ b = c;
+ pmb = pmc;
+ pb = pc;
+ } else {
+ a = c;
+ pma = pmc;
+ pa = pc;
+ }
+ c = a.add(b, mContext).multiply(oneHalf, mContext);
+ }
+ }
+ final BigDecimal nP = new BigDecimal(numberOfPoints, mContext);
+ BigDecimal tmp1 = pmc.subtract(c.multiply(pc, mContext), mContext);
+ tmp1 = tmp1.multiply(nP);
+ tmp1 = tmp1.pow(2, mContext);
+ BigDecimal tmp2 = c.pow(2, mContext);
+ tmp2 = BigDecimal.ONE.subtract(tmp2, mContext);
+ tmp2 = tmp2.multiply(two, mContext);
+ tmp2 = tmp2.divide(tmp1, mContext);
+
+ points[i] = c;
+ weights[i] = tmp2;
+
+ final int idx = numberOfPoints - i - 1;
+ points[idx] = c.negate(mContext);
+ weights[idx] = tmp2;
+ }
+ // If "numberOfPoints" is odd, 0 is a root.
+ // Note: as written, the test for oddness will work for negative
+ // integers too (although it is not necessary here), preventing
+ // a FindBugs warning.
+ if (numberOfPoints % 2 != 0) {
+ BigDecimal pmc = BigDecimal.ONE;
+ for (int j = 1; j < numberOfPoints; j += 2) {
+ final BigDecimal b_j = new BigDecimal(j, mContext);
+ final BigDecimal b_j_p_1 = new BigDecimal(j + 1, mContext);
+
+ // pmc = -j * pmc / (j + 1);
+ pmc = pmc.multiply(b_j, mContext);
+ pmc = pmc.divide(b_j_p_1, mContext);
+ pmc = pmc.negate(mContext);
+ }
+
+ // 2 / pow(numberOfPoints * pmc, 2);
+ final BigDecimal nP = new BigDecimal(numberOfPoints, mContext);
+ BigDecimal tmp1 = pmc.multiply(nP, mContext);
+ tmp1 = tmp1.pow(2, mContext);
+ BigDecimal tmp2 = two.divide(tmp1, mContext);
+
+ points[iMax] = BigDecimal.ZERO;
+ weights[iMax] = tmp2;
+ }
+
+ return new Pair<BigDecimal[], BigDecimal[]>(points, weights);
+ }
+}
diff --git a/src/main/java/org/apache/commons/math3/analysis/integration/gauss/LegendreRuleFactory.java b/src/main/java/org/apache/commons/math3/analysis/integration/gauss/LegendreRuleFactory.java
new file mode 100644
index 0000000..cb0bfec
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/analysis/integration/gauss/LegendreRuleFactory.java
@@ -0,0 +1,140 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math3.analysis.integration.gauss;
+
+import org.apache.commons.math3.exception.DimensionMismatchException;
+import org.apache.commons.math3.util.Pair;
+
+/**
+ * Factory that creates Gauss-type quadrature rule using Legendre polynomials.
+ * In this implementation, the lower and upper bounds of the natural interval
+ * of integration are -1 and 1, respectively.
+ * The Legendre polynomials are evaluated using the recurrence relation
+ * presented in <a href="http://en.wikipedia.org/wiki/Abramowitz_and_Stegun">
+ * Abramowitz and Stegun, 1964</a>.
+ *
+ * @since 3.1
+ */
+public class LegendreRuleFactory extends BaseRuleFactory<Double> {
+ /** {@inheritDoc} */
+ @Override
+ protected Pair<Double[], Double[]> computeRule(int numberOfPoints)
+ throws DimensionMismatchException {
+
+ if (numberOfPoints == 1) {
+ // Break recursion.
+ return new Pair<Double[], Double[]>(new Double[] { 0d },
+ new Double[] { 2d });
+ }
+
+ // Get previous rule.
+ // If it has not been computed yet it will trigger a recursive call
+ // to this method.
+ final Double[] previousPoints = getRuleInternal(numberOfPoints - 1).getFirst();
+
+ // Compute next rule.
+ final Double[] points = new Double[numberOfPoints];
+ final Double[] weights = new Double[numberOfPoints];
+
+ // Find i-th root of P[n+1] by bracketing.
+ final int iMax = numberOfPoints / 2;
+ for (int i = 0; i < iMax; i++) {
+ // Lower-bound of the interval.
+ double a = (i == 0) ? -1 : previousPoints[i - 1].doubleValue();
+ // Upper-bound of the interval.
+ double b = (iMax == 1) ? 1 : previousPoints[i].doubleValue();
+ // P[j-1](a)
+ double pma = 1;
+ // P[j](a)
+ double pa = a;
+ // P[j-1](b)
+ double pmb = 1;
+ // P[j](b)
+ double pb = b;
+ for (int j = 1; j < numberOfPoints; j++) {
+ final int two_j_p_1 = 2 * j + 1;
+ final int j_p_1 = j + 1;
+ // P[j+1](a)
+ final double ppa = (two_j_p_1 * a * pa - j * pma) / j_p_1;
+ // P[j+1](b)
+ final double ppb = (two_j_p_1 * b * pb - j * pmb) / j_p_1;
+ pma = pa;
+ pa = ppa;
+ pmb = pb;
+ pb = ppb;
+ }
+ // Now pa = P[n+1](a), and pma = P[n](a) (same holds for b).
+ // Middle of the interval.
+ double c = 0.5 * (a + b);
+ // P[j-1](c)
+ double pmc = 1;
+ // P[j](c)
+ double pc = c;
+ boolean done = false;
+ while (!done) {
+ done = b - a <= Math.ulp(c);
+ pmc = 1;
+ pc = c;
+ for (int j = 1; j < numberOfPoints; j++) {
+ // P[j+1](c)
+ final double ppc = ((2 * j + 1) * c * pc - j * pmc) / (j + 1);
+ pmc = pc;
+ pc = ppc;
+ }
+ // Now pc = P[n+1](c) and pmc = P[n](c).
+ if (!done) {
+ if (pa * pc <= 0) {
+ b = c;
+ pmb = pmc;
+ pb = pc;
+ } else {
+ a = c;
+ pma = pmc;
+ pa = pc;
+ }
+ c = 0.5 * (a + b);
+ }
+ }
+ final double d = numberOfPoints * (pmc - c * pc);
+ final double w = 2 * (1 - c * c) / (d * d);
+
+ points[i] = c;
+ weights[i] = w;
+
+ final int idx = numberOfPoints - i - 1;
+ points[idx] = -c;
+ weights[idx] = w;
+ }
+ // If "numberOfPoints" is odd, 0 is a root.
+ // Note: as written, the test for oddness will work for negative
+ // integers too (although it is not necessary here), preventing
+ // a FindBugs warning.
+ if (numberOfPoints % 2 != 0) {
+ double pmc = 1;
+ for (int j = 1; j < numberOfPoints; j += 2) {
+ pmc = -j * pmc / (j + 1);
+ }
+ final double d = numberOfPoints * pmc;
+ final double w = 2 / (d * d);
+
+ points[iMax] = 0d;
+ weights[iMax] = w;
+ }
+
+ return new Pair<Double[], Double[]>(points, weights);
+ }
+}
diff --git a/src/main/java/org/apache/commons/math3/analysis/integration/gauss/SymmetricGaussIntegrator.java b/src/main/java/org/apache/commons/math3/analysis/integration/gauss/SymmetricGaussIntegrator.java
new file mode 100644
index 0000000..7fa4884
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/analysis/integration/gauss/SymmetricGaussIntegrator.java
@@ -0,0 +1,103 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math3.analysis.integration.gauss;
+
+import org.apache.commons.math3.analysis.UnivariateFunction;
+import org.apache.commons.math3.exception.DimensionMismatchException;
+import org.apache.commons.math3.exception.NonMonotonicSequenceException;
+import org.apache.commons.math3.util.Pair;
+
+/**
+ * This class's implements {@link #integrate(UnivariateFunction) integrate}
+ * method assuming that the integral is symmetric about 0.
+ * This allows to reduce numerical errors.
+ *
+ * @since 3.3
+ */
+public class SymmetricGaussIntegrator extends GaussIntegrator {
+ /**
+ * Creates an integrator from the given {@code points} and {@code weights}.
+ * The integration interval is defined by the first and last value of
+ * {@code points} which must be sorted in increasing order.
+ *
+ * @param points Integration points.
+ * @param weights Weights of the corresponding integration nodes.
+ * @throws NonMonotonicSequenceException if the {@code points} are not
+ * sorted in increasing order.
+ * @throws DimensionMismatchException if points and weights don't have the same length
+ */
+ public SymmetricGaussIntegrator(double[] points,
+ double[] weights)
+ throws NonMonotonicSequenceException, DimensionMismatchException {
+ super(points, weights);
+ }
+
+ /**
+ * Creates an integrator from the given pair of points (first element of
+ * the pair) and weights (second element of the pair.
+ *
+ * @param pointsAndWeights Integration points and corresponding weights.
+ * @throws NonMonotonicSequenceException if the {@code points} are not
+ * sorted in increasing order.
+ *
+ * @see #SymmetricGaussIntegrator(double[], double[])
+ */
+ public SymmetricGaussIntegrator(Pair<double[], double[]> pointsAndWeights)
+ throws NonMonotonicSequenceException {
+ this(pointsAndWeights.getFirst(), pointsAndWeights.getSecond());
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public double integrate(UnivariateFunction f) {
+ final int ruleLength = getNumberOfPoints();
+
+ if (ruleLength == 1) {
+ return getWeight(0) * f.value(0d);
+ }
+
+ final int iMax = ruleLength / 2;
+ double s = 0;
+ double c = 0;
+ for (int i = 0; i < iMax; i++) {
+ final double p = getPoint(i);
+ final double w = getWeight(i);
+
+ final double f1 = f.value(p);
+ final double f2 = f.value(-p);
+
+ final double y = w * (f1 + f2) - c;
+ final double t = s + y;
+
+ c = (t - s) - y;
+ s = t;
+ }
+
+ if (ruleLength % 2 != 0) {
+ final double w = getWeight(iMax);
+
+ final double y = w * f.value(0d) - c;
+ final double t = s + y;
+
+ s = t;
+ }
+
+ return s;
+ }
+}
diff --git a/src/main/java/org/apache/commons/math3/analysis/integration/gauss/package-info.java b/src/main/java/org/apache/commons/math3/analysis/integration/gauss/package-info.java
new file mode 100644
index 0000000..066da30
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/analysis/integration/gauss/package-info.java
@@ -0,0 +1,22 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/**
+ *
+ * Gauss family of quadrature schemes.
+ *
+ */
+package org.apache.commons.math3.analysis.integration.gauss;
diff --git a/src/main/java/org/apache/commons/math3/analysis/integration/package-info.java b/src/main/java/org/apache/commons/math3/analysis/integration/package-info.java
new file mode 100644
index 0000000..f4df3ba
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/analysis/integration/package-info.java
@@ -0,0 +1,22 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/**
+ *
+ * Numerical integration (quadrature) algorithms for univariate real functions.
+ *
+ */
+package org.apache.commons.math3.analysis.integration;
diff --git a/src/main/java/org/apache/commons/math3/analysis/interpolation/AkimaSplineInterpolator.java b/src/main/java/org/apache/commons/math3/analysis/interpolation/AkimaSplineInterpolator.java
new file mode 100644
index 0000000..5b89dfe
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/analysis/interpolation/AkimaSplineInterpolator.java
@@ -0,0 +1,215 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math3.analysis.interpolation;
+
+import org.apache.commons.math3.analysis.polynomials.PolynomialFunction;
+import org.apache.commons.math3.analysis.polynomials.PolynomialSplineFunction;
+import org.apache.commons.math3.exception.DimensionMismatchException;
+import org.apache.commons.math3.exception.NonMonotonicSequenceException;
+import org.apache.commons.math3.exception.NullArgumentException;
+import org.apache.commons.math3.exception.NumberIsTooSmallException;
+import org.apache.commons.math3.exception.util.LocalizedFormats;
+import org.apache.commons.math3.util.FastMath;
+import org.apache.commons.math3.util.MathArrays;
+import org.apache.commons.math3.util.Precision;
+
+/**
+ * Computes a cubic spline interpolation for the data set using the Akima
+ * algorithm, as originally formulated by Hiroshi Akima in his 1970 paper
+ * "A New Method of Interpolation and Smooth Curve Fitting Based on Local Procedures."
+ * J. ACM 17, 4 (October 1970), 589-602. DOI=10.1145/321607.321609
+ * http://doi.acm.org/10.1145/321607.321609
+ * <p>
+ * This implementation is based on the Akima implementation in the CubicSpline
+ * class in the Math.NET Numerics library. The method referenced is
+ * CubicSpline.InterpolateAkimaSorted
+ * </p>
+ * <p>
+ * The {@link #interpolate(double[], double[]) interpolate} method returns a
+ * {@link PolynomialSplineFunction} consisting of n cubic polynomials, defined
+ * over the subintervals determined by the x values, {@code x[0] < x[i] ... < x[n]}.
+ * The Akima algorithm requires that {@code n >= 5}.
+ * </p>
+ */
+public class AkimaSplineInterpolator
+ implements UnivariateInterpolator {
+ /** The minimum number of points that are needed to compute the function. */
+ private static final int MINIMUM_NUMBER_POINTS = 5;
+
+ /**
+ * Computes an interpolating function for the data set.
+ *
+ * @param xvals the arguments for the interpolation points
+ * @param yvals the values for the interpolation points
+ * @return a function which interpolates the data set
+ * @throws DimensionMismatchException if {@code xvals} and {@code yvals} have
+ * different sizes.
+ * @throws NonMonotonicSequenceException if {@code xvals} is not sorted in
+ * strict increasing order.
+ * @throws NumberIsTooSmallException if the size of {@code xvals} is smaller
+ * than 5.
+ */
+ public PolynomialSplineFunction interpolate(double[] xvals,
+ double[] yvals)
+ throws DimensionMismatchException,
+ NumberIsTooSmallException,
+ NonMonotonicSequenceException {
+ if (xvals == null ||
+ yvals == null) {
+ throw new NullArgumentException();
+ }
+
+ if (xvals.length != yvals.length) {
+ throw new DimensionMismatchException(xvals.length, yvals.length);
+ }
+
+ if (xvals.length < MINIMUM_NUMBER_POINTS) {
+ throw new NumberIsTooSmallException(LocalizedFormats.NUMBER_OF_POINTS,
+ xvals.length,
+ MINIMUM_NUMBER_POINTS, true);
+ }
+
+ MathArrays.checkOrder(xvals);
+
+ final int numberOfDiffAndWeightElements = xvals.length - 1;
+
+ final double[] differences = new double[numberOfDiffAndWeightElements];
+ final double[] weights = new double[numberOfDiffAndWeightElements];
+
+ for (int i = 0; i < differences.length; i++) {
+ differences[i] = (yvals[i + 1] - yvals[i]) / (xvals[i + 1] - xvals[i]);
+ }
+
+ for (int i = 1; i < weights.length; i++) {
+ weights[i] = FastMath.abs(differences[i] - differences[i - 1]);
+ }
+
+ // Prepare Hermite interpolation scheme.
+ final double[] firstDerivatives = new double[xvals.length];
+
+ for (int i = 2; i < firstDerivatives.length - 2; i++) {
+ final double wP = weights[i + 1];
+ final double wM = weights[i - 1];
+ if (Precision.equals(wP, 0.0) &&
+ Precision.equals(wM, 0.0)) {
+ final double xv = xvals[i];
+ final double xvP = xvals[i + 1];
+ final double xvM = xvals[i - 1];
+ firstDerivatives[i] = (((xvP - xv) * differences[i - 1]) + ((xv - xvM) * differences[i])) / (xvP - xvM);
+ } else {
+ firstDerivatives[i] = ((wP * differences[i - 1]) + (wM * differences[i])) / (wP + wM);
+ }
+ }
+
+ firstDerivatives[0] = differentiateThreePoint(xvals, yvals, 0, 0, 1, 2);
+ firstDerivatives[1] = differentiateThreePoint(xvals, yvals, 1, 0, 1, 2);
+ firstDerivatives[xvals.length - 2] = differentiateThreePoint(xvals, yvals, xvals.length - 2,
+ xvals.length - 3, xvals.length - 2,
+ xvals.length - 1);
+ firstDerivatives[xvals.length - 1] = differentiateThreePoint(xvals, yvals, xvals.length - 1,
+ xvals.length - 3, xvals.length - 2,
+ xvals.length - 1);
+
+ return interpolateHermiteSorted(xvals, yvals, firstDerivatives);
+ }
+
+ /**
+ * Three point differentiation helper, modeled off of the same method in the
+ * Math.NET CubicSpline class. This is used by both the Apache Math and the
+ * Math.NET Akima Cubic Spline algorithms
+ *
+ * @param xvals x values to calculate the numerical derivative with
+ * @param yvals y values to calculate the numerical derivative with
+ * @param indexOfDifferentiation index of the elemnt we are calculating the derivative around
+ * @param indexOfFirstSample index of the first element to sample for the three point method
+ * @param indexOfSecondsample index of the second element to sample for the three point method
+ * @param indexOfThirdSample index of the third element to sample for the three point method
+ * @return the derivative
+ */
+ private double differentiateThreePoint(double[] xvals, double[] yvals,
+ int indexOfDifferentiation,
+ int indexOfFirstSample,
+ int indexOfSecondsample,
+ int indexOfThirdSample) {
+ final double x0 = yvals[indexOfFirstSample];
+ final double x1 = yvals[indexOfSecondsample];
+ final double x2 = yvals[indexOfThirdSample];
+
+ final double t = xvals[indexOfDifferentiation] - xvals[indexOfFirstSample];
+ final double t1 = xvals[indexOfSecondsample] - xvals[indexOfFirstSample];
+ final double t2 = xvals[indexOfThirdSample] - xvals[indexOfFirstSample];
+
+ final double a = (x2 - x0 - (t2 / t1 * (x1 - x0))) / (t2 * t2 - t1 * t2);
+ final double b = (x1 - x0 - a * t1 * t1) / t1;
+
+ return (2 * a * t) + b;
+ }
+
+ /**
+ * Creates a Hermite cubic spline interpolation from the set of (x,y) value
+ * pairs and their derivatives. This is modeled off of the
+ * InterpolateHermiteSorted method in the Math.NET CubicSpline class.
+ *
+ * @param xvals x values for interpolation
+ * @param yvals y values for interpolation
+ * @param firstDerivatives first derivative values of the function
+ * @return polynomial that fits the function
+ */
+ private PolynomialSplineFunction interpolateHermiteSorted(double[] xvals,
+ double[] yvals,
+ double[] firstDerivatives) {
+ if (xvals.length != yvals.length) {
+ throw new DimensionMismatchException(xvals.length, yvals.length);
+ }
+
+ if (xvals.length != firstDerivatives.length) {
+ throw new DimensionMismatchException(xvals.length,
+ firstDerivatives.length);
+ }
+
+ final int minimumLength = 2;
+ if (xvals.length < minimumLength) {
+ throw new NumberIsTooSmallException(LocalizedFormats.NUMBER_OF_POINTS,
+ xvals.length, minimumLength,
+ true);
+ }
+
+ final int size = xvals.length - 1;
+ final PolynomialFunction[] polynomials = new PolynomialFunction[size];
+ final double[] coefficients = new double[4];
+
+ for (int i = 0; i < polynomials.length; i++) {
+ final double w = xvals[i + 1] - xvals[i];
+ final double w2 = w * w;
+
+ final double yv = yvals[i];
+ final double yvP = yvals[i + 1];
+
+ final double fd = firstDerivatives[i];
+ final double fdP = firstDerivatives[i + 1];
+
+ coefficients[0] = yv;
+ coefficients[1] = firstDerivatives[i];
+ coefficients[2] = (3 * (yvP - yv) / w - 2 * fd - fdP) / w;
+ coefficients[3] = (2 * (yv - yvP) / w + fd + fdP) / w2;
+ polynomials[i] = new PolynomialFunction(coefficients);
+ }
+
+ return new PolynomialSplineFunction(xvals, polynomials);
+
+ }
+}
diff --git a/src/main/java/org/apache/commons/math3/analysis/interpolation/BicubicInterpolatingFunction.java b/src/main/java/org/apache/commons/math3/analysis/interpolation/BicubicInterpolatingFunction.java
new file mode 100644
index 0000000..454d8f8
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/analysis/interpolation/BicubicInterpolatingFunction.java
@@ -0,0 +1,325 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math3.analysis.interpolation;
+
+import java.util.Arrays;
+import org.apache.commons.math3.analysis.BivariateFunction;
+import org.apache.commons.math3.exception.DimensionMismatchException;
+import org.apache.commons.math3.exception.NoDataException;
+import org.apache.commons.math3.exception.OutOfRangeException;
+import org.apache.commons.math3.exception.NonMonotonicSequenceException;
+import org.apache.commons.math3.util.MathArrays;
+
+/**
+ * Function that implements the
+ * <a href="http://en.wikipedia.org/wiki/Bicubic_interpolation">
+ * bicubic spline interpolation</a>.
+ *
+ * @since 3.4
+ */
+public class BicubicInterpolatingFunction
+ implements BivariateFunction {
+ /** Number of coefficients. */
+ private static final int NUM_COEFF = 16;
+ /**
+ * Matrix to compute the spline coefficients from the function values
+ * and function derivatives values
+ */
+ private static final double[][] AINV = {
+ { 1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 },
+ { 0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0 },
+ { -3,3,0,0,-2,-1,0,0,0,0,0,0,0,0,0,0 },
+ { 2,-2,0,0,1,1,0,0,0,0,0,0,0,0,0,0 },
+ { 0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0 },
+ { 0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0 },
+ { 0,0,0,0,0,0,0,0,-3,3,0,0,-2,-1,0,0 },
+ { 0,0,0,0,0,0,0,0,2,-2,0,0,1,1,0,0 },
+ { -3,0,3,0,0,0,0,0,-2,0,-1,0,0,0,0,0 },
+ { 0,0,0,0,-3,0,3,0,0,0,0,0,-2,0,-1,0 },
+ { 9,-9,-9,9,6,3,-6,-3,6,-6,3,-3,4,2,2,1 },
+ { -6,6,6,-6,-3,-3,3,3,-4,4,-2,2,-2,-2,-1,-1 },
+ { 2,0,-2,0,0,0,0,0,1,0,1,0,0,0,0,0 },
+ { 0,0,0,0,2,0,-2,0,0,0,0,0,1,0,1,0 },
+ { -6,6,6,-6,-4,-2,4,2,-3,3,-3,3,-2,-1,-2,-1 },
+ { 4,-4,-4,4,2,2,-2,-2,2,-2,2,-2,1,1,1,1 }
+ };
+
+ /** Samples x-coordinates */
+ private final double[] xval;
+ /** Samples y-coordinates */
+ private final double[] yval;
+ /** Set of cubic splines patching the whole data grid */
+ private final BicubicFunction[][] splines;
+
+ /**
+ * @param x Sample values of the x-coordinate, in increasing order.
+ * @param y Sample values of the y-coordinate, in increasing order.
+ * @param f Values of the function on every grid point.
+ * @param dFdX Values of the partial derivative of function with respect
+ * to x on every grid point.
+ * @param dFdY Values of the partial derivative of function with respect
+ * to y on every grid point.
+ * @param d2FdXdY Values of the cross partial derivative of function on
+ * every grid point.
+ * @throws DimensionMismatchException if the various arrays do not contain
+ * the expected number of elements.
+ * @throws NonMonotonicSequenceException if {@code x} or {@code y} are
+ * not strictly increasing.
+ * @throws NoDataException if any of the arrays has zero length.
+ */
+ public BicubicInterpolatingFunction(double[] x,
+ double[] y,
+ double[][] f,
+ double[][] dFdX,
+ double[][] dFdY,
+ double[][] d2FdXdY)
+ throws DimensionMismatchException,
+ NoDataException,
+ NonMonotonicSequenceException {
+ final int xLen = x.length;
+ final int yLen = y.length;
+
+ if (xLen == 0 || yLen == 0 || f.length == 0 || f[0].length == 0) {
+ throw new NoDataException();
+ }
+ if (xLen != f.length) {
+ throw new DimensionMismatchException(xLen, f.length);
+ }
+ if (xLen != dFdX.length) {
+ throw new DimensionMismatchException(xLen, dFdX.length);
+ }
+ if (xLen != dFdY.length) {
+ throw new DimensionMismatchException(xLen, dFdY.length);
+ }
+ if (xLen != d2FdXdY.length) {
+ throw new DimensionMismatchException(xLen, d2FdXdY.length);
+ }
+
+ MathArrays.checkOrder(x);
+ MathArrays.checkOrder(y);
+
+ xval = x.clone();
+ yval = y.clone();
+
+ final int lastI = xLen - 1;
+ final int lastJ = yLen - 1;
+ splines = new BicubicFunction[lastI][lastJ];
+
+ for (int i = 0; i < lastI; i++) {
+ if (f[i].length != yLen) {
+ throw new DimensionMismatchException(f[i].length, yLen);
+ }
+ if (dFdX[i].length != yLen) {
+ throw new DimensionMismatchException(dFdX[i].length, yLen);
+ }
+ if (dFdY[i].length != yLen) {
+ throw new DimensionMismatchException(dFdY[i].length, yLen);
+ }
+ if (d2FdXdY[i].length != yLen) {
+ throw new DimensionMismatchException(d2FdXdY[i].length, yLen);
+ }
+ final int ip1 = i + 1;
+ final double xR = xval[ip1] - xval[i];
+ for (int j = 0; j < lastJ; j++) {
+ final int jp1 = j + 1;
+ final double yR = yval[jp1] - yval[j];
+ final double xRyR = xR * yR;
+ final double[] beta = new double[] {
+ f[i][j], f[ip1][j], f[i][jp1], f[ip1][jp1],
+ dFdX[i][j] * xR, dFdX[ip1][j] * xR, dFdX[i][jp1] * xR, dFdX[ip1][jp1] * xR,
+ dFdY[i][j] * yR, dFdY[ip1][j] * yR, dFdY[i][jp1] * yR, dFdY[ip1][jp1] * yR,
+ d2FdXdY[i][j] * xRyR, d2FdXdY[ip1][j] * xRyR, d2FdXdY[i][jp1] * xRyR, d2FdXdY[ip1][jp1] * xRyR
+ };
+
+ splines[i][j] = new BicubicFunction(computeSplineCoefficients(beta));
+ }
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public double value(double x, double y)
+ throws OutOfRangeException {
+ final int i = searchIndex(x, xval);
+ final int j = searchIndex(y, yval);
+
+ final double xN = (x - xval[i]) / (xval[i + 1] - xval[i]);
+ final double yN = (y - yval[j]) / (yval[j + 1] - yval[j]);
+
+ return splines[i][j].value(xN, yN);
+ }
+
+ /**
+ * Indicates whether a point is within the interpolation range.
+ *
+ * @param x First coordinate.
+ * @param y Second coordinate.
+ * @return {@code true} if (x, y) is a valid point.
+ */
+ public boolean isValidPoint(double x, double y) {
+ if (x < xval[0] ||
+ x > xval[xval.length - 1] ||
+ y < yval[0] ||
+ y > yval[yval.length - 1]) {
+ return false;
+ } else {
+ return true;
+ }
+ }
+
+ /**
+ * @param c Coordinate.
+ * @param val Coordinate samples.
+ * @return the index in {@code val} corresponding to the interval
+ * containing {@code c}.
+ * @throws OutOfRangeException if {@code c} is out of the
+ * range defined by the boundary values of {@code val}.
+ */
+ private int searchIndex(double c, double[] val) {
+ final int r = Arrays.binarySearch(val, c);
+
+ if (r == -1 ||
+ r == -val.length - 1) {
+ throw new OutOfRangeException(c, val[0], val[val.length - 1]);
+ }
+
+ if (r < 0) {
+ // "c" in within an interpolation sub-interval: Return the
+ // index of the sample at the lower end of the sub-interval.
+ return -r - 2;
+ }
+ final int last = val.length - 1;
+ if (r == last) {
+ // "c" is the last sample of the range: Return the index
+ // of the sample at the lower end of the last sub-interval.
+ return last - 1;
+ }
+
+ // "c" is another sample point.
+ return r;
+ }
+
+ /**
+ * Compute the spline coefficients from the list of function values and
+ * function partial derivatives values at the four corners of a grid
+ * element. They must be specified in the following order:
+ * <ul>
+ * <li>f(0,0)</li>
+ * <li>f(1,0)</li>
+ * <li>f(0,1)</li>
+ * <li>f(1,1)</li>
+ * <li>f<sub>x</sub>(0,0)</li>
+ * <li>f<sub>x</sub>(1,0)</li>
+ * <li>f<sub>x</sub>(0,1)</li>
+ * <li>f<sub>x</sub>(1,1)</li>
+ * <li>f<sub>y</sub>(0,0)</li>
+ * <li>f<sub>y</sub>(1,0)</li>
+ * <li>f<sub>y</sub>(0,1)</li>
+ * <li>f<sub>y</sub>(1,1)</li>
+ * <li>f<sub>xy</sub>(0,0)</li>
+ * <li>f<sub>xy</sub>(1,0)</li>
+ * <li>f<sub>xy</sub>(0,1)</li>
+ * <li>f<sub>xy</sub>(1,1)</li>
+ * </ul>
+ * where the subscripts indicate the partial derivative with respect to
+ * the corresponding variable(s).
+ *
+ * @param beta List of function values and function partial derivatives
+ * values.
+ * @return the spline coefficients.
+ */
+ private double[] computeSplineCoefficients(double[] beta) {
+ final double[] a = new double[NUM_COEFF];
+
+ for (int i = 0; i < NUM_COEFF; i++) {
+ double result = 0;
+ final double[] row = AINV[i];
+ for (int j = 0; j < NUM_COEFF; j++) {
+ result += row[j] * beta[j];
+ }
+ a[i] = result;
+ }
+
+ return a;
+ }
+}
+
+/**
+ * Bicubic function.
+ */
+class BicubicFunction implements BivariateFunction {
+ /** Number of points. */
+ private static final short N = 4;
+ /** Coefficients */
+ private final double[][] a;
+
+ /**
+ * Simple constructor.
+ *
+ * @param coeff Spline coefficients.
+ */
+ BicubicFunction(double[] coeff) {
+ a = new double[N][N];
+ for (int j = 0; j < N; j++) {
+ final double[] aJ = a[j];
+ for (int i = 0; i < N; i++) {
+ aJ[i] = coeff[i * N + j];
+ }
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public double value(double x, double y) {
+ if (x < 0 || x > 1) {
+ throw new OutOfRangeException(x, 0, 1);
+ }
+ if (y < 0 || y > 1) {
+ throw new OutOfRangeException(y, 0, 1);
+ }
+
+ final double x2 = x * x;
+ final double x3 = x2 * x;
+ final double[] pX = {1, x, x2, x3};
+
+ final double y2 = y * y;
+ final double y3 = y2 * y;
+ final double[] pY = {1, y, y2, y3};
+
+ return apply(pX, pY, a);
+ }
+
+ /**
+ * Compute the value of the bicubic polynomial.
+ *
+ * @param pX Powers of the x-coordinate.
+ * @param pY Powers of the y-coordinate.
+ * @param coeff Spline coefficients.
+ * @return the interpolated value.
+ */
+ private double apply(double[] pX, double[] pY, double[][] coeff) {
+ double result = 0;
+ for (int i = 0; i < N; i++) {
+ final double r = MathArrays.linearCombination(coeff[i], pY);
+ result += r * pX[i];
+ }
+
+ return result;
+ }
+}
diff --git a/src/main/java/org/apache/commons/math3/analysis/interpolation/BicubicInterpolator.java b/src/main/java/org/apache/commons/math3/analysis/interpolation/BicubicInterpolator.java
new file mode 100644
index 0000000..4aebdff
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/analysis/interpolation/BicubicInterpolator.java
@@ -0,0 +1,113 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math3.analysis.interpolation;
+
+import org.apache.commons.math3.exception.DimensionMismatchException;
+import org.apache.commons.math3.exception.NoDataException;
+import org.apache.commons.math3.exception.NonMonotonicSequenceException;
+import org.apache.commons.math3.exception.NumberIsTooSmallException;
+import org.apache.commons.math3.util.MathArrays;
+
+/**
+ * Generates a {@link BicubicInterpolatingFunction bicubic interpolating
+ * function}.
+ * <p>
+ * Caveat: Because the interpolation scheme requires that derivatives be
+ * specified at the sample points, those are approximated with finite
+ * differences (using the 2-points symmetric formulae).
+ * Since their values are undefined at the borders of the provided
+ * interpolation ranges, the interpolated values will be wrong at the
+ * edges of the patch.
+ * The {@code interpolate} method will return a function that overrides
+ * {@link BicubicInterpolatingFunction#isValidPoint(double,double)} to
+ * indicate points where the interpolation will be inaccurate.
+ * </p>
+ *
+ * @since 3.4
+ */
+public class BicubicInterpolator
+ implements BivariateGridInterpolator {
+ /**
+ * {@inheritDoc}
+ */
+ public BicubicInterpolatingFunction interpolate(final double[] xval,
+ final double[] yval,
+ final double[][] fval)
+ throws NoDataException, DimensionMismatchException,
+ NonMonotonicSequenceException, NumberIsTooSmallException {
+ if (xval.length == 0 || yval.length == 0 || fval.length == 0) {
+ throw new NoDataException();
+ }
+ if (xval.length != fval.length) {
+ throw new DimensionMismatchException(xval.length, fval.length);
+ }
+
+ MathArrays.checkOrder(xval);
+ MathArrays.checkOrder(yval);
+
+ final int xLen = xval.length;
+ final int yLen = yval.length;
+
+ // Approximation to the partial derivatives using finite differences.
+ final double[][] dFdX = new double[xLen][yLen];
+ final double[][] dFdY = new double[xLen][yLen];
+ final double[][] d2FdXdY = new double[xLen][yLen];
+ for (int i = 1; i < xLen - 1; i++) {
+ final int nI = i + 1;
+ final int pI = i - 1;
+
+ final double nX = xval[nI];
+ final double pX = xval[pI];
+
+ final double deltaX = nX - pX;
+
+ for (int j = 1; j < yLen - 1; j++) {
+ final int nJ = j + 1;
+ final int pJ = j - 1;
+
+ final double nY = yval[nJ];
+ final double pY = yval[pJ];
+
+ final double deltaY = nY - pY;
+
+ dFdX[i][j] = (fval[nI][j] - fval[pI][j]) / deltaX;
+ dFdY[i][j] = (fval[i][nJ] - fval[i][pJ]) / deltaY;
+
+ final double deltaXY = deltaX * deltaY;
+
+ d2FdXdY[i][j] = (fval[nI][nJ] - fval[nI][pJ] - fval[pI][nJ] + fval[pI][pJ]) / deltaXY;
+ }
+ }
+
+ // Create the interpolating function.
+ return new BicubicInterpolatingFunction(xval, yval, fval,
+ dFdX, dFdY, d2FdXdY) {
+ /** {@inheritDoc} */
+ @Override
+ public boolean isValidPoint(double x, double y) {
+ if (x < xval[1] ||
+ x > xval[xval.length - 2] ||
+ y < yval[1] ||
+ y > yval[yval.length - 2]) {
+ return false;
+ } else {
+ return true;
+ }
+ }
+ };
+ }
+}
diff --git a/src/main/java/org/apache/commons/math3/analysis/interpolation/BicubicSplineInterpolatingFunction.java b/src/main/java/org/apache/commons/math3/analysis/interpolation/BicubicSplineInterpolatingFunction.java
new file mode 100644
index 0000000..8f1771a
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/analysis/interpolation/BicubicSplineInterpolatingFunction.java
@@ -0,0 +1,641 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math3.analysis.interpolation;
+
+import java.util.Arrays;
+import org.apache.commons.math3.analysis.BivariateFunction;
+import org.apache.commons.math3.exception.DimensionMismatchException;
+import org.apache.commons.math3.exception.NoDataException;
+import org.apache.commons.math3.exception.OutOfRangeException;
+import org.apache.commons.math3.exception.NonMonotonicSequenceException;
+import org.apache.commons.math3.util.MathArrays;
+
+/**
+ * Function that implements the
+ * <a href="http://en.wikipedia.org/wiki/Bicubic_interpolation">
+ * bicubic spline interpolation</a>. Due to numerical accuracy issues this should not
+ * be used.
+ *
+ * @since 2.1
+ * @deprecated as of 3.4 replaced by
+ * {@link org.apache.commons.math3.analysis.interpolation.PiecewiseBicubicSplineInterpolatingFunction}
+ */
+@Deprecated
+public class BicubicSplineInterpolatingFunction
+ implements BivariateFunction {
+ /** Number of coefficients. */
+ private static final int NUM_COEFF = 16;
+ /**
+ * Matrix to compute the spline coefficients from the function values
+ * and function derivatives values
+ */
+ private static final double[][] AINV = {
+ { 1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 },
+ { 0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0 },
+ { -3,3,0,0,-2,-1,0,0,0,0,0,0,0,0,0,0 },
+ { 2,-2,0,0,1,1,0,0,0,0,0,0,0,0,0,0 },
+ { 0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0 },
+ { 0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0 },
+ { 0,0,0,0,0,0,0,0,-3,3,0,0,-2,-1,0,0 },
+ { 0,0,0,0,0,0,0,0,2,-2,0,0,1,1,0,0 },
+ { -3,0,3,0,0,0,0,0,-2,0,-1,0,0,0,0,0 },
+ { 0,0,0,0,-3,0,3,0,0,0,0,0,-2,0,-1,0 },
+ { 9,-9,-9,9,6,3,-6,-3,6,-6,3,-3,4,2,2,1 },
+ { -6,6,6,-6,-3,-3,3,3,-4,4,-2,2,-2,-2,-1,-1 },
+ { 2,0,-2,0,0,0,0,0,1,0,1,0,0,0,0,0 },
+ { 0,0,0,0,2,0,-2,0,0,0,0,0,1,0,1,0 },
+ { -6,6,6,-6,-4,-2,4,2,-3,3,-3,3,-2,-1,-2,-1 },
+ { 4,-4,-4,4,2,2,-2,-2,2,-2,2,-2,1,1,1,1 }
+ };
+
+ /** Samples x-coordinates */
+ private final double[] xval;
+ /** Samples y-coordinates */
+ private final double[] yval;
+ /** Set of cubic splines patching the whole data grid */
+ private final BicubicSplineFunction[][] splines;
+ /**
+ * Partial derivatives.
+ * The value of the first index determines the kind of derivatives:
+ * 0 = first partial derivatives wrt x
+ * 1 = first partial derivatives wrt y
+ * 2 = second partial derivatives wrt x
+ * 3 = second partial derivatives wrt y
+ * 4 = cross partial derivatives
+ */
+ private final BivariateFunction[][][] partialDerivatives;
+
+ /**
+ * @param x Sample values of the x-coordinate, in increasing order.
+ * @param y Sample values of the y-coordinate, in increasing order.
+ * @param f Values of the function on every grid point.
+ * @param dFdX Values of the partial derivative of function with respect
+ * to x on every grid point.
+ * @param dFdY Values of the partial derivative of function with respect
+ * to y on every grid point.
+ * @param d2FdXdY Values of the cross partial derivative of function on
+ * every grid point.
+ * @throws DimensionMismatchException if the various arrays do not contain
+ * the expected number of elements.
+ * @throws NonMonotonicSequenceException if {@code x} or {@code y} are
+ * not strictly increasing.
+ * @throws NoDataException if any of the arrays has zero length.
+ */
+ public BicubicSplineInterpolatingFunction(double[] x,
+ double[] y,
+ double[][] f,
+ double[][] dFdX,
+ double[][] dFdY,
+ double[][] d2FdXdY)
+ throws DimensionMismatchException,
+ NoDataException,
+ NonMonotonicSequenceException {
+ this(x, y, f, dFdX, dFdY, d2FdXdY, false);
+ }
+
+ /**
+ * @param x Sample values of the x-coordinate, in increasing order.
+ * @param y Sample values of the y-coordinate, in increasing order.
+ * @param f Values of the function on every grid point.
+ * @param dFdX Values of the partial derivative of function with respect
+ * to x on every grid point.
+ * @param dFdY Values of the partial derivative of function with respect
+ * to y on every grid point.
+ * @param d2FdXdY Values of the cross partial derivative of function on
+ * every grid point.
+ * @param initializeDerivatives Whether to initialize the internal data
+ * needed for calling any of the methods that compute the partial derivatives
+ * this function.
+ * @throws DimensionMismatchException if the various arrays do not contain
+ * the expected number of elements.
+ * @throws NonMonotonicSequenceException if {@code x} or {@code y} are
+ * not strictly increasing.
+ * @throws NoDataException if any of the arrays has zero length.
+ *
+ * @see #partialDerivativeX(double,double)
+ * @see #partialDerivativeY(double,double)
+ * @see #partialDerivativeXX(double,double)
+ * @see #partialDerivativeYY(double,double)
+ * @see #partialDerivativeXY(double,double)
+ */
+ public BicubicSplineInterpolatingFunction(double[] x,
+ double[] y,
+ double[][] f,
+ double[][] dFdX,
+ double[][] dFdY,
+ double[][] d2FdXdY,
+ boolean initializeDerivatives)
+ throws DimensionMismatchException,
+ NoDataException,
+ NonMonotonicSequenceException {
+ final int xLen = x.length;
+ final int yLen = y.length;
+
+ if (xLen == 0 || yLen == 0 || f.length == 0 || f[0].length == 0) {
+ throw new NoDataException();
+ }
+ if (xLen != f.length) {
+ throw new DimensionMismatchException(xLen, f.length);
+ }
+ if (xLen != dFdX.length) {
+ throw new DimensionMismatchException(xLen, dFdX.length);
+ }
+ if (xLen != dFdY.length) {
+ throw new DimensionMismatchException(xLen, dFdY.length);
+ }
+ if (xLen != d2FdXdY.length) {
+ throw new DimensionMismatchException(xLen, d2FdXdY.length);
+ }
+
+ MathArrays.checkOrder(x);
+ MathArrays.checkOrder(y);
+
+ xval = x.clone();
+ yval = y.clone();
+
+ final int lastI = xLen - 1;
+ final int lastJ = yLen - 1;
+ splines = new BicubicSplineFunction[lastI][lastJ];
+
+ for (int i = 0; i < lastI; i++) {
+ if (f[i].length != yLen) {
+ throw new DimensionMismatchException(f[i].length, yLen);
+ }
+ if (dFdX[i].length != yLen) {
+ throw new DimensionMismatchException(dFdX[i].length, yLen);
+ }
+ if (dFdY[i].length != yLen) {
+ throw new DimensionMismatchException(dFdY[i].length, yLen);
+ }
+ if (d2FdXdY[i].length != yLen) {
+ throw new DimensionMismatchException(d2FdXdY[i].length, yLen);
+ }
+ final int ip1 = i + 1;
+ for (int j = 0; j < lastJ; j++) {
+ final int jp1 = j + 1;
+ final double[] beta = new double[] {
+ f[i][j], f[ip1][j], f[i][jp1], f[ip1][jp1],
+ dFdX[i][j], dFdX[ip1][j], dFdX[i][jp1], dFdX[ip1][jp1],
+ dFdY[i][j], dFdY[ip1][j], dFdY[i][jp1], dFdY[ip1][jp1],
+ d2FdXdY[i][j], d2FdXdY[ip1][j], d2FdXdY[i][jp1], d2FdXdY[ip1][jp1]
+ };
+
+ splines[i][j] = new BicubicSplineFunction(computeSplineCoefficients(beta),
+ initializeDerivatives);
+ }
+ }
+
+ if (initializeDerivatives) {
+ // Compute all partial derivatives.
+ partialDerivatives = new BivariateFunction[5][lastI][lastJ];
+
+ for (int i = 0; i < lastI; i++) {
+ for (int j = 0; j < lastJ; j++) {
+ final BicubicSplineFunction bcs = splines[i][j];
+ partialDerivatives[0][i][j] = bcs.partialDerivativeX();
+ partialDerivatives[1][i][j] = bcs.partialDerivativeY();
+ partialDerivatives[2][i][j] = bcs.partialDerivativeXX();
+ partialDerivatives[3][i][j] = bcs.partialDerivativeYY();
+ partialDerivatives[4][i][j] = bcs.partialDerivativeXY();
+ }
+ }
+ } else {
+ // Partial derivative methods cannot be used.
+ partialDerivatives = null;
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public double value(double x, double y)
+ throws OutOfRangeException {
+ final int i = searchIndex(x, xval);
+ final int j = searchIndex(y, yval);
+
+ final double xN = (x - xval[i]) / (xval[i + 1] - xval[i]);
+ final double yN = (y - yval[j]) / (yval[j + 1] - yval[j]);
+
+ return splines[i][j].value(xN, yN);
+ }
+
+ /**
+ * Indicates whether a point is within the interpolation range.
+ *
+ * @param x First coordinate.
+ * @param y Second coordinate.
+ * @return {@code true} if (x, y) is a valid point.
+ * @since 3.3
+ */
+ public boolean isValidPoint(double x, double y) {
+ if (x < xval[0] ||
+ x > xval[xval.length - 1] ||
+ y < yval[0] ||
+ y > yval[yval.length - 1]) {
+ return false;
+ } else {
+ return true;
+ }
+ }
+
+ /**
+ * @param x x-coordinate.
+ * @param y y-coordinate.
+ * @return the value at point (x, y) of the first partial derivative with
+ * respect to x.
+ * @throws OutOfRangeException if {@code x} (resp. {@code y}) is outside
+ * the range defined by the boundary values of {@code xval} (resp.
+ * {@code yval}).
+ * @throws NullPointerException if the internal data were not initialized
+ * (cf. {@link #BicubicSplineInterpolatingFunction(double[],double[],double[][],
+ * double[][],double[][],double[][],boolean) constructor}).
+ */
+ public double partialDerivativeX(double x, double y)
+ throws OutOfRangeException {
+ return partialDerivative(0, x, y);
+ }
+ /**
+ * @param x x-coordinate.
+ * @param y y-coordinate.
+ * @return the value at point (x, y) of the first partial derivative with
+ * respect to y.
+ * @throws OutOfRangeException if {@code x} (resp. {@code y}) is outside
+ * the range defined by the boundary values of {@code xval} (resp.
+ * {@code yval}).
+ * @throws NullPointerException if the internal data were not initialized
+ * (cf. {@link #BicubicSplineInterpolatingFunction(double[],double[],double[][],
+ * double[][],double[][],double[][],boolean) constructor}).
+ */
+ public double partialDerivativeY(double x, double y)
+ throws OutOfRangeException {
+ return partialDerivative(1, x, y);
+ }
+ /**
+ * @param x x-coordinate.
+ * @param y y-coordinate.
+ * @return the value at point (x, y) of the second partial derivative with
+ * respect to x.
+ * @throws OutOfRangeException if {@code x} (resp. {@code y}) is outside
+ * the range defined by the boundary values of {@code xval} (resp.
+ * {@code yval}).
+ * @throws NullPointerException if the internal data were not initialized
+ * (cf. {@link #BicubicSplineInterpolatingFunction(double[],double[],double[][],
+ * double[][],double[][],double[][],boolean) constructor}).
+ */
+ public double partialDerivativeXX(double x, double y)
+ throws OutOfRangeException {
+ return partialDerivative(2, x, y);
+ }
+ /**
+ * @param x x-coordinate.
+ * @param y y-coordinate.
+ * @return the value at point (x, y) of the second partial derivative with
+ * respect to y.
+ * @throws OutOfRangeException if {@code x} (resp. {@code y}) is outside
+ * the range defined by the boundary values of {@code xval} (resp.
+ * {@code yval}).
+ * @throws NullPointerException if the internal data were not initialized
+ * (cf. {@link #BicubicSplineInterpolatingFunction(double[],double[],double[][],
+ * double[][],double[][],double[][],boolean) constructor}).
+ */
+ public double partialDerivativeYY(double x, double y)
+ throws OutOfRangeException {
+ return partialDerivative(3, x, y);
+ }
+ /**
+ * @param x x-coordinate.
+ * @param y y-coordinate.
+ * @return the value at point (x, y) of the second partial cross-derivative.
+ * @throws OutOfRangeException if {@code x} (resp. {@code y}) is outside
+ * the range defined by the boundary values of {@code xval} (resp.
+ * {@code yval}).
+ * @throws NullPointerException if the internal data were not initialized
+ * (cf. {@link #BicubicSplineInterpolatingFunction(double[],double[],double[][],
+ * double[][],double[][],double[][],boolean) constructor}).
+ */
+ public double partialDerivativeXY(double x, double y)
+ throws OutOfRangeException {
+ return partialDerivative(4, x, y);
+ }
+
+ /**
+ * @param which First index in {@link #partialDerivatives}.
+ * @param x x-coordinate.
+ * @param y y-coordinate.
+ * @return the value at point (x, y) of the selected partial derivative.
+ * @throws OutOfRangeException if {@code x} (resp. {@code y}) is outside
+ * the range defined by the boundary values of {@code xval} (resp.
+ * {@code yval}).
+ * @throws NullPointerException if the internal data were not initialized
+ * (cf. {@link #BicubicSplineInterpolatingFunction(double[],double[],double[][],
+ * double[][],double[][],double[][],boolean) constructor}).
+ */
+ private double partialDerivative(int which, double x, double y)
+ throws OutOfRangeException {
+ final int i = searchIndex(x, xval);
+ final int j = searchIndex(y, yval);
+
+ final double xN = (x - xval[i]) / (xval[i + 1] - xval[i]);
+ final double yN = (y - yval[j]) / (yval[j + 1] - yval[j]);
+
+ return partialDerivatives[which][i][j].value(xN, yN);
+ }
+
+ /**
+ * @param c Coordinate.
+ * @param val Coordinate samples.
+ * @return the index in {@code val} corresponding to the interval
+ * containing {@code c}.
+ * @throws OutOfRangeException if {@code c} is out of the
+ * range defined by the boundary values of {@code val}.
+ */
+ private int searchIndex(double c, double[] val) {
+ final int r = Arrays.binarySearch(val, c);
+
+ if (r == -1 ||
+ r == -val.length - 1) {
+ throw new OutOfRangeException(c, val[0], val[val.length - 1]);
+ }
+
+ if (r < 0) {
+ // "c" in within an interpolation sub-interval: Return the
+ // index of the sample at the lower end of the sub-interval.
+ return -r - 2;
+ }
+ final int last = val.length - 1;
+ if (r == last) {
+ // "c" is the last sample of the range: Return the index
+ // of the sample at the lower end of the last sub-interval.
+ return last - 1;
+ }
+
+ // "c" is another sample point.
+ return r;
+ }
+
+ /**
+ * Compute the spline coefficients from the list of function values and
+ * function partial derivatives values at the four corners of a grid
+ * element. They must be specified in the following order:
+ * <ul>
+ * <li>f(0,0)</li>
+ * <li>f(1,0)</li>
+ * <li>f(0,1)</li>
+ * <li>f(1,1)</li>
+ * <li>f<sub>x</sub>(0,0)</li>
+ * <li>f<sub>x</sub>(1,0)</li>
+ * <li>f<sub>x</sub>(0,1)</li>
+ * <li>f<sub>x</sub>(1,1)</li>
+ * <li>f<sub>y</sub>(0,0)</li>
+ * <li>f<sub>y</sub>(1,0)</li>
+ * <li>f<sub>y</sub>(0,1)</li>
+ * <li>f<sub>y</sub>(1,1)</li>
+ * <li>f<sub>xy</sub>(0,0)</li>
+ * <li>f<sub>xy</sub>(1,0)</li>
+ * <li>f<sub>xy</sub>(0,1)</li>
+ * <li>f<sub>xy</sub>(1,1)</li>
+ * </ul>
+ * where the subscripts indicate the partial derivative with respect to
+ * the corresponding variable(s).
+ *
+ * @param beta List of function values and function partial derivatives
+ * values.
+ * @return the spline coefficients.
+ */
+ private double[] computeSplineCoefficients(double[] beta) {
+ final double[] a = new double[NUM_COEFF];
+
+ for (int i = 0; i < NUM_COEFF; i++) {
+ double result = 0;
+ final double[] row = AINV[i];
+ for (int j = 0; j < NUM_COEFF; j++) {
+ result += row[j] * beta[j];
+ }
+ a[i] = result;
+ }
+
+ return a;
+ }
+}
+
+/**
+ * 2D-spline function.
+ *
+ */
+class BicubicSplineFunction implements BivariateFunction {
+ /** Number of points. */
+ private static final short N = 4;
+ /** Coefficients */
+ private final double[][] a;
+ /** First partial derivative along x. */
+ private final BivariateFunction partialDerivativeX;
+ /** First partial derivative along y. */
+ private final BivariateFunction partialDerivativeY;
+ /** Second partial derivative along x. */
+ private final BivariateFunction partialDerivativeXX;
+ /** Second partial derivative along y. */
+ private final BivariateFunction partialDerivativeYY;
+ /** Second crossed partial derivative. */
+ private final BivariateFunction partialDerivativeXY;
+
+ /**
+ * Simple constructor.
+ *
+ * @param coeff Spline coefficients.
+ */
+ BicubicSplineFunction(double[] coeff) {
+ this(coeff, false);
+ }
+
+ /**
+ * Simple constructor.
+ *
+ * @param coeff Spline coefficients.
+ * @param initializeDerivatives Whether to initialize the internal data
+ * needed for calling any of the methods that compute the partial derivatives
+ * this function.
+ */
+ BicubicSplineFunction(double[] coeff, boolean initializeDerivatives) {
+ a = new double[N][N];
+ for (int i = 0; i < N; i++) {
+ for (int j = 0; j < N; j++) {
+ a[i][j] = coeff[i * N + j];
+ }
+ }
+
+ if (initializeDerivatives) {
+ // Compute all partial derivatives functions.
+ final double[][] aX = new double[N][N];
+ final double[][] aY = new double[N][N];
+ final double[][] aXX = new double[N][N];
+ final double[][] aYY = new double[N][N];
+ final double[][] aXY = new double[N][N];
+
+ for (int i = 0; i < N; i++) {
+ for (int j = 0; j < N; j++) {
+ final double c = a[i][j];
+ aX[i][j] = i * c;
+ aY[i][j] = j * c;
+ aXX[i][j] = (i - 1) * aX[i][j];
+ aYY[i][j] = (j - 1) * aY[i][j];
+ aXY[i][j] = j * aX[i][j];
+ }
+ }
+
+ partialDerivativeX = new BivariateFunction() {
+ /** {@inheritDoc} */
+ public double value(double x, double y) {
+ final double x2 = x * x;
+ final double[] pX = {0, 1, x, x2};
+
+ final double y2 = y * y;
+ final double y3 = y2 * y;
+ final double[] pY = {1, y, y2, y3};
+
+ return apply(pX, pY, aX);
+ }
+ };
+ partialDerivativeY = new BivariateFunction() {
+ /** {@inheritDoc} */
+ public double value(double x, double y) {
+ final double x2 = x * x;
+ final double x3 = x2 * x;
+ final double[] pX = {1, x, x2, x3};
+
+ final double y2 = y * y;
+ final double[] pY = {0, 1, y, y2};
+
+ return apply(pX, pY, aY);
+ }
+ };
+ partialDerivativeXX = new BivariateFunction() {
+ /** {@inheritDoc} */
+ public double value(double x, double y) {
+ final double[] pX = {0, 0, 1, x};
+
+ final double y2 = y * y;
+ final double y3 = y2 * y;
+ final double[] pY = {1, y, y2, y3};
+
+ return apply(pX, pY, aXX);
+ }
+ };
+ partialDerivativeYY = new BivariateFunction() {
+ /** {@inheritDoc} */
+ public double value(double x, double y) {
+ final double x2 = x * x;
+ final double x3 = x2 * x;
+ final double[] pX = {1, x, x2, x3};
+
+ final double[] pY = {0, 0, 1, y};
+
+ return apply(pX, pY, aYY);
+ }
+ };
+ partialDerivativeXY = new BivariateFunction() {
+ /** {@inheritDoc} */
+ public double value(double x, double y) {
+ final double x2 = x * x;
+ final double[] pX = {0, 1, x, x2};
+
+ final double y2 = y * y;
+ final double[] pY = {0, 1, y, y2};
+
+ return apply(pX, pY, aXY);
+ }
+ };
+ } else {
+ partialDerivativeX = null;
+ partialDerivativeY = null;
+ partialDerivativeXX = null;
+ partialDerivativeYY = null;
+ partialDerivativeXY = null;
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public double value(double x, double y) {
+ if (x < 0 || x > 1) {
+ throw new OutOfRangeException(x, 0, 1);
+ }
+ if (y < 0 || y > 1) {
+ throw new OutOfRangeException(y, 0, 1);
+ }
+
+ final double x2 = x * x;
+ final double x3 = x2 * x;
+ final double[] pX = {1, x, x2, x3};
+
+ final double y2 = y * y;
+ final double y3 = y2 * y;
+ final double[] pY = {1, y, y2, y3};
+
+ return apply(pX, pY, a);
+ }
+
+ /**
+ * Compute the value of the bicubic polynomial.
+ *
+ * @param pX Powers of the x-coordinate.
+ * @param pY Powers of the y-coordinate.
+ * @param coeff Spline coefficients.
+ * @return the interpolated value.
+ */
+ private double apply(double[] pX, double[] pY, double[][] coeff) {
+ double result = 0;
+ for (int i = 0; i < N; i++) {
+ for (int j = 0; j < N; j++) {
+ result += coeff[i][j] * pX[i] * pY[j];
+ }
+ }
+
+ return result;
+ }
+
+ /**
+ * @return the partial derivative wrt {@code x}.
+ */
+ public BivariateFunction partialDerivativeX() {
+ return partialDerivativeX;
+ }
+ /**
+ * @return the partial derivative wrt {@code y}.
+ */
+ public BivariateFunction partialDerivativeY() {
+ return partialDerivativeY;
+ }
+ /**
+ * @return the second partial derivative wrt {@code x}.
+ */
+ public BivariateFunction partialDerivativeXX() {
+ return partialDerivativeXX;
+ }
+ /**
+ * @return the second partial derivative wrt {@code y}.
+ */
+ public BivariateFunction partialDerivativeYY() {
+ return partialDerivativeYY;
+ }
+ /**
+ * @return the second partial cross-derivative.
+ */
+ public BivariateFunction partialDerivativeXY() {
+ return partialDerivativeXY;
+ }
+}
diff --git a/src/main/java/org/apache/commons/math3/analysis/interpolation/BicubicSplineInterpolator.java b/src/main/java/org/apache/commons/math3/analysis/interpolation/BicubicSplineInterpolator.java
new file mode 100644
index 0000000..09acd07
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/analysis/interpolation/BicubicSplineInterpolator.java
@@ -0,0 +1,176 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math3.analysis.interpolation;
+
+import org.apache.commons.math3.analysis.UnivariateFunction;
+import org.apache.commons.math3.analysis.polynomials.PolynomialSplineFunction;
+import org.apache.commons.math3.exception.DimensionMismatchException;
+import org.apache.commons.math3.exception.NoDataException;
+import org.apache.commons.math3.exception.NonMonotonicSequenceException;
+import org.apache.commons.math3.exception.NumberIsTooSmallException;
+import org.apache.commons.math3.util.MathArrays;
+
+/**
+ * Generates a bicubic interpolating function. Due to numerical accuracy issues this should not
+ * be used.
+ *
+ * @since 2.2
+ * @deprecated as of 3.4 replaced by {@link org.apache.commons.math3.analysis.interpolation.PiecewiseBicubicSplineInterpolator}
+ */
+@Deprecated
+public class BicubicSplineInterpolator
+ implements BivariateGridInterpolator {
+ /** Whether to initialize internal data used to compute the analytical
+ derivatives of the splines. */
+ private final boolean initializeDerivatives;
+
+ /**
+ * Default constructor.
+ * The argument {@link #BicubicSplineInterpolator(boolean) initializeDerivatives}
+ * is set to {@code false}.
+ */
+ public BicubicSplineInterpolator() {
+ this(false);
+ }
+
+ /**
+ * Creates an interpolator.
+ *
+ * @param initializeDerivatives Whether to initialize the internal data
+ * needed for calling any of the methods that compute the partial derivatives
+ * of the {@link BicubicSplineInterpolatingFunction function} returned from
+ * the call to {@link #interpolate(double[],double[],double[][]) interpolate}.
+ */
+ public BicubicSplineInterpolator(boolean initializeDerivatives) {
+ this.initializeDerivatives = initializeDerivatives;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public BicubicSplineInterpolatingFunction interpolate(final double[] xval,
+ final double[] yval,
+ final double[][] fval)
+ throws NoDataException, DimensionMismatchException,
+ NonMonotonicSequenceException, NumberIsTooSmallException {
+ if (xval.length == 0 || yval.length == 0 || fval.length == 0) {
+ throw new NoDataException();
+ }
+ if (xval.length != fval.length) {
+ throw new DimensionMismatchException(xval.length, fval.length);
+ }
+
+ MathArrays.checkOrder(xval);
+ MathArrays.checkOrder(yval);
+
+ final int xLen = xval.length;
+ final int yLen = yval.length;
+
+ // Samples (first index is y-coordinate, i.e. subarray variable is x)
+ // 0 <= i < xval.length
+ // 0 <= j < yval.length
+ // fX[j][i] = f(xval[i], yval[j])
+ final double[][] fX = new double[yLen][xLen];
+ for (int i = 0; i < xLen; i++) {
+ if (fval[i].length != yLen) {
+ throw new DimensionMismatchException(fval[i].length, yLen);
+ }
+
+ for (int j = 0; j < yLen; j++) {
+ fX[j][i] = fval[i][j];
+ }
+ }
+
+ final SplineInterpolator spInterpolator = new SplineInterpolator();
+
+ // For each line y[j] (0 <= j < yLen), construct a 1D spline with
+ // respect to variable x
+ final PolynomialSplineFunction[] ySplineX = new PolynomialSplineFunction[yLen];
+ for (int j = 0; j < yLen; j++) {
+ ySplineX[j] = spInterpolator.interpolate(xval, fX[j]);
+ }
+
+ // For each line x[i] (0 <= i < xLen), construct a 1D spline with
+ // respect to variable y generated by array fY_1[i]
+ final PolynomialSplineFunction[] xSplineY = new PolynomialSplineFunction[xLen];
+ for (int i = 0; i < xLen; i++) {
+ xSplineY[i] = spInterpolator.interpolate(yval, fval[i]);
+ }
+
+ // Partial derivatives with respect to x at the grid knots
+ final double[][] dFdX = new double[xLen][yLen];
+ for (int j = 0; j < yLen; j++) {
+ final UnivariateFunction f = ySplineX[j].derivative();
+ for (int i = 0; i < xLen; i++) {
+ dFdX[i][j] = f.value(xval[i]);
+ }
+ }
+
+ // Partial derivatives with respect to y at the grid knots
+ final double[][] dFdY = new double[xLen][yLen];
+ for (int i = 0; i < xLen; i++) {
+ final UnivariateFunction f = xSplineY[i].derivative();
+ for (int j = 0; j < yLen; j++) {
+ dFdY[i][j] = f.value(yval[j]);
+ }
+ }
+
+ // Cross partial derivatives
+ final double[][] d2FdXdY = new double[xLen][yLen];
+ for (int i = 0; i < xLen ; i++) {
+ final int nI = nextIndex(i, xLen);
+ final int pI = previousIndex(i);
+ for (int j = 0; j < yLen; j++) {
+ final int nJ = nextIndex(j, yLen);
+ final int pJ = previousIndex(j);
+ d2FdXdY[i][j] = (fval[nI][nJ] - fval[nI][pJ] -
+ fval[pI][nJ] + fval[pI][pJ]) /
+ ((xval[nI] - xval[pI]) * (yval[nJ] - yval[pJ]));
+ }
+ }
+
+ // Create the interpolating splines
+ return new BicubicSplineInterpolatingFunction(xval, yval, fval,
+ dFdX, dFdY, d2FdXdY,
+ initializeDerivatives);
+ }
+
+ /**
+ * Computes the next index of an array, clipping if necessary.
+ * It is assumed (but not checked) that {@code i >= 0}.
+ *
+ * @param i Index.
+ * @param max Upper limit of the array.
+ * @return the next index.
+ */
+ private int nextIndex(int i, int max) {
+ final int index = i + 1;
+ return index < max ? index : index - 1;
+ }
+ /**
+ * Computes the previous index of an array, clipping if necessary.
+ * It is assumed (but not checked) that {@code i} is smaller than the size
+ * of the array.
+ *
+ * @param i Index.
+ * @return the previous index.
+ */
+ private int previousIndex(int i) {
+ final int index = i - 1;
+ return index >= 0 ? index : 0;
+ }
+}
diff --git a/src/main/java/org/apache/commons/math3/analysis/interpolation/BivariateGridInterpolator.java b/src/main/java/org/apache/commons/math3/analysis/interpolation/BivariateGridInterpolator.java
new file mode 100644
index 0000000..94d75ad
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/analysis/interpolation/BivariateGridInterpolator.java
@@ -0,0 +1,51 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math3.analysis.interpolation;
+
+import org.apache.commons.math3.analysis.BivariateFunction;
+import org.apache.commons.math3.exception.DimensionMismatchException;
+import org.apache.commons.math3.exception.NoDataException;
+import org.apache.commons.math3.exception.NonMonotonicSequenceException;
+import org.apache.commons.math3.exception.NumberIsTooSmallException;
+
+/**
+ * Interface representing a bivariate real interpolating function where the
+ * sample points must be specified on a regular grid.
+ *
+ */
+public interface BivariateGridInterpolator {
+ /**
+ * Compute an interpolating function for the dataset.
+ *
+ * @param xval All the x-coordinates of the interpolation points, sorted
+ * in increasing order.
+ * @param yval All the y-coordinates of the interpolation points, sorted
+ * in increasing order.
+ * @param fval The values of the interpolation points on all the grid knots:
+ * {@code fval[i][j] = f(xval[i], yval[j])}.
+ * @return a function which interpolates the dataset.
+ * @throws NoDataException if any of the arrays has zero length.
+ * @throws DimensionMismatchException if the array lengths are inconsistent.
+ * @throws NonMonotonicSequenceException if the array is not sorted.
+ * @throws NumberIsTooSmallException if the number of points is too small for
+ * the order of the interpolation
+ */
+ BivariateFunction interpolate(double[] xval, double[] yval,
+ double[][] fval)
+ throws NoDataException, DimensionMismatchException,
+ NonMonotonicSequenceException, NumberIsTooSmallException;
+}
diff --git a/src/main/java/org/apache/commons/math3/analysis/interpolation/DividedDifferenceInterpolator.java b/src/main/java/org/apache/commons/math3/analysis/interpolation/DividedDifferenceInterpolator.java
new file mode 100644
index 0000000..86b988c
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/analysis/interpolation/DividedDifferenceInterpolator.java
@@ -0,0 +1,120 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math3.analysis.interpolation;
+
+import java.io.Serializable;
+import org.apache.commons.math3.analysis.polynomials.PolynomialFunctionLagrangeForm;
+import org.apache.commons.math3.analysis.polynomials.PolynomialFunctionNewtonForm;
+import org.apache.commons.math3.exception.DimensionMismatchException;
+import org.apache.commons.math3.exception.NumberIsTooSmallException;
+import org.apache.commons.math3.exception.NonMonotonicSequenceException;
+
+/**
+ * Implements the <a href=
+ * "http://mathworld.wolfram.com/NewtonsDividedDifferenceInterpolationFormula.html">
+ * Divided Difference Algorithm</a> for interpolation of real univariate
+ * functions. For reference, see <b>Introduction to Numerical Analysis</b>,
+ * ISBN 038795452X, chapter 2.
+ * <p>
+ * The actual code of Neville's evaluation is in PolynomialFunctionLagrangeForm,
+ * this class provides an easy-to-use interface to it.</p>
+ *
+ * @since 1.2
+ */
+public class DividedDifferenceInterpolator
+ implements UnivariateInterpolator, Serializable {
+ /** serializable version identifier */
+ private static final long serialVersionUID = 107049519551235069L;
+
+ /**
+ * Compute an interpolating function for the dataset.
+ *
+ * @param x Interpolating points array.
+ * @param y Interpolating values array.
+ * @return a function which interpolates the dataset.
+ * @throws DimensionMismatchException if the array lengths are different.
+ * @throws NumberIsTooSmallException if the number of points is less than 2.
+ * @throws NonMonotonicSequenceException if {@code x} is not sorted in
+ * strictly increasing order.
+ */
+ public PolynomialFunctionNewtonForm interpolate(double x[], double y[])
+ throws DimensionMismatchException,
+ NumberIsTooSmallException,
+ NonMonotonicSequenceException {
+ /**
+ * a[] and c[] are defined in the general formula of Newton form:
+ * p(x) = a[0] + a[1](x-c[0]) + a[2](x-c[0])(x-c[1]) + ... +
+ * a[n](x-c[0])(x-c[1])...(x-c[n-1])
+ */
+ PolynomialFunctionLagrangeForm.verifyInterpolationArray(x, y, true);
+
+ /**
+ * When used for interpolation, the Newton form formula becomes
+ * p(x) = f[x0] + f[x0,x1](x-x0) + f[x0,x1,x2](x-x0)(x-x1) + ... +
+ * f[x0,x1,...,x[n-1]](x-x0)(x-x1)...(x-x[n-2])
+ * Therefore, a[k] = f[x0,x1,...,xk], c[k] = x[k].
+ * <p>
+ * Note x[], y[], a[] have the same length but c[]'s size is one less.</p>
+ */
+ final double[] c = new double[x.length-1];
+ System.arraycopy(x, 0, c, 0, c.length);
+
+ final double[] a = computeDividedDifference(x, y);
+ return new PolynomialFunctionNewtonForm(a, c);
+ }
+
+ /**
+ * Return a copy of the divided difference array.
+ * <p>
+ * The divided difference array is defined recursively by <pre>
+ * f[x0] = f(x0)
+ * f[x0,x1,...,xk] = (f[x1,...,xk] - f[x0,...,x[k-1]]) / (xk - x0)
+ * </pre>
+ * <p>
+ * The computational complexity is \(O(n^2)\) where \(n\) is the common
+ * length of {@code x} and {@code y}.</p>
+ *
+ * @param x Interpolating points array.
+ * @param y Interpolating values array.
+ * @return a fresh copy of the divided difference array.
+ * @throws DimensionMismatchException if the array lengths are different.
+ * @throws NumberIsTooSmallException if the number of points is less than 2.
+ * @throws NonMonotonicSequenceException
+ * if {@code x} is not sorted in strictly increasing order.
+ */
+ protected static double[] computeDividedDifference(final double x[], final double y[])
+ throws DimensionMismatchException,
+ NumberIsTooSmallException,
+ NonMonotonicSequenceException {
+ PolynomialFunctionLagrangeForm.verifyInterpolationArray(x, y, true);
+
+ final double[] divdiff = y.clone(); // initialization
+
+ final int n = x.length;
+ final double[] a = new double [n];
+ a[0] = divdiff[0];
+ for (int i = 1; i < n; i++) {
+ for (int j = 0; j < n-i; j++) {
+ final double denominator = x[j+i] - x[j];
+ divdiff[j] = (divdiff[j+1] - divdiff[j]) / denominator;
+ }
+ a[i] = divdiff[0];
+ }
+
+ return a;
+ }
+}
diff --git a/src/main/java/org/apache/commons/math3/analysis/interpolation/FieldHermiteInterpolator.java b/src/main/java/org/apache/commons/math3/analysis/interpolation/FieldHermiteInterpolator.java
new file mode 100644
index 0000000..9125b83
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/analysis/interpolation/FieldHermiteInterpolator.java
@@ -0,0 +1,209 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math3.analysis.interpolation;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.apache.commons.math3.FieldElement;
+import org.apache.commons.math3.exception.DimensionMismatchException;
+import org.apache.commons.math3.exception.MathArithmeticException;
+import org.apache.commons.math3.exception.NoDataException;
+import org.apache.commons.math3.exception.NullArgumentException;
+import org.apache.commons.math3.exception.ZeroException;
+import org.apache.commons.math3.exception.util.LocalizedFormats;
+import org.apache.commons.math3.util.MathArrays;
+import org.apache.commons.math3.util.MathUtils;
+
+/** Polynomial interpolator using both sample values and sample derivatives.
+ * <p>
+ * The interpolation polynomials match all sample points, including both values
+ * and provided derivatives. There is one polynomial for each component of
+ * the values vector. All polynomials have the same degree. The degree of the
+ * polynomials depends on the number of points and number of derivatives at each
+ * point. For example the interpolation polynomials for n sample points without
+ * any derivatives all have degree n-1. The interpolation polynomials for n
+ * sample points with the two extreme points having value and first derivative
+ * and the remaining points having value only all have degree n+1. The
+ * interpolation polynomial for n sample points with value, first and second
+ * derivative for all points all have degree 3n-1.
+ * </p>
+ *
+ * @param <T> Type of the field elements.
+ *
+ * @since 3.2
+ */
+public class FieldHermiteInterpolator<T extends FieldElement<T>> {
+
+ /** Sample abscissae. */
+ private final List<T> abscissae;
+
+ /** Top diagonal of the divided differences array. */
+ private final List<T[]> topDiagonal;
+
+ /** Bottom diagonal of the divided differences array. */
+ private final List<T[]> bottomDiagonal;
+
+ /** Create an empty interpolator.
+ */
+ public FieldHermiteInterpolator() {
+ this.abscissae = new ArrayList<T>();
+ this.topDiagonal = new ArrayList<T[]>();
+ this.bottomDiagonal = new ArrayList<T[]>();
+ }
+
+ /** Add a sample point.
+ * <p>
+ * This method must be called once for each sample point. It is allowed to
+ * mix some calls with values only with calls with values and first
+ * derivatives.
+ * </p>
+ * <p>
+ * The point abscissae for all calls <em>must</em> be different.
+ * </p>
+ * @param x abscissa of the sample point
+ * @param value value and derivatives of the sample point
+ * (if only one row is passed, it is the value, if two rows are
+ * passed the first one is the value and the second the derivative
+ * and so on)
+ * @exception ZeroException if the abscissa difference between added point
+ * and a previous point is zero (i.e. the two points are at same abscissa)
+ * @exception MathArithmeticException if the number of derivatives is larger
+ * than 20, which prevents computation of a factorial
+ * @throws DimensionMismatchException if derivative structures are inconsistent
+ * @throws NullArgumentException if x is null
+ */
+ public void addSamplePoint(final T x, final T[] ... value)
+ throws ZeroException, MathArithmeticException,
+ DimensionMismatchException, NullArgumentException {
+
+ MathUtils.checkNotNull(x);
+ T factorial = x.getField().getOne();
+ for (int i = 0; i < value.length; ++i) {
+
+ final T[] y = value[i].clone();
+ if (i > 1) {
+ factorial = factorial.multiply(i);
+ final T inv = factorial.reciprocal();
+ for (int j = 0; j < y.length; ++j) {
+ y[j] = y[j].multiply(inv);
+ }
+ }
+
+ // update the bottom diagonal of the divided differences array
+ final int n = abscissae.size();
+ bottomDiagonal.add(n - i, y);
+ T[] bottom0 = y;
+ for (int j = i; j < n; ++j) {
+ final T[] bottom1 = bottomDiagonal.get(n - (j + 1));
+ if (x.equals(abscissae.get(n - (j + 1)))) {
+ throw new ZeroException(LocalizedFormats.DUPLICATED_ABSCISSA_DIVISION_BY_ZERO, x);
+ }
+ final T inv = x.subtract(abscissae.get(n - (j + 1))).reciprocal();
+ for (int k = 0; k < y.length; ++k) {
+ bottom1[k] = inv.multiply(bottom0[k].subtract(bottom1[k]));
+ }
+ bottom0 = bottom1;
+ }
+
+ // update the top diagonal of the divided differences array
+ topDiagonal.add(bottom0.clone());
+
+ // update the abscissae array
+ abscissae.add(x);
+
+ }
+
+ }
+
+ /** Interpolate value at a specified abscissa.
+ * @param x interpolation abscissa
+ * @return interpolated value
+ * @exception NoDataException if sample is empty
+ * @throws NullArgumentException if x is null
+ */
+ public T[] value(T x) throws NoDataException, NullArgumentException {
+
+ // safety check
+ MathUtils.checkNotNull(x);
+ if (abscissae.isEmpty()) {
+ throw new NoDataException(LocalizedFormats.EMPTY_INTERPOLATION_SAMPLE);
+ }
+
+ final T[] value = MathArrays.buildArray(x.getField(), topDiagonal.get(0).length);
+ T valueCoeff = x.getField().getOne();
+ for (int i = 0; i < topDiagonal.size(); ++i) {
+ T[] dividedDifference = topDiagonal.get(i);
+ for (int k = 0; k < value.length; ++k) {
+ value[k] = value[k].add(dividedDifference[k].multiply(valueCoeff));
+ }
+ final T deltaX = x.subtract(abscissae.get(i));
+ valueCoeff = valueCoeff.multiply(deltaX);
+ }
+
+ return value;
+
+ }
+
+ /** Interpolate value and first derivatives at a specified abscissa.
+ * @param x interpolation abscissa
+ * @param order maximum derivation order
+ * @return interpolated value and derivatives (value in row 0,
+ * 1<sup>st</sup> derivative in row 1, ... n<sup>th</sup> derivative in row n)
+ * @exception NoDataException if sample is empty
+ * @throws NullArgumentException if x is null
+ */
+ public T[][] derivatives(T x, int order) throws NoDataException, NullArgumentException {
+
+ // safety check
+ MathUtils.checkNotNull(x);
+ if (abscissae.isEmpty()) {
+ throw new NoDataException(LocalizedFormats.EMPTY_INTERPOLATION_SAMPLE);
+ }
+
+ final T zero = x.getField().getZero();
+ final T one = x.getField().getOne();
+ final T[] tj = MathArrays.buildArray(x.getField(), order + 1);
+ tj[0] = zero;
+ for (int i = 0; i < order; ++i) {
+ tj[i + 1] = tj[i].add(one);
+ }
+
+ final T[][] derivatives =
+ MathArrays.buildArray(x.getField(), order + 1, topDiagonal.get(0).length);
+ final T[] valueCoeff = MathArrays.buildArray(x.getField(), order + 1);
+ valueCoeff[0] = x.getField().getOne();
+ for (int i = 0; i < topDiagonal.size(); ++i) {
+ T[] dividedDifference = topDiagonal.get(i);
+ final T deltaX = x.subtract(abscissae.get(i));
+ for (int j = order; j >= 0; --j) {
+ for (int k = 0; k < derivatives[j].length; ++k) {
+ derivatives[j][k] =
+ derivatives[j][k].add(dividedDifference[k].multiply(valueCoeff[j]));
+ }
+ valueCoeff[j] = valueCoeff[j].multiply(deltaX);
+ if (j > 0) {
+ valueCoeff[j] = valueCoeff[j].add(tj[j].multiply(valueCoeff[j - 1]));
+ }
+ }
+ }
+
+ return derivatives;
+
+ }
+
+}
diff --git a/src/main/java/org/apache/commons/math3/analysis/interpolation/HermiteInterpolator.java b/src/main/java/org/apache/commons/math3/analysis/interpolation/HermiteInterpolator.java
new file mode 100644
index 0000000..15ed322
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/analysis/interpolation/HermiteInterpolator.java
@@ -0,0 +1,239 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math3.analysis.interpolation;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+import org.apache.commons.math3.analysis.differentiation.DerivativeStructure;
+import org.apache.commons.math3.analysis.differentiation.UnivariateDifferentiableVectorFunction;
+import org.apache.commons.math3.analysis.polynomials.PolynomialFunction;
+import org.apache.commons.math3.exception.MathArithmeticException;
+import org.apache.commons.math3.exception.NoDataException;
+import org.apache.commons.math3.exception.ZeroException;
+import org.apache.commons.math3.exception.util.LocalizedFormats;
+import org.apache.commons.math3.util.CombinatoricsUtils;
+
+/** Polynomial interpolator using both sample values and sample derivatives.
+ * <p>
+ * The interpolation polynomials match all sample points, including both values
+ * and provided derivatives. There is one polynomial for each component of
+ * the values vector. All polynomials have the same degree. The degree of the
+ * polynomials depends on the number of points and number of derivatives at each
+ * point. For example the interpolation polynomials for n sample points without
+ * any derivatives all have degree n-1. The interpolation polynomials for n
+ * sample points with the two extreme points having value and first derivative
+ * and the remaining points having value only all have degree n+1. The
+ * interpolation polynomial for n sample points with value, first and second
+ * derivative for all points all have degree 3n-1.
+ * </p>
+ *
+ * @since 3.1
+ */
+public class HermiteInterpolator implements UnivariateDifferentiableVectorFunction {
+
+ /** Sample abscissae. */
+ private final List<Double> abscissae;
+
+ /** Top diagonal of the divided differences array. */
+ private final List<double[]> topDiagonal;
+
+ /** Bottom diagonal of the divided differences array. */
+ private final List<double[]> bottomDiagonal;
+
+ /** Create an empty interpolator.
+ */
+ public HermiteInterpolator() {
+ this.abscissae = new ArrayList<Double>();
+ this.topDiagonal = new ArrayList<double[]>();
+ this.bottomDiagonal = new ArrayList<double[]>();
+ }
+
+ /** Add a sample point.
+ * <p>
+ * This method must be called once for each sample point. It is allowed to
+ * mix some calls with values only with calls with values and first
+ * derivatives.
+ * </p>
+ * <p>
+ * The point abscissae for all calls <em>must</em> be different.
+ * </p>
+ * @param x abscissa of the sample point
+ * @param value value and derivatives of the sample point
+ * (if only one row is passed, it is the value, if two rows are
+ * passed the first one is the value and the second the derivative
+ * and so on)
+ * @exception ZeroException if the abscissa difference between added point
+ * and a previous point is zero (i.e. the two points are at same abscissa)
+ * @exception MathArithmeticException if the number of derivatives is larger
+ * than 20, which prevents computation of a factorial
+ */
+ public void addSamplePoint(final double x, final double[] ... value)
+ throws ZeroException, MathArithmeticException {
+
+ for (int i = 0; i < value.length; ++i) {
+
+ final double[] y = value[i].clone();
+ if (i > 1) {
+ double inv = 1.0 / CombinatoricsUtils.factorial(i);
+ for (int j = 0; j < y.length; ++j) {
+ y[j] *= inv;
+ }
+ }
+
+ // update the bottom diagonal of the divided differences array
+ final int n = abscissae.size();
+ bottomDiagonal.add(n - i, y);
+ double[] bottom0 = y;
+ for (int j = i; j < n; ++j) {
+ final double[] bottom1 = bottomDiagonal.get(n - (j + 1));
+ final double inv = 1.0 / (x - abscissae.get(n - (j + 1)));
+ if (Double.isInfinite(inv)) {
+ throw new ZeroException(LocalizedFormats.DUPLICATED_ABSCISSA_DIVISION_BY_ZERO, x);
+ }
+ for (int k = 0; k < y.length; ++k) {
+ bottom1[k] = inv * (bottom0[k] - bottom1[k]);
+ }
+ bottom0 = bottom1;
+ }
+
+ // update the top diagonal of the divided differences array
+ topDiagonal.add(bottom0.clone());
+
+ // update the abscissae array
+ abscissae.add(x);
+
+ }
+
+ }
+
+ /** Compute the interpolation polynomials.
+ * @return interpolation polynomials array
+ * @exception NoDataException if sample is empty
+ */
+ public PolynomialFunction[] getPolynomials()
+ throws NoDataException {
+
+ // safety check
+ checkInterpolation();
+
+ // iteration initialization
+ final PolynomialFunction zero = polynomial(0);
+ PolynomialFunction[] polynomials = new PolynomialFunction[topDiagonal.get(0).length];
+ for (int i = 0; i < polynomials.length; ++i) {
+ polynomials[i] = zero;
+ }
+ PolynomialFunction coeff = polynomial(1);
+
+ // build the polynomials by iterating on the top diagonal of the divided differences array
+ for (int i = 0; i < topDiagonal.size(); ++i) {
+ double[] tdi = topDiagonal.get(i);
+ for (int k = 0; k < polynomials.length; ++k) {
+ polynomials[k] = polynomials[k].add(coeff.multiply(polynomial(tdi[k])));
+ }
+ coeff = coeff.multiply(polynomial(-abscissae.get(i), 1.0));
+ }
+
+ return polynomials;
+
+ }
+
+ /** Interpolate value at a specified abscissa.
+ * <p>
+ * Calling this method is equivalent to call the {@link PolynomialFunction#value(double)
+ * value} methods of all polynomials returned by {@link #getPolynomials() getPolynomials},
+ * except it does not build the intermediate polynomials, so this method is faster and
+ * numerically more stable.
+ * </p>
+ * @param x interpolation abscissa
+ * @return interpolated value
+ * @exception NoDataException if sample is empty
+ */
+ public double[] value(double x)
+ throws NoDataException {
+
+ // safety check
+ checkInterpolation();
+
+ final double[] value = new double[topDiagonal.get(0).length];
+ double valueCoeff = 1;
+ for (int i = 0; i < topDiagonal.size(); ++i) {
+ double[] dividedDifference = topDiagonal.get(i);
+ for (int k = 0; k < value.length; ++k) {
+ value[k] += dividedDifference[k] * valueCoeff;
+ }
+ final double deltaX = x - abscissae.get(i);
+ valueCoeff *= deltaX;
+ }
+
+ return value;
+
+ }
+
+ /** Interpolate value at a specified abscissa.
+ * <p>
+ * Calling this method is equivalent to call the {@link
+ * PolynomialFunction#value(DerivativeStructure) value} methods of all polynomials
+ * returned by {@link #getPolynomials() getPolynomials}, except it does not build the
+ * intermediate polynomials, so this method is faster and numerically more stable.
+ * </p>
+ * @param x interpolation abscissa
+ * @return interpolated value
+ * @exception NoDataException if sample is empty
+ */
+ public DerivativeStructure[] value(final DerivativeStructure x)
+ throws NoDataException {
+
+ // safety check
+ checkInterpolation();
+
+ final DerivativeStructure[] value = new DerivativeStructure[topDiagonal.get(0).length];
+ Arrays.fill(value, x.getField().getZero());
+ DerivativeStructure valueCoeff = x.getField().getOne();
+ for (int i = 0; i < topDiagonal.size(); ++i) {
+ double[] dividedDifference = topDiagonal.get(i);
+ for (int k = 0; k < value.length; ++k) {
+ value[k] = value[k].add(valueCoeff.multiply(dividedDifference[k]));
+ }
+ final DerivativeStructure deltaX = x.subtract(abscissae.get(i));
+ valueCoeff = valueCoeff.multiply(deltaX);
+ }
+
+ return value;
+
+ }
+
+ /** Check interpolation can be performed.
+ * @exception NoDataException if interpolation cannot be performed
+ * because sample is empty
+ */
+ private void checkInterpolation() throws NoDataException {
+ if (abscissae.isEmpty()) {
+ throw new NoDataException(LocalizedFormats.EMPTY_INTERPOLATION_SAMPLE);
+ }
+ }
+
+ /** Create a polynomial from its coefficients.
+ * @param c polynomials coefficients
+ * @return polynomial
+ */
+ private PolynomialFunction polynomial(double ... c) {
+ return new PolynomialFunction(c);
+ }
+
+}
diff --git a/src/main/java/org/apache/commons/math3/analysis/interpolation/InterpolatingMicrosphere.java b/src/main/java/org/apache/commons/math3/analysis/interpolation/InterpolatingMicrosphere.java
new file mode 100644
index 0000000..dc600bd
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/analysis/interpolation/InterpolatingMicrosphere.java
@@ -0,0 +1,385 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math3.analysis.interpolation;
+
+import java.util.List;
+import java.util.ArrayList;
+import org.apache.commons.math3.random.UnitSphereRandomVectorGenerator;
+import org.apache.commons.math3.exception.DimensionMismatchException;
+import org.apache.commons.math3.exception.NotPositiveException;
+import org.apache.commons.math3.exception.NotStrictlyPositiveException;
+import org.apache.commons.math3.exception.MaxCountExceededException;
+import org.apache.commons.math3.exception.OutOfRangeException;
+import org.apache.commons.math3.util.FastMath;
+import org.apache.commons.math3.util.MathArrays;
+
+/**
+ * Utility class for the {@link MicrosphereProjectionInterpolator} algorithm.
+ *
+ * @since 3.6
+ */
+public class InterpolatingMicrosphere {
+ /** Microsphere. */
+ private final List<Facet> microsphere;
+ /** Microsphere data. */
+ private final List<FacetData> microsphereData;
+ /** Space dimension. */
+ private final int dimension;
+ /** Number of surface elements. */
+ private final int size;
+ /** Maximum fraction of the facets that can be dark. */
+ private final double maxDarkFraction;
+ /** Lowest non-zero illumination. */
+ private final double darkThreshold;
+ /** Background value. */
+ private final double background;
+
+ /**
+ * Create an unitialiazed sphere.
+ * Sub-classes are responsible for calling the {@code add(double[]) add}
+ * method in order to initialize all the sphere's facets.
+ *
+ * @param dimension Dimension of the data space.
+ * @param size Number of surface elements of the sphere.
+ * @param maxDarkFraction Maximum fraction of the facets that can be dark.
+ * If the fraction of "non-illuminated" facets is larger, no estimation
+ * of the value will be performed, and the {@code background} value will
+ * be returned instead.
+ * @param darkThreshold Value of the illumination below which a facet is
+ * considered dark.
+ * @param background Value returned when the {@code maxDarkFraction}
+ * threshold is exceeded.
+ * @throws NotStrictlyPositiveException if {@code dimension <= 0}
+ * or {@code size <= 0}.
+ * @throws NotPositiveException if {@code darkThreshold < 0}.
+ * @throws OutOfRangeException if {@code maxDarkFraction} does not
+ * belong to the interval {@code [0, 1]}.
+ */
+ protected InterpolatingMicrosphere(int dimension,
+ int size,
+ double maxDarkFraction,
+ double darkThreshold,
+ double background) {
+ if (dimension <= 0) {
+ throw new NotStrictlyPositiveException(dimension);
+ }
+ if (size <= 0) {
+ throw new NotStrictlyPositiveException(size);
+ }
+ if (maxDarkFraction < 0 ||
+ maxDarkFraction > 1) {
+ throw new OutOfRangeException(maxDarkFraction, 0, 1);
+ }
+ if (darkThreshold < 0) {
+ throw new NotPositiveException(darkThreshold);
+ }
+
+ this.dimension = dimension;
+ this.size = size;
+ this.maxDarkFraction = maxDarkFraction;
+ this.darkThreshold = darkThreshold;
+ this.background = background;
+ microsphere = new ArrayList<Facet>(size);
+ microsphereData = new ArrayList<FacetData>(size);
+ }
+
+ /**
+ * Create a sphere from randomly sampled vectors.
+ *
+ * @param dimension Dimension of the data space.
+ * @param size Number of surface elements of the sphere.
+ * @param rand Unit vector generator for creating the microsphere.
+ * @param maxDarkFraction Maximum fraction of the facets that can be dark.
+ * If the fraction of "non-illuminated" facets is larger, no estimation
+ * of the value will be performed, and the {@code background} value will
+ * be returned instead.
+ * @param darkThreshold Value of the illumination below which a facet
+ * is considered dark.
+ * @param background Value returned when the {@code maxDarkFraction}
+ * threshold is exceeded.
+ * @throws DimensionMismatchException if the size of the generated
+ * vectors does not match the dimension set in the constructor.
+ * @throws NotStrictlyPositiveException if {@code dimension <= 0}
+ * or {@code size <= 0}.
+ * @throws NotPositiveException if {@code darkThreshold < 0}.
+ * @throws OutOfRangeException if {@code maxDarkFraction} does not
+ * belong to the interval {@code [0, 1]}.
+ */
+ public InterpolatingMicrosphere(int dimension,
+ int size,
+ double maxDarkFraction,
+ double darkThreshold,
+ double background,
+ UnitSphereRandomVectorGenerator rand) {
+ this(dimension, size, maxDarkFraction, darkThreshold, background);
+
+ // Generate the microsphere normals, assuming that a number of
+ // randomly generated normals will represent a sphere.
+ for (int i = 0; i < size; i++) {
+ add(rand.nextVector(), false);
+ }
+ }
+
+ /**
+ * Copy constructor.
+ *
+ * @param other Instance to copy.
+ */
+ protected InterpolatingMicrosphere(InterpolatingMicrosphere other) {
+ dimension = other.dimension;
+ size = other.size;
+ maxDarkFraction = other.maxDarkFraction;
+ darkThreshold = other.darkThreshold;
+ background = other.background;
+
+ // Field can be shared.
+ microsphere = other.microsphere;
+
+ // Field must be copied.
+ microsphereData = new ArrayList<FacetData>(size);
+ for (FacetData fd : other.microsphereData) {
+ microsphereData.add(new FacetData(fd.illumination(), fd.sample()));
+ }
+ }
+
+ /**
+ * Perform a copy.
+ *
+ * @return a copy of this instance.
+ */
+ public InterpolatingMicrosphere copy() {
+ return new InterpolatingMicrosphere(this);
+ }
+
+ /**
+ * Get the space dimensionality.
+ *
+ * @return the number of space dimensions.
+ */
+ public int getDimension() {
+ return dimension;
+ }
+
+ /**
+ * Get the size of the sphere.
+ *
+ * @return the number of surface elements of the microspshere.
+ */
+ public int getSize() {
+ return size;
+ }
+
+ /**
+ * Estimate the value at the requested location.
+ * This microsphere is placed at the given {@code point}, contribution
+ * of the given {@code samplePoints} to each sphere facet is computed
+ * (illumination) and the interpolation is performed (integration of
+ * the illumination).
+ *
+ * @param point Interpolation point.
+ * @param samplePoints Sampling data points.
+ * @param sampleValues Sampling data values at the corresponding
+ * {@code samplePoints}.
+ * @param exponent Exponent used in the power law that computes
+ * the weights (distance dimming factor) of the sample data.
+ * @param noInterpolationTolerance When the distance between the
+ * {@code point} and one of the {@code samplePoints} is less than
+ * this value, no interpolation will be performed, and the value
+ * of the sample will just be returned.
+ * @return the estimated value at the given {@code point}.
+ * @throws NotPositiveException if {@code exponent < 0}.
+ */
+ public double value(double[] point,
+ double[][] samplePoints,
+ double[] sampleValues,
+ double exponent,
+ double noInterpolationTolerance) {
+ if (exponent < 0) {
+ throw new NotPositiveException(exponent);
+ }
+
+ clear();
+
+ // Contribution of each sample point to the illumination of the
+ // microsphere's facets.
+ final int numSamples = samplePoints.length;
+ for (int i = 0; i < numSamples; i++) {
+ // Vector between interpolation point and current sample point.
+ final double[] diff = MathArrays.ebeSubtract(samplePoints[i], point);
+ final double diffNorm = MathArrays.safeNorm(diff);
+
+ if (FastMath.abs(diffNorm) < noInterpolationTolerance) {
+ // No need to interpolate, as the interpolation point is
+ // actually (very close to) one of the sampled points.
+ return sampleValues[i];
+ }
+
+ final double weight = FastMath.pow(diffNorm, -exponent);
+ illuminate(diff, sampleValues[i], weight);
+ }
+
+ return interpolate();
+ }
+
+ /**
+ * Replace {@code i}-th facet of the microsphere.
+ * Method for initializing the microsphere facets.
+ *
+ * @param normal Facet's normal vector.
+ * @param copy Whether to copy the given array.
+ * @throws DimensionMismatchException if the length of {@code n}
+ * does not match the space dimension.
+ * @throws MaxCountExceededException if the method has been called
+ * more times than the size of the sphere.
+ */
+ protected void add(double[] normal,
+ boolean copy) {
+ if (microsphere.size() >= size) {
+ throw new MaxCountExceededException(size);
+ }
+ if (normal.length > dimension) {
+ throw new DimensionMismatchException(normal.length, dimension);
+ }
+
+ microsphere.add(new Facet(copy ? normal.clone() : normal));
+ microsphereData.add(new FacetData(0d, 0d));
+ }
+
+ /**
+ * Interpolation.
+ *
+ * @return the value estimated from the current illumination of the
+ * microsphere.
+ */
+ private double interpolate() {
+ // Number of non-illuminated facets.
+ int darkCount = 0;
+
+ double value = 0;
+ double totalWeight = 0;
+ for (FacetData fd : microsphereData) {
+ final double iV = fd.illumination();
+ if (iV != 0d) {
+ value += iV * fd.sample();
+ totalWeight += iV;
+ } else {
+ ++darkCount;
+ }
+ }
+
+ final double darkFraction = darkCount / (double) size;
+
+ return darkFraction <= maxDarkFraction ?
+ value / totalWeight :
+ background;
+ }
+
+ /**
+ * Illumination.
+ *
+ * @param sampleDirection Vector whose origin is at the interpolation
+ * point and tail is at the sample location.
+ * @param sampleValue Data value of the sample.
+ * @param weight Weight.
+ */
+ private void illuminate(double[] sampleDirection,
+ double sampleValue,
+ double weight) {
+ for (int i = 0; i < size; i++) {
+ final double[] n = microsphere.get(i).getNormal();
+ final double cos = MathArrays.cosAngle(n, sampleDirection);
+
+ if (cos > 0) {
+ final double illumination = cos * weight;
+
+ if (illumination > darkThreshold &&
+ illumination > microsphereData.get(i).illumination()) {
+ microsphereData.set(i, new FacetData(illumination, sampleValue));
+ }
+ }
+ }
+ }
+
+ /**
+ * Reset the all the {@link Facet facets} data to zero.
+ */
+ private void clear() {
+ for (int i = 0; i < size; i++) {
+ microsphereData.set(i, new FacetData(0d, 0d));
+ }
+ }
+
+ /**
+ * Microsphere "facet" (surface element).
+ */
+ private static class Facet {
+ /** Normal vector characterizing a surface element. */
+ private final double[] normal;
+
+ /**
+ * @param n Normal vector characterizing a surface element
+ * of the microsphere. No copy is made.
+ */
+ Facet(double[] n) {
+ normal = n;
+ }
+
+ /**
+ * Return a reference to the vector normal to this facet.
+ *
+ * @return the normal vector.
+ */
+ public double[] getNormal() {
+ return normal;
+ }
+ }
+
+ /**
+ * Data associated with each {@link Facet}.
+ */
+ private static class FacetData {
+ /** Illumination received from the sample. */
+ private final double illumination;
+ /** Data value of the sample. */
+ private final double sample;
+
+ /**
+ * @param illumination Illumination.
+ * @param sample Data value.
+ */
+ FacetData(double illumination, double sample) {
+ this.illumination = illumination;
+ this.sample = sample;
+ }
+
+ /**
+ * Get the illumination.
+ * @return the illumination.
+ */
+ public double illumination() {
+ return illumination;
+ }
+
+ /**
+ * Get the data value.
+ * @return the data value.
+ */
+ public double sample() {
+ return sample;
+ }
+ }
+}
diff --git a/src/main/java/org/apache/commons/math3/analysis/interpolation/InterpolatingMicrosphere2D.java b/src/main/java/org/apache/commons/math3/analysis/interpolation/InterpolatingMicrosphere2D.java
new file mode 100644
index 0000000..fdc01b2
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/analysis/interpolation/InterpolatingMicrosphere2D.java
@@ -0,0 +1,87 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math3.analysis.interpolation;
+
+import org.apache.commons.math3.util.FastMath;
+import org.apache.commons.math3.util.MathUtils;
+
+/**
+ * Utility class for the {@link MicrosphereProjectionInterpolator} algorithm.
+ * For 2D interpolation, this class constructs the microsphere as a series of
+ * evenly spaced facets (rather than generating random normals as in the
+ * base implementation).
+ *
+ * @since 3.6
+ */
+public class InterpolatingMicrosphere2D extends InterpolatingMicrosphere {
+ /** Space dimension. */
+ private static final int DIMENSION = 2;
+
+ /**
+ * Create a sphere from vectors regularly sampled around a circle.
+ *
+ * @param size Number of surface elements of the sphere.
+ * @param maxDarkFraction Maximum fraction of the facets that can be dark.
+ * If the fraction of "non-illuminated" facets is larger, no estimation
+ * of the value will be performed, and the {@code background} value will
+ * be returned instead.
+ * @param darkThreshold Value of the illumination below which a facet is
+ * considered dark.
+ * @param background Value returned when the {@code maxDarkFraction}
+ * threshold is exceeded.
+ * @throws org.apache.commons.math3.exception.NotStrictlyPositiveException
+ * if {@code size <= 0}.
+ * @throws org.apache.commons.math3.exception.NotPositiveException if
+ * {@code darkThreshold < 0}.
+ * @throws org.apache.commons.math3.exception.OutOfRangeException if
+ * {@code maxDarkFraction} does not belong to the interval {@code [0, 1]}.
+ */
+ public InterpolatingMicrosphere2D(int size,
+ double maxDarkFraction,
+ double darkThreshold,
+ double background) {
+ super(DIMENSION, size, maxDarkFraction, darkThreshold, background);
+
+ // Generate the microsphere normals.
+ for (int i = 0; i < size; i++) {
+ final double angle = i * MathUtils.TWO_PI / size;
+
+ add(new double[] { FastMath.cos(angle),
+ FastMath.sin(angle) },
+ false);
+ }
+ }
+
+ /**
+ * Copy constructor.
+ *
+ * @param other Instance to copy.
+ */
+ protected InterpolatingMicrosphere2D(InterpolatingMicrosphere2D other) {
+ super(other);
+ }
+
+ /**
+ * Perform a copy.
+ *
+ * @return a copy of this instance.
+ */
+ @Override
+ public InterpolatingMicrosphere2D copy() {
+ return new InterpolatingMicrosphere2D(this);
+ }
+}
diff --git a/src/main/java/org/apache/commons/math3/analysis/interpolation/LinearInterpolator.java b/src/main/java/org/apache/commons/math3/analysis/interpolation/LinearInterpolator.java
new file mode 100644
index 0000000..7e0e69b
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/analysis/interpolation/LinearInterpolator.java
@@ -0,0 +1,79 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math3.analysis.interpolation;
+
+import org.apache.commons.math3.analysis.polynomials.PolynomialFunction;
+import org.apache.commons.math3.analysis.polynomials.PolynomialSplineFunction;
+import org.apache.commons.math3.util.MathArrays;
+import org.apache.commons.math3.exception.DimensionMismatchException;
+import org.apache.commons.math3.exception.NumberIsTooSmallException;
+import org.apache.commons.math3.exception.NonMonotonicSequenceException;
+import org.apache.commons.math3.exception.util.LocalizedFormats;
+
+/**
+ * Implements a linear function for interpolation of real univariate functions.
+ *
+ */
+public class LinearInterpolator implements UnivariateInterpolator {
+ /**
+ * Computes a linear interpolating function for the data set.
+ *
+ * @param x the arguments for the interpolation points
+ * @param y the values for the interpolation points
+ * @return a function which interpolates the data set
+ * @throws DimensionMismatchException if {@code x} and {@code y}
+ * have different sizes.
+ * @throws NonMonotonicSequenceException if {@code x} is not sorted in
+ * strict increasing order.
+ * @throws NumberIsTooSmallException if the size of {@code x} is smaller
+ * than 2.
+ */
+ public PolynomialSplineFunction interpolate(double x[], double y[])
+ throws DimensionMismatchException,
+ NumberIsTooSmallException,
+ NonMonotonicSequenceException {
+ if (x.length != y.length) {
+ throw new DimensionMismatchException(x.length, y.length);
+ }
+
+ if (x.length < 2) {
+ throw new NumberIsTooSmallException(LocalizedFormats.NUMBER_OF_POINTS,
+ x.length, 2, true);
+ }
+
+ // Number of intervals. The number of data points is n + 1.
+ int n = x.length - 1;
+
+ MathArrays.checkOrder(x);
+
+ // Slope of the lines between the datapoints.
+ final double m[] = new double[n];
+ for (int i = 0; i < n; i++) {
+ m[i] = (y[i + 1] - y[i]) / (x[i + 1] - x[i]);
+ }
+
+ final PolynomialFunction polynomials[] = new PolynomialFunction[n];
+ final double coefficients[] = new double[2];
+ for (int i = 0; i < n; i++) {
+ coefficients[0] = y[i];
+ coefficients[1] = m[i];
+ polynomials[i] = new PolynomialFunction(coefficients);
+ }
+
+ return new PolynomialSplineFunction(x, polynomials);
+ }
+}
diff --git a/src/main/java/org/apache/commons/math3/analysis/interpolation/LoessInterpolator.java b/src/main/java/org/apache/commons/math3/analysis/interpolation/LoessInterpolator.java
new file mode 100644
index 0000000..3ffbe93
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/analysis/interpolation/LoessInterpolator.java
@@ -0,0 +1,473 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math3.analysis.interpolation;
+
+import java.io.Serializable;
+import java.util.Arrays;
+
+import org.apache.commons.math3.analysis.polynomials.PolynomialSplineFunction;
+import org.apache.commons.math3.exception.DimensionMismatchException;
+import org.apache.commons.math3.exception.NoDataException;
+import org.apache.commons.math3.exception.NonMonotonicSequenceException;
+import org.apache.commons.math3.exception.NotFiniteNumberException;
+import org.apache.commons.math3.exception.NotPositiveException;
+import org.apache.commons.math3.exception.NumberIsTooSmallException;
+import org.apache.commons.math3.exception.OutOfRangeException;
+import org.apache.commons.math3.exception.util.LocalizedFormats;
+import org.apache.commons.math3.util.FastMath;
+import org.apache.commons.math3.util.MathArrays;
+import org.apache.commons.math3.util.MathUtils;
+
+/**
+ * Implements the <a href="http://en.wikipedia.org/wiki/Local_regression">
+ * Local Regression Algorithm</a> (also Loess, Lowess) for interpolation of
+ * real univariate functions.
+ * <p>
+ * For reference, see
+ * <a href="http://amstat.tandfonline.com/doi/abs/10.1080/01621459.1979.10481038">
+ * William S. Cleveland - Robust Locally Weighted Regression and Smoothing
+ * Scatterplots</a>
+ * </p>
+ * This class implements both the loess method and serves as an interpolation
+ * adapter to it, allowing one to build a spline on the obtained loess fit.
+ *
+ * @since 2.0
+ */
+public class LoessInterpolator
+ implements UnivariateInterpolator, Serializable {
+ /** Default value of the bandwidth parameter. */
+ public static final double DEFAULT_BANDWIDTH = 0.3;
+ /** Default value of the number of robustness iterations. */
+ public static final int DEFAULT_ROBUSTNESS_ITERS = 2;
+ /**
+ * Default value for accuracy.
+ * @since 2.1
+ */
+ public static final double DEFAULT_ACCURACY = 1e-12;
+ /** serializable version identifier. */
+ private static final long serialVersionUID = 5204927143605193821L;
+ /**
+ * The bandwidth parameter: when computing the loess fit at
+ * a particular point, this fraction of source points closest
+ * to the current point is taken into account for computing
+ * a least-squares regression.
+ * <p>
+ * A sensible value is usually 0.25 to 0.5.</p>
+ */
+ private final double bandwidth;
+ /**
+ * The number of robustness iterations parameter: this many
+ * robustness iterations are done.
+ * <p>
+ * A sensible value is usually 0 (just the initial fit without any
+ * robustness iterations) to 4.</p>
+ */
+ private final int robustnessIters;
+ /**
+ * If the median residual at a certain robustness iteration
+ * is less than this amount, no more iterations are done.
+ */
+ private final double accuracy;
+
+ /**
+ * Constructs a new {@link LoessInterpolator}
+ * with a bandwidth of {@link #DEFAULT_BANDWIDTH},
+ * {@link #DEFAULT_ROBUSTNESS_ITERS} robustness iterations
+ * and an accuracy of {#link #DEFAULT_ACCURACY}.
+ * See {@link #LoessInterpolator(double, int, double)} for an explanation of
+ * the parameters.
+ */
+ public LoessInterpolator() {
+ this.bandwidth = DEFAULT_BANDWIDTH;
+ this.robustnessIters = DEFAULT_ROBUSTNESS_ITERS;
+ this.accuracy = DEFAULT_ACCURACY;
+ }
+
+ /**
+ * Construct a new {@link LoessInterpolator}
+ * with given bandwidth and number of robustness iterations.
+ * <p>
+ * Calling this constructor is equivalent to calling {link {@link
+ * #LoessInterpolator(double, int, double) LoessInterpolator(bandwidth,
+ * robustnessIters, LoessInterpolator.DEFAULT_ACCURACY)}
+ * </p>
+ *
+ * @param bandwidth when computing the loess fit at
+ * a particular point, this fraction of source points closest
+ * to the current point is taken into account for computing
+ * a least-squares regression.
+ * A sensible value is usually 0.25 to 0.5, the default value is
+ * {@link #DEFAULT_BANDWIDTH}.
+ * @param robustnessIters This many robustness iterations are done.
+ * A sensible value is usually 0 (just the initial fit without any
+ * robustness iterations) to 4, the default value is
+ * {@link #DEFAULT_ROBUSTNESS_ITERS}.
+
+ * @see #LoessInterpolator(double, int, double)
+ */
+ public LoessInterpolator(double bandwidth, int robustnessIters) {
+ this(bandwidth, robustnessIters, DEFAULT_ACCURACY);
+ }
+
+ /**
+ * Construct a new {@link LoessInterpolator}
+ * with given bandwidth, number of robustness iterations and accuracy.
+ *
+ * @param bandwidth when computing the loess fit at
+ * a particular point, this fraction of source points closest
+ * to the current point is taken into account for computing
+ * a least-squares regression.
+ * A sensible value is usually 0.25 to 0.5, the default value is
+ * {@link #DEFAULT_BANDWIDTH}.
+ * @param robustnessIters This many robustness iterations are done.
+ * A sensible value is usually 0 (just the initial fit without any
+ * robustness iterations) to 4, the default value is
+ * {@link #DEFAULT_ROBUSTNESS_ITERS}.
+ * @param accuracy If the median residual at a certain robustness iteration
+ * is less than this amount, no more iterations are done.
+ * @throws OutOfRangeException if bandwidth does not lie in the interval [0,1].
+ * @throws NotPositiveException if {@code robustnessIters} is negative.
+ * @see #LoessInterpolator(double, int)
+ * @since 2.1
+ */
+ public LoessInterpolator(double bandwidth, int robustnessIters, double accuracy)
+ throws OutOfRangeException,
+ NotPositiveException {
+ if (bandwidth < 0 ||
+ bandwidth > 1) {
+ throw new OutOfRangeException(LocalizedFormats.BANDWIDTH, bandwidth, 0, 1);
+ }
+ this.bandwidth = bandwidth;
+ if (robustnessIters < 0) {
+ throw new NotPositiveException(LocalizedFormats.ROBUSTNESS_ITERATIONS, robustnessIters);
+ }
+ this.robustnessIters = robustnessIters;
+ this.accuracy = accuracy;
+ }
+
+ /**
+ * Compute an interpolating function by performing a loess fit
+ * on the data at the original abscissae and then building a cubic spline
+ * with a
+ * {@link org.apache.commons.math3.analysis.interpolation.SplineInterpolator}
+ * on the resulting fit.
+ *
+ * @param xval the arguments for the interpolation points
+ * @param yval the values for the interpolation points
+ * @return A cubic spline built upon a loess fit to the data at the original abscissae
+ * @throws NonMonotonicSequenceException if {@code xval} not sorted in
+ * strictly increasing order.
+ * @throws DimensionMismatchException if {@code xval} and {@code yval} have
+ * different sizes.
+ * @throws NoDataException if {@code xval} or {@code yval} has zero size.
+ * @throws NotFiniteNumberException if any of the arguments and values are
+ * not finite real numbers.
+ * @throws NumberIsTooSmallException if the bandwidth is too small to
+ * accomodate the size of the input data (i.e. the bandwidth must be
+ * larger than 2/n).
+ */
+ public final PolynomialSplineFunction interpolate(final double[] xval,
+ final double[] yval)
+ throws NonMonotonicSequenceException,
+ DimensionMismatchException,
+ NoDataException,
+ NotFiniteNumberException,
+ NumberIsTooSmallException {
+ return new SplineInterpolator().interpolate(xval, smooth(xval, yval));
+ }
+
+ /**
+ * Compute a weighted loess fit on the data at the original abscissae.
+ *
+ * @param xval Arguments for the interpolation points.
+ * @param yval Values for the interpolation points.
+ * @param weights point weights: coefficients by which the robustness weight
+ * of a point is multiplied.
+ * @return the values of the loess fit at corresponding original abscissae.
+ * @throws NonMonotonicSequenceException if {@code xval} not sorted in
+ * strictly increasing order.
+ * @throws DimensionMismatchException if {@code xval} and {@code yval} have
+ * different sizes.
+ * @throws NoDataException if {@code xval} or {@code yval} has zero size.
+ * @throws NotFiniteNumberException if any of the arguments and values are
+ not finite real numbers.
+ * @throws NumberIsTooSmallException if the bandwidth is too small to
+ * accomodate the size of the input data (i.e. the bandwidth must be
+ * larger than 2/n).
+ * @since 2.1
+ */
+ public final double[] smooth(final double[] xval, final double[] yval,
+ final double[] weights)
+ throws NonMonotonicSequenceException,
+ DimensionMismatchException,
+ NoDataException,
+ NotFiniteNumberException,
+ NumberIsTooSmallException {
+ if (xval.length != yval.length) {
+ throw new DimensionMismatchException(xval.length, yval.length);
+ }
+
+ final int n = xval.length;
+
+ if (n == 0) {
+ throw new NoDataException();
+ }
+
+ checkAllFiniteReal(xval);
+ checkAllFiniteReal(yval);
+ checkAllFiniteReal(weights);
+
+ MathArrays.checkOrder(xval);
+
+ if (n == 1) {
+ return new double[]{yval[0]};
+ }
+
+ if (n == 2) {
+ return new double[]{yval[0], yval[1]};
+ }
+
+ int bandwidthInPoints = (int) (bandwidth * n);
+
+ if (bandwidthInPoints < 2) {
+ throw new NumberIsTooSmallException(LocalizedFormats.BANDWIDTH,
+ bandwidthInPoints, 2, true);
+ }
+
+ final double[] res = new double[n];
+
+ final double[] residuals = new double[n];
+ final double[] sortedResiduals = new double[n];
+
+ final double[] robustnessWeights = new double[n];
+
+ // Do an initial fit and 'robustnessIters' robustness iterations.
+ // This is equivalent to doing 'robustnessIters+1' robustness iterations
+ // starting with all robustness weights set to 1.
+ Arrays.fill(robustnessWeights, 1);
+
+ for (int iter = 0; iter <= robustnessIters; ++iter) {
+ final int[] bandwidthInterval = {0, bandwidthInPoints - 1};
+ // At each x, compute a local weighted linear regression
+ for (int i = 0; i < n; ++i) {
+ final double x = xval[i];
+
+ // Find out the interval of source points on which
+ // a regression is to be made.
+ if (i > 0) {
+ updateBandwidthInterval(xval, weights, i, bandwidthInterval);
+ }
+
+ final int ileft = bandwidthInterval[0];
+ final int iright = bandwidthInterval[1];
+
+ // Compute the point of the bandwidth interval that is
+ // farthest from x
+ final int edge;
+ if (xval[i] - xval[ileft] > xval[iright] - xval[i]) {
+ edge = ileft;
+ } else {
+ edge = iright;
+ }
+
+ // Compute a least-squares linear fit weighted by
+ // the product of robustness weights and the tricube
+ // weight function.
+ // See http://en.wikipedia.org/wiki/Linear_regression
+ // (section "Univariate linear case")
+ // and http://en.wikipedia.org/wiki/Weighted_least_squares
+ // (section "Weighted least squares")
+ double sumWeights = 0;
+ double sumX = 0;
+ double sumXSquared = 0;
+ double sumY = 0;
+ double sumXY = 0;
+ double denom = FastMath.abs(1.0 / (xval[edge] - x));
+ for (int k = ileft; k <= iright; ++k) {
+ final double xk = xval[k];
+ final double yk = yval[k];
+ final double dist = (k < i) ? x - xk : xk - x;
+ final double w = tricube(dist * denom) * robustnessWeights[k] * weights[k];
+ final double xkw = xk * w;
+ sumWeights += w;
+ sumX += xkw;
+ sumXSquared += xk * xkw;
+ sumY += yk * w;
+ sumXY += yk * xkw;
+ }
+
+ final double meanX = sumX / sumWeights;
+ final double meanY = sumY / sumWeights;
+ final double meanXY = sumXY / sumWeights;
+ final double meanXSquared = sumXSquared / sumWeights;
+
+ final double beta;
+ if (FastMath.sqrt(FastMath.abs(meanXSquared - meanX * meanX)) < accuracy) {
+ beta = 0;
+ } else {
+ beta = (meanXY - meanX * meanY) / (meanXSquared - meanX * meanX);
+ }
+
+ final double alpha = meanY - beta * meanX;
+
+ res[i] = beta * x + alpha;
+ residuals[i] = FastMath.abs(yval[i] - res[i]);
+ }
+
+ // No need to recompute the robustness weights at the last
+ // iteration, they won't be needed anymore
+ if (iter == robustnessIters) {
+ break;
+ }
+
+ // Recompute the robustness weights.
+
+ // Find the median residual.
+ // An arraycopy and a sort are completely tractable here,
+ // because the preceding loop is a lot more expensive
+ System.arraycopy(residuals, 0, sortedResiduals, 0, n);
+ Arrays.sort(sortedResiduals);
+ final double medianResidual = sortedResiduals[n / 2];
+
+ if (FastMath.abs(medianResidual) < accuracy) {
+ break;
+ }
+
+ for (int i = 0; i < n; ++i) {
+ final double arg = residuals[i] / (6 * medianResidual);
+ if (arg >= 1) {
+ robustnessWeights[i] = 0;
+ } else {
+ final double w = 1 - arg * arg;
+ robustnessWeights[i] = w * w;
+ }
+ }
+ }
+
+ return res;
+ }
+
+ /**
+ * Compute a loess fit on the data at the original abscissae.
+ *
+ * @param xval the arguments for the interpolation points
+ * @param yval the values for the interpolation points
+ * @return values of the loess fit at corresponding original abscissae
+ * @throws NonMonotonicSequenceException if {@code xval} not sorted in
+ * strictly increasing order.
+ * @throws DimensionMismatchException if {@code xval} and {@code yval} have
+ * different sizes.
+ * @throws NoDataException if {@code xval} or {@code yval} has zero size.
+ * @throws NotFiniteNumberException if any of the arguments and values are
+ * not finite real numbers.
+ * @throws NumberIsTooSmallException if the bandwidth is too small to
+ * accomodate the size of the input data (i.e. the bandwidth must be
+ * larger than 2/n).
+ */
+ public final double[] smooth(final double[] xval, final double[] yval)
+ throws NonMonotonicSequenceException,
+ DimensionMismatchException,
+ NoDataException,
+ NotFiniteNumberException,
+ NumberIsTooSmallException {
+ if (xval.length != yval.length) {
+ throw new DimensionMismatchException(xval.length, yval.length);
+ }
+
+ final double[] unitWeights = new double[xval.length];
+ Arrays.fill(unitWeights, 1.0);
+
+ return smooth(xval, yval, unitWeights);
+ }
+
+ /**
+ * Given an index interval into xval that embraces a certain number of
+ * points closest to {@code xval[i-1]}, update the interval so that it
+ * embraces the same number of points closest to {@code xval[i]},
+ * ignoring zero weights.
+ *
+ * @param xval Arguments array.
+ * @param weights Weights array.
+ * @param i Index around which the new interval should be computed.
+ * @param bandwidthInterval a two-element array {left, right} such that:
+ * {@code (left==0 or xval[i] - xval[left-1] > xval[right] - xval[i])}
+ * and
+ * {@code (right==xval.length-1 or xval[right+1] - xval[i] > xval[i] - xval[left])}.
+ * The array will be updated.
+ */
+ private static void updateBandwidthInterval(final double[] xval, final double[] weights,
+ final int i,
+ final int[] bandwidthInterval) {
+ final int left = bandwidthInterval[0];
+ final int right = bandwidthInterval[1];
+
+ // The right edge should be adjusted if the next point to the right
+ // is closer to xval[i] than the leftmost point of the current interval
+ int nextRight = nextNonzero(weights, right);
+ if (nextRight < xval.length && xval[nextRight] - xval[i] < xval[i] - xval[left]) {
+ int nextLeft = nextNonzero(weights, bandwidthInterval[0]);
+ bandwidthInterval[0] = nextLeft;
+ bandwidthInterval[1] = nextRight;
+ }
+ }
+
+ /**
+ * Return the smallest index {@code j} such that
+ * {@code j > i && (j == weights.length || weights[j] != 0)}.
+ *
+ * @param weights Weights array.
+ * @param i Index from which to start search.
+ * @return the smallest compliant index.
+ */
+ private static int nextNonzero(final double[] weights, final int i) {
+ int j = i + 1;
+ while(j < weights.length && weights[j] == 0) {
+ ++j;
+ }
+ return j;
+ }
+
+ /**
+ * Compute the
+ * <a href="http://en.wikipedia.org/wiki/Local_regression#Weight_function">tricube</a>
+ * weight function
+ *
+ * @param x Argument.
+ * @return <code>(1 - |x|<sup>3</sup>)<sup>3</sup></code> for |x| &lt; 1, 0 otherwise.
+ */
+ private static double tricube(final double x) {
+ final double absX = FastMath.abs(x);
+ if (absX >= 1.0) {
+ return 0.0;
+ }
+ final double tmp = 1 - absX * absX * absX;
+ return tmp * tmp * tmp;
+ }
+
+ /**
+ * Check that all elements of an array are finite real numbers.
+ *
+ * @param values Values array.
+ * @throws org.apache.commons.math3.exception.NotFiniteNumberException
+ * if one of the values is not a finite real number.
+ */
+ private static void checkAllFiniteReal(final double[] values) {
+ for (int i = 0; i < values.length; i++) {
+ MathUtils.checkFinite(values[i]);
+ }
+ }
+}
diff --git a/src/main/java/org/apache/commons/math3/analysis/interpolation/MicrosphereInterpolatingFunction.java b/src/main/java/org/apache/commons/math3/analysis/interpolation/MicrosphereInterpolatingFunction.java
new file mode 100644
index 0000000..58be772
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/analysis/interpolation/MicrosphereInterpolatingFunction.java
@@ -0,0 +1,253 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math3.analysis.interpolation;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.commons.math3.analysis.MultivariateFunction;
+import org.apache.commons.math3.exception.DimensionMismatchException;
+import org.apache.commons.math3.exception.NoDataException;
+import org.apache.commons.math3.exception.NullArgumentException;
+import org.apache.commons.math3.linear.ArrayRealVector;
+import org.apache.commons.math3.linear.RealVector;
+import org.apache.commons.math3.random.UnitSphereRandomVectorGenerator;
+import org.apache.commons.math3.util.FastMath;
+
+/**
+ * Interpolating function that implements the
+ * <a href="http://www.dudziak.com/microsphere.php">Microsphere Projection</a>.
+ *
+ * @deprecated Code will be removed in 4.0. Use {@link InterpolatingMicrosphere}
+ * and {@link MicrosphereProjectionInterpolator} instead.
+ */
+@Deprecated
+public class MicrosphereInterpolatingFunction
+ implements MultivariateFunction {
+ /**
+ * Space dimension.
+ */
+ private final int dimension;
+ /**
+ * Internal accounting data for the interpolation algorithm.
+ * Each element of the list corresponds to one surface element of
+ * the microsphere.
+ */
+ private final List<MicrosphereSurfaceElement> microsphere;
+ /**
+ * Exponent used in the power law that computes the weights of the
+ * sample data.
+ */
+ private final double brightnessExponent;
+ /**
+ * Sample data.
+ */
+ private final Map<RealVector, Double> samples;
+
+ /**
+ * Class for storing the accounting data needed to perform the
+ * microsphere projection.
+ */
+ private static class MicrosphereSurfaceElement {
+ /** Normal vector characterizing a surface element. */
+ private final RealVector normal;
+ /** Illumination received from the brightest sample. */
+ private double brightestIllumination;
+ /** Brightest sample. */
+ private Map.Entry<RealVector, Double> brightestSample;
+
+ /**
+ * @param n Normal vector characterizing a surface element
+ * of the microsphere.
+ */
+ MicrosphereSurfaceElement(double[] n) {
+ normal = new ArrayRealVector(n);
+ }
+
+ /**
+ * Return the normal vector.
+ * @return the normal vector
+ */
+ RealVector normal() {
+ return normal;
+ }
+
+ /**
+ * Reset "illumination" and "sampleIndex".
+ */
+ void reset() {
+ brightestIllumination = 0;
+ brightestSample = null;
+ }
+
+ /**
+ * Store the illumination and index of the brightest sample.
+ * @param illuminationFromSample illumination received from sample
+ * @param sample current sample illuminating the element
+ */
+ void store(final double illuminationFromSample,
+ final Map.Entry<RealVector, Double> sample) {
+ if (illuminationFromSample > this.brightestIllumination) {
+ this.brightestIllumination = illuminationFromSample;
+ this.brightestSample = sample;
+ }
+ }
+
+ /**
+ * Get the illumination of the element.
+ * @return the illumination.
+ */
+ double illumination() {
+ return brightestIllumination;
+ }
+
+ /**
+ * Get the sample illuminating the element the most.
+ * @return the sample.
+ */
+ Map.Entry<RealVector, Double> sample() {
+ return brightestSample;
+ }
+ }
+
+ /**
+ * @param xval Arguments for the interpolation points.
+ * {@code xval[i][0]} is the first component of interpolation point
+ * {@code i}, {@code xval[i][1]} is the second component, and so on
+ * until {@code xval[i][d-1]}, the last component of that interpolation
+ * point (where {@code dimension} is thus the dimension of the sampled
+ * space).
+ * @param yval Values for the interpolation points.
+ * @param brightnessExponent Brightness dimming factor.
+ * @param microsphereElements Number of surface elements of the
+ * microsphere.
+ * @param rand Unit vector generator for creating the microsphere.
+ * @throws DimensionMismatchException if the lengths of {@code yval} and
+ * {@code xval} (equal to {@code n}, the number of interpolation points)
+ * do not match, or the the arrays {@code xval[0]} ... {@code xval[n]},
+ * have lengths different from {@code dimension}.
+ * @throws NoDataException if there an array has zero-length.
+ * @throws NullArgumentException if an argument is {@code null}.
+ */
+ public MicrosphereInterpolatingFunction(double[][] xval,
+ double[] yval,
+ int brightnessExponent,
+ int microsphereElements,
+ UnitSphereRandomVectorGenerator rand)
+ throws DimensionMismatchException,
+ NoDataException,
+ NullArgumentException {
+ if (xval == null ||
+ yval == null) {
+ throw new NullArgumentException();
+ }
+ if (xval.length == 0) {
+ throw new NoDataException();
+ }
+ if (xval.length != yval.length) {
+ throw new DimensionMismatchException(xval.length, yval.length);
+ }
+ if (xval[0] == null) {
+ throw new NullArgumentException();
+ }
+
+ dimension = xval[0].length;
+ this.brightnessExponent = brightnessExponent;
+
+ // Copy data samples.
+ samples = new HashMap<RealVector, Double>(yval.length);
+ for (int i = 0; i < xval.length; ++i) {
+ final double[] xvalI = xval[i];
+ if (xvalI == null) {
+ throw new NullArgumentException();
+ }
+ if (xvalI.length != dimension) {
+ throw new DimensionMismatchException(xvalI.length, dimension);
+ }
+
+ samples.put(new ArrayRealVector(xvalI), yval[i]);
+ }
+
+ microsphere = new ArrayList<MicrosphereSurfaceElement>(microsphereElements);
+ // Generate the microsphere, assuming that a fairly large number of
+ // randomly generated normals will represent a sphere.
+ for (int i = 0; i < microsphereElements; i++) {
+ microsphere.add(new MicrosphereSurfaceElement(rand.nextVector()));
+ }
+ }
+
+ /**
+ * @param point Interpolation point.
+ * @return the interpolated value.
+ * @throws DimensionMismatchException if point dimension does not math sample
+ */
+ public double value(double[] point) throws DimensionMismatchException {
+ final RealVector p = new ArrayRealVector(point);
+
+ // Reset.
+ for (MicrosphereSurfaceElement md : microsphere) {
+ md.reset();
+ }
+
+ // Compute contribution of each sample points to the microsphere elements illumination
+ for (Map.Entry<RealVector, Double> sd : samples.entrySet()) {
+
+ // Vector between interpolation point and current sample point.
+ final RealVector diff = sd.getKey().subtract(p);
+ final double diffNorm = diff.getNorm();
+
+ if (FastMath.abs(diffNorm) < FastMath.ulp(1d)) {
+ // No need to interpolate, as the interpolation point is
+ // actually (very close to) one of the sampled points.
+ return sd.getValue();
+ }
+
+ for (MicrosphereSurfaceElement md : microsphere) {
+ final double w = FastMath.pow(diffNorm, -brightnessExponent);
+ md.store(cosAngle(diff, md.normal()) * w, sd);
+ }
+
+ }
+
+ // Interpolation calculation.
+ double value = 0;
+ double totalWeight = 0;
+ for (MicrosphereSurfaceElement md : microsphere) {
+ final double iV = md.illumination();
+ final Map.Entry<RealVector, Double> sd = md.sample();
+ if (sd != null) {
+ value += iV * sd.getValue();
+ totalWeight += iV;
+ }
+ }
+
+ return value / totalWeight;
+ }
+
+ /**
+ * Compute the cosine of the angle between 2 vectors.
+ *
+ * @param v Vector.
+ * @param w Vector.
+ * @return the cosine of the angle between {@code v} and {@code w}.
+ */
+ private double cosAngle(final RealVector v, final RealVector w) {
+ return v.dotProduct(w) / (v.getNorm() * w.getNorm());
+ }
+}
diff --git a/src/main/java/org/apache/commons/math3/analysis/interpolation/MicrosphereInterpolator.java b/src/main/java/org/apache/commons/math3/analysis/interpolation/MicrosphereInterpolator.java
new file mode 100644
index 0000000..d9174bc
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/analysis/interpolation/MicrosphereInterpolator.java
@@ -0,0 +1,105 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math3.analysis.interpolation;
+
+import org.apache.commons.math3.analysis.MultivariateFunction;
+import org.apache.commons.math3.exception.NotPositiveException;
+import org.apache.commons.math3.exception.NotStrictlyPositiveException;
+import org.apache.commons.math3.exception.NoDataException;
+import org.apache.commons.math3.exception.DimensionMismatchException;
+import org.apache.commons.math3.exception.NullArgumentException;
+import org.apache.commons.math3.random.UnitSphereRandomVectorGenerator;
+
+/**
+ * Interpolator that implements the algorithm described in
+ * <em>William Dudziak</em>'s
+ * <a href="http://www.dudziak.com/microsphere.pdf">MS thesis</a>.
+ *
+ * @since 2.1
+ * @deprecated Code will be removed in 4.0. Use {@link InterpolatingMicrosphere}
+ * and {@link MicrosphereProjectionInterpolator} instead.
+ */
+@Deprecated
+public class MicrosphereInterpolator
+ implements MultivariateInterpolator {
+ /**
+ * Default number of surface elements that composes the microsphere.
+ */
+ public static final int DEFAULT_MICROSPHERE_ELEMENTS = 2000;
+ /**
+ * Default exponent used the weights calculation.
+ */
+ public static final int DEFAULT_BRIGHTNESS_EXPONENT = 2;
+ /**
+ * Number of surface elements of the microsphere.
+ */
+ private final int microsphereElements;
+ /**
+ * Exponent used in the power law that computes the weights of the
+ * sample data.
+ */
+ private final int brightnessExponent;
+
+ /**
+ * Create a microsphere interpolator with default settings.
+ * Calling this constructor is equivalent to call {@link
+ * #MicrosphereInterpolator(int, int)
+ * MicrosphereInterpolator(MicrosphereInterpolator.DEFAULT_MICROSPHERE_ELEMENTS,
+ * MicrosphereInterpolator.DEFAULT_BRIGHTNESS_EXPONENT)}.
+ */
+ public MicrosphereInterpolator() {
+ this(DEFAULT_MICROSPHERE_ELEMENTS, DEFAULT_BRIGHTNESS_EXPONENT);
+ }
+
+ /** Create a microsphere interpolator.
+ * @param elements Number of surface elements of the microsphere.
+ * @param exponent Exponent used in the power law that computes the
+ * weights (distance dimming factor) of the sample data.
+ * @throws NotPositiveException if {@code exponent < 0}.
+ * @throws NotStrictlyPositiveException if {@code elements <= 0}.
+ */
+ public MicrosphereInterpolator(final int elements,
+ final int exponent)
+ throws NotPositiveException,
+ NotStrictlyPositiveException {
+ if (exponent < 0) {
+ throw new NotPositiveException(exponent);
+ }
+ if (elements <= 0) {
+ throw new NotStrictlyPositiveException(elements);
+ }
+
+ microsphereElements = elements;
+ brightnessExponent = exponent;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public MultivariateFunction interpolate(final double[][] xval,
+ final double[] yval)
+ throws DimensionMismatchException,
+ NoDataException,
+ NullArgumentException {
+ final UnitSphereRandomVectorGenerator rand
+ = new UnitSphereRandomVectorGenerator(xval[0].length);
+ return new MicrosphereInterpolatingFunction(xval, yval,
+ brightnessExponent,
+ microsphereElements,
+ rand);
+ }
+}
diff --git a/src/main/java/org/apache/commons/math3/analysis/interpolation/MicrosphereProjectionInterpolator.java b/src/main/java/org/apache/commons/math3/analysis/interpolation/MicrosphereProjectionInterpolator.java
new file mode 100644
index 0000000..28f5b26
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/analysis/interpolation/MicrosphereProjectionInterpolator.java
@@ -0,0 +1,164 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math3.analysis.interpolation;
+
+import org.apache.commons.math3.analysis.MultivariateFunction;
+import org.apache.commons.math3.exception.DimensionMismatchException;
+import org.apache.commons.math3.exception.NoDataException;
+import org.apache.commons.math3.exception.NotPositiveException;
+import org.apache.commons.math3.exception.NullArgumentException;
+import org.apache.commons.math3.random.UnitSphereRandomVectorGenerator;
+
+/**
+ * Interpolator that implements the algorithm described in
+ * <em>William Dudziak</em>'s
+ * <a href="http://www.dudziak.com/microsphere.pdf">MS thesis</a>.
+ *
+ * @since 3.6
+ */
+public class MicrosphereProjectionInterpolator
+ implements MultivariateInterpolator {
+ /** Brightness exponent. */
+ private final double exponent;
+ /** Microsphere. */
+ private final InterpolatingMicrosphere microsphere;
+ /** Whether to share the sphere. */
+ private final boolean sharedSphere;
+ /** Tolerance value below which no interpolation is necessary. */
+ private final double noInterpolationTolerance;
+
+ /**
+ * Create a microsphere interpolator.
+ *
+ * @param dimension Space dimension.
+ * @param elements Number of surface elements of the microsphere.
+ * @param exponent Exponent used in the power law that computes the
+ * @param maxDarkFraction Maximum fraction of the facets that can be dark.
+ * If the fraction of "non-illuminated" facets is larger, no estimation
+ * of the value will be performed, and the {@code background} value will
+ * be returned instead.
+ * @param darkThreshold Value of the illumination below which a facet is
+ * considered dark.
+ * @param background Value returned when the {@code maxDarkFraction}
+ * threshold is exceeded.
+ * @param sharedSphere Whether the sphere can be shared among the
+ * interpolating function instances. If {@code true}, the instances
+ * will share the same data, and thus will <em>not</em> be thread-safe.
+ * @param noInterpolationTolerance When the distance between an
+ * interpolated point and one of the sample points is less than this
+ * value, no interpolation will be performed (the value of the sample
+ * will be returned).
+ * @throws org.apache.commons.math3.exception.NotStrictlyPositiveException
+ * if {@code dimension <= 0} or {@code elements <= 0}.
+ * @throws NotPositiveException if {@code exponent < 0}.
+ * @throws NotPositiveException if {@code darkThreshold < 0}.
+ * @throws org.apache.commons.math3.exception.OutOfRangeException if
+ * {@code maxDarkFraction} does not belong to the interval {@code [0, 1]}.
+ */
+ public MicrosphereProjectionInterpolator(int dimension,
+ int elements,
+ double maxDarkFraction,
+ double darkThreshold,
+ double background,
+ double exponent,
+ boolean sharedSphere,
+ double noInterpolationTolerance) {
+ this(new InterpolatingMicrosphere(dimension,
+ elements,
+ maxDarkFraction,
+ darkThreshold,
+ background,
+ new UnitSphereRandomVectorGenerator(dimension)),
+ exponent,
+ sharedSphere,
+ noInterpolationTolerance);
+ }
+
+ /**
+ * Create a microsphere interpolator.
+ *
+ * @param microsphere Microsphere.
+ * @param exponent Exponent used in the power law that computes the
+ * weights (distance dimming factor) of the sample data.
+ * @param sharedSphere Whether the sphere can be shared among the
+ * interpolating function instances. If {@code true}, the instances
+ * will share the same data, and thus will <em>not</em> be thread-safe.
+ * @param noInterpolationTolerance When the distance between an
+ * interpolated point and one of the sample points is less than this
+ * value, no interpolation will be performed (the value of the sample
+ * will be returned).
+ * @throws NotPositiveException if {@code exponent < 0}.
+ */
+ public MicrosphereProjectionInterpolator(InterpolatingMicrosphere microsphere,
+ double exponent,
+ boolean sharedSphere,
+ double noInterpolationTolerance)
+ throws NotPositiveException {
+ if (exponent < 0) {
+ throw new NotPositiveException(exponent);
+ }
+
+ this.microsphere = microsphere;
+ this.exponent = exponent;
+ this.sharedSphere = sharedSphere;
+ this.noInterpolationTolerance = noInterpolationTolerance;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @throws DimensionMismatchException if the space dimension of the
+ * given samples does not match the space dimension of the microsphere.
+ */
+ public MultivariateFunction interpolate(final double[][] xval,
+ final double[] yval)
+ throws DimensionMismatchException,
+ NoDataException,
+ NullArgumentException {
+ if (xval == null ||
+ yval == null) {
+ throw new NullArgumentException();
+ }
+ if (xval.length == 0) {
+ throw new NoDataException();
+ }
+ if (xval.length != yval.length) {
+ throw new DimensionMismatchException(xval.length, yval.length);
+ }
+ if (xval[0] == null) {
+ throw new NullArgumentException();
+ }
+ final int dimension = microsphere.getDimension();
+ if (dimension != xval[0].length) {
+ throw new DimensionMismatchException(xval[0].length, dimension);
+ }
+
+ // Microsphere copy.
+ final InterpolatingMicrosphere m = sharedSphere ? microsphere : microsphere.copy();
+
+ return new MultivariateFunction() {
+ /** {inheritDoc} */
+ public double value(double[] point) {
+ return m.value(point,
+ xval,
+ yval,
+ exponent,
+ noInterpolationTolerance);
+ }
+ };
+ }
+}
diff --git a/src/main/java/org/apache/commons/math3/analysis/interpolation/MultivariateInterpolator.java b/src/main/java/org/apache/commons/math3/analysis/interpolation/MultivariateInterpolator.java
new file mode 100644
index 0000000..7d76374
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/analysis/interpolation/MultivariateInterpolator.java
@@ -0,0 +1,51 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math3.analysis.interpolation;
+
+import org.apache.commons.math3.analysis.MultivariateFunction;
+import org.apache.commons.math3.exception.DimensionMismatchException;
+import org.apache.commons.math3.exception.MathIllegalArgumentException;
+import org.apache.commons.math3.exception.NoDataException;
+import org.apache.commons.math3.exception.NullArgumentException;
+
+/**
+ * Interface representing a univariate real interpolating function.
+ *
+ * @since 2.1
+ */
+public interface MultivariateInterpolator {
+
+ /**
+ * Computes an interpolating function for the data set.
+ *
+ * @param xval the arguments for the interpolation points.
+ * {@code xval[i][0]} is the first component of interpolation point
+ * {@code i}, {@code xval[i][1]} is the second component, and so on
+ * until {@code xval[i][d-1]}, the last component of that interpolation
+ * point (where {@code d} is thus the dimension of the space).
+ * @param yval the values for the interpolation points
+ * @return a function which interpolates the data set
+ * @throws MathIllegalArgumentException if the arguments violate assumptions
+ * made by the interpolation algorithm.
+ * @throws DimensionMismatchException when the array dimensions are not consistent.
+ * @throws NoDataException if an array has zero-length.
+ * @throws NullArgumentException if the arguments are {@code null}.
+ */
+ MultivariateFunction interpolate(double[][] xval, double[] yval)
+ throws MathIllegalArgumentException, DimensionMismatchException,
+ NoDataException, NullArgumentException;
+}
diff --git a/src/main/java/org/apache/commons/math3/analysis/interpolation/NevilleInterpolator.java b/src/main/java/org/apache/commons/math3/analysis/interpolation/NevilleInterpolator.java
new file mode 100644
index 0000000..6b47451
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/analysis/interpolation/NevilleInterpolator.java
@@ -0,0 +1,60 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math3.analysis.interpolation;
+
+import java.io.Serializable;
+
+import org.apache.commons.math3.analysis.polynomials.PolynomialFunctionLagrangeForm;
+import org.apache.commons.math3.exception.DimensionMismatchException;
+import org.apache.commons.math3.exception.NumberIsTooSmallException;
+import org.apache.commons.math3.exception.NonMonotonicSequenceException;
+
+/**
+ * Implements the <a href="http://mathworld.wolfram.com/NevillesAlgorithm.html">
+ * Neville's Algorithm</a> for interpolation of real univariate functions. For
+ * reference, see <b>Introduction to Numerical Analysis</b>, ISBN 038795452X,
+ * chapter 2.
+ * <p>
+ * The actual code of Neville's algorithm is in PolynomialFunctionLagrangeForm,
+ * this class provides an easy-to-use interface to it.</p>
+ *
+ * @since 1.2
+ */
+public class NevilleInterpolator implements UnivariateInterpolator,
+ Serializable {
+
+ /** serializable version identifier */
+ static final long serialVersionUID = 3003707660147873733L;
+
+ /**
+ * Computes an interpolating function for the data set.
+ *
+ * @param x Interpolating points.
+ * @param y Interpolating values.
+ * @return a function which interpolates the data set
+ * @throws DimensionMismatchException if the array lengths are different.
+ * @throws NumberIsTooSmallException if the number of points is less than 2.
+ * @throws NonMonotonicSequenceException if two abscissae have the same
+ * value.
+ */
+ public PolynomialFunctionLagrangeForm interpolate(double x[], double y[])
+ throws DimensionMismatchException,
+ NumberIsTooSmallException,
+ NonMonotonicSequenceException {
+ return new PolynomialFunctionLagrangeForm(x, y);
+ }
+}
diff --git a/src/main/java/org/apache/commons/math3/analysis/interpolation/PiecewiseBicubicSplineInterpolatingFunction.java b/src/main/java/org/apache/commons/math3/analysis/interpolation/PiecewiseBicubicSplineInterpolatingFunction.java
new file mode 100644
index 0000000..7dd135a
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/analysis/interpolation/PiecewiseBicubicSplineInterpolatingFunction.java
@@ -0,0 +1,210 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math3.analysis.interpolation;
+
+import java.util.Arrays;
+import org.apache.commons.math3.analysis.BivariateFunction;
+import org.apache.commons.math3.analysis.polynomials.PolynomialSplineFunction;
+import org.apache.commons.math3.exception.DimensionMismatchException;
+import org.apache.commons.math3.exception.InsufficientDataException;
+import org.apache.commons.math3.exception.NoDataException;
+import org.apache.commons.math3.exception.NullArgumentException;
+import org.apache.commons.math3.exception.OutOfRangeException;
+import org.apache.commons.math3.exception.NonMonotonicSequenceException;
+import org.apache.commons.math3.util.MathArrays;
+
+/**
+ * Function that implements the
+ * <a href="http://www.paulinternet.nl/?page=bicubic">bicubic spline</a>
+ * interpolation.
+ * This implementation currently uses {@link AkimaSplineInterpolator} as the
+ * underlying one-dimensional interpolator, which requires 5 sample points;
+ * insufficient data will raise an exception when the
+ * {@link #value(double,double) value} method is called.
+ *
+ * @since 3.4
+ */
+public class PiecewiseBicubicSplineInterpolatingFunction
+ implements BivariateFunction {
+ /** The minimum number of points that are needed to compute the function. */
+ private static final int MIN_NUM_POINTS = 5;
+ /** Samples x-coordinates */
+ private final double[] xval;
+ /** Samples y-coordinates */
+ private final double[] yval;
+ /** Set of cubic splines patching the whole data grid */
+ private final double[][] fval;
+
+ /**
+ * @param x Sample values of the x-coordinate, in increasing order.
+ * @param y Sample values of the y-coordinate, in increasing order.
+ * @param f Values of the function on every grid point. the expected number
+ * of elements.
+ * @throws NonMonotonicSequenceException if {@code x} or {@code y} are not
+ * strictly increasing.
+ * @throws NullArgumentException if any of the arguments are null
+ * @throws NoDataException if any of the arrays has zero length.
+ * @throws DimensionMismatchException if the length of x and y don't match the row, column
+ * height of f
+ */
+ public PiecewiseBicubicSplineInterpolatingFunction(double[] x,
+ double[] y,
+ double[][] f)
+ throws DimensionMismatchException,
+ NullArgumentException,
+ NoDataException,
+ NonMonotonicSequenceException {
+ if (x == null ||
+ y == null ||
+ f == null ||
+ f[0] == null) {
+ throw new NullArgumentException();
+ }
+
+ final int xLen = x.length;
+ final int yLen = y.length;
+
+ if (xLen == 0 ||
+ yLen == 0 ||
+ f.length == 0 ||
+ f[0].length == 0) {
+ throw new NoDataException();
+ }
+
+ if (xLen < MIN_NUM_POINTS ||
+ yLen < MIN_NUM_POINTS ||
+ f.length < MIN_NUM_POINTS ||
+ f[0].length < MIN_NUM_POINTS) {
+ throw new InsufficientDataException();
+ }
+
+ if (xLen != f.length) {
+ throw new DimensionMismatchException(xLen, f.length);
+ }
+
+ if (yLen != f[0].length) {
+ throw new DimensionMismatchException(yLen, f[0].length);
+ }
+
+ MathArrays.checkOrder(x);
+ MathArrays.checkOrder(y);
+
+ xval = x.clone();
+ yval = y.clone();
+ fval = f.clone();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public double value(double x,
+ double y)
+ throws OutOfRangeException {
+ final AkimaSplineInterpolator interpolator = new AkimaSplineInterpolator();
+ final int offset = 2;
+ final int count = offset + 3;
+ final int i = searchIndex(x, xval, offset, count);
+ final int j = searchIndex(y, yval, offset, count);
+
+ final double xArray[] = new double[count];
+ final double yArray[] = new double[count];
+ final double zArray[] = new double[count];
+ final double interpArray[] = new double[count];
+
+ for (int index = 0; index < count; index++) {
+ xArray[index] = xval[i + index];
+ yArray[index] = yval[j + index];
+ }
+
+ for (int zIndex = 0; zIndex < count; zIndex++) {
+ for (int index = 0; index < count; index++) {
+ zArray[index] = fval[i + index][j + zIndex];
+ }
+ final PolynomialSplineFunction spline = interpolator.interpolate(xArray, zArray);
+ interpArray[zIndex] = spline.value(x);
+ }
+
+ final PolynomialSplineFunction spline = interpolator.interpolate(yArray, interpArray);
+
+ double returnValue = spline.value(y);
+
+ return returnValue;
+ }
+
+ /**
+ * Indicates whether a point is within the interpolation range.
+ *
+ * @param x First coordinate.
+ * @param y Second coordinate.
+ * @return {@code true} if (x, y) is a valid point.
+ * @since 3.3
+ */
+ public boolean isValidPoint(double x,
+ double y) {
+ if (x < xval[0] ||
+ x > xval[xval.length - 1] ||
+ y < yval[0] ||
+ y > yval[yval.length - 1]) {
+ return false;
+ } else {
+ return true;
+ }
+ }
+
+ /**
+ * @param c Coordinate.
+ * @param val Coordinate samples.
+ * @param offset how far back from found value to offset for querying
+ * @param count total number of elements forward from beginning that will be
+ * queried
+ * @return the index in {@code val} corresponding to the interval containing
+ * {@code c}.
+ * @throws OutOfRangeException if {@code c} is out of the range defined by
+ * the boundary values of {@code val}.
+ */
+ private int searchIndex(double c,
+ double[] val,
+ int offset,
+ int count) {
+ int r = Arrays.binarySearch(val, c);
+
+ if (r == -1 || r == -val.length - 1) {
+ throw new OutOfRangeException(c, val[0], val[val.length - 1]);
+ }
+
+ if (r < 0) {
+ // "c" in within an interpolation sub-interval, which returns
+ // negative
+ // need to remove the negative sign for consistency
+ r = -r - offset - 1;
+ } else {
+ r -= offset;
+ }
+
+ if (r < 0) {
+ r = 0;
+ }
+
+ if ((r + count) >= val.length) {
+ // "c" is the last sample of the range: Return the index
+ // of the sample at the lower end of the last sub-interval.
+ r = val.length - count;
+ }
+
+ return r;
+ }
+}
diff --git a/src/main/java/org/apache/commons/math3/analysis/interpolation/PiecewiseBicubicSplineInterpolator.java b/src/main/java/org/apache/commons/math3/analysis/interpolation/PiecewiseBicubicSplineInterpolator.java
new file mode 100644
index 0000000..826f328
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/analysis/interpolation/PiecewiseBicubicSplineInterpolator.java
@@ -0,0 +1,61 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math3.analysis.interpolation;
+
+import org.apache.commons.math3.exception.DimensionMismatchException;
+import org.apache.commons.math3.exception.NoDataException;
+import org.apache.commons.math3.exception.NonMonotonicSequenceException;
+import org.apache.commons.math3.exception.NullArgumentException;
+import org.apache.commons.math3.util.MathArrays;
+
+/**
+ * Generates a piecewise-bicubic interpolating function.
+ *
+ * @since 2.2
+ */
+public class PiecewiseBicubicSplineInterpolator
+ implements BivariateGridInterpolator {
+
+ /**
+ * {@inheritDoc}
+ */
+ public PiecewiseBicubicSplineInterpolatingFunction interpolate( final double[] xval,
+ final double[] yval,
+ final double[][] fval)
+ throws DimensionMismatchException,
+ NullArgumentException,
+ NoDataException,
+ NonMonotonicSequenceException {
+ if ( xval == null ||
+ yval == null ||
+ fval == null ||
+ fval[0] == null ) {
+ throw new NullArgumentException();
+ }
+
+ if ( xval.length == 0 ||
+ yval.length == 0 ||
+ fval.length == 0 ) {
+ throw new NoDataException();
+ }
+
+ MathArrays.checkOrder(xval);
+ MathArrays.checkOrder(yval);
+
+ return new PiecewiseBicubicSplineInterpolatingFunction( xval, yval, fval );
+ }
+}
diff --git a/src/main/java/org/apache/commons/math3/analysis/interpolation/SmoothingPolynomialBicubicSplineInterpolator.java b/src/main/java/org/apache/commons/math3/analysis/interpolation/SmoothingPolynomialBicubicSplineInterpolator.java
new file mode 100644
index 0000000..e1639b2
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/analysis/interpolation/SmoothingPolynomialBicubicSplineInterpolator.java
@@ -0,0 +1,171 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math3.analysis.interpolation;
+
+import org.apache.commons.math3.exception.DimensionMismatchException;
+import org.apache.commons.math3.exception.NoDataException;
+import org.apache.commons.math3.exception.NonMonotonicSequenceException;
+import org.apache.commons.math3.exception.NotPositiveException;
+import org.apache.commons.math3.exception.NullArgumentException;
+import org.apache.commons.math3.util.MathArrays;
+import org.apache.commons.math3.util.Precision;
+import org.apache.commons.math3.optim.nonlinear.vector.jacobian.GaussNewtonOptimizer;
+import org.apache.commons.math3.fitting.PolynomialFitter;
+import org.apache.commons.math3.analysis.polynomials.PolynomialFunction;
+import org.apache.commons.math3.optim.SimpleVectorValueChecker;
+
+/**
+ * Generates a bicubic interpolation function.
+ * Prior to generating the interpolating function, the input is smoothed using
+ * polynomial fitting.
+ *
+ * @since 2.2
+ * @deprecated To be removed in 4.0 (see MATH-1166).
+ */
+@Deprecated
+public class SmoothingPolynomialBicubicSplineInterpolator
+ extends BicubicSplineInterpolator {
+ /** Fitter for x. */
+ private final PolynomialFitter xFitter;
+ /** Degree of the fitting polynomial. */
+ private final int xDegree;
+ /** Fitter for y. */
+ private final PolynomialFitter yFitter;
+ /** Degree of the fitting polynomial. */
+ private final int yDegree;
+
+ /**
+ * Default constructor. The degree of the fitting polynomials is set to 3.
+ */
+ public SmoothingPolynomialBicubicSplineInterpolator() {
+ this(3);
+ }
+
+ /**
+ * @param degree Degree of the polynomial fitting functions.
+ * @exception NotPositiveException if degree is not positive
+ */
+ public SmoothingPolynomialBicubicSplineInterpolator(int degree)
+ throws NotPositiveException {
+ this(degree, degree);
+ }
+
+ /**
+ * @param xDegree Degree of the polynomial fitting functions along the
+ * x-dimension.
+ * @param yDegree Degree of the polynomial fitting functions along the
+ * y-dimension.
+ * @exception NotPositiveException if degrees are not positive
+ */
+ public SmoothingPolynomialBicubicSplineInterpolator(int xDegree, int yDegree)
+ throws NotPositiveException {
+ if (xDegree < 0) {
+ throw new NotPositiveException(xDegree);
+ }
+ if (yDegree < 0) {
+ throw new NotPositiveException(yDegree);
+ }
+ this.xDegree = xDegree;
+ this.yDegree = yDegree;
+
+ final double safeFactor = 1e2;
+ final SimpleVectorValueChecker checker
+ = new SimpleVectorValueChecker(safeFactor * Precision.EPSILON,
+ safeFactor * Precision.SAFE_MIN);
+ xFitter = new PolynomialFitter(new GaussNewtonOptimizer(false, checker));
+ yFitter = new PolynomialFitter(new GaussNewtonOptimizer(false, checker));
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public BicubicSplineInterpolatingFunction interpolate(final double[] xval,
+ final double[] yval,
+ final double[][] fval)
+ throws NoDataException, NullArgumentException,
+ DimensionMismatchException, NonMonotonicSequenceException {
+ if (xval.length == 0 || yval.length == 0 || fval.length == 0) {
+ throw new NoDataException();
+ }
+ if (xval.length != fval.length) {
+ throw new DimensionMismatchException(xval.length, fval.length);
+ }
+
+ final int xLen = xval.length;
+ final int yLen = yval.length;
+
+ for (int i = 0; i < xLen; i++) {
+ if (fval[i].length != yLen) {
+ throw new DimensionMismatchException(fval[i].length, yLen);
+ }
+ }
+
+ MathArrays.checkOrder(xval);
+ MathArrays.checkOrder(yval);
+
+ // For each line y[j] (0 <= j < yLen), construct a polynomial, with
+ // respect to variable x, fitting array fval[][j]
+ final PolynomialFunction[] yPolyX = new PolynomialFunction[yLen];
+ for (int j = 0; j < yLen; j++) {
+ xFitter.clearObservations();
+ for (int i = 0; i < xLen; i++) {
+ xFitter.addObservedPoint(1, xval[i], fval[i][j]);
+ }
+
+ // Initial guess for the fit is zero for each coefficients (of which
+ // there are "xDegree" + 1).
+ yPolyX[j] = new PolynomialFunction(xFitter.fit(new double[xDegree + 1]));
+ }
+
+ // For every knot (xval[i], yval[j]) of the grid, calculate corrected
+ // values fval_1
+ final double[][] fval_1 = new double[xLen][yLen];
+ for (int j = 0; j < yLen; j++) {
+ final PolynomialFunction f = yPolyX[j];
+ for (int i = 0; i < xLen; i++) {
+ fval_1[i][j] = f.value(xval[i]);
+ }
+ }
+
+ // For each line x[i] (0 <= i < xLen), construct a polynomial, with
+ // respect to variable y, fitting array fval_1[i][]
+ final PolynomialFunction[] xPolyY = new PolynomialFunction[xLen];
+ for (int i = 0; i < xLen; i++) {
+ yFitter.clearObservations();
+ for (int j = 0; j < yLen; j++) {
+ yFitter.addObservedPoint(1, yval[j], fval_1[i][j]);
+ }
+
+ // Initial guess for the fit is zero for each coefficients (of which
+ // there are "yDegree" + 1).
+ xPolyY[i] = new PolynomialFunction(yFitter.fit(new double[yDegree + 1]));
+ }
+
+ // For every knot (xval[i], yval[j]) of the grid, calculate corrected
+ // values fval_2
+ final double[][] fval_2 = new double[xLen][yLen];
+ for (int i = 0; i < xLen; i++) {
+ final PolynomialFunction f = xPolyY[i];
+ for (int j = 0; j < yLen; j++) {
+ fval_2[i][j] = f.value(yval[j]);
+ }
+ }
+
+ return super.interpolate(xval, yval, fval_2);
+ }
+}
diff --git a/src/main/java/org/apache/commons/math3/analysis/interpolation/SplineInterpolator.java b/src/main/java/org/apache/commons/math3/analysis/interpolation/SplineInterpolator.java
new file mode 100644
index 0000000..f37e1b1
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/analysis/interpolation/SplineInterpolator.java
@@ -0,0 +1,127 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math3.analysis.interpolation;
+
+import org.apache.commons.math3.analysis.polynomials.PolynomialFunction;
+import org.apache.commons.math3.analysis.polynomials.PolynomialSplineFunction;
+import org.apache.commons.math3.exception.DimensionMismatchException;
+import org.apache.commons.math3.exception.NonMonotonicSequenceException;
+import org.apache.commons.math3.exception.NumberIsTooSmallException;
+import org.apache.commons.math3.exception.util.LocalizedFormats;
+import org.apache.commons.math3.util.MathArrays;
+
+/**
+ * Computes a natural (also known as "free", "unclamped") cubic spline interpolation for the data set.
+ * <p>
+ * The {@link #interpolate(double[], double[])} method returns a {@link PolynomialSplineFunction}
+ * consisting of n cubic polynomials, defined over the subintervals determined by the x values,
+ * {@code x[0] < x[i] ... < x[n].} The x values are referred to as "knot points."
+ * <p>
+ * The value of the PolynomialSplineFunction at a point x that is greater than or equal to the smallest
+ * knot point and strictly less than the largest knot point is computed by finding the subinterval to which
+ * x belongs and computing the value of the corresponding polynomial at <code>x - x[i] </code> where
+ * <code>i</code> is the index of the subinterval. See {@link PolynomialSplineFunction} for more details.
+ * </p>
+ * <p>
+ * The interpolating polynomials satisfy: <ol>
+ * <li>The value of the PolynomialSplineFunction at each of the input x values equals the
+ * corresponding y value.</li>
+ * <li>Adjacent polynomials are equal through two derivatives at the knot points (i.e., adjacent polynomials
+ * "match up" at the knot points, as do their first and second derivatives).</li>
+ * </ol>
+ * <p>
+ * The cubic spline interpolation algorithm implemented is as described in R.L. Burden, J.D. Faires,
+ * <u>Numerical Analysis</u>, 4th Ed., 1989, PWS-Kent, ISBN 0-53491-585-X, pp 126-131.
+ * </p>
+ *
+ */
+public class SplineInterpolator implements UnivariateInterpolator {
+ /**
+ * Computes an interpolating function for the data set.
+ * @param x the arguments for the interpolation points
+ * @param y the values for the interpolation points
+ * @return a function which interpolates the data set
+ * @throws DimensionMismatchException if {@code x} and {@code y}
+ * have different sizes.
+ * @throws NonMonotonicSequenceException if {@code x} is not sorted in
+ * strict increasing order.
+ * @throws NumberIsTooSmallException if the size of {@code x} is smaller
+ * than 3.
+ */
+ public PolynomialSplineFunction interpolate(double x[], double y[])
+ throws DimensionMismatchException,
+ NumberIsTooSmallException,
+ NonMonotonicSequenceException {
+ if (x.length != y.length) {
+ throw new DimensionMismatchException(x.length, y.length);
+ }
+
+ if (x.length < 3) {
+ throw new NumberIsTooSmallException(LocalizedFormats.NUMBER_OF_POINTS,
+ x.length, 3, true);
+ }
+
+ // Number of intervals. The number of data points is n + 1.
+ final int n = x.length - 1;
+
+ MathArrays.checkOrder(x);
+
+ // Differences between knot points
+ final double h[] = new double[n];
+ for (int i = 0; i < n; i++) {
+ h[i] = x[i + 1] - x[i];
+ }
+
+ final double mu[] = new double[n];
+ final double z[] = new double[n + 1];
+ mu[0] = 0d;
+ z[0] = 0d;
+ double g = 0;
+ for (int i = 1; i < n; i++) {
+ g = 2d * (x[i+1] - x[i - 1]) - h[i - 1] * mu[i -1];
+ mu[i] = h[i] / g;
+ z[i] = (3d * (y[i + 1] * h[i - 1] - y[i] * (x[i + 1] - x[i - 1])+ y[i - 1] * h[i]) /
+ (h[i - 1] * h[i]) - h[i - 1] * z[i - 1]) / g;
+ }
+
+ // cubic spline coefficients -- b is linear, c quadratic, d is cubic (original y's are constants)
+ final double b[] = new double[n];
+ final double c[] = new double[n + 1];
+ final double d[] = new double[n];
+
+ z[n] = 0d;
+ c[n] = 0d;
+
+ for (int j = n -1; j >=0; j--) {
+ c[j] = z[j] - mu[j] * c[j + 1];
+ b[j] = (y[j + 1] - y[j]) / h[j] - h[j] * (c[j + 1] + 2d * c[j]) / 3d;
+ d[j] = (c[j + 1] - c[j]) / (3d * h[j]);
+ }
+
+ final PolynomialFunction polynomials[] = new PolynomialFunction[n];
+ final double coefficients[] = new double[4];
+ for (int i = 0; i < n; i++) {
+ coefficients[0] = y[i];
+ coefficients[1] = b[i];
+ coefficients[2] = c[i];
+ coefficients[3] = d[i];
+ polynomials[i] = new PolynomialFunction(coefficients);
+ }
+
+ return new PolynomialSplineFunction(x, polynomials);
+ }
+}
diff --git a/src/main/java/org/apache/commons/math3/analysis/interpolation/TricubicInterpolatingFunction.java b/src/main/java/org/apache/commons/math3/analysis/interpolation/TricubicInterpolatingFunction.java
new file mode 100644
index 0000000..27e9a65
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/analysis/interpolation/TricubicInterpolatingFunction.java
@@ -0,0 +1,508 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math3.analysis.interpolation;
+
+import org.apache.commons.math3.analysis.TrivariateFunction;
+import org.apache.commons.math3.exception.DimensionMismatchException;
+import org.apache.commons.math3.exception.NoDataException;
+import org.apache.commons.math3.exception.NonMonotonicSequenceException;
+import org.apache.commons.math3.exception.OutOfRangeException;
+import org.apache.commons.math3.util.MathArrays;
+
+/**
+ * Function that implements the
+ * <a href="http://en.wikipedia.org/wiki/Tricubic_interpolation">
+ * tricubic spline interpolation</a>, as proposed in
+ * <blockquote>
+ * Tricubic interpolation in three dimensions,
+ * F. Lekien and J. Marsden,
+ * <em>Int. J. Numer. Meth. Eng</em> 2005; <b>63</b>:455-471
+ * </blockquote>
+ *
+ * @since 3.4.
+ */
+public class TricubicInterpolatingFunction
+ implements TrivariateFunction {
+ /**
+ * Matrix to compute the spline coefficients from the function values
+ * and function derivatives values
+ */
+ private static final double[][] AINV = {
+ { 1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 },
+ { 0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 },
+ { -3,3,0,0,0,0,0,0,-2,-1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 },
+ { 2,-2,0,0,0,0,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 },
+ { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 },
+ { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 },
+ { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,-3,3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,-2,-1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 },
+ { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,-2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 },
+ { -3,0,3,0,0,0,0,0,0,0,0,0,0,0,0,0,-2,0,-1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 },
+ { 0,0,0,0,0,0,0,0,-3,0,3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,-2,0,-1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 },
+ { 9,-9,-9,9,0,0,0,0,6,3,-6,-3,0,0,0,0,6,-6,3,-3,0,0,0,0,0,0,0,0,0,0,0,0,4,2,2,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 },
+ { -6,6,6,-6,0,0,0,0,-3,-3,3,3,0,0,0,0,-4,4,-2,2,0,0,0,0,0,0,0,0,0,0,0,0,-2,-2,-1,-1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 },
+ { 2,0,-2,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 },
+ { 0,0,0,0,0,0,0,0,2,0,-2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 },
+ { -6,6,6,-6,0,0,0,0,-4,-2,4,2,0,0,0,0,-3,3,-3,3,0,0,0,0,0,0,0,0,0,0,0,0,-2,-1,-2,-1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 },
+ { 4,-4,-4,4,0,0,0,0,2,2,-2,-2,0,0,0,0,2,-2,2,-2,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 },
+ { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 },
+ { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 },
+ { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,-3,3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,-2,-1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 },
+ { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,-2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 },
+ { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 },
+ { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0 },
+ { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,-3,3,0,0,0,0,0,0,-2,-1,0,0,0,0,0,0 },
+ { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,-2,0,0,0,0,0,0,1,1,0,0,0,0,0,0 },
+ { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,-3,0,3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,-2,0,-1,0,0,0,0,0,0,0,0,0,0,0,0,0 },
+ { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,-3,0,3,0,0,0,0,0,0,0,0,0,0,0,0,0,-2,0,-1,0,0,0,0,0 },
+ { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,9,-9,-9,9,0,0,0,0,0,0,0,0,0,0,0,0,6,3,-6,-3,0,0,0,0,6,-6,3,-3,0,0,0,0,4,2,2,1,0,0,0,0 },
+ { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,-6,6,6,-6,0,0,0,0,0,0,0,0,0,0,0,0,-3,-3,3,3,0,0,0,0,-4,4,-2,2,0,0,0,0,-2,-2,-1,-1,0,0,0,0 },
+ { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,0,-2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0 },
+ { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,0,-2,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,1,0,0,0,0,0 },
+ { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,-6,6,6,-6,0,0,0,0,0,0,0,0,0,0,0,0,-4,-2,4,2,0,0,0,0,-3,3,-3,3,0,0,0,0,-2,-1,-2,-1,0,0,0,0 },
+ { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,4,-4,-4,4,0,0,0,0,0,0,0,0,0,0,0,0,2,2,-2,-2,0,0,0,0,2,-2,2,-2,0,0,0,0,1,1,1,1,0,0,0,0 },
+ {-3,0,0,0,3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,-2,0,0,0,-1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 },
+ { 0,0,0,0,0,0,0,0,-3,0,0,0,3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,-2,0,0,0,-1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 },
+ { 9,-9,0,0,-9,9,0,0,6,3,0,0,-6,-3,0,0,0,0,0,0,0,0,0,0,6,-6,0,0,3,-3,0,0,0,0,0,0,0,0,0,0,4,2,0,0,2,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 },
+ { -6,6,0,0,6,-6,0,0,-3,-3,0,0,3,3,0,0,0,0,0,0,0,0,0,0,-4,4,0,0,-2,2,0,0,0,0,0,0,0,0,0,0,-2,-2,0,0,-1,-1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 },
+ { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,-3,0,0,0,3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,-2,0,0,0,-1,0,0,0,0,0,0,0,0,0,0,0 },
+ { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,-3,0,0,0,3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,-2,0,0,0,-1,0,0,0 },
+ { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,9,-9,0,0,-9,9,0,0,0,0,0,0,0,0,0,0,6,3,0,0,-6,-3,0,0,0,0,0,0,0,0,0,0,6,-6,0,0,3,-3,0,0,4,2,0,0,2,1,0,0 },
+ { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,-6,6,0,0,6,-6,0,0,0,0,0,0,0,0,0,0,-3,-3,0,0,3,3,0,0,0,0,0,0,0,0,0,0,-4,4,0,0,-2,2,0,0,-2,-2,0,0,-1,-1,0,0 },
+ { 9,0,-9,0,-9,0,9,0,0,0,0,0,0,0,0,0,6,0,3,0,-6,0,-3,0,6,0,-6,0,3,0,-3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,4,0,2,0,2,0,1,0,0,0,0,0,0,0,0,0 },
+ { 0,0,0,0,0,0,0,0,9,0,-9,0,-9,0,9,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,6,0,3,0,-6,0,-3,0,6,0,-6,0,3,0,-3,0,0,0,0,0,0,0,0,0,4,0,2,0,2,0,1,0 },
+ { -27,27,27,-27,27,-27,-27,27,-18,-9,18,9,18,9,-18,-9,-18,18,-9,9,18,-18,9,-9,-18,18,18,-18,-9,9,9,-9,-12,-6,-6,-3,12,6,6,3,-12,-6,12,6,-6,-3,6,3,-12,12,-6,6,-6,6,-3,3,-8,-4,-4,-2,-4,-2,-2,-1 },
+ { 18,-18,-18,18,-18,18,18,-18,9,9,-9,-9,-9,-9,9,9,12,-12,6,-6,-12,12,-6,6,12,-12,-12,12,6,-6,-6,6,6,6,3,3,-6,-6,-3,-3,6,6,-6,-6,3,3,-3,-3,8,-8,4,-4,4,-4,2,-2,4,4,2,2,2,2,1,1 },
+ { -6,0,6,0,6,0,-6,0,0,0,0,0,0,0,0,0,-3,0,-3,0,3,0,3,0,-4,0,4,0,-2,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,-2,0,-2,0,-1,0,-1,0,0,0,0,0,0,0,0,0 },
+ { 0,0,0,0,0,0,0,0,-6,0,6,0,6,0,-6,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,-3,0,-3,0,3,0,3,0,-4,0,4,0,-2,0,2,0,0,0,0,0,0,0,0,0,-2,0,-2,0,-1,0,-1,0 },
+ { 18,-18,-18,18,-18,18,18,-18,12,6,-12,-6,-12,-6,12,6,9,-9,9,-9,-9,9,-9,9,12,-12,-12,12,6,-6,-6,6,6,3,6,3,-6,-3,-6,-3,8,4,-8,-4,4,2,-4,-2,6,-6,6,-6,3,-3,3,-3,4,2,4,2,2,1,2,1 },
+ { -12,12,12,-12,12,-12,-12,12,-6,-6,6,6,6,6,-6,-6,-6,6,-6,6,6,-6,6,-6,-8,8,8,-8,-4,4,4,-4,-3,-3,-3,-3,3,3,3,3,-4,-4,4,4,-2,-2,2,2,-4,4,-4,4,-2,2,-2,2,-2,-2,-2,-2,-1,-1,-1,-1 },
+ { 2,0,0,0,-2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 },
+ { 0,0,0,0,0,0,0,0,2,0,0,0,-2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 },
+ { -6,6,0,0,6,-6,0,0,-4,-2,0,0,4,2,0,0,0,0,0,0,0,0,0,0,-3,3,0,0,-3,3,0,0,0,0,0,0,0,0,0,0,-2,-1,0,0,-2,-1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 },
+ { 4,-4,0,0,-4,4,0,0,2,2,0,0,-2,-2,0,0,0,0,0,0,0,0,0,0,2,-2,0,0,2,-2,0,0,0,0,0,0,0,0,0,0,1,1,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 },
+ { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,0,0,0,-2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0 },
+ { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,0,0,0,-2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,1,0,0,0 },
+ { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,-6,6,0,0,6,-6,0,0,0,0,0,0,0,0,0,0,-4,-2,0,0,4,2,0,0,0,0,0,0,0,0,0,0,-3,3,0,0,-3,3,0,0,-2,-1,0,0,-2,-1,0,0 },
+ { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,4,-4,0,0,-4,4,0,0,0,0,0,0,0,0,0,0,2,2,0,0,-2,-2,0,0,0,0,0,0,0,0,0,0,2,-2,0,0,2,-2,0,0,1,1,0,0,1,1,0,0 },
+ { -6,0,6,0,6,0,-6,0,0,0,0,0,0,0,0,0,-4,0,-2,0,4,0,2,0,-3,0,3,0,-3,0,3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,-2,0,-1,0,-2,0,-1,0,0,0,0,0,0,0,0,0 },
+ { 0,0,0,0,0,0,0,0,-6,0,6,0,6,0,-6,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,-4,0,-2,0,4,0,2,0,-3,0,3,0,-3,0,3,0,0,0,0,0,0,0,0,0,-2,0,-1,0,-2,0,-1,0 },
+ { 18,-18,-18,18,-18,18,18,-18,12,6,-12,-6,-12,-6,12,6,12,-12,6,-6,-12,12,-6,6,9,-9,-9,9,9,-9,-9,9,8,4,4,2,-8,-4,-4,-2,6,3,-6,-3,6,3,-6,-3,6,-6,3,-3,6,-6,3,-3,4,2,2,1,4,2,2,1 },
+ { -12,12,12,-12,12,-12,-12,12,-6,-6,6,6,6,6,-6,-6,-8,8,-4,4,8,-8,4,-4,-6,6,6,-6,-6,6,6,-6,-4,-4,-2,-2,4,4,2,2,-3,-3,3,3,-3,-3,3,3,-4,4,-2,2,-4,4,-2,2,-2,-2,-1,-1,-2,-2,-1,-1 },
+ { 4,0,-4,0,-4,0,4,0,0,0,0,0,0,0,0,0,2,0,2,0,-2,0,-2,0,2,0,-2,0,2,0,-2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,1,0,1,0,1,0,0,0,0,0,0,0,0,0 },
+ { 0,0,0,0,0,0,0,0,4,0,-4,0,-4,0,4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,0,2,0,-2,0,-2,0,2,0,-2,0,2,0,-2,0,0,0,0,0,0,0,0,0,1,0,1,0,1,0,1,0 },
+ { -12,12,12,-12,12,-12,-12,12,-8,-4,8,4,8,4,-8,-4,-6,6,-6,6,6,-6,6,-6,-6,6,6,-6,-6,6,6,-6,-4,-2,-4,-2,4,2,4,2,-4,-2,4,2,-4,-2,4,2,-3,3,-3,3,-3,3,-3,3,-2,-1,-2,-1,-2,-1,-2,-1 },
+ { 8,-8,-8,8,-8,8,8,-8,4,4,-4,-4,-4,-4,4,4,4,-4,4,-4,-4,4,-4,4,4,-4,-4,4,4,-4,-4,4,2,2,2,2,-2,-2,-2,-2,2,2,-2,-2,2,2,-2,-2,2,-2,2,-2,2,-2,2,-2,1,1,1,1,1,1,1,1 }
+ };
+
+ /** Samples x-coordinates */
+ private final double[] xval;
+ /** Samples y-coordinates */
+ private final double[] yval;
+ /** Samples z-coordinates */
+ private final double[] zval;
+ /** Set of cubic splines patching the whole data grid */
+ private final TricubicFunction[][][] splines;
+
+ /**
+ * @param x Sample values of the x-coordinate, in increasing order.
+ * @param y Sample values of the y-coordinate, in increasing order.
+ * @param z Sample values of the y-coordinate, in increasing order.
+ * @param f Values of the function on every grid point.
+ * @param dFdX Values of the partial derivative of function with respect to x on every grid point.
+ * @param dFdY Values of the partial derivative of function with respect to y on every grid point.
+ * @param dFdZ Values of the partial derivative of function with respect to z on every grid point.
+ * @param d2FdXdY Values of the cross partial derivative of function on every grid point.
+ * @param d2FdXdZ Values of the cross partial derivative of function on every grid point.
+ * @param d2FdYdZ Values of the cross partial derivative of function on every grid point.
+ * @param d3FdXdYdZ Values of the cross partial derivative of function on every grid point.
+ * @throws NoDataException if any of the arrays has zero length.
+ * @throws DimensionMismatchException if the various arrays do not contain the expected number of elements.
+ * @throws NonMonotonicSequenceException if {@code x}, {@code y} or {@code z} are not strictly increasing.
+ */
+ public TricubicInterpolatingFunction(double[] x,
+ double[] y,
+ double[] z,
+ double[][][] f,
+ double[][][] dFdX,
+ double[][][] dFdY,
+ double[][][] dFdZ,
+ double[][][] d2FdXdY,
+ double[][][] d2FdXdZ,
+ double[][][] d2FdYdZ,
+ double[][][] d3FdXdYdZ)
+ throws NoDataException,
+ DimensionMismatchException,
+ NonMonotonicSequenceException {
+ final int xLen = x.length;
+ final int yLen = y.length;
+ final int zLen = z.length;
+
+ if (xLen == 0 || yLen == 0 || z.length == 0 || f.length == 0 || f[0].length == 0) {
+ throw new NoDataException();
+ }
+ if (xLen != f.length) {
+ throw new DimensionMismatchException(xLen, f.length);
+ }
+ if (xLen != dFdX.length) {
+ throw new DimensionMismatchException(xLen, dFdX.length);
+ }
+ if (xLen != dFdY.length) {
+ throw new DimensionMismatchException(xLen, dFdY.length);
+ }
+ if (xLen != dFdZ.length) {
+ throw new DimensionMismatchException(xLen, dFdZ.length);
+ }
+ if (xLen != d2FdXdY.length) {
+ throw new DimensionMismatchException(xLen, d2FdXdY.length);
+ }
+ if (xLen != d2FdXdZ.length) {
+ throw new DimensionMismatchException(xLen, d2FdXdZ.length);
+ }
+ if (xLen != d2FdYdZ.length) {
+ throw new DimensionMismatchException(xLen, d2FdYdZ.length);
+ }
+ if (xLen != d3FdXdYdZ.length) {
+ throw new DimensionMismatchException(xLen, d3FdXdYdZ.length);
+ }
+
+ MathArrays.checkOrder(x);
+ MathArrays.checkOrder(y);
+ MathArrays.checkOrder(z);
+
+ xval = x.clone();
+ yval = y.clone();
+ zval = z.clone();
+
+ final int lastI = xLen - 1;
+ final int lastJ = yLen - 1;
+ final int lastK = zLen - 1;
+ splines = new TricubicFunction[lastI][lastJ][lastK];
+
+ for (int i = 0; i < lastI; i++) {
+ if (f[i].length != yLen) {
+ throw new DimensionMismatchException(f[i].length, yLen);
+ }
+ if (dFdX[i].length != yLen) {
+ throw new DimensionMismatchException(dFdX[i].length, yLen);
+ }
+ if (dFdY[i].length != yLen) {
+ throw new DimensionMismatchException(dFdY[i].length, yLen);
+ }
+ if (dFdZ[i].length != yLen) {
+ throw new DimensionMismatchException(dFdZ[i].length, yLen);
+ }
+ if (d2FdXdY[i].length != yLen) {
+ throw new DimensionMismatchException(d2FdXdY[i].length, yLen);
+ }
+ if (d2FdXdZ[i].length != yLen) {
+ throw new DimensionMismatchException(d2FdXdZ[i].length, yLen);
+ }
+ if (d2FdYdZ[i].length != yLen) {
+ throw new DimensionMismatchException(d2FdYdZ[i].length, yLen);
+ }
+ if (d3FdXdYdZ[i].length != yLen) {
+ throw new DimensionMismatchException(d3FdXdYdZ[i].length, yLen);
+ }
+
+ final int ip1 = i + 1;
+ final double xR = xval[ip1] - xval[i];
+ for (int j = 0; j < lastJ; j++) {
+ if (f[i][j].length != zLen) {
+ throw new DimensionMismatchException(f[i][j].length, zLen);
+ }
+ if (dFdX[i][j].length != zLen) {
+ throw new DimensionMismatchException(dFdX[i][j].length, zLen);
+ }
+ if (dFdY[i][j].length != zLen) {
+ throw new DimensionMismatchException(dFdY[i][j].length, zLen);
+ }
+ if (dFdZ[i][j].length != zLen) {
+ throw new DimensionMismatchException(dFdZ[i][j].length, zLen);
+ }
+ if (d2FdXdY[i][j].length != zLen) {
+ throw new DimensionMismatchException(d2FdXdY[i][j].length, zLen);
+ }
+ if (d2FdXdZ[i][j].length != zLen) {
+ throw new DimensionMismatchException(d2FdXdZ[i][j].length, zLen);
+ }
+ if (d2FdYdZ[i][j].length != zLen) {
+ throw new DimensionMismatchException(d2FdYdZ[i][j].length, zLen);
+ }
+ if (d3FdXdYdZ[i][j].length != zLen) {
+ throw new DimensionMismatchException(d3FdXdYdZ[i][j].length, zLen);
+ }
+
+ final int jp1 = j + 1;
+ final double yR = yval[jp1] - yval[j];
+ final double xRyR = xR * yR;
+ for (int k = 0; k < lastK; k++) {
+ final int kp1 = k + 1;
+ final double zR = zval[kp1] - zval[k];
+ final double xRzR = xR * zR;
+ final double yRzR = yR * zR;
+ final double xRyRzR = xR * yRzR;
+
+ final double[] beta = new double[] {
+ f[i][j][k], f[ip1][j][k],
+ f[i][jp1][k], f[ip1][jp1][k],
+ f[i][j][kp1], f[ip1][j][kp1],
+ f[i][jp1][kp1], f[ip1][jp1][kp1],
+
+ dFdX[i][j][k] * xR, dFdX[ip1][j][k] * xR,
+ dFdX[i][jp1][k] * xR, dFdX[ip1][jp1][k] * xR,
+ dFdX[i][j][kp1] * xR, dFdX[ip1][j][kp1] * xR,
+ dFdX[i][jp1][kp1] * xR, dFdX[ip1][jp1][kp1] * xR,
+
+ dFdY[i][j][k] * yR, dFdY[ip1][j][k] * yR,
+ dFdY[i][jp1][k] * yR, dFdY[ip1][jp1][k] * yR,
+ dFdY[i][j][kp1] * yR, dFdY[ip1][j][kp1] * yR,
+ dFdY[i][jp1][kp1] * yR, dFdY[ip1][jp1][kp1] * yR,
+
+ dFdZ[i][j][k] * zR, dFdZ[ip1][j][k] * zR,
+ dFdZ[i][jp1][k] * zR, dFdZ[ip1][jp1][k] * zR,
+ dFdZ[i][j][kp1] * zR, dFdZ[ip1][j][kp1] * zR,
+ dFdZ[i][jp1][kp1] * zR, dFdZ[ip1][jp1][kp1] * zR,
+
+ d2FdXdY[i][j][k] * xRyR, d2FdXdY[ip1][j][k] * xRyR,
+ d2FdXdY[i][jp1][k] * xRyR, d2FdXdY[ip1][jp1][k] * xRyR,
+ d2FdXdY[i][j][kp1] * xRyR, d2FdXdY[ip1][j][kp1] * xRyR,
+ d2FdXdY[i][jp1][kp1] * xRyR, d2FdXdY[ip1][jp1][kp1] * xRyR,
+
+ d2FdXdZ[i][j][k] * xRzR, d2FdXdZ[ip1][j][k] * xRzR,
+ d2FdXdZ[i][jp1][k] * xRzR, d2FdXdZ[ip1][jp1][k] * xRzR,
+ d2FdXdZ[i][j][kp1] * xRzR, d2FdXdZ[ip1][j][kp1] * xRzR,
+ d2FdXdZ[i][jp1][kp1] * xRzR, d2FdXdZ[ip1][jp1][kp1] * xRzR,
+
+ d2FdYdZ[i][j][k] * yRzR, d2FdYdZ[ip1][j][k] * yRzR,
+ d2FdYdZ[i][jp1][k] * yRzR, d2FdYdZ[ip1][jp1][k] * yRzR,
+ d2FdYdZ[i][j][kp1] * yRzR, d2FdYdZ[ip1][j][kp1] * yRzR,
+ d2FdYdZ[i][jp1][kp1] * yRzR, d2FdYdZ[ip1][jp1][kp1] * yRzR,
+
+ d3FdXdYdZ[i][j][k] * xRyRzR, d3FdXdYdZ[ip1][j][k] * xRyRzR,
+ d3FdXdYdZ[i][jp1][k] * xRyRzR, d3FdXdYdZ[ip1][jp1][k] * xRyRzR,
+ d3FdXdYdZ[i][j][kp1] * xRyRzR, d3FdXdYdZ[ip1][j][kp1] * xRyRzR,
+ d3FdXdYdZ[i][jp1][kp1] * xRyRzR, d3FdXdYdZ[ip1][jp1][kp1] * xRyRzR,
+ };
+
+ splines[i][j][k] = new TricubicFunction(computeCoefficients(beta));
+ }
+ }
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @throws OutOfRangeException if any of the variables is outside its interpolation range.
+ */
+ public double value(double x, double y, double z)
+ throws OutOfRangeException {
+ final int i = searchIndex(x, xval);
+ if (i == -1) {
+ throw new OutOfRangeException(x, xval[0], xval[xval.length - 1]);
+ }
+ final int j = searchIndex(y, yval);
+ if (j == -1) {
+ throw new OutOfRangeException(y, yval[0], yval[yval.length - 1]);
+ }
+ final int k = searchIndex(z, zval);
+ if (k == -1) {
+ throw new OutOfRangeException(z, zval[0], zval[zval.length - 1]);
+ }
+
+ final double xN = (x - xval[i]) / (xval[i + 1] - xval[i]);
+ final double yN = (y - yval[j]) / (yval[j + 1] - yval[j]);
+ final double zN = (z - zval[k]) / (zval[k + 1] - zval[k]);
+
+ return splines[i][j][k].value(xN, yN, zN);
+ }
+
+ /**
+ * Indicates whether a point is within the interpolation range.
+ *
+ * @param x First coordinate.
+ * @param y Second coordinate.
+ * @param z Third coordinate.
+ * @return {@code true} if (x, y, z) is a valid point.
+ */
+ public boolean isValidPoint(double x, double y, double z) {
+ if (x < xval[0] ||
+ x > xval[xval.length - 1] ||
+ y < yval[0] ||
+ y > yval[yval.length - 1] ||
+ z < zval[0] ||
+ z > zval[zval.length - 1]) {
+ return false;
+ } else {
+ return true;
+ }
+ }
+
+ /**
+ * @param c Coordinate.
+ * @param val Coordinate samples.
+ * @return the index in {@code val} corresponding to the interval containing {@code c}, or {@code -1}
+ * if {@code c} is out of the range defined by the end values of {@code val}.
+ */
+ private int searchIndex(double c, double[] val) {
+ if (c < val[0]) {
+ return -1;
+ }
+
+ final int max = val.length;
+ for (int i = 1; i < max; i++) {
+ if (c <= val[i]) {
+ return i - 1;
+ }
+ }
+
+ return -1;
+ }
+
+ /**
+ * Compute the spline coefficients from the list of function values and
+ * function partial derivatives values at the four corners of a grid
+ * element. They must be specified in the following order:
+ * <ul>
+ * <li>f(0,0,0)</li>
+ * <li>f(1,0,0)</li>
+ * <li>f(0,1,0)</li>
+ * <li>f(1,1,0)</li>
+ * <li>f(0,0,1)</li>
+ * <li>f(1,0,1)</li>
+ * <li>f(0,1,1)</li>
+ * <li>f(1,1,1)</li>
+ *
+ * <li>f<sub>x</sub>(0,0,0)</li>
+ * <li>... <em>(same order as above)</em></li>
+ * <li>f<sub>x</sub>(1,1,1)</li>
+ *
+ * <li>f<sub>y</sub>(0,0,0)</li>
+ * <li>... <em>(same order as above)</em></li>
+ * <li>f<sub>y</sub>(1,1,1)</li>
+ *
+ * <li>f<sub>z</sub>(0,0,0)</li>
+ * <li>... <em>(same order as above)</em></li>
+ * <li>f<sub>z</sub>(1,1,1)</li>
+ *
+ * <li>f<sub>xy</sub>(0,0,0)</li>
+ * <li>... <em>(same order as above)</em></li>
+ * <li>f<sub>xy</sub>(1,1,1)</li>
+ *
+ * <li>f<sub>xz</sub>(0,0,0)</li>
+ * <li>... <em>(same order as above)</em></li>
+ * <li>f<sub>xz</sub>(1,1,1)</li>
+ *
+ * <li>f<sub>yz</sub>(0,0,0)</li>
+ * <li>... <em>(same order as above)</em></li>
+ * <li>f<sub>yz</sub>(1,1,1)</li>
+ *
+ * <li>f<sub>xyz</sub>(0,0,0)</li>
+ * <li>... <em>(same order as above)</em></li>
+ * <li>f<sub>xyz</sub>(1,1,1)</li>
+ * </ul>
+ * where the subscripts indicate the partial derivative with respect to
+ * the corresponding variable(s).
+ *
+ * @param beta List of function values and function partial derivatives values.
+ * @return the spline coefficients.
+ */
+ private double[] computeCoefficients(double[] beta) {
+ final int sz = 64;
+ final double[] a = new double[sz];
+
+ for (int i = 0; i < sz; i++) {
+ double result = 0;
+ final double[] row = AINV[i];
+ for (int j = 0; j < sz; j++) {
+ result += row[j] * beta[j];
+ }
+ a[i] = result;
+ }
+
+ return a;
+ }
+}
+
+/**
+ * 3D-spline function.
+ *
+ */
+class TricubicFunction
+ implements TrivariateFunction {
+ /** Number of points. */
+ private static final short N = 4;
+ /** Coefficients */
+ private final double[][][] a = new double[N][N][N];
+
+ /**
+ * @param aV List of spline coefficients.
+ */
+ TricubicFunction(double[] aV) {
+ for (int i = 0; i < N; i++) {
+ for (int j = 0; j < N; j++) {
+ for (int k = 0; k < N; k++) {
+ a[i][j][k] = aV[i + N * (j + N * k)];
+ }
+ }
+ }
+ }
+
+ /**
+ * @param x x-coordinate of the interpolation point.
+ * @param y y-coordinate of the interpolation point.
+ * @param z z-coordinate of the interpolation point.
+ * @return the interpolated value.
+ * @throws OutOfRangeException if {@code x}, {@code y} or
+ * {@code z} are not in the interval {@code [0, 1]}.
+ */
+ public double value(double x, double y, double z)
+ throws OutOfRangeException {
+ if (x < 0 || x > 1) {
+ throw new OutOfRangeException(x, 0, 1);
+ }
+ if (y < 0 || y > 1) {
+ throw new OutOfRangeException(y, 0, 1);
+ }
+ if (z < 0 || z > 1) {
+ throw new OutOfRangeException(z, 0, 1);
+ }
+
+ final double x2 = x * x;
+ final double x3 = x2 * x;
+ final double[] pX = { 1, x, x2, x3 };
+
+ final double y2 = y * y;
+ final double y3 = y2 * y;
+ final double[] pY = { 1, y, y2, y3 };
+
+ final double z2 = z * z;
+ final double z3 = z2 * z;
+ final double[] pZ = { 1, z, z2, z3 };
+
+ double result = 0;
+ for (int i = 0; i < N; i++) {
+ for (int j = 0; j < N; j++) {
+ for (int k = 0; k < N; k++) {
+ result += a[i][j][k] * pX[i] * pY[j] * pZ[k];
+ }
+ }
+ }
+
+ return result;
+ }
+}
diff --git a/src/main/java/org/apache/commons/math3/analysis/interpolation/TricubicInterpolator.java b/src/main/java/org/apache/commons/math3/analysis/interpolation/TricubicInterpolator.java
new file mode 100644
index 0000000..ec9e3bb
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/analysis/interpolation/TricubicInterpolator.java
@@ -0,0 +1,143 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math3.analysis.interpolation;
+
+import org.apache.commons.math3.exception.DimensionMismatchException;
+import org.apache.commons.math3.exception.NoDataException;
+import org.apache.commons.math3.exception.NonMonotonicSequenceException;
+import org.apache.commons.math3.exception.NumberIsTooSmallException;
+import org.apache.commons.math3.util.MathArrays;
+
+/**
+ * Generates a tricubic interpolating function.
+ *
+ * @since 3.4
+ */
+public class TricubicInterpolator
+ implements TrivariateGridInterpolator {
+ /**
+ * {@inheritDoc}
+ */
+ public TricubicInterpolatingFunction interpolate(final double[] xval,
+ final double[] yval,
+ final double[] zval,
+ final double[][][] fval)
+ throws NoDataException, NumberIsTooSmallException,
+ DimensionMismatchException, NonMonotonicSequenceException {
+ if (xval.length == 0 || yval.length == 0 || zval.length == 0 || fval.length == 0) {
+ throw new NoDataException();
+ }
+ if (xval.length != fval.length) {
+ throw new DimensionMismatchException(xval.length, fval.length);
+ }
+
+ MathArrays.checkOrder(xval);
+ MathArrays.checkOrder(yval);
+ MathArrays.checkOrder(zval);
+
+ final int xLen = xval.length;
+ final int yLen = yval.length;
+ final int zLen = zval.length;
+
+ // Approximation to the partial derivatives using finite differences.
+ final double[][][] dFdX = new double[xLen][yLen][zLen];
+ final double[][][] dFdY = new double[xLen][yLen][zLen];
+ final double[][][] dFdZ = new double[xLen][yLen][zLen];
+ final double[][][] d2FdXdY = new double[xLen][yLen][zLen];
+ final double[][][] d2FdXdZ = new double[xLen][yLen][zLen];
+ final double[][][] d2FdYdZ = new double[xLen][yLen][zLen];
+ final double[][][] d3FdXdYdZ = new double[xLen][yLen][zLen];
+
+ for (int i = 1; i < xLen - 1; i++) {
+ if (yval.length != fval[i].length) {
+ throw new DimensionMismatchException(yval.length, fval[i].length);
+ }
+
+ final int nI = i + 1;
+ final int pI = i - 1;
+
+ final double nX = xval[nI];
+ final double pX = xval[pI];
+
+ final double deltaX = nX - pX;
+
+ for (int j = 1; j < yLen - 1; j++) {
+ if (zval.length != fval[i][j].length) {
+ throw new DimensionMismatchException(zval.length, fval[i][j].length);
+ }
+
+ final int nJ = j + 1;
+ final int pJ = j - 1;
+
+ final double nY = yval[nJ];
+ final double pY = yval[pJ];
+
+ final double deltaY = nY - pY;
+ final double deltaXY = deltaX * deltaY;
+
+ for (int k = 1; k < zLen - 1; k++) {
+ final int nK = k + 1;
+ final int pK = k - 1;
+
+ final double nZ = zval[nK];
+ final double pZ = zval[pK];
+
+ final double deltaZ = nZ - pZ;
+
+ dFdX[i][j][k] = (fval[nI][j][k] - fval[pI][j][k]) / deltaX;
+ dFdY[i][j][k] = (fval[i][nJ][k] - fval[i][pJ][k]) / deltaY;
+ dFdZ[i][j][k] = (fval[i][j][nK] - fval[i][j][pK]) / deltaZ;
+
+ final double deltaXZ = deltaX * deltaZ;
+ final double deltaYZ = deltaY * deltaZ;
+
+ d2FdXdY[i][j][k] = (fval[nI][nJ][k] - fval[nI][pJ][k] - fval[pI][nJ][k] + fval[pI][pJ][k]) / deltaXY;
+ d2FdXdZ[i][j][k] = (fval[nI][j][nK] - fval[nI][j][pK] - fval[pI][j][nK] + fval[pI][j][pK]) / deltaXZ;
+ d2FdYdZ[i][j][k] = (fval[i][nJ][nK] - fval[i][nJ][pK] - fval[i][pJ][nK] + fval[i][pJ][pK]) / deltaYZ;
+
+ final double deltaXYZ = deltaXY * deltaZ;
+
+ d3FdXdYdZ[i][j][k] = (fval[nI][nJ][nK] - fval[nI][pJ][nK] -
+ fval[pI][nJ][nK] + fval[pI][pJ][nK] -
+ fval[nI][nJ][pK] + fval[nI][pJ][pK] +
+ fval[pI][nJ][pK] - fval[pI][pJ][pK]) / deltaXYZ;
+ }
+ }
+ }
+
+ // Create the interpolating function.
+ return new TricubicInterpolatingFunction(xval, yval, zval, fval,
+ dFdX, dFdY, dFdZ,
+ d2FdXdY, d2FdXdZ, d2FdYdZ,
+ d3FdXdYdZ) {
+ /** {@inheritDoc} */
+ @Override
+ public boolean isValidPoint(double x, double y, double z) {
+ if (x < xval[1] ||
+ x > xval[xval.length - 2] ||
+ y < yval[1] ||
+ y > yval[yval.length - 2] ||
+ z < zval[1] ||
+ z > zval[zval.length - 2]) {
+ return false;
+ } else {
+ return true;
+ }
+ }
+ };
+ }
+}
diff --git a/src/main/java/org/apache/commons/math3/analysis/interpolation/TricubicSplineInterpolatingFunction.java b/src/main/java/org/apache/commons/math3/analysis/interpolation/TricubicSplineInterpolatingFunction.java
new file mode 100644
index 0000000..96aebd3
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/analysis/interpolation/TricubicSplineInterpolatingFunction.java
@@ -0,0 +1,482 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math3.analysis.interpolation;
+
+import org.apache.commons.math3.analysis.TrivariateFunction;
+import org.apache.commons.math3.exception.DimensionMismatchException;
+import org.apache.commons.math3.exception.NoDataException;
+import org.apache.commons.math3.exception.NonMonotonicSequenceException;
+import org.apache.commons.math3.exception.OutOfRangeException;
+import org.apache.commons.math3.util.MathArrays;
+
+/**
+ * Function that implements the
+ * <a href="http://en.wikipedia.org/wiki/Tricubic_interpolation">
+ * tricubic spline interpolation</a>, as proposed in
+ * <blockquote>
+ * Tricubic interpolation in three dimensions,
+ * F. Lekien and J. Marsden,
+ * <em>Int. J. Numer. Meth. Engng</em> 2005; <b>63</b>:455-471
+ * </blockquote>
+ *
+ * @since 2.2
+ * @deprecated To be removed in 4.0 (see MATH-1166).
+ */
+@Deprecated
+public class TricubicSplineInterpolatingFunction
+ implements TrivariateFunction {
+ /**
+ * Matrix to compute the spline coefficients from the function values
+ * and function derivatives values
+ */
+ private static final double[][] AINV = {
+ { 1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 },
+ { 0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 },
+ { -3,3,0,0,0,0,0,0,-2,-1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 },
+ { 2,-2,0,0,0,0,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 },
+ { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 },
+ { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 },
+ { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,-3,3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,-2,-1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 },
+ { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,-2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 },
+ { -3,0,3,0,0,0,0,0,0,0,0,0,0,0,0,0,-2,0,-1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 },
+ { 0,0,0,0,0,0,0,0,-3,0,3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,-2,0,-1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 },
+ { 9,-9,-9,9,0,0,0,0,6,3,-6,-3,0,0,0,0,6,-6,3,-3,0,0,0,0,0,0,0,0,0,0,0,0,4,2,2,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 },
+ { -6,6,6,-6,0,0,0,0,-3,-3,3,3,0,0,0,0,-4,4,-2,2,0,0,0,0,0,0,0,0,0,0,0,0,-2,-2,-1,-1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 },
+ { 2,0,-2,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 },
+ { 0,0,0,0,0,0,0,0,2,0,-2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 },
+ { -6,6,6,-6,0,0,0,0,-4,-2,4,2,0,0,0,0,-3,3,-3,3,0,0,0,0,0,0,0,0,0,0,0,0,-2,-1,-2,-1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 },
+ { 4,-4,-4,4,0,0,0,0,2,2,-2,-2,0,0,0,0,2,-2,2,-2,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 },
+ { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 },
+ { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 },
+ { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,-3,3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,-2,-1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 },
+ { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,-2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 },
+ { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 },
+ { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0 },
+ { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,-3,3,0,0,0,0,0,0,-2,-1,0,0,0,0,0,0 },
+ { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,-2,0,0,0,0,0,0,1,1,0,0,0,0,0,0 },
+ { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,-3,0,3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,-2,0,-1,0,0,0,0,0,0,0,0,0,0,0,0,0 },
+ { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,-3,0,3,0,0,0,0,0,0,0,0,0,0,0,0,0,-2,0,-1,0,0,0,0,0 },
+ { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,9,-9,-9,9,0,0,0,0,0,0,0,0,0,0,0,0,6,3,-6,-3,0,0,0,0,6,-6,3,-3,0,0,0,0,4,2,2,1,0,0,0,0 },
+ { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,-6,6,6,-6,0,0,0,0,0,0,0,0,0,0,0,0,-3,-3,3,3,0,0,0,0,-4,4,-2,2,0,0,0,0,-2,-2,-1,-1,0,0,0,0 },
+ { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,0,-2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0 },
+ { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,0,-2,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,1,0,0,0,0,0 },
+ { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,-6,6,6,-6,0,0,0,0,0,0,0,0,0,0,0,0,-4,-2,4,2,0,0,0,0,-3,3,-3,3,0,0,0,0,-2,-1,-2,-1,0,0,0,0 },
+ { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,4,-4,-4,4,0,0,0,0,0,0,0,0,0,0,0,0,2,2,-2,-2,0,0,0,0,2,-2,2,-2,0,0,0,0,1,1,1,1,0,0,0,0 },
+ {-3,0,0,0,3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,-2,0,0,0,-1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 },
+ { 0,0,0,0,0,0,0,0,-3,0,0,0,3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,-2,0,0,0,-1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 },
+ { 9,-9,0,0,-9,9,0,0,6,3,0,0,-6,-3,0,0,0,0,0,0,0,0,0,0,6,-6,0,0,3,-3,0,0,0,0,0,0,0,0,0,0,4,2,0,0,2,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 },
+ { -6,6,0,0,6,-6,0,0,-3,-3,0,0,3,3,0,0,0,0,0,0,0,0,0,0,-4,4,0,0,-2,2,0,0,0,0,0,0,0,0,0,0,-2,-2,0,0,-1,-1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 },
+ { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,-3,0,0,0,3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,-2,0,0,0,-1,0,0,0,0,0,0,0,0,0,0,0 },
+ { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,-3,0,0,0,3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,-2,0,0,0,-1,0,0,0 },
+ { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,9,-9,0,0,-9,9,0,0,0,0,0,0,0,0,0,0,6,3,0,0,-6,-3,0,0,0,0,0,0,0,0,0,0,6,-6,0,0,3,-3,0,0,4,2,0,0,2,1,0,0 },
+ { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,-6,6,0,0,6,-6,0,0,0,0,0,0,0,0,0,0,-3,-3,0,0,3,3,0,0,0,0,0,0,0,0,0,0,-4,4,0,0,-2,2,0,0,-2,-2,0,0,-1,-1,0,0 },
+ { 9,0,-9,0,-9,0,9,0,0,0,0,0,0,0,0,0,6,0,3,0,-6,0,-3,0,6,0,-6,0,3,0,-3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,4,0,2,0,2,0,1,0,0,0,0,0,0,0,0,0 },
+ { 0,0,0,0,0,0,0,0,9,0,-9,0,-9,0,9,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,6,0,3,0,-6,0,-3,0,6,0,-6,0,3,0,-3,0,0,0,0,0,0,0,0,0,4,0,2,0,2,0,1,0 },
+ { -27,27,27,-27,27,-27,-27,27,-18,-9,18,9,18,9,-18,-9,-18,18,-9,9,18,-18,9,-9,-18,18,18,-18,-9,9,9,-9,-12,-6,-6,-3,12,6,6,3,-12,-6,12,6,-6,-3,6,3,-12,12,-6,6,-6,6,-3,3,-8,-4,-4,-2,-4,-2,-2,-1 },
+ { 18,-18,-18,18,-18,18,18,-18,9,9,-9,-9,-9,-9,9,9,12,-12,6,-6,-12,12,-6,6,12,-12,-12,12,6,-6,-6,6,6,6,3,3,-6,-6,-3,-3,6,6,-6,-6,3,3,-3,-3,8,-8,4,-4,4,-4,2,-2,4,4,2,2,2,2,1,1 },
+ { -6,0,6,0,6,0,-6,0,0,0,0,0,0,0,0,0,-3,0,-3,0,3,0,3,0,-4,0,4,0,-2,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,-2,0,-2,0,-1,0,-1,0,0,0,0,0,0,0,0,0 },
+ { 0,0,0,0,0,0,0,0,-6,0,6,0,6,0,-6,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,-3,0,-3,0,3,0,3,0,-4,0,4,0,-2,0,2,0,0,0,0,0,0,0,0,0,-2,0,-2,0,-1,0,-1,0 },
+ { 18,-18,-18,18,-18,18,18,-18,12,6,-12,-6,-12,-6,12,6,9,-9,9,-9,-9,9,-9,9,12,-12,-12,12,6,-6,-6,6,6,3,6,3,-6,-3,-6,-3,8,4,-8,-4,4,2,-4,-2,6,-6,6,-6,3,-3,3,-3,4,2,4,2,2,1,2,1 },
+ { -12,12,12,-12,12,-12,-12,12,-6,-6,6,6,6,6,-6,-6,-6,6,-6,6,6,-6,6,-6,-8,8,8,-8,-4,4,4,-4,-3,-3,-3,-3,3,3,3,3,-4,-4,4,4,-2,-2,2,2,-4,4,-4,4,-2,2,-2,2,-2,-2,-2,-2,-1,-1,-1,-1 },
+ { 2,0,0,0,-2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 },
+ { 0,0,0,0,0,0,0,0,2,0,0,0,-2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 },
+ { -6,6,0,0,6,-6,0,0,-4,-2,0,0,4,2,0,0,0,0,0,0,0,0,0,0,-3,3,0,0,-3,3,0,0,0,0,0,0,0,0,0,0,-2,-1,0,0,-2,-1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 },
+ { 4,-4,0,0,-4,4,0,0,2,2,0,0,-2,-2,0,0,0,0,0,0,0,0,0,0,2,-2,0,0,2,-2,0,0,0,0,0,0,0,0,0,0,1,1,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 },
+ { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,0,0,0,-2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0 },
+ { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,0,0,0,-2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,1,0,0,0 },
+ { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,-6,6,0,0,6,-6,0,0,0,0,0,0,0,0,0,0,-4,-2,0,0,4,2,0,0,0,0,0,0,0,0,0,0,-3,3,0,0,-3,3,0,0,-2,-1,0,0,-2,-1,0,0 },
+ { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,4,-4,0,0,-4,4,0,0,0,0,0,0,0,0,0,0,2,2,0,0,-2,-2,0,0,0,0,0,0,0,0,0,0,2,-2,0,0,2,-2,0,0,1,1,0,0,1,1,0,0 },
+ { -6,0,6,0,6,0,-6,0,0,0,0,0,0,0,0,0,-4,0,-2,0,4,0,2,0,-3,0,3,0,-3,0,3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,-2,0,-1,0,-2,0,-1,0,0,0,0,0,0,0,0,0 },
+ { 0,0,0,0,0,0,0,0,-6,0,6,0,6,0,-6,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,-4,0,-2,0,4,0,2,0,-3,0,3,0,-3,0,3,0,0,0,0,0,0,0,0,0,-2,0,-1,0,-2,0,-1,0 },
+ { 18,-18,-18,18,-18,18,18,-18,12,6,-12,-6,-12,-6,12,6,12,-12,6,-6,-12,12,-6,6,9,-9,-9,9,9,-9,-9,9,8,4,4,2,-8,-4,-4,-2,6,3,-6,-3,6,3,-6,-3,6,-6,3,-3,6,-6,3,-3,4,2,2,1,4,2,2,1 },
+ { -12,12,12,-12,12,-12,-12,12,-6,-6,6,6,6,6,-6,-6,-8,8,-4,4,8,-8,4,-4,-6,6,6,-6,-6,6,6,-6,-4,-4,-2,-2,4,4,2,2,-3,-3,3,3,-3,-3,3,3,-4,4,-2,2,-4,4,-2,2,-2,-2,-1,-1,-2,-2,-1,-1 },
+ { 4,0,-4,0,-4,0,4,0,0,0,0,0,0,0,0,0,2,0,2,0,-2,0,-2,0,2,0,-2,0,2,0,-2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,1,0,1,0,1,0,0,0,0,0,0,0,0,0 },
+ { 0,0,0,0,0,0,0,0,4,0,-4,0,-4,0,4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,0,2,0,-2,0,-2,0,2,0,-2,0,2,0,-2,0,0,0,0,0,0,0,0,0,1,0,1,0,1,0,1,0 },
+ { -12,12,12,-12,12,-12,-12,12,-8,-4,8,4,8,4,-8,-4,-6,6,-6,6,6,-6,6,-6,-6,6,6,-6,-6,6,6,-6,-4,-2,-4,-2,4,2,4,2,-4,-2,4,2,-4,-2,4,2,-3,3,-3,3,-3,3,-3,3,-2,-1,-2,-1,-2,-1,-2,-1 },
+ { 8,-8,-8,8,-8,8,8,-8,4,4,-4,-4,-4,-4,4,4,4,-4,4,-4,-4,4,-4,4,4,-4,-4,4,4,-4,-4,4,2,2,2,2,-2,-2,-2,-2,2,2,-2,-2,2,2,-2,-2,2,-2,2,-2,2,-2,2,-2,1,1,1,1,1,1,1,1 }
+ };
+
+ /** Samples x-coordinates */
+ private final double[] xval;
+ /** Samples y-coordinates */
+ private final double[] yval;
+ /** Samples z-coordinates */
+ private final double[] zval;
+ /** Set of cubic splines pacthing the whole data grid */
+ private final TricubicSplineFunction[][][] splines;
+
+ /**
+ * @param x Sample values of the x-coordinate, in increasing order.
+ * @param y Sample values of the y-coordinate, in increasing order.
+ * @param z Sample values of the y-coordinate, in increasing order.
+ * @param f Values of the function on every grid point.
+ * @param dFdX Values of the partial derivative of function with respect to x on every grid point.
+ * @param dFdY Values of the partial derivative of function with respect to y on every grid point.
+ * @param dFdZ Values of the partial derivative of function with respect to z on every grid point.
+ * @param d2FdXdY Values of the cross partial derivative of function on every grid point.
+ * @param d2FdXdZ Values of the cross partial derivative of function on every grid point.
+ * @param d2FdYdZ Values of the cross partial derivative of function on every grid point.
+ * @param d3FdXdYdZ Values of the cross partial derivative of function on every grid point.
+ * @throws NoDataException if any of the arrays has zero length.
+ * @throws DimensionMismatchException if the various arrays do not contain the expected number of elements.
+ * @throws NonMonotonicSequenceException if {@code x}, {@code y} or {@code z} are not strictly increasing.
+ */
+ public TricubicSplineInterpolatingFunction(double[] x,
+ double[] y,
+ double[] z,
+ double[][][] f,
+ double[][][] dFdX,
+ double[][][] dFdY,
+ double[][][] dFdZ,
+ double[][][] d2FdXdY,
+ double[][][] d2FdXdZ,
+ double[][][] d2FdYdZ,
+ double[][][] d3FdXdYdZ)
+ throws NoDataException,
+ DimensionMismatchException,
+ NonMonotonicSequenceException {
+ final int xLen = x.length;
+ final int yLen = y.length;
+ final int zLen = z.length;
+
+ if (xLen == 0 || yLen == 0 || z.length == 0 || f.length == 0 || f[0].length == 0) {
+ throw new NoDataException();
+ }
+ if (xLen != f.length) {
+ throw new DimensionMismatchException(xLen, f.length);
+ }
+ if (xLen != dFdX.length) {
+ throw new DimensionMismatchException(xLen, dFdX.length);
+ }
+ if (xLen != dFdY.length) {
+ throw new DimensionMismatchException(xLen, dFdY.length);
+ }
+ if (xLen != dFdZ.length) {
+ throw new DimensionMismatchException(xLen, dFdZ.length);
+ }
+ if (xLen != d2FdXdY.length) {
+ throw new DimensionMismatchException(xLen, d2FdXdY.length);
+ }
+ if (xLen != d2FdXdZ.length) {
+ throw new DimensionMismatchException(xLen, d2FdXdZ.length);
+ }
+ if (xLen != d2FdYdZ.length) {
+ throw new DimensionMismatchException(xLen, d2FdYdZ.length);
+ }
+ if (xLen != d3FdXdYdZ.length) {
+ throw new DimensionMismatchException(xLen, d3FdXdYdZ.length);
+ }
+
+ MathArrays.checkOrder(x);
+ MathArrays.checkOrder(y);
+ MathArrays.checkOrder(z);
+
+ xval = x.clone();
+ yval = y.clone();
+ zval = z.clone();
+
+ final int lastI = xLen - 1;
+ final int lastJ = yLen - 1;
+ final int lastK = zLen - 1;
+ splines = new TricubicSplineFunction[lastI][lastJ][lastK];
+
+ for (int i = 0; i < lastI; i++) {
+ if (f[i].length != yLen) {
+ throw new DimensionMismatchException(f[i].length, yLen);
+ }
+ if (dFdX[i].length != yLen) {
+ throw new DimensionMismatchException(dFdX[i].length, yLen);
+ }
+ if (dFdY[i].length != yLen) {
+ throw new DimensionMismatchException(dFdY[i].length, yLen);
+ }
+ if (dFdZ[i].length != yLen) {
+ throw new DimensionMismatchException(dFdZ[i].length, yLen);
+ }
+ if (d2FdXdY[i].length != yLen) {
+ throw new DimensionMismatchException(d2FdXdY[i].length, yLen);
+ }
+ if (d2FdXdZ[i].length != yLen) {
+ throw new DimensionMismatchException(d2FdXdZ[i].length, yLen);
+ }
+ if (d2FdYdZ[i].length != yLen) {
+ throw new DimensionMismatchException(d2FdYdZ[i].length, yLen);
+ }
+ if (d3FdXdYdZ[i].length != yLen) {
+ throw new DimensionMismatchException(d3FdXdYdZ[i].length, yLen);
+ }
+
+ final int ip1 = i + 1;
+ for (int j = 0; j < lastJ; j++) {
+ if (f[i][j].length != zLen) {
+ throw new DimensionMismatchException(f[i][j].length, zLen);
+ }
+ if (dFdX[i][j].length != zLen) {
+ throw new DimensionMismatchException(dFdX[i][j].length, zLen);
+ }
+ if (dFdY[i][j].length != zLen) {
+ throw new DimensionMismatchException(dFdY[i][j].length, zLen);
+ }
+ if (dFdZ[i][j].length != zLen) {
+ throw new DimensionMismatchException(dFdZ[i][j].length, zLen);
+ }
+ if (d2FdXdY[i][j].length != zLen) {
+ throw new DimensionMismatchException(d2FdXdY[i][j].length, zLen);
+ }
+ if (d2FdXdZ[i][j].length != zLen) {
+ throw new DimensionMismatchException(d2FdXdZ[i][j].length, zLen);
+ }
+ if (d2FdYdZ[i][j].length != zLen) {
+ throw new DimensionMismatchException(d2FdYdZ[i][j].length, zLen);
+ }
+ if (d3FdXdYdZ[i][j].length != zLen) {
+ throw new DimensionMismatchException(d3FdXdYdZ[i][j].length, zLen);
+ }
+
+ final int jp1 = j + 1;
+ for (int k = 0; k < lastK; k++) {
+ final int kp1 = k + 1;
+
+ final double[] beta = new double[] {
+ f[i][j][k], f[ip1][j][k],
+ f[i][jp1][k], f[ip1][jp1][k],
+ f[i][j][kp1], f[ip1][j][kp1],
+ f[i][jp1][kp1], f[ip1][jp1][kp1],
+
+ dFdX[i][j][k], dFdX[ip1][j][k],
+ dFdX[i][jp1][k], dFdX[ip1][jp1][k],
+ dFdX[i][j][kp1], dFdX[ip1][j][kp1],
+ dFdX[i][jp1][kp1], dFdX[ip1][jp1][kp1],
+
+ dFdY[i][j][k], dFdY[ip1][j][k],
+ dFdY[i][jp1][k], dFdY[ip1][jp1][k],
+ dFdY[i][j][kp1], dFdY[ip1][j][kp1],
+ dFdY[i][jp1][kp1], dFdY[ip1][jp1][kp1],
+
+ dFdZ[i][j][k], dFdZ[ip1][j][k],
+ dFdZ[i][jp1][k], dFdZ[ip1][jp1][k],
+ dFdZ[i][j][kp1], dFdZ[ip1][j][kp1],
+ dFdZ[i][jp1][kp1], dFdZ[ip1][jp1][kp1],
+
+ d2FdXdY[i][j][k], d2FdXdY[ip1][j][k],
+ d2FdXdY[i][jp1][k], d2FdXdY[ip1][jp1][k],
+ d2FdXdY[i][j][kp1], d2FdXdY[ip1][j][kp1],
+ d2FdXdY[i][jp1][kp1], d2FdXdY[ip1][jp1][kp1],
+
+ d2FdXdZ[i][j][k], d2FdXdZ[ip1][j][k],
+ d2FdXdZ[i][jp1][k], d2FdXdZ[ip1][jp1][k],
+ d2FdXdZ[i][j][kp1], d2FdXdZ[ip1][j][kp1],
+ d2FdXdZ[i][jp1][kp1], d2FdXdZ[ip1][jp1][kp1],
+
+ d2FdYdZ[i][j][k], d2FdYdZ[ip1][j][k],
+ d2FdYdZ[i][jp1][k], d2FdYdZ[ip1][jp1][k],
+ d2FdYdZ[i][j][kp1], d2FdYdZ[ip1][j][kp1],
+ d2FdYdZ[i][jp1][kp1], d2FdYdZ[ip1][jp1][kp1],
+
+ d3FdXdYdZ[i][j][k], d3FdXdYdZ[ip1][j][k],
+ d3FdXdYdZ[i][jp1][k], d3FdXdYdZ[ip1][jp1][k],
+ d3FdXdYdZ[i][j][kp1], d3FdXdYdZ[ip1][j][kp1],
+ d3FdXdYdZ[i][jp1][kp1], d3FdXdYdZ[ip1][jp1][kp1],
+ };
+
+ splines[i][j][k] = new TricubicSplineFunction(computeSplineCoefficients(beta));
+ }
+ }
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @throws OutOfRangeException if any of the variables is outside its interpolation range.
+ */
+ public double value(double x, double y, double z)
+ throws OutOfRangeException {
+ final int i = searchIndex(x, xval);
+ if (i == -1) {
+ throw new OutOfRangeException(x, xval[0], xval[xval.length - 1]);
+ }
+ final int j = searchIndex(y, yval);
+ if (j == -1) {
+ throw new OutOfRangeException(y, yval[0], yval[yval.length - 1]);
+ }
+ final int k = searchIndex(z, zval);
+ if (k == -1) {
+ throw new OutOfRangeException(z, zval[0], zval[zval.length - 1]);
+ }
+
+ final double xN = (x - xval[i]) / (xval[i + 1] - xval[i]);
+ final double yN = (y - yval[j]) / (yval[j + 1] - yval[j]);
+ final double zN = (z - zval[k]) / (zval[k + 1] - zval[k]);
+
+ return splines[i][j][k].value(xN, yN, zN);
+ }
+
+ /**
+ * @param c Coordinate.
+ * @param val Coordinate samples.
+ * @return the index in {@code val} corresponding to the interval containing {@code c}, or {@code -1}
+ * if {@code c} is out of the range defined by the end values of {@code val}.
+ */
+ private int searchIndex(double c, double[] val) {
+ if (c < val[0]) {
+ return -1;
+ }
+
+ final int max = val.length;
+ for (int i = 1; i < max; i++) {
+ if (c <= val[i]) {
+ return i - 1;
+ }
+ }
+
+ return -1;
+ }
+
+ /**
+ * Compute the spline coefficients from the list of function values and
+ * function partial derivatives values at the four corners of a grid
+ * element. They must be specified in the following order:
+ * <ul>
+ * <li>f(0,0,0)</li>
+ * <li>f(1,0,0)</li>
+ * <li>f(0,1,0)</li>
+ * <li>f(1,1,0)</li>
+ * <li>f(0,0,1)</li>
+ * <li>f(1,0,1)</li>
+ * <li>f(0,1,1)</li>
+ * <li>f(1,1,1)</li>
+ *
+ * <li>f<sub>x</sub>(0,0,0)</li>
+ * <li>... <em>(same order as above)</em></li>
+ * <li>f<sub>x</sub>(1,1,1)</li>
+ *
+ * <li>f<sub>y</sub>(0,0,0)</li>
+ * <li>... <em>(same order as above)</em></li>
+ * <li>f<sub>y</sub>(1,1,1)</li>
+ *
+ * <li>f<sub>z</sub>(0,0,0)</li>
+ * <li>... <em>(same order as above)</em></li>
+ * <li>f<sub>z</sub>(1,1,1)</li>
+ *
+ * <li>f<sub>xy</sub>(0,0,0)</li>
+ * <li>... <em>(same order as above)</em></li>
+ * <li>f<sub>xy</sub>(1,1,1)</li>
+ *
+ * <li>f<sub>xz</sub>(0,0,0)</li>
+ * <li>... <em>(same order as above)</em></li>
+ * <li>f<sub>xz</sub>(1,1,1)</li>
+ *
+ * <li>f<sub>yz</sub>(0,0,0)</li>
+ * <li>... <em>(same order as above)</em></li>
+ * <li>f<sub>yz</sub>(1,1,1)</li>
+ *
+ * <li>f<sub>xyz</sub>(0,0,0)</li>
+ * <li>... <em>(same order as above)</em></li>
+ * <li>f<sub>xyz</sub>(1,1,1)</li>
+ * </ul>
+ * where the subscripts indicate the partial derivative with respect to
+ * the corresponding variable(s).
+ *
+ * @param beta List of function values and function partial derivatives values.
+ * @return the spline coefficients.
+ */
+ private double[] computeSplineCoefficients(double[] beta) {
+ final int sz = 64;
+ final double[] a = new double[sz];
+
+ for (int i = 0; i < sz; i++) {
+ double result = 0;
+ final double[] row = AINV[i];
+ for (int j = 0; j < sz; j++) {
+ result += row[j] * beta[j];
+ }
+ a[i] = result;
+ }
+
+ return a;
+ }
+}
+
+/**
+ * 3D-spline function.
+ *
+ */
+class TricubicSplineFunction
+ implements TrivariateFunction {
+ /** Number of points. */
+ private static final short N = 4;
+ /** Coefficients */
+ private final double[][][] a = new double[N][N][N];
+
+ /**
+ * @param aV List of spline coefficients.
+ */
+ TricubicSplineFunction(double[] aV) {
+ for (int i = 0; i < N; i++) {
+ for (int j = 0; j < N; j++) {
+ for (int k = 0; k < N; k++) {
+ a[i][j][k] = aV[i + N * (j + N * k)];
+ }
+ }
+ }
+ }
+
+ /**
+ * @param x x-coordinate of the interpolation point.
+ * @param y y-coordinate of the interpolation point.
+ * @param z z-coordinate of the interpolation point.
+ * @return the interpolated value.
+ * @throws OutOfRangeException if {@code x}, {@code y} or
+ * {@code z} are not in the interval {@code [0, 1]}.
+ */
+ public double value(double x, double y, double z)
+ throws OutOfRangeException {
+ if (x < 0 || x > 1) {
+ throw new OutOfRangeException(x, 0, 1);
+ }
+ if (y < 0 || y > 1) {
+ throw new OutOfRangeException(y, 0, 1);
+ }
+ if (z < 0 || z > 1) {
+ throw new OutOfRangeException(z, 0, 1);
+ }
+
+ final double x2 = x * x;
+ final double x3 = x2 * x;
+ final double[] pX = { 1, x, x2, x3 };
+
+ final double y2 = y * y;
+ final double y3 = y2 * y;
+ final double[] pY = { 1, y, y2, y3 };
+
+ final double z2 = z * z;
+ final double z3 = z2 * z;
+ final double[] pZ = { 1, z, z2, z3 };
+
+ double result = 0;
+ for (int i = 0; i < N; i++) {
+ for (int j = 0; j < N; j++) {
+ for (int k = 0; k < N; k++) {
+ result += a[i][j][k] * pX[i] * pY[j] * pZ[k];
+ }
+ }
+ }
+
+ return result;
+ }
+}
diff --git a/src/main/java/org/apache/commons/math3/analysis/interpolation/TricubicSplineInterpolator.java b/src/main/java/org/apache/commons/math3/analysis/interpolation/TricubicSplineInterpolator.java
new file mode 100644
index 0000000..7f43e6f
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/analysis/interpolation/TricubicSplineInterpolator.java
@@ -0,0 +1,201 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math3.analysis.interpolation;
+
+import org.apache.commons.math3.exception.DimensionMismatchException;
+import org.apache.commons.math3.exception.NoDataException;
+import org.apache.commons.math3.exception.NonMonotonicSequenceException;
+import org.apache.commons.math3.exception.NumberIsTooSmallException;
+import org.apache.commons.math3.util.MathArrays;
+
+/**
+ * Generates a tricubic interpolating function.
+ *
+ * @since 2.2
+ * @deprecated To be removed in 4.0 (see MATH-1166).
+ */
+@Deprecated
+public class TricubicSplineInterpolator
+ implements TrivariateGridInterpolator {
+ /**
+ * {@inheritDoc}
+ */
+ public TricubicSplineInterpolatingFunction interpolate(final double[] xval,
+ final double[] yval,
+ final double[] zval,
+ final double[][][] fval)
+ throws NoDataException, NumberIsTooSmallException,
+ DimensionMismatchException, NonMonotonicSequenceException {
+ if (xval.length == 0 || yval.length == 0 || zval.length == 0 || fval.length == 0) {
+ throw new NoDataException();
+ }
+ if (xval.length != fval.length) {
+ throw new DimensionMismatchException(xval.length, fval.length);
+ }
+
+ MathArrays.checkOrder(xval);
+ MathArrays.checkOrder(yval);
+ MathArrays.checkOrder(zval);
+
+ final int xLen = xval.length;
+ final int yLen = yval.length;
+ final int zLen = zval.length;
+
+ // Samples, re-ordered as (z, x, y) and (y, z, x) tuplets
+ // fvalXY[k][i][j] = f(xval[i], yval[j], zval[k])
+ // fvalZX[j][k][i] = f(xval[i], yval[j], zval[k])
+ final double[][][] fvalXY = new double[zLen][xLen][yLen];
+ final double[][][] fvalZX = new double[yLen][zLen][xLen];
+ for (int i = 0; i < xLen; i++) {
+ if (fval[i].length != yLen) {
+ throw new DimensionMismatchException(fval[i].length, yLen);
+ }
+
+ for (int j = 0; j < yLen; j++) {
+ if (fval[i][j].length != zLen) {
+ throw new DimensionMismatchException(fval[i][j].length, zLen);
+ }
+
+ for (int k = 0; k < zLen; k++) {
+ final double v = fval[i][j][k];
+ fvalXY[k][i][j] = v;
+ fvalZX[j][k][i] = v;
+ }
+ }
+ }
+
+ final BicubicSplineInterpolator bsi = new BicubicSplineInterpolator(true);
+
+ // For each line x[i] (0 <= i < xLen), construct a 2D spline in y and z
+ final BicubicSplineInterpolatingFunction[] xSplineYZ
+ = new BicubicSplineInterpolatingFunction[xLen];
+ for (int i = 0; i < xLen; i++) {
+ xSplineYZ[i] = bsi.interpolate(yval, zval, fval[i]);
+ }
+
+ // For each line y[j] (0 <= j < yLen), construct a 2D spline in z and x
+ final BicubicSplineInterpolatingFunction[] ySplineZX
+ = new BicubicSplineInterpolatingFunction[yLen];
+ for (int j = 0; j < yLen; j++) {
+ ySplineZX[j] = bsi.interpolate(zval, xval, fvalZX[j]);
+ }
+
+ // For each line z[k] (0 <= k < zLen), construct a 2D spline in x and y
+ final BicubicSplineInterpolatingFunction[] zSplineXY
+ = new BicubicSplineInterpolatingFunction[zLen];
+ for (int k = 0; k < zLen; k++) {
+ zSplineXY[k] = bsi.interpolate(xval, yval, fvalXY[k]);
+ }
+
+ // Partial derivatives wrt x and wrt y
+ final double[][][] dFdX = new double[xLen][yLen][zLen];
+ final double[][][] dFdY = new double[xLen][yLen][zLen];
+ final double[][][] d2FdXdY = new double[xLen][yLen][zLen];
+ for (int k = 0; k < zLen; k++) {
+ final BicubicSplineInterpolatingFunction f = zSplineXY[k];
+ for (int i = 0; i < xLen; i++) {
+ final double x = xval[i];
+ for (int j = 0; j < yLen; j++) {
+ final double y = yval[j];
+ dFdX[i][j][k] = f.partialDerivativeX(x, y);
+ dFdY[i][j][k] = f.partialDerivativeY(x, y);
+ d2FdXdY[i][j][k] = f.partialDerivativeXY(x, y);
+ }
+ }
+ }
+
+ // Partial derivatives wrt y and wrt z
+ final double[][][] dFdZ = new double[xLen][yLen][zLen];
+ final double[][][] d2FdYdZ = new double[xLen][yLen][zLen];
+ for (int i = 0; i < xLen; i++) {
+ final BicubicSplineInterpolatingFunction f = xSplineYZ[i];
+ for (int j = 0; j < yLen; j++) {
+ final double y = yval[j];
+ for (int k = 0; k < zLen; k++) {
+ final double z = zval[k];
+ dFdZ[i][j][k] = f.partialDerivativeY(y, z);
+ d2FdYdZ[i][j][k] = f.partialDerivativeXY(y, z);
+ }
+ }
+ }
+
+ // Partial derivatives wrt x and wrt z
+ final double[][][] d2FdZdX = new double[xLen][yLen][zLen];
+ for (int j = 0; j < yLen; j++) {
+ final BicubicSplineInterpolatingFunction f = ySplineZX[j];
+ for (int k = 0; k < zLen; k++) {
+ final double z = zval[k];
+ for (int i = 0; i < xLen; i++) {
+ final double x = xval[i];
+ d2FdZdX[i][j][k] = f.partialDerivativeXY(z, x);
+ }
+ }
+ }
+
+ // Third partial cross-derivatives
+ final double[][][] d3FdXdYdZ = new double[xLen][yLen][zLen];
+ for (int i = 0; i < xLen ; i++) {
+ final int nI = nextIndex(i, xLen);
+ final int pI = previousIndex(i);
+ for (int j = 0; j < yLen; j++) {
+ final int nJ = nextIndex(j, yLen);
+ final int pJ = previousIndex(j);
+ for (int k = 0; k < zLen; k++) {
+ final int nK = nextIndex(k, zLen);
+ final int pK = previousIndex(k);
+
+ // XXX Not sure about this formula
+ d3FdXdYdZ[i][j][k] = (fval[nI][nJ][nK] - fval[nI][pJ][nK] -
+ fval[pI][nJ][nK] + fval[pI][pJ][nK] -
+ fval[nI][nJ][pK] + fval[nI][pJ][pK] +
+ fval[pI][nJ][pK] - fval[pI][pJ][pK]) /
+ ((xval[nI] - xval[pI]) * (yval[nJ] - yval[pJ]) * (zval[nK] - zval[pK])) ;
+ }
+ }
+ }
+
+ // Create the interpolating splines
+ return new TricubicSplineInterpolatingFunction(xval, yval, zval, fval,
+ dFdX, dFdY, dFdZ,
+ d2FdXdY, d2FdZdX, d2FdYdZ,
+ d3FdXdYdZ);
+ }
+
+ /**
+ * Compute the next index of an array, clipping if necessary.
+ * It is assumed (but not checked) that {@code i} is larger than or equal to 0.
+ *
+ * @param i Index
+ * @param max Upper limit of the array
+ * @return the next index
+ */
+ private int nextIndex(int i, int max) {
+ final int index = i + 1;
+ return index < max ? index : index - 1;
+ }
+ /**
+ * Compute the previous index of an array, clipping if necessary.
+ * It is assumed (but not checked) that {@code i} is smaller than the size of the array.
+ *
+ * @param i Index
+ * @return the previous index
+ */
+ private int previousIndex(int i) {
+ final int index = i - 1;
+ return index >= 0 ? index : 0;
+ }
+}
diff --git a/src/main/java/org/apache/commons/math3/analysis/interpolation/TrivariateGridInterpolator.java b/src/main/java/org/apache/commons/math3/analysis/interpolation/TrivariateGridInterpolator.java
new file mode 100644
index 0000000..ec69715
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/analysis/interpolation/TrivariateGridInterpolator.java
@@ -0,0 +1,54 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math3.analysis.interpolation;
+
+import org.apache.commons.math3.analysis.TrivariateFunction;
+import org.apache.commons.math3.exception.DimensionMismatchException;
+import org.apache.commons.math3.exception.NoDataException;
+import org.apache.commons.math3.exception.NonMonotonicSequenceException;
+import org.apache.commons.math3.exception.NumberIsTooSmallException;
+
+/**
+ * Interface representing a trivariate real interpolating function where the
+ * sample points must be specified on a regular grid.
+ *
+ * @since 2.2
+ */
+public interface TrivariateGridInterpolator {
+ /**
+ * Compute an interpolating function for the dataset.
+ *
+ * @param xval All the x-coordinates of the interpolation points, sorted
+ * in increasing order.
+ * @param yval All the y-coordinates of the interpolation points, sorted
+ * in increasing order.
+ * @param zval All the z-coordinates of the interpolation points, sorted
+ * in increasing order.
+ * @param fval the values of the interpolation points on all the grid knots:
+ * {@code fval[i][j][k] = f(xval[i], yval[j], zval[k])}.
+ * @return a function that interpolates the data set.
+ * @throws NoDataException if any of the arrays has zero length.
+ * @throws DimensionMismatchException if the array lengths are inconsistent.
+ * @throws NonMonotonicSequenceException if arrays are not sorted
+ * @throws NumberIsTooSmallException if the number of points is too small for
+ * the order of the interpolation
+ */
+ TrivariateFunction interpolate(double[] xval, double[] yval, double[] zval,
+ double[][][] fval)
+ throws NoDataException, NumberIsTooSmallException,
+ DimensionMismatchException, NonMonotonicSequenceException;
+}
diff --git a/src/main/java/org/apache/commons/math3/analysis/interpolation/UnivariateInterpolator.java b/src/main/java/org/apache/commons/math3/analysis/interpolation/UnivariateInterpolator.java
new file mode 100644
index 0000000..f7a1bd1
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/analysis/interpolation/UnivariateInterpolator.java
@@ -0,0 +1,41 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math3.analysis.interpolation;
+
+import org.apache.commons.math3.analysis.UnivariateFunction;
+import org.apache.commons.math3.exception.DimensionMismatchException;
+import org.apache.commons.math3.exception.MathIllegalArgumentException;
+
+/**
+ * Interface representing a univariate real interpolating function.
+ *
+ */
+public interface UnivariateInterpolator {
+ /**
+ * Compute an interpolating function for the dataset.
+ *
+ * @param xval Arguments for the interpolation points.
+ * @param yval Values for the interpolation points.
+ * @return a function which interpolates the dataset.
+ * @throws MathIllegalArgumentException
+ * if the arguments violate assumptions made by the interpolation
+ * algorithm.
+ * @throws DimensionMismatchException if arrays lengthes do not match
+ */
+ UnivariateFunction interpolate(double xval[], double yval[])
+ throws MathIllegalArgumentException, DimensionMismatchException;
+}
diff --git a/src/main/java/org/apache/commons/math3/analysis/interpolation/UnivariatePeriodicInterpolator.java b/src/main/java/org/apache/commons/math3/analysis/interpolation/UnivariatePeriodicInterpolator.java
new file mode 100644
index 0000000..88760f5
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/analysis/interpolation/UnivariatePeriodicInterpolator.java
@@ -0,0 +1,124 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math3.analysis.interpolation;
+
+import org.apache.commons.math3.analysis.UnivariateFunction;
+import org.apache.commons.math3.util.MathUtils;
+import org.apache.commons.math3.util.MathArrays;
+import org.apache.commons.math3.exception.MathIllegalArgumentException;
+import org.apache.commons.math3.exception.NonMonotonicSequenceException;
+import org.apache.commons.math3.exception.NumberIsTooSmallException;
+
+/**
+ * Adapter for classes implementing the {@link UnivariateInterpolator}
+ * interface.
+ * The data to be interpolated is assumed to be periodic. Thus values that are
+ * outside of the range can be passed to the interpolation function: They will
+ * be wrapped into the initial range before being passed to the class that
+ * actually computes the interpolation.
+ *
+ */
+public class UnivariatePeriodicInterpolator
+ implements UnivariateInterpolator {
+ /** Default number of extension points of the samples array. */
+ public static final int DEFAULT_EXTEND = 5;
+ /** Interpolator. */
+ private final UnivariateInterpolator interpolator;
+ /** Period. */
+ private final double period;
+ /** Number of extension points. */
+ private final int extend;
+
+ /**
+ * Builds an interpolator.
+ *
+ * @param interpolator Interpolator.
+ * @param period Period.
+ * @param extend Number of points to be appended at the beginning and
+ * end of the sample arrays in order to avoid interpolation failure at
+ * the (periodic) boundaries of the orginal interval. The value is the
+ * number of sample points which the original {@code interpolator} needs
+ * on each side of the interpolated point.
+ */
+ public UnivariatePeriodicInterpolator(UnivariateInterpolator interpolator,
+ double period,
+ int extend) {
+ this.interpolator = interpolator;
+ this.period = period;
+ this.extend = extend;
+ }
+
+ /**
+ * Builds an interpolator.
+ * Uses {@link #DEFAULT_EXTEND} as the number of extension points on each side
+ * of the original abscissae range.
+ *
+ * @param interpolator Interpolator.
+ * @param period Period.
+ */
+ public UnivariatePeriodicInterpolator(UnivariateInterpolator interpolator,
+ double period) {
+ this(interpolator, period, DEFAULT_EXTEND);
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @throws NumberIsTooSmallException if the number of extension points
+ * is larger than the size of {@code xval}.
+ */
+ public UnivariateFunction interpolate(double[] xval,
+ double[] yval)
+ throws NumberIsTooSmallException, NonMonotonicSequenceException {
+ if (xval.length < extend) {
+ throw new NumberIsTooSmallException(xval.length, extend, true);
+ }
+
+ MathArrays.checkOrder(xval);
+ final double offset = xval[0];
+
+ final int len = xval.length + extend * 2;
+ final double[] x = new double[len];
+ final double[] y = new double[len];
+ for (int i = 0; i < xval.length; i++) {
+ final int index = i + extend;
+ x[index] = MathUtils.reduce(xval[i], period, offset);
+ y[index] = yval[i];
+ }
+
+ // Wrap to enable interpolation at the boundaries.
+ for (int i = 0; i < extend; i++) {
+ int index = xval.length - extend + i;
+ x[i] = MathUtils.reduce(xval[index], period, offset) - period;
+ y[i] = yval[index];
+
+ index = len - extend + i;
+ x[index] = MathUtils.reduce(xval[i], period, offset) + period;
+ y[index] = yval[i];
+ }
+
+ MathArrays.sortInPlace(x, y);
+
+ final UnivariateFunction f = interpolator.interpolate(x, y);
+ return new UnivariateFunction() {
+ /** {@inheritDoc} */
+ public double value(final double x) throws MathIllegalArgumentException {
+ return f.value(MathUtils.reduce(x, period, offset));
+ }
+ };
+ }
+}
diff --git a/src/main/java/org/apache/commons/math3/analysis/interpolation/package-info.java b/src/main/java/org/apache/commons/math3/analysis/interpolation/package-info.java
new file mode 100644
index 0000000..b4b25dd
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/analysis/interpolation/package-info.java
@@ -0,0 +1,22 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/**
+ *
+ * Univariate real functions interpolation algorithms.
+ *
+ */
+package org.apache.commons.math3.analysis.interpolation;
diff --git a/src/main/java/org/apache/commons/math3/analysis/package-info.java b/src/main/java/org/apache/commons/math3/analysis/package-info.java
new file mode 100644
index 0000000..e122bbb
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/analysis/package-info.java
@@ -0,0 +1,27 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/**
+ * Parent package for common numerical analysis procedures, including root finding, function
+ * interpolation and integration. Note that optimization (i.e. minimization and maximization) is a
+ * separate top-level package.
+ *
+ * <p>Function interfaces are intended to be implemented by user code to represent domain problems.
+ * The algorithms provided by the library operate on these functions to find their roots, or
+ * integrate them, or ... Functions can be multivariate or univariate, real vectorial or
+ * matrix-valued, and they can be differentiable or not.
+ */
+package org.apache.commons.math3.analysis;
diff --git a/src/main/java/org/apache/commons/math3/analysis/polynomials/PolynomialFunction.java b/src/main/java/org/apache/commons/math3/analysis/polynomials/PolynomialFunction.java
new file mode 100644
index 0000000..69be04a
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/analysis/polynomials/PolynomialFunction.java
@@ -0,0 +1,412 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math3.analysis.polynomials;
+
+import java.io.Serializable;
+import java.util.Arrays;
+
+import org.apache.commons.math3.analysis.DifferentiableUnivariateFunction;
+import org.apache.commons.math3.analysis.ParametricUnivariateFunction;
+import org.apache.commons.math3.analysis.UnivariateFunction;
+import org.apache.commons.math3.analysis.differentiation.DerivativeStructure;
+import org.apache.commons.math3.analysis.differentiation.UnivariateDifferentiableFunction;
+import org.apache.commons.math3.exception.NoDataException;
+import org.apache.commons.math3.exception.NullArgumentException;
+import org.apache.commons.math3.exception.util.LocalizedFormats;
+import org.apache.commons.math3.util.FastMath;
+import org.apache.commons.math3.util.MathUtils;
+
+/**
+ * Immutable representation of a real polynomial function with real coefficients.
+ * <p>
+ * <a href="http://mathworld.wolfram.com/HornersMethod.html">Horner's Method</a>
+ * is used to evaluate the function.</p>
+ *
+ */
+public class PolynomialFunction implements UnivariateDifferentiableFunction, DifferentiableUnivariateFunction, Serializable {
+ /**
+ * Serialization identifier
+ */
+ private static final long serialVersionUID = -7726511984200295583L;
+ /**
+ * The coefficients of the polynomial, ordered by degree -- i.e.,
+ * coefficients[0] is the constant term and coefficients[n] is the
+ * coefficient of x^n where n is the degree of the polynomial.
+ */
+ private final double coefficients[];
+
+ /**
+ * Construct a polynomial with the given coefficients. The first element
+ * of the coefficients array is the constant term. Higher degree
+ * coefficients follow in sequence. The degree of the resulting polynomial
+ * is the index of the last non-null element of the array, or 0 if all elements
+ * are null.
+ * <p>
+ * The constructor makes a copy of the input array and assigns the copy to
+ * the coefficients property.</p>
+ *
+ * @param c Polynomial coefficients.
+ * @throws NullArgumentException if {@code c} is {@code null}.
+ * @throws NoDataException if {@code c} is empty.
+ */
+ public PolynomialFunction(double c[])
+ throws NullArgumentException, NoDataException {
+ super();
+ MathUtils.checkNotNull(c);
+ int n = c.length;
+ if (n == 0) {
+ throw new NoDataException(LocalizedFormats.EMPTY_POLYNOMIALS_COEFFICIENTS_ARRAY);
+ }
+ while ((n > 1) && (c[n - 1] == 0)) {
+ --n;
+ }
+ this.coefficients = new double[n];
+ System.arraycopy(c, 0, this.coefficients, 0, n);
+ }
+
+ /**
+ * Compute the value of the function for the given argument.
+ * <p>
+ * The value returned is </p><p>
+ * {@code coefficients[n] * x^n + ... + coefficients[1] * x + coefficients[0]}
+ * </p>
+ *
+ * @param x Argument for which the function value should be computed.
+ * @return the value of the polynomial at the given point.
+ * @see UnivariateFunction#value(double)
+ */
+ public double value(double x) {
+ return evaluate(coefficients, x);
+ }
+
+ /**
+ * Returns the degree of the polynomial.
+ *
+ * @return the degree of the polynomial.
+ */
+ public int degree() {
+ return coefficients.length - 1;
+ }
+
+ /**
+ * Returns a copy of the coefficients array.
+ * <p>
+ * Changes made to the returned copy will not affect the coefficients of
+ * the polynomial.</p>
+ *
+ * @return a fresh copy of the coefficients array.
+ */
+ public double[] getCoefficients() {
+ return coefficients.clone();
+ }
+
+ /**
+ * Uses Horner's Method to evaluate the polynomial with the given coefficients at
+ * the argument.
+ *
+ * @param coefficients Coefficients of the polynomial to evaluate.
+ * @param argument Input value.
+ * @return the value of the polynomial.
+ * @throws NoDataException if {@code coefficients} is empty.
+ * @throws NullArgumentException if {@code coefficients} is {@code null}.
+ */
+ protected static double evaluate(double[] coefficients, double argument)
+ throws NullArgumentException, NoDataException {
+ MathUtils.checkNotNull(coefficients);
+ int n = coefficients.length;
+ if (n == 0) {
+ throw new NoDataException(LocalizedFormats.EMPTY_POLYNOMIALS_COEFFICIENTS_ARRAY);
+ }
+ double result = coefficients[n - 1];
+ for (int j = n - 2; j >= 0; j--) {
+ result = argument * result + coefficients[j];
+ }
+ return result;
+ }
+
+
+ /** {@inheritDoc}
+ * @since 3.1
+ * @throws NoDataException if {@code coefficients} is empty.
+ * @throws NullArgumentException if {@code coefficients} is {@code null}.
+ */
+ public DerivativeStructure value(final DerivativeStructure t)
+ throws NullArgumentException, NoDataException {
+ MathUtils.checkNotNull(coefficients);
+ int n = coefficients.length;
+ if (n == 0) {
+ throw new NoDataException(LocalizedFormats.EMPTY_POLYNOMIALS_COEFFICIENTS_ARRAY);
+ }
+ DerivativeStructure result =
+ new DerivativeStructure(t.getFreeParameters(), t.getOrder(), coefficients[n - 1]);
+ for (int j = n - 2; j >= 0; j--) {
+ result = result.multiply(t).add(coefficients[j]);
+ }
+ return result;
+ }
+
+ /**
+ * Add a polynomial to the instance.
+ *
+ * @param p Polynomial to add.
+ * @return a new polynomial which is the sum of the instance and {@code p}.
+ */
+ public PolynomialFunction add(final PolynomialFunction p) {
+ // identify the lowest degree polynomial
+ final int lowLength = FastMath.min(coefficients.length, p.coefficients.length);
+ final int highLength = FastMath.max(coefficients.length, p.coefficients.length);
+
+ // build the coefficients array
+ double[] newCoefficients = new double[highLength];
+ for (int i = 0; i < lowLength; ++i) {
+ newCoefficients[i] = coefficients[i] + p.coefficients[i];
+ }
+ System.arraycopy((coefficients.length < p.coefficients.length) ?
+ p.coefficients : coefficients,
+ lowLength,
+ newCoefficients, lowLength,
+ highLength - lowLength);
+
+ return new PolynomialFunction(newCoefficients);
+ }
+
+ /**
+ * Subtract a polynomial from the instance.
+ *
+ * @param p Polynomial to subtract.
+ * @return a new polynomial which is the instance minus {@code p}.
+ */
+ public PolynomialFunction subtract(final PolynomialFunction p) {
+ // identify the lowest degree polynomial
+ int lowLength = FastMath.min(coefficients.length, p.coefficients.length);
+ int highLength = FastMath.max(coefficients.length, p.coefficients.length);
+
+ // build the coefficients array
+ double[] newCoefficients = new double[highLength];
+ for (int i = 0; i < lowLength; ++i) {
+ newCoefficients[i] = coefficients[i] - p.coefficients[i];
+ }
+ if (coefficients.length < p.coefficients.length) {
+ for (int i = lowLength; i < highLength; ++i) {
+ newCoefficients[i] = -p.coefficients[i];
+ }
+ } else {
+ System.arraycopy(coefficients, lowLength, newCoefficients, lowLength,
+ highLength - lowLength);
+ }
+
+ return new PolynomialFunction(newCoefficients);
+ }
+
+ /**
+ * Negate the instance.
+ *
+ * @return a new polynomial with all coefficients negated
+ */
+ public PolynomialFunction negate() {
+ double[] newCoefficients = new double[coefficients.length];
+ for (int i = 0; i < coefficients.length; ++i) {
+ newCoefficients[i] = -coefficients[i];
+ }
+ return new PolynomialFunction(newCoefficients);
+ }
+
+ /**
+ * Multiply the instance by a polynomial.
+ *
+ * @param p Polynomial to multiply by.
+ * @return a new polynomial equal to this times {@code p}
+ */
+ public PolynomialFunction multiply(final PolynomialFunction p) {
+ double[] newCoefficients = new double[coefficients.length + p.coefficients.length - 1];
+
+ for (int i = 0; i < newCoefficients.length; ++i) {
+ newCoefficients[i] = 0.0;
+ for (int j = FastMath.max(0, i + 1 - p.coefficients.length);
+ j < FastMath.min(coefficients.length, i + 1);
+ ++j) {
+ newCoefficients[i] += coefficients[j] * p.coefficients[i-j];
+ }
+ }
+
+ return new PolynomialFunction(newCoefficients);
+ }
+
+ /**
+ * Returns the coefficients of the derivative of the polynomial with the given coefficients.
+ *
+ * @param coefficients Coefficients of the polynomial to differentiate.
+ * @return the coefficients of the derivative or {@code null} if coefficients has length 1.
+ * @throws NoDataException if {@code coefficients} is empty.
+ * @throws NullArgumentException if {@code coefficients} is {@code null}.
+ */
+ protected static double[] differentiate(double[] coefficients)
+ throws NullArgumentException, NoDataException {
+ MathUtils.checkNotNull(coefficients);
+ int n = coefficients.length;
+ if (n == 0) {
+ throw new NoDataException(LocalizedFormats.EMPTY_POLYNOMIALS_COEFFICIENTS_ARRAY);
+ }
+ if (n == 1) {
+ return new double[]{0};
+ }
+ double[] result = new double[n - 1];
+ for (int i = n - 1; i > 0; i--) {
+ result[i - 1] = i * coefficients[i];
+ }
+ return result;
+ }
+
+ /**
+ * Returns the derivative as a {@link PolynomialFunction}.
+ *
+ * @return the derivative polynomial.
+ */
+ public PolynomialFunction polynomialDerivative() {
+ return new PolynomialFunction(differentiate(coefficients));
+ }
+
+ /**
+ * Returns the derivative as a {@link UnivariateFunction}.
+ *
+ * @return the derivative function.
+ */
+ public UnivariateFunction derivative() {
+ return polynomialDerivative();
+ }
+
+ /**
+ * Returns a string representation of the polynomial.
+ *
+ * <p>The representation is user oriented. Terms are displayed lowest
+ * degrees first. The multiplications signs, coefficients equals to
+ * one and null terms are not displayed (except if the polynomial is 0,
+ * in which case the 0 constant term is displayed). Addition of terms
+ * with negative coefficients are replaced by subtraction of terms
+ * with positive coefficients except for the first displayed term
+ * (i.e. we display <code>-3</code> for a constant negative polynomial,
+ * but <code>1 - 3 x + x^2</code> if the negative coefficient is not
+ * the first one displayed).</p>
+ *
+ * @return a string representation of the polynomial.
+ */
+ @Override
+ public String toString() {
+ StringBuilder s = new StringBuilder();
+ if (coefficients[0] == 0.0) {
+ if (coefficients.length == 1) {
+ return "0";
+ }
+ } else {
+ s.append(toString(coefficients[0]));
+ }
+
+ for (int i = 1; i < coefficients.length; ++i) {
+ if (coefficients[i] != 0) {
+ if (s.length() > 0) {
+ if (coefficients[i] < 0) {
+ s.append(" - ");
+ } else {
+ s.append(" + ");
+ }
+ } else {
+ if (coefficients[i] < 0) {
+ s.append("-");
+ }
+ }
+
+ double absAi = FastMath.abs(coefficients[i]);
+ if ((absAi - 1) != 0) {
+ s.append(toString(absAi));
+ s.append(' ');
+ }
+
+ s.append("x");
+ if (i > 1) {
+ s.append('^');
+ s.append(Integer.toString(i));
+ }
+ }
+ }
+
+ return s.toString();
+ }
+
+ /**
+ * Creates a string representing a coefficient, removing ".0" endings.
+ *
+ * @param coeff Coefficient.
+ * @return a string representation of {@code coeff}.
+ */
+ private static String toString(double coeff) {
+ final String c = Double.toString(coeff);
+ if (c.endsWith(".0")) {
+ return c.substring(0, c.length() - 2);
+ } else {
+ return c;
+ }
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public int hashCode() {
+ final int prime = 31;
+ int result = 1;
+ result = prime * result + Arrays.hashCode(coefficients);
+ return result;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj) {
+ return true;
+ }
+ if (!(obj instanceof PolynomialFunction)) {
+ return false;
+ }
+ PolynomialFunction other = (PolynomialFunction) obj;
+ if (!Arrays.equals(coefficients, other.coefficients)) {
+ return false;
+ }
+ return true;
+ }
+
+ /**
+ * Dedicated parametric polynomial class.
+ *
+ * @since 3.0
+ */
+ public static class Parametric implements ParametricUnivariateFunction {
+ /** {@inheritDoc} */
+ public double[] gradient(double x, double ... parameters) {
+ final double[] gradient = new double[parameters.length];
+ double xn = 1.0;
+ for (int i = 0; i < parameters.length; ++i) {
+ gradient[i] = xn;
+ xn *= x;
+ }
+ return gradient;
+ }
+
+ /** {@inheritDoc} */
+ public double value(final double x, final double ... parameters)
+ throws NoDataException {
+ return PolynomialFunction.evaluate(parameters, x);
+ }
+ }
+}
diff --git a/src/main/java/org/apache/commons/math3/analysis/polynomials/PolynomialFunctionLagrangeForm.java b/src/main/java/org/apache/commons/math3/analysis/polynomials/PolynomialFunctionLagrangeForm.java
new file mode 100644
index 0000000..9d812df
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/analysis/polynomials/PolynomialFunctionLagrangeForm.java
@@ -0,0 +1,326 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math3.analysis.polynomials;
+
+import org.apache.commons.math3.analysis.UnivariateFunction;
+import org.apache.commons.math3.util.FastMath;
+import org.apache.commons.math3.util.MathArrays;
+import org.apache.commons.math3.exception.DimensionMismatchException;
+import org.apache.commons.math3.exception.NonMonotonicSequenceException;
+import org.apache.commons.math3.exception.NumberIsTooSmallException;
+import org.apache.commons.math3.exception.util.LocalizedFormats;
+
+/**
+ * Implements the representation of a real polynomial function in
+ * <a href="http://mathworld.wolfram.com/LagrangeInterpolatingPolynomial.html">
+ * Lagrange Form</a>. For reference, see <b>Introduction to Numerical
+ * Analysis</b>, ISBN 038795452X, chapter 2.
+ * <p>
+ * The approximated function should be smooth enough for Lagrange polynomial
+ * to work well. Otherwise, consider using splines instead.</p>
+ *
+ * @since 1.2
+ */
+public class PolynomialFunctionLagrangeForm implements UnivariateFunction {
+ /**
+ * The coefficients of the polynomial, ordered by degree -- i.e.
+ * coefficients[0] is the constant term and coefficients[n] is the
+ * coefficient of x^n where n is the degree of the polynomial.
+ */
+ private double coefficients[];
+ /**
+ * Interpolating points (abscissas).
+ */
+ private final double x[];
+ /**
+ * Function values at interpolating points.
+ */
+ private final double y[];
+ /**
+ * Whether the polynomial coefficients are available.
+ */
+ private boolean coefficientsComputed;
+
+ /**
+ * Construct a Lagrange polynomial with the given abscissas and function
+ * values. The order of interpolating points are not important.
+ * <p>
+ * The constructor makes copy of the input arrays and assigns them.</p>
+ *
+ * @param x interpolating points
+ * @param y function values at interpolating points
+ * @throws DimensionMismatchException if the array lengths are different.
+ * @throws NumberIsTooSmallException if the number of points is less than 2.
+ * @throws NonMonotonicSequenceException
+ * if two abscissae have the same value.
+ */
+ public PolynomialFunctionLagrangeForm(double x[], double y[])
+ throws DimensionMismatchException, NumberIsTooSmallException, NonMonotonicSequenceException {
+ this.x = new double[x.length];
+ this.y = new double[y.length];
+ System.arraycopy(x, 0, this.x, 0, x.length);
+ System.arraycopy(y, 0, this.y, 0, y.length);
+ coefficientsComputed = false;
+
+ if (!verifyInterpolationArray(x, y, false)) {
+ MathArrays.sortInPlace(this.x, this.y);
+ // Second check in case some abscissa is duplicated.
+ verifyInterpolationArray(this.x, this.y, true);
+ }
+ }
+
+ /**
+ * Calculate the function value at the given point.
+ *
+ * @param z Point at which the function value is to be computed.
+ * @return the function value.
+ * @throws DimensionMismatchException if {@code x} and {@code y} have
+ * different lengths.
+ * @throws org.apache.commons.math3.exception.NonMonotonicSequenceException
+ * if {@code x} is not sorted in strictly increasing order.
+ * @throws NumberIsTooSmallException if the size of {@code x} is less
+ * than 2.
+ */
+ public double value(double z) {
+ return evaluateInternal(x, y, z);
+ }
+
+ /**
+ * Returns the degree of the polynomial.
+ *
+ * @return the degree of the polynomial
+ */
+ public int degree() {
+ return x.length - 1;
+ }
+
+ /**
+ * Returns a copy of the interpolating points array.
+ * <p>
+ * Changes made to the returned copy will not affect the polynomial.</p>
+ *
+ * @return a fresh copy of the interpolating points array
+ */
+ public double[] getInterpolatingPoints() {
+ double[] out = new double[x.length];
+ System.arraycopy(x, 0, out, 0, x.length);
+ return out;
+ }
+
+ /**
+ * Returns a copy of the interpolating values array.
+ * <p>
+ * Changes made to the returned copy will not affect the polynomial.</p>
+ *
+ * @return a fresh copy of the interpolating values array
+ */
+ public double[] getInterpolatingValues() {
+ double[] out = new double[y.length];
+ System.arraycopy(y, 0, out, 0, y.length);
+ return out;
+ }
+
+ /**
+ * Returns a copy of the coefficients array.
+ * <p>
+ * Changes made to the returned copy will not affect the polynomial.</p>
+ * <p>
+ * Note that coefficients computation can be ill-conditioned. Use with caution
+ * and only when it is necessary.</p>
+ *
+ * @return a fresh copy of the coefficients array
+ */
+ public double[] getCoefficients() {
+ if (!coefficientsComputed) {
+ computeCoefficients();
+ }
+ double[] out = new double[coefficients.length];
+ System.arraycopy(coefficients, 0, out, 0, coefficients.length);
+ return out;
+ }
+
+ /**
+ * Evaluate the Lagrange polynomial using
+ * <a href="http://mathworld.wolfram.com/NevillesAlgorithm.html">
+ * Neville's Algorithm</a>. It takes O(n^2) time.
+ *
+ * @param x Interpolating points array.
+ * @param y Interpolating values array.
+ * @param z Point at which the function value is to be computed.
+ * @return the function value.
+ * @throws DimensionMismatchException if {@code x} and {@code y} have
+ * different lengths.
+ * @throws NonMonotonicSequenceException
+ * if {@code x} is not sorted in strictly increasing order.
+ * @throws NumberIsTooSmallException if the size of {@code x} is less
+ * than 2.
+ */
+ public static double evaluate(double x[], double y[], double z)
+ throws DimensionMismatchException, NumberIsTooSmallException, NonMonotonicSequenceException {
+ if (verifyInterpolationArray(x, y, false)) {
+ return evaluateInternal(x, y, z);
+ }
+
+ // Array is not sorted.
+ final double[] xNew = new double[x.length];
+ final double[] yNew = new double[y.length];
+ System.arraycopy(x, 0, xNew, 0, x.length);
+ System.arraycopy(y, 0, yNew, 0, y.length);
+
+ MathArrays.sortInPlace(xNew, yNew);
+ // Second check in case some abscissa is duplicated.
+ verifyInterpolationArray(xNew, yNew, true);
+ return evaluateInternal(xNew, yNew, z);
+ }
+
+ /**
+ * Evaluate the Lagrange polynomial using
+ * <a href="http://mathworld.wolfram.com/NevillesAlgorithm.html">
+ * Neville's Algorithm</a>. It takes O(n^2) time.
+ *
+ * @param x Interpolating points array.
+ * @param y Interpolating values array.
+ * @param z Point at which the function value is to be computed.
+ * @return the function value.
+ * @throws DimensionMismatchException if {@code x} and {@code y} have
+ * different lengths.
+ * @throws org.apache.commons.math3.exception.NonMonotonicSequenceException
+ * if {@code x} is not sorted in strictly increasing order.
+ * @throws NumberIsTooSmallException if the size of {@code x} is less
+ * than 2.
+ */
+ private static double evaluateInternal(double x[], double y[], double z) {
+ int nearest = 0;
+ final int n = x.length;
+ final double[] c = new double[n];
+ final double[] d = new double[n];
+ double min_dist = Double.POSITIVE_INFINITY;
+ for (int i = 0; i < n; i++) {
+ // initialize the difference arrays
+ c[i] = y[i];
+ d[i] = y[i];
+ // find out the abscissa closest to z
+ final double dist = FastMath.abs(z - x[i]);
+ if (dist < min_dist) {
+ nearest = i;
+ min_dist = dist;
+ }
+ }
+
+ // initial approximation to the function value at z
+ double value = y[nearest];
+
+ for (int i = 1; i < n; i++) {
+ for (int j = 0; j < n-i; j++) {
+ final double tc = x[j] - z;
+ final double td = x[i+j] - z;
+ final double divider = x[j] - x[i+j];
+ // update the difference arrays
+ final double w = (c[j+1] - d[j]) / divider;
+ c[j] = tc * w;
+ d[j] = td * w;
+ }
+ // sum up the difference terms to get the final value
+ if (nearest < 0.5*(n-i+1)) {
+ value += c[nearest]; // fork down
+ } else {
+ nearest--;
+ value += d[nearest]; // fork up
+ }
+ }
+
+ return value;
+ }
+
+ /**
+ * Calculate the coefficients of Lagrange polynomial from the
+ * interpolation data. It takes O(n^2) time.
+ * Note that this computation can be ill-conditioned: Use with caution
+ * and only when it is necessary.
+ */
+ protected void computeCoefficients() {
+ final int n = degree() + 1;
+ coefficients = new double[n];
+ for (int i = 0; i < n; i++) {
+ coefficients[i] = 0.0;
+ }
+
+ // c[] are the coefficients of P(x) = (x-x[0])(x-x[1])...(x-x[n-1])
+ final double[] c = new double[n+1];
+ c[0] = 1.0;
+ for (int i = 0; i < n; i++) {
+ for (int j = i; j > 0; j--) {
+ c[j] = c[j-1] - c[j] * x[i];
+ }
+ c[0] *= -x[i];
+ c[i+1] = 1;
+ }
+
+ final double[] tc = new double[n];
+ for (int i = 0; i < n; i++) {
+ // d = (x[i]-x[0])...(x[i]-x[i-1])(x[i]-x[i+1])...(x[i]-x[n-1])
+ double d = 1;
+ for (int j = 0; j < n; j++) {
+ if (i != j) {
+ d *= x[i] - x[j];
+ }
+ }
+ final double t = y[i] / d;
+ // Lagrange polynomial is the sum of n terms, each of which is a
+ // polynomial of degree n-1. tc[] are the coefficients of the i-th
+ // numerator Pi(x) = (x-x[0])...(x-x[i-1])(x-x[i+1])...(x-x[n-1]).
+ tc[n-1] = c[n]; // actually c[n] = 1
+ coefficients[n-1] += t * tc[n-1];
+ for (int j = n-2; j >= 0; j--) {
+ tc[j] = c[j+1] + tc[j+1] * x[i];
+ coefficients[j] += t * tc[j];
+ }
+ }
+
+ coefficientsComputed = true;
+ }
+
+ /**
+ * Check that the interpolation arrays are valid.
+ * The arrays features checked by this method are that both arrays have the
+ * same length and this length is at least 2.
+ *
+ * @param x Interpolating points array.
+ * @param y Interpolating values array.
+ * @param abort Whether to throw an exception if {@code x} is not sorted.
+ * @throws DimensionMismatchException if the array lengths are different.
+ * @throws NumberIsTooSmallException if the number of points is less than 2.
+ * @throws org.apache.commons.math3.exception.NonMonotonicSequenceException
+ * if {@code x} is not sorted in strictly increasing order and {@code abort}
+ * is {@code true}.
+ * @return {@code false} if the {@code x} is not sorted in increasing order,
+ * {@code true} otherwise.
+ * @see #evaluate(double[], double[], double)
+ * @see #computeCoefficients()
+ */
+ public static boolean verifyInterpolationArray(double x[], double y[], boolean abort)
+ throws DimensionMismatchException, NumberIsTooSmallException, NonMonotonicSequenceException {
+ if (x.length != y.length) {
+ throw new DimensionMismatchException(x.length, y.length);
+ }
+ if (x.length < 2) {
+ throw new NumberIsTooSmallException(LocalizedFormats.WRONG_NUMBER_OF_POINTS, 2, x.length, true);
+ }
+
+ return MathArrays.checkOrder(x, MathArrays.OrderDirection.INCREASING, true, abort);
+ }
+}
diff --git a/src/main/java/org/apache/commons/math3/analysis/polynomials/PolynomialFunctionNewtonForm.java b/src/main/java/org/apache/commons/math3/analysis/polynomials/PolynomialFunctionNewtonForm.java
new file mode 100644
index 0000000..fc2f1fd
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/analysis/polynomials/PolynomialFunctionNewtonForm.java
@@ -0,0 +1,245 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math3.analysis.polynomials;
+
+import org.apache.commons.math3.analysis.differentiation.DerivativeStructure;
+import org.apache.commons.math3.analysis.differentiation.UnivariateDifferentiableFunction;
+import org.apache.commons.math3.exception.DimensionMismatchException;
+import org.apache.commons.math3.exception.NoDataException;
+import org.apache.commons.math3.exception.NullArgumentException;
+import org.apache.commons.math3.exception.util.LocalizedFormats;
+import org.apache.commons.math3.util.MathUtils;
+
+/**
+ * Implements the representation of a real polynomial function in
+ * Newton Form. For reference, see <b>Elementary Numerical Analysis</b>,
+ * ISBN 0070124477, chapter 2.
+ * <p>
+ * The formula of polynomial in Newton form is
+ * p(x) = a[0] + a[1](x-c[0]) + a[2](x-c[0])(x-c[1]) + ... +
+ * a[n](x-c[0])(x-c[1])...(x-c[n-1])
+ * Note that the length of a[] is one more than the length of c[]</p>
+ *
+ * @since 1.2
+ */
+public class PolynomialFunctionNewtonForm implements UnivariateDifferentiableFunction {
+
+ /**
+ * The coefficients of the polynomial, ordered by degree -- i.e.
+ * coefficients[0] is the constant term and coefficients[n] is the
+ * coefficient of x^n where n is the degree of the polynomial.
+ */
+ private double coefficients[];
+
+ /**
+ * Centers of the Newton polynomial.
+ */
+ private final double c[];
+
+ /**
+ * When all c[i] = 0, a[] becomes normal polynomial coefficients,
+ * i.e. a[i] = coefficients[i].
+ */
+ private final double a[];
+
+ /**
+ * Whether the polynomial coefficients are available.
+ */
+ private boolean coefficientsComputed;
+
+ /**
+ * Construct a Newton polynomial with the given a[] and c[]. The order of
+ * centers are important in that if c[] shuffle, then values of a[] would
+ * completely change, not just a permutation of old a[].
+ * <p>
+ * The constructor makes copy of the input arrays and assigns them.</p>
+ *
+ * @param a Coefficients in Newton form formula.
+ * @param c Centers.
+ * @throws NullArgumentException if any argument is {@code null}.
+ * @throws NoDataException if any array has zero length.
+ * @throws DimensionMismatchException if the size difference between
+ * {@code a} and {@code c} is not equal to 1.
+ */
+ public PolynomialFunctionNewtonForm(double a[], double c[])
+ throws NullArgumentException, NoDataException, DimensionMismatchException {
+
+ verifyInputArray(a, c);
+ this.a = new double[a.length];
+ this.c = new double[c.length];
+ System.arraycopy(a, 0, this.a, 0, a.length);
+ System.arraycopy(c, 0, this.c, 0, c.length);
+ coefficientsComputed = false;
+ }
+
+ /**
+ * Calculate the function value at the given point.
+ *
+ * @param z Point at which the function value is to be computed.
+ * @return the function value.
+ */
+ public double value(double z) {
+ return evaluate(a, c, z);
+ }
+
+ /**
+ * {@inheritDoc}
+ * @since 3.1
+ */
+ public DerivativeStructure value(final DerivativeStructure t) {
+ verifyInputArray(a, c);
+
+ final int n = c.length;
+ DerivativeStructure value = new DerivativeStructure(t.getFreeParameters(), t.getOrder(), a[n]);
+ for (int i = n - 1; i >= 0; i--) {
+ value = t.subtract(c[i]).multiply(value).add(a[i]);
+ }
+
+ return value;
+
+ }
+
+ /**
+ * Returns the degree of the polynomial.
+ *
+ * @return the degree of the polynomial
+ */
+ public int degree() {
+ return c.length;
+ }
+
+ /**
+ * Returns a copy of coefficients in Newton form formula.
+ * <p>
+ * Changes made to the returned copy will not affect the polynomial.</p>
+ *
+ * @return a fresh copy of coefficients in Newton form formula
+ */
+ public double[] getNewtonCoefficients() {
+ double[] out = new double[a.length];
+ System.arraycopy(a, 0, out, 0, a.length);
+ return out;
+ }
+
+ /**
+ * Returns a copy of the centers array.
+ * <p>
+ * Changes made to the returned copy will not affect the polynomial.</p>
+ *
+ * @return a fresh copy of the centers array.
+ */
+ public double[] getCenters() {
+ double[] out = new double[c.length];
+ System.arraycopy(c, 0, out, 0, c.length);
+ return out;
+ }
+
+ /**
+ * Returns a copy of the coefficients array.
+ * <p>
+ * Changes made to the returned copy will not affect the polynomial.</p>
+ *
+ * @return a fresh copy of the coefficients array.
+ */
+ public double[] getCoefficients() {
+ if (!coefficientsComputed) {
+ computeCoefficients();
+ }
+ double[] out = new double[coefficients.length];
+ System.arraycopy(coefficients, 0, out, 0, coefficients.length);
+ return out;
+ }
+
+ /**
+ * Evaluate the Newton polynomial using nested multiplication. It is
+ * also called <a href="http://mathworld.wolfram.com/HornersRule.html">
+ * Horner's Rule</a> and takes O(N) time.
+ *
+ * @param a Coefficients in Newton form formula.
+ * @param c Centers.
+ * @param z Point at which the function value is to be computed.
+ * @return the function value.
+ * @throws NullArgumentException if any argument is {@code null}.
+ * @throws NoDataException if any array has zero length.
+ * @throws DimensionMismatchException if the size difference between
+ * {@code a} and {@code c} is not equal to 1.
+ */
+ public static double evaluate(double a[], double c[], double z)
+ throws NullArgumentException, DimensionMismatchException, NoDataException {
+ verifyInputArray(a, c);
+
+ final int n = c.length;
+ double value = a[n];
+ for (int i = n - 1; i >= 0; i--) {
+ value = a[i] + (z - c[i]) * value;
+ }
+
+ return value;
+ }
+
+ /**
+ * Calculate the normal polynomial coefficients given the Newton form.
+ * It also uses nested multiplication but takes O(N^2) time.
+ */
+ protected void computeCoefficients() {
+ final int n = degree();
+
+ coefficients = new double[n+1];
+ for (int i = 0; i <= n; i++) {
+ coefficients[i] = 0.0;
+ }
+
+ coefficients[0] = a[n];
+ for (int i = n-1; i >= 0; i--) {
+ for (int j = n-i; j > 0; j--) {
+ coefficients[j] = coefficients[j-1] - c[i] * coefficients[j];
+ }
+ coefficients[0] = a[i] - c[i] * coefficients[0];
+ }
+
+ coefficientsComputed = true;
+ }
+
+ /**
+ * Verifies that the input arrays are valid.
+ * <p>
+ * The centers must be distinct for interpolation purposes, but not
+ * for general use. Thus it is not verified here.</p>
+ *
+ * @param a the coefficients in Newton form formula
+ * @param c the centers
+ * @throws NullArgumentException if any argument is {@code null}.
+ * @throws NoDataException if any array has zero length.
+ * @throws DimensionMismatchException if the size difference between
+ * {@code a} and {@code c} is not equal to 1.
+ * @see org.apache.commons.math3.analysis.interpolation.DividedDifferenceInterpolator#computeDividedDifference(double[],
+ * double[])
+ */
+ protected static void verifyInputArray(double a[], double c[])
+ throws NullArgumentException, NoDataException, DimensionMismatchException {
+ MathUtils.checkNotNull(a);
+ MathUtils.checkNotNull(c);
+ if (a.length == 0 || c.length == 0) {
+ throw new NoDataException(LocalizedFormats.EMPTY_POLYNOMIALS_COEFFICIENTS_ARRAY);
+ }
+ if (a.length != c.length + 1) {
+ throw new DimensionMismatchException(LocalizedFormats.ARRAY_SIZES_SHOULD_HAVE_DIFFERENCE_1,
+ a.length, c.length);
+ }
+ }
+
+}
diff --git a/src/main/java/org/apache/commons/math3/analysis/polynomials/PolynomialSplineFunction.java b/src/main/java/org/apache/commons/math3/analysis/polynomials/PolynomialSplineFunction.java
new file mode 100644
index 0000000..ed5a4f9
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/analysis/polynomials/PolynomialSplineFunction.java
@@ -0,0 +1,246 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math3.analysis.polynomials;
+
+import java.util.Arrays;
+
+import org.apache.commons.math3.analysis.DifferentiableUnivariateFunction;
+import org.apache.commons.math3.analysis.UnivariateFunction;
+import org.apache.commons.math3.analysis.differentiation.DerivativeStructure;
+import org.apache.commons.math3.analysis.differentiation.UnivariateDifferentiableFunction;
+import org.apache.commons.math3.exception.DimensionMismatchException;
+import org.apache.commons.math3.exception.NonMonotonicSequenceException;
+import org.apache.commons.math3.exception.NullArgumentException;
+import org.apache.commons.math3.exception.NumberIsTooSmallException;
+import org.apache.commons.math3.exception.OutOfRangeException;
+import org.apache.commons.math3.exception.util.LocalizedFormats;
+import org.apache.commons.math3.util.MathArrays;
+
+/**
+ * Represents a polynomial spline function.
+ * <p>
+ * A <strong>polynomial spline function</strong> consists of a set of
+ * <i>interpolating polynomials</i> and an ascending array of domain
+ * <i>knot points</i>, determining the intervals over which the spline function
+ * is defined by the constituent polynomials. The polynomials are assumed to
+ * have been computed to match the values of another function at the knot
+ * points. The value consistency constraints are not currently enforced by
+ * <code>PolynomialSplineFunction</code> itself, but are assumed to hold among
+ * the polynomials and knot points passed to the constructor.</p>
+ * <p>
+ * N.B.: The polynomials in the <code>polynomials</code> property must be
+ * centered on the knot points to compute the spline function values.
+ * See below.</p>
+ * <p>
+ * The domain of the polynomial spline function is
+ * <code>[smallest knot, largest knot]</code>. Attempts to evaluate the
+ * function at values outside of this range generate IllegalArgumentExceptions.
+ * </p>
+ * <p>
+ * The value of the polynomial spline function for an argument <code>x</code>
+ * is computed as follows:
+ * <ol>
+ * <li>The knot array is searched to find the segment to which <code>x</code>
+ * belongs. If <code>x</code> is less than the smallest knot point or greater
+ * than the largest one, an <code>IllegalArgumentException</code>
+ * is thrown.</li>
+ * <li> Let <code>j</code> be the index of the largest knot point that is less
+ * than or equal to <code>x</code>. The value returned is
+ * {@code polynomials[j](x - knot[j])}</li></ol>
+ *
+ */
+public class PolynomialSplineFunction implements UnivariateDifferentiableFunction, DifferentiableUnivariateFunction {
+ /**
+ * Spline segment interval delimiters (knots).
+ * Size is n + 1 for n segments.
+ */
+ private final double knots[];
+ /**
+ * The polynomial functions that make up the spline. The first element
+ * determines the value of the spline over the first subinterval, the
+ * second over the second, etc. Spline function values are determined by
+ * evaluating these functions at {@code (x - knot[i])} where i is the
+ * knot segment to which x belongs.
+ */
+ private final PolynomialFunction polynomials[];
+ /**
+ * Number of spline segments. It is equal to the number of polynomials and
+ * to the number of partition points - 1.
+ */
+ private final int n;
+
+
+ /**
+ * Construct a polynomial spline function with the given segment delimiters
+ * and interpolating polynomials.
+ * The constructor copies both arrays and assigns the copies to the knots
+ * and polynomials properties, respectively.
+ *
+ * @param knots Spline segment interval delimiters.
+ * @param polynomials Polynomial functions that make up the spline.
+ * @throws NullArgumentException if either of the input arrays is {@code null}.
+ * @throws NumberIsTooSmallException if knots has length less than 2.
+ * @throws DimensionMismatchException if {@code polynomials.length != knots.length - 1}.
+ * @throws NonMonotonicSequenceException if the {@code knots} array is not strictly increasing.
+ *
+ */
+ public PolynomialSplineFunction(double knots[], PolynomialFunction polynomials[])
+ throws NullArgumentException, NumberIsTooSmallException,
+ DimensionMismatchException, NonMonotonicSequenceException{
+ if (knots == null ||
+ polynomials == null) {
+ throw new NullArgumentException();
+ }
+ if (knots.length < 2) {
+ throw new NumberIsTooSmallException(LocalizedFormats.NOT_ENOUGH_POINTS_IN_SPLINE_PARTITION,
+ 2, knots.length, false);
+ }
+ if (knots.length - 1 != polynomials.length) {
+ throw new DimensionMismatchException(polynomials.length, knots.length);
+ }
+ MathArrays.checkOrder(knots);
+
+ this.n = knots.length -1;
+ this.knots = new double[n + 1];
+ System.arraycopy(knots, 0, this.knots, 0, n + 1);
+ this.polynomials = new PolynomialFunction[n];
+ System.arraycopy(polynomials, 0, this.polynomials, 0, n);
+ }
+
+ /**
+ * Compute the value for the function.
+ * See {@link PolynomialSplineFunction} for details on the algorithm for
+ * computing the value of the function.
+ *
+ * @param v Point for which the function value should be computed.
+ * @return the value.
+ * @throws OutOfRangeException if {@code v} is outside of the domain of the
+ * spline function (smaller than the smallest knot point or larger than the
+ * largest knot point).
+ */
+ public double value(double v) {
+ if (v < knots[0] || v > knots[n]) {
+ throw new OutOfRangeException(v, knots[0], knots[n]);
+ }
+ int i = Arrays.binarySearch(knots, v);
+ if (i < 0) {
+ i = -i - 2;
+ }
+ // This will handle the case where v is the last knot value
+ // There are only n-1 polynomials, so if v is the last knot
+ // then we will use the last polynomial to calculate the value.
+ if ( i >= polynomials.length ) {
+ i--;
+ }
+ return polynomials[i].value(v - knots[i]);
+ }
+
+ /**
+ * Get the derivative of the polynomial spline function.
+ *
+ * @return the derivative function.
+ */
+ public UnivariateFunction derivative() {
+ return polynomialSplineDerivative();
+ }
+
+ /**
+ * Get the derivative of the polynomial spline function.
+ *
+ * @return the derivative function.
+ */
+ public PolynomialSplineFunction polynomialSplineDerivative() {
+ PolynomialFunction derivativePolynomials[] = new PolynomialFunction[n];
+ for (int i = 0; i < n; i++) {
+ derivativePolynomials[i] = polynomials[i].polynomialDerivative();
+ }
+ return new PolynomialSplineFunction(knots, derivativePolynomials);
+ }
+
+
+ /** {@inheritDoc}
+ * @since 3.1
+ */
+ public DerivativeStructure value(final DerivativeStructure t) {
+ final double t0 = t.getValue();
+ if (t0 < knots[0] || t0 > knots[n]) {
+ throw new OutOfRangeException(t0, knots[0], knots[n]);
+ }
+ int i = Arrays.binarySearch(knots, t0);
+ if (i < 0) {
+ i = -i - 2;
+ }
+ // This will handle the case where t is the last knot value
+ // There are only n-1 polynomials, so if t is the last knot
+ // then we will use the last polynomial to calculate the value.
+ if ( i >= polynomials.length ) {
+ i--;
+ }
+ return polynomials[i].value(t.subtract(knots[i]));
+ }
+
+ /**
+ * Get the number of spline segments.
+ * It is also the number of polynomials and the number of knot points - 1.
+ *
+ * @return the number of spline segments.
+ */
+ public int getN() {
+ return n;
+ }
+
+ /**
+ * Get a copy of the interpolating polynomials array.
+ * It returns a fresh copy of the array. Changes made to the copy will
+ * not affect the polynomials property.
+ *
+ * @return the interpolating polynomials.
+ */
+ public PolynomialFunction[] getPolynomials() {
+ PolynomialFunction p[] = new PolynomialFunction[n];
+ System.arraycopy(polynomials, 0, p, 0, n);
+ return p;
+ }
+
+ /**
+ * Get an array copy of the knot points.
+ * It returns a fresh copy of the array. Changes made to the copy
+ * will not affect the knots property.
+ *
+ * @return the knot points.
+ */
+ public double[] getKnots() {
+ double out[] = new double[n + 1];
+ System.arraycopy(knots, 0, out, 0, n + 1);
+ return out;
+ }
+
+ /**
+ * Indicates whether a point is within the interpolation range.
+ *
+ * @param x Point.
+ * @return {@code true} if {@code x} is a valid point.
+ */
+ public boolean isValidPoint(double x) {
+ if (x < knots[0] ||
+ x > knots[n]) {
+ return false;
+ } else {
+ return true;
+ }
+ }
+}
diff --git a/src/main/java/org/apache/commons/math3/analysis/polynomials/PolynomialsUtils.java b/src/main/java/org/apache/commons/math3/analysis/polynomials/PolynomialsUtils.java
new file mode 100644
index 0000000..bb697b8
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/analysis/polynomials/PolynomialsUtils.java
@@ -0,0 +1,444 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math3.analysis.polynomials;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.commons.math3.fraction.BigFraction;
+import org.apache.commons.math3.util.CombinatoricsUtils;
+import org.apache.commons.math3.util.FastMath;
+
+/**
+ * A collection of static methods that operate on or return polynomials.
+ *
+ * @since 2.0
+ */
+public class PolynomialsUtils {
+
+ /** Coefficients for Chebyshev polynomials. */
+ private static final List<BigFraction> CHEBYSHEV_COEFFICIENTS;
+
+ /** Coefficients for Hermite polynomials. */
+ private static final List<BigFraction> HERMITE_COEFFICIENTS;
+
+ /** Coefficients for Laguerre polynomials. */
+ private static final List<BigFraction> LAGUERRE_COEFFICIENTS;
+
+ /** Coefficients for Legendre polynomials. */
+ private static final List<BigFraction> LEGENDRE_COEFFICIENTS;
+
+ /** Coefficients for Jacobi polynomials. */
+ private static final Map<JacobiKey, List<BigFraction>> JACOBI_COEFFICIENTS;
+
+ static {
+
+ // initialize recurrence for Chebyshev polynomials
+ // T0(X) = 1, T1(X) = 0 + 1 * X
+ CHEBYSHEV_COEFFICIENTS = new ArrayList<BigFraction>();
+ CHEBYSHEV_COEFFICIENTS.add(BigFraction.ONE);
+ CHEBYSHEV_COEFFICIENTS.add(BigFraction.ZERO);
+ CHEBYSHEV_COEFFICIENTS.add(BigFraction.ONE);
+
+ // initialize recurrence for Hermite polynomials
+ // H0(X) = 1, H1(X) = 0 + 2 * X
+ HERMITE_COEFFICIENTS = new ArrayList<BigFraction>();
+ HERMITE_COEFFICIENTS.add(BigFraction.ONE);
+ HERMITE_COEFFICIENTS.add(BigFraction.ZERO);
+ HERMITE_COEFFICIENTS.add(BigFraction.TWO);
+
+ // initialize recurrence for Laguerre polynomials
+ // L0(X) = 1, L1(X) = 1 - 1 * X
+ LAGUERRE_COEFFICIENTS = new ArrayList<BigFraction>();
+ LAGUERRE_COEFFICIENTS.add(BigFraction.ONE);
+ LAGUERRE_COEFFICIENTS.add(BigFraction.ONE);
+ LAGUERRE_COEFFICIENTS.add(BigFraction.MINUS_ONE);
+
+ // initialize recurrence for Legendre polynomials
+ // P0(X) = 1, P1(X) = 0 + 1 * X
+ LEGENDRE_COEFFICIENTS = new ArrayList<BigFraction>();
+ LEGENDRE_COEFFICIENTS.add(BigFraction.ONE);
+ LEGENDRE_COEFFICIENTS.add(BigFraction.ZERO);
+ LEGENDRE_COEFFICIENTS.add(BigFraction.ONE);
+
+ // initialize map for Jacobi polynomials
+ JACOBI_COEFFICIENTS = new HashMap<JacobiKey, List<BigFraction>>();
+
+ }
+
+ /**
+ * Private constructor, to prevent instantiation.
+ */
+ private PolynomialsUtils() {
+ }
+
+ /**
+ * Create a Chebyshev polynomial of the first kind.
+ * <p><a href="https://en.wikipedia.org/wiki/Chebyshev_polynomials">Chebyshev
+ * polynomials of the first kind</a> are orthogonal polynomials.
+ * They can be defined by the following recurrence relations:</p><p>
+ * \(
+ * T_0(x) = 1 \\
+ * T_1(x) = x \\
+ * T_{k+1}(x) = 2x T_k(x) - T_{k-1}(x)
+ * \)
+ * </p>
+ * @param degree degree of the polynomial
+ * @return Chebyshev polynomial of specified degree
+ */
+ public static PolynomialFunction createChebyshevPolynomial(final int degree) {
+ return buildPolynomial(degree, CHEBYSHEV_COEFFICIENTS,
+ new RecurrenceCoefficientsGenerator() {
+ /** Fixed recurrence coefficients. */
+ private final BigFraction[] coeffs = { BigFraction.ZERO, BigFraction.TWO, BigFraction.ONE };
+ /** {@inheritDoc} */
+ public BigFraction[] generate(int k) {
+ return coeffs;
+ }
+ });
+ }
+
+ /**
+ * Create a Hermite polynomial.
+ * <p><a href="http://mathworld.wolfram.com/HermitePolynomial.html">Hermite
+ * polynomials</a> are orthogonal polynomials.
+ * They can be defined by the following recurrence relations:</p><p>
+ * \(
+ * H_0(x) = 1 \\
+ * H_1(x) = 2x \\
+ * H_{k+1}(x) = 2x H_k(X) - 2k H_{k-1}(x)
+ * \)
+ * </p>
+
+ * @param degree degree of the polynomial
+ * @return Hermite polynomial of specified degree
+ */
+ public static PolynomialFunction createHermitePolynomial(final int degree) {
+ return buildPolynomial(degree, HERMITE_COEFFICIENTS,
+ new RecurrenceCoefficientsGenerator() {
+ /** {@inheritDoc} */
+ public BigFraction[] generate(int k) {
+ return new BigFraction[] {
+ BigFraction.ZERO,
+ BigFraction.TWO,
+ new BigFraction(2 * k)};
+ }
+ });
+ }
+
+ /**
+ * Create a Laguerre polynomial.
+ * <p><a href="http://mathworld.wolfram.com/LaguerrePolynomial.html">Laguerre
+ * polynomials</a> are orthogonal polynomials.
+ * They can be defined by the following recurrence relations:</p><p>
+ * \(
+ * L_0(x) = 1 \\
+ * L_1(x) = 1 - x \\
+ * (k+1) L_{k+1}(x) = (2k + 1 - x) L_k(x) - k L_{k-1}(x)
+ * \)
+ * </p>
+ * @param degree degree of the polynomial
+ * @return Laguerre polynomial of specified degree
+ */
+ public static PolynomialFunction createLaguerrePolynomial(final int degree) {
+ return buildPolynomial(degree, LAGUERRE_COEFFICIENTS,
+ new RecurrenceCoefficientsGenerator() {
+ /** {@inheritDoc} */
+ public BigFraction[] generate(int k) {
+ final int kP1 = k + 1;
+ return new BigFraction[] {
+ new BigFraction(2 * k + 1, kP1),
+ new BigFraction(-1, kP1),
+ new BigFraction(k, kP1)};
+ }
+ });
+ }
+
+ /**
+ * Create a Legendre polynomial.
+ * <p><a href="http://mathworld.wolfram.com/LegendrePolynomial.html">Legendre
+ * polynomials</a> are orthogonal polynomials.
+ * They can be defined by the following recurrence relations:</p><p>
+ * \(
+ * P_0(x) = 1 \\
+ * P_1(x) = x \\
+ * (k+1) P_{k+1}(x) = (2k+1) x P_k(x) - k P_{k-1}(x)
+ * \)
+ * </p>
+ * @param degree degree of the polynomial
+ * @return Legendre polynomial of specified degree
+ */
+ public static PolynomialFunction createLegendrePolynomial(final int degree) {
+ return buildPolynomial(degree, LEGENDRE_COEFFICIENTS,
+ new RecurrenceCoefficientsGenerator() {
+ /** {@inheritDoc} */
+ public BigFraction[] generate(int k) {
+ final int kP1 = k + 1;
+ return new BigFraction[] {
+ BigFraction.ZERO,
+ new BigFraction(k + kP1, kP1),
+ new BigFraction(k, kP1)};
+ }
+ });
+ }
+
+ /**
+ * Create a Jacobi polynomial.
+ * <p><a href="http://mathworld.wolfram.com/JacobiPolynomial.html">Jacobi
+ * polynomials</a> are orthogonal polynomials.
+ * They can be defined by the following recurrence relations:</p><p>
+ * \(
+ * P_0^{vw}(x) = 1 \\
+ * P_{-1}^{vw}(x) = 0 \\
+ * 2k(k + v + w)(2k + v + w - 2) P_k^{vw}(x) = \\
+ * (2k + v + w - 1)[(2k + v + w)(2k + v + w - 2) x + v^2 - w^2] P_{k-1}^{vw}(x) \\
+ * - 2(k + v - 1)(k + w - 1)(2k + v + w) P_{k-2}^{vw}(x)
+ * \)
+ * </p>
+ * @param degree degree of the polynomial
+ * @param v first exponent
+ * @param w second exponent
+ * @return Jacobi polynomial of specified degree
+ */
+ public static PolynomialFunction createJacobiPolynomial(final int degree, final int v, final int w) {
+
+ // select the appropriate list
+ final JacobiKey key = new JacobiKey(v, w);
+
+ if (!JACOBI_COEFFICIENTS.containsKey(key)) {
+
+ // allocate a new list for v, w
+ final List<BigFraction> list = new ArrayList<BigFraction>();
+ JACOBI_COEFFICIENTS.put(key, list);
+
+ // Pv,w,0(x) = 1;
+ list.add(BigFraction.ONE);
+
+ // P1(x) = (v - w) / 2 + (2 + v + w) * X / 2
+ list.add(new BigFraction(v - w, 2));
+ list.add(new BigFraction(2 + v + w, 2));
+
+ }
+
+ return buildPolynomial(degree, JACOBI_COEFFICIENTS.get(key),
+ new RecurrenceCoefficientsGenerator() {
+ /** {@inheritDoc} */
+ public BigFraction[] generate(int k) {
+ k++;
+ final int kvw = k + v + w;
+ final int twoKvw = kvw + k;
+ final int twoKvwM1 = twoKvw - 1;
+ final int twoKvwM2 = twoKvw - 2;
+ final int den = 2 * k * kvw * twoKvwM2;
+
+ return new BigFraction[] {
+ new BigFraction(twoKvwM1 * (v * v - w * w), den),
+ new BigFraction(twoKvwM1 * twoKvw * twoKvwM2, den),
+ new BigFraction(2 * (k + v - 1) * (k + w - 1) * twoKvw, den)
+ };
+ }
+ });
+
+ }
+
+ /** Inner class for Jacobi polynomials keys. */
+ private static class JacobiKey {
+
+ /** First exponent. */
+ private final int v;
+
+ /** Second exponent. */
+ private final int w;
+
+ /** Simple constructor.
+ * @param v first exponent
+ * @param w second exponent
+ */
+ JacobiKey(final int v, final int w) {
+ this.v = v;
+ this.w = w;
+ }
+
+ /** Get hash code.
+ * @return hash code
+ */
+ @Override
+ public int hashCode() {
+ return (v << 16) ^ w;
+ }
+
+ /** Check if the instance represent the same key as another instance.
+ * @param key other key
+ * @return true if the instance and the other key refer to the same polynomial
+ */
+ @Override
+ public boolean equals(final Object key) {
+
+ if ((key == null) || !(key instanceof JacobiKey)) {
+ return false;
+ }
+
+ final JacobiKey otherK = (JacobiKey) key;
+ return (v == otherK.v) && (w == otherK.w);
+
+ }
+ }
+
+ /**
+ * Compute the coefficients of the polynomial \(P_s(x)\)
+ * whose values at point {@code x} will be the same as the those from the
+ * original polynomial \(P(x)\) when computed at {@code x + shift}.
+ * <p>
+ * More precisely, let \(\Delta = \) {@code shift} and let
+ * \(P_s(x) = P(x + \Delta)\). The returned array
+ * consists of the coefficients of \(P_s\). So if \(a_0, ..., a_{n-1}\)
+ * are the coefficients of \(P\), then the returned array
+ * \(b_0, ..., b_{n-1}\) satisfies the identity
+ * \(\sum_{i=0}^{n-1} b_i x^i = \sum_{i=0}^{n-1} a_i (x + \Delta)^i\) for all \(x\).
+ *
+ * @param coefficients Coefficients of the original polynomial.
+ * @param shift Shift value.
+ * @return the coefficients \(b_i\) of the shifted
+ * polynomial.
+ */
+ public static double[] shift(final double[] coefficients,
+ final double shift) {
+ final int dp1 = coefficients.length;
+ final double[] newCoefficients = new double[dp1];
+
+ // Pascal triangle.
+ final int[][] coeff = new int[dp1][dp1];
+ for (int i = 0; i < dp1; i++){
+ for(int j = 0; j <= i; j++){
+ coeff[i][j] = (int) CombinatoricsUtils.binomialCoefficient(i, j);
+ }
+ }
+
+ // First polynomial coefficient.
+ for (int i = 0; i < dp1; i++){
+ newCoefficients[0] += coefficients[i] * FastMath.pow(shift, i);
+ }
+
+ // Superior order.
+ final int d = dp1 - 1;
+ for (int i = 0; i < d; i++) {
+ for (int j = i; j < d; j++){
+ newCoefficients[i + 1] += coeff[j + 1][j - i] *
+ coefficients[j + 1] * FastMath.pow(shift, j - i);
+ }
+ }
+
+ return newCoefficients;
+ }
+
+
+ /** Get the coefficients array for a given degree.
+ * @param degree degree of the polynomial
+ * @param coefficients list where the computed coefficients are stored
+ * @param generator recurrence coefficients generator
+ * @return coefficients array
+ */
+ private static PolynomialFunction buildPolynomial(final int degree,
+ final List<BigFraction> coefficients,
+ final RecurrenceCoefficientsGenerator generator) {
+ synchronized (coefficients) {
+ final int maxDegree = (int) FastMath.floor(FastMath.sqrt(2 * coefficients.size())) - 1;
+ if (degree > maxDegree) {
+ computeUpToDegree(degree, maxDegree, generator, coefficients);
+ }
+ }
+
+ // coefficient for polynomial 0 is l [0]
+ // coefficients for polynomial 1 are l [1] ... l [2] (degrees 0 ... 1)
+ // coefficients for polynomial 2 are l [3] ... l [5] (degrees 0 ... 2)
+ // coefficients for polynomial 3 are l [6] ... l [9] (degrees 0 ... 3)
+ // coefficients for polynomial 4 are l[10] ... l[14] (degrees 0 ... 4)
+ // coefficients for polynomial 5 are l[15] ... l[20] (degrees 0 ... 5)
+ // coefficients for polynomial 6 are l[21] ... l[27] (degrees 0 ... 6)
+ // ...
+ final int start = degree * (degree + 1) / 2;
+
+ final double[] a = new double[degree + 1];
+ for (int i = 0; i <= degree; ++i) {
+ a[i] = coefficients.get(start + i).doubleValue();
+ }
+
+ // build the polynomial
+ return new PolynomialFunction(a);
+
+ }
+
+ /** Compute polynomial coefficients up to a given degree.
+ * @param degree maximal degree
+ * @param maxDegree current maximal degree
+ * @param generator recurrence coefficients generator
+ * @param coefficients list where the computed coefficients should be appended
+ */
+ private static void computeUpToDegree(final int degree, final int maxDegree,
+ final RecurrenceCoefficientsGenerator generator,
+ final List<BigFraction> coefficients) {
+
+ int startK = (maxDegree - 1) * maxDegree / 2;
+ for (int k = maxDegree; k < degree; ++k) {
+
+ // start indices of two previous polynomials Pk(X) and Pk-1(X)
+ int startKm1 = startK;
+ startK += k;
+
+ // Pk+1(X) = (a[0] + a[1] X) Pk(X) - a[2] Pk-1(X)
+ BigFraction[] ai = generator.generate(k);
+
+ BigFraction ck = coefficients.get(startK);
+ BigFraction ckm1 = coefficients.get(startKm1);
+
+ // degree 0 coefficient
+ coefficients.add(ck.multiply(ai[0]).subtract(ckm1.multiply(ai[2])));
+
+ // degree 1 to degree k-1 coefficients
+ for (int i = 1; i < k; ++i) {
+ final BigFraction ckPrev = ck;
+ ck = coefficients.get(startK + i);
+ ckm1 = coefficients.get(startKm1 + i);
+ coefficients.add(ck.multiply(ai[0]).add(ckPrev.multiply(ai[1])).subtract(ckm1.multiply(ai[2])));
+ }
+
+ // degree k coefficient
+ final BigFraction ckPrev = ck;
+ ck = coefficients.get(startK + k);
+ coefficients.add(ck.multiply(ai[0]).add(ckPrev.multiply(ai[1])));
+
+ // degree k+1 coefficient
+ coefficients.add(ck.multiply(ai[1]));
+
+ }
+
+ }
+
+ /** Interface for recurrence coefficients generation. */
+ private interface RecurrenceCoefficientsGenerator {
+ /**
+ * Generate recurrence coefficients.
+ * @param k highest degree of the polynomials used in the recurrence
+ * @return an array of three coefficients such that
+ * \( P_{k+1}(x) = (a[0] + a[1] x) P_k(x) - a[2] P_{k-1}(x) \)
+ */
+ BigFraction[] generate(int k);
+ }
+
+}
diff --git a/src/main/java/org/apache/commons/math3/analysis/polynomials/package-info.java b/src/main/java/org/apache/commons/math3/analysis/polynomials/package-info.java
new file mode 100644
index 0000000..85b99f7
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/analysis/polynomials/package-info.java
@@ -0,0 +1,23 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/**
+ *
+ * Univariate real polynomials implementations, seen as differentiable
+ * univariate real functions.
+ *
+ */
+package org.apache.commons.math3.analysis.polynomials;
diff --git a/src/main/java/org/apache/commons/math3/analysis/solvers/AbstractDifferentiableUnivariateSolver.java b/src/main/java/org/apache/commons/math3/analysis/solvers/AbstractDifferentiableUnivariateSolver.java
new file mode 100644
index 0000000..d0fda00
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/analysis/solvers/AbstractDifferentiableUnivariateSolver.java
@@ -0,0 +1,82 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math3.analysis.solvers;
+
+import org.apache.commons.math3.analysis.DifferentiableUnivariateFunction;
+import org.apache.commons.math3.analysis.UnivariateFunction;
+import org.apache.commons.math3.exception.TooManyEvaluationsException;
+
+/**
+ * Provide a default implementation for several functions useful to generic
+ * solvers.
+ *
+ * @since 3.0
+ * @deprecated as of 3.1, replaced by {@link AbstractUnivariateDifferentiableSolver}
+ */
+@Deprecated
+public abstract class AbstractDifferentiableUnivariateSolver
+ extends BaseAbstractUnivariateSolver<DifferentiableUnivariateFunction>
+ implements DifferentiableUnivariateSolver {
+ /** Derivative of the function to solve. */
+ private UnivariateFunction functionDerivative;
+
+ /**
+ * Construct a solver with given absolute accuracy.
+ *
+ * @param absoluteAccuracy Maximum absolute error.
+ */
+ protected AbstractDifferentiableUnivariateSolver(final double absoluteAccuracy) {
+ super(absoluteAccuracy);
+ }
+
+ /**
+ * Construct a solver with given accuracies.
+ *
+ * @param relativeAccuracy Maximum relative error.
+ * @param absoluteAccuracy Maximum absolute error.
+ * @param functionValueAccuracy Maximum function value error.
+ */
+ protected AbstractDifferentiableUnivariateSolver(final double relativeAccuracy,
+ final double absoluteAccuracy,
+ final double functionValueAccuracy) {
+ super(relativeAccuracy, absoluteAccuracy, functionValueAccuracy);
+ }
+
+ /**
+ * Compute the objective function value.
+ *
+ * @param point Point at which the objective function must be evaluated.
+ * @return the objective function value at specified point.
+ * @throws TooManyEvaluationsException if the maximal number of evaluations is exceeded.
+ */
+ protected double computeDerivativeObjectiveValue(double point)
+ throws TooManyEvaluationsException {
+ incrementEvaluationCount();
+ return functionDerivative.value(point);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ protected void setup(int maxEval, DifferentiableUnivariateFunction f,
+ double min, double max, double startValue) {
+ super.setup(maxEval, f, min, max, startValue);
+ functionDerivative = f.derivative();
+ }
+}
diff --git a/src/main/java/org/apache/commons/math3/analysis/solvers/AbstractPolynomialSolver.java b/src/main/java/org/apache/commons/math3/analysis/solvers/AbstractPolynomialSolver.java
new file mode 100644
index 0000000..d641e87
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/analysis/solvers/AbstractPolynomialSolver.java
@@ -0,0 +1,80 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math3.analysis.solvers;
+
+import org.apache.commons.math3.analysis.polynomials.PolynomialFunction;
+
+/**
+ * Base class for solvers.
+ *
+ * @since 3.0
+ */
+public abstract class AbstractPolynomialSolver
+ extends BaseAbstractUnivariateSolver<PolynomialFunction>
+ implements PolynomialSolver {
+ /** Function. */
+ private PolynomialFunction polynomialFunction;
+
+ /**
+ * Construct a solver with given absolute accuracy.
+ *
+ * @param absoluteAccuracy Maximum absolute error.
+ */
+ protected AbstractPolynomialSolver(final double absoluteAccuracy) {
+ super(absoluteAccuracy);
+ }
+ /**
+ * Construct a solver with given accuracies.
+ *
+ * @param relativeAccuracy Maximum relative error.
+ * @param absoluteAccuracy Maximum absolute error.
+ */
+ protected AbstractPolynomialSolver(final double relativeAccuracy,
+ final double absoluteAccuracy) {
+ super(relativeAccuracy, absoluteAccuracy);
+ }
+ /**
+ * Construct a solver with given accuracies.
+ *
+ * @param relativeAccuracy Maximum relative error.
+ * @param absoluteAccuracy Maximum absolute error.
+ * @param functionValueAccuracy Maximum function value error.
+ */
+ protected AbstractPolynomialSolver(final double relativeAccuracy,
+ final double absoluteAccuracy,
+ final double functionValueAccuracy) {
+ super(relativeAccuracy, absoluteAccuracy, functionValueAccuracy);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ protected void setup(int maxEval, PolynomialFunction f,
+ double min, double max, double startValue) {
+ super.setup(maxEval, f, min, max, startValue);
+ polynomialFunction = f;
+ }
+
+ /**
+ * @return the coefficients of the polynomial function.
+ */
+ protected double[] getCoefficients() {
+ return polynomialFunction.getCoefficients();
+ }
+}
diff --git a/src/main/java/org/apache/commons/math3/analysis/solvers/AbstractUnivariateDifferentiableSolver.java b/src/main/java/org/apache/commons/math3/analysis/solvers/AbstractUnivariateDifferentiableSolver.java
new file mode 100644
index 0000000..9745e9b
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/analysis/solvers/AbstractUnivariateDifferentiableSolver.java
@@ -0,0 +1,82 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math3.analysis.solvers;
+
+import org.apache.commons.math3.analysis.differentiation.DerivativeStructure;
+import org.apache.commons.math3.analysis.differentiation.UnivariateDifferentiableFunction;
+import org.apache.commons.math3.exception.TooManyEvaluationsException;
+
+/**
+ * Provide a default implementation for several functions useful to generic
+ * solvers.
+ *
+ * @since 3.1
+ */
+public abstract class AbstractUnivariateDifferentiableSolver
+ extends BaseAbstractUnivariateSolver<UnivariateDifferentiableFunction>
+ implements UnivariateDifferentiableSolver {
+
+ /** Function to solve. */
+ private UnivariateDifferentiableFunction function;
+
+ /**
+ * Construct a solver with given absolute accuracy.
+ *
+ * @param absoluteAccuracy Maximum absolute error.
+ */
+ protected AbstractUnivariateDifferentiableSolver(final double absoluteAccuracy) {
+ super(absoluteAccuracy);
+ }
+
+ /**
+ * Construct a solver with given accuracies.
+ *
+ * @param relativeAccuracy Maximum relative error.
+ * @param absoluteAccuracy Maximum absolute error.
+ * @param functionValueAccuracy Maximum function value error.
+ */
+ protected AbstractUnivariateDifferentiableSolver(final double relativeAccuracy,
+ final double absoluteAccuracy,
+ final double functionValueAccuracy) {
+ super(relativeAccuracy, absoluteAccuracy, functionValueAccuracy);
+ }
+
+ /**
+ * Compute the objective function value.
+ *
+ * @param point Point at which the objective function must be evaluated.
+ * @return the objective function value and derivative at specified point.
+ * @throws TooManyEvaluationsException
+ * if the maximal number of evaluations is exceeded.
+ */
+ protected DerivativeStructure computeObjectiveValueAndDerivative(double point)
+ throws TooManyEvaluationsException {
+ incrementEvaluationCount();
+ return function.value(new DerivativeStructure(1, 1, 0, point));
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ protected void setup(int maxEval, UnivariateDifferentiableFunction f,
+ double min, double max, double startValue) {
+ super.setup(maxEval, f, min, max, startValue);
+ function = f;
+ }
+}
diff --git a/src/main/java/org/apache/commons/math3/analysis/solvers/AbstractUnivariateSolver.java b/src/main/java/org/apache/commons/math3/analysis/solvers/AbstractUnivariateSolver.java
new file mode 100644
index 0000000..078c70f
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/analysis/solvers/AbstractUnivariateSolver.java
@@ -0,0 +1,60 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math3.analysis.solvers;
+
+import org.apache.commons.math3.analysis.UnivariateFunction;
+
+/**
+ * Base class for solvers.
+ *
+ * @since 3.0
+ */
+public abstract class AbstractUnivariateSolver
+ extends BaseAbstractUnivariateSolver<UnivariateFunction>
+ implements UnivariateSolver {
+ /**
+ * Construct a solver with given absolute accuracy.
+ *
+ * @param absoluteAccuracy Maximum absolute error.
+ */
+ protected AbstractUnivariateSolver(final double absoluteAccuracy) {
+ super(absoluteAccuracy);
+ }
+ /**
+ * Construct a solver with given accuracies.
+ *
+ * @param relativeAccuracy Maximum relative error.
+ * @param absoluteAccuracy Maximum absolute error.
+ */
+ protected AbstractUnivariateSolver(final double relativeAccuracy,
+ final double absoluteAccuracy) {
+ super(relativeAccuracy, absoluteAccuracy);
+ }
+ /**
+ * Construct a solver with given accuracies.
+ *
+ * @param relativeAccuracy Maximum relative error.
+ * @param absoluteAccuracy Maximum absolute error.
+ * @param functionValueAccuracy Maximum function value error.
+ */
+ protected AbstractUnivariateSolver(final double relativeAccuracy,
+ final double absoluteAccuracy,
+ final double functionValueAccuracy) {
+ super(relativeAccuracy, absoluteAccuracy, functionValueAccuracy);
+ }
+}
diff --git a/src/main/java/org/apache/commons/math3/analysis/solvers/AllowedSolution.java b/src/main/java/org/apache/commons/math3/analysis/solvers/AllowedSolution.java
new file mode 100644
index 0000000..a02a29b
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/analysis/solvers/AllowedSolution.java
@@ -0,0 +1,75 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math3.analysis.solvers;
+
+
+/** The kinds of solutions that a {@link BracketedUnivariateSolver
+ * (bracketed univariate real) root-finding algorithm} may accept as solutions.
+ * This basically controls whether or not under-approximations and
+ * over-approximations are allowed.
+ *
+ * <p>If all solutions are accepted ({@link #ANY_SIDE}), then the solution
+ * that the root-finding algorithm returns for a given root may be equal to the
+ * actual root, but it may also be an approximation that is slightly smaller
+ * or slightly larger than the actual root. Root-finding algorithms generally
+ * only guarantee that the returned solution is within the requested
+ * tolerances. In certain cases however, in particular for
+ * {@link org.apache.commons.math3.ode.events.EventHandler state events} of
+ * {@link org.apache.commons.math3.ode.ODEIntegrator ODE solvers}, it
+ * may be necessary to guarantee that a solution is returned that lies on a
+ * specific side the solution.</p>
+ *
+ * @see BracketedUnivariateSolver
+ * @since 3.0
+ */
+public enum AllowedSolution {
+ /** There are no additional side restriction on the solutions for
+ * root-finding. That is, both under-approximations and over-approximations
+ * are allowed. So, if a function f(x) has a root at x = x0, then the
+ * root-finding result s may be smaller than x0, equal to x0, or greater
+ * than x0.
+ */
+ ANY_SIDE,
+
+ /** Only solutions that are less than or equal to the actual root are
+ * acceptable as solutions for root-finding. In other words,
+ * over-approximations are not allowed. So, if a function f(x) has a root
+ * at x = x0, then the root-finding result s must satisfy s &lt;= x0.
+ */
+ LEFT_SIDE,
+
+ /** Only solutions that are greater than or equal to the actual root are
+ * acceptable as solutions for root-finding. In other words,
+ * under-approximations are not allowed. So, if a function f(x) has a root
+ * at x = x0, then the root-finding result s must satisfy s &gt;= x0.
+ */
+ RIGHT_SIDE,
+
+ /** Only solutions for which values are less than or equal to zero are
+ * acceptable as solutions for root-finding. So, if a function f(x) has
+ * a root at x = x0, then the root-finding result s must satisfy f(s) &lt;= 0.
+ */
+ BELOW_SIDE,
+
+ /** Only solutions for which values are greater than or equal to zero are
+ * acceptable as solutions for root-finding. So, if a function f(x) has
+ * a root at x = x0, then the root-finding result s must satisfy f(s) &gt;= 0.
+ */
+ ABOVE_SIDE;
+
+}
diff --git a/src/main/java/org/apache/commons/math3/analysis/solvers/BaseAbstractUnivariateSolver.java b/src/main/java/org/apache/commons/math3/analysis/solvers/BaseAbstractUnivariateSolver.java
new file mode 100644
index 0000000..12b30c6
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/analysis/solvers/BaseAbstractUnivariateSolver.java
@@ -0,0 +1,318 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math3.analysis.solvers;
+
+import org.apache.commons.math3.analysis.UnivariateFunction;
+import org.apache.commons.math3.exception.MaxCountExceededException;
+import org.apache.commons.math3.exception.NoBracketingException;
+import org.apache.commons.math3.exception.TooManyEvaluationsException;
+import org.apache.commons.math3.exception.NumberIsTooLargeException;
+import org.apache.commons.math3.exception.NullArgumentException;
+import org.apache.commons.math3.util.IntegerSequence;
+import org.apache.commons.math3.util.MathUtils;
+
+/**
+ * Provide a default implementation for several functions useful to generic
+ * solvers.
+ * The default values for relative and function tolerances are 1e-14
+ * and 1e-15, respectively. It is however highly recommended to not
+ * rely on the default, but rather carefully consider values that match
+ * user's expectations, as well as the specifics of each implementation.
+ *
+ * @param <FUNC> Type of function to solve.
+ *
+ * @since 2.0
+ */
+public abstract class BaseAbstractUnivariateSolver<FUNC extends UnivariateFunction>
+ implements BaseUnivariateSolver<FUNC> {
+ /** Default relative accuracy. */
+ private static final double DEFAULT_RELATIVE_ACCURACY = 1e-14;
+ /** Default function value accuracy. */
+ private static final double DEFAULT_FUNCTION_VALUE_ACCURACY = 1e-15;
+ /** Function value accuracy. */
+ private final double functionValueAccuracy;
+ /** Absolute accuracy. */
+ private final double absoluteAccuracy;
+ /** Relative accuracy. */
+ private final double relativeAccuracy;
+ /** Evaluations counter. */
+ private IntegerSequence.Incrementor evaluations;
+ /** Lower end of search interval. */
+ private double searchMin;
+ /** Higher end of search interval. */
+ private double searchMax;
+ /** Initial guess. */
+ private double searchStart;
+ /** Function to solve. */
+ private FUNC function;
+
+ /**
+ * Construct a solver with given absolute accuracy.
+ *
+ * @param absoluteAccuracy Maximum absolute error.
+ */
+ protected BaseAbstractUnivariateSolver(final double absoluteAccuracy) {
+ this(DEFAULT_RELATIVE_ACCURACY,
+ absoluteAccuracy,
+ DEFAULT_FUNCTION_VALUE_ACCURACY);
+ }
+
+ /**
+ * Construct a solver with given accuracies.
+ *
+ * @param relativeAccuracy Maximum relative error.
+ * @param absoluteAccuracy Maximum absolute error.
+ */
+ protected BaseAbstractUnivariateSolver(final double relativeAccuracy,
+ final double absoluteAccuracy) {
+ this(relativeAccuracy,
+ absoluteAccuracy,
+ DEFAULT_FUNCTION_VALUE_ACCURACY);
+ }
+
+ /**
+ * Construct a solver with given accuracies.
+ *
+ * @param relativeAccuracy Maximum relative error.
+ * @param absoluteAccuracy Maximum absolute error.
+ * @param functionValueAccuracy Maximum function value error.
+ */
+ protected BaseAbstractUnivariateSolver(final double relativeAccuracy,
+ final double absoluteAccuracy,
+ final double functionValueAccuracy) {
+ this.absoluteAccuracy = absoluteAccuracy;
+ this.relativeAccuracy = relativeAccuracy;
+ this.functionValueAccuracy = functionValueAccuracy;
+ this.evaluations = IntegerSequence.Incrementor.create();
+ }
+
+ /** {@inheritDoc} */
+ public int getMaxEvaluations() {
+ return evaluations.getMaximalCount();
+ }
+ /** {@inheritDoc} */
+ public int getEvaluations() {
+ return evaluations.getCount();
+ }
+ /**
+ * @return the lower end of the search interval.
+ */
+ public double getMin() {
+ return searchMin;
+ }
+ /**
+ * @return the higher end of the search interval.
+ */
+ public double getMax() {
+ return searchMax;
+ }
+ /**
+ * @return the initial guess.
+ */
+ public double getStartValue() {
+ return searchStart;
+ }
+ /**
+ * {@inheritDoc}
+ */
+ public double getAbsoluteAccuracy() {
+ return absoluteAccuracy;
+ }
+ /**
+ * {@inheritDoc}
+ */
+ public double getRelativeAccuracy() {
+ return relativeAccuracy;
+ }
+ /**
+ * {@inheritDoc}
+ */
+ public double getFunctionValueAccuracy() {
+ return functionValueAccuracy;
+ }
+
+ /**
+ * Compute the objective function value.
+ *
+ * @param point Point at which the objective function must be evaluated.
+ * @return the objective function value at specified point.
+ * @throws TooManyEvaluationsException if the maximal number of evaluations
+ * is exceeded.
+ */
+ protected double computeObjectiveValue(double point)
+ throws TooManyEvaluationsException {
+ incrementEvaluationCount();
+ return function.value(point);
+ }
+
+ /**
+ * Prepare for computation.
+ * Subclasses must call this method if they override any of the
+ * {@code solve} methods.
+ *
+ * @param f Function to solve.
+ * @param min Lower bound for the interval.
+ * @param max Upper bound for the interval.
+ * @param startValue Start value to use.
+ * @param maxEval Maximum number of evaluations.
+ * @exception NullArgumentException if f is null
+ */
+ protected void setup(int maxEval,
+ FUNC f,
+ double min, double max,
+ double startValue)
+ throws NullArgumentException {
+ // Checks.
+ MathUtils.checkNotNull(f);
+
+ // Reset.
+ searchMin = min;
+ searchMax = max;
+ searchStart = startValue;
+ function = f;
+ evaluations = evaluations.withMaximalCount(maxEval).withStart(0);
+ }
+
+ /** {@inheritDoc} */
+ public double solve(int maxEval, FUNC f, double min, double max, double startValue)
+ throws TooManyEvaluationsException,
+ NoBracketingException {
+ // Initialization.
+ setup(maxEval, f, min, max, startValue);
+
+ // Perform computation.
+ return doSolve();
+ }
+
+ /** {@inheritDoc} */
+ public double solve(int maxEval, FUNC f, double min, double max) {
+ return solve(maxEval, f, min, max, min + 0.5 * (max - min));
+ }
+
+ /** {@inheritDoc} */
+ public double solve(int maxEval, FUNC f, double startValue)
+ throws TooManyEvaluationsException,
+ NoBracketingException {
+ return solve(maxEval, f, Double.NaN, Double.NaN, startValue);
+ }
+
+ /**
+ * Method for implementing actual optimization algorithms in derived
+ * classes.
+ *
+ * @return the root.
+ * @throws TooManyEvaluationsException if the maximal number of evaluations
+ * is exceeded.
+ * @throws NoBracketingException if the initial search interval does not bracket
+ * a root and the solver requires it.
+ */
+ protected abstract double doSolve()
+ throws TooManyEvaluationsException, NoBracketingException;
+
+ /**
+ * Check whether the function takes opposite signs at the endpoints.
+ *
+ * @param lower Lower endpoint.
+ * @param upper Upper endpoint.
+ * @return {@code true} if the function values have opposite signs at the
+ * given points.
+ */
+ protected boolean isBracketing(final double lower,
+ final double upper) {
+ return UnivariateSolverUtils.isBracketing(function, lower, upper);
+ }
+
+ /**
+ * Check whether the arguments form a (strictly) increasing sequence.
+ *
+ * @param start First number.
+ * @param mid Second number.
+ * @param end Third number.
+ * @return {@code true} if the arguments form an increasing sequence.
+ */
+ protected boolean isSequence(final double start,
+ final double mid,
+ final double end) {
+ return UnivariateSolverUtils.isSequence(start, mid, end);
+ }
+
+ /**
+ * Check that the endpoints specify an interval.
+ *
+ * @param lower Lower endpoint.
+ * @param upper Upper endpoint.
+ * @throws NumberIsTooLargeException if {@code lower >= upper}.
+ */
+ protected void verifyInterval(final double lower,
+ final double upper)
+ throws NumberIsTooLargeException {
+ UnivariateSolverUtils.verifyInterval(lower, upper);
+ }
+
+ /**
+ * Check that {@code lower < initial < upper}.
+ *
+ * @param lower Lower endpoint.
+ * @param initial Initial value.
+ * @param upper Upper endpoint.
+ * @throws NumberIsTooLargeException if {@code lower >= initial} or
+ * {@code initial >= upper}.
+ */
+ protected void verifySequence(final double lower,
+ final double initial,
+ final double upper)
+ throws NumberIsTooLargeException {
+ UnivariateSolverUtils.verifySequence(lower, initial, upper);
+ }
+
+ /**
+ * Check that the endpoints specify an interval and the function takes
+ * opposite signs at the endpoints.
+ *
+ * @param lower Lower endpoint.
+ * @param upper Upper endpoint.
+ * @throws NullArgumentException if the function has not been set.
+ * @throws NoBracketingException if the function has the same sign at
+ * the endpoints.
+ */
+ protected void verifyBracketing(final double lower,
+ final double upper)
+ throws NullArgumentException,
+ NoBracketingException {
+ UnivariateSolverUtils.verifyBracketing(function, lower, upper);
+ }
+
+ /**
+ * Increment the evaluation count by one.
+ * Method {@link #computeObjectiveValue(double)} calls this method internally.
+ * It is provided for subclasses that do not exclusively use
+ * {@code computeObjectiveValue} to solve the function.
+ * See e.g. {@link AbstractUnivariateDifferentiableSolver}.
+ *
+ * @throws TooManyEvaluationsException when the allowed number of function
+ * evaluations has been exhausted.
+ */
+ protected void incrementEvaluationCount()
+ throws TooManyEvaluationsException {
+ try {
+ evaluations.increment();
+ } catch (MaxCountExceededException e) {
+ throw new TooManyEvaluationsException(e.getMax());
+ }
+ }
+}
diff --git a/src/main/java/org/apache/commons/math3/analysis/solvers/BaseSecantSolver.java b/src/main/java/org/apache/commons/math3/analysis/solvers/BaseSecantSolver.java
new file mode 100644
index 0000000..44a2173
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/analysis/solvers/BaseSecantSolver.java
@@ -0,0 +1,278 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math3.analysis.solvers;
+
+import org.apache.commons.math3.util.FastMath;
+import org.apache.commons.math3.analysis.UnivariateFunction;
+import org.apache.commons.math3.exception.ConvergenceException;
+import org.apache.commons.math3.exception.MathInternalError;
+
+/**
+ * Base class for all bracketing <em>Secant</em>-based methods for root-finding
+ * (approximating a zero of a univariate real function).
+ *
+ * <p>Implementation of the {@link RegulaFalsiSolver <em>Regula Falsi</em>} and
+ * {@link IllinoisSolver <em>Illinois</em>} methods is based on the
+ * following article: M. Dowell and P. Jarratt,
+ * <em>A modified regula falsi method for computing the root of an
+ * equation</em>, BIT Numerical Mathematics, volume 11, number 2,
+ * pages 168-174, Springer, 1971.</p>
+ *
+ * <p>Implementation of the {@link PegasusSolver <em>Pegasus</em>} method is
+ * based on the following article: M. Dowell and P. Jarratt,
+ * <em>The "Pegasus" method for computing the root of an equation</em>,
+ * BIT Numerical Mathematics, volume 12, number 4, pages 503-508, Springer,
+ * 1972.</p>
+ *
+ * <p>The {@link SecantSolver <em>Secant</em>} method is <em>not</em> a
+ * bracketing method, so it is not implemented here. It has a separate
+ * implementation.</p>
+ *
+ * @since 3.0
+ */
+public abstract class BaseSecantSolver
+ extends AbstractUnivariateSolver
+ implements BracketedUnivariateSolver<UnivariateFunction> {
+
+ /** Default absolute accuracy. */
+ protected static final double DEFAULT_ABSOLUTE_ACCURACY = 1e-6;
+
+ /** The kinds of solutions that the algorithm may accept. */
+ private AllowedSolution allowed;
+
+ /** The <em>Secant</em>-based root-finding method to use. */
+ private final Method method;
+
+ /**
+ * Construct a solver.
+ *
+ * @param absoluteAccuracy Absolute accuracy.
+ * @param method <em>Secant</em>-based root-finding method to use.
+ */
+ protected BaseSecantSolver(final double absoluteAccuracy, final Method method) {
+ super(absoluteAccuracy);
+ this.allowed = AllowedSolution.ANY_SIDE;
+ this.method = method;
+ }
+
+ /**
+ * Construct a solver.
+ *
+ * @param relativeAccuracy Relative accuracy.
+ * @param absoluteAccuracy Absolute accuracy.
+ * @param method <em>Secant</em>-based root-finding method to use.
+ */
+ protected BaseSecantSolver(final double relativeAccuracy,
+ final double absoluteAccuracy,
+ final Method method) {
+ super(relativeAccuracy, absoluteAccuracy);
+ this.allowed = AllowedSolution.ANY_SIDE;
+ this.method = method;
+ }
+
+ /**
+ * Construct a solver.
+ *
+ * @param relativeAccuracy Maximum relative error.
+ * @param absoluteAccuracy Maximum absolute error.
+ * @param functionValueAccuracy Maximum function value error.
+ * @param method <em>Secant</em>-based root-finding method to use
+ */
+ protected BaseSecantSolver(final double relativeAccuracy,
+ final double absoluteAccuracy,
+ final double functionValueAccuracy,
+ final Method method) {
+ super(relativeAccuracy, absoluteAccuracy, functionValueAccuracy);
+ this.allowed = AllowedSolution.ANY_SIDE;
+ this.method = method;
+ }
+
+ /** {@inheritDoc} */
+ public double solve(final int maxEval, final UnivariateFunction f,
+ final double min, final double max,
+ final AllowedSolution allowedSolution) {
+ return solve(maxEval, f, min, max, min + 0.5 * (max - min), allowedSolution);
+ }
+
+ /** {@inheritDoc} */
+ public double solve(final int maxEval, final UnivariateFunction f,
+ final double min, final double max, final double startValue,
+ final AllowedSolution allowedSolution) {
+ this.allowed = allowedSolution;
+ return super.solve(maxEval, f, min, max, startValue);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public double solve(final int maxEval, final UnivariateFunction f,
+ final double min, final double max, final double startValue) {
+ return solve(maxEval, f, min, max, startValue, AllowedSolution.ANY_SIDE);
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @throws ConvergenceException if the algorithm failed due to finite
+ * precision.
+ */
+ @Override
+ protected final double doSolve()
+ throws ConvergenceException {
+ // Get initial solution
+ double x0 = getMin();
+ double x1 = getMax();
+ double f0 = computeObjectiveValue(x0);
+ double f1 = computeObjectiveValue(x1);
+
+ // If one of the bounds is the exact root, return it. Since these are
+ // not under-approximations or over-approximations, we can return them
+ // regardless of the allowed solutions.
+ if (f0 == 0.0) {
+ return x0;
+ }
+ if (f1 == 0.0) {
+ return x1;
+ }
+
+ // Verify bracketing of initial solution.
+ verifyBracketing(x0, x1);
+
+ // Get accuracies.
+ final double ftol = getFunctionValueAccuracy();
+ final double atol = getAbsoluteAccuracy();
+ final double rtol = getRelativeAccuracy();
+
+ // Keep track of inverted intervals, meaning that the left bound is
+ // larger than the right bound.
+ boolean inverted = false;
+
+ // Keep finding better approximations.
+ while (true) {
+ // Calculate the next approximation.
+ final double x = x1 - ((f1 * (x1 - x0)) / (f1 - f0));
+ final double fx = computeObjectiveValue(x);
+
+ // If the new approximation is the exact root, return it. Since
+ // this is not an under-approximation or an over-approximation,
+ // we can return it regardless of the allowed solutions.
+ if (fx == 0.0) {
+ return x;
+ }
+
+ // Update the bounds with the new approximation.
+ if (f1 * fx < 0) {
+ // The value of x1 has switched to the other bound, thus inverting
+ // the interval.
+ x0 = x1;
+ f0 = f1;
+ inverted = !inverted;
+ } else {
+ switch (method) {
+ case ILLINOIS:
+ f0 *= 0.5;
+ break;
+ case PEGASUS:
+ f0 *= f1 / (f1 + fx);
+ break;
+ case REGULA_FALSI:
+ // Detect early that algorithm is stuck, instead of waiting
+ // for the maximum number of iterations to be exceeded.
+ if (x == x1) {
+ throw new ConvergenceException();
+ }
+ break;
+ default:
+ // Should never happen.
+ throw new MathInternalError();
+ }
+ }
+ // Update from [x0, x1] to [x0, x].
+ x1 = x;
+ f1 = fx;
+
+ // If the function value of the last approximation is too small,
+ // given the function value accuracy, then we can't get closer to
+ // the root than we already are.
+ if (FastMath.abs(f1) <= ftol) {
+ switch (allowed) {
+ case ANY_SIDE:
+ return x1;
+ case LEFT_SIDE:
+ if (inverted) {
+ return x1;
+ }
+ break;
+ case RIGHT_SIDE:
+ if (!inverted) {
+ return x1;
+ }
+ break;
+ case BELOW_SIDE:
+ if (f1 <= 0) {
+ return x1;
+ }
+ break;
+ case ABOVE_SIDE:
+ if (f1 >= 0) {
+ return x1;
+ }
+ break;
+ default:
+ throw new MathInternalError();
+ }
+ }
+
+ // If the current interval is within the given accuracies, we
+ // are satisfied with the current approximation.
+ if (FastMath.abs(x1 - x0) < FastMath.max(rtol * FastMath.abs(x1),
+ atol)) {
+ switch (allowed) {
+ case ANY_SIDE:
+ return x1;
+ case LEFT_SIDE:
+ return inverted ? x1 : x0;
+ case RIGHT_SIDE:
+ return inverted ? x0 : x1;
+ case BELOW_SIDE:
+ return (f1 <= 0) ? x1 : x0;
+ case ABOVE_SIDE:
+ return (f1 >= 0) ? x1 : x0;
+ default:
+ throw new MathInternalError();
+ }
+ }
+ }
+ }
+
+ /** <em>Secant</em>-based root-finding methods. */
+ protected enum Method {
+
+ /**
+ * The {@link RegulaFalsiSolver <em>Regula Falsi</em>} or
+ * <em>False Position</em> method.
+ */
+ REGULA_FALSI,
+
+ /** The {@link IllinoisSolver <em>Illinois</em>} method. */
+ ILLINOIS,
+
+ /** The {@link PegasusSolver <em>Pegasus</em>} method. */
+ PEGASUS;
+
+ }
+}
diff --git a/src/main/java/org/apache/commons/math3/analysis/solvers/BaseUnivariateSolver.java b/src/main/java/org/apache/commons/math3/analysis/solvers/BaseUnivariateSolver.java
new file mode 100644
index 0000000..f00590e
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/analysis/solvers/BaseUnivariateSolver.java
@@ -0,0 +1,142 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math3.analysis.solvers;
+
+import org.apache.commons.math3.analysis.UnivariateFunction;
+import org.apache.commons.math3.exception.MathIllegalArgumentException;
+import org.apache.commons.math3.exception.TooManyEvaluationsException;
+
+
+/**
+ * Interface for (univariate real) rootfinding algorithms.
+ * Implementations will search for only one zero in the given interval.
+ *
+ * This class is not intended for use outside of the Apache Commons Math
+ * library, regular user should rely on more specific interfaces like
+ * {@link UnivariateSolver}, {@link PolynomialSolver} or {@link
+ * DifferentiableUnivariateSolver}.
+ * @param <FUNC> Type of function to solve.
+ *
+ * @since 3.0
+ * @see UnivariateSolver
+ * @see PolynomialSolver
+ * @see DifferentiableUnivariateSolver
+ */
+public interface BaseUnivariateSolver<FUNC extends UnivariateFunction> {
+ /**
+ * Get the maximum number of function evaluations.
+ *
+ * @return the maximum number of function evaluations.
+ */
+ int getMaxEvaluations();
+
+ /**
+ * Get the number of evaluations of the objective function.
+ * The number of evaluations corresponds to the last call to the
+ * {@code optimize} method. It is 0 if the method has not been
+ * called yet.
+ *
+ * @return the number of evaluations of the objective function.
+ */
+ int getEvaluations();
+
+ /**
+ * Get the absolute accuracy of the solver. Solutions returned by the
+ * solver should be accurate to this tolerance, i.e., if &epsilon; is the
+ * absolute accuracy of the solver and {@code v} is a value returned by
+ * one of the {@code solve} methods, then a root of the function should
+ * exist somewhere in the interval ({@code v} - &epsilon;, {@code v} + &epsilon;).
+ *
+ * @return the absolute accuracy.
+ */
+ double getAbsoluteAccuracy();
+
+ /**
+ * Get the relative accuracy of the solver. The contract for relative
+ * accuracy is the same as {@link #getAbsoluteAccuracy()}, but using
+ * relative, rather than absolute error. If &rho; is the relative accuracy
+ * configured for a solver and {@code v} is a value returned, then a root
+ * of the function should exist somewhere in the interval
+ * ({@code v} - &rho; {@code v}, {@code v} + &rho; {@code v}).
+ *
+ * @return the relative accuracy.
+ */
+ double getRelativeAccuracy();
+
+ /**
+ * Get the function value accuracy of the solver. If {@code v} is
+ * a value returned by the solver for a function {@code f},
+ * then by contract, {@code |f(v)|} should be less than or equal to
+ * the function value accuracy configured for the solver.
+ *
+ * @return the function value accuracy.
+ */
+ double getFunctionValueAccuracy();
+
+ /**
+ * Solve for a zero root in the given interval.
+ * A solver may require that the interval brackets a single zero root.
+ * Solvers that do require bracketing should be able to handle the case
+ * where one of the endpoints is itself a root.
+ *
+ * @param maxEval Maximum number of evaluations.
+ * @param f Function to solve.
+ * @param min Lower bound for the interval.
+ * @param max Upper bound for the interval.
+ * @return a value where the function is zero.
+ * @throws MathIllegalArgumentException
+ * if the arguments do not satisfy the requirements specified by the solver.
+ * @throws TooManyEvaluationsException if
+ * the allowed number of evaluations is exceeded.
+ */
+ double solve(int maxEval, FUNC f, double min, double max)
+ throws MathIllegalArgumentException, TooManyEvaluationsException;
+
+ /**
+ * Solve for a zero in the given interval, start at {@code startValue}.
+ * A solver may require that the interval brackets a single zero root.
+ * Solvers that do require bracketing should be able to handle the case
+ * where one of the endpoints is itself a root.
+ *
+ * @param maxEval Maximum number of evaluations.
+ * @param f Function to solve.
+ * @param min Lower bound for the interval.
+ * @param max Upper bound for the interval.
+ * @param startValue Start value to use.
+ * @return a value where the function is zero.
+ * @throws MathIllegalArgumentException
+ * if the arguments do not satisfy the requirements specified by the solver.
+ * @throws TooManyEvaluationsException if
+ * the allowed number of evaluations is exceeded.
+ */
+ double solve(int maxEval, FUNC f, double min, double max, double startValue)
+ throws MathIllegalArgumentException, TooManyEvaluationsException;
+
+ /**
+ * Solve for a zero in the vicinity of {@code startValue}.
+ *
+ * @param f Function to solve.
+ * @param startValue Start value to use.
+ * @return a value where the function is zero.
+ * @param maxEval Maximum number of evaluations.
+ * @throws org.apache.commons.math3.exception.MathIllegalArgumentException
+ * if the arguments do not satisfy the requirements specified by the solver.
+ * @throws org.apache.commons.math3.exception.TooManyEvaluationsException if
+ * the allowed number of evaluations is exceeded.
+ */
+ double solve(int maxEval, FUNC f, double startValue);
+}
diff --git a/src/main/java/org/apache/commons/math3/analysis/solvers/BisectionSolver.java b/src/main/java/org/apache/commons/math3/analysis/solvers/BisectionSolver.java
new file mode 100644
index 0000000..49f4057
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/analysis/solvers/BisectionSolver.java
@@ -0,0 +1,91 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math3.analysis.solvers;
+
+import org.apache.commons.math3.util.FastMath;
+import org.apache.commons.math3.exception.TooManyEvaluationsException;
+
+/**
+ * Implements the <a href="http://mathworld.wolfram.com/Bisection.html">
+ * bisection algorithm</a> for finding zeros of univariate real functions.
+ * <p>
+ * The function should be continuous but not necessarily smooth.</p>
+ *
+ */
+public class BisectionSolver extends AbstractUnivariateSolver {
+ /** Default absolute accuracy. */
+ private static final double DEFAULT_ABSOLUTE_ACCURACY = 1e-6;
+
+ /**
+ * Construct a solver with default accuracy (1e-6).
+ */
+ public BisectionSolver() {
+ this(DEFAULT_ABSOLUTE_ACCURACY);
+ }
+ /**
+ * Construct a solver.
+ *
+ * @param absoluteAccuracy Absolute accuracy.
+ */
+ public BisectionSolver(double absoluteAccuracy) {
+ super(absoluteAccuracy);
+ }
+ /**
+ * Construct a solver.
+ *
+ * @param relativeAccuracy Relative accuracy.
+ * @param absoluteAccuracy Absolute accuracy.
+ */
+ public BisectionSolver(double relativeAccuracy,
+ double absoluteAccuracy) {
+ super(relativeAccuracy, absoluteAccuracy);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ protected double doSolve()
+ throws TooManyEvaluationsException {
+ double min = getMin();
+ double max = getMax();
+ verifyInterval(min, max);
+ final double absoluteAccuracy = getAbsoluteAccuracy();
+ double m;
+ double fm;
+ double fmin;
+
+ while (true) {
+ m = UnivariateSolverUtils.midpoint(min, max);
+ fmin = computeObjectiveValue(min);
+ fm = computeObjectiveValue(m);
+
+ if (fm * fmin > 0) {
+ // max and m bracket the root.
+ min = m;
+ } else {
+ // min and m bracket the root.
+ max = m;
+ }
+
+ if (FastMath.abs(max - min) <= absoluteAccuracy) {
+ m = UnivariateSolverUtils.midpoint(min, max);
+ return m;
+ }
+ }
+ }
+}
diff --git a/src/main/java/org/apache/commons/math3/analysis/solvers/BracketedRealFieldUnivariateSolver.java b/src/main/java/org/apache/commons/math3/analysis/solvers/BracketedRealFieldUnivariateSolver.java
new file mode 100644
index 0000000..55562e5
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/analysis/solvers/BracketedRealFieldUnivariateSolver.java
@@ -0,0 +1,142 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math3.analysis.solvers;
+
+import org.apache.commons.math3.RealFieldElement;
+import org.apache.commons.math3.analysis.RealFieldUnivariateFunction;
+
+/** Interface for {@link UnivariateSolver (univariate real) root-finding
+ * algorithms} that maintain a bracketed solution. There are several advantages
+ * to having such root-finding algorithms:
+ * <ul>
+ * <li>The bracketed solution guarantees that the root is kept within the
+ * interval. As such, these algorithms generally also guarantee
+ * convergence.</li>
+ * <li>The bracketed solution means that we have the opportunity to only
+ * return roots that are greater than or equal to the actual root, or
+ * are less than or equal to the actual root. That is, we can control
+ * whether under-approximations and over-approximations are
+ * {@link AllowedSolution allowed solutions}. Other root-finding
+ * algorithms can usually only guarantee that the solution (the root that
+ * was found) is around the actual root.</li>
+ * </ul>
+ *
+ * <p>For backwards compatibility, all root-finding algorithms must have
+ * {@link AllowedSolution#ANY_SIDE ANY_SIDE} as default for the allowed
+ * solutions.</p>
+ *
+ * @see AllowedSolution
+ * @param <T> the type of the field elements
+ * @since 3.6
+ */
+public interface BracketedRealFieldUnivariateSolver<T extends RealFieldElement<T>> {
+
+ /**
+ * Get the maximum number of function evaluations.
+ *
+ * @return the maximum number of function evaluations.
+ */
+ int getMaxEvaluations();
+
+ /**
+ * Get the number of evaluations of the objective function.
+ * The number of evaluations corresponds to the last call to the
+ * {@code optimize} method. It is 0 if the method has not been
+ * called yet.
+ *
+ * @return the number of evaluations of the objective function.
+ */
+ int getEvaluations();
+
+ /**
+ * Get the absolute accuracy of the solver. Solutions returned by the
+ * solver should be accurate to this tolerance, i.e., if &epsilon; is the
+ * absolute accuracy of the solver and {@code v} is a value returned by
+ * one of the {@code solve} methods, then a root of the function should
+ * exist somewhere in the interval ({@code v} - &epsilon;, {@code v} + &epsilon;).
+ *
+ * @return the absolute accuracy.
+ */
+ T getAbsoluteAccuracy();
+
+ /**
+ * Get the relative accuracy of the solver. The contract for relative
+ * accuracy is the same as {@link #getAbsoluteAccuracy()}, but using
+ * relative, rather than absolute error. If &rho; is the relative accuracy
+ * configured for a solver and {@code v} is a value returned, then a root
+ * of the function should exist somewhere in the interval
+ * ({@code v} - &rho; {@code v}, {@code v} + &rho; {@code v}).
+ *
+ * @return the relative accuracy.
+ */
+ T getRelativeAccuracy();
+
+ /**
+ * Get the function value accuracy of the solver. If {@code v} is
+ * a value returned by the solver for a function {@code f},
+ * then by contract, {@code |f(v)|} should be less than or equal to
+ * the function value accuracy configured for the solver.
+ *
+ * @return the function value accuracy.
+ */
+ T getFunctionValueAccuracy();
+
+ /**
+ * Solve for a zero in the given interval.
+ * A solver may require that the interval brackets a single zero root.
+ * Solvers that do require bracketing should be able to handle the case
+ * where one of the endpoints is itself a root.
+ *
+ * @param maxEval Maximum number of evaluations.
+ * @param f Function to solve.
+ * @param min Lower bound for the interval.
+ * @param max Upper bound for the interval.
+ * @param allowedSolution The kind of solutions that the root-finding algorithm may
+ * accept as solutions.
+ * @return A value where the function is zero.
+ * @throws org.apache.commons.math3.exception.MathIllegalArgumentException
+ * if the arguments do not satisfy the requirements specified by the solver.
+ * @throws org.apache.commons.math3.exception.TooManyEvaluationsException if
+ * the allowed number of evaluations is exceeded.
+ */
+ T solve(int maxEval, RealFieldUnivariateFunction<T> f, T min, T max,
+ AllowedSolution allowedSolution);
+
+ /**
+ * Solve for a zero in the given interval, start at {@code startValue}.
+ * A solver may require that the interval brackets a single zero root.
+ * Solvers that do require bracketing should be able to handle the case
+ * where one of the endpoints is itself a root.
+ *
+ * @param maxEval Maximum number of evaluations.
+ * @param f Function to solve.
+ * @param min Lower bound for the interval.
+ * @param max Upper bound for the interval.
+ * @param startValue Start value to use.
+ * @param allowedSolution The kind of solutions that the root-finding algorithm may
+ * accept as solutions.
+ * @return A value where the function is zero.
+ * @throws org.apache.commons.math3.exception.MathIllegalArgumentException
+ * if the arguments do not satisfy the requirements specified by the solver.
+ * @throws org.apache.commons.math3.exception.TooManyEvaluationsException if
+ * the allowed number of evaluations is exceeded.
+ */
+ T solve(int maxEval, RealFieldUnivariateFunction<T> f, T min, T max, T startValue,
+ AllowedSolution allowedSolution);
+
+}
diff --git a/src/main/java/org/apache/commons/math3/analysis/solvers/BracketedUnivariateSolver.java b/src/main/java/org/apache/commons/math3/analysis/solvers/BracketedUnivariateSolver.java
new file mode 100644
index 0000000..789fc99
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/analysis/solvers/BracketedUnivariateSolver.java
@@ -0,0 +1,92 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math3.analysis.solvers;
+
+import org.apache.commons.math3.analysis.UnivariateFunction;
+
+/** Interface for {@link UnivariateSolver (univariate real) root-finding
+ * algorithms} that maintain a bracketed solution. There are several advantages
+ * to having such root-finding algorithms:
+ * <ul>
+ * <li>The bracketed solution guarantees that the root is kept within the
+ * interval. As such, these algorithms generally also guarantee
+ * convergence.</li>
+ * <li>The bracketed solution means that we have the opportunity to only
+ * return roots that are greater than or equal to the actual root, or
+ * are less than or equal to the actual root. That is, we can control
+ * whether under-approximations and over-approximations are
+ * {@link AllowedSolution allowed solutions}. Other root-finding
+ * algorithms can usually only guarantee that the solution (the root that
+ * was found) is around the actual root.</li>
+ * </ul>
+ *
+ * <p>For backwards compatibility, all root-finding algorithms must have
+ * {@link AllowedSolution#ANY_SIDE ANY_SIDE} as default for the allowed
+ * solutions.</p>
+ * @param <FUNC> Type of function to solve.
+ *
+ * @see AllowedSolution
+ * @since 3.0
+ */
+public interface BracketedUnivariateSolver<FUNC extends UnivariateFunction>
+ extends BaseUnivariateSolver<FUNC> {
+
+ /**
+ * Solve for a zero in the given interval.
+ * A solver may require that the interval brackets a single zero root.
+ * Solvers that do require bracketing should be able to handle the case
+ * where one of the endpoints is itself a root.
+ *
+ * @param maxEval Maximum number of evaluations.
+ * @param f Function to solve.
+ * @param min Lower bound for the interval.
+ * @param max Upper bound for the interval.
+ * @param allowedSolution The kind of solutions that the root-finding algorithm may
+ * accept as solutions.
+ * @return A value where the function is zero.
+ * @throws org.apache.commons.math3.exception.MathIllegalArgumentException
+ * if the arguments do not satisfy the requirements specified by the solver.
+ * @throws org.apache.commons.math3.exception.TooManyEvaluationsException if
+ * the allowed number of evaluations is exceeded.
+ */
+ double solve(int maxEval, FUNC f, double min, double max,
+ AllowedSolution allowedSolution);
+
+ /**
+ * Solve for a zero in the given interval, start at {@code startValue}.
+ * A solver may require that the interval brackets a single zero root.
+ * Solvers that do require bracketing should be able to handle the case
+ * where one of the endpoints is itself a root.
+ *
+ * @param maxEval Maximum number of evaluations.
+ * @param f Function to solve.
+ * @param min Lower bound for the interval.
+ * @param max Upper bound for the interval.
+ * @param startValue Start value to use.
+ * @param allowedSolution The kind of solutions that the root-finding algorithm may
+ * accept as solutions.
+ * @return A value where the function is zero.
+ * @throws org.apache.commons.math3.exception.MathIllegalArgumentException
+ * if the arguments do not satisfy the requirements specified by the solver.
+ * @throws org.apache.commons.math3.exception.TooManyEvaluationsException if
+ * the allowed number of evaluations is exceeded.
+ */
+ double solve(int maxEval, FUNC f, double min, double max, double startValue,
+ AllowedSolution allowedSolution);
+
+}
diff --git a/src/main/java/org/apache/commons/math3/analysis/solvers/BracketingNthOrderBrentSolver.java b/src/main/java/org/apache/commons/math3/analysis/solvers/BracketingNthOrderBrentSolver.java
new file mode 100644
index 0000000..956bf9e
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/analysis/solvers/BracketingNthOrderBrentSolver.java
@@ -0,0 +1,411 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math3.analysis.solvers;
+
+
+import org.apache.commons.math3.analysis.UnivariateFunction;
+import org.apache.commons.math3.exception.MathInternalError;
+import org.apache.commons.math3.exception.NoBracketingException;
+import org.apache.commons.math3.exception.NumberIsTooLargeException;
+import org.apache.commons.math3.exception.NumberIsTooSmallException;
+import org.apache.commons.math3.exception.TooManyEvaluationsException;
+import org.apache.commons.math3.util.FastMath;
+import org.apache.commons.math3.util.Precision;
+
+/**
+ * This class implements a modification of the <a
+ * href="http://mathworld.wolfram.com/BrentsMethod.html"> Brent algorithm</a>.
+ * <p>
+ * The changes with respect to the original Brent algorithm are:
+ * <ul>
+ * <li>the returned value is chosen in the current interval according
+ * to user specified {@link AllowedSolution},</li>
+ * <li>the maximal order for the invert polynomial root search is
+ * user-specified instead of being invert quadratic only</li>
+ * </ul><p>
+ * The given interval must bracket the root.</p>
+ *
+ */
+public class BracketingNthOrderBrentSolver
+ extends AbstractUnivariateSolver
+ implements BracketedUnivariateSolver<UnivariateFunction> {
+
+ /** Default absolute accuracy. */
+ private static final double DEFAULT_ABSOLUTE_ACCURACY = 1e-6;
+
+ /** Default maximal order. */
+ private static final int DEFAULT_MAXIMAL_ORDER = 5;
+
+ /** Maximal aging triggering an attempt to balance the bracketing interval. */
+ private static final int MAXIMAL_AGING = 2;
+
+ /** Reduction factor for attempts to balance the bracketing interval. */
+ private static final double REDUCTION_FACTOR = 1.0 / 16.0;
+
+ /** Maximal order. */
+ private final int maximalOrder;
+
+ /** The kinds of solutions that the algorithm may accept. */
+ private AllowedSolution allowed;
+
+ /**
+ * Construct a solver with default accuracy and maximal order (1e-6 and 5 respectively)
+ */
+ public BracketingNthOrderBrentSolver() {
+ this(DEFAULT_ABSOLUTE_ACCURACY, DEFAULT_MAXIMAL_ORDER);
+ }
+
+ /**
+ * Construct a solver.
+ *
+ * @param absoluteAccuracy Absolute accuracy.
+ * @param maximalOrder maximal order.
+ * @exception NumberIsTooSmallException if maximal order is lower than 2
+ */
+ public BracketingNthOrderBrentSolver(final double absoluteAccuracy,
+ final int maximalOrder)
+ throws NumberIsTooSmallException {
+ super(absoluteAccuracy);
+ if (maximalOrder < 2) {
+ throw new NumberIsTooSmallException(maximalOrder, 2, true);
+ }
+ this.maximalOrder = maximalOrder;
+ this.allowed = AllowedSolution.ANY_SIDE;
+ }
+
+ /**
+ * Construct a solver.
+ *
+ * @param relativeAccuracy Relative accuracy.
+ * @param absoluteAccuracy Absolute accuracy.
+ * @param maximalOrder maximal order.
+ * @exception NumberIsTooSmallException if maximal order is lower than 2
+ */
+ public BracketingNthOrderBrentSolver(final double relativeAccuracy,
+ final double absoluteAccuracy,
+ final int maximalOrder)
+ throws NumberIsTooSmallException {
+ super(relativeAccuracy, absoluteAccuracy);
+ if (maximalOrder < 2) {
+ throw new NumberIsTooSmallException(maximalOrder, 2, true);
+ }
+ this.maximalOrder = maximalOrder;
+ this.allowed = AllowedSolution.ANY_SIDE;
+ }
+
+ /**
+ * Construct a solver.
+ *
+ * @param relativeAccuracy Relative accuracy.
+ * @param absoluteAccuracy Absolute accuracy.
+ * @param functionValueAccuracy Function value accuracy.
+ * @param maximalOrder maximal order.
+ * @exception NumberIsTooSmallException if maximal order is lower than 2
+ */
+ public BracketingNthOrderBrentSolver(final double relativeAccuracy,
+ final double absoluteAccuracy,
+ final double functionValueAccuracy,
+ final int maximalOrder)
+ throws NumberIsTooSmallException {
+ super(relativeAccuracy, absoluteAccuracy, functionValueAccuracy);
+ if (maximalOrder < 2) {
+ throw new NumberIsTooSmallException(maximalOrder, 2, true);
+ }
+ this.maximalOrder = maximalOrder;
+ this.allowed = AllowedSolution.ANY_SIDE;
+ }
+
+ /** Get the maximal order.
+ * @return maximal order
+ */
+ public int getMaximalOrder() {
+ return maximalOrder;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ protected double doSolve()
+ throws TooManyEvaluationsException,
+ NumberIsTooLargeException,
+ NoBracketingException {
+ // prepare arrays with the first points
+ final double[] x = new double[maximalOrder + 1];
+ final double[] y = new double[maximalOrder + 1];
+ x[0] = getMin();
+ x[1] = getStartValue();
+ x[2] = getMax();
+ verifySequence(x[0], x[1], x[2]);
+
+ // evaluate initial guess
+ y[1] = computeObjectiveValue(x[1]);
+ if (Precision.equals(y[1], 0.0, 1)) {
+ // return the initial guess if it is a perfect root.
+ return x[1];
+ }
+
+ // evaluate first endpoint
+ y[0] = computeObjectiveValue(x[0]);
+ if (Precision.equals(y[0], 0.0, 1)) {
+ // return the first endpoint if it is a perfect root.
+ return x[0];
+ }
+
+ int nbPoints;
+ int signChangeIndex;
+ if (y[0] * y[1] < 0) {
+
+ // reduce interval if it brackets the root
+ nbPoints = 2;
+ signChangeIndex = 1;
+
+ } else {
+
+ // evaluate second endpoint
+ y[2] = computeObjectiveValue(x[2]);
+ if (Precision.equals(y[2], 0.0, 1)) {
+ // return the second endpoint if it is a perfect root.
+ return x[2];
+ }
+
+ if (y[1] * y[2] < 0) {
+ // use all computed point as a start sampling array for solving
+ nbPoints = 3;
+ signChangeIndex = 2;
+ } else {
+ throw new NoBracketingException(x[0], x[2], y[0], y[2]);
+ }
+
+ }
+
+ // prepare a work array for inverse polynomial interpolation
+ final double[] tmpX = new double[x.length];
+
+ // current tightest bracketing of the root
+ double xA = x[signChangeIndex - 1];
+ double yA = y[signChangeIndex - 1];
+ double absYA = FastMath.abs(yA);
+ int agingA = 0;
+ double xB = x[signChangeIndex];
+ double yB = y[signChangeIndex];
+ double absYB = FastMath.abs(yB);
+ int agingB = 0;
+
+ // search loop
+ while (true) {
+
+ // check convergence of bracketing interval
+ final double xTol = getAbsoluteAccuracy() +
+ getRelativeAccuracy() * FastMath.max(FastMath.abs(xA), FastMath.abs(xB));
+ if (((xB - xA) <= xTol) || (FastMath.max(absYA, absYB) < getFunctionValueAccuracy())) {
+ switch (allowed) {
+ case ANY_SIDE :
+ return absYA < absYB ? xA : xB;
+ case LEFT_SIDE :
+ return xA;
+ case RIGHT_SIDE :
+ return xB;
+ case BELOW_SIDE :
+ return (yA <= 0) ? xA : xB;
+ case ABOVE_SIDE :
+ return (yA < 0) ? xB : xA;
+ default :
+ // this should never happen
+ throw new MathInternalError();
+ }
+ }
+
+ // target for the next evaluation point
+ double targetY;
+ if (agingA >= MAXIMAL_AGING) {
+ // we keep updating the high bracket, try to compensate this
+ final int p = agingA - MAXIMAL_AGING;
+ final double weightA = (1 << p) - 1;
+ final double weightB = p + 1;
+ targetY = (weightA * yA - weightB * REDUCTION_FACTOR * yB) / (weightA + weightB);
+ } else if (agingB >= MAXIMAL_AGING) {
+ // we keep updating the low bracket, try to compensate this
+ final int p = agingB - MAXIMAL_AGING;
+ final double weightA = p + 1;
+ final double weightB = (1 << p) - 1;
+ targetY = (weightB * yB - weightA * REDUCTION_FACTOR * yA) / (weightA + weightB);
+ } else {
+ // bracketing is balanced, try to find the root itself
+ targetY = 0;
+ }
+
+ // make a few attempts to guess a root,
+ double nextX;
+ int start = 0;
+ int end = nbPoints;
+ do {
+
+ // guess a value for current target, using inverse polynomial interpolation
+ System.arraycopy(x, start, tmpX, start, end - start);
+ nextX = guessX(targetY, tmpX, y, start, end);
+
+ if (!((nextX > xA) && (nextX < xB))) {
+ // the guessed root is not strictly inside of the tightest bracketing interval
+
+ // the guessed root is either not strictly inside the interval or it
+ // is a NaN (which occurs when some sampling points share the same y)
+ // we try again with a lower interpolation order
+ if (signChangeIndex - start >= end - signChangeIndex) {
+ // we have more points before the sign change, drop the lowest point
+ ++start;
+ } else {
+ // we have more points after sign change, drop the highest point
+ --end;
+ }
+
+ // we need to do one more attempt
+ nextX = Double.NaN;
+
+ }
+
+ } while (Double.isNaN(nextX) && (end - start > 1));
+
+ if (Double.isNaN(nextX)) {
+ // fall back to bisection
+ nextX = xA + 0.5 * (xB - xA);
+ start = signChangeIndex - 1;
+ end = signChangeIndex;
+ }
+
+ // evaluate the function at the guessed root
+ final double nextY = computeObjectiveValue(nextX);
+ if (Precision.equals(nextY, 0.0, 1)) {
+ // we have found an exact root, since it is not an approximation
+ // we don't need to bother about the allowed solutions setting
+ return nextX;
+ }
+
+ if ((nbPoints > 2) && (end - start != nbPoints)) {
+
+ // we have been forced to ignore some points to keep bracketing,
+ // they are probably too far from the root, drop them from now on
+ nbPoints = end - start;
+ System.arraycopy(x, start, x, 0, nbPoints);
+ System.arraycopy(y, start, y, 0, nbPoints);
+ signChangeIndex -= start;
+
+ } else if (nbPoints == x.length) {
+
+ // we have to drop one point in order to insert the new one
+ nbPoints--;
+
+ // keep the tightest bracketing interval as centered as possible
+ if (signChangeIndex >= (x.length + 1) / 2) {
+ // we drop the lowest point, we have to shift the arrays and the index
+ System.arraycopy(x, 1, x, 0, nbPoints);
+ System.arraycopy(y, 1, y, 0, nbPoints);
+ --signChangeIndex;
+ }
+
+ }
+
+ // insert the last computed point
+ //(by construction, we know it lies inside the tightest bracketing interval)
+ System.arraycopy(x, signChangeIndex, x, signChangeIndex + 1, nbPoints - signChangeIndex);
+ x[signChangeIndex] = nextX;
+ System.arraycopy(y, signChangeIndex, y, signChangeIndex + 1, nbPoints - signChangeIndex);
+ y[signChangeIndex] = nextY;
+ ++nbPoints;
+
+ // update the bracketing interval
+ if (nextY * yA <= 0) {
+ // the sign change occurs before the inserted point
+ xB = nextX;
+ yB = nextY;
+ absYB = FastMath.abs(yB);
+ ++agingA;
+ agingB = 0;
+ } else {
+ // the sign change occurs after the inserted point
+ xA = nextX;
+ yA = nextY;
+ absYA = FastMath.abs(yA);
+ agingA = 0;
+ ++agingB;
+
+ // update the sign change index
+ signChangeIndex++;
+
+ }
+
+ }
+
+ }
+
+ /** Guess an x value by n<sup>th</sup> order inverse polynomial interpolation.
+ * <p>
+ * The x value is guessed by evaluating polynomial Q(y) at y = targetY, where Q
+ * is built such that for all considered points (x<sub>i</sub>, y<sub>i</sub>),
+ * Q(y<sub>i</sub>) = x<sub>i</sub>.
+ * </p>
+ * @param targetY target value for y
+ * @param x reference points abscissas for interpolation,
+ * note that this array <em>is</em> modified during computation
+ * @param y reference points ordinates for interpolation
+ * @param start start index of the points to consider (inclusive)
+ * @param end end index of the points to consider (exclusive)
+ * @return guessed root (will be a NaN if two points share the same y)
+ */
+ private double guessX(final double targetY, final double[] x, final double[] y,
+ final int start, final int end) {
+
+ // compute Q Newton coefficients by divided differences
+ for (int i = start; i < end - 1; ++i) {
+ final int delta = i + 1 - start;
+ for (int j = end - 1; j > i; --j) {
+ x[j] = (x[j] - x[j-1]) / (y[j] - y[j - delta]);
+ }
+ }
+
+ // evaluate Q(targetY)
+ double x0 = 0;
+ for (int j = end - 1; j >= start; --j) {
+ x0 = x[j] + x0 * (targetY - y[j]);
+ }
+
+ return x0;
+
+ }
+
+ /** {@inheritDoc} */
+ public double solve(int maxEval, UnivariateFunction f, double min,
+ double max, AllowedSolution allowedSolution)
+ throws TooManyEvaluationsException,
+ NumberIsTooLargeException,
+ NoBracketingException {
+ this.allowed = allowedSolution;
+ return super.solve(maxEval, f, min, max);
+ }
+
+ /** {@inheritDoc} */
+ public double solve(int maxEval, UnivariateFunction f, double min,
+ double max, double startValue,
+ AllowedSolution allowedSolution)
+ throws TooManyEvaluationsException,
+ NumberIsTooLargeException,
+ NoBracketingException {
+ this.allowed = allowedSolution;
+ return super.solve(maxEval, f, min, max, startValue);
+ }
+
+}
diff --git a/src/main/java/org/apache/commons/math3/analysis/solvers/BrentSolver.java b/src/main/java/org/apache/commons/math3/analysis/solvers/BrentSolver.java
new file mode 100644
index 0000000..cf69410
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/analysis/solvers/BrentSolver.java
@@ -0,0 +1,243 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math3.analysis.solvers;
+
+
+import org.apache.commons.math3.exception.NoBracketingException;
+import org.apache.commons.math3.exception.NumberIsTooLargeException;
+import org.apache.commons.math3.exception.TooManyEvaluationsException;
+import org.apache.commons.math3.util.FastMath;
+import org.apache.commons.math3.util.Precision;
+
+/**
+ * This class implements the <a href="http://mathworld.wolfram.com/BrentsMethod.html">
+ * Brent algorithm</a> for finding zeros of real univariate functions.
+ * The function should be continuous but not necessarily smooth.
+ * The {@code solve} method returns a zero {@code x} of the function {@code f}
+ * in the given interval {@code [a, b]} to within a tolerance
+ * {@code 2 eps abs(x) + t} where {@code eps} is the relative accuracy and
+ * {@code t} is the absolute accuracy.
+ * <p>The given interval must bracket the root.</p>
+ * <p>
+ * The reference implementation is given in chapter 4 of
+ * <blockquote>
+ * <b>Algorithms for Minimization Without Derivatives</b>,
+ * <em>Richard P. Brent</em>,
+ * Dover, 2002
+ * </blockquote>
+ *
+ * @see BaseAbstractUnivariateSolver
+ */
+public class BrentSolver extends AbstractUnivariateSolver {
+
+ /** Default absolute accuracy. */
+ private static final double DEFAULT_ABSOLUTE_ACCURACY = 1e-6;
+
+ /**
+ * Construct a solver with default absolute accuracy (1e-6).
+ */
+ public BrentSolver() {
+ this(DEFAULT_ABSOLUTE_ACCURACY);
+ }
+ /**
+ * Construct a solver.
+ *
+ * @param absoluteAccuracy Absolute accuracy.
+ */
+ public BrentSolver(double absoluteAccuracy) {
+ super(absoluteAccuracy);
+ }
+ /**
+ * Construct a solver.
+ *
+ * @param relativeAccuracy Relative accuracy.
+ * @param absoluteAccuracy Absolute accuracy.
+ */
+ public BrentSolver(double relativeAccuracy,
+ double absoluteAccuracy) {
+ super(relativeAccuracy, absoluteAccuracy);
+ }
+ /**
+ * Construct a solver.
+ *
+ * @param relativeAccuracy Relative accuracy.
+ * @param absoluteAccuracy Absolute accuracy.
+ * @param functionValueAccuracy Function value accuracy.
+ *
+ * @see BaseAbstractUnivariateSolver#BaseAbstractUnivariateSolver(double,double,double)
+ */
+ public BrentSolver(double relativeAccuracy,
+ double absoluteAccuracy,
+ double functionValueAccuracy) {
+ super(relativeAccuracy, absoluteAccuracy, functionValueAccuracy);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ protected double doSolve()
+ throws NoBracketingException,
+ TooManyEvaluationsException,
+ NumberIsTooLargeException {
+ double min = getMin();
+ double max = getMax();
+ final double initial = getStartValue();
+ final double functionValueAccuracy = getFunctionValueAccuracy();
+
+ verifySequence(min, initial, max);
+
+ // Return the initial guess if it is good enough.
+ double yInitial = computeObjectiveValue(initial);
+ if (FastMath.abs(yInitial) <= functionValueAccuracy) {
+ return initial;
+ }
+
+ // Return the first endpoint if it is good enough.
+ double yMin = computeObjectiveValue(min);
+ if (FastMath.abs(yMin) <= functionValueAccuracy) {
+ return min;
+ }
+
+ // Reduce interval if min and initial bracket the root.
+ if (yInitial * yMin < 0) {
+ return brent(min, initial, yMin, yInitial);
+ }
+
+ // Return the second endpoint if it is good enough.
+ double yMax = computeObjectiveValue(max);
+ if (FastMath.abs(yMax) <= functionValueAccuracy) {
+ return max;
+ }
+
+ // Reduce interval if initial and max bracket the root.
+ if (yInitial * yMax < 0) {
+ return brent(initial, max, yInitial, yMax);
+ }
+
+ throw new NoBracketingException(min, max, yMin, yMax);
+ }
+
+ /**
+ * Search for a zero inside the provided interval.
+ * This implementation is based on the algorithm described at page 58 of
+ * the book
+ * <blockquote>
+ * <b>Algorithms for Minimization Without Derivatives</b>,
+ * <it>Richard P. Brent</it>,
+ * Dover 0-486-41998-3
+ * </blockquote>
+ *
+ * @param lo Lower bound of the search interval.
+ * @param hi Higher bound of the search interval.
+ * @param fLo Function value at the lower bound of the search interval.
+ * @param fHi Function value at the higher bound of the search interval.
+ * @return the value where the function is zero.
+ */
+ private double brent(double lo, double hi,
+ double fLo, double fHi) {
+ double a = lo;
+ double fa = fLo;
+ double b = hi;
+ double fb = fHi;
+ double c = a;
+ double fc = fa;
+ double d = b - a;
+ double e = d;
+
+ final double t = getAbsoluteAccuracy();
+ final double eps = getRelativeAccuracy();
+
+ while (true) {
+ if (FastMath.abs(fc) < FastMath.abs(fb)) {
+ a = b;
+ b = c;
+ c = a;
+ fa = fb;
+ fb = fc;
+ fc = fa;
+ }
+
+ final double tol = 2 * eps * FastMath.abs(b) + t;
+ final double m = 0.5 * (c - b);
+
+ if (FastMath.abs(m) <= tol ||
+ Precision.equals(fb, 0)) {
+ return b;
+ }
+ if (FastMath.abs(e) < tol ||
+ FastMath.abs(fa) <= FastMath.abs(fb)) {
+ // Force bisection.
+ d = m;
+ e = d;
+ } else {
+ double s = fb / fa;
+ double p;
+ double q;
+ // The equality test (a == c) is intentional,
+ // it is part of the original Brent's method and
+ // it should NOT be replaced by proximity test.
+ if (a == c) {
+ // Linear interpolation.
+ p = 2 * m * s;
+ q = 1 - s;
+ } else {
+ // Inverse quadratic interpolation.
+ q = fa / fc;
+ final double r = fb / fc;
+ p = s * (2 * m * q * (q - r) - (b - a) * (r - 1));
+ q = (q - 1) * (r - 1) * (s - 1);
+ }
+ if (p > 0) {
+ q = -q;
+ } else {
+ p = -p;
+ }
+ s = e;
+ e = d;
+ if (p >= 1.5 * m * q - FastMath.abs(tol * q) ||
+ p >= FastMath.abs(0.5 * s * q)) {
+ // Inverse quadratic interpolation gives a value
+ // in the wrong direction, or progress is slow.
+ // Fall back to bisection.
+ d = m;
+ e = d;
+ } else {
+ d = p / q;
+ }
+ }
+ a = b;
+ fa = fb;
+
+ if (FastMath.abs(d) > tol) {
+ b += d;
+ } else if (m > 0) {
+ b += tol;
+ } else {
+ b -= tol;
+ }
+ fb = computeObjectiveValue(b);
+ if ((fb > 0 && fc > 0) ||
+ (fb <= 0 && fc <= 0)) {
+ c = a;
+ fc = fa;
+ d = b - a;
+ e = d;
+ }
+ }
+ }
+}
diff --git a/src/main/java/org/apache/commons/math3/analysis/solvers/DifferentiableUnivariateSolver.java b/src/main/java/org/apache/commons/math3/analysis/solvers/DifferentiableUnivariateSolver.java
new file mode 100644
index 0000000..b9ae158
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/analysis/solvers/DifferentiableUnivariateSolver.java
@@ -0,0 +1,30 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math3.analysis.solvers;
+
+import org.apache.commons.math3.analysis.DifferentiableUnivariateFunction;
+
+
+/**
+ * Interface for (univariate real) rootfinding algorithms.
+ * Implementations will search for only one zero in the given interval.
+ *
+ * @deprecated as of 3.1, replaced by {@link UnivariateDifferentiableSolver}
+ */
+@Deprecated
+public interface DifferentiableUnivariateSolver
+ extends BaseUnivariateSolver<DifferentiableUnivariateFunction> {}
diff --git a/src/main/java/org/apache/commons/math3/analysis/solvers/FieldBracketingNthOrderBrentSolver.java b/src/main/java/org/apache/commons/math3/analysis/solvers/FieldBracketingNthOrderBrentSolver.java
new file mode 100644
index 0000000..f0ca8b9
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/analysis/solvers/FieldBracketingNthOrderBrentSolver.java
@@ -0,0 +1,446 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math3.analysis.solvers;
+
+
+import org.apache.commons.math3.Field;
+import org.apache.commons.math3.RealFieldElement;
+import org.apache.commons.math3.analysis.RealFieldUnivariateFunction;
+import org.apache.commons.math3.exception.MathInternalError;
+import org.apache.commons.math3.exception.NoBracketingException;
+import org.apache.commons.math3.exception.NullArgumentException;
+import org.apache.commons.math3.exception.NumberIsTooSmallException;
+import org.apache.commons.math3.util.IntegerSequence;
+import org.apache.commons.math3.util.MathArrays;
+import org.apache.commons.math3.util.MathUtils;
+import org.apache.commons.math3.util.Precision;
+
+/**
+ * This class implements a modification of the <a
+ * href="http://mathworld.wolfram.com/BrentsMethod.html"> Brent algorithm</a>.
+ * <p>
+ * The changes with respect to the original Brent algorithm are:
+ * <ul>
+ * <li>the returned value is chosen in the current interval according
+ * to user specified {@link AllowedSolution}</li>
+ * <li>the maximal order for the invert polynomial root search is
+ * user-specified instead of being invert quadratic only</li>
+ * </ul><p>
+ * The given interval must bracket the root.</p>
+ *
+ * @param <T> the type of the field elements
+ * @since 3.6
+ */
+public class FieldBracketingNthOrderBrentSolver<T extends RealFieldElement<T>>
+ implements BracketedRealFieldUnivariateSolver<T> {
+
+ /** Maximal aging triggering an attempt to balance the bracketing interval. */
+ private static final int MAXIMAL_AGING = 2;
+
+ /** Field to which the elements belong. */
+ private final Field<T> field;
+
+ /** Maximal order. */
+ private final int maximalOrder;
+
+ /** Function value accuracy. */
+ private final T functionValueAccuracy;
+
+ /** Absolute accuracy. */
+ private final T absoluteAccuracy;
+
+ /** Relative accuracy. */
+ private final T relativeAccuracy;
+
+ /** Evaluations counter. */
+ private IntegerSequence.Incrementor evaluations;
+
+ /**
+ * Construct a solver.
+ *
+ * @param relativeAccuracy Relative accuracy.
+ * @param absoluteAccuracy Absolute accuracy.
+ * @param functionValueAccuracy Function value accuracy.
+ * @param maximalOrder maximal order.
+ * @exception NumberIsTooSmallException if maximal order is lower than 2
+ */
+ public FieldBracketingNthOrderBrentSolver(final T relativeAccuracy,
+ final T absoluteAccuracy,
+ final T functionValueAccuracy,
+ final int maximalOrder)
+ throws NumberIsTooSmallException {
+ if (maximalOrder < 2) {
+ throw new NumberIsTooSmallException(maximalOrder, 2, true);
+ }
+ this.field = relativeAccuracy.getField();
+ this.maximalOrder = maximalOrder;
+ this.absoluteAccuracy = absoluteAccuracy;
+ this.relativeAccuracy = relativeAccuracy;
+ this.functionValueAccuracy = functionValueAccuracy;
+ this.evaluations = IntegerSequence.Incrementor.create();
+ }
+
+ /** Get the maximal order.
+ * @return maximal order
+ */
+ public int getMaximalOrder() {
+ return maximalOrder;
+ }
+
+ /**
+ * Get the maximal number of function evaluations.
+ *
+ * @return the maximal number of function evaluations.
+ */
+ public int getMaxEvaluations() {
+ return evaluations.getMaximalCount();
+ }
+
+ /**
+ * Get the number of evaluations of the objective function.
+ * The number of evaluations corresponds to the last call to the
+ * {@code optimize} method. It is 0 if the method has not been
+ * called yet.
+ *
+ * @return the number of evaluations of the objective function.
+ */
+ public int getEvaluations() {
+ return evaluations.getCount();
+ }
+
+ /**
+ * Get the absolute accuracy.
+ * @return absolute accuracy
+ */
+ public T getAbsoluteAccuracy() {
+ return absoluteAccuracy;
+ }
+
+ /**
+ * Get the relative accuracy.
+ * @return relative accuracy
+ */
+ public T getRelativeAccuracy() {
+ return relativeAccuracy;
+ }
+
+ /**
+ * Get the function accuracy.
+ * @return function accuracy
+ */
+ public T getFunctionValueAccuracy() {
+ return functionValueAccuracy;
+ }
+
+ /**
+ * Solve for a zero in the given interval.
+ * A solver may require that the interval brackets a single zero root.
+ * Solvers that do require bracketing should be able to handle the case
+ * where one of the endpoints is itself a root.
+ *
+ * @param maxEval Maximum number of evaluations.
+ * @param f Function to solve.
+ * @param min Lower bound for the interval.
+ * @param max Upper bound for the interval.
+ * @param allowedSolution The kind of solutions that the root-finding algorithm may
+ * accept as solutions.
+ * @return a value where the function is zero.
+ * @exception NullArgumentException if f is null.
+ * @exception NoBracketingException if root cannot be bracketed
+ */
+ public T solve(final int maxEval, final RealFieldUnivariateFunction<T> f,
+ final T min, final T max, final AllowedSolution allowedSolution)
+ throws NullArgumentException, NoBracketingException {
+ return solve(maxEval, f, min, max, min.add(max).divide(2), allowedSolution);
+ }
+
+ /**
+ * Solve for a zero in the given interval, start at {@code startValue}.
+ * A solver may require that the interval brackets a single zero root.
+ * Solvers that do require bracketing should be able to handle the case
+ * where one of the endpoints is itself a root.
+ *
+ * @param maxEval Maximum number of evaluations.
+ * @param f Function to solve.
+ * @param min Lower bound for the interval.
+ * @param max Upper bound for the interval.
+ * @param startValue Start value to use.
+ * @param allowedSolution The kind of solutions that the root-finding algorithm may
+ * accept as solutions.
+ * @return a value where the function is zero.
+ * @exception NullArgumentException if f is null.
+ * @exception NoBracketingException if root cannot be bracketed
+ */
+ public T solve(final int maxEval, final RealFieldUnivariateFunction<T> f,
+ final T min, final T max, final T startValue,
+ final AllowedSolution allowedSolution)
+ throws NullArgumentException, NoBracketingException {
+
+ // Checks.
+ MathUtils.checkNotNull(f);
+
+ // Reset.
+ evaluations = evaluations.withMaximalCount(maxEval).withStart(0);
+ T zero = field.getZero();
+ T nan = zero.add(Double.NaN);
+
+ // prepare arrays with the first points
+ final T[] x = MathArrays.buildArray(field, maximalOrder + 1);
+ final T[] y = MathArrays.buildArray(field, maximalOrder + 1);
+ x[0] = min;
+ x[1] = startValue;
+ x[2] = max;
+
+ // evaluate initial guess
+ evaluations.increment();
+ y[1] = f.value(x[1]);
+ if (Precision.equals(y[1].getReal(), 0.0, 1)) {
+ // return the initial guess if it is a perfect root.
+ return x[1];
+ }
+
+ // evaluate first endpoint
+ evaluations.increment();
+ y[0] = f.value(x[0]);
+ if (Precision.equals(y[0].getReal(), 0.0, 1)) {
+ // return the first endpoint if it is a perfect root.
+ return x[0];
+ }
+
+ int nbPoints;
+ int signChangeIndex;
+ if (y[0].multiply(y[1]).getReal() < 0) {
+
+ // reduce interval if it brackets the root
+ nbPoints = 2;
+ signChangeIndex = 1;
+
+ } else {
+
+ // evaluate second endpoint
+ evaluations.increment();
+ y[2] = f.value(x[2]);
+ if (Precision.equals(y[2].getReal(), 0.0, 1)) {
+ // return the second endpoint if it is a perfect root.
+ return x[2];
+ }
+
+ if (y[1].multiply(y[2]).getReal() < 0) {
+ // use all computed point as a start sampling array for solving
+ nbPoints = 3;
+ signChangeIndex = 2;
+ } else {
+ throw new NoBracketingException(x[0].getReal(), x[2].getReal(),
+ y[0].getReal(), y[2].getReal());
+ }
+
+ }
+
+ // prepare a work array for inverse polynomial interpolation
+ final T[] tmpX = MathArrays.buildArray(field, x.length);
+
+ // current tightest bracketing of the root
+ T xA = x[signChangeIndex - 1];
+ T yA = y[signChangeIndex - 1];
+ T absXA = xA.abs();
+ T absYA = yA.abs();
+ int agingA = 0;
+ T xB = x[signChangeIndex];
+ T yB = y[signChangeIndex];
+ T absXB = xB.abs();
+ T absYB = yB.abs();
+ int agingB = 0;
+
+ // search loop
+ while (true) {
+
+ // check convergence of bracketing interval
+ T maxX = absXA.subtract(absXB).getReal() < 0 ? absXB : absXA;
+ T maxY = absYA.subtract(absYB).getReal() < 0 ? absYB : absYA;
+ final T xTol = absoluteAccuracy.add(relativeAccuracy.multiply(maxX));
+ if (xB.subtract(xA).subtract(xTol).getReal() <= 0 ||
+ maxY.subtract(functionValueAccuracy).getReal() < 0) {
+ switch (allowedSolution) {
+ case ANY_SIDE :
+ return absYA.subtract(absYB).getReal() < 0 ? xA : xB;
+ case LEFT_SIDE :
+ return xA;
+ case RIGHT_SIDE :
+ return xB;
+ case BELOW_SIDE :
+ return yA.getReal() <= 0 ? xA : xB;
+ case ABOVE_SIDE :
+ return yA.getReal() < 0 ? xB : xA;
+ default :
+ // this should never happen
+ throw new MathInternalError(null);
+ }
+ }
+
+ // target for the next evaluation point
+ T targetY;
+ if (agingA >= MAXIMAL_AGING) {
+ // we keep updating the high bracket, try to compensate this
+ targetY = yB.divide(16).negate();
+ } else if (agingB >= MAXIMAL_AGING) {
+ // we keep updating the low bracket, try to compensate this
+ targetY = yA.divide(16).negate();
+ } else {
+ // bracketing is balanced, try to find the root itself
+ targetY = zero;
+ }
+
+ // make a few attempts to guess a root,
+ T nextX;
+ int start = 0;
+ int end = nbPoints;
+ do {
+
+ // guess a value for current target, using inverse polynomial interpolation
+ System.arraycopy(x, start, tmpX, start, end - start);
+ nextX = guessX(targetY, tmpX, y, start, end);
+
+ if (!((nextX.subtract(xA).getReal() > 0) && (nextX.subtract(xB).getReal() < 0))) {
+ // the guessed root is not strictly inside of the tightest bracketing interval
+
+ // the guessed root is either not strictly inside the interval or it
+ // is a NaN (which occurs when some sampling points share the same y)
+ // we try again with a lower interpolation order
+ if (signChangeIndex - start >= end - signChangeIndex) {
+ // we have more points before the sign change, drop the lowest point
+ ++start;
+ } else {
+ // we have more points after sign change, drop the highest point
+ --end;
+ }
+
+ // we need to do one more attempt
+ nextX = nan;
+
+ }
+
+ } while (Double.isNaN(nextX.getReal()) && (end - start > 1));
+
+ if (Double.isNaN(nextX.getReal())) {
+ // fall back to bisection
+ nextX = xA.add(xB.subtract(xA).divide(2));
+ start = signChangeIndex - 1;
+ end = signChangeIndex;
+ }
+
+ // evaluate the function at the guessed root
+ evaluations.increment();
+ final T nextY = f.value(nextX);
+ if (Precision.equals(nextY.getReal(), 0.0, 1)) {
+ // we have found an exact root, since it is not an approximation
+ // we don't need to bother about the allowed solutions setting
+ return nextX;
+ }
+
+ if ((nbPoints > 2) && (end - start != nbPoints)) {
+
+ // we have been forced to ignore some points to keep bracketing,
+ // they are probably too far from the root, drop them from now on
+ nbPoints = end - start;
+ System.arraycopy(x, start, x, 0, nbPoints);
+ System.arraycopy(y, start, y, 0, nbPoints);
+ signChangeIndex -= start;
+
+ } else if (nbPoints == x.length) {
+
+ // we have to drop one point in order to insert the new one
+ nbPoints--;
+
+ // keep the tightest bracketing interval as centered as possible
+ if (signChangeIndex >= (x.length + 1) / 2) {
+ // we drop the lowest point, we have to shift the arrays and the index
+ System.arraycopy(x, 1, x, 0, nbPoints);
+ System.arraycopy(y, 1, y, 0, nbPoints);
+ --signChangeIndex;
+ }
+
+ }
+
+ // insert the last computed point
+ //(by construction, we know it lies inside the tightest bracketing interval)
+ System.arraycopy(x, signChangeIndex, x, signChangeIndex + 1, nbPoints - signChangeIndex);
+ x[signChangeIndex] = nextX;
+ System.arraycopy(y, signChangeIndex, y, signChangeIndex + 1, nbPoints - signChangeIndex);
+ y[signChangeIndex] = nextY;
+ ++nbPoints;
+
+ // update the bracketing interval
+ if (nextY.multiply(yA).getReal() <= 0) {
+ // the sign change occurs before the inserted point
+ xB = nextX;
+ yB = nextY;
+ absYB = yB.abs();
+ ++agingA;
+ agingB = 0;
+ } else {
+ // the sign change occurs after the inserted point
+ xA = nextX;
+ yA = nextY;
+ absYA = yA.abs();
+ agingA = 0;
+ ++agingB;
+
+ // update the sign change index
+ signChangeIndex++;
+
+ }
+
+ }
+
+ }
+
+ /** Guess an x value by n<sup>th</sup> order inverse polynomial interpolation.
+ * <p>
+ * The x value is guessed by evaluating polynomial Q(y) at y = targetY, where Q
+ * is built such that for all considered points (x<sub>i</sub>, y<sub>i</sub>),
+ * Q(y<sub>i</sub>) = x<sub>i</sub>.
+ * </p>
+ * @param targetY target value for y
+ * @param x reference points abscissas for interpolation,
+ * note that this array <em>is</em> modified during computation
+ * @param y reference points ordinates for interpolation
+ * @param start start index of the points to consider (inclusive)
+ * @param end end index of the points to consider (exclusive)
+ * @return guessed root (will be a NaN if two points share the same y)
+ */
+ private T guessX(final T targetY, final T[] x, final T[] y,
+ final int start, final int end) {
+
+ // compute Q Newton coefficients by divided differences
+ for (int i = start; i < end - 1; ++i) {
+ final int delta = i + 1 - start;
+ for (int j = end - 1; j > i; --j) {
+ x[j] = x[j].subtract(x[j-1]).divide(y[j].subtract(y[j - delta]));
+ }
+ }
+
+ // evaluate Q(targetY)
+ T x0 = field.getZero();
+ for (int j = end - 1; j >= start; --j) {
+ x0 = x[j].add(x0.multiply(targetY.subtract(y[j])));
+ }
+
+ return x0;
+
+ }
+
+}
diff --git a/src/main/java/org/apache/commons/math3/analysis/solvers/IllinoisSolver.java b/src/main/java/org/apache/commons/math3/analysis/solvers/IllinoisSolver.java
new file mode 100644
index 0000000..bd3bc71
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/analysis/solvers/IllinoisSolver.java
@@ -0,0 +1,82 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math3.analysis.solvers;
+
+
+/**
+ * Implements the <em>Illinois</em> method for root-finding (approximating
+ * a zero of a univariate real function). It is a modified
+ * {@link RegulaFalsiSolver <em>Regula Falsi</em>} method.
+ *
+ * <p>Like the <em>Regula Falsi</em> method, convergence is guaranteed by
+ * maintaining a bracketed solution. The <em>Illinois</em> method however,
+ * should converge much faster than the original <em>Regula Falsi</em>
+ * method. Furthermore, this implementation of the <em>Illinois</em> method
+ * should not suffer from the same implementation issues as the <em>Regula
+ * Falsi</em> method, which may fail to convergence in certain cases.</p>
+ *
+ * <p>The <em>Illinois</em> method assumes that the function is continuous,
+ * but not necessarily smooth.</p>
+ *
+ * <p>Implementation based on the following article: M. Dowell and P. Jarratt,
+ * <em>A modified regula falsi method for computing the root of an
+ * equation</em>, BIT Numerical Mathematics, volume 11, number 2,
+ * pages 168-174, Springer, 1971.</p>
+ *
+ * @since 3.0
+ */
+public class IllinoisSolver extends BaseSecantSolver {
+
+ /** Construct a solver with default accuracy (1e-6). */
+ public IllinoisSolver() {
+ super(DEFAULT_ABSOLUTE_ACCURACY, Method.ILLINOIS);
+ }
+
+ /**
+ * Construct a solver.
+ *
+ * @param absoluteAccuracy Absolute accuracy.
+ */
+ public IllinoisSolver(final double absoluteAccuracy) {
+ super(absoluteAccuracy, Method.ILLINOIS);
+ }
+
+ /**
+ * Construct a solver.
+ *
+ * @param relativeAccuracy Relative accuracy.
+ * @param absoluteAccuracy Absolute accuracy.
+ */
+ public IllinoisSolver(final double relativeAccuracy,
+ final double absoluteAccuracy) {
+ super(relativeAccuracy, absoluteAccuracy, Method.ILLINOIS);
+ }
+
+ /**
+ * Construct a solver.
+ *
+ * @param relativeAccuracy Relative accuracy.
+ * @param absoluteAccuracy Absolute accuracy.
+ * @param functionValueAccuracy Maximum function value error.
+ */
+ public IllinoisSolver(final double relativeAccuracy,
+ final double absoluteAccuracy,
+ final double functionValueAccuracy) {
+ super(relativeAccuracy, absoluteAccuracy, functionValueAccuracy, Method.PEGASUS);
+ }
+}
diff --git a/src/main/java/org/apache/commons/math3/analysis/solvers/LaguerreSolver.java b/src/main/java/org/apache/commons/math3/analysis/solvers/LaguerreSolver.java
new file mode 100644
index 0000000..5312dac
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/analysis/solvers/LaguerreSolver.java
@@ -0,0 +1,440 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math3.analysis.solvers;
+
+import org.apache.commons.math3.analysis.polynomials.PolynomialFunction;
+import org.apache.commons.math3.complex.Complex;
+import org.apache.commons.math3.complex.ComplexUtils;
+import org.apache.commons.math3.exception.NoBracketingException;
+import org.apache.commons.math3.exception.NoDataException;
+import org.apache.commons.math3.exception.NullArgumentException;
+import org.apache.commons.math3.exception.NumberIsTooLargeException;
+import org.apache.commons.math3.exception.TooManyEvaluationsException;
+import org.apache.commons.math3.exception.util.LocalizedFormats;
+import org.apache.commons.math3.util.FastMath;
+
+/**
+ * Implements the <a href="http://mathworld.wolfram.com/LaguerresMethod.html">
+ * Laguerre's Method</a> for root finding of real coefficient polynomials.
+ * For reference, see
+ * <blockquote>
+ * <b>A First Course in Numerical Analysis</b>,
+ * ISBN 048641454X, chapter 8.
+ * </blockquote>
+ * Laguerre's method is global in the sense that it can start with any initial
+ * approximation and be able to solve all roots from that point.
+ * The algorithm requires a bracketing condition.
+ *
+ * @since 1.2
+ */
+public class LaguerreSolver extends AbstractPolynomialSolver {
+ /** Default absolute accuracy. */
+ private static final double DEFAULT_ABSOLUTE_ACCURACY = 1e-6;
+ /** Complex solver. */
+ private final ComplexSolver complexSolver = new ComplexSolver();
+
+ /**
+ * Construct a solver with default accuracy (1e-6).
+ */
+ public LaguerreSolver() {
+ this(DEFAULT_ABSOLUTE_ACCURACY);
+ }
+ /**
+ * Construct a solver.
+ *
+ * @param absoluteAccuracy Absolute accuracy.
+ */
+ public LaguerreSolver(double absoluteAccuracy) {
+ super(absoluteAccuracy);
+ }
+ /**
+ * Construct a solver.
+ *
+ * @param relativeAccuracy Relative accuracy.
+ * @param absoluteAccuracy Absolute accuracy.
+ */
+ public LaguerreSolver(double relativeAccuracy,
+ double absoluteAccuracy) {
+ super(relativeAccuracy, absoluteAccuracy);
+ }
+ /**
+ * Construct a solver.
+ *
+ * @param relativeAccuracy Relative accuracy.
+ * @param absoluteAccuracy Absolute accuracy.
+ * @param functionValueAccuracy Function value accuracy.
+ */
+ public LaguerreSolver(double relativeAccuracy,
+ double absoluteAccuracy,
+ double functionValueAccuracy) {
+ super(relativeAccuracy, absoluteAccuracy, functionValueAccuracy);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public double doSolve()
+ throws TooManyEvaluationsException,
+ NumberIsTooLargeException,
+ NoBracketingException {
+ final double min = getMin();
+ final double max = getMax();
+ final double initial = getStartValue();
+ final double functionValueAccuracy = getFunctionValueAccuracy();
+
+ verifySequence(min, initial, max);
+
+ // Return the initial guess if it is good enough.
+ final double yInitial = computeObjectiveValue(initial);
+ if (FastMath.abs(yInitial) <= functionValueAccuracy) {
+ return initial;
+ }
+
+ // Return the first endpoint if it is good enough.
+ final double yMin = computeObjectiveValue(min);
+ if (FastMath.abs(yMin) <= functionValueAccuracy) {
+ return min;
+ }
+
+ // Reduce interval if min and initial bracket the root.
+ if (yInitial * yMin < 0) {
+ return laguerre(min, initial, yMin, yInitial);
+ }
+
+ // Return the second endpoint if it is good enough.
+ final double yMax = computeObjectiveValue(max);
+ if (FastMath.abs(yMax) <= functionValueAccuracy) {
+ return max;
+ }
+
+ // Reduce interval if initial and max bracket the root.
+ if (yInitial * yMax < 0) {
+ return laguerre(initial, max, yInitial, yMax);
+ }
+
+ throw new NoBracketingException(min, max, yMin, yMax);
+ }
+
+ /**
+ * Find a real root in the given interval.
+ *
+ * Despite the bracketing condition, the root returned by
+ * {@link LaguerreSolver.ComplexSolver#solve(Complex[],Complex)} may
+ * not be a real zero inside {@code [min, max]}.
+ * For example, <code> p(x) = x<sup>3</sup> + 1, </code>
+ * with {@code min = -2}, {@code max = 2}, {@code initial = 0}.
+ * When it occurs, this code calls
+ * {@link LaguerreSolver.ComplexSolver#solveAll(Complex[],Complex)}
+ * in order to obtain all roots and picks up one real root.
+ *
+ * @param lo Lower bound of the search interval.
+ * @param hi Higher bound of the search interval.
+ * @param fLo Function value at the lower bound of the search interval.
+ * @param fHi Function value at the higher bound of the search interval.
+ * @return the point at which the function value is zero.
+ * @deprecated This method should not be part of the public API: It will
+ * be made private in version 4.0.
+ */
+ @Deprecated
+ public double laguerre(double lo, double hi,
+ double fLo, double fHi) {
+ final Complex c[] = ComplexUtils.convertToComplex(getCoefficients());
+
+ final Complex initial = new Complex(0.5 * (lo + hi), 0);
+ final Complex z = complexSolver.solve(c, initial);
+ if (complexSolver.isRoot(lo, hi, z)) {
+ return z.getReal();
+ } else {
+ double r = Double.NaN;
+ // Solve all roots and select the one we are seeking.
+ Complex[] root = complexSolver.solveAll(c, initial);
+ for (int i = 0; i < root.length; i++) {
+ if (complexSolver.isRoot(lo, hi, root[i])) {
+ r = root[i].getReal();
+ break;
+ }
+ }
+ return r;
+ }
+ }
+
+ /**
+ * Find all complex roots for the polynomial with the given
+ * coefficients, starting from the given initial value.
+ * <p>
+ * Note: This method is not part of the API of {@link BaseUnivariateSolver}.</p>
+ *
+ * @param coefficients Polynomial coefficients.
+ * @param initial Start value.
+ * @return the full set of complex roots of the polynomial
+ * @throws org.apache.commons.math3.exception.TooManyEvaluationsException
+ * if the maximum number of evaluations is exceeded when solving for one of the roots
+ * @throws NullArgumentException if the {@code coefficients} is
+ * {@code null}.
+ * @throws NoDataException if the {@code coefficients} array is empty.
+ * @since 3.1
+ */
+ public Complex[] solveAllComplex(double[] coefficients,
+ double initial)
+ throws NullArgumentException,
+ NoDataException,
+ TooManyEvaluationsException {
+ return solveAllComplex(coefficients, initial, Integer.MAX_VALUE);
+ }
+
+ /**
+ * Find all complex roots for the polynomial with the given
+ * coefficients, starting from the given initial value.
+ * <p>
+ * Note: This method is not part of the API of {@link BaseUnivariateSolver}.</p>
+ *
+ * @param coefficients polynomial coefficients
+ * @param initial start value
+ * @param maxEval maximum number of evaluations
+ * @return the full set of complex roots of the polynomial
+ * @throws org.apache.commons.math3.exception.TooManyEvaluationsException
+ * if the maximum number of evaluations is exceeded when solving for one of the roots
+ * @throws NullArgumentException if the {@code coefficients} is
+ * {@code null}
+ * @throws NoDataException if the {@code coefficients} array is empty
+ * @since 3.5
+ */
+ public Complex[] solveAllComplex(double[] coefficients,
+ double initial, int maxEval)
+ throws NullArgumentException,
+ NoDataException,
+ TooManyEvaluationsException {
+ setup(maxEval,
+ new PolynomialFunction(coefficients),
+ Double.NEGATIVE_INFINITY,
+ Double.POSITIVE_INFINITY,
+ initial);
+ return complexSolver.solveAll(ComplexUtils.convertToComplex(coefficients),
+ new Complex(initial, 0d));
+ }
+
+ /**
+ * Find a complex root for the polynomial with the given coefficients,
+ * starting from the given initial value.
+ * <p>
+ * Note: This method is not part of the API of {@link BaseUnivariateSolver}.</p>
+ *
+ * @param coefficients Polynomial coefficients.
+ * @param initial Start value.
+ * @return a complex root of the polynomial
+ * @throws org.apache.commons.math3.exception.TooManyEvaluationsException
+ * if the maximum number of evaluations is exceeded.
+ * @throws NullArgumentException if the {@code coefficients} is
+ * {@code null}.
+ * @throws NoDataException if the {@code coefficients} array is empty.
+ * @since 3.1
+ */
+ public Complex solveComplex(double[] coefficients,
+ double initial)
+ throws NullArgumentException,
+ NoDataException,
+ TooManyEvaluationsException {
+ return solveComplex(coefficients, initial, Integer.MAX_VALUE);
+ }
+
+ /**
+ * Find a complex root for the polynomial with the given coefficients,
+ * starting from the given initial value.
+ * <p>
+ * Note: This method is not part of the API of {@link BaseUnivariateSolver}.</p>
+ *
+ * @param coefficients polynomial coefficients
+ * @param initial start value
+ * @param maxEval maximum number of evaluations
+ * @return a complex root of the polynomial
+ * @throws org.apache.commons.math3.exception.TooManyEvaluationsException
+ * if the maximum number of evaluations is exceeded
+ * @throws NullArgumentException if the {@code coefficients} is
+ * {@code null}
+ * @throws NoDataException if the {@code coefficients} array is empty
+ * @since 3.1
+ */
+ public Complex solveComplex(double[] coefficients,
+ double initial, int maxEval)
+ throws NullArgumentException,
+ NoDataException,
+ TooManyEvaluationsException {
+ setup(maxEval,
+ new PolynomialFunction(coefficients),
+ Double.NEGATIVE_INFINITY,
+ Double.POSITIVE_INFINITY,
+ initial);
+ return complexSolver.solve(ComplexUtils.convertToComplex(coefficients),
+ new Complex(initial, 0d));
+ }
+
+ /**
+ * Class for searching all (complex) roots.
+ */
+ private class ComplexSolver {
+ /**
+ * Check whether the given complex root is actually a real zero
+ * in the given interval, within the solver tolerance level.
+ *
+ * @param min Lower bound for the interval.
+ * @param max Upper bound for the interval.
+ * @param z Complex root.
+ * @return {@code true} if z is a real zero.
+ */
+ public boolean isRoot(double min, double max, Complex z) {
+ if (isSequence(min, z.getReal(), max)) {
+ double tolerance = FastMath.max(getRelativeAccuracy() * z.abs(), getAbsoluteAccuracy());
+ return (FastMath.abs(z.getImaginary()) <= tolerance) ||
+ (z.abs() <= getFunctionValueAccuracy());
+ }
+ return false;
+ }
+
+ /**
+ * Find all complex roots for the polynomial with the given
+ * coefficients, starting from the given initial value.
+ *
+ * @param coefficients Polynomial coefficients.
+ * @param initial Start value.
+ * @return the point at which the function value is zero.
+ * @throws org.apache.commons.math3.exception.TooManyEvaluationsException
+ * if the maximum number of evaluations is exceeded.
+ * @throws NullArgumentException if the {@code coefficients} is
+ * {@code null}.
+ * @throws NoDataException if the {@code coefficients} array is empty.
+ */
+ public Complex[] solveAll(Complex coefficients[], Complex initial)
+ throws NullArgumentException,
+ NoDataException,
+ TooManyEvaluationsException {
+ if (coefficients == null) {
+ throw new NullArgumentException();
+ }
+ final int n = coefficients.length - 1;
+ if (n == 0) {
+ throw new NoDataException(LocalizedFormats.POLYNOMIAL);
+ }
+ // Coefficients for deflated polynomial.
+ final Complex c[] = new Complex[n + 1];
+ for (int i = 0; i <= n; i++) {
+ c[i] = coefficients[i];
+ }
+
+ // Solve individual roots successively.
+ final Complex root[] = new Complex[n];
+ for (int i = 0; i < n; i++) {
+ final Complex subarray[] = new Complex[n - i + 1];
+ System.arraycopy(c, 0, subarray, 0, subarray.length);
+ root[i] = solve(subarray, initial);
+ // Polynomial deflation using synthetic division.
+ Complex newc = c[n - i];
+ Complex oldc = null;
+ for (int j = n - i - 1; j >= 0; j--) {
+ oldc = c[j];
+ c[j] = newc;
+ newc = oldc.add(newc.multiply(root[i]));
+ }
+ }
+
+ return root;
+ }
+
+ /**
+ * Find a complex root for the polynomial with the given coefficients,
+ * starting from the given initial value.
+ *
+ * @param coefficients Polynomial coefficients.
+ * @param initial Start value.
+ * @return the point at which the function value is zero.
+ * @throws org.apache.commons.math3.exception.TooManyEvaluationsException
+ * if the maximum number of evaluations is exceeded.
+ * @throws NullArgumentException if the {@code coefficients} is
+ * {@code null}.
+ * @throws NoDataException if the {@code coefficients} array is empty.
+ */
+ public Complex solve(Complex coefficients[], Complex initial)
+ throws NullArgumentException,
+ NoDataException,
+ TooManyEvaluationsException {
+ if (coefficients == null) {
+ throw new NullArgumentException();
+ }
+
+ final int n = coefficients.length - 1;
+ if (n == 0) {
+ throw new NoDataException(LocalizedFormats.POLYNOMIAL);
+ }
+
+ final double absoluteAccuracy = getAbsoluteAccuracy();
+ final double relativeAccuracy = getRelativeAccuracy();
+ final double functionValueAccuracy = getFunctionValueAccuracy();
+
+ final Complex nC = new Complex(n, 0);
+ final Complex n1C = new Complex(n - 1, 0);
+
+ Complex z = initial;
+ Complex oldz = new Complex(Double.POSITIVE_INFINITY,
+ Double.POSITIVE_INFINITY);
+ while (true) {
+ // Compute pv (polynomial value), dv (derivative value), and
+ // d2v (second derivative value) simultaneously.
+ Complex pv = coefficients[n];
+ Complex dv = Complex.ZERO;
+ Complex d2v = Complex.ZERO;
+ for (int j = n-1; j >= 0; j--) {
+ d2v = dv.add(z.multiply(d2v));
+ dv = pv.add(z.multiply(dv));
+ pv = coefficients[j].add(z.multiply(pv));
+ }
+ d2v = d2v.multiply(new Complex(2.0, 0.0));
+
+ // Check for convergence.
+ final double tolerance = FastMath.max(relativeAccuracy * z.abs(),
+ absoluteAccuracy);
+ if ((z.subtract(oldz)).abs() <= tolerance) {
+ return z;
+ }
+ if (pv.abs() <= functionValueAccuracy) {
+ return z;
+ }
+
+ // Now pv != 0, calculate the new approximation.
+ final Complex G = dv.divide(pv);
+ final Complex G2 = G.multiply(G);
+ final Complex H = G2.subtract(d2v.divide(pv));
+ final Complex delta = n1C.multiply((nC.multiply(H)).subtract(G2));
+ // Choose a denominator larger in magnitude.
+ final Complex deltaSqrt = delta.sqrt();
+ final Complex dplus = G.add(deltaSqrt);
+ final Complex dminus = G.subtract(deltaSqrt);
+ final Complex denominator = dplus.abs() > dminus.abs() ? dplus : dminus;
+ // Perturb z if denominator is zero, for instance,
+ // p(x) = x^3 + 1, z = 0.
+ if (denominator.equals(new Complex(0.0, 0.0))) {
+ z = z.add(new Complex(absoluteAccuracy, absoluteAccuracy));
+ oldz = new Complex(Double.POSITIVE_INFINITY,
+ Double.POSITIVE_INFINITY);
+ } else {
+ oldz = z;
+ z = z.subtract(nC.divide(denominator));
+ }
+ incrementEvaluationCount();
+ }
+ }
+ }
+}
diff --git a/src/main/java/org/apache/commons/math3/analysis/solvers/MullerSolver.java b/src/main/java/org/apache/commons/math3/analysis/solvers/MullerSolver.java
new file mode 100644
index 0000000..2e59ed4
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/analysis/solvers/MullerSolver.java
@@ -0,0 +1,202 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math3.analysis.solvers;
+
+import org.apache.commons.math3.exception.NoBracketingException;
+import org.apache.commons.math3.exception.NumberIsTooLargeException;
+import org.apache.commons.math3.exception.TooManyEvaluationsException;
+import org.apache.commons.math3.util.FastMath;
+
+/**
+ * This class implements the <a href="http://mathworld.wolfram.com/MullersMethod.html">
+ * Muller's Method</a> for root finding of real univariate functions. For
+ * reference, see <b>Elementary Numerical Analysis</b>, ISBN 0070124477,
+ * chapter 3.
+ * <p>
+ * Muller's method applies to both real and complex functions, but here we
+ * restrict ourselves to real functions.
+ * This class differs from {@link MullerSolver} in the way it avoids complex
+ * operations.</p><p>
+ * Muller's original method would have function evaluation at complex point.
+ * Since our f(x) is real, we have to find ways to avoid that. Bracketing
+ * condition is one way to go: by requiring bracketing in every iteration,
+ * the newly computed approximation is guaranteed to be real.</p>
+ * <p>
+ * Normally Muller's method converges quadratically in the vicinity of a
+ * zero, however it may be very slow in regions far away from zeros. For
+ * example, f(x) = exp(x) - 1, min = -50, max = 100. In such case we use
+ * bisection as a safety backup if it performs very poorly.</p>
+ * <p>
+ * The formulas here use divided differences directly.</p>
+ *
+ * @since 1.2
+ * @see MullerSolver2
+ */
+public class MullerSolver extends AbstractUnivariateSolver {
+
+ /** Default absolute accuracy. */
+ private static final double DEFAULT_ABSOLUTE_ACCURACY = 1e-6;
+
+ /**
+ * Construct a solver with default accuracy (1e-6).
+ */
+ public MullerSolver() {
+ this(DEFAULT_ABSOLUTE_ACCURACY);
+ }
+ /**
+ * Construct a solver.
+ *
+ * @param absoluteAccuracy Absolute accuracy.
+ */
+ public MullerSolver(double absoluteAccuracy) {
+ super(absoluteAccuracy);
+ }
+ /**
+ * Construct a solver.
+ *
+ * @param relativeAccuracy Relative accuracy.
+ * @param absoluteAccuracy Absolute accuracy.
+ */
+ public MullerSolver(double relativeAccuracy,
+ double absoluteAccuracy) {
+ super(relativeAccuracy, absoluteAccuracy);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ protected double doSolve()
+ throws TooManyEvaluationsException,
+ NumberIsTooLargeException,
+ NoBracketingException {
+ final double min = getMin();
+ final double max = getMax();
+ final double initial = getStartValue();
+
+ final double functionValueAccuracy = getFunctionValueAccuracy();
+
+ verifySequence(min, initial, max);
+
+ // check for zeros before verifying bracketing
+ final double fMin = computeObjectiveValue(min);
+ if (FastMath.abs(fMin) < functionValueAccuracy) {
+ return min;
+ }
+ final double fMax = computeObjectiveValue(max);
+ if (FastMath.abs(fMax) < functionValueAccuracy) {
+ return max;
+ }
+ final double fInitial = computeObjectiveValue(initial);
+ if (FastMath.abs(fInitial) < functionValueAccuracy) {
+ return initial;
+ }
+
+ verifyBracketing(min, max);
+
+ if (isBracketing(min, initial)) {
+ return solve(min, initial, fMin, fInitial);
+ } else {
+ return solve(initial, max, fInitial, fMax);
+ }
+ }
+
+ /**
+ * Find a real root in the given interval.
+ *
+ * @param min Lower bound for the interval.
+ * @param max Upper bound for the interval.
+ * @param fMin function value at the lower bound.
+ * @param fMax function value at the upper bound.
+ * @return the point at which the function value is zero.
+ * @throws TooManyEvaluationsException if the allowed number of calls to
+ * the function to be solved has been exhausted.
+ */
+ private double solve(double min, double max,
+ double fMin, double fMax)
+ throws TooManyEvaluationsException {
+ final double relativeAccuracy = getRelativeAccuracy();
+ final double absoluteAccuracy = getAbsoluteAccuracy();
+ final double functionValueAccuracy = getFunctionValueAccuracy();
+
+ // [x0, x2] is the bracketing interval in each iteration
+ // x1 is the last approximation and an interpolation point in (x0, x2)
+ // x is the new root approximation and new x1 for next round
+ // d01, d12, d012 are divided differences
+
+ double x0 = min;
+ double y0 = fMin;
+ double x2 = max;
+ double y2 = fMax;
+ double x1 = 0.5 * (x0 + x2);
+ double y1 = computeObjectiveValue(x1);
+
+ double oldx = Double.POSITIVE_INFINITY;
+ while (true) {
+ // Muller's method employs quadratic interpolation through
+ // x0, x1, x2 and x is the zero of the interpolating parabola.
+ // Due to bracketing condition, this parabola must have two
+ // real roots and we choose one in [x0, x2] to be x.
+ final double d01 = (y1 - y0) / (x1 - x0);
+ final double d12 = (y2 - y1) / (x2 - x1);
+ final double d012 = (d12 - d01) / (x2 - x0);
+ final double c1 = d01 + (x1 - x0) * d012;
+ final double delta = c1 * c1 - 4 * y1 * d012;
+ final double xplus = x1 + (-2.0 * y1) / (c1 + FastMath.sqrt(delta));
+ final double xminus = x1 + (-2.0 * y1) / (c1 - FastMath.sqrt(delta));
+ // xplus and xminus are two roots of parabola and at least
+ // one of them should lie in (x0, x2)
+ final double x = isSequence(x0, xplus, x2) ? xplus : xminus;
+ final double y = computeObjectiveValue(x);
+
+ // check for convergence
+ final double tolerance = FastMath.max(relativeAccuracy * FastMath.abs(x), absoluteAccuracy);
+ if (FastMath.abs(x - oldx) <= tolerance ||
+ FastMath.abs(y) <= functionValueAccuracy) {
+ return x;
+ }
+
+ // Bisect if convergence is too slow. Bisection would waste
+ // our calculation of x, hopefully it won't happen often.
+ // the real number equality test x == x1 is intentional and
+ // completes the proximity tests above it
+ boolean bisect = (x < x1 && (x1 - x0) > 0.95 * (x2 - x0)) ||
+ (x > x1 && (x2 - x1) > 0.95 * (x2 - x0)) ||
+ (x == x1);
+ // prepare the new bracketing interval for next iteration
+ if (!bisect) {
+ x0 = x < x1 ? x0 : x1;
+ y0 = x < x1 ? y0 : y1;
+ x2 = x > x1 ? x2 : x1;
+ y2 = x > x1 ? y2 : y1;
+ x1 = x; y1 = y;
+ oldx = x;
+ } else {
+ double xm = 0.5 * (x0 + x2);
+ double ym = computeObjectiveValue(xm);
+ if (FastMath.signum(y0) + FastMath.signum(ym) == 0.0) {
+ x2 = xm; y2 = ym;
+ } else {
+ x0 = xm; y0 = ym;
+ }
+ x1 = 0.5 * (x0 + x2);
+ y1 = computeObjectiveValue(x1);
+ oldx = Double.POSITIVE_INFINITY;
+ }
+ }
+ }
+}
diff --git a/src/main/java/org/apache/commons/math3/analysis/solvers/MullerSolver2.java b/src/main/java/org/apache/commons/math3/analysis/solvers/MullerSolver2.java
new file mode 100644
index 0000000..864cfd5
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/analysis/solvers/MullerSolver2.java
@@ -0,0 +1,168 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math3.analysis.solvers;
+
+import org.apache.commons.math3.exception.NoBracketingException;
+import org.apache.commons.math3.exception.NumberIsTooLargeException;
+import org.apache.commons.math3.exception.TooManyEvaluationsException;
+import org.apache.commons.math3.util.FastMath;
+
+/**
+ * This class implements the <a href="http://mathworld.wolfram.com/MullersMethod.html">
+ * Muller's Method</a> for root finding of real univariate functions. For
+ * reference, see <b>Elementary Numerical Analysis</b>, ISBN 0070124477,
+ * chapter 3.
+ * <p>
+ * Muller's method applies to both real and complex functions, but here we
+ * restrict ourselves to real functions.
+ * This class differs from {@link MullerSolver} in the way it avoids complex
+ * operations.</p><p>
+ * Except for the initial [min, max], it does not require bracketing
+ * condition, e.g. f(x0), f(x1), f(x2) can have the same sign. If a complex
+ * number arises in the computation, we simply use its modulus as a real
+ * approximation.</p>
+ * <p>
+ * Because the interval may not be bracketing, the bisection alternative is
+ * not applicable here. However in practice our treatment usually works
+ * well, especially near real zeroes where the imaginary part of the complex
+ * approximation is often negligible.</p>
+ * <p>
+ * The formulas here do not use divided differences directly.</p>
+ *
+ * @since 1.2
+ * @see MullerSolver
+ */
+public class MullerSolver2 extends AbstractUnivariateSolver {
+
+ /** Default absolute accuracy. */
+ private static final double DEFAULT_ABSOLUTE_ACCURACY = 1e-6;
+
+ /**
+ * Construct a solver with default accuracy (1e-6).
+ */
+ public MullerSolver2() {
+ this(DEFAULT_ABSOLUTE_ACCURACY);
+ }
+ /**
+ * Construct a solver.
+ *
+ * @param absoluteAccuracy Absolute accuracy.
+ */
+ public MullerSolver2(double absoluteAccuracy) {
+ super(absoluteAccuracy);
+ }
+ /**
+ * Construct a solver.
+ *
+ * @param relativeAccuracy Relative accuracy.
+ * @param absoluteAccuracy Absolute accuracy.
+ */
+ public MullerSolver2(double relativeAccuracy,
+ double absoluteAccuracy) {
+ super(relativeAccuracy, absoluteAccuracy);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ protected double doSolve()
+ throws TooManyEvaluationsException,
+ NumberIsTooLargeException,
+ NoBracketingException {
+ final double min = getMin();
+ final double max = getMax();
+
+ verifyInterval(min, max);
+
+ final double relativeAccuracy = getRelativeAccuracy();
+ final double absoluteAccuracy = getAbsoluteAccuracy();
+ final double functionValueAccuracy = getFunctionValueAccuracy();
+
+ // x2 is the last root approximation
+ // x is the new approximation and new x2 for next round
+ // x0 < x1 < x2 does not hold here
+
+ double x0 = min;
+ double y0 = computeObjectiveValue(x0);
+ if (FastMath.abs(y0) < functionValueAccuracy) {
+ return x0;
+ }
+ double x1 = max;
+ double y1 = computeObjectiveValue(x1);
+ if (FastMath.abs(y1) < functionValueAccuracy) {
+ return x1;
+ }
+
+ if(y0 * y1 > 0) {
+ throw new NoBracketingException(x0, x1, y0, y1);
+ }
+
+ double x2 = 0.5 * (x0 + x1);
+ double y2 = computeObjectiveValue(x2);
+
+ double oldx = Double.POSITIVE_INFINITY;
+ while (true) {
+ // quadratic interpolation through x0, x1, x2
+ final double q = (x2 - x1) / (x1 - x0);
+ final double a = q * (y2 - (1 + q) * y1 + q * y0);
+ final double b = (2 * q + 1) * y2 - (1 + q) * (1 + q) * y1 + q * q * y0;
+ final double c = (1 + q) * y2;
+ final double delta = b * b - 4 * a * c;
+ double x;
+ final double denominator;
+ if (delta >= 0.0) {
+ // choose a denominator larger in magnitude
+ double dplus = b + FastMath.sqrt(delta);
+ double dminus = b - FastMath.sqrt(delta);
+ denominator = FastMath.abs(dplus) > FastMath.abs(dminus) ? dplus : dminus;
+ } else {
+ // take the modulus of (B +/- FastMath.sqrt(delta))
+ denominator = FastMath.sqrt(b * b - delta);
+ }
+ if (denominator != 0) {
+ x = x2 - 2.0 * c * (x2 - x1) / denominator;
+ // perturb x if it exactly coincides with x1 or x2
+ // the equality tests here are intentional
+ while (x == x1 || x == x2) {
+ x += absoluteAccuracy;
+ }
+ } else {
+ // extremely rare case, get a random number to skip it
+ x = min + FastMath.random() * (max - min);
+ oldx = Double.POSITIVE_INFINITY;
+ }
+ final double y = computeObjectiveValue(x);
+
+ // check for convergence
+ final double tolerance = FastMath.max(relativeAccuracy * FastMath.abs(x), absoluteAccuracy);
+ if (FastMath.abs(x - oldx) <= tolerance ||
+ FastMath.abs(y) <= functionValueAccuracy) {
+ return x;
+ }
+
+ // prepare the next iteration
+ x0 = x1;
+ y0 = y1;
+ x1 = x2;
+ y1 = y2;
+ x2 = x;
+ y2 = y;
+ oldx = x;
+ }
+ }
+}
diff --git a/src/main/java/org/apache/commons/math3/analysis/solvers/NewtonRaphsonSolver.java b/src/main/java/org/apache/commons/math3/analysis/solvers/NewtonRaphsonSolver.java
new file mode 100644
index 0000000..4cf2688
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/analysis/solvers/NewtonRaphsonSolver.java
@@ -0,0 +1,92 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math3.analysis.solvers;
+
+import org.apache.commons.math3.analysis.differentiation.DerivativeStructure;
+import org.apache.commons.math3.analysis.differentiation.UnivariateDifferentiableFunction;
+import org.apache.commons.math3.util.FastMath;
+import org.apache.commons.math3.exception.TooManyEvaluationsException;
+
+/**
+ * Implements <a href="http://mathworld.wolfram.com/NewtonsMethod.html">
+ * Newton's Method</a> for finding zeros of real univariate differentiable
+ * functions.
+ *
+ * @since 3.1
+ */
+public class NewtonRaphsonSolver extends AbstractUnivariateDifferentiableSolver {
+ /** Default absolute accuracy. */
+ private static final double DEFAULT_ABSOLUTE_ACCURACY = 1e-6;
+
+ /**
+ * Construct a solver.
+ */
+ public NewtonRaphsonSolver() {
+ this(DEFAULT_ABSOLUTE_ACCURACY);
+ }
+ /**
+ * Construct a solver.
+ *
+ * @param absoluteAccuracy Absolute accuracy.
+ */
+ public NewtonRaphsonSolver(double absoluteAccuracy) {
+ super(absoluteAccuracy);
+ }
+
+ /**
+ * Find a zero near the midpoint of {@code min} and {@code max}.
+ *
+ * @param f Function to solve.
+ * @param min Lower bound for the interval.
+ * @param max Upper bound for the interval.
+ * @param maxEval Maximum number of evaluations.
+ * @return the value where the function is zero.
+ * @throws org.apache.commons.math3.exception.TooManyEvaluationsException
+ * if the maximum evaluation count is exceeded.
+ * @throws org.apache.commons.math3.exception.NumberIsTooLargeException
+ * if {@code min >= max}.
+ */
+ @Override
+ public double solve(int maxEval, final UnivariateDifferentiableFunction f,
+ final double min, final double max)
+ throws TooManyEvaluationsException {
+ return super.solve(maxEval, f, UnivariateSolverUtils.midpoint(min, max));
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ protected double doSolve()
+ throws TooManyEvaluationsException {
+ final double startValue = getStartValue();
+ final double absoluteAccuracy = getAbsoluteAccuracy();
+
+ double x0 = startValue;
+ double x1;
+ while (true) {
+ final DerivativeStructure y0 = computeObjectiveValueAndDerivative(x0);
+ x1 = x0 - (y0.getValue() / y0.getPartialDerivative(1));
+ if (FastMath.abs(x1 - x0) <= absoluteAccuracy) {
+ return x1;
+ }
+
+ x0 = x1;
+ }
+ }
+}
diff --git a/src/main/java/org/apache/commons/math3/analysis/solvers/NewtonSolver.java b/src/main/java/org/apache/commons/math3/analysis/solvers/NewtonSolver.java
new file mode 100644
index 0000000..3ba7bf2
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/analysis/solvers/NewtonSolver.java
@@ -0,0 +1,92 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math3.analysis.solvers;
+
+import org.apache.commons.math3.analysis.DifferentiableUnivariateFunction;
+import org.apache.commons.math3.util.FastMath;
+import org.apache.commons.math3.exception.TooManyEvaluationsException;
+
+/**
+ * Implements <a href="http://mathworld.wolfram.com/NewtonsMethod.html">
+ * Newton's Method</a> for finding zeros of real univariate functions.
+ * <p>
+ * The function should be continuous but not necessarily smooth.</p>
+ *
+ * @deprecated as of 3.1, replaced by {@link NewtonRaphsonSolver}
+ */
+@Deprecated
+public class NewtonSolver extends AbstractDifferentiableUnivariateSolver {
+ /** Default absolute accuracy. */
+ private static final double DEFAULT_ABSOLUTE_ACCURACY = 1e-6;
+
+ /**
+ * Construct a solver.
+ */
+ public NewtonSolver() {
+ this(DEFAULT_ABSOLUTE_ACCURACY);
+ }
+ /**
+ * Construct a solver.
+ *
+ * @param absoluteAccuracy Absolute accuracy.
+ */
+ public NewtonSolver(double absoluteAccuracy) {
+ super(absoluteAccuracy);
+ }
+
+ /**
+ * Find a zero near the midpoint of {@code min} and {@code max}.
+ *
+ * @param f Function to solve.
+ * @param min Lower bound for the interval.
+ * @param max Upper bound for the interval.
+ * @param maxEval Maximum number of evaluations.
+ * @return the value where the function is zero.
+ * @throws org.apache.commons.math3.exception.TooManyEvaluationsException
+ * if the maximum evaluation count is exceeded.
+ * @throws org.apache.commons.math3.exception.NumberIsTooLargeException
+ * if {@code min >= max}.
+ */
+ @Override
+ public double solve(int maxEval, final DifferentiableUnivariateFunction f,
+ final double min, final double max)
+ throws TooManyEvaluationsException {
+ return super.solve(maxEval, f, UnivariateSolverUtils.midpoint(min, max));
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ protected double doSolve()
+ throws TooManyEvaluationsException {
+ final double startValue = getStartValue();
+ final double absoluteAccuracy = getAbsoluteAccuracy();
+
+ double x0 = startValue;
+ double x1;
+ while (true) {
+ x1 = x0 - (computeObjectiveValue(x0) / computeDerivativeObjectiveValue(x0));
+ if (FastMath.abs(x1 - x0) <= absoluteAccuracy) {
+ return x1;
+ }
+
+ x0 = x1;
+ }
+ }
+}
diff --git a/src/main/java/org/apache/commons/math3/analysis/solvers/PegasusSolver.java b/src/main/java/org/apache/commons/math3/analysis/solvers/PegasusSolver.java
new file mode 100644
index 0000000..0d80895
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/analysis/solvers/PegasusSolver.java
@@ -0,0 +1,84 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math3.analysis.solvers;
+
+/**
+ * Implements the <em>Pegasus</em> method for root-finding (approximating
+ * a zero of a univariate real function). It is a modified
+ * {@link RegulaFalsiSolver <em>Regula Falsi</em>} method.
+ *
+ * <p>Like the <em>Regula Falsi</em> method, convergence is guaranteed by
+ * maintaining a bracketed solution. The <em>Pegasus</em> method however,
+ * should converge much faster than the original <em>Regula Falsi</em>
+ * method. Furthermore, this implementation of the <em>Pegasus</em> method
+ * should not suffer from the same implementation issues as the <em>Regula
+ * Falsi</em> method, which may fail to convergence in certain cases. Also,
+ * the <em>Pegasus</em> method should converge faster than the
+ * {@link IllinoisSolver <em>Illinois</em>} method, another <em>Regula
+ * Falsi</em>-based method.</p>
+ *
+ * <p>The <em>Pegasus</em> method assumes that the function is continuous,
+ * but not necessarily smooth.</p>
+ *
+ * <p>Implementation based on the following article: M. Dowell and P. Jarratt,
+ * <em>The "Pegasus" method for computing the root of an equation</em>,
+ * BIT Numerical Mathematics, volume 12, number 4, pages 503-508, Springer,
+ * 1972.</p>
+ *
+ * @since 3.0
+ */
+public class PegasusSolver extends BaseSecantSolver {
+
+ /** Construct a solver with default accuracy (1e-6). */
+ public PegasusSolver() {
+ super(DEFAULT_ABSOLUTE_ACCURACY, Method.PEGASUS);
+ }
+
+ /**
+ * Construct a solver.
+ *
+ * @param absoluteAccuracy Absolute accuracy.
+ */
+ public PegasusSolver(final double absoluteAccuracy) {
+ super(absoluteAccuracy, Method.PEGASUS);
+ }
+
+ /**
+ * Construct a solver.
+ *
+ * @param relativeAccuracy Relative accuracy.
+ * @param absoluteAccuracy Absolute accuracy.
+ */
+ public PegasusSolver(final double relativeAccuracy,
+ final double absoluteAccuracy) {
+ super(relativeAccuracy, absoluteAccuracy, Method.PEGASUS);
+ }
+
+ /**
+ * Construct a solver.
+ *
+ * @param relativeAccuracy Relative accuracy.
+ * @param absoluteAccuracy Absolute accuracy.
+ * @param functionValueAccuracy Maximum function value error.
+ */
+ public PegasusSolver(final double relativeAccuracy,
+ final double absoluteAccuracy,
+ final double functionValueAccuracy) {
+ super(relativeAccuracy, absoluteAccuracy, functionValueAccuracy, Method.PEGASUS);
+ }
+}
diff --git a/src/main/java/org/apache/commons/math3/analysis/solvers/PolynomialSolver.java b/src/main/java/org/apache/commons/math3/analysis/solvers/PolynomialSolver.java
new file mode 100644
index 0000000..c21f076
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/analysis/solvers/PolynomialSolver.java
@@ -0,0 +1,28 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math3.analysis.solvers;
+
+import org.apache.commons.math3.analysis.polynomials.PolynomialFunction;
+
+/**
+ * Interface for (polynomial) root-finding algorithms.
+ * Implementations will search for only one zero in the given interval.
+ *
+ * @since 3.0
+ */
+public interface PolynomialSolver
+ extends BaseUnivariateSolver<PolynomialFunction> {}
diff --git a/src/main/java/org/apache/commons/math3/analysis/solvers/RegulaFalsiSolver.java b/src/main/java/org/apache/commons/math3/analysis/solvers/RegulaFalsiSolver.java
new file mode 100644
index 0000000..cfb7055
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/analysis/solvers/RegulaFalsiSolver.java
@@ -0,0 +1,94 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math3.analysis.solvers;
+
+/**
+ * Implements the <em>Regula Falsi</em> or <em>False position</em> method for
+ * root-finding (approximating a zero of a univariate real function). It is a
+ * modified {@link SecantSolver <em>Secant</em>} method.
+ *
+ * <p>The <em>Regula Falsi</em> method is included for completeness, for
+ * testing purposes, for educational purposes, for comparison to other
+ * algorithms, etc. It is however <strong>not</strong> intended to be used
+ * for actual problems, as one of the bounds often remains fixed, resulting
+ * in very slow convergence. Instead, one of the well-known modified
+ * <em>Regula Falsi</em> algorithms can be used ({@link IllinoisSolver
+ * <em>Illinois</em>} or {@link PegasusSolver <em>Pegasus</em>}). These two
+ * algorithms solve the fundamental issues of the original <em>Regula
+ * Falsi</em> algorithm, and greatly out-performs it for most, if not all,
+ * (practical) functions.
+ *
+ * <p>Unlike the <em>Secant</em> method, the <em>Regula Falsi</em> guarantees
+ * convergence, by maintaining a bracketed solution. Note however, that due to
+ * the finite/limited precision of Java's {@link Double double} type, which is
+ * used in this implementation, the algorithm may get stuck in a situation
+ * where it no longer makes any progress. Such cases are detected and result
+ * in a {@code ConvergenceException} exception being thrown. In other words,
+ * the algorithm theoretically guarantees convergence, but the implementation
+ * does not.</p>
+ *
+ * <p>The <em>Regula Falsi</em> method assumes that the function is continuous,
+ * but not necessarily smooth.</p>
+ *
+ * <p>Implementation based on the following article: M. Dowell and P. Jarratt,
+ * <em>A modified regula falsi method for computing the root of an
+ * equation</em>, BIT Numerical Mathematics, volume 11, number 2,
+ * pages 168-174, Springer, 1971.</p>
+ *
+ * @since 3.0
+ */
+public class RegulaFalsiSolver extends BaseSecantSolver {
+
+ /** Construct a solver with default accuracy (1e-6). */
+ public RegulaFalsiSolver() {
+ super(DEFAULT_ABSOLUTE_ACCURACY, Method.REGULA_FALSI);
+ }
+
+ /**
+ * Construct a solver.
+ *
+ * @param absoluteAccuracy Absolute accuracy.
+ */
+ public RegulaFalsiSolver(final double absoluteAccuracy) {
+ super(absoluteAccuracy, Method.REGULA_FALSI);
+ }
+
+ /**
+ * Construct a solver.
+ *
+ * @param relativeAccuracy Relative accuracy.
+ * @param absoluteAccuracy Absolute accuracy.
+ */
+ public RegulaFalsiSolver(final double relativeAccuracy,
+ final double absoluteAccuracy) {
+ super(relativeAccuracy, absoluteAccuracy, Method.REGULA_FALSI);
+ }
+
+ /**
+ * Construct a solver.
+ *
+ * @param relativeAccuracy Relative accuracy.
+ * @param absoluteAccuracy Absolute accuracy.
+ * @param functionValueAccuracy Maximum function value error.
+ */
+ public RegulaFalsiSolver(final double relativeAccuracy,
+ final double absoluteAccuracy,
+ final double functionValueAccuracy) {
+ super(relativeAccuracy, absoluteAccuracy, functionValueAccuracy, Method.REGULA_FALSI);
+ }
+}
diff --git a/src/main/java/org/apache/commons/math3/analysis/solvers/RiddersSolver.java b/src/main/java/org/apache/commons/math3/analysis/solvers/RiddersSolver.java
new file mode 100644
index 0000000..d83f595
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/analysis/solvers/RiddersSolver.java
@@ -0,0 +1,142 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math3.analysis.solvers;
+
+import org.apache.commons.math3.util.FastMath;
+import org.apache.commons.math3.exception.NoBracketingException;
+import org.apache.commons.math3.exception.TooManyEvaluationsException;
+
+/**
+ * Implements the <a href="http://mathworld.wolfram.com/RiddersMethod.html">
+ * Ridders' Method</a> for root finding of real univariate functions. For
+ * reference, see C. Ridders, <i>A new algorithm for computing a single root
+ * of a real continuous function </i>, IEEE Transactions on Circuits and
+ * Systems, 26 (1979), 979 - 980.
+ * <p>
+ * The function should be continuous but not necessarily smooth.</p>
+ *
+ * @since 1.2
+ */
+public class RiddersSolver extends AbstractUnivariateSolver {
+ /** Default absolute accuracy. */
+ private static final double DEFAULT_ABSOLUTE_ACCURACY = 1e-6;
+
+ /**
+ * Construct a solver with default accuracy (1e-6).
+ */
+ public RiddersSolver() {
+ this(DEFAULT_ABSOLUTE_ACCURACY);
+ }
+ /**
+ * Construct a solver.
+ *
+ * @param absoluteAccuracy Absolute accuracy.
+ */
+ public RiddersSolver(double absoluteAccuracy) {
+ super(absoluteAccuracy);
+ }
+ /**
+ * Construct a solver.
+ *
+ * @param relativeAccuracy Relative accuracy.
+ * @param absoluteAccuracy Absolute accuracy.
+ */
+ public RiddersSolver(double relativeAccuracy,
+ double absoluteAccuracy) {
+ super(relativeAccuracy, absoluteAccuracy);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ protected double doSolve()
+ throws TooManyEvaluationsException,
+ NoBracketingException {
+ double min = getMin();
+ double max = getMax();
+ // [x1, x2] is the bracketing interval in each iteration
+ // x3 is the midpoint of [x1, x2]
+ // x is the new root approximation and an endpoint of the new interval
+ double x1 = min;
+ double y1 = computeObjectiveValue(x1);
+ double x2 = max;
+ double y2 = computeObjectiveValue(x2);
+
+ // check for zeros before verifying bracketing
+ if (y1 == 0) {
+ return min;
+ }
+ if (y2 == 0) {
+ return max;
+ }
+ verifyBracketing(min, max);
+
+ final double absoluteAccuracy = getAbsoluteAccuracy();
+ final double functionValueAccuracy = getFunctionValueAccuracy();
+ final double relativeAccuracy = getRelativeAccuracy();
+
+ double oldx = Double.POSITIVE_INFINITY;
+ while (true) {
+ // calculate the new root approximation
+ final double x3 = 0.5 * (x1 + x2);
+ final double y3 = computeObjectiveValue(x3);
+ if (FastMath.abs(y3) <= functionValueAccuracy) {
+ return x3;
+ }
+ final double delta = 1 - (y1 * y2) / (y3 * y3); // delta > 1 due to bracketing
+ final double correction = (FastMath.signum(y2) * FastMath.signum(y3)) *
+ (x3 - x1) / FastMath.sqrt(delta);
+ final double x = x3 - correction; // correction != 0
+ final double y = computeObjectiveValue(x);
+
+ // check for convergence
+ final double tolerance = FastMath.max(relativeAccuracy * FastMath.abs(x), absoluteAccuracy);
+ if (FastMath.abs(x - oldx) <= tolerance) {
+ return x;
+ }
+ if (FastMath.abs(y) <= functionValueAccuracy) {
+ return x;
+ }
+
+ // prepare the new interval for next iteration
+ // Ridders' method guarantees x1 < x < x2
+ if (correction > 0.0) { // x1 < x < x3
+ if (FastMath.signum(y1) + FastMath.signum(y) == 0.0) {
+ x2 = x;
+ y2 = y;
+ } else {
+ x1 = x;
+ x2 = x3;
+ y1 = y;
+ y2 = y3;
+ }
+ } else { // x3 < x < x2
+ if (FastMath.signum(y2) + FastMath.signum(y) == 0.0) {
+ x1 = x;
+ y1 = y;
+ } else {
+ x1 = x3;
+ x2 = x;
+ y1 = y3;
+ y2 = y;
+ }
+ }
+ oldx = x;
+ }
+ }
+}
diff --git a/src/main/java/org/apache/commons/math3/analysis/solvers/SecantSolver.java b/src/main/java/org/apache/commons/math3/analysis/solvers/SecantSolver.java
new file mode 100644
index 0000000..d866cf8
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/analysis/solvers/SecantSolver.java
@@ -0,0 +1,135 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math3.analysis.solvers;
+
+import org.apache.commons.math3.util.FastMath;
+import org.apache.commons.math3.exception.NoBracketingException;
+import org.apache.commons.math3.exception.TooManyEvaluationsException;
+
+/**
+ * Implements the <em>Secant</em> method for root-finding (approximating a
+ * zero of a univariate real function). The solution that is maintained is
+ * not bracketed, and as such convergence is not guaranteed.
+ *
+ * <p>Implementation based on the following article: M. Dowell and P. Jarratt,
+ * <em>A modified regula falsi method for computing the root of an
+ * equation</em>, BIT Numerical Mathematics, volume 11, number 2,
+ * pages 168-174, Springer, 1971.</p>
+ *
+ * <p>Note that since release 3.0 this class implements the actual
+ * <em>Secant</em> algorithm, and not a modified one. As such, the 3.0 version
+ * is not backwards compatible with previous versions. To use an algorithm
+ * similar to the pre-3.0 releases, use the
+ * {@link IllinoisSolver <em>Illinois</em>} algorithm or the
+ * {@link PegasusSolver <em>Pegasus</em>} algorithm.</p>
+ *
+ */
+public class SecantSolver extends AbstractUnivariateSolver {
+
+ /** Default absolute accuracy. */
+ protected static final double DEFAULT_ABSOLUTE_ACCURACY = 1e-6;
+
+ /** Construct a solver with default accuracy (1e-6). */
+ public SecantSolver() {
+ super(DEFAULT_ABSOLUTE_ACCURACY);
+ }
+
+ /**
+ * Construct a solver.
+ *
+ * @param absoluteAccuracy absolute accuracy
+ */
+ public SecantSolver(final double absoluteAccuracy) {
+ super(absoluteAccuracy);
+ }
+
+ /**
+ * Construct a solver.
+ *
+ * @param relativeAccuracy relative accuracy
+ * @param absoluteAccuracy absolute accuracy
+ */
+ public SecantSolver(final double relativeAccuracy,
+ final double absoluteAccuracy) {
+ super(relativeAccuracy, absoluteAccuracy);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ protected final double doSolve()
+ throws TooManyEvaluationsException,
+ NoBracketingException {
+ // Get initial solution
+ double x0 = getMin();
+ double x1 = getMax();
+ double f0 = computeObjectiveValue(x0);
+ double f1 = computeObjectiveValue(x1);
+
+ // If one of the bounds is the exact root, return it. Since these are
+ // not under-approximations or over-approximations, we can return them
+ // regardless of the allowed solutions.
+ if (f0 == 0.0) {
+ return x0;
+ }
+ if (f1 == 0.0) {
+ return x1;
+ }
+
+ // Verify bracketing of initial solution.
+ verifyBracketing(x0, x1);
+
+ // Get accuracies.
+ final double ftol = getFunctionValueAccuracy();
+ final double atol = getAbsoluteAccuracy();
+ final double rtol = getRelativeAccuracy();
+
+ // Keep finding better approximations.
+ while (true) {
+ // Calculate the next approximation.
+ final double x = x1 - ((f1 * (x1 - x0)) / (f1 - f0));
+ final double fx = computeObjectiveValue(x);
+
+ // If the new approximation is the exact root, return it. Since
+ // this is not an under-approximation or an over-approximation,
+ // we can return it regardless of the allowed solutions.
+ if (fx == 0.0) {
+ return x;
+ }
+
+ // Update the bounds with the new approximation.
+ x0 = x1;
+ f0 = f1;
+ x1 = x;
+ f1 = fx;
+
+ // If the function value of the last approximation is too small,
+ // given the function value accuracy, then we can't get closer to
+ // the root than we already are.
+ if (FastMath.abs(f1) <= ftol) {
+ return x1;
+ }
+
+ // If the current interval is within the given accuracies, we
+ // are satisfied with the current approximation.
+ if (FastMath.abs(x1 - x0) < FastMath.max(rtol * FastMath.abs(x1), atol)) {
+ return x1;
+ }
+ }
+ }
+
+}
diff --git a/src/main/java/org/apache/commons/math3/analysis/solvers/UnivariateDifferentiableSolver.java b/src/main/java/org/apache/commons/math3/analysis/solvers/UnivariateDifferentiableSolver.java
new file mode 100644
index 0000000..82bbead
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/analysis/solvers/UnivariateDifferentiableSolver.java
@@ -0,0 +1,29 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math3.analysis.solvers;
+
+import org.apache.commons.math3.analysis.differentiation.UnivariateDifferentiableFunction;
+
+
+/**
+ * Interface for (univariate real) rootfinding algorithms.
+ * Implementations will search for only one zero in the given interval.
+ *
+ * @since 3.1
+ */
+public interface UnivariateDifferentiableSolver
+ extends BaseUnivariateSolver<UnivariateDifferentiableFunction> {}
diff --git a/src/main/java/org/apache/commons/math3/analysis/solvers/UnivariateSolver.java b/src/main/java/org/apache/commons/math3/analysis/solvers/UnivariateSolver.java
new file mode 100644
index 0000000..484e67a
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/analysis/solvers/UnivariateSolver.java
@@ -0,0 +1,28 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math3.analysis.solvers;
+
+import org.apache.commons.math3.analysis.UnivariateFunction;
+
+
+/**
+ * Interface for (univariate real) root-finding algorithms.
+ * Implementations will search for only one zero in the given interval.
+ *
+ */
+public interface UnivariateSolver
+ extends BaseUnivariateSolver<UnivariateFunction> {}
diff --git a/src/main/java/org/apache/commons/math3/analysis/solvers/UnivariateSolverUtils.java b/src/main/java/org/apache/commons/math3/analysis/solvers/UnivariateSolverUtils.java
new file mode 100644
index 0000000..71b34c5
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/analysis/solvers/UnivariateSolverUtils.java
@@ -0,0 +1,467 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math3.analysis.solvers;
+
+import org.apache.commons.math3.analysis.UnivariateFunction;
+import org.apache.commons.math3.exception.NoBracketingException;
+import org.apache.commons.math3.exception.NotStrictlyPositiveException;
+import org.apache.commons.math3.exception.NullArgumentException;
+import org.apache.commons.math3.exception.NumberIsTooLargeException;
+import org.apache.commons.math3.exception.util.LocalizedFormats;
+import org.apache.commons.math3.util.FastMath;
+
+/**
+ * Utility routines for {@link UnivariateSolver} objects.
+ *
+ */
+public class UnivariateSolverUtils {
+ /**
+ * Class contains only static methods.
+ */
+ private UnivariateSolverUtils() {}
+
+ /**
+ * Convenience method to find a zero of a univariate real function. A default
+ * solver is used.
+ *
+ * @param function Function.
+ * @param x0 Lower bound for the interval.
+ * @param x1 Upper bound for the interval.
+ * @return a value where the function is zero.
+ * @throws NoBracketingException if the function has the same sign at the
+ * endpoints.
+ * @throws NullArgumentException if {@code function} is {@code null}.
+ */
+ public static double solve(UnivariateFunction function, double x0, double x1)
+ throws NullArgumentException,
+ NoBracketingException {
+ if (function == null) {
+ throw new NullArgumentException(LocalizedFormats.FUNCTION);
+ }
+ final UnivariateSolver solver = new BrentSolver();
+ return solver.solve(Integer.MAX_VALUE, function, x0, x1);
+ }
+
+ /**
+ * Convenience method to find a zero of a univariate real function. A default
+ * solver is used.
+ *
+ * @param function Function.
+ * @param x0 Lower bound for the interval.
+ * @param x1 Upper bound for the interval.
+ * @param absoluteAccuracy Accuracy to be used by the solver.
+ * @return a value where the function is zero.
+ * @throws NoBracketingException if the function has the same sign at the
+ * endpoints.
+ * @throws NullArgumentException if {@code function} is {@code null}.
+ */
+ public static double solve(UnivariateFunction function,
+ double x0, double x1,
+ double absoluteAccuracy)
+ throws NullArgumentException,
+ NoBracketingException {
+ if (function == null) {
+ throw new NullArgumentException(LocalizedFormats.FUNCTION);
+ }
+ final UnivariateSolver solver = new BrentSolver(absoluteAccuracy);
+ return solver.solve(Integer.MAX_VALUE, function, x0, x1);
+ }
+
+ /**
+ * Force a root found by a non-bracketing solver to lie on a specified side,
+ * as if the solver were a bracketing one.
+ *
+ * @param maxEval maximal number of new evaluations of the function
+ * (evaluations already done for finding the root should have already been subtracted
+ * from this number)
+ * @param f function to solve
+ * @param bracketing bracketing solver to use for shifting the root
+ * @param baseRoot original root found by a previous non-bracketing solver
+ * @param min minimal bound of the search interval
+ * @param max maximal bound of the search interval
+ * @param allowedSolution the kind of solutions that the root-finding algorithm may
+ * accept as solutions.
+ * @return a root approximation, on the specified side of the exact root
+ * @throws NoBracketingException if the function has the same sign at the
+ * endpoints.
+ */
+ public static double forceSide(final int maxEval, final UnivariateFunction f,
+ final BracketedUnivariateSolver<UnivariateFunction> bracketing,
+ final double baseRoot, final double min, final double max,
+ final AllowedSolution allowedSolution)
+ throws NoBracketingException {
+
+ if (allowedSolution == AllowedSolution.ANY_SIDE) {
+ // no further bracketing required
+ return baseRoot;
+ }
+
+ // find a very small interval bracketing the root
+ final double step = FastMath.max(bracketing.getAbsoluteAccuracy(),
+ FastMath.abs(baseRoot * bracketing.getRelativeAccuracy()));
+ double xLo = FastMath.max(min, baseRoot - step);
+ double fLo = f.value(xLo);
+ double xHi = FastMath.min(max, baseRoot + step);
+ double fHi = f.value(xHi);
+ int remainingEval = maxEval - 2;
+ while (remainingEval > 0) {
+
+ if ((fLo >= 0 && fHi <= 0) || (fLo <= 0 && fHi >= 0)) {
+ // compute the root on the selected side
+ return bracketing.solve(remainingEval, f, xLo, xHi, baseRoot, allowedSolution);
+ }
+
+ // try increasing the interval
+ boolean changeLo = false;
+ boolean changeHi = false;
+ if (fLo < fHi) {
+ // increasing function
+ if (fLo >= 0) {
+ changeLo = true;
+ } else {
+ changeHi = true;
+ }
+ } else if (fLo > fHi) {
+ // decreasing function
+ if (fLo <= 0) {
+ changeLo = true;
+ } else {
+ changeHi = true;
+ }
+ } else {
+ // unknown variation
+ changeLo = true;
+ changeHi = true;
+ }
+
+ // update the lower bound
+ if (changeLo) {
+ xLo = FastMath.max(min, xLo - step);
+ fLo = f.value(xLo);
+ remainingEval--;
+ }
+
+ // update the higher bound
+ if (changeHi) {
+ xHi = FastMath.min(max, xHi + step);
+ fHi = f.value(xHi);
+ remainingEval--;
+ }
+
+ }
+
+ throw new NoBracketingException(LocalizedFormats.FAILED_BRACKETING,
+ xLo, xHi, fLo, fHi,
+ maxEval - remainingEval, maxEval, baseRoot,
+ min, max);
+
+ }
+
+ /**
+ * This method simply calls {@link #bracket(UnivariateFunction, double, double, double,
+ * double, double, int) bracket(function, initial, lowerBound, upperBound, q, r, maximumIterations)}
+ * with {@code q} and {@code r} set to 1.0 and {@code maximumIterations} set to {@code Integer.MAX_VALUE}.
+ * <p>
+ * <strong>Note: </strong> this method can take {@code Integer.MAX_VALUE}
+ * iterations to throw a {@code ConvergenceException.} Unless you are
+ * confident that there is a root between {@code lowerBound} and
+ * {@code upperBound} near {@code initial}, it is better to use
+ * {@link #bracket(UnivariateFunction, double, double, double, double,double, int)
+ * bracket(function, initial, lowerBound, upperBound, q, r, maximumIterations)},
+ * explicitly specifying the maximum number of iterations.</p>
+ *
+ * @param function Function.
+ * @param initial Initial midpoint of interval being expanded to
+ * bracket a root.
+ * @param lowerBound Lower bound (a is never lower than this value)
+ * @param upperBound Upper bound (b never is greater than this
+ * value).
+ * @return a two-element array holding a and b.
+ * @throws NoBracketingException if a root cannot be bracketted.
+ * @throws NotStrictlyPositiveException if {@code maximumIterations <= 0}.
+ * @throws NullArgumentException if {@code function} is {@code null}.
+ */
+ public static double[] bracket(UnivariateFunction function,
+ double initial,
+ double lowerBound, double upperBound)
+ throws NullArgumentException,
+ NotStrictlyPositiveException,
+ NoBracketingException {
+ return bracket(function, initial, lowerBound, upperBound, 1.0, 1.0, Integer.MAX_VALUE);
+ }
+
+ /**
+ * This method simply calls {@link #bracket(UnivariateFunction, double, double, double,
+ * double, double, int) bracket(function, initial, lowerBound, upperBound, q, r, maximumIterations)}
+ * with {@code q} and {@code r} set to 1.0.
+ * @param function Function.
+ * @param initial Initial midpoint of interval being expanded to
+ * bracket a root.
+ * @param lowerBound Lower bound (a is never lower than this value).
+ * @param upperBound Upper bound (b never is greater than this
+ * value).
+ * @param maximumIterations Maximum number of iterations to perform
+ * @return a two element array holding a and b.
+ * @throws NoBracketingException if the algorithm fails to find a and b
+ * satisfying the desired conditions.
+ * @throws NotStrictlyPositiveException if {@code maximumIterations <= 0}.
+ * @throws NullArgumentException if {@code function} is {@code null}.
+ */
+ public static double[] bracket(UnivariateFunction function,
+ double initial,
+ double lowerBound, double upperBound,
+ int maximumIterations)
+ throws NullArgumentException,
+ NotStrictlyPositiveException,
+ NoBracketingException {
+ return bracket(function, initial, lowerBound, upperBound, 1.0, 1.0, maximumIterations);
+ }
+
+ /**
+ * This method attempts to find two values a and b satisfying <ul>
+ * <li> {@code lowerBound <= a < initial < b <= upperBound} </li>
+ * <li> {@code f(a) * f(b) <= 0} </li>
+ * </ul>
+ * If {@code f} is continuous on {@code [a,b]}, this means that {@code a}
+ * and {@code b} bracket a root of {@code f}.
+ * <p>
+ * The algorithm checks the sign of \( f(l_k) \) and \( f(u_k) \) for increasing
+ * values of k, where \( l_k = max(lower, initial - \delta_k) \),
+ * \( u_k = min(upper, initial + \delta_k) \), using recurrence
+ * \( \delta_{k+1} = r \delta_k + q, \delta_0 = 0\) and starting search with \( k=1 \).
+ * The algorithm stops when one of the following happens: <ul>
+ * <li> at least one positive and one negative value have been found -- success!</li>
+ * <li> both endpoints have reached their respective limits -- NoBracketingException </li>
+ * <li> {@code maximumIterations} iterations elapse -- NoBracketingException </li></ul>
+ * <p>
+ * If different signs are found at first iteration ({@code k=1}), then the returned
+ * interval will be \( [a, b] = [l_1, u_1] \). If different signs are found at a later
+ * iteration {@code k>1}, then the returned interval will be either
+ * \( [a, b] = [l_{k+1}, l_{k}] \) or \( [a, b] = [u_{k}, u_{k+1}] \). A root solver called
+ * with these parameters will therefore start with the smallest bracketing interval known
+ * at this step.
+ * </p>
+ * <p>
+ * Interval expansion rate is tuned by changing the recurrence parameters {@code r} and
+ * {@code q}. When the multiplicative factor {@code r} is set to 1, the sequence is a
+ * simple arithmetic sequence with linear increase. When the multiplicative factor {@code r}
+ * is larger than 1, the sequence has an asymptotically exponential rate. Note than the
+ * additive parameter {@code q} should never be set to zero, otherwise the interval would
+ * degenerate to the single initial point for all values of {@code k}.
+ * </p>
+ * <p>
+ * As a rule of thumb, when the location of the root is expected to be approximately known
+ * within some error margin, {@code r} should be set to 1 and {@code q} should be set to the
+ * order of magnitude of the error margin. When the location of the root is really a wild guess,
+ * then {@code r} should be set to a value larger than 1 (typically 2 to double the interval
+ * length at each iteration) and {@code q} should be set according to half the initial
+ * search interval length.
+ * </p>
+ * <p>
+ * As an example, if we consider the trivial function {@code f(x) = 1 - x} and use
+ * {@code initial = 4}, {@code r = 1}, {@code q = 2}, the algorithm will compute
+ * {@code f(4-2) = f(2) = -1} and {@code f(4+2) = f(6) = -5} for {@code k = 1}, then
+ * {@code f(4-4) = f(0) = +1} and {@code f(4+4) = f(8) = -7} for {@code k = 2}. Then it will
+ * return the interval {@code [0, 2]} as the smallest one known to be bracketing the root.
+ * As shown by this example, the initial value (here {@code 4}) may lie outside of the returned
+ * bracketing interval.
+ * </p>
+ * @param function function to check
+ * @param initial Initial midpoint of interval being expanded to
+ * bracket a root.
+ * @param lowerBound Lower bound (a is never lower than this value).
+ * @param upperBound Upper bound (b never is greater than this
+ * value).
+ * @param q additive offset used to compute bounds sequence (must be strictly positive)
+ * @param r multiplicative factor used to compute bounds sequence
+ * @param maximumIterations Maximum number of iterations to perform
+ * @return a two element array holding the bracketing values.
+ * @exception NoBracketingException if function cannot be bracketed in the search interval
+ */
+ public static double[] bracket(final UnivariateFunction function, final double initial,
+ final double lowerBound, final double upperBound,
+ final double q, final double r, final int maximumIterations)
+ throws NoBracketingException {
+
+ if (function == null) {
+ throw new NullArgumentException(LocalizedFormats.FUNCTION);
+ }
+ if (q <= 0) {
+ throw new NotStrictlyPositiveException(q);
+ }
+ if (maximumIterations <= 0) {
+ throw new NotStrictlyPositiveException(LocalizedFormats.INVALID_MAX_ITERATIONS, maximumIterations);
+ }
+ verifySequence(lowerBound, initial, upperBound);
+
+ // initialize the recurrence
+ double a = initial;
+ double b = initial;
+ double fa = Double.NaN;
+ double fb = Double.NaN;
+ double delta = 0;
+
+ for (int numIterations = 0;
+ (numIterations < maximumIterations) && (a > lowerBound || b < upperBound);
+ ++numIterations) {
+
+ final double previousA = a;
+ final double previousFa = fa;
+ final double previousB = b;
+ final double previousFb = fb;
+
+ delta = r * delta + q;
+ a = FastMath.max(initial - delta, lowerBound);
+ b = FastMath.min(initial + delta, upperBound);
+ fa = function.value(a);
+ fb = function.value(b);
+
+ if (numIterations == 0) {
+ // at first iteration, we don't have a previous interval
+ // we simply compare both sides of the initial interval
+ if (fa * fb <= 0) {
+ // the first interval already brackets a root
+ return new double[] { a, b };
+ }
+ } else {
+ // we have a previous interval with constant sign and expand it,
+ // we expect sign changes to occur at boundaries
+ if (fa * previousFa <= 0) {
+ // sign change detected at near lower bound
+ return new double[] { a, previousA };
+ } else if (fb * previousFb <= 0) {
+ // sign change detected at near upper bound
+ return new double[] { previousB, b };
+ }
+ }
+
+ }
+
+ // no bracketing found
+ throw new NoBracketingException(a, b, fa, fb);
+
+ }
+
+ /**
+ * Compute the midpoint of two values.
+ *
+ * @param a first value.
+ * @param b second value.
+ * @return the midpoint.
+ */
+ public static double midpoint(double a, double b) {
+ return (a + b) * 0.5;
+ }
+
+ /**
+ * Check whether the interval bounds bracket a root. That is, if the
+ * values at the endpoints are not equal to zero, then the function takes
+ * opposite signs at the endpoints.
+ *
+ * @param function Function.
+ * @param lower Lower endpoint.
+ * @param upper Upper endpoint.
+ * @return {@code true} if the function values have opposite signs at the
+ * given points.
+ * @throws NullArgumentException if {@code function} is {@code null}.
+ */
+ public static boolean isBracketing(UnivariateFunction function,
+ final double lower,
+ final double upper)
+ throws NullArgumentException {
+ if (function == null) {
+ throw new NullArgumentException(LocalizedFormats.FUNCTION);
+ }
+ final double fLo = function.value(lower);
+ final double fHi = function.value(upper);
+ return (fLo >= 0 && fHi <= 0) || (fLo <= 0 && fHi >= 0);
+ }
+
+ /**
+ * Check whether the arguments form a (strictly) increasing sequence.
+ *
+ * @param start First number.
+ * @param mid Second number.
+ * @param end Third number.
+ * @return {@code true} if the arguments form an increasing sequence.
+ */
+ public static boolean isSequence(final double start,
+ final double mid,
+ final double end) {
+ return (start < mid) && (mid < end);
+ }
+
+ /**
+ * Check that the endpoints specify an interval.
+ *
+ * @param lower Lower endpoint.
+ * @param upper Upper endpoint.
+ * @throws NumberIsTooLargeException if {@code lower >= upper}.
+ */
+ public static void verifyInterval(final double lower,
+ final double upper)
+ throws NumberIsTooLargeException {
+ if (lower >= upper) {
+ throw new NumberIsTooLargeException(LocalizedFormats.ENDPOINTS_NOT_AN_INTERVAL,
+ lower, upper, false);
+ }
+ }
+
+ /**
+ * Check that {@code lower < initial < upper}.
+ *
+ * @param lower Lower endpoint.
+ * @param initial Initial value.
+ * @param upper Upper endpoint.
+ * @throws NumberIsTooLargeException if {@code lower >= initial} or
+ * {@code initial >= upper}.
+ */
+ public static void verifySequence(final double lower,
+ final double initial,
+ final double upper)
+ throws NumberIsTooLargeException {
+ verifyInterval(lower, initial);
+ verifyInterval(initial, upper);
+ }
+
+ /**
+ * Check that the endpoints specify an interval and the end points
+ * bracket a root.
+ *
+ * @param function Function.
+ * @param lower Lower endpoint.
+ * @param upper Upper endpoint.
+ * @throws NoBracketingException if the function has the same sign at the
+ * endpoints.
+ * @throws NullArgumentException if {@code function} is {@code null}.
+ */
+ public static void verifyBracketing(UnivariateFunction function,
+ final double lower,
+ final double upper)
+ throws NullArgumentException,
+ NoBracketingException {
+ if (function == null) {
+ throw new NullArgumentException(LocalizedFormats.FUNCTION);
+ }
+ verifyInterval(lower, upper);
+ if (!isBracketing(function, lower, upper)) {
+ throw new NoBracketingException(lower, upper,
+ function.value(lower),
+ function.value(upper));
+ }
+ }
+}
diff --git a/src/main/java/org/apache/commons/math3/analysis/solvers/package-info.java b/src/main/java/org/apache/commons/math3/analysis/solvers/package-info.java
new file mode 100644
index 0000000..eb15fbc
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/analysis/solvers/package-info.java
@@ -0,0 +1,22 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/**
+ *
+ * Root finding algorithms, for univariate real functions.
+ *
+ */
+package org.apache.commons.math3.analysis.solvers;
diff --git a/src/main/java/org/apache/commons/math3/complex/Complex.java b/src/main/java/org/apache/commons/math3/complex/Complex.java
new file mode 100644
index 0000000..cd8d794
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/complex/Complex.java
@@ -0,0 +1,1219 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math3.complex;
+
+import org.apache.commons.math3.FieldElement;
+import org.apache.commons.math3.exception.NotPositiveException;
+import org.apache.commons.math3.exception.NullArgumentException;
+import org.apache.commons.math3.exception.util.LocalizedFormats;
+import org.apache.commons.math3.util.FastMath;
+import org.apache.commons.math3.util.MathUtils;
+import org.apache.commons.math3.util.Precision;
+
+import java.io.Serializable;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Representation of a Complex number, i.e. a number which has both a real and imaginary part.
+ *
+ * <p>Implementations of arithmetic operations handle {@code NaN} and infinite values according to
+ * the rules for {@link java.lang.Double}, i.e. {@link #equals} is an equivalence relation for all
+ * instances that have a {@code NaN} in either real or imaginary part, e.g. the following are
+ * considered equal:
+ *
+ * <ul>
+ * <li>{@code 1 + NaNi}
+ * <li>{@code NaN + i}
+ * <li>{@code NaN + NaNi}
+ * </ul>
+ *
+ * <p>Note that this contradicts the IEEE-754 standard for floating point numbers (according to
+ * which the test {@code x == x} must fail if {@code x} is {@code NaN}). The method {@link
+ * org.apache.commons.math3.util.Precision#equals(double,double,int) equals for primitive double} in
+ * {@link org.apache.commons.math3.util.Precision} conforms with IEEE-754 while this class conforms
+ * with the standard behavior for Java object types.
+ */
+public class Complex implements FieldElement<Complex>, Serializable {
+ /** The square root of -1. A number representing "0.0 + 1.0i" */
+ public static final Complex I = new Complex(0.0, 1.0);
+
+ // CHECKSTYLE: stop ConstantName
+ /** A complex number representing "NaN + NaNi" */
+ public static final Complex NaN = new Complex(Double.NaN, Double.NaN);
+
+ // CHECKSTYLE: resume ConstantName
+ /** A complex number representing "+INF + INFi" */
+ public static final Complex INF =
+ new Complex(Double.POSITIVE_INFINITY, Double.POSITIVE_INFINITY);
+
+ /** A complex number representing "1.0 + 0.0i" */
+ public static final Complex ONE = new Complex(1.0, 0.0);
+
+ /** A complex number representing "0.0 + 0.0i" */
+ public static final Complex ZERO = new Complex(0.0, 0.0);
+
+ /** Serializable version identifier */
+ private static final long serialVersionUID = -6195664516687396620L;
+
+ /** The imaginary part. */
+ private final double imaginary;
+
+ /** The real part. */
+ private final double real;
+
+ /** Record whether this complex number is equal to NaN. */
+ private final transient boolean isNaN;
+
+ /** Record whether this complex number is infinite. */
+ private final transient boolean isInfinite;
+
+ /**
+ * Create a complex number given only the real part.
+ *
+ * @param real Real part.
+ */
+ public Complex(double real) {
+ this(real, 0.0);
+ }
+
+ /**
+ * Create a complex number given the real and imaginary parts.
+ *
+ * @param real Real part.
+ * @param imaginary Imaginary part.
+ */
+ public Complex(double real, double imaginary) {
+ this.real = real;
+ this.imaginary = imaginary;
+
+ isNaN = Double.isNaN(real) || Double.isNaN(imaginary);
+ isInfinite = !isNaN && (Double.isInfinite(real) || Double.isInfinite(imaginary));
+ }
+
+ /**
+ * Return the absolute value of this complex number. Returns {@code NaN} if either real or
+ * imaginary part is {@code NaN} and {@code Double.POSITIVE_INFINITY} if neither part is {@code
+ * NaN}, but at least one part is infinite.
+ *
+ * @return the absolute value.
+ */
+ public double abs() {
+ if (isNaN) {
+ return Double.NaN;
+ }
+ if (isInfinite()) {
+ return Double.POSITIVE_INFINITY;
+ }
+ if (FastMath.abs(real) < FastMath.abs(imaginary)) {
+ if (imaginary == 0.0) {
+ return FastMath.abs(real);
+ }
+ double q = real / imaginary;
+ return FastMath.abs(imaginary) * FastMath.sqrt(1 + q * q);
+ } else {
+ if (real == 0.0) {
+ return FastMath.abs(imaginary);
+ }
+ double q = imaginary / real;
+ return FastMath.abs(real) * FastMath.sqrt(1 + q * q);
+ }
+ }
+
+ /**
+ * Returns a {@code Complex} whose value is {@code (this + addend)}. Uses the definitional
+ * formula
+ *
+ * <p>{@code (a + bi) + (c + di) = (a+c) + (b+d)i} If either {@code this} or {@code addend} has
+ * a {@code NaN} value in either part, {@link #NaN} is returned; otherwise {@code Infinite} and
+ * {@code NaN} values are returned in the parts of the result according to the rules for {@link
+ * java.lang.Double} arithmetic.
+ *
+ * @param addend Value to be added to this {@code Complex}.
+ * @return {@code this + addend}.
+ * @throws NullArgumentException if {@code addend} is {@code null}.
+ */
+ public Complex add(Complex addend) throws NullArgumentException {
+ MathUtils.checkNotNull(addend);
+ if (isNaN || addend.isNaN) {
+ return NaN;
+ }
+
+ return createComplex(real + addend.getReal(), imaginary + addend.getImaginary());
+ }
+
+ /**
+ * Returns a {@code Complex} whose value is {@code (this + addend)}, with {@code addend}
+ * interpreted as a real number.
+ *
+ * @param addend Value to be added to this {@code Complex}.
+ * @return {@code this + addend}.
+ * @see #add(Complex)
+ */
+ public Complex add(double addend) {
+ if (isNaN || Double.isNaN(addend)) {
+ return NaN;
+ }
+
+ return createComplex(real + addend, imaginary);
+ }
+
+ /**
+ * Returns the conjugate of this complex number. The conjugate of {@code a + bi} is {@code a -
+ * bi}.
+ *
+ * <p>{@link #NaN} is returned if either the real or imaginary part of this Complex number
+ * equals {@code Double.NaN}.
+ *
+ * <p>If the imaginary part is infinite, and the real part is not {@code NaN}, the returned
+ * value has infinite imaginary part of the opposite sign, e.g. the conjugate of {@code 1 +
+ * POSITIVE_INFINITY i} is {@code 1 - NEGATIVE_INFINITY i}.
+ *
+ * @return the conjugate of this Complex object.
+ */
+ public Complex conjugate() {
+ if (isNaN) {
+ return NaN;
+ }
+
+ return createComplex(real, -imaginary);
+ }
+
+ /**
+ * Returns a {@code Complex} whose value is {@code (this / divisor)}. Implements the
+ * definitional formula
+ *
+ * <pre>
+ * <code>
+ * a + bi ac + bd + (bc - ad)i
+ * ----------- = -------------------------
+ * c + di c<sup>2</sup> + d<sup>2</sup>
+ * </code>
+ * </pre>
+ *
+ * but uses <a href="http://doi.acm.org/10.1145/1039813.1039814">prescaling of operands</a> to
+ * limit the effects of overflows and underflows in the computation.
+ *
+ * <p>{@code Infinite} and {@code NaN} values are handled according to the following rules,
+ * applied in the order presented:
+ *
+ * <ul>
+ * <li>If either {@code this} or {@code divisor} has a {@code NaN} value in either part,
+ * {@link #NaN} is returned.
+ * <li>If {@code divisor} equals {@link #ZERO}, {@link #NaN} is returned.
+ * <li>If {@code this} and {@code divisor} are both infinite, {@link #NaN} is returned.
+ * <li>If {@code this} is finite (i.e., has no {@code Infinite} or {@code NaN} parts) and
+ * {@code divisor} is infinite (one or both parts infinite), {@link #ZERO} is returned.
+ * <li>If {@code this} is infinite and {@code divisor} is finite, {@code NaN} values are
+ * returned in the parts of the result if the {@link java.lang.Double} rules applied to
+ * the definitional formula force {@code NaN} results.
+ * </ul>
+ *
+ * @param divisor Value by which this {@code Complex} is to be divided.
+ * @return {@code this / divisor}.
+ * @throws NullArgumentException if {@code divisor} is {@code null}.
+ */
+ public Complex divide(Complex divisor) throws NullArgumentException {
+ MathUtils.checkNotNull(divisor);
+ if (isNaN || divisor.isNaN) {
+ return NaN;
+ }
+
+ final double c = divisor.getReal();
+ final double d = divisor.getImaginary();
+ if (c == 0.0 && d == 0.0) {
+ return NaN;
+ }
+
+ if (divisor.isInfinite() && !isInfinite()) {
+ return ZERO;
+ }
+
+ if (FastMath.abs(c) < FastMath.abs(d)) {
+ double q = c / d;
+ double denominator = c * q + d;
+ return createComplex(
+ (real * q + imaginary) / denominator, (imaginary * q - real) / denominator);
+ } else {
+ double q = d / c;
+ double denominator = d * q + c;
+ return createComplex(
+ (imaginary * q + real) / denominator, (imaginary - real * q) / denominator);
+ }
+ }
+
+ /**
+ * Returns a {@code Complex} whose value is {@code (this / divisor)}, with {@code divisor}
+ * interpreted as a real number.
+ *
+ * @param divisor Value by which this {@code Complex} is to be divided.
+ * @return {@code this / divisor}.
+ * @see #divide(Complex)
+ */
+ public Complex divide(double divisor) {
+ if (isNaN || Double.isNaN(divisor)) {
+ return NaN;
+ }
+ if (divisor == 0d) {
+ return NaN;
+ }
+ if (Double.isInfinite(divisor)) {
+ return !isInfinite() ? ZERO : NaN;
+ }
+ return createComplex(real / divisor, imaginary / divisor);
+ }
+
+ /** {@inheritDoc} */
+ public Complex reciprocal() {
+ if (isNaN) {
+ return NaN;
+ }
+
+ if (real == 0.0 && imaginary == 0.0) {
+ return INF;
+ }
+
+ if (isInfinite) {
+ return ZERO;
+ }
+
+ if (FastMath.abs(real) < FastMath.abs(imaginary)) {
+ double q = real / imaginary;
+ double scale = 1. / (real * q + imaginary);
+ return createComplex(scale * q, -scale);
+ } else {
+ double q = imaginary / real;
+ double scale = 1. / (imaginary * q + real);
+ return createComplex(scale, -scale * q);
+ }
+ }
+
+ /**
+ * Test for equality with another object. If both the real and imaginary parts of two complex
+ * numbers are exactly the same, and neither is {@code Double.NaN}, the two Complex objects are
+ * considered to be equal. The behavior is the same as for JDK's {@link Double#equals(Object)
+ * Double}:
+ *
+ * <ul>
+ * <li>All {@code NaN} values are considered to be equal, i.e, if either (or both) real and
+ * imaginary parts of the complex number are equal to {@code Double.NaN}, the complex
+ * number is equal to {@code NaN}.
+ * <li>Instances constructed with different representations of zero (i.e. either "0" or "-0")
+ * are <em>not</em> considered to be equal.
+ * </ul>
+ *
+ * @param other Object to test for equality with this instance.
+ * @return {@code true} if the objects are equal, {@code false} if object is {@code null}, not
+ * an instance of {@code Complex}, or not equal to this instance.
+ */
+ @Override
+ public boolean equals(Object other) {
+ if (this == other) {
+ return true;
+ }
+ if (other instanceof Complex) {
+ Complex c = (Complex) other;
+ if (c.isNaN) {
+ return isNaN;
+ } else {
+ return MathUtils.equals(real, c.real) && MathUtils.equals(imaginary, c.imaginary);
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Test for the floating-point equality between Complex objects. It returns {@code true} if both
+ * arguments are equal or within the range of allowed error (inclusive).
+ *
+ * @param x First value (cannot be {@code null}).
+ * @param y Second value (cannot be {@code null}).
+ * @param maxUlps {@code (maxUlps - 1)} is the number of floating point values between the real
+ * (resp. imaginary) parts of {@code x} and {@code y}.
+ * @return {@code true} if there are fewer than {@code maxUlps} floating point values between
+ * the real (resp. imaginary) parts of {@code x} and {@code y}.
+ * @see Precision#equals(double,double,int)
+ * @since 3.3
+ */
+ public static boolean equals(Complex x, Complex y, int maxUlps) {
+ return Precision.equals(x.real, y.real, maxUlps)
+ && Precision.equals(x.imaginary, y.imaginary, maxUlps);
+ }
+
+ /**
+ * Returns {@code true} iff the values are equal as defined by {@link
+ * #equals(Complex,Complex,int) equals(x, y, 1)}.
+ *
+ * @param x First value (cannot be {@code null}).
+ * @param y Second value (cannot be {@code null}).
+ * @return {@code true} if the values are equal.
+ * @since 3.3
+ */
+ public static boolean equals(Complex x, Complex y) {
+ return equals(x, y, 1);
+ }
+
+ /**
+ * Returns {@code true} if, both for the real part and for the imaginary part, there is no
+ * double value strictly between the arguments or the difference between them is within the
+ * range of allowed error (inclusive). Returns {@code false} if either of the arguments is NaN.
+ *
+ * @param x First value (cannot be {@code null}).
+ * @param y Second value (cannot be {@code null}).
+ * @param eps Amount of allowed absolute error.
+ * @return {@code true} if the values are two adjacent floating point numbers or they are within
+ * range of each other.
+ * @see Precision#equals(double,double,double)
+ * @since 3.3
+ */
+ public static boolean equals(Complex x, Complex y, double eps) {
+ return Precision.equals(x.real, y.real, eps)
+ && Precision.equals(x.imaginary, y.imaginary, eps);
+ }
+
+ /**
+ * Returns {@code true} if, both for the real part and for the imaginary part, there is no
+ * double value strictly between the arguments or the relative difference between them is
+ * smaller or equal to the given tolerance. Returns {@code false} if either of the arguments is
+ * NaN.
+ *
+ * @param x First value (cannot be {@code null}).
+ * @param y Second value (cannot be {@code null}).
+ * @param eps Amount of allowed relative error.
+ * @return {@code true} if the values are two adjacent floating point numbers or they are within
+ * range of each other.
+ * @see Precision#equalsWithRelativeTolerance(double,double,double)
+ * @since 3.3
+ */
+ public static boolean equalsWithRelativeTolerance(Complex x, Complex y, double eps) {
+ return Precision.equalsWithRelativeTolerance(x.real, y.real, eps)
+ && Precision.equalsWithRelativeTolerance(x.imaginary, y.imaginary, eps);
+ }
+
+ /**
+ * Get a hashCode for the complex number. Any {@code Double.NaN} value in real or imaginary part
+ * produces the same hash code {@code 7}.
+ *
+ * @return a hash code value for this object.
+ */
+ @Override
+ public int hashCode() {
+ if (isNaN) {
+ return 7;
+ }
+ return 37 * (17 * MathUtils.hash(imaginary) + MathUtils.hash(real));
+ }
+
+ /**
+ * Access the imaginary part.
+ *
+ * @return the imaginary part.
+ */
+ public double getImaginary() {
+ return imaginary;
+ }
+
+ /**
+ * Access the real part.
+ *
+ * @return the real part.
+ */
+ public double getReal() {
+ return real;
+ }
+
+ /**
+ * Checks whether either or both parts of this complex number is {@code NaN}.
+ *
+ * @return true if either or both parts of this complex number is {@code NaN}; false otherwise.
+ */
+ public boolean isNaN() {
+ return isNaN;
+ }
+
+ /**
+ * Checks whether either the real or imaginary part of this complex number takes an infinite
+ * value (either {@code Double.POSITIVE_INFINITY} or {@code Double.NEGATIVE_INFINITY}) and
+ * neither part is {@code NaN}.
+ *
+ * @return true if one or both parts of this complex number are infinite and neither part is
+ * {@code NaN}.
+ */
+ public boolean isInfinite() {
+ return isInfinite;
+ }
+
+ /**
+ * Returns a {@code Complex} whose value is {@code this * factor}. Implements preliminary checks
+ * for {@code NaN} and infinity followed by the definitional formula:
+ *
+ * <p>{@code (a + bi)(c + di) = (ac - bd) + (ad + bc)i} Returns {@link #NaN} if either {@code
+ * this} or {@code factor} has one or more {@code NaN} parts.
+ *
+ * <p>Returns {@link #INF} if neither {@code this} nor {@code factor} has one or more {@code
+ * NaN} parts and if either {@code this} or {@code factor} has one or more infinite parts (same
+ * result is returned regardless of the sign of the components).
+ *
+ * <p>Returns finite values in components of the result per the definitional formula in all
+ * remaining cases.
+ *
+ * @param factor value to be multiplied by this {@code Complex}.
+ * @return {@code this * factor}.
+ * @throws NullArgumentException if {@code factor} is {@code null}.
+ */
+ public Complex multiply(Complex factor) throws NullArgumentException {
+ MathUtils.checkNotNull(factor);
+ if (isNaN || factor.isNaN) {
+ return NaN;
+ }
+ if (Double.isInfinite(real)
+ || Double.isInfinite(imaginary)
+ || Double.isInfinite(factor.real)
+ || Double.isInfinite(factor.imaginary)) {
+ // we don't use isInfinite() to avoid testing for NaN again
+ return INF;
+ }
+ return createComplex(
+ real * factor.real - imaginary * factor.imaginary,
+ real * factor.imaginary + imaginary * factor.real);
+ }
+
+ /**
+ * Returns a {@code Complex} whose value is {@code this * factor}, with {@code factor}
+ * interpreted as a integer number.
+ *
+ * @param factor value to be multiplied by this {@code Complex}.
+ * @return {@code this * factor}.
+ * @see #multiply(Complex)
+ */
+ public Complex multiply(final int factor) {
+ if (isNaN) {
+ return NaN;
+ }
+ if (Double.isInfinite(real) || Double.isInfinite(imaginary)) {
+ return INF;
+ }
+ return createComplex(real * factor, imaginary * factor);
+ }
+
+ /**
+ * Returns a {@code Complex} whose value is {@code this * factor}, with {@code factor}
+ * interpreted as a real number.
+ *
+ * @param factor value to be multiplied by this {@code Complex}.
+ * @return {@code this * factor}.
+ * @see #multiply(Complex)
+ */
+ public Complex multiply(double factor) {
+ if (isNaN || Double.isNaN(factor)) {
+ return NaN;
+ }
+ if (Double.isInfinite(real) || Double.isInfinite(imaginary) || Double.isInfinite(factor)) {
+ // we don't use isInfinite() to avoid testing for NaN again
+ return INF;
+ }
+ return createComplex(real * factor, imaginary * factor);
+ }
+
+ /**
+ * Returns a {@code Complex} whose value is {@code (-this)}. Returns {@code NaN} if either real
+ * or imaginary part of this Complex number is {@code Double.NaN}.
+ *
+ * @return {@code -this}.
+ */
+ public Complex negate() {
+ if (isNaN) {
+ return NaN;
+ }
+
+ return createComplex(-real, -imaginary);
+ }
+
+ /**
+ * Returns a {@code Complex} whose value is {@code (this - subtrahend)}. Uses the definitional
+ * formula
+ *
+ * <p>{@code (a + bi) - (c + di) = (a-c) + (b-d)i} If either {@code this} or {@code subtrahend}
+ * has a {@code NaN]} value in either part, {@link #NaN} is returned; otherwise infinite and
+ * {@code NaN} values are returned in the parts of the result according to the rules for {@link
+ * java.lang.Double} arithmetic.
+ *
+ * @param subtrahend value to be subtracted from this {@code Complex}.
+ * @return {@code this - subtrahend}.
+ * @throws NullArgumentException if {@code subtrahend} is {@code null}.
+ */
+ public Complex subtract(Complex subtrahend) throws NullArgumentException {
+ MathUtils.checkNotNull(subtrahend);
+ if (isNaN || subtrahend.isNaN) {
+ return NaN;
+ }
+
+ return createComplex(real - subtrahend.getReal(), imaginary - subtrahend.getImaginary());
+ }
+
+ /**
+ * Returns a {@code Complex} whose value is {@code (this - subtrahend)}.
+ *
+ * @param subtrahend value to be subtracted from this {@code Complex}.
+ * @return {@code this - subtrahend}.
+ * @see #subtract(Complex)
+ */
+ public Complex subtract(double subtrahend) {
+ if (isNaN || Double.isNaN(subtrahend)) {
+ return NaN;
+ }
+ return createComplex(real - subtrahend, imaginary);
+ }
+
+ /**
+ * Compute the <a href="http://mathworld.wolfram.com/InverseCosine.html" TARGET="_top"> inverse
+ * cosine</a> of this complex number. Implements the formula:
+ *
+ * <p>{@code acos(z) = -i (log(z + i (sqrt(1 - z<sup>2</sup>))))} Returns {@link Complex#NaN} if
+ * either real or imaginary part of the input argument is {@code NaN} or infinite.
+ *
+ * @return the inverse cosine of this complex number.
+ * @since 1.2
+ */
+ public Complex acos() {
+ if (isNaN) {
+ return NaN;
+ }
+
+ return this.add(this.sqrt1z().multiply(I)).log().multiply(I.negate());
+ }
+
+ /**
+ * Compute the <a href="http://mathworld.wolfram.com/InverseSine.html" TARGET="_top"> inverse
+ * sine</a> of this complex number. Implements the formula:
+ *
+ * <p>{@code asin(z) = -i (log(sqrt(1 - z<sup>2</sup>) + iz))}
+ *
+ * <p>Returns {@link Complex#NaN} if either real or imaginary part of the input argument is
+ * {@code NaN} or infinite.
+ *
+ * @return the inverse sine of this complex number.
+ * @since 1.2
+ */
+ public Complex asin() {
+ if (isNaN) {
+ return NaN;
+ }
+
+ return sqrt1z().add(this.multiply(I)).log().multiply(I.negate());
+ }
+
+ /**
+ * Compute the <a href="http://mathworld.wolfram.com/InverseTangent.html" TARGET="_top"> inverse
+ * tangent</a> of this complex number. Implements the formula:
+ *
+ * <p>{@code atan(z) = (i/2) log((i + z)/(i - z))}
+ *
+ * <p>Returns {@link Complex#NaN} if either real or imaginary part of the input argument is
+ * {@code NaN} or infinite.
+ *
+ * @return the inverse tangent of this complex number
+ * @since 1.2
+ */
+ public Complex atan() {
+ if (isNaN) {
+ return NaN;
+ }
+
+ return this.add(I)
+ .divide(I.subtract(this))
+ .log()
+ .multiply(I.divide(createComplex(2.0, 0.0)));
+ }
+
+ /**
+ * Compute the <a href="http://mathworld.wolfram.com/Cosine.html" TARGET="_top"> cosine</a> of
+ * this complex number. Implements the formula:
+ *
+ * <p>{@code cos(a + bi) = cos(a)cosh(b) - sin(a)sinh(b)i}
+ *
+ * <p>where the (real) functions on the right-hand side are {@link FastMath#sin}, {@link
+ * FastMath#cos}, {@link FastMath#cosh} and {@link FastMath#sinh}.
+ *
+ * <p>Returns {@link Complex#NaN} if either real or imaginary part of the input argument is
+ * {@code NaN}.
+ *
+ * <p>Infinite values in real or imaginary parts of the input may result in infinite or NaN
+ * values returned in parts of the result.
+ *
+ * <pre>
+ * Examples:
+ * <code>
+ * cos(1 &plusmn; INFINITY i) = 1 \u2213 INFINITY i
+ * cos(&plusmn;INFINITY + i) = NaN + NaN i
+ * cos(&plusmn;INFINITY &plusmn; INFINITY i) = NaN + NaN i
+ * </code>
+ * </pre>
+ *
+ * @return the cosine of this complex number.
+ * @since 1.2
+ */
+ public Complex cos() {
+ if (isNaN) {
+ return NaN;
+ }
+
+ return createComplex(
+ FastMath.cos(real) * FastMath.cosh(imaginary),
+ -FastMath.sin(real) * FastMath.sinh(imaginary));
+ }
+
+ /**
+ * Compute the <a href="http://mathworld.wolfram.com/HyperbolicCosine.html" TARGET="_top">
+ * hyperbolic cosine</a> of this complex number. Implements the formula:
+ *
+ * <pre>
+ * <code>
+ * cosh(a + bi) = cosh(a)cos(b) + sinh(a)sin(b)i
+ * </code>
+ * </pre>
+ *
+ * where the (real) functions on the right-hand side are {@link FastMath#sin}, {@link
+ * FastMath#cos}, {@link FastMath#cosh} and {@link FastMath#sinh}.
+ *
+ * <p>Returns {@link Complex#NaN} if either real or imaginary part of the input argument is
+ * {@code NaN}. Infinite values in real or imaginary parts of the input may result in infinite
+ * or NaN values returned in parts of the result.
+ *
+ * <pre>
+ * Examples:
+ * <code>
+ * cosh(1 &plusmn; INFINITY i) = NaN + NaN i
+ * cosh(&plusmn;INFINITY + i) = INFINITY &plusmn; INFINITY i
+ * cosh(&plusmn;INFINITY &plusmn; INFINITY i) = NaN + NaN i
+ * </code>
+ * </pre>
+ *
+ * @return the hyperbolic cosine of this complex number.
+ * @since 1.2
+ */
+ public Complex cosh() {
+ if (isNaN) {
+ return NaN;
+ }
+
+ return createComplex(
+ FastMath.cosh(real) * FastMath.cos(imaginary),
+ FastMath.sinh(real) * FastMath.sin(imaginary));
+ }
+
+ /**
+ * Compute the <a href="http://mathworld.wolfram.com/ExponentialFunction.html" TARGET="_top">
+ * exponential function</a> of this complex number. Implements the formula:
+ *
+ * <pre>
+ * <code>
+ * exp(a + bi) = exp(a)cos(b) + exp(a)sin(b)i
+ * </code>
+ * </pre>
+ *
+ * where the (real) functions on the right-hand side are {@link FastMath#exp}, {@link
+ * FastMath#cos}, and {@link FastMath#sin}.
+ *
+ * <p>Returns {@link Complex#NaN} if either real or imaginary part of the input argument is
+ * {@code NaN}. Infinite values in real or imaginary parts of the input may result in infinite
+ * or NaN values returned in parts of the result.
+ *
+ * <pre>
+ * Examples:
+ * <code>
+ * exp(1 &plusmn; INFINITY i) = NaN + NaN i
+ * exp(INFINITY + i) = INFINITY + INFINITY i
+ * exp(-INFINITY + i) = 0 + 0i
+ * exp(&plusmn;INFINITY &plusmn; INFINITY i) = NaN + NaN i
+ * </code>
+ * </pre>
+ *
+ * @return <code><i>e</i><sup>this</sup></code>.
+ * @since 1.2
+ */
+ public Complex exp() {
+ if (isNaN) {
+ return NaN;
+ }
+
+ double expReal = FastMath.exp(real);
+ return createComplex(expReal * FastMath.cos(imaginary), expReal * FastMath.sin(imaginary));
+ }
+
+ /**
+ * Compute the <a href="http://mathworld.wolfram.com/NaturalLogarithm.html" TARGET="_top">
+ * natural logarithm</a> of this complex number. Implements the formula:
+ *
+ * <pre>
+ * <code>
+ * log(a + bi) = ln(|a + bi|) + arg(a + bi)i
+ * </code>
+ * </pre>
+ *
+ * where ln on the right hand side is {@link FastMath#log}, {@code |a + bi|} is the modulus,
+ * {@link Complex#abs}, and {@code arg(a + bi) = }{@link FastMath#atan2}(b, a).
+ *
+ * <p>Returns {@link Complex#NaN} if either real or imaginary part of the input argument is
+ * {@code NaN}. Infinite (or critical) values in real or imaginary parts of the input may result
+ * in infinite or NaN values returned in parts of the result.
+ *
+ * <pre>
+ * Examples:
+ * <code>
+ * log(1 &plusmn; INFINITY i) = INFINITY &plusmn; (&pi;/2)i
+ * log(INFINITY + i) = INFINITY + 0i
+ * log(-INFINITY + i) = INFINITY + &pi;i
+ * log(INFINITY &plusmn; INFINITY i) = INFINITY &plusmn; (&pi;/4)i
+ * log(-INFINITY &plusmn; INFINITY i) = INFINITY &plusmn; (3&pi;/4)i
+ * log(0 + 0i) = -INFINITY + 0i
+ * </code>
+ * </pre>
+ *
+ * @return the value <code>ln &nbsp; this</code>, the natural logarithm of {@code this}.
+ * @since 1.2
+ */
+ public Complex log() {
+ if (isNaN) {
+ return NaN;
+ }
+
+ return createComplex(FastMath.log(abs()), FastMath.atan2(imaginary, real));
+ }
+
+ /**
+ * Returns of value of this complex number raised to the power of {@code x}. Implements the
+ * formula:
+ *
+ * <pre>
+ * <code>
+ * y<sup>x</sup> = exp(x&middot;log(y))
+ * </code>
+ * </pre>
+ *
+ * where {@code exp} and {@code log} are {@link #exp} and {@link #log}, respectively.
+ *
+ * <p>Returns {@link Complex#NaN} if either real or imaginary part of the input argument is
+ * {@code NaN} or infinite, or if {@code y} equals {@link Complex#ZERO}.
+ *
+ * @param x exponent to which this {@code Complex} is to be raised.
+ * @return <code> this<sup>x</sup></code>.
+ * @throws NullArgumentException if x is {@code null}.
+ * @since 1.2
+ */
+ public Complex pow(Complex x) throws NullArgumentException {
+ MathUtils.checkNotNull(x);
+ return this.log().multiply(x).exp();
+ }
+
+ /**
+ * Returns of value of this complex number raised to the power of {@code x}.
+ *
+ * @param x exponent to which this {@code Complex} is to be raised.
+ * @return <code>this<sup>x</sup></code>.
+ * @see #pow(Complex)
+ */
+ public Complex pow(double x) {
+ return this.log().multiply(x).exp();
+ }
+
+ /**
+ * Compute the <a href="http://mathworld.wolfram.com/Sine.html" TARGET="_top"> sine</a> of this
+ * complex number. Implements the formula:
+ *
+ * <pre>
+ * <code>
+ * sin(a + bi) = sin(a)cosh(b) - cos(a)sinh(b)i
+ * </code>
+ * </pre>
+ *
+ * where the (real) functions on the right-hand side are {@link FastMath#sin}, {@link
+ * FastMath#cos}, {@link FastMath#cosh} and {@link FastMath#sinh}.
+ *
+ * <p>Returns {@link Complex#NaN} if either real or imaginary part of the input argument is
+ * {@code NaN}.
+ *
+ * <p>Infinite values in real or imaginary parts of the input may result in infinite or {@code
+ * NaN} values returned in parts of the result.
+ *
+ * <pre>
+ * Examples:
+ * <code>
+ * sin(1 &plusmn; INFINITY i) = 1 &plusmn; INFINITY i
+ * sin(&plusmn;INFINITY + i) = NaN + NaN i
+ * sin(&plusmn;INFINITY &plusmn; INFINITY i) = NaN + NaN i
+ * </code>
+ * </pre>
+ *
+ * @return the sine of this complex number.
+ * @since 1.2
+ */
+ public Complex sin() {
+ if (isNaN) {
+ return NaN;
+ }
+
+ return createComplex(
+ FastMath.sin(real) * FastMath.cosh(imaginary),
+ FastMath.cos(real) * FastMath.sinh(imaginary));
+ }
+
+ /**
+ * Compute the <a href="http://mathworld.wolfram.com/HyperbolicSine.html" TARGET="_top">
+ * hyperbolic sine</a> of this complex number. Implements the formula:
+ *
+ * <pre>
+ * <code>
+ * sinh(a + bi) = sinh(a)cos(b)) + cosh(a)sin(b)i
+ * </code>
+ * </pre>
+ *
+ * where the (real) functions on the right-hand side are {@link FastMath#sin}, {@link
+ * FastMath#cos}, {@link FastMath#cosh} and {@link FastMath#sinh}.
+ *
+ * <p>Returns {@link Complex#NaN} if either real or imaginary part of the input argument is
+ * {@code NaN}.
+ *
+ * <p>Infinite values in real or imaginary parts of the input may result in infinite or NaN
+ * values returned in parts of the result.
+ *
+ * <pre>
+ * Examples:
+ * <code>
+ * sinh(1 &plusmn; INFINITY i) = NaN + NaN i
+ * sinh(&plusmn;INFINITY + i) = &plusmn; INFINITY + INFINITY i
+ * sinh(&plusmn;INFINITY &plusmn; INFINITY i) = NaN + NaN i
+ * </code>
+ * </pre>
+ *
+ * @return the hyperbolic sine of {@code this}.
+ * @since 1.2
+ */
+ public Complex sinh() {
+ if (isNaN) {
+ return NaN;
+ }
+
+ return createComplex(
+ FastMath.sinh(real) * FastMath.cos(imaginary),
+ FastMath.cosh(real) * FastMath.sin(imaginary));
+ }
+
+ /**
+ * Compute the <a href="http://mathworld.wolfram.com/SquareRoot.html" TARGET="_top"> square
+ * root</a> of this complex number. Implements the following algorithm to compute {@code sqrt(a
+ * + bi)}:
+ *
+ * <ol>
+ * <li>Let {@code t = sqrt((|a| + |a + bi|) / 2)}
+ * <li>
+ * <pre>if {@code a &#8805; 0} return {@code t + (b/2t)i}
+ * else return {@code |b|/2t + sign(b)t i }</pre>
+ * </ol>
+ *
+ * where
+ *
+ * <ul>
+ * <li>{@code |a| = }{@link FastMath#abs}(a)
+ * <li>{@code |a + bi| = }{@link Complex#abs}(a + bi)
+ * <li>{@code sign(b) = }{@link FastMath#copySign(double,double) copySign(1d, b)}
+ * </ul>
+ *
+ * <p>Returns {@link Complex#NaN} if either real or imaginary part of the input argument is
+ * {@code NaN}. Infinite values in real or imaginary parts of the input may result in infinite
+ * or NaN values returned in parts of the result.
+ *
+ * <pre>
+ * Examples:
+ * <code>
+ * sqrt(1 &plusmn; INFINITY i) = INFINITY + NaN i
+ * sqrt(INFINITY + i) = INFINITY + 0i
+ * sqrt(-INFINITY + i) = 0 + INFINITY i
+ * sqrt(INFINITY &plusmn; INFINITY i) = INFINITY + NaN i
+ * sqrt(-INFINITY &plusmn; INFINITY i) = NaN &plusmn; INFINITY i
+ * </code>
+ * </pre>
+ *
+ * @return the square root of {@code this}.
+ * @since 1.2
+ */
+ public Complex sqrt() {
+ if (isNaN) {
+ return NaN;
+ }
+
+ if (real == 0.0 && imaginary == 0.0) {
+ return createComplex(0.0, 0.0);
+ }
+
+ double t = FastMath.sqrt((FastMath.abs(real) + abs()) / 2.0);
+ if (real >= 0.0) {
+ return createComplex(t, imaginary / (2.0 * t));
+ } else {
+ return createComplex(
+ FastMath.abs(imaginary) / (2.0 * t), FastMath.copySign(1d, imaginary) * t);
+ }
+ }
+
+ /**
+ * Compute the <a href="http://mathworld.wolfram.com/SquareRoot.html" TARGET="_top"> square
+ * root</a> of <code>1 - this<sup>2</sup></code> for this complex number. Computes the result
+ * directly as {@code sqrt(ONE.subtract(z.multiply(z)))}.
+ *
+ * <p>Returns {@link Complex#NaN} if either real or imaginary part of the input argument is
+ * {@code NaN}. Infinite values in real or imaginary parts of the input may result in infinite
+ * or NaN values returned in parts of the result.
+ *
+ * @return the square root of <code>1 - this<sup>2</sup></code>.
+ * @since 1.2
+ */
+ public Complex sqrt1z() {
+ return createComplex(1.0, 0.0).subtract(this.multiply(this)).sqrt();
+ }
+
+ /**
+ * Compute the <a href="http://mathworld.wolfram.com/Tangent.html" TARGET="_top"> tangent</a> of
+ * this complex number. Implements the formula:
+ *
+ * <pre>
+ * <code>
+ * tan(a + bi) = sin(2a)/(cos(2a)+cosh(2b)) + [sinh(2b)/(cos(2a)+cosh(2b))]i
+ * </code>
+ * </pre>
+ *
+ * where the (real) functions on the right-hand side are {@link FastMath#sin}, {@link
+ * FastMath#cos}, {@link FastMath#cosh} and {@link FastMath#sinh}.
+ *
+ * <p>Returns {@link Complex#NaN} if either real or imaginary part of the input argument is
+ * {@code NaN}. Infinite (or critical) values in real or imaginary parts of the input may result
+ * in infinite or NaN values returned in parts of the result.
+ *
+ * <pre>
+ * Examples:
+ * <code>
+ * tan(a &plusmn; INFINITY i) = 0 &plusmn; i
+ * tan(&plusmn;INFINITY + bi) = NaN + NaN i
+ * tan(&plusmn;INFINITY &plusmn; INFINITY i) = NaN + NaN i
+ * tan(&plusmn;&pi;/2 + 0 i) = &plusmn;INFINITY + NaN i
+ * </code>
+ * </pre>
+ *
+ * @return the tangent of {@code this}.
+ * @since 1.2
+ */
+ public Complex tan() {
+ if (isNaN || Double.isInfinite(real)) {
+ return NaN;
+ }
+ if (imaginary > 20.0) {
+ return createComplex(0.0, 1.0);
+ }
+ if (imaginary < -20.0) {
+ return createComplex(0.0, -1.0);
+ }
+
+ double real2 = 2.0 * real;
+ double imaginary2 = 2.0 * imaginary;
+ double d = FastMath.cos(real2) + FastMath.cosh(imaginary2);
+
+ return createComplex(FastMath.sin(real2) / d, FastMath.sinh(imaginary2) / d);
+ }
+
+ /**
+ * Compute the <a href="http://mathworld.wolfram.com/HyperbolicTangent.html" TARGET="_top">
+ * hyperbolic tangent</a> of this complex number. Implements the formula:
+ *
+ * <pre>
+ * <code>
+ * tan(a + bi) = sinh(2a)/(cosh(2a)+cos(2b)) + [sin(2b)/(cosh(2a)+cos(2b))]i
+ * </code>
+ * </pre>
+ *
+ * where the (real) functions on the right-hand side are {@link FastMath#sin}, {@link
+ * FastMath#cos}, {@link FastMath#cosh} and {@link FastMath#sinh}.
+ *
+ * <p>Returns {@link Complex#NaN} if either real or imaginary part of the input argument is
+ * {@code NaN}. Infinite values in real or imaginary parts of the input may result in infinite
+ * or NaN values returned in parts of the result.
+ *
+ * <pre>
+ * Examples:
+ * <code>
+ * tanh(a &plusmn; INFINITY i) = NaN + NaN i
+ * tanh(&plusmn;INFINITY + bi) = &plusmn;1 + 0 i
+ * tanh(&plusmn;INFINITY &plusmn; INFINITY i) = NaN + NaN i
+ * tanh(0 + (&pi;/2)i) = NaN + INFINITY i
+ * </code>
+ * </pre>
+ *
+ * @return the hyperbolic tangent of {@code this}.
+ * @since 1.2
+ */
+ public Complex tanh() {
+ if (isNaN || Double.isInfinite(imaginary)) {
+ return NaN;
+ }
+ if (real > 20.0) {
+ return createComplex(1.0, 0.0);
+ }
+ if (real < -20.0) {
+ return createComplex(-1.0, 0.0);
+ }
+ double real2 = 2.0 * real;
+ double imaginary2 = 2.0 * imaginary;
+ double d = FastMath.cosh(real2) + FastMath.cos(imaginary2);
+
+ return createComplex(FastMath.sinh(real2) / d, FastMath.sin(imaginary2) / d);
+ }
+
+ /**
+ * Compute the argument of this complex number. The argument is the angle phi between the
+ * positive real axis and the point representing this number in the complex plane. The value
+ * returned is between -PI (not inclusive) and PI (inclusive), with negative values returned for
+ * numbers with negative imaginary parts.
+ *
+ * <p>If either real or imaginary part (or both) is NaN, NaN is returned. Infinite parts are
+ * handled as {@code Math.atan2} handles them, essentially treating finite parts as zero in the
+ * presence of an infinite coordinate and returning a multiple of pi/4 depending on the signs of
+ * the infinite parts. See the javadoc for {@code Math.atan2} for full details.
+ *
+ * @return the argument of {@code this}.
+ */
+ public double getArgument() {
+ return FastMath.atan2(getImaginary(), getReal());
+ }
+
+ /**
+ * Computes the n-th roots of this complex number. The nth roots are defined by the formula:
+ *
+ * <pre>
+ * <code>
+ * z<sub>k</sub> = abs<sup>1/n</sup> (cos(phi + 2&pi;k/n) + i (sin(phi + 2&pi;k/n))
+ * </code>
+ * </pre>
+ *
+ * for <i>{@code k=0, 1, ..., n-1}</i>, where {@code abs} and {@code phi} are respectively the
+ * {@link #abs() modulus} and {@link #getArgument() argument} of this complex number.
+ *
+ * <p>If one or both parts of this complex number is NaN, a list with just one element, {@link
+ * #NaN} is returned. if neither part is NaN, but at least one part is infinite, the result is a
+ * one-element list containing {@link #INF}.
+ *
+ * @param n Degree of root.
+ * @return a List of all {@code n}-th roots of {@code this}.
+ * @throws NotPositiveException if {@code n <= 0}.
+ * @since 2.0
+ */
+ public List<Complex> nthRoot(int n) throws NotPositiveException {
+
+ if (n <= 0) {
+ throw new NotPositiveException(
+ LocalizedFormats.CANNOT_COMPUTE_NTH_ROOT_FOR_NEGATIVE_N, n);
+ }
+
+ final List<Complex> result = new ArrayList<Complex>();
+
+ if (isNaN) {
+ result.add(NaN);
+ return result;
+ }
+ if (isInfinite()) {
+ result.add(INF);
+ return result;
+ }
+
+ // nth root of abs -- faster / more accurate to use a solver here?
+ final double nthRootOfAbs = FastMath.pow(abs(), 1.0 / n);
+
+ // Compute nth roots of complex number with k = 0, 1, ... n-1
+ final double nthPhi = getArgument() / n;
+ final double slice = 2 * FastMath.PI / n;
+ double innerPart = nthPhi;
+ for (int k = 0; k < n; k++) {
+ // inner part
+ final double realPart = nthRootOfAbs * FastMath.cos(innerPart);
+ final double imaginaryPart = nthRootOfAbs * FastMath.sin(innerPart);
+ result.add(createComplex(realPart, imaginaryPart));
+ innerPart += slice;
+ }
+
+ return result;
+ }
+
+ /**
+ * Create a complex number given the real and imaginary parts.
+ *
+ * @param realPart Real part.
+ * @param imaginaryPart Imaginary part.
+ * @return a new complex number instance.
+ * @since 1.2
+ * @see #valueOf(double, double)
+ */
+ protected Complex createComplex(double realPart, double imaginaryPart) {
+ return new Complex(realPart, imaginaryPart);
+ }
+
+ /**
+ * Create a complex number given the real and imaginary parts.
+ *
+ * @param realPart Real part.
+ * @param imaginaryPart Imaginary part.
+ * @return a Complex instance.
+ */
+ public static Complex valueOf(double realPart, double imaginaryPart) {
+ if (Double.isNaN(realPart) || Double.isNaN(imaginaryPart)) {
+ return NaN;
+ }
+ return new Complex(realPart, imaginaryPart);
+ }
+
+ /**
+ * Create a complex number given only the real part.
+ *
+ * @param realPart Real part.
+ * @return a Complex instance.
+ */
+ public static Complex valueOf(double realPart) {
+ if (Double.isNaN(realPart)) {
+ return NaN;
+ }
+ return new Complex(realPart);
+ }
+
+ /**
+ * Resolve the transient fields in a deserialized Complex Object. Subclasses will need to
+ * override {@link #createComplex} to deserialize properly.
+ *
+ * @return A Complex instance with all fields resolved.
+ * @since 2.0
+ */
+ protected final Object readResolve() {
+ return createComplex(real, imaginary);
+ }
+
+ /** {@inheritDoc} */
+ public ComplexField getField() {
+ return ComplexField.getInstance();
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public String toString() {
+ return "(" + real + ", " + imaginary + ")";
+ }
+}
diff --git a/src/main/java/org/apache/commons/math3/complex/ComplexField.java b/src/main/java/org/apache/commons/math3/complex/ComplexField.java
new file mode 100644
index 0000000..4baff6f
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/complex/ComplexField.java
@@ -0,0 +1,87 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math3.complex;
+
+import org.apache.commons.math3.Field;
+import org.apache.commons.math3.FieldElement;
+
+import java.io.Serializable;
+
+/**
+ * Representation of the complex numbers field.
+ *
+ * <p>This class is a singleton.
+ *
+ * @see Complex
+ * @since 2.0
+ */
+public class ComplexField implements Field<Complex>, Serializable {
+
+ /** Serializable version identifier. */
+ private static final long serialVersionUID = -6130362688700788798L;
+
+ /** Private constructor for the singleton. */
+ private ComplexField() {}
+
+ /**
+ * Get the unique instance.
+ *
+ * @return the unique instance
+ */
+ public static ComplexField getInstance() {
+ return LazyHolder.INSTANCE;
+ }
+
+ /** {@inheritDoc} */
+ public Complex getOne() {
+ return Complex.ONE;
+ }
+
+ /** {@inheritDoc} */
+ public Complex getZero() {
+ return Complex.ZERO;
+ }
+
+ /** {@inheritDoc} */
+ public Class<? extends FieldElement<Complex>> getRuntimeClass() {
+ return Complex.class;
+ }
+
+ // CHECKSTYLE: stop HideUtilityClassConstructor
+ /**
+ * Holder for the instance.
+ *
+ * <p>We use here the Initialization On Demand Holder Idiom.
+ */
+ private static class LazyHolder {
+ /** Cached field instance. */
+ private static final ComplexField INSTANCE = new ComplexField();
+ }
+
+ // CHECKSTYLE: resume HideUtilityClassConstructor
+
+ /**
+ * Handle deserialization of the singleton.
+ *
+ * @return the singleton instance
+ */
+ private Object readResolve() {
+ // return the singleton instance
+ return LazyHolder.INSTANCE;
+ }
+}
diff --git a/src/main/java/org/apache/commons/math3/complex/ComplexFormat.java b/src/main/java/org/apache/commons/math3/complex/ComplexFormat.java
new file mode 100644
index 0000000..309326a
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/complex/ComplexFormat.java
@@ -0,0 +1,420 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math3.complex;
+
+import org.apache.commons.math3.exception.MathIllegalArgumentException;
+import org.apache.commons.math3.exception.MathParseException;
+import org.apache.commons.math3.exception.NoDataException;
+import org.apache.commons.math3.exception.NullArgumentException;
+import org.apache.commons.math3.exception.util.LocalizedFormats;
+import org.apache.commons.math3.util.CompositeFormat;
+
+import java.text.FieldPosition;
+import java.text.NumberFormat;
+import java.text.ParsePosition;
+import java.util.Locale;
+
+/**
+ * Formats a Complex number in cartesian format "Re(c) + Im(c)i". 'i' can be replaced with 'j' (or
+ * anything else), and the number format for both real and imaginary parts can be configured.
+ */
+public class ComplexFormat {
+
+ /** The default imaginary character. */
+ private static final String DEFAULT_IMAGINARY_CHARACTER = "i";
+
+ /** The notation used to signify the imaginary part of the complex number. */
+ private final String imaginaryCharacter;
+
+ /** The format used for the imaginary part. */
+ private final NumberFormat imaginaryFormat;
+
+ /** The format used for the real part. */
+ private final NumberFormat realFormat;
+
+ /**
+ * Create an instance with the default imaginary character, 'i', and the default number format
+ * for both real and imaginary parts.
+ */
+ public ComplexFormat() {
+ this.imaginaryCharacter = DEFAULT_IMAGINARY_CHARACTER;
+ this.imaginaryFormat = CompositeFormat.getDefaultNumberFormat();
+ this.realFormat = imaginaryFormat;
+ }
+
+ /**
+ * Create an instance with a custom number format for both real and imaginary parts.
+ *
+ * @param format the custom format for both real and imaginary parts.
+ * @throws NullArgumentException if {@code realFormat} is {@code null}.
+ */
+ public ComplexFormat(NumberFormat format) throws NullArgumentException {
+ if (format == null) {
+ throw new NullArgumentException(LocalizedFormats.IMAGINARY_FORMAT);
+ }
+ this.imaginaryCharacter = DEFAULT_IMAGINARY_CHARACTER;
+ this.imaginaryFormat = format;
+ this.realFormat = format;
+ }
+
+ /**
+ * Create an instance with a custom number format for the real part and a custom number format
+ * for the imaginary part.
+ *
+ * @param realFormat the custom format for the real part.
+ * @param imaginaryFormat the custom format for the imaginary part.
+ * @throws NullArgumentException if {@code imaginaryFormat} is {@code null}.
+ * @throws NullArgumentException if {@code realFormat} is {@code null}.
+ */
+ public ComplexFormat(NumberFormat realFormat, NumberFormat imaginaryFormat)
+ throws NullArgumentException {
+ if (imaginaryFormat == null) {
+ throw new NullArgumentException(LocalizedFormats.IMAGINARY_FORMAT);
+ }
+ if (realFormat == null) {
+ throw new NullArgumentException(LocalizedFormats.REAL_FORMAT);
+ }
+
+ this.imaginaryCharacter = DEFAULT_IMAGINARY_CHARACTER;
+ this.imaginaryFormat = imaginaryFormat;
+ this.realFormat = realFormat;
+ }
+
+ /**
+ * Create an instance with a custom imaginary character, and the default number format for both
+ * real and imaginary parts.
+ *
+ * @param imaginaryCharacter The custom imaginary character.
+ * @throws NullArgumentException if {@code imaginaryCharacter} is {@code null}.
+ * @throws NoDataException if {@code imaginaryCharacter} is an empty string.
+ */
+ public ComplexFormat(String imaginaryCharacter) throws NullArgumentException, NoDataException {
+ this(imaginaryCharacter, CompositeFormat.getDefaultNumberFormat());
+ }
+
+ /**
+ * Create an instance with a custom imaginary character, and a custom number format for both
+ * real and imaginary parts.
+ *
+ * @param imaginaryCharacter The custom imaginary character.
+ * @param format the custom format for both real and imaginary parts.
+ * @throws NullArgumentException if {@code imaginaryCharacter} is {@code null}.
+ * @throws NoDataException if {@code imaginaryCharacter} is an empty string.
+ * @throws NullArgumentException if {@code format} is {@code null}.
+ */
+ public ComplexFormat(String imaginaryCharacter, NumberFormat format)
+ throws NullArgumentException, NoDataException {
+ this(imaginaryCharacter, format, format);
+ }
+
+ /**
+ * Create an instance with a custom imaginary character, a custom number format for the real
+ * part, and a custom number format for the imaginary part.
+ *
+ * @param imaginaryCharacter The custom imaginary character.
+ * @param realFormat the custom format for the real part.
+ * @param imaginaryFormat the custom format for the imaginary part.
+ * @throws NullArgumentException if {@code imaginaryCharacter} is {@code null}.
+ * @throws NoDataException if {@code imaginaryCharacter} is an empty string.
+ * @throws NullArgumentException if {@code imaginaryFormat} is {@code null}.
+ * @throws NullArgumentException if {@code realFormat} is {@code null}.
+ */
+ public ComplexFormat(
+ String imaginaryCharacter, NumberFormat realFormat, NumberFormat imaginaryFormat)
+ throws NullArgumentException, NoDataException {
+ if (imaginaryCharacter == null) {
+ throw new NullArgumentException();
+ }
+ if (imaginaryCharacter.length() == 0) {
+ throw new NoDataException();
+ }
+ if (imaginaryFormat == null) {
+ throw new NullArgumentException(LocalizedFormats.IMAGINARY_FORMAT);
+ }
+ if (realFormat == null) {
+ throw new NullArgumentException(LocalizedFormats.REAL_FORMAT);
+ }
+
+ this.imaginaryCharacter = imaginaryCharacter;
+ this.imaginaryFormat = imaginaryFormat;
+ this.realFormat = realFormat;
+ }
+
+ /**
+ * Get the set of locales for which complex formats are available.
+ *
+ * <p>This is the same set as the {@link NumberFormat} set.
+ *
+ * @return available complex format locales.
+ */
+ public static Locale[] getAvailableLocales() {
+ return NumberFormat.getAvailableLocales();
+ }
+
+ /**
+ * This method calls {@link #format(Object,StringBuffer,FieldPosition)}.
+ *
+ * @param c Complex object to format.
+ * @return A formatted number in the form "Re(c) + Im(c)i".
+ */
+ public String format(Complex c) {
+ return format(c, new StringBuffer(), new FieldPosition(0)).toString();
+ }
+
+ /**
+ * This method calls {@link #format(Object,StringBuffer,FieldPosition)}.
+ *
+ * @param c Double object to format.
+ * @return A formatted number.
+ */
+ public String format(Double c) {
+ return format(new Complex(c, 0), new StringBuffer(), new FieldPosition(0)).toString();
+ }
+
+ /**
+ * Formats a {@link Complex} object to produce a string.
+ *
+ * @param complex the object to format.
+ * @param toAppendTo where the text is to be appended
+ * @param pos On input: an alignment field, if desired. On output: the offsets of the alignment
+ * field
+ * @return the value passed in as toAppendTo.
+ */
+ public StringBuffer format(Complex complex, StringBuffer toAppendTo, FieldPosition pos) {
+ pos.setBeginIndex(0);
+ pos.setEndIndex(0);
+
+ // format real
+ double re = complex.getReal();
+ CompositeFormat.formatDouble(re, getRealFormat(), toAppendTo, pos);
+
+ // format sign and imaginary
+ double im = complex.getImaginary();
+ StringBuffer imAppendTo;
+ if (im < 0.0) {
+ toAppendTo.append(" - ");
+ imAppendTo = formatImaginary(-im, new StringBuffer(), pos);
+ toAppendTo.append(imAppendTo);
+ toAppendTo.append(getImaginaryCharacter());
+ } else if (im > 0.0 || Double.isNaN(im)) {
+ toAppendTo.append(" + ");
+ imAppendTo = formatImaginary(im, new StringBuffer(), pos);
+ toAppendTo.append(imAppendTo);
+ toAppendTo.append(getImaginaryCharacter());
+ }
+
+ return toAppendTo;
+ }
+
+ /**
+ * Format the absolute value of the imaginary part.
+ *
+ * @param absIm Absolute value of the imaginary part of a complex number.
+ * @param toAppendTo where the text is to be appended.
+ * @param pos On input: an alignment field, if desired. On output: the offsets of the alignment
+ * field.
+ * @return the value passed in as toAppendTo.
+ */
+ private StringBuffer formatImaginary(double absIm, StringBuffer toAppendTo, FieldPosition pos) {
+ pos.setBeginIndex(0);
+ pos.setEndIndex(0);
+
+ CompositeFormat.formatDouble(absIm, getImaginaryFormat(), toAppendTo, pos);
+ if (toAppendTo.toString().equals("1")) {
+ // Remove the character "1" if it is the only one.
+ toAppendTo.setLength(0);
+ }
+
+ return toAppendTo;
+ }
+
+ /**
+ * Formats a object to produce a string. {@code obj} must be either a {@link Complex} object or
+ * a {@link Number} object. Any other type of object will result in an {@link
+ * IllegalArgumentException} being thrown.
+ *
+ * @param obj the object to format.
+ * @param toAppendTo where the text is to be appended
+ * @param pos On input: an alignment field, if desired. On output: the offsets of the alignment
+ * field
+ * @return the value passed in as toAppendTo.
+ * @see java.text.Format#format(java.lang.Object, java.lang.StringBuffer,
+ * java.text.FieldPosition)
+ * @throws MathIllegalArgumentException is {@code obj} is not a valid type.
+ */
+ public StringBuffer format(Object obj, StringBuffer toAppendTo, FieldPosition pos)
+ throws MathIllegalArgumentException {
+
+ StringBuffer ret = null;
+
+ if (obj instanceof Complex) {
+ ret = format((Complex) obj, toAppendTo, pos);
+ } else if (obj instanceof Number) {
+ ret = format(new Complex(((Number) obj).doubleValue(), 0.0), toAppendTo, pos);
+ } else {
+ throw new MathIllegalArgumentException(
+ LocalizedFormats.CANNOT_FORMAT_INSTANCE_AS_COMPLEX, obj.getClass().getName());
+ }
+
+ return ret;
+ }
+
+ /**
+ * Access the imaginaryCharacter.
+ *
+ * @return the imaginaryCharacter.
+ */
+ public String getImaginaryCharacter() {
+ return imaginaryCharacter;
+ }
+
+ /**
+ * Access the imaginaryFormat.
+ *
+ * @return the imaginaryFormat.
+ */
+ public NumberFormat getImaginaryFormat() {
+ return imaginaryFormat;
+ }
+
+ /**
+ * Returns the default complex format for the current locale.
+ *
+ * @return the default complex format.
+ */
+ public static ComplexFormat getInstance() {
+ return getInstance(Locale.getDefault());
+ }
+
+ /**
+ * Returns the default complex format for the given locale.
+ *
+ * @param locale the specific locale used by the format.
+ * @return the complex format specific to the given locale.
+ */
+ public static ComplexFormat getInstance(Locale locale) {
+ NumberFormat f = CompositeFormat.getDefaultNumberFormat(locale);
+ return new ComplexFormat(f);
+ }
+
+ /**
+ * Returns the default complex format for the given locale.
+ *
+ * @param locale the specific locale used by the format.
+ * @param imaginaryCharacter Imaginary character.
+ * @return the complex format specific to the given locale.
+ * @throws NullArgumentException if {@code imaginaryCharacter} is {@code null}.
+ * @throws NoDataException if {@code imaginaryCharacter} is an empty string.
+ */
+ public static ComplexFormat getInstance(String imaginaryCharacter, Locale locale)
+ throws NullArgumentException, NoDataException {
+ NumberFormat f = CompositeFormat.getDefaultNumberFormat(locale);
+ return new ComplexFormat(imaginaryCharacter, f);
+ }
+
+ /**
+ * Access the realFormat.
+ *
+ * @return the realFormat.
+ */
+ public NumberFormat getRealFormat() {
+ return realFormat;
+ }
+
+ /**
+ * Parses a string to produce a {@link Complex} object.
+ *
+ * @param source the string to parse.
+ * @return the parsed {@link Complex} object.
+ * @throws MathParseException if the beginning of the specified string cannot be parsed.
+ */
+ public Complex parse(String source) throws MathParseException {
+ ParsePosition parsePosition = new ParsePosition(0);
+ Complex result = parse(source, parsePosition);
+ if (parsePosition.getIndex() == 0) {
+ throw new MathParseException(source, parsePosition.getErrorIndex(), Complex.class);
+ }
+ return result;
+ }
+
+ /**
+ * Parses a string to produce a {@link Complex} object.
+ *
+ * @param source the string to parse
+ * @param pos input/ouput parsing parameter.
+ * @return the parsed {@link Complex} object.
+ */
+ public Complex parse(String source, ParsePosition pos) {
+ int initialIndex = pos.getIndex();
+
+ // parse whitespace
+ CompositeFormat.parseAndIgnoreWhitespace(source, pos);
+
+ // parse real
+ Number re = CompositeFormat.parseNumber(source, getRealFormat(), pos);
+ if (re == null) {
+ // invalid real number
+ // set index back to initial, error index should already be set
+ pos.setIndex(initialIndex);
+ return null;
+ }
+
+ // parse sign
+ int startIndex = pos.getIndex();
+ char c = CompositeFormat.parseNextCharacter(source, pos);
+ int sign = 0;
+ switch (c) {
+ case 0:
+ // no sign
+ // return real only complex number
+ return new Complex(re.doubleValue(), 0.0);
+ case '-':
+ sign = -1;
+ break;
+ case '+':
+ sign = 1;
+ break;
+ default:
+ // invalid sign
+ // set index back to initial, error index should be the last
+ // character examined.
+ pos.setIndex(initialIndex);
+ pos.setErrorIndex(startIndex);
+ return null;
+ }
+
+ // parse whitespace
+ CompositeFormat.parseAndIgnoreWhitespace(source, pos);
+
+ // parse imaginary
+ Number im = CompositeFormat.parseNumber(source, getRealFormat(), pos);
+ if (im == null) {
+ // invalid imaginary number
+ // set index back to initial, error index should already be set
+ pos.setIndex(initialIndex);
+ return null;
+ }
+
+ // parse imaginary character
+ if (!CompositeFormat.parseFixedstring(source, getImaginaryCharacter(), pos)) {
+ return null;
+ }
+
+ return new Complex(re.doubleValue(), im.doubleValue() * sign);
+ }
+}
diff --git a/src/main/java/org/apache/commons/math3/complex/ComplexUtils.java b/src/main/java/org/apache/commons/math3/complex/ComplexUtils.java
new file mode 100644
index 0000000..6fca857
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/complex/ComplexUtils.java
@@ -0,0 +1,82 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math3.complex;
+
+import org.apache.commons.math3.exception.MathIllegalArgumentException;
+import org.apache.commons.math3.exception.util.LocalizedFormats;
+import org.apache.commons.math3.util.FastMath;
+
+/**
+ * Static implementations of common {@link org.apache.commons.math3.complex.Complex} utilities
+ * functions.
+ */
+public class ComplexUtils {
+
+ /** Default constructor. */
+ private ComplexUtils() {}
+
+ /**
+ * Creates a complex number from the given polar representation.
+ *
+ * <p>The value returned is <code>r&middot;e<sup>i&middot;theta</sup></code>, computed as <code>
+ * r&middot;cos(theta) + r&middot;sin(theta)i</code>
+ *
+ * <p>If either <code>r</code> or <code>theta</code> is NaN, or <code>theta</code> is infinite,
+ * {@link Complex#NaN} is returned.
+ *
+ * <p>If <code>r</code> is infinite and <code>theta</code> is finite, infinite or NaN values may
+ * be returned in parts of the result, following the rules for double arithmetic.
+ *
+ * <pre>
+ * Examples:
+ * <code>
+ * polar2Complex(INFINITY, &pi;/4) = INFINITY + INFINITY i
+ * polar2Complex(INFINITY, 0) = INFINITY + NaN i
+ * polar2Complex(INFINITY, -&pi;/4) = INFINITY - INFINITY i
+ * polar2Complex(INFINITY, 5&pi;/4) = -INFINITY - INFINITY i </code></pre>
+ *
+ * @param r the modulus of the complex number to create
+ * @param theta the argument of the complex number to create
+ * @return <code>r&middot;e<sup>i&middot;theta</sup></code>
+ * @throws MathIllegalArgumentException if {@code r} is negative.
+ * @since 1.1
+ */
+ public static Complex polar2Complex(double r, double theta)
+ throws MathIllegalArgumentException {
+ if (r < 0) {
+ throw new MathIllegalArgumentException(LocalizedFormats.NEGATIVE_COMPLEX_MODULE, r);
+ }
+ return new Complex(r * FastMath.cos(theta), r * FastMath.sin(theta));
+ }
+
+ /**
+ * Convert an array of primitive doubles to an array of {@code Complex} objects.
+ *
+ * @param real Array of numbers to be converted to their {@code Complex} equivalent.
+ * @return an array of {@code Complex} objects.
+ * @since 3.1
+ */
+ public static Complex[] convertToComplex(double[] real) {
+ final Complex c[] = new Complex[real.length];
+ for (int i = 0; i < real.length; i++) {
+ c[i] = new Complex(real[i], 0);
+ }
+
+ return c;
+ }
+}
diff --git a/src/main/java/org/apache/commons/math3/complex/Quaternion.java b/src/main/java/org/apache/commons/math3/complex/Quaternion.java
new file mode 100644
index 0000000..94d23cd
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/complex/Quaternion.java
@@ -0,0 +1,434 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math3.complex;
+
+import org.apache.commons.math3.exception.DimensionMismatchException;
+import org.apache.commons.math3.exception.ZeroException;
+import org.apache.commons.math3.exception.util.LocalizedFormats;
+import org.apache.commons.math3.util.FastMath;
+import org.apache.commons.math3.util.MathUtils;
+import org.apache.commons.math3.util.Precision;
+
+import java.io.Serializable;
+
+/**
+ * This class implements <a href="http://mathworld.wolfram.com/Quaternion.html">quaternions</a>
+ * (Hamilton's hypercomplex numbers). <br>
+ * Instance of this class are guaranteed to be immutable.
+ *
+ * @since 3.1
+ */
+public final class Quaternion implements Serializable {
+ /** Identity quaternion. */
+ public static final Quaternion IDENTITY = new Quaternion(1, 0, 0, 0);
+
+ /** Zero quaternion. */
+ public static final Quaternion ZERO = new Quaternion(0, 0, 0, 0);
+
+ /** i */
+ public static final Quaternion I = new Quaternion(0, 1, 0, 0);
+
+ /** j */
+ public static final Quaternion J = new Quaternion(0, 0, 1, 0);
+
+ /** k */
+ public static final Quaternion K = new Quaternion(0, 0, 0, 1);
+
+ /** Serializable version identifier. */
+ private static final long serialVersionUID = 20092012L;
+
+ /** First component (scalar part). */
+ private final double q0;
+
+ /** Second component (first vector part). */
+ private final double q1;
+
+ /** Third component (second vector part). */
+ private final double q2;
+
+ /** Fourth component (third vector part). */
+ private final double q3;
+
+ /**
+ * Builds a quaternion from its components.
+ *
+ * @param a Scalar component.
+ * @param b First vector component.
+ * @param c Second vector component.
+ * @param d Third vector component.
+ */
+ public Quaternion(final double a, final double b, final double c, final double d) {
+ this.q0 = a;
+ this.q1 = b;
+ this.q2 = c;
+ this.q3 = d;
+ }
+
+ /**
+ * Builds a quaternion from scalar and vector parts.
+ *
+ * @param scalar Scalar part of the quaternion.
+ * @param v Components of the vector part of the quaternion.
+ * @throws DimensionMismatchException if the array length is not 3.
+ */
+ public Quaternion(final double scalar, final double[] v) throws DimensionMismatchException {
+ if (v.length != 3) {
+ throw new DimensionMismatchException(v.length, 3);
+ }
+ this.q0 = scalar;
+ this.q1 = v[0];
+ this.q2 = v[1];
+ this.q3 = v[2];
+ }
+
+ /**
+ * Builds a pure quaternion from a vector (assuming that the scalar part is zero).
+ *
+ * @param v Components of the vector part of the pure quaternion.
+ */
+ public Quaternion(final double[] v) {
+ this(0, v);
+ }
+
+ /**
+ * Returns the conjugate quaternion of the instance.
+ *
+ * @return the conjugate quaternion
+ */
+ public Quaternion getConjugate() {
+ return new Quaternion(q0, -q1, -q2, -q3);
+ }
+
+ /**
+ * Returns the Hamilton product of two quaternions.
+ *
+ * @param q1 First quaternion.
+ * @param q2 Second quaternion.
+ * @return the product {@code q1} and {@code q2}, in that order.
+ */
+ public static Quaternion multiply(final Quaternion q1, final Quaternion q2) {
+ // Components of the first quaternion.
+ final double q1a = q1.getQ0();
+ final double q1b = q1.getQ1();
+ final double q1c = q1.getQ2();
+ final double q1d = q1.getQ3();
+
+ // Components of the second quaternion.
+ final double q2a = q2.getQ0();
+ final double q2b = q2.getQ1();
+ final double q2c = q2.getQ2();
+ final double q2d = q2.getQ3();
+
+ // Components of the product.
+ final double w = q1a * q2a - q1b * q2b - q1c * q2c - q1d * q2d;
+ final double x = q1a * q2b + q1b * q2a + q1c * q2d - q1d * q2c;
+ final double y = q1a * q2c - q1b * q2d + q1c * q2a + q1d * q2b;
+ final double z = q1a * q2d + q1b * q2c - q1c * q2b + q1d * q2a;
+
+ return new Quaternion(w, x, y, z);
+ }
+
+ /**
+ * Returns the Hamilton product of the instance by a quaternion.
+ *
+ * @param q Quaternion.
+ * @return the product of this instance with {@code q}, in that order.
+ */
+ public Quaternion multiply(final Quaternion q) {
+ return multiply(this, q);
+ }
+
+ /**
+ * Computes the sum of two quaternions.
+ *
+ * @param q1 Quaternion.
+ * @param q2 Quaternion.
+ * @return the sum of {@code q1} and {@code q2}.
+ */
+ public static Quaternion add(final Quaternion q1, final Quaternion q2) {
+ return new Quaternion(
+ q1.getQ0() + q2.getQ0(),
+ q1.getQ1() + q2.getQ1(),
+ q1.getQ2() + q2.getQ2(),
+ q1.getQ3() + q2.getQ3());
+ }
+
+ /**
+ * Computes the sum of the instance and another quaternion.
+ *
+ * @param q Quaternion.
+ * @return the sum of this instance and {@code q}
+ */
+ public Quaternion add(final Quaternion q) {
+ return add(this, q);
+ }
+
+ /**
+ * Subtracts two quaternions.
+ *
+ * @param q1 First Quaternion.
+ * @param q2 Second quaternion.
+ * @return the difference between {@code q1} and {@code q2}.
+ */
+ public static Quaternion subtract(final Quaternion q1, final Quaternion q2) {
+ return new Quaternion(
+ q1.getQ0() - q2.getQ0(),
+ q1.getQ1() - q2.getQ1(),
+ q1.getQ2() - q2.getQ2(),
+ q1.getQ3() - q2.getQ3());
+ }
+
+ /**
+ * Subtracts a quaternion from the instance.
+ *
+ * @param q Quaternion.
+ * @return the difference between this instance and {@code q}.
+ */
+ public Quaternion subtract(final Quaternion q) {
+ return subtract(this, q);
+ }
+
+ /**
+ * Computes the dot-product of two quaternions.
+ *
+ * @param q1 Quaternion.
+ * @param q2 Quaternion.
+ * @return the dot product of {@code q1} and {@code q2}.
+ */
+ public static double dotProduct(final Quaternion q1, final Quaternion q2) {
+ return q1.getQ0() * q2.getQ0()
+ + q1.getQ1() * q2.getQ1()
+ + q1.getQ2() * q2.getQ2()
+ + q1.getQ3() * q2.getQ3();
+ }
+
+ /**
+ * Computes the dot-product of the instance by a quaternion.
+ *
+ * @param q Quaternion.
+ * @return the dot product of this instance and {@code q}.
+ */
+ public double dotProduct(final Quaternion q) {
+ return dotProduct(this, q);
+ }
+
+ /**
+ * Computes the norm of the quaternion.
+ *
+ * @return the norm.
+ */
+ public double getNorm() {
+ return FastMath.sqrt(q0 * q0 + q1 * q1 + q2 * q2 + q3 * q3);
+ }
+
+ /**
+ * Computes the normalized quaternion (the versor of the instance). The norm of the quaternion
+ * must not be zero.
+ *
+ * @return a normalized quaternion.
+ * @throws ZeroException if the norm of the quaternion is zero.
+ */
+ public Quaternion normalize() {
+ final double norm = getNorm();
+
+ if (norm < Precision.SAFE_MIN) {
+ throw new ZeroException(LocalizedFormats.NORM, norm);
+ }
+
+ return new Quaternion(q0 / norm, q1 / norm, q2 / norm, q3 / norm);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public boolean equals(Object other) {
+ if (this == other) {
+ return true;
+ }
+ if (other instanceof Quaternion) {
+ final Quaternion q = (Quaternion) other;
+ return q0 == q.getQ0() && q1 == q.getQ1() && q2 == q.getQ2() && q3 == q.getQ3();
+ }
+
+ return false;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public int hashCode() {
+ // "Effective Java" (second edition, p. 47).
+ int result = 17;
+ for (double comp : new double[] {q0, q1, q2, q3}) {
+ final int c = MathUtils.hash(comp);
+ result = 31 * result + c;
+ }
+ return result;
+ }
+
+ /**
+ * Checks whether this instance is equal to another quaternion within a given tolerance.
+ *
+ * @param q Quaternion with which to compare the current quaternion.
+ * @param eps Tolerance.
+ * @return {@code true} if the each of the components are equal within the allowed absolute
+ * error.
+ */
+ public boolean equals(final Quaternion q, final double eps) {
+ return Precision.equals(q0, q.getQ0(), eps)
+ && Precision.equals(q1, q.getQ1(), eps)
+ && Precision.equals(q2, q.getQ2(), eps)
+ && Precision.equals(q3, q.getQ3(), eps);
+ }
+
+ /**
+ * Checks whether the instance is a unit quaternion within a given tolerance.
+ *
+ * @param eps Tolerance (absolute error).
+ * @return {@code true} if the norm is 1 within the given tolerance, {@code false} otherwise
+ */
+ public boolean isUnitQuaternion(double eps) {
+ return Precision.equals(getNorm(), 1d, eps);
+ }
+
+ /**
+ * Checks whether the instance is a pure quaternion within a given tolerance.
+ *
+ * @param eps Tolerance (absolute error).
+ * @return {@code true} if the scalar part of the quaternion is zero.
+ */
+ public boolean isPureQuaternion(double eps) {
+ return FastMath.abs(getQ0()) <= eps;
+ }
+
+ /**
+ * Returns the polar form of the quaternion.
+ *
+ * @return the unit quaternion with positive scalar part.
+ */
+ public Quaternion getPositivePolarForm() {
+ if (getQ0() < 0) {
+ final Quaternion unitQ = normalize();
+ // The quaternion of rotation (normalized quaternion) q and -q
+ // are equivalent (i.e. represent the same rotation).
+ return new Quaternion(-unitQ.getQ0(), -unitQ.getQ1(), -unitQ.getQ2(), -unitQ.getQ3());
+ } else {
+ return this.normalize();
+ }
+ }
+
+ /**
+ * Returns the inverse of this instance. The norm of the quaternion must not be zero.
+ *
+ * @return the inverse.
+ * @throws ZeroException if the norm (squared) of the quaternion is zero.
+ */
+ public Quaternion getInverse() {
+ final double squareNorm = q0 * q0 + q1 * q1 + q2 * q2 + q3 * q3;
+ if (squareNorm < Precision.SAFE_MIN) {
+ throw new ZeroException(LocalizedFormats.NORM, squareNorm);
+ }
+
+ return new Quaternion(
+ q0 / squareNorm, -q1 / squareNorm, -q2 / squareNorm, -q3 / squareNorm);
+ }
+
+ /**
+ * Gets the first component of the quaternion (scalar part).
+ *
+ * @return the scalar part.
+ */
+ public double getQ0() {
+ return q0;
+ }
+
+ /**
+ * Gets the second component of the quaternion (first component of the vector part).
+ *
+ * @return the first component of the vector part.
+ */
+ public double getQ1() {
+ return q1;
+ }
+
+ /**
+ * Gets the third component of the quaternion (second component of the vector part).
+ *
+ * @return the second component of the vector part.
+ */
+ public double getQ2() {
+ return q2;
+ }
+
+ /**
+ * Gets the fourth component of the quaternion (third component of the vector part).
+ *
+ * @return the third component of the vector part.
+ */
+ public double getQ3() {
+ return q3;
+ }
+
+ /**
+ * Gets the scalar part of the quaternion.
+ *
+ * @return the scalar part.
+ * @see #getQ0()
+ */
+ public double getScalarPart() {
+ return getQ0();
+ }
+
+ /**
+ * Gets the three components of the vector part of the quaternion.
+ *
+ * @return the vector part.
+ * @see #getQ1()
+ * @see #getQ2()
+ * @see #getQ3()
+ */
+ public double[] getVectorPart() {
+ return new double[] {getQ1(), getQ2(), getQ3()};
+ }
+
+ /**
+ * Multiplies the instance by a scalar.
+ *
+ * @param alpha Scalar factor.
+ * @return a scaled quaternion.
+ */
+ public Quaternion multiply(final double alpha) {
+ return new Quaternion(alpha * q0, alpha * q1, alpha * q2, alpha * q3);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public String toString() {
+ final String sp = " ";
+ final StringBuilder s = new StringBuilder();
+ s.append("[")
+ .append(q0)
+ .append(sp)
+ .append(q1)
+ .append(sp)
+ .append(q2)
+ .append(sp)
+ .append(q3)
+ .append("]");
+
+ return s.toString();
+ }
+}
diff --git a/src/main/java/org/apache/commons/math3/complex/RootsOfUnity.java b/src/main/java/org/apache/commons/math3/complex/RootsOfUnity.java
new file mode 100644
index 0000000..9e0cab7
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/complex/RootsOfUnity.java
@@ -0,0 +1,198 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math3.complex;
+
+import org.apache.commons.math3.exception.MathIllegalArgumentException;
+import org.apache.commons.math3.exception.MathIllegalStateException;
+import org.apache.commons.math3.exception.OutOfRangeException;
+import org.apache.commons.math3.exception.ZeroException;
+import org.apache.commons.math3.exception.util.LocalizedFormats;
+import org.apache.commons.math3.util.FastMath;
+
+import java.io.Serializable;
+
+/**
+ * A helper class for the computation and caching of the {@code n}-th roots of unity.
+ *
+ * @since 3.0
+ */
+public class RootsOfUnity implements Serializable {
+
+ /** Serializable version id. */
+ private static final long serialVersionUID = 20120201L;
+
+ /** Number of roots of unity. */
+ private int omegaCount;
+
+ /** Real part of the roots. */
+ private double[] omegaReal;
+
+ /**
+ * Imaginary part of the {@code n}-th roots of unity, for positive values of {@code n}. In this
+ * array, the roots are stored in counter-clockwise order.
+ */
+ private double[] omegaImaginaryCounterClockwise;
+
+ /**
+ * Imaginary part of the {@code n}-th roots of unity, for negative values of {@code n}. In this
+ * array, the roots are stored in clockwise order.
+ */
+ private double[] omegaImaginaryClockwise;
+
+ /**
+ * {@code true} if {@link #computeRoots(int)} was called with a positive value of its argument
+ * {@code n}. In this case, counter-clockwise ordering of the roots of unity should be used.
+ */
+ private boolean isCounterClockWise;
+
+ /** Build an engine for computing the {@code n}-th roots of unity. */
+ public RootsOfUnity() {
+
+ omegaCount = 0;
+ omegaReal = null;
+ omegaImaginaryCounterClockwise = null;
+ omegaImaginaryClockwise = null;
+ isCounterClockWise = true;
+ }
+
+ /**
+ * Returns {@code true} if {@link #computeRoots(int)} was called with a positive value of its
+ * argument {@code n}. If {@code true}, then counter-clockwise ordering of the roots of unity
+ * should be used.
+ *
+ * @return {@code true} if the roots of unity are stored in counter-clockwise order
+ * @throws MathIllegalStateException if no roots of unity have been computed yet
+ */
+ public synchronized boolean isCounterClockWise() throws MathIllegalStateException {
+
+ if (omegaCount == 0) {
+ throw new MathIllegalStateException(LocalizedFormats.ROOTS_OF_UNITY_NOT_COMPUTED_YET);
+ }
+ return isCounterClockWise;
+ }
+
+ /**
+ * Computes the {@code n}-th roots of unity. The roots are stored in {@code omega[]}, such that
+ * {@code omega[k] = w ^ k}, where {@code k = 0, ..., n - 1}, {@code w = exp(2 * pi * i / n)}
+ * and {@code i = sqrt(-1)}.
+ *
+ * <p>Note that {@code n} can be positive of negative
+ *
+ * <ul>
+ * <li>{@code abs(n)} is always the number of roots of unity.
+ * <li>If {@code n > 0}, then the roots are stored in counter-clockwise order.
+ * <li>If {@code n < 0}, then the roots are stored in clockwise order.
+ * </ul>
+ *
+ * @param n the (signed) number of roots of unity to be computed
+ * @throws ZeroException if {@code n = 0}
+ */
+ public synchronized void computeRoots(int n) throws ZeroException {
+
+ if (n == 0) {
+ throw new ZeroException(LocalizedFormats.CANNOT_COMPUTE_0TH_ROOT_OF_UNITY);
+ }
+
+ isCounterClockWise = n > 0;
+
+ // avoid repetitive calculations
+ final int absN = FastMath.abs(n);
+
+ if (absN == omegaCount) {
+ return;
+ }
+
+ // calculate everything from scratch
+ final double t = 2.0 * FastMath.PI / absN;
+ final double cosT = FastMath.cos(t);
+ final double sinT = FastMath.sin(t);
+ omegaReal = new double[absN];
+ omegaImaginaryCounterClockwise = new double[absN];
+ omegaImaginaryClockwise = new double[absN];
+ omegaReal[0] = 1.0;
+ omegaImaginaryCounterClockwise[0] = 0.0;
+ omegaImaginaryClockwise[0] = 0.0;
+ for (int i = 1; i < absN; i++) {
+ omegaReal[i] = omegaReal[i - 1] * cosT - omegaImaginaryCounterClockwise[i - 1] * sinT;
+ omegaImaginaryCounterClockwise[i] =
+ omegaReal[i - 1] * sinT + omegaImaginaryCounterClockwise[i - 1] * cosT;
+ omegaImaginaryClockwise[i] = -omegaImaginaryCounterClockwise[i];
+ }
+ omegaCount = absN;
+ }
+
+ /**
+ * Get the real part of the {@code k}-th {@code n}-th root of unity.
+ *
+ * @param k index of the {@code n}-th root of unity
+ * @return real part of the {@code k}-th {@code n}-th root of unity
+ * @throws MathIllegalStateException if no roots of unity have been computed yet
+ * @throws MathIllegalArgumentException if {@code k} is out of range
+ */
+ public synchronized double getReal(int k)
+ throws MathIllegalStateException, MathIllegalArgumentException {
+
+ if (omegaCount == 0) {
+ throw new MathIllegalStateException(LocalizedFormats.ROOTS_OF_UNITY_NOT_COMPUTED_YET);
+ }
+ if ((k < 0) || (k >= omegaCount)) {
+ throw new OutOfRangeException(
+ LocalizedFormats.OUT_OF_RANGE_ROOT_OF_UNITY_INDEX,
+ Integer.valueOf(k),
+ Integer.valueOf(0),
+ Integer.valueOf(omegaCount - 1));
+ }
+
+ return omegaReal[k];
+ }
+
+ /**
+ * Get the imaginary part of the {@code k}-th {@code n}-th root of unity.
+ *
+ * @param k index of the {@code n}-th root of unity
+ * @return imaginary part of the {@code k}-th {@code n}-th root of unity
+ * @throws MathIllegalStateException if no roots of unity have been computed yet
+ * @throws OutOfRangeException if {@code k} is out of range
+ */
+ public synchronized double getImaginary(int k)
+ throws MathIllegalStateException, OutOfRangeException {
+
+ if (omegaCount == 0) {
+ throw new MathIllegalStateException(LocalizedFormats.ROOTS_OF_UNITY_NOT_COMPUTED_YET);
+ }
+ if ((k < 0) || (k >= omegaCount)) {
+ throw new OutOfRangeException(
+ LocalizedFormats.OUT_OF_RANGE_ROOT_OF_UNITY_INDEX,
+ Integer.valueOf(k),
+ Integer.valueOf(0),
+ Integer.valueOf(omegaCount - 1));
+ }
+
+ return isCounterClockWise ? omegaImaginaryCounterClockwise[k] : omegaImaginaryClockwise[k];
+ }
+
+ /**
+ * Returns the number of roots of unity currently stored. If {@link #computeRoots(int)} was
+ * called with {@code n}, then this method returns {@code abs(n)}. If no roots of unity have
+ * been computed yet, this method returns 0.
+ *
+ * @return the number of roots of unity currently stored
+ */
+ public synchronized int getNumberOfRoots() {
+ return omegaCount;
+ }
+}
diff --git a/src/main/java/org/apache/commons/math3/complex/package-info.java b/src/main/java/org/apache/commons/math3/complex/package-info.java
new file mode 100644
index 0000000..4528581
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/complex/package-info.java
@@ -0,0 +1,18 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/** Complex number type and implementations of complex transcendental functions. */
+package org.apache.commons.math3.complex;
diff --git a/src/main/java/org/apache/commons/math3/dfp/BracketingNthOrderBrentSolverDFP.java b/src/main/java/org/apache/commons/math3/dfp/BracketingNthOrderBrentSolverDFP.java
new file mode 100644
index 0000000..23d4861
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/dfp/BracketingNthOrderBrentSolverDFP.java
@@ -0,0 +1,161 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math3.dfp;
+
+import org.apache.commons.math3.analysis.RealFieldUnivariateFunction;
+import org.apache.commons.math3.analysis.solvers.AllowedSolution;
+import org.apache.commons.math3.analysis.solvers.FieldBracketingNthOrderBrentSolver;
+import org.apache.commons.math3.exception.NoBracketingException;
+import org.apache.commons.math3.exception.NullArgumentException;
+import org.apache.commons.math3.exception.NumberIsTooSmallException;
+import org.apache.commons.math3.util.MathUtils;
+
+/**
+ * This class implements a modification of the <a
+ * href="http://mathworld.wolfram.com/BrentsMethod.html">Brent algorithm</a>.
+ *
+ * <p>The changes with respect to the original Brent algorithm are:
+ *
+ * <ul>
+ * <li>the returned value is chosen in the current interval according to user specified {@link
+ * AllowedSolution},
+ * <li>the maximal order for the invert polynomial root search is user-specified instead of being
+ * invert quadratic only
+ * </ul>
+ *
+ * The given interval must bracket the root.
+ *
+ * @deprecated as of 3.6 replaced with {@link FieldBracketingNthOrderBrentSolver}
+ */
+@Deprecated
+public class BracketingNthOrderBrentSolverDFP extends FieldBracketingNthOrderBrentSolver<Dfp> {
+
+ /**
+ * Construct a solver.
+ *
+ * @param relativeAccuracy Relative accuracy.
+ * @param absoluteAccuracy Absolute accuracy.
+ * @param functionValueAccuracy Function value accuracy.
+ * @param maximalOrder maximal order.
+ * @exception NumberIsTooSmallException if maximal order is lower than 2
+ */
+ public BracketingNthOrderBrentSolverDFP(
+ final Dfp relativeAccuracy,
+ final Dfp absoluteAccuracy,
+ final Dfp functionValueAccuracy,
+ final int maximalOrder)
+ throws NumberIsTooSmallException {
+ super(relativeAccuracy, absoluteAccuracy, functionValueAccuracy, maximalOrder);
+ }
+
+ /**
+ * Get the absolute accuracy.
+ *
+ * @return absolute accuracy
+ */
+ @Override
+ public Dfp getAbsoluteAccuracy() {
+ return super.getAbsoluteAccuracy();
+ }
+
+ /**
+ * Get the relative accuracy.
+ *
+ * @return relative accuracy
+ */
+ @Override
+ public Dfp getRelativeAccuracy() {
+ return super.getRelativeAccuracy();
+ }
+
+ /**
+ * Get the function accuracy.
+ *
+ * @return function accuracy
+ */
+ @Override
+ public Dfp getFunctionValueAccuracy() {
+ return super.getFunctionValueAccuracy();
+ }
+
+ /**
+ * Solve for a zero in the given interval. A solver may require that the interval brackets a
+ * single zero root. Solvers that do require bracketing should be able to handle the case where
+ * one of the endpoints is itself a root.
+ *
+ * @param maxEval Maximum number of evaluations.
+ * @param f Function to solve.
+ * @param min Lower bound for the interval.
+ * @param max Upper bound for the interval.
+ * @param allowedSolution The kind of solutions that the root-finding algorithm may accept as
+ * solutions.
+ * @return a value where the function is zero.
+ * @exception NullArgumentException if f is null.
+ * @exception NoBracketingException if root cannot be bracketed
+ */
+ public Dfp solve(
+ final int maxEval,
+ final UnivariateDfpFunction f,
+ final Dfp min,
+ final Dfp max,
+ final AllowedSolution allowedSolution)
+ throws NullArgumentException, NoBracketingException {
+ return solve(maxEval, f, min, max, min.add(max).divide(2), allowedSolution);
+ }
+
+ /**
+ * Solve for a zero in the given interval, start at {@code startValue}. A solver may require
+ * that the interval brackets a single zero root. Solvers that do require bracketing should be
+ * able to handle the case where one of the endpoints is itself a root.
+ *
+ * @param maxEval Maximum number of evaluations.
+ * @param f Function to solve.
+ * @param min Lower bound for the interval.
+ * @param max Upper bound for the interval.
+ * @param startValue Start value to use.
+ * @param allowedSolution The kind of solutions that the root-finding algorithm may accept as
+ * solutions.
+ * @return a value where the function is zero.
+ * @exception NullArgumentException if f is null.
+ * @exception NoBracketingException if root cannot be bracketed
+ */
+ public Dfp solve(
+ final int maxEval,
+ final UnivariateDfpFunction f,
+ final Dfp min,
+ final Dfp max,
+ final Dfp startValue,
+ final AllowedSolution allowedSolution)
+ throws NullArgumentException, NoBracketingException {
+
+ // checks
+ MathUtils.checkNotNull(f);
+
+ // wrap the function
+ RealFieldUnivariateFunction<Dfp> fieldF =
+ new RealFieldUnivariateFunction<Dfp>() {
+
+ /** {@inheritDoc} */
+ public Dfp value(final Dfp x) {
+ return f.value(x);
+ }
+ };
+
+ // delegate to general field solver
+ return solve(maxEval, fieldF, min, max, startValue, allowedSolution);
+ }
+}
diff --git a/src/main/java/org/apache/commons/math3/dfp/Dfp.java b/src/main/java/org/apache/commons/math3/dfp/Dfp.java
new file mode 100644
index 0000000..4f59a19
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/dfp/Dfp.java
@@ -0,0 +1,3082 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math3.dfp;
+
+import org.apache.commons.math3.RealFieldElement;
+import org.apache.commons.math3.exception.DimensionMismatchException;
+import org.apache.commons.math3.util.FastMath;
+
+import java.util.Arrays;
+
+/**
+ * Decimal floating point library for Java
+ *
+ * <p>Another floating point class. This one is built using radix 10000 which is 10<sup>4</sup>, so
+ * its almost decimal.
+ *
+ * <p>The design goals here are:
+ *
+ * <ol>
+ * <li>Decimal math, or close to it
+ * <li>Settable precision (but no mix between numbers using different settings)
+ * <li>Portability. Code should be kept as portable as possible.
+ * <li>Performance
+ * <li>Accuracy - Results should always be +/- 1 ULP for basic algebraic operation
+ * <li>Comply with IEEE 854-1987 as much as possible. (See IEEE 854-1987 notes below)
+ * </ol>
+ *
+ * <p>Trade offs:
+ *
+ * <ol>
+ * <li>Memory foot print. I'm using more memory than necessary to represent numbers to get better
+ * performance.
+ * <li>Digits are bigger, so rounding is a greater loss. So, if you really need 12 decimal digits,
+ * better use 4 base 10000 digits there can be one partially filled.
+ * </ol>
+ *
+ * <p>Numbers are represented in the following form:
+ *
+ * <pre>
+ * n = sign &times; mant &times; (radix)<sup>exp</sup>;</p>
+ * </pre>
+ *
+ * where sign is &plusmn;1, mantissa represents a fractional number between zero and one. mant[0] is
+ * the least significant digit. exp is in the range of -32767 to 32768
+ *
+ * <p>IEEE 854-1987 Notes and differences
+ *
+ * <p>IEEE 854 requires the radix to be either 2 or 10. The radix here is 10000, so that requirement
+ * is not met, but it is possible that a subclassed can be made to make it behave as a radix 10
+ * number. It is my opinion that if it looks and behaves as a radix 10 number then it is one and
+ * that requirement would be met.
+ *
+ * <p>The radix of 10000 was chosen because it should be faster to operate on 4 decimal digits at
+ * once instead of one at a time. Radix 10 behavior can be realized by adding an additional rounding
+ * step to ensure that the number of decimal digits represented is constant.
+ *
+ * <p>The IEEE standard specifically leaves out internal data encoding, so it is reasonable to
+ * conclude that such a subclass of this radix 10000 system is merely an encoding of a radix 10
+ * system.
+ *
+ * <p>IEEE 854 also specifies the existence of "sub-normal" numbers. This class does not contain any
+ * such entities. The most significant radix 10000 digit is always non-zero. Instead, we support
+ * "gradual underflow" by raising the underflow flag for numbers less with exponent less than
+ * expMin, but don't flush to zero until the exponent reaches MIN_EXP-digits. Thus the smallest
+ * number we can represent would be: 1E(-(MIN_EXP-digits-1)*4), eg, for digits=5, MIN_EXP=-32767,
+ * that would be 1e-131092.
+ *
+ * <p>IEEE 854 defines that the implied radix point lies just to the right of the most significant
+ * digit and to the left of the remaining digits. This implementation puts the implied radix point
+ * to the left of all digits including the most significant one. The most significant digit here is
+ * the one just to the right of the radix point. This is a fine detail and is really only a matter
+ * of definition. Any side effects of this can be rendered invisible by a subclass.
+ *
+ * @see DfpField
+ * @since 2.2
+ */
+public class Dfp implements RealFieldElement<Dfp> {
+
+ /** The radix, or base of this system. Set to 10000 */
+ public static final int RADIX = 10000;
+
+ /** The minimum exponent before underflow is signaled. Flush to zero occurs at minExp-DIGITS */
+ public static final int MIN_EXP = -32767;
+
+ /** The maximum exponent before overflow is signaled and results flushed to infinity */
+ public static final int MAX_EXP = 32768;
+
+ /** The amount under/overflows are scaled by before going to trap handler */
+ public static final int ERR_SCALE = 32760;
+
+ /** Indicator value for normal finite numbers. */
+ public static final byte FINITE = 0;
+
+ /** Indicator value for Infinity. */
+ public static final byte INFINITE = 1;
+
+ /** Indicator value for signaling NaN. */
+ public static final byte SNAN = 2;
+
+ /** Indicator value for quiet NaN. */
+ public static final byte QNAN = 3;
+
+ /** String for NaN representation. */
+ private static final String NAN_STRING = "NaN";
+
+ /** String for positive infinity representation. */
+ private static final String POS_INFINITY_STRING = "Infinity";
+
+ /** String for negative infinity representation. */
+ private static final String NEG_INFINITY_STRING = "-Infinity";
+
+ /** Name for traps triggered by addition. */
+ private static final String ADD_TRAP = "add";
+
+ /** Name for traps triggered by multiplication. */
+ private static final String MULTIPLY_TRAP = "multiply";
+
+ /** Name for traps triggered by division. */
+ private static final String DIVIDE_TRAP = "divide";
+
+ /** Name for traps triggered by square root. */
+ private static final String SQRT_TRAP = "sqrt";
+
+ /** Name for traps triggered by alignment. */
+ private static final String ALIGN_TRAP = "align";
+
+ /** Name for traps triggered by truncation. */
+ private static final String TRUNC_TRAP = "trunc";
+
+ /** Name for traps triggered by nextAfter. */
+ private static final String NEXT_AFTER_TRAP = "nextAfter";
+
+ /** Name for traps triggered by lessThan. */
+ private static final String LESS_THAN_TRAP = "lessThan";
+
+ /** Name for traps triggered by greaterThan. */
+ private static final String GREATER_THAN_TRAP = "greaterThan";
+
+ /** Name for traps triggered by newInstance. */
+ private static final String NEW_INSTANCE_TRAP = "newInstance";
+
+ /** Mantissa. */
+ protected int[] mant;
+
+ /** Sign bit: 1 for positive, -1 for negative. */
+ protected byte sign;
+
+ /** Exponent. */
+ protected int exp;
+
+ /** Indicator for non-finite / non-number values. */
+ protected byte nans;
+
+ /** Factory building similar Dfp's. */
+ private final DfpField field;
+
+ /**
+ * Makes an instance with a value of zero.
+ *
+ * @param field field to which this instance belongs
+ */
+ protected Dfp(final DfpField field) {
+ mant = new int[field.getRadixDigits()];
+ sign = 1;
+ exp = 0;
+ nans = FINITE;
+ this.field = field;
+ }
+
+ /**
+ * Create an instance from a byte value.
+ *
+ * @param field field to which this instance belongs
+ * @param x value to convert to an instance
+ */
+ protected Dfp(final DfpField field, byte x) {
+ this(field, (long) x);
+ }
+
+ /**
+ * Create an instance from an int value.
+ *
+ * @param field field to which this instance belongs
+ * @param x value to convert to an instance
+ */
+ protected Dfp(final DfpField field, int x) {
+ this(field, (long) x);
+ }
+
+ /**
+ * Create an instance from a long value.
+ *
+ * @param field field to which this instance belongs
+ * @param x value to convert to an instance
+ */
+ protected Dfp(final DfpField field, long x) {
+
+ // initialize as if 0
+ mant = new int[field.getRadixDigits()];
+ nans = FINITE;
+ this.field = field;
+
+ boolean isLongMin = false;
+ if (x == Long.MIN_VALUE) {
+ // special case for Long.MIN_VALUE (-9223372036854775808)
+ // we must shift it before taking its absolute value
+ isLongMin = true;
+ ++x;
+ }
+
+ // set the sign
+ if (x < 0) {
+ sign = -1;
+ x = -x;
+ } else {
+ sign = 1;
+ }
+
+ exp = 0;
+ while (x != 0) {
+ System.arraycopy(mant, mant.length - exp, mant, mant.length - 1 - exp, exp);
+ mant[mant.length - 1] = (int) (x % RADIX);
+ x /= RADIX;
+ exp++;
+ }
+
+ if (isLongMin) {
+ // remove the shift added for Long.MIN_VALUE
+ // we know in this case that fixing the last digit is sufficient
+ for (int i = 0; i < mant.length - 1; i++) {
+ if (mant[i] != 0) {
+ mant[i]++;
+ break;
+ }
+ }
+ }
+ }
+
+ /**
+ * Create an instance from a double value.
+ *
+ * @param field field to which this instance belongs
+ * @param x value to convert to an instance
+ */
+ protected Dfp(final DfpField field, double x) {
+
+ // initialize as if 0
+ mant = new int[field.getRadixDigits()];
+ sign = 1;
+ exp = 0;
+ nans = FINITE;
+ this.field = field;
+
+ long bits = Double.doubleToLongBits(x);
+ long mantissa = bits & 0x000fffffffffffffL;
+ int exponent = (int) ((bits & 0x7ff0000000000000L) >> 52) - 1023;
+
+ if (exponent == -1023) {
+ // Zero or sub-normal
+ if (x == 0) {
+ // make sure 0 has the right sign
+ if ((bits & 0x8000000000000000L) != 0) {
+ sign = -1;
+ }
+ return;
+ }
+
+ exponent++;
+
+ // Normalize the subnormal number
+ while ((mantissa & 0x0010000000000000L) == 0) {
+ exponent--;
+ mantissa <<= 1;
+ }
+ mantissa &= 0x000fffffffffffffL;
+ }
+
+ if (exponent == 1024) {
+ // infinity or NAN
+ if (x != x) {
+ sign = (byte) 1;
+ nans = QNAN;
+ } else if (x < 0) {
+ sign = (byte) -1;
+ nans = INFINITE;
+ } else {
+ sign = (byte) 1;
+ nans = INFINITE;
+ }
+ return;
+ }
+
+ Dfp xdfp = new Dfp(field, mantissa);
+ xdfp =
+ xdfp.divide(new Dfp(field, 4503599627370496l))
+ .add(field.getOne()); // Divide by 2^52, then add one
+ xdfp = xdfp.multiply(DfpMath.pow(field.getTwo(), exponent));
+
+ if ((bits & 0x8000000000000000L) != 0) {
+ xdfp = xdfp.negate();
+ }
+
+ System.arraycopy(xdfp.mant, 0, mant, 0, mant.length);
+ sign = xdfp.sign;
+ exp = xdfp.exp;
+ nans = xdfp.nans;
+ }
+
+ /**
+ * Copy constructor.
+ *
+ * @param d instance to copy
+ */
+ public Dfp(final Dfp d) {
+ mant = d.mant.clone();
+ sign = d.sign;
+ exp = d.exp;
+ nans = d.nans;
+ field = d.field;
+ }
+
+ /**
+ * Create an instance from a String representation.
+ *
+ * @param field field to which this instance belongs
+ * @param s string representation of the instance
+ */
+ protected Dfp(final DfpField field, final String s) {
+
+ // initialize as if 0
+ mant = new int[field.getRadixDigits()];
+ sign = 1;
+ exp = 0;
+ nans = FINITE;
+ this.field = field;
+
+ boolean decimalFound = false;
+ final int rsize = 4; // size of radix in decimal digits
+ final int offset = 4; // Starting offset into Striped
+ final char[] striped = new char[getRadixDigits() * rsize + offset * 2];
+
+ // Check some special cases
+ if (s.equals(POS_INFINITY_STRING)) {
+ sign = (byte) 1;
+ nans = INFINITE;
+ return;
+ }
+
+ if (s.equals(NEG_INFINITY_STRING)) {
+ sign = (byte) -1;
+ nans = INFINITE;
+ return;
+ }
+
+ if (s.equals(NAN_STRING)) {
+ sign = (byte) 1;
+ nans = QNAN;
+ return;
+ }
+
+ // Check for scientific notation
+ int p = s.indexOf("e");
+ if (p == -1) { // try upper case?
+ p = s.indexOf("E");
+ }
+
+ final String fpdecimal;
+ int sciexp = 0;
+ if (p != -1) {
+ // scientific notation
+ fpdecimal = s.substring(0, p);
+ String fpexp = s.substring(p + 1);
+ boolean negative = false;
+
+ for (int i = 0; i < fpexp.length(); i++) {
+ if (fpexp.charAt(i) == '-') {
+ negative = true;
+ continue;
+ }
+ if (fpexp.charAt(i) >= '0' && fpexp.charAt(i) <= '9') {
+ sciexp = sciexp * 10 + fpexp.charAt(i) - '0';
+ }
+ }
+
+ if (negative) {
+ sciexp = -sciexp;
+ }
+ } else {
+ // normal case
+ fpdecimal = s;
+ }
+
+ // If there is a minus sign in the number then it is negative
+ if (fpdecimal.indexOf("-") != -1) {
+ sign = -1;
+ }
+
+ // First off, find all of the leading zeros, trailing zeros, and significant digits
+ p = 0;
+
+ // Move p to first significant digit
+ int decimalPos = 0;
+ for (; ; ) {
+ if (fpdecimal.charAt(p) >= '1' && fpdecimal.charAt(p) <= '9') {
+ break;
+ }
+
+ if (decimalFound && fpdecimal.charAt(p) == '0') {
+ decimalPos--;
+ }
+
+ if (fpdecimal.charAt(p) == '.') {
+ decimalFound = true;
+ }
+
+ p++;
+
+ if (p == fpdecimal.length()) {
+ break;
+ }
+ }
+
+ // Copy the string onto Stripped
+ int q = offset;
+ striped[0] = '0';
+ striped[1] = '0';
+ striped[2] = '0';
+ striped[3] = '0';
+ int significantDigits = 0;
+ for (; ; ) {
+ if (p == (fpdecimal.length())) {
+ break;
+ }
+
+ // Don't want to run pass the end of the array
+ if (q == mant.length * rsize + offset + 1) {
+ break;
+ }
+
+ if (fpdecimal.charAt(p) == '.') {
+ decimalFound = true;
+ decimalPos = significantDigits;
+ p++;
+ continue;
+ }
+
+ if (fpdecimal.charAt(p) < '0' || fpdecimal.charAt(p) > '9') {
+ p++;
+ continue;
+ }
+
+ striped[q] = fpdecimal.charAt(p);
+ q++;
+ p++;
+ significantDigits++;
+ }
+
+ // If the decimal point has been found then get rid of trailing zeros.
+ if (decimalFound && q != offset) {
+ for (; ; ) {
+ q--;
+ if (q == offset) {
+ break;
+ }
+ if (striped[q] == '0') {
+ significantDigits--;
+ } else {
+ break;
+ }
+ }
+ }
+
+ // special case of numbers like "0.00000"
+ if (decimalFound && significantDigits == 0) {
+ decimalPos = 0;
+ }
+
+ // Implicit decimal point at end of number if not present
+ if (!decimalFound) {
+ decimalPos = q - offset;
+ }
+
+ // Find the number of significant trailing zeros
+ q = offset; // set q to point to first sig digit
+ p = significantDigits - 1 + offset;
+
+ while (p > q) {
+ if (striped[p] != '0') {
+ break;
+ }
+ p--;
+ }
+
+ // Make sure the decimal is on a mod 10000 boundary
+ int i = ((rsize * 100) - decimalPos - sciexp % rsize) % rsize;
+ q -= i;
+ decimalPos += i;
+
+ // Make the mantissa length right by adding zeros at the end if necessary
+ while ((p - q) < (mant.length * rsize)) {
+ for (i = 0; i < rsize; i++) {
+ striped[++p] = '0';
+ }
+ }
+
+ // Ok, now we know how many trailing zeros there are,
+ // and where the least significant digit is
+ for (i = mant.length - 1; i >= 0; i--) {
+ mant[i] =
+ (striped[q] - '0') * 1000
+ + (striped[q + 1] - '0') * 100
+ + (striped[q + 2] - '0') * 10
+ + (striped[q + 3] - '0');
+ q += 4;
+ }
+
+ exp = (decimalPos + sciexp) / rsize;
+
+ if (q < striped.length) {
+ // Is there possible another digit?
+ round((striped[q] - '0') * 1000);
+ }
+ }
+
+ /**
+ * Creates an instance with a non-finite value.
+ *
+ * @param field field to which this instance belongs
+ * @param sign sign of the Dfp to create
+ * @param nans code of the value, must be one of {@link #INFINITE}, {@link #SNAN}, {@link #QNAN}
+ */
+ protected Dfp(final DfpField field, final byte sign, final byte nans) {
+ this.field = field;
+ this.mant = new int[field.getRadixDigits()];
+ this.sign = sign;
+ this.exp = 0;
+ this.nans = nans;
+ }
+
+ /**
+ * Create an instance with a value of 0. Use this internally in preference to constructors to
+ * facilitate subclasses
+ *
+ * @return a new instance with a value of 0
+ */
+ public Dfp newInstance() {
+ return new Dfp(getField());
+ }
+
+ /**
+ * Create an instance from a byte value.
+ *
+ * @param x value to convert to an instance
+ * @return a new instance with value x
+ */
+ public Dfp newInstance(final byte x) {
+ return new Dfp(getField(), x);
+ }
+
+ /**
+ * Create an instance from an int value.
+ *
+ * @param x value to convert to an instance
+ * @return a new instance with value x
+ */
+ public Dfp newInstance(final int x) {
+ return new Dfp(getField(), x);
+ }
+
+ /**
+ * Create an instance from a long value.
+ *
+ * @param x value to convert to an instance
+ * @return a new instance with value x
+ */
+ public Dfp newInstance(final long x) {
+ return new Dfp(getField(), x);
+ }
+
+ /**
+ * Create an instance from a double value.
+ *
+ * @param x value to convert to an instance
+ * @return a new instance with value x
+ */
+ public Dfp newInstance(final double x) {
+ return new Dfp(getField(), x);
+ }
+
+ /**
+ * Create an instance by copying an existing one. Use this internally in preference to
+ * constructors to facilitate subclasses.
+ *
+ * @param d instance to copy
+ * @return a new instance with the same value as d
+ */
+ public Dfp newInstance(final Dfp d) {
+
+ // make sure we don't mix number with different precision
+ if (field.getRadixDigits() != d.field.getRadixDigits()) {
+ field.setIEEEFlagsBits(DfpField.FLAG_INVALID);
+ final Dfp result = newInstance(getZero());
+ result.nans = QNAN;
+ return dotrap(DfpField.FLAG_INVALID, NEW_INSTANCE_TRAP, d, result);
+ }
+
+ return new Dfp(d);
+ }
+
+ /**
+ * Create an instance from a String representation. Use this internally in preference to
+ * constructors to facilitate subclasses.
+ *
+ * @param s string representation of the instance
+ * @return a new instance parsed from specified string
+ */
+ public Dfp newInstance(final String s) {
+ return new Dfp(field, s);
+ }
+
+ /**
+ * Creates an instance with a non-finite value.
+ *
+ * @param sig sign of the Dfp to create
+ * @param code code of the value, must be one of {@link #INFINITE}, {@link #SNAN}, {@link #QNAN}
+ * @return a new instance with a non-finite value
+ */
+ public Dfp newInstance(final byte sig, final byte code) {
+ return field.newDfp(sig, code);
+ }
+
+ /**
+ * Get the {@link org.apache.commons.math3.Field Field} (really a {@link DfpField}) to which the
+ * instance belongs.
+ *
+ * <p>The field is linked to the number of digits and acts as a factory for {@link Dfp}
+ * instances.
+ *
+ * @return {@link org.apache.commons.math3.Field Field} (really a {@link DfpField}) to which the
+ * instance belongs
+ */
+ public DfpField getField() {
+ return field;
+ }
+
+ /**
+ * Get the number of radix digits of the instance.
+ *
+ * @return number of radix digits
+ */
+ public int getRadixDigits() {
+ return field.getRadixDigits();
+ }
+
+ /**
+ * Get the constant 0.
+ *
+ * @return a Dfp with value zero
+ */
+ public Dfp getZero() {
+ return field.getZero();
+ }
+
+ /**
+ * Get the constant 1.
+ *
+ * @return a Dfp with value one
+ */
+ public Dfp getOne() {
+ return field.getOne();
+ }
+
+ /**
+ * Get the constant 2.
+ *
+ * @return a Dfp with value two
+ */
+ public Dfp getTwo() {
+ return field.getTwo();
+ }
+
+ /** Shift the mantissa left, and adjust the exponent to compensate. */
+ protected void shiftLeft() {
+ for (int i = mant.length - 1; i > 0; i--) {
+ mant[i] = mant[i - 1];
+ }
+ mant[0] = 0;
+ exp--;
+ }
+
+ /* Note that shiftRight() does not call round() as that round() itself
+ uses shiftRight() */
+ /** Shift the mantissa right, and adjust the exponent to compensate. */
+ protected void shiftRight() {
+ for (int i = 0; i < mant.length - 1; i++) {
+ mant[i] = mant[i + 1];
+ }
+ mant[mant.length - 1] = 0;
+ exp++;
+ }
+
+ /**
+ * Make our exp equal to the supplied one, this may cause rounding. Also causes de-normalized
+ * numbers. These numbers are generally dangerous because most routines assume normalized
+ * numbers. Align doesn't round, so it will return the last digit destroyed by shifting right.
+ *
+ * @param e desired exponent
+ * @return last digit destroyed by shifting right
+ */
+ protected int align(int e) {
+ int lostdigit = 0;
+ boolean inexact = false;
+
+ int diff = exp - e;
+
+ int adiff = diff;
+ if (adiff < 0) {
+ adiff = -adiff;
+ }
+
+ if (diff == 0) {
+ return 0;
+ }
+
+ if (adiff > (mant.length + 1)) {
+ // Special case
+ Arrays.fill(mant, 0);
+ exp = e;
+
+ field.setIEEEFlagsBits(DfpField.FLAG_INEXACT);
+ dotrap(DfpField.FLAG_INEXACT, ALIGN_TRAP, this, this);
+
+ return 0;
+ }
+
+ for (int i = 0; i < adiff; i++) {
+ if (diff < 0) {
+ /* Keep track of loss -- only signal inexact after losing 2 digits.
+ * the first lost digit is returned to add() and may be incorporated
+ * into the result.
+ */
+ if (lostdigit != 0) {
+ inexact = true;
+ }
+
+ lostdigit = mant[0];
+
+ shiftRight();
+ } else {
+ shiftLeft();
+ }
+ }
+
+ if (inexact) {
+ field.setIEEEFlagsBits(DfpField.FLAG_INEXACT);
+ dotrap(DfpField.FLAG_INEXACT, ALIGN_TRAP, this, this);
+ }
+
+ return lostdigit;
+ }
+
+ /**
+ * Check if instance is less than x.
+ *
+ * @param x number to check instance against
+ * @return true if instance is less than x and neither are NaN, false otherwise
+ */
+ public boolean lessThan(final Dfp x) {
+
+ // make sure we don't mix number with different precision
+ if (field.getRadixDigits() != x.field.getRadixDigits()) {
+ field.setIEEEFlagsBits(DfpField.FLAG_INVALID);
+ final Dfp result = newInstance(getZero());
+ result.nans = QNAN;
+ dotrap(DfpField.FLAG_INVALID, LESS_THAN_TRAP, x, result);
+ return false;
+ }
+
+ /* if a nan is involved, signal invalid and return false */
+ if (isNaN() || x.isNaN()) {
+ field.setIEEEFlagsBits(DfpField.FLAG_INVALID);
+ dotrap(DfpField.FLAG_INVALID, LESS_THAN_TRAP, x, newInstance(getZero()));
+ return false;
+ }
+
+ return compare(this, x) < 0;
+ }
+
+ /**
+ * Check if instance is greater than x.
+ *
+ * @param x number to check instance against
+ * @return true if instance is greater than x and neither are NaN, false otherwise
+ */
+ public boolean greaterThan(final Dfp x) {
+
+ // make sure we don't mix number with different precision
+ if (field.getRadixDigits() != x.field.getRadixDigits()) {
+ field.setIEEEFlagsBits(DfpField.FLAG_INVALID);
+ final Dfp result = newInstance(getZero());
+ result.nans = QNAN;
+ dotrap(DfpField.FLAG_INVALID, GREATER_THAN_TRAP, x, result);
+ return false;
+ }
+
+ /* if a nan is involved, signal invalid and return false */
+ if (isNaN() || x.isNaN()) {
+ field.setIEEEFlagsBits(DfpField.FLAG_INVALID);
+ dotrap(DfpField.FLAG_INVALID, GREATER_THAN_TRAP, x, newInstance(getZero()));
+ return false;
+ }
+
+ return compare(this, x) > 0;
+ }
+
+ /**
+ * Check if instance is less than or equal to 0.
+ *
+ * @return true if instance is not NaN and less than or equal to 0, false otherwise
+ */
+ public boolean negativeOrNull() {
+
+ if (isNaN()) {
+ field.setIEEEFlagsBits(DfpField.FLAG_INVALID);
+ dotrap(DfpField.FLAG_INVALID, LESS_THAN_TRAP, this, newInstance(getZero()));
+ return false;
+ }
+
+ return (sign < 0) || ((mant[mant.length - 1] == 0) && !isInfinite());
+ }
+
+ /**
+ * Check if instance is strictly less than 0.
+ *
+ * @return true if instance is not NaN and less than or equal to 0, false otherwise
+ */
+ public boolean strictlyNegative() {
+
+ if (isNaN()) {
+ field.setIEEEFlagsBits(DfpField.FLAG_INVALID);
+ dotrap(DfpField.FLAG_INVALID, LESS_THAN_TRAP, this, newInstance(getZero()));
+ return false;
+ }
+
+ return (sign < 0) && ((mant[mant.length - 1] != 0) || isInfinite());
+ }
+
+ /**
+ * Check if instance is greater than or equal to 0.
+ *
+ * @return true if instance is not NaN and greater than or equal to 0, false otherwise
+ */
+ public boolean positiveOrNull() {
+
+ if (isNaN()) {
+ field.setIEEEFlagsBits(DfpField.FLAG_INVALID);
+ dotrap(DfpField.FLAG_INVALID, LESS_THAN_TRAP, this, newInstance(getZero()));
+ return false;
+ }
+
+ return (sign > 0) || ((mant[mant.length - 1] == 0) && !isInfinite());
+ }
+
+ /**
+ * Check if instance is strictly greater than 0.
+ *
+ * @return true if instance is not NaN and greater than or equal to 0, false otherwise
+ */
+ public boolean strictlyPositive() {
+
+ if (isNaN()) {
+ field.setIEEEFlagsBits(DfpField.FLAG_INVALID);
+ dotrap(DfpField.FLAG_INVALID, LESS_THAN_TRAP, this, newInstance(getZero()));
+ return false;
+ }
+
+ return (sign > 0) && ((mant[mant.length - 1] != 0) || isInfinite());
+ }
+
+ /**
+ * Get the absolute value of instance.
+ *
+ * @return absolute value of instance
+ * @since 3.2
+ */
+ public Dfp abs() {
+ Dfp result = newInstance(this);
+ result.sign = 1;
+ return result;
+ }
+
+ /**
+ * Check if instance is infinite.
+ *
+ * @return true if instance is infinite
+ */
+ public boolean isInfinite() {
+ return nans == INFINITE;
+ }
+
+ /**
+ * Check if instance is not a number.
+ *
+ * @return true if instance is not a number
+ */
+ public boolean isNaN() {
+ return (nans == QNAN) || (nans == SNAN);
+ }
+
+ /**
+ * Check if instance is equal to zero.
+ *
+ * @return true if instance is equal to zero
+ */
+ public boolean isZero() {
+
+ if (isNaN()) {
+ field.setIEEEFlagsBits(DfpField.FLAG_INVALID);
+ dotrap(DfpField.FLAG_INVALID, LESS_THAN_TRAP, this, newInstance(getZero()));
+ return false;
+ }
+
+ return (mant[mant.length - 1] == 0) && !isInfinite();
+ }
+
+ /**
+ * Check if instance is equal to x.
+ *
+ * @param other object to check instance against
+ * @return true if instance is equal to x and neither are NaN, false otherwise
+ */
+ @Override
+ public boolean equals(final Object other) {
+
+ if (other instanceof Dfp) {
+ final Dfp x = (Dfp) other;
+ if (isNaN() || x.isNaN() || field.getRadixDigits() != x.field.getRadixDigits()) {
+ return false;
+ }
+
+ return compare(this, x) == 0;
+ }
+
+ return false;
+ }
+
+ /**
+ * Gets a hashCode for the instance.
+ *
+ * @return a hash code value for this object
+ */
+ @Override
+ public int hashCode() {
+ return 17 + (isZero() ? 0 : (sign << 8)) + (nans << 16) + exp + Arrays.hashCode(mant);
+ }
+
+ /**
+ * Check if instance is not equal to x.
+ *
+ * @param x number to check instance against
+ * @return true if instance is not equal to x and neither are NaN, false otherwise
+ */
+ public boolean unequal(final Dfp x) {
+ if (isNaN() || x.isNaN() || field.getRadixDigits() != x.field.getRadixDigits()) {
+ return false;
+ }
+
+ return greaterThan(x) || lessThan(x);
+ }
+
+ /**
+ * Compare two instances.
+ *
+ * @param a first instance in comparison
+ * @param b second instance in comparison
+ * @return -1 if a<b, 1 if a>b and 0 if a==b Note this method does not properly handle NaNs or
+ * numbers with different precision.
+ */
+ private static int compare(final Dfp a, final Dfp b) {
+ // Ignore the sign of zero
+ if (a.mant[a.mant.length - 1] == 0
+ && b.mant[b.mant.length - 1] == 0
+ && a.nans == FINITE
+ && b.nans == FINITE) {
+ return 0;
+ }
+
+ if (a.sign != b.sign) {
+ if (a.sign == -1) {
+ return -1;
+ } else {
+ return 1;
+ }
+ }
+
+ // deal with the infinities
+ if (a.nans == INFINITE && b.nans == FINITE) {
+ return a.sign;
+ }
+
+ if (a.nans == FINITE && b.nans == INFINITE) {
+ return -b.sign;
+ }
+
+ if (a.nans == INFINITE && b.nans == INFINITE) {
+ return 0;
+ }
+
+ // Handle special case when a or b is zero, by ignoring the exponents
+ if (b.mant[b.mant.length - 1] != 0 && a.mant[b.mant.length - 1] != 0) {
+ if (a.exp < b.exp) {
+ return -a.sign;
+ }
+
+ if (a.exp > b.exp) {
+ return a.sign;
+ }
+ }
+
+ // compare the mantissas
+ for (int i = a.mant.length - 1; i >= 0; i--) {
+ if (a.mant[i] > b.mant[i]) {
+ return a.sign;
+ }
+
+ if (a.mant[i] < b.mant[i]) {
+ return -a.sign;
+ }
+ }
+
+ return 0;
+ }
+
+ /**
+ * Round to nearest integer using the round-half-even method. That is round to nearest integer
+ * unless both are equidistant. In which case round to the even one.
+ *
+ * @return rounded value
+ * @since 3.2
+ */
+ public Dfp rint() {
+ return trunc(DfpField.RoundingMode.ROUND_HALF_EVEN);
+ }
+
+ /**
+ * Round to an integer using the round floor mode. That is, round toward -Infinity
+ *
+ * @return rounded value
+ * @since 3.2
+ */
+ public Dfp floor() {
+ return trunc(DfpField.RoundingMode.ROUND_FLOOR);
+ }
+
+ /**
+ * Round to an integer using the round ceil mode. That is, round toward +Infinity
+ *
+ * @return rounded value
+ * @since 3.2
+ */
+ public Dfp ceil() {
+ return trunc(DfpField.RoundingMode.ROUND_CEIL);
+ }
+
+ /**
+ * Returns the IEEE remainder.
+ *
+ * @param d divisor
+ * @return this less n &times; d, where n is the integer closest to this/d
+ * @since 3.2
+ */
+ public Dfp remainder(final Dfp d) {
+
+ final Dfp result = this.subtract(this.divide(d).rint().multiply(d));
+
+ // IEEE 854-1987 says that if the result is zero, then it carries the sign of this
+ if (result.mant[mant.length - 1] == 0) {
+ result.sign = sign;
+ }
+
+ return result;
+ }
+
+ /**
+ * Does the integer conversions with the specified rounding.
+ *
+ * @param rmode rounding mode to use
+ * @return truncated value
+ */
+ protected Dfp trunc(final DfpField.RoundingMode rmode) {
+ boolean changed = false;
+
+ if (isNaN()) {
+ return newInstance(this);
+ }
+
+ if (nans == INFINITE) {
+ return newInstance(this);
+ }
+
+ if (mant[mant.length - 1] == 0) {
+ // a is zero
+ return newInstance(this);
+ }
+
+ /* If the exponent is less than zero then we can certainly
+ * return zero */
+ if (exp < 0) {
+ field.setIEEEFlagsBits(DfpField.FLAG_INEXACT);
+ Dfp result = newInstance(getZero());
+ result = dotrap(DfpField.FLAG_INEXACT, TRUNC_TRAP, this, result);
+ return result;
+ }
+
+ /* If the exponent is greater than or equal to digits, then it
+ * must already be an integer since there is no precision left
+ * for any fractional part */
+
+ if (exp >= mant.length) {
+ return newInstance(this);
+ }
+
+ /* General case: create another dfp, result, that contains the
+ * a with the fractional part lopped off. */
+
+ Dfp result = newInstance(this);
+ for (int i = 0; i < mant.length - result.exp; i++) {
+ changed |= result.mant[i] != 0;
+ result.mant[i] = 0;
+ }
+
+ if (changed) {
+ switch (rmode) {
+ case ROUND_FLOOR:
+ if (result.sign == -1) {
+ // then we must increment the mantissa by one
+ result = result.add(newInstance(-1));
+ }
+ break;
+
+ case ROUND_CEIL:
+ if (result.sign == 1) {
+ // then we must increment the mantissa by one
+ result = result.add(getOne());
+ }
+ break;
+
+ case ROUND_HALF_EVEN:
+ default:
+ final Dfp half = newInstance("0.5");
+ Dfp a = subtract(result); // difference between this and result
+ a.sign = 1; // force positive (take abs)
+ if (a.greaterThan(half)) {
+ a = newInstance(getOne());
+ a.sign = sign;
+ result = result.add(a);
+ }
+
+ /** If exactly equal to 1/2 and odd then increment */
+ if (a.equals(half)
+ && result.exp > 0
+ && (result.mant[mant.length - result.exp] & 1) != 0) {
+ a = newInstance(getOne());
+ a.sign = sign;
+ result = result.add(a);
+ }
+ break;
+ }
+
+ field.setIEEEFlagsBits(DfpField.FLAG_INEXACT); // signal inexact
+ result = dotrap(DfpField.FLAG_INEXACT, TRUNC_TRAP, this, result);
+ return result;
+ }
+
+ return result;
+ }
+
+ /**
+ * Convert this to an integer. If greater than 2147483647, it returns 2147483647. If less than
+ * -2147483648 it returns -2147483648.
+ *
+ * @return converted number
+ */
+ public int intValue() {
+ Dfp rounded;
+ int result = 0;
+
+ rounded = rint();
+
+ if (rounded.greaterThan(newInstance(2147483647))) {
+ return 2147483647;
+ }
+
+ if (rounded.lessThan(newInstance(-2147483648))) {
+ return -2147483648;
+ }
+
+ for (int i = mant.length - 1; i >= mant.length - rounded.exp; i--) {
+ result = result * RADIX + rounded.mant[i];
+ }
+
+ if (rounded.sign == -1) {
+ result = -result;
+ }
+
+ return result;
+ }
+
+ /**
+ * Get the exponent of the greatest power of 10000 that is less than or equal to the absolute
+ * value of this. I.E. if this is 10<sup>6</sup> then log10K would return 1.
+ *
+ * @return integer base 10000 logarithm
+ */
+ public int log10K() {
+ return exp - 1;
+ }
+
+ /**
+ * Get the specified power of 10000.
+ *
+ * @param e desired power
+ * @return 10000<sup>e</sup>
+ */
+ public Dfp power10K(final int e) {
+ Dfp d = newInstance(getOne());
+ d.exp = e + 1;
+ return d;
+ }
+
+ /**
+ * Get the exponent of the greatest power of 10 that is less than or equal to abs(this).
+ *
+ * @return integer base 10 logarithm
+ * @since 3.2
+ */
+ public int intLog10() {
+ if (mant[mant.length - 1] > 1000) {
+ return exp * 4 - 1;
+ }
+ if (mant[mant.length - 1] > 100) {
+ return exp * 4 - 2;
+ }
+ if (mant[mant.length - 1] > 10) {
+ return exp * 4 - 3;
+ }
+ return exp * 4 - 4;
+ }
+
+ /**
+ * Return the specified power of 10.
+ *
+ * @param e desired power
+ * @return 10<sup>e</sup>
+ */
+ public Dfp power10(final int e) {
+ Dfp d = newInstance(getOne());
+
+ if (e >= 0) {
+ d.exp = e / 4 + 1;
+ } else {
+ d.exp = (e + 1) / 4;
+ }
+
+ switch ((e % 4 + 4) % 4) {
+ case 0:
+ break;
+ case 1:
+ d = d.multiply(10);
+ break;
+ case 2:
+ d = d.multiply(100);
+ break;
+ default:
+ d = d.multiply(1000);
+ }
+
+ return d;
+ }
+
+ /**
+ * Negate the mantissa of this by computing the complement. Leaves the sign bit unchanged, used
+ * internally by add. Denormalized numbers are handled properly here.
+ *
+ * @param extra ???
+ * @return ???
+ */
+ protected int complement(int extra) {
+
+ extra = RADIX - extra;
+ for (int i = 0; i < mant.length; i++) {
+ mant[i] = RADIX - mant[i] - 1;
+ }
+
+ int rh = extra / RADIX;
+ extra -= rh * RADIX;
+ for (int i = 0; i < mant.length; i++) {
+ final int r = mant[i] + rh;
+ rh = r / RADIX;
+ mant[i] = r - rh * RADIX;
+ }
+
+ return extra;
+ }
+
+ /**
+ * Add x to this.
+ *
+ * @param x number to add
+ * @return sum of this and x
+ */
+ public Dfp add(final Dfp x) {
+
+ // make sure we don't mix number with different precision
+ if (field.getRadixDigits() != x.field.getRadixDigits()) {
+ field.setIEEEFlagsBits(DfpField.FLAG_INVALID);
+ final Dfp result = newInstance(getZero());
+ result.nans = QNAN;
+ return dotrap(DfpField.FLAG_INVALID, ADD_TRAP, x, result);
+ }
+
+ /* handle special cases */
+ if (nans != FINITE || x.nans != FINITE) {
+ if (isNaN()) {
+ return this;
+ }
+
+ if (x.isNaN()) {
+ return x;
+ }
+
+ if (nans == INFINITE && x.nans == FINITE) {
+ return this;
+ }
+
+ if (x.nans == INFINITE && nans == FINITE) {
+ return x;
+ }
+
+ if (x.nans == INFINITE && nans == INFINITE && sign == x.sign) {
+ return x;
+ }
+
+ if (x.nans == INFINITE && nans == INFINITE && sign != x.sign) {
+ field.setIEEEFlagsBits(DfpField.FLAG_INVALID);
+ Dfp result = newInstance(getZero());
+ result.nans = QNAN;
+ result = dotrap(DfpField.FLAG_INVALID, ADD_TRAP, x, result);
+ return result;
+ }
+ }
+
+ /* copy this and the arg */
+ Dfp a = newInstance(this);
+ Dfp b = newInstance(x);
+
+ /* initialize the result object */
+ Dfp result = newInstance(getZero());
+
+ /* Make all numbers positive, but remember their sign */
+ final byte asign = a.sign;
+ final byte bsign = b.sign;
+
+ a.sign = 1;
+ b.sign = 1;
+
+ /* The result will be signed like the arg with greatest magnitude */
+ byte rsign = bsign;
+ if (compare(a, b) > 0) {
+ rsign = asign;
+ }
+
+ /* Handle special case when a or b is zero, by setting the exponent
+ of the zero number equal to the other one. This avoids an alignment
+ which would cause catastropic loss of precision */
+ if (b.mant[mant.length - 1] == 0) {
+ b.exp = a.exp;
+ }
+
+ if (a.mant[mant.length - 1] == 0) {
+ a.exp = b.exp;
+ }
+
+ /* align number with the smaller exponent */
+ int aextradigit = 0;
+ int bextradigit = 0;
+ if (a.exp < b.exp) {
+ aextradigit = a.align(b.exp);
+ } else {
+ bextradigit = b.align(a.exp);
+ }
+
+ /* complement the smaller of the two if the signs are different */
+ if (asign != bsign) {
+ if (asign == rsign) {
+ bextradigit = b.complement(bextradigit);
+ } else {
+ aextradigit = a.complement(aextradigit);
+ }
+ }
+
+ /* add the mantissas */
+ int rh = 0; /* acts as a carry */
+ for (int i = 0; i < mant.length; i++) {
+ final int r = a.mant[i] + b.mant[i] + rh;
+ rh = r / RADIX;
+ result.mant[i] = r - rh * RADIX;
+ }
+ result.exp = a.exp;
+ result.sign = rsign;
+
+ /* handle overflow -- note, when asign!=bsign an overflow is
+ * normal and should be ignored. */
+
+ if (rh != 0 && (asign == bsign)) {
+ final int lostdigit = result.mant[0];
+ result.shiftRight();
+ result.mant[mant.length - 1] = rh;
+ final int excp = result.round(lostdigit);
+ if (excp != 0) {
+ result = dotrap(excp, ADD_TRAP, x, result);
+ }
+ }
+
+ /* normalize the result */
+ for (int i = 0; i < mant.length; i++) {
+ if (result.mant[mant.length - 1] != 0) {
+ break;
+ }
+ result.shiftLeft();
+ if (i == 0) {
+ result.mant[0] = aextradigit + bextradigit;
+ aextradigit = 0;
+ bextradigit = 0;
+ }
+ }
+
+ /* result is zero if after normalization the most sig. digit is zero */
+ if (result.mant[mant.length - 1] == 0) {
+ result.exp = 0;
+
+ if (asign != bsign) {
+ // Unless adding 2 negative zeros, sign is positive
+ result.sign = 1; // Per IEEE 854-1987 Section 6.3
+ }
+ }
+
+ /* Call round to test for over/under flows */
+ final int excp = result.round(aextradigit + bextradigit);
+ if (excp != 0) {
+ result = dotrap(excp, ADD_TRAP, x, result);
+ }
+
+ return result;
+ }
+
+ /**
+ * Returns a number that is this number with the sign bit reversed.
+ *
+ * @return the opposite of this
+ */
+ public Dfp negate() {
+ Dfp result = newInstance(this);
+ result.sign = (byte) -result.sign;
+ return result;
+ }
+
+ /**
+ * Subtract x from this.
+ *
+ * @param x number to subtract
+ * @return difference of this and a
+ */
+ public Dfp subtract(final Dfp x) {
+ return add(x.negate());
+ }
+
+ /**
+ * Round this given the next digit n using the current rounding mode.
+ *
+ * @param n ???
+ * @return the IEEE flag if an exception occurred
+ */
+ protected int round(int n) {
+ boolean inc = false;
+ switch (field.getRoundingMode()) {
+ case ROUND_DOWN:
+ inc = false;
+ break;
+
+ case ROUND_UP:
+ inc = n != 0; // round up if n!=0
+ break;
+
+ case ROUND_HALF_UP:
+ inc = n >= 5000; // round half up
+ break;
+
+ case ROUND_HALF_DOWN:
+ inc = n > 5000; // round half down
+ break;
+
+ case ROUND_HALF_EVEN:
+ inc = n > 5000 || (n == 5000 && (mant[0] & 1) == 1); // round half-even
+ break;
+
+ case ROUND_HALF_ODD:
+ inc = n > 5000 || (n == 5000 && (mant[0] & 1) == 0); // round half-odd
+ break;
+
+ case ROUND_CEIL:
+ inc = sign == 1 && n != 0; // round ceil
+ break;
+
+ case ROUND_FLOOR:
+ default:
+ inc = sign == -1 && n != 0; // round floor
+ break;
+ }
+
+ if (inc) {
+ // increment if necessary
+ int rh = 1;
+ for (int i = 0; i < mant.length; i++) {
+ final int r = mant[i] + rh;
+ rh = r / RADIX;
+ mant[i] = r - rh * RADIX;
+ }
+
+ if (rh != 0) {
+ shiftRight();
+ mant[mant.length - 1] = rh;
+ }
+ }
+
+ // check for exceptional cases and raise signals if necessary
+ if (exp < MIN_EXP) {
+ // Gradual Underflow
+ field.setIEEEFlagsBits(DfpField.FLAG_UNDERFLOW);
+ return DfpField.FLAG_UNDERFLOW;
+ }
+
+ if (exp > MAX_EXP) {
+ // Overflow
+ field.setIEEEFlagsBits(DfpField.FLAG_OVERFLOW);
+ return DfpField.FLAG_OVERFLOW;
+ }
+
+ if (n != 0) {
+ // Inexact
+ field.setIEEEFlagsBits(DfpField.FLAG_INEXACT);
+ return DfpField.FLAG_INEXACT;
+ }
+
+ return 0;
+ }
+
+ /**
+ * Multiply this by x.
+ *
+ * @param x multiplicand
+ * @return product of this and x
+ */
+ public Dfp multiply(final Dfp x) {
+
+ // make sure we don't mix number with different precision
+ if (field.getRadixDigits() != x.field.getRadixDigits()) {
+ field.setIEEEFlagsBits(DfpField.FLAG_INVALID);
+ final Dfp result = newInstance(getZero());
+ result.nans = QNAN;
+ return dotrap(DfpField.FLAG_INVALID, MULTIPLY_TRAP, x, result);
+ }
+
+ Dfp result = newInstance(getZero());
+
+ /* handle special cases */
+ if (nans != FINITE || x.nans != FINITE) {
+ if (isNaN()) {
+ return this;
+ }
+
+ if (x.isNaN()) {
+ return x;
+ }
+
+ if (nans == INFINITE && x.nans == FINITE && x.mant[mant.length - 1] != 0) {
+ result = newInstance(this);
+ result.sign = (byte) (sign * x.sign);
+ return result;
+ }
+
+ if (x.nans == INFINITE && nans == FINITE && mant[mant.length - 1] != 0) {
+ result = newInstance(x);
+ result.sign = (byte) (sign * x.sign);
+ return result;
+ }
+
+ if (x.nans == INFINITE && nans == INFINITE) {
+ result = newInstance(this);
+ result.sign = (byte) (sign * x.sign);
+ return result;
+ }
+
+ if ((x.nans == INFINITE && nans == FINITE && mant[mant.length - 1] == 0)
+ || (nans == INFINITE && x.nans == FINITE && x.mant[mant.length - 1] == 0)) {
+ field.setIEEEFlagsBits(DfpField.FLAG_INVALID);
+ result = newInstance(getZero());
+ result.nans = QNAN;
+ result = dotrap(DfpField.FLAG_INVALID, MULTIPLY_TRAP, x, result);
+ return result;
+ }
+ }
+
+ int[] product = new int[mant.length * 2]; // Big enough to hold even the largest result
+
+ for (int i = 0; i < mant.length; i++) {
+ int rh = 0; // acts as a carry
+ for (int j = 0; j < mant.length; j++) {
+ int r = mant[i] * x.mant[j]; // multiply the 2 digits
+ r += product[i + j] + rh; // add to the product digit with carry in
+
+ rh = r / RADIX;
+ product[i + j] = r - rh * RADIX;
+ }
+ product[i + mant.length] = rh;
+ }
+
+ // Find the most sig digit
+ int md = mant.length * 2 - 1; // default, in case result is zero
+ for (int i = mant.length * 2 - 1; i >= 0; i--) {
+ if (product[i] != 0) {
+ md = i;
+ break;
+ }
+ }
+
+ // Copy the digits into the result
+ for (int i = 0; i < mant.length; i++) {
+ result.mant[mant.length - i - 1] = product[md - i];
+ }
+
+ // Fixup the exponent.
+ result.exp = exp + x.exp + md - 2 * mant.length + 1;
+ result.sign = (byte) ((sign == x.sign) ? 1 : -1);
+
+ if (result.mant[mant.length - 1] == 0) {
+ // if result is zero, set exp to zero
+ result.exp = 0;
+ }
+
+ final int excp;
+ if (md > (mant.length - 1)) {
+ excp = result.round(product[md - mant.length]);
+ } else {
+ excp = result.round(0); // has no effect except to check status
+ }
+
+ if (excp != 0) {
+ result = dotrap(excp, MULTIPLY_TRAP, x, result);
+ }
+
+ return result;
+ }
+
+ /**
+ * Multiply this by a single digit x.
+ *
+ * @param x multiplicand
+ * @return product of this and x
+ */
+ public Dfp multiply(final int x) {
+ if (x >= 0 && x < RADIX) {
+ return multiplyFast(x);
+ } else {
+ return multiply(newInstance(x));
+ }
+ }
+
+ /**
+ * Multiply this by a single digit 0&lt;=x&lt;radix. There are speed advantages in this special
+ * case.
+ *
+ * @param x multiplicand
+ * @return product of this and x
+ */
+ private Dfp multiplyFast(final int x) {
+ Dfp result = newInstance(this);
+
+ /* handle special cases */
+ if (nans != FINITE) {
+ if (isNaN()) {
+ return this;
+ }
+
+ if (nans == INFINITE && x != 0) {
+ result = newInstance(this);
+ return result;
+ }
+
+ if (nans == INFINITE && x == 0) {
+ field.setIEEEFlagsBits(DfpField.FLAG_INVALID);
+ result = newInstance(getZero());
+ result.nans = QNAN;
+ result =
+ dotrap(
+ DfpField.FLAG_INVALID,
+ MULTIPLY_TRAP,
+ newInstance(getZero()),
+ result);
+ return result;
+ }
+ }
+
+ /* range check x */
+ if (x < 0 || x >= RADIX) {
+ field.setIEEEFlagsBits(DfpField.FLAG_INVALID);
+ result = newInstance(getZero());
+ result.nans = QNAN;
+ result = dotrap(DfpField.FLAG_INVALID, MULTIPLY_TRAP, result, result);
+ return result;
+ }
+
+ int rh = 0;
+ for (int i = 0; i < mant.length; i++) {
+ final int r = mant[i] * x + rh;
+ rh = r / RADIX;
+ result.mant[i] = r - rh * RADIX;
+ }
+
+ int lostdigit = 0;
+ if (rh != 0) {
+ lostdigit = result.mant[0];
+ result.shiftRight();
+ result.mant[mant.length - 1] = rh;
+ }
+
+ if (result.mant[mant.length - 1] == 0) { // if result is zero, set exp to zero
+ result.exp = 0;
+ }
+
+ final int excp = result.round(lostdigit);
+ if (excp != 0) {
+ result = dotrap(excp, MULTIPLY_TRAP, result, result);
+ }
+
+ return result;
+ }
+
+ /**
+ * Divide this by divisor.
+ *
+ * @param divisor divisor
+ * @return quotient of this by divisor
+ */
+ public Dfp divide(Dfp divisor) {
+ int dividend[]; // current status of the dividend
+ int quotient[]; // quotient
+ int remainder[]; // remainder
+ int qd; // current quotient digit we're working with
+ int nsqd; // number of significant quotient digits we have
+ int trial = 0; // trial quotient digit
+ int minadj; // minimum adjustment
+ boolean trialgood; // Flag to indicate a good trail digit
+ int md = 0; // most sig digit in result
+ int excp; // exceptions
+
+ // make sure we don't mix number with different precision
+ if (field.getRadixDigits() != divisor.field.getRadixDigits()) {
+ field.setIEEEFlagsBits(DfpField.FLAG_INVALID);
+ final Dfp result = newInstance(getZero());
+ result.nans = QNAN;
+ return dotrap(DfpField.FLAG_INVALID, DIVIDE_TRAP, divisor, result);
+ }
+
+ Dfp result = newInstance(getZero());
+
+ /* handle special cases */
+ if (nans != FINITE || divisor.nans != FINITE) {
+ if (isNaN()) {
+ return this;
+ }
+
+ if (divisor.isNaN()) {
+ return divisor;
+ }
+
+ if (nans == INFINITE && divisor.nans == FINITE) {
+ result = newInstance(this);
+ result.sign = (byte) (sign * divisor.sign);
+ return result;
+ }
+
+ if (divisor.nans == INFINITE && nans == FINITE) {
+ result = newInstance(getZero());
+ result.sign = (byte) (sign * divisor.sign);
+ return result;
+ }
+
+ if (divisor.nans == INFINITE && nans == INFINITE) {
+ field.setIEEEFlagsBits(DfpField.FLAG_INVALID);
+ result = newInstance(getZero());
+ result.nans = QNAN;
+ result = dotrap(DfpField.FLAG_INVALID, DIVIDE_TRAP, divisor, result);
+ return result;
+ }
+ }
+
+ /* Test for divide by zero */
+ if (divisor.mant[mant.length - 1] == 0) {
+ field.setIEEEFlagsBits(DfpField.FLAG_DIV_ZERO);
+ result = newInstance(getZero());
+ result.sign = (byte) (sign * divisor.sign);
+ result.nans = INFINITE;
+ result = dotrap(DfpField.FLAG_DIV_ZERO, DIVIDE_TRAP, divisor, result);
+ return result;
+ }
+
+ dividend = new int[mant.length + 1]; // one extra digit needed
+ quotient =
+ new int[mant.length + 2]; // two extra digits needed 1 for overflow, 1 for rounding
+ remainder = new int[mant.length + 1]; // one extra digit needed
+
+ /* Initialize our most significant digits to zero */
+
+ dividend[mant.length] = 0;
+ quotient[mant.length] = 0;
+ quotient[mant.length + 1] = 0;
+ remainder[mant.length] = 0;
+
+ /* copy our mantissa into the dividend, initialize the
+ quotient while we are at it */
+
+ for (int i = 0; i < mant.length; i++) {
+ dividend[i] = mant[i];
+ quotient[i] = 0;
+ remainder[i] = 0;
+ }
+
+ /* outer loop. Once per quotient digit */
+ nsqd = 0;
+ for (qd = mant.length + 1; qd >= 0; qd--) {
+ /* Determine outer limits of our quotient digit */
+
+ // r = most sig 2 digits of dividend
+ final int divMsb = dividend[mant.length] * RADIX + dividend[mant.length - 1];
+ int min = divMsb / (divisor.mant[mant.length - 1] + 1);
+ int max = (divMsb + 1) / divisor.mant[mant.length - 1];
+
+ trialgood = false;
+ while (!trialgood) {
+ // try the mean
+ trial = (min + max) / 2;
+
+ /* Multiply by divisor and store as remainder */
+ int rh = 0;
+ for (int i = 0; i < mant.length + 1; i++) {
+ int dm = (i < mant.length) ? divisor.mant[i] : 0;
+ final int r = (dm * trial) + rh;
+ rh = r / RADIX;
+ remainder[i] = r - rh * RADIX;
+ }
+
+ /* subtract the remainder from the dividend */
+ rh = 1; // carry in to aid the subtraction
+ for (int i = 0; i < mant.length + 1; i++) {
+ final int r = ((RADIX - 1) - remainder[i]) + dividend[i] + rh;
+ rh = r / RADIX;
+ remainder[i] = r - rh * RADIX;
+ }
+
+ /* Lets analyze what we have here */
+ if (rh == 0) {
+ // trial is too big -- negative remainder
+ max = trial - 1;
+ continue;
+ }
+
+ /* find out how far off the remainder is telling us we are */
+ minadj = (remainder[mant.length] * RADIX) + remainder[mant.length - 1];
+ minadj /= divisor.mant[mant.length - 1] + 1;
+
+ if (minadj >= 2) {
+ min = trial + minadj; // update the minimum
+ continue;
+ }
+
+ /* May have a good one here, check more thoroughly. Basically
+ its a good one if it is less than the divisor */
+ trialgood = false; // assume false
+ for (int i = mant.length - 1; i >= 0; i--) {
+ if (divisor.mant[i] > remainder[i]) {
+ trialgood = true;
+ }
+ if (divisor.mant[i] < remainder[i]) {
+ break;
+ }
+ }
+
+ if (remainder[mant.length] != 0) {
+ trialgood = false;
+ }
+
+ if (trialgood == false) {
+ min = trial + 1;
+ }
+ }
+
+ /* Great we have a digit! */
+ quotient[qd] = trial;
+ if (trial != 0 || nsqd != 0) {
+ nsqd++;
+ }
+
+ if (field.getRoundingMode() == DfpField.RoundingMode.ROUND_DOWN
+ && nsqd == mant.length) {
+ // We have enough for this mode
+ break;
+ }
+
+ if (nsqd > mant.length) {
+ // We have enough digits
+ break;
+ }
+
+ /* move the remainder into the dividend while left shifting */
+ dividend[0] = 0;
+ for (int i = 0; i < mant.length; i++) {
+ dividend[i + 1] = remainder[i];
+ }
+ }
+
+ /* Find the most sig digit */
+ md = mant.length; // default
+ for (int i = mant.length + 1; i >= 0; i--) {
+ if (quotient[i] != 0) {
+ md = i;
+ break;
+ }
+ }
+
+ /* Copy the digits into the result */
+ for (int i = 0; i < mant.length; i++) {
+ result.mant[mant.length - i - 1] = quotient[md - i];
+ }
+
+ /* Fixup the exponent. */
+ result.exp = exp - divisor.exp + md - mant.length;
+ result.sign = (byte) ((sign == divisor.sign) ? 1 : -1);
+
+ if (result.mant[mant.length - 1] == 0) { // if result is zero, set exp to zero
+ result.exp = 0;
+ }
+
+ if (md > (mant.length - 1)) {
+ excp = result.round(quotient[md - mant.length]);
+ } else {
+ excp = result.round(0);
+ }
+
+ if (excp != 0) {
+ result = dotrap(excp, DIVIDE_TRAP, divisor, result);
+ }
+
+ return result;
+ }
+
+ /**
+ * Divide by a single digit less than radix. Special case, so there are speed advantages. 0
+ * &lt;= divisor &lt; radix
+ *
+ * @param divisor divisor
+ * @return quotient of this by divisor
+ */
+ public Dfp divide(int divisor) {
+
+ // Handle special cases
+ if (nans != FINITE) {
+ if (isNaN()) {
+ return this;
+ }
+
+ if (nans == INFINITE) {
+ return newInstance(this);
+ }
+ }
+
+ // Test for divide by zero
+ if (divisor == 0) {
+ field.setIEEEFlagsBits(DfpField.FLAG_DIV_ZERO);
+ Dfp result = newInstance(getZero());
+ result.sign = sign;
+ result.nans = INFINITE;
+ result = dotrap(DfpField.FLAG_DIV_ZERO, DIVIDE_TRAP, getZero(), result);
+ return result;
+ }
+
+ // range check divisor
+ if (divisor < 0 || divisor >= RADIX) {
+ field.setIEEEFlagsBits(DfpField.FLAG_INVALID);
+ Dfp result = newInstance(getZero());
+ result.nans = QNAN;
+ result = dotrap(DfpField.FLAG_INVALID, DIVIDE_TRAP, result, result);
+ return result;
+ }
+
+ Dfp result = newInstance(this);
+
+ int rl = 0;
+ for (int i = mant.length - 1; i >= 0; i--) {
+ final int r = rl * RADIX + result.mant[i];
+ final int rh = r / divisor;
+ rl = r - rh * divisor;
+ result.mant[i] = rh;
+ }
+
+ if (result.mant[mant.length - 1] == 0) {
+ // normalize
+ result.shiftLeft();
+ final int r = rl * RADIX; // compute the next digit and put it in
+ final int rh = r / divisor;
+ rl = r - rh * divisor;
+ result.mant[0] = rh;
+ }
+
+ final int excp = result.round(rl * RADIX / divisor); // do the rounding
+ if (excp != 0) {
+ result = dotrap(excp, DIVIDE_TRAP, result, result);
+ }
+
+ return result;
+ }
+
+ /** {@inheritDoc} */
+ public Dfp reciprocal() {
+ return field.getOne().divide(this);
+ }
+
+ /**
+ * Compute the square root.
+ *
+ * @return square root of the instance
+ * @since 3.2
+ */
+ public Dfp sqrt() {
+
+ // check for unusual cases
+ if (nans == FINITE && mant[mant.length - 1] == 0) {
+ // if zero
+ return newInstance(this);
+ }
+
+ if (nans != FINITE) {
+ if (nans == INFINITE && sign == 1) {
+ // if positive infinity
+ return newInstance(this);
+ }
+
+ if (nans == QNAN) {
+ return newInstance(this);
+ }
+
+ if (nans == SNAN) {
+ Dfp result;
+
+ field.setIEEEFlagsBits(DfpField.FLAG_INVALID);
+ result = newInstance(this);
+ result = dotrap(DfpField.FLAG_INVALID, SQRT_TRAP, null, result);
+ return result;
+ }
+ }
+
+ if (sign == -1) {
+ // if negative
+ Dfp result;
+
+ field.setIEEEFlagsBits(DfpField.FLAG_INVALID);
+ result = newInstance(this);
+ result.nans = QNAN;
+ result = dotrap(DfpField.FLAG_INVALID, SQRT_TRAP, null, result);
+ return result;
+ }
+
+ Dfp x = newInstance(this);
+
+ /* Lets make a reasonable guess as to the size of the square root */
+ if (x.exp < -1 || x.exp > 1) {
+ x.exp = this.exp / 2;
+ }
+
+ /* Coarsely estimate the mantissa */
+ switch (x.mant[mant.length - 1] / 2000) {
+ case 0:
+ x.mant[mant.length - 1] = x.mant[mant.length - 1] / 2 + 1;
+ break;
+ case 2:
+ x.mant[mant.length - 1] = 1500;
+ break;
+ case 3:
+ x.mant[mant.length - 1] = 2200;
+ break;
+ default:
+ x.mant[mant.length - 1] = 3000;
+ }
+
+ Dfp dx = newInstance(x);
+
+ /* Now that we have the first pass estimate, compute the rest
+ by the formula dx = (y - x*x) / (2x); */
+
+ Dfp px = getZero();
+ Dfp ppx = getZero();
+ while (x.unequal(px)) {
+ dx = newInstance(x);
+ dx.sign = -1;
+ dx = dx.add(this.divide(x));
+ dx = dx.divide(2);
+ ppx = px;
+ px = x;
+ x = x.add(dx);
+
+ if (x.equals(ppx)) {
+ // alternating between two values
+ break;
+ }
+
+ // if dx is zero, break. Note testing the most sig digit
+ // is a sufficient test since dx is normalized
+ if (dx.mant[mant.length - 1] == 0) {
+ break;
+ }
+ }
+
+ return x;
+ }
+
+ /**
+ * Get a string representation of the instance.
+ *
+ * @return string representation of the instance
+ */
+ @Override
+ public String toString() {
+ if (nans != FINITE) {
+ // if non-finite exceptional cases
+ if (nans == INFINITE) {
+ return (sign < 0) ? NEG_INFINITY_STRING : POS_INFINITY_STRING;
+ } else {
+ return NAN_STRING;
+ }
+ }
+
+ if (exp > mant.length || exp < -1) {
+ return dfp2sci();
+ }
+
+ return dfp2string();
+ }
+
+ /**
+ * Convert an instance to a string using scientific notation.
+ *
+ * @return string representation of the instance in scientific notation
+ */
+ protected String dfp2sci() {
+ char rawdigits[] = new char[mant.length * 4];
+ char outputbuffer[] = new char[mant.length * 4 + 20];
+ int p;
+ int q;
+ int e;
+ int ae;
+ int shf;
+
+ // Get all the digits
+ p = 0;
+ for (int i = mant.length - 1; i >= 0; i--) {
+ rawdigits[p++] = (char) ((mant[i] / 1000) + '0');
+ rawdigits[p++] = (char) (((mant[i] / 100) % 10) + '0');
+ rawdigits[p++] = (char) (((mant[i] / 10) % 10) + '0');
+ rawdigits[p++] = (char) (((mant[i]) % 10) + '0');
+ }
+
+ // Find the first non-zero one
+ for (p = 0; p < rawdigits.length; p++) {
+ if (rawdigits[p] != '0') {
+ break;
+ }
+ }
+ shf = p;
+
+ // Now do the conversion
+ q = 0;
+ if (sign == -1) {
+ outputbuffer[q++] = '-';
+ }
+
+ if (p != rawdigits.length) {
+ // there are non zero digits...
+ outputbuffer[q++] = rawdigits[p++];
+ outputbuffer[q++] = '.';
+
+ while (p < rawdigits.length) {
+ outputbuffer[q++] = rawdigits[p++];
+ }
+ } else {
+ outputbuffer[q++] = '0';
+ outputbuffer[q++] = '.';
+ outputbuffer[q++] = '0';
+ outputbuffer[q++] = 'e';
+ outputbuffer[q++] = '0';
+ return new String(outputbuffer, 0, 5);
+ }
+
+ outputbuffer[q++] = 'e';
+
+ // Find the msd of the exponent
+
+ e = exp * 4 - shf - 1;
+ ae = e;
+ if (e < 0) {
+ ae = -e;
+ }
+
+ // Find the largest p such that p < e
+ for (p = 1000000000; p > ae; p /= 10) {
+ // nothing to do
+ }
+
+ if (e < 0) {
+ outputbuffer[q++] = '-';
+ }
+
+ while (p > 0) {
+ outputbuffer[q++] = (char) (ae / p + '0');
+ ae %= p;
+ p /= 10;
+ }
+
+ return new String(outputbuffer, 0, q);
+ }
+
+ /**
+ * Convert an instance to a string using normal notation.
+ *
+ * @return string representation of the instance in normal notation
+ */
+ protected String dfp2string() {
+ char buffer[] = new char[mant.length * 4 + 20];
+ int p = 1;
+ int q;
+ int e = exp;
+ boolean pointInserted = false;
+
+ buffer[0] = ' ';
+
+ if (e <= 0) {
+ buffer[p++] = '0';
+ buffer[p++] = '.';
+ pointInserted = true;
+ }
+
+ while (e < 0) {
+ buffer[p++] = '0';
+ buffer[p++] = '0';
+ buffer[p++] = '0';
+ buffer[p++] = '0';
+ e++;
+ }
+
+ for (int i = mant.length - 1; i >= 0; i--) {
+ buffer[p++] = (char) ((mant[i] / 1000) + '0');
+ buffer[p++] = (char) (((mant[i] / 100) % 10) + '0');
+ buffer[p++] = (char) (((mant[i] / 10) % 10) + '0');
+ buffer[p++] = (char) (((mant[i]) % 10) + '0');
+ if (--e == 0) {
+ buffer[p++] = '.';
+ pointInserted = true;
+ }
+ }
+
+ while (e > 0) {
+ buffer[p++] = '0';
+ buffer[p++] = '0';
+ buffer[p++] = '0';
+ buffer[p++] = '0';
+ e--;
+ }
+
+ if (!pointInserted) {
+ // Ensure we have a radix point!
+ buffer[p++] = '.';
+ }
+
+ // Suppress leading zeros
+ q = 1;
+ while (buffer[q] == '0') {
+ q++;
+ }
+ if (buffer[q] == '.') {
+ q--;
+ }
+
+ // Suppress trailing zeros
+ while (buffer[p - 1] == '0') {
+ p--;
+ }
+
+ // Insert sign
+ if (sign < 0) {
+ buffer[--q] = '-';
+ }
+
+ return new String(buffer, q, p - q);
+ }
+
+ /**
+ * Raises a trap. This does not set the corresponding flag however.
+ *
+ * @param type the trap type
+ * @param what - name of routine trap occurred in
+ * @param oper - input operator to function
+ * @param result - the result computed prior to the trap
+ * @return The suggested return value from the trap handler
+ */
+ public Dfp dotrap(int type, String what, Dfp oper, Dfp result) {
+ Dfp def = result;
+
+ switch (type) {
+ case DfpField.FLAG_INVALID:
+ def = newInstance(getZero());
+ def.sign = result.sign;
+ def.nans = QNAN;
+ break;
+
+ case DfpField.FLAG_DIV_ZERO:
+ if (nans == FINITE && mant[mant.length - 1] != 0) {
+ // normal case, we are finite, non-zero
+ def = newInstance(getZero());
+ def.sign = (byte) (sign * oper.sign);
+ def.nans = INFINITE;
+ }
+
+ if (nans == FINITE && mant[mant.length - 1] == 0) {
+ // 0/0
+ def = newInstance(getZero());
+ def.nans = QNAN;
+ }
+
+ if (nans == INFINITE || nans == QNAN) {
+ def = newInstance(getZero());
+ def.nans = QNAN;
+ }
+
+ if (nans == INFINITE || nans == SNAN) {
+ def = newInstance(getZero());
+ def.nans = QNAN;
+ }
+ break;
+
+ case DfpField.FLAG_UNDERFLOW:
+ if ((result.exp + mant.length) < MIN_EXP) {
+ def = newInstance(getZero());
+ def.sign = result.sign;
+ } else {
+ def = newInstance(result); // gradual underflow
+ }
+ result.exp += ERR_SCALE;
+ break;
+
+ case DfpField.FLAG_OVERFLOW:
+ result.exp -= ERR_SCALE;
+ def = newInstance(getZero());
+ def.sign = result.sign;
+ def.nans = INFINITE;
+ break;
+
+ default:
+ def = result;
+ break;
+ }
+
+ return trap(type, what, oper, def, result);
+ }
+
+ /**
+ * Trap handler. Subclasses may override this to provide trap functionality per IEEE 854-1987.
+ *
+ * @param type The exception type - e.g. FLAG_OVERFLOW
+ * @param what The name of the routine we were in e.g. divide()
+ * @param oper An operand to this function if any
+ * @param def The default return value if trap not enabled
+ * @param result The result that is specified to be delivered per IEEE 854, if any
+ * @return the value that should be return by the operation triggering the trap
+ */
+ protected Dfp trap(int type, String what, Dfp oper, Dfp def, Dfp result) {
+ return def;
+ }
+
+ /**
+ * Returns the type - one of FINITE, INFINITE, SNAN, QNAN.
+ *
+ * @return type of the number
+ */
+ public int classify() {
+ return nans;
+ }
+
+ /**
+ * Creates an instance that is the same as x except that it has the sign of y. abs(x) =
+ * dfp.copysign(x, dfp.one)
+ *
+ * @param x number to get the value from
+ * @param y number to get the sign from
+ * @return a number with the value of x and the sign of y
+ */
+ public static Dfp copysign(final Dfp x, final Dfp y) {
+ Dfp result = x.newInstance(x);
+ result.sign = y.sign;
+ return result;
+ }
+
+ /**
+ * Returns the next number greater than this one in the direction of x. If this==x then simply
+ * returns this.
+ *
+ * @param x direction where to look at
+ * @return closest number next to instance in the direction of x
+ */
+ public Dfp nextAfter(final Dfp x) {
+
+ // make sure we don't mix number with different precision
+ if (field.getRadixDigits() != x.field.getRadixDigits()) {
+ field.setIEEEFlagsBits(DfpField.FLAG_INVALID);
+ final Dfp result = newInstance(getZero());
+ result.nans = QNAN;
+ return dotrap(DfpField.FLAG_INVALID, NEXT_AFTER_TRAP, x, result);
+ }
+
+ // if this is greater than x
+ boolean up = false;
+ if (this.lessThan(x)) {
+ up = true;
+ }
+
+ if (compare(this, x) == 0) {
+ return newInstance(x);
+ }
+
+ if (lessThan(getZero())) {
+ up = !up;
+ }
+
+ final Dfp inc;
+ Dfp result;
+ if (up) {
+ inc = newInstance(getOne());
+ inc.exp = this.exp - mant.length + 1;
+ inc.sign = this.sign;
+
+ if (this.equals(getZero())) {
+ inc.exp = MIN_EXP - mant.length;
+ }
+
+ result = add(inc);
+ } else {
+ inc = newInstance(getOne());
+ inc.exp = this.exp;
+ inc.sign = this.sign;
+
+ if (this.equals(inc)) {
+ inc.exp = this.exp - mant.length;
+ } else {
+ inc.exp = this.exp - mant.length + 1;
+ }
+
+ if (this.equals(getZero())) {
+ inc.exp = MIN_EXP - mant.length;
+ }
+
+ result = this.subtract(inc);
+ }
+
+ if (result.classify() == INFINITE && this.classify() != INFINITE) {
+ field.setIEEEFlagsBits(DfpField.FLAG_INEXACT);
+ result = dotrap(DfpField.FLAG_INEXACT, NEXT_AFTER_TRAP, x, result);
+ }
+
+ if (result.equals(getZero()) && this.equals(getZero()) == false) {
+ field.setIEEEFlagsBits(DfpField.FLAG_INEXACT);
+ result = dotrap(DfpField.FLAG_INEXACT, NEXT_AFTER_TRAP, x, result);
+ }
+
+ return result;
+ }
+
+ /**
+ * Convert the instance into a double.
+ *
+ * @return a double approximating the instance
+ * @see #toSplitDouble()
+ */
+ public double toDouble() {
+
+ if (isInfinite()) {
+ if (lessThan(getZero())) {
+ return Double.NEGATIVE_INFINITY;
+ } else {
+ return Double.POSITIVE_INFINITY;
+ }
+ }
+
+ if (isNaN()) {
+ return Double.NaN;
+ }
+
+ Dfp y = this;
+ boolean negate = false;
+ int cmp0 = compare(this, getZero());
+ if (cmp0 == 0) {
+ return sign < 0 ? -0.0 : +0.0;
+ } else if (cmp0 < 0) {
+ y = negate();
+ negate = true;
+ }
+
+ /* Find the exponent, first estimate by integer log10, then adjust.
+ Should be faster than doing a natural logarithm. */
+ int exponent = (int) (y.intLog10() * 3.32);
+ if (exponent < 0) {
+ exponent--;
+ }
+
+ Dfp tempDfp = DfpMath.pow(getTwo(), exponent);
+ while (tempDfp.lessThan(y) || tempDfp.equals(y)) {
+ tempDfp = tempDfp.multiply(2);
+ exponent++;
+ }
+ exponent--;
+
+ /* We have the exponent, now work on the mantissa */
+
+ y = y.divide(DfpMath.pow(getTwo(), exponent));
+ if (exponent > -1023) {
+ y = y.subtract(getOne());
+ }
+
+ if (exponent < -1074) {
+ return 0;
+ }
+
+ if (exponent > 1023) {
+ return negate ? Double.NEGATIVE_INFINITY : Double.POSITIVE_INFINITY;
+ }
+
+ y = y.multiply(newInstance(4503599627370496l)).rint();
+ String str = y.toString();
+ str = str.substring(0, str.length() - 1);
+ long mantissa = Long.parseLong(str);
+
+ if (mantissa == 4503599627370496L) {
+ // Handle special case where we round up to next power of two
+ mantissa = 0;
+ exponent++;
+ }
+
+ /* Its going to be subnormal, so make adjustments */
+ if (exponent <= -1023) {
+ exponent--;
+ }
+
+ while (exponent < -1023) {
+ exponent++;
+ mantissa >>>= 1;
+ }
+
+ long bits = mantissa | ((exponent + 1023L) << 52);
+ double x = Double.longBitsToDouble(bits);
+
+ if (negate) {
+ x = -x;
+ }
+
+ return x;
+ }
+
+ /**
+ * Convert the instance into a split double.
+ *
+ * @return an array of two doubles which sum represent the instance
+ * @see #toDouble()
+ */
+ public double[] toSplitDouble() {
+ double split[] = new double[2];
+ long mask = 0xffffffffc0000000L;
+
+ split[0] = Double.longBitsToDouble(Double.doubleToLongBits(toDouble()) & mask);
+ split[1] = subtract(newInstance(split[0])).toDouble();
+
+ return split;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @since 3.2
+ */
+ public double getReal() {
+ return toDouble();
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @since 3.2
+ */
+ public Dfp add(final double a) {
+ return add(newInstance(a));
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @since 3.2
+ */
+ public Dfp subtract(final double a) {
+ return subtract(newInstance(a));
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @since 3.2
+ */
+ public Dfp multiply(final double a) {
+ return multiply(newInstance(a));
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @since 3.2
+ */
+ public Dfp divide(final double a) {
+ return divide(newInstance(a));
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @since 3.2
+ */
+ public Dfp remainder(final double a) {
+ return remainder(newInstance(a));
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @since 3.2
+ */
+ public long round() {
+ return FastMath.round(toDouble());
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @since 3.2
+ */
+ public Dfp signum() {
+ if (isNaN() || isZero()) {
+ return this;
+ } else {
+ return newInstance(sign > 0 ? +1 : -1);
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @since 3.2
+ */
+ public Dfp copySign(final Dfp s) {
+ if ((sign >= 0 && s.sign >= 0) || (sign < 0 && s.sign < 0)) { // Sign is currently OK
+ return this;
+ }
+ return negate(); // flip sign
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @since 3.2
+ */
+ public Dfp copySign(final double s) {
+ long sb = Double.doubleToLongBits(s);
+ if ((sign >= 0 && sb >= 0) || (sign < 0 && sb < 0)) { // Sign is currently OK
+ return this;
+ }
+ return negate(); // flip sign
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @since 3.2
+ */
+ public Dfp scalb(final int n) {
+ return multiply(DfpMath.pow(getTwo(), n));
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @since 3.2
+ */
+ public Dfp hypot(final Dfp y) {
+ return multiply(this).add(y.multiply(y)).sqrt();
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @since 3.2
+ */
+ public Dfp cbrt() {
+ return rootN(3);
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @since 3.2
+ */
+ public Dfp rootN(final int n) {
+ return (sign >= 0)
+ ? DfpMath.pow(this, getOne().divide(n))
+ : DfpMath.pow(negate(), getOne().divide(n)).negate();
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @since 3.2
+ */
+ public Dfp pow(final double p) {
+ return DfpMath.pow(this, newInstance(p));
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @since 3.2
+ */
+ public Dfp pow(final int n) {
+ return DfpMath.pow(this, n);
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @since 3.2
+ */
+ public Dfp pow(final Dfp e) {
+ return DfpMath.pow(this, e);
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @since 3.2
+ */
+ public Dfp exp() {
+ return DfpMath.exp(this);
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @since 3.2
+ */
+ public Dfp expm1() {
+ return DfpMath.exp(this).subtract(getOne());
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @since 3.2
+ */
+ public Dfp log() {
+ return DfpMath.log(this);
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @since 3.2
+ */
+ public Dfp log1p() {
+ return DfpMath.log(this.add(getOne()));
+ }
+
+ // TODO: deactivate this implementation (and return type) in 4.0
+ /**
+ * Get the exponent of the greatest power of 10 that is less than or equal to abs(this).
+ *
+ * @return integer base 10 logarithm
+ * @deprecated as of 3.2, replaced by {@link #intLog10()}, in 4.0 the return type will be
+ * changed to Dfp
+ */
+ @Deprecated
+ public int log10() {
+ return intLog10();
+ }
+
+ // TODO: activate this implementation (and return type) in 4.0
+ // /** {@inheritDoc}
+ // * @since 3.2
+ // */
+ // public Dfp log10() {
+ // return DfpMath.log(this).divide(DfpMath.log(newInstance(10)));
+ // }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @since 3.2
+ */
+ public Dfp cos() {
+ return DfpMath.cos(this);
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @since 3.2
+ */
+ public Dfp sin() {
+ return DfpMath.sin(this);
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @since 3.2
+ */
+ public Dfp tan() {
+ return DfpMath.tan(this);
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @since 3.2
+ */
+ public Dfp acos() {
+ return DfpMath.acos(this);
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @since 3.2
+ */
+ public Dfp asin() {
+ return DfpMath.asin(this);
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @since 3.2
+ */
+ public Dfp atan() {
+ return DfpMath.atan(this);
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @since 3.2
+ */
+ public Dfp atan2(final Dfp x) throws DimensionMismatchException {
+
+ // compute r = sqrt(x^2+y^2)
+ final Dfp r = x.multiply(x).add(multiply(this)).sqrt();
+
+ if (x.sign >= 0) {
+
+ // compute atan2(y, x) = 2 atan(y / (r + x))
+ return getTwo().multiply(divide(r.add(x)).atan());
+
+ } else {
+
+ // compute atan2(y, x) = +/- pi - 2 atan(y / (r - x))
+ final Dfp tmp = getTwo().multiply(divide(r.subtract(x)).atan());
+ final Dfp pmPi = newInstance((tmp.sign <= 0) ? -FastMath.PI : FastMath.PI);
+ return pmPi.subtract(tmp);
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @since 3.2
+ */
+ public Dfp cosh() {
+ return DfpMath.exp(this).add(DfpMath.exp(negate())).divide(2);
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @since 3.2
+ */
+ public Dfp sinh() {
+ return DfpMath.exp(this).subtract(DfpMath.exp(negate())).divide(2);
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @since 3.2
+ */
+ public Dfp tanh() {
+ final Dfp ePlus = DfpMath.exp(this);
+ final Dfp eMinus = DfpMath.exp(negate());
+ return ePlus.subtract(eMinus).divide(ePlus.add(eMinus));
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @since 3.2
+ */
+ public Dfp acosh() {
+ return multiply(this).subtract(getOne()).sqrt().add(this).log();
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @since 3.2
+ */
+ public Dfp asinh() {
+ return multiply(this).add(getOne()).sqrt().add(this).log();
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @since 3.2
+ */
+ public Dfp atanh() {
+ return getOne().add(this).divide(getOne().subtract(this)).log().divide(2);
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @since 3.2
+ */
+ public Dfp linearCombination(final Dfp[] a, final Dfp[] b) throws DimensionMismatchException {
+ if (a.length != b.length) {
+ throw new DimensionMismatchException(a.length, b.length);
+ }
+ Dfp r = getZero();
+ for (int i = 0; i < a.length; ++i) {
+ r = r.add(a[i].multiply(b[i]));
+ }
+ return r;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @since 3.2
+ */
+ public Dfp linearCombination(final double[] a, final Dfp[] b)
+ throws DimensionMismatchException {
+ if (a.length != b.length) {
+ throw new DimensionMismatchException(a.length, b.length);
+ }
+ Dfp r = getZero();
+ for (int i = 0; i < a.length; ++i) {
+ r = r.add(b[i].multiply(a[i]));
+ }
+ return r;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @since 3.2
+ */
+ public Dfp linearCombination(final Dfp a1, final Dfp b1, final Dfp a2, final Dfp b2) {
+ return a1.multiply(b1).add(a2.multiply(b2));
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @since 3.2
+ */
+ public Dfp linearCombination(final double a1, final Dfp b1, final double a2, final Dfp b2) {
+ return b1.multiply(a1).add(b2.multiply(a2));
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @since 3.2
+ */
+ public Dfp linearCombination(
+ final Dfp a1, final Dfp b1, final Dfp a2, final Dfp b2, final Dfp a3, final Dfp b3) {
+ return a1.multiply(b1).add(a2.multiply(b2)).add(a3.multiply(b3));
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @since 3.2
+ */
+ public Dfp linearCombination(
+ final double a1,
+ final Dfp b1,
+ final double a2,
+ final Dfp b2,
+ final double a3,
+ final Dfp b3) {
+ return b1.multiply(a1).add(b2.multiply(a2)).add(b3.multiply(a3));
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @since 3.2
+ */
+ public Dfp linearCombination(
+ final Dfp a1,
+ final Dfp b1,
+ final Dfp a2,
+ final Dfp b2,
+ final Dfp a3,
+ final Dfp b3,
+ final Dfp a4,
+ final Dfp b4) {
+ return a1.multiply(b1).add(a2.multiply(b2)).add(a3.multiply(b3)).add(a4.multiply(b4));
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @since 3.2
+ */
+ public Dfp linearCombination(
+ final double a1,
+ final Dfp b1,
+ final double a2,
+ final Dfp b2,
+ final double a3,
+ final Dfp b3,
+ final double a4,
+ final Dfp b4) {
+ return b1.multiply(a1).add(b2.multiply(a2)).add(b3.multiply(a3)).add(b4.multiply(a4));
+ }
+}
diff --git a/src/main/java/org/apache/commons/math3/dfp/DfpDec.java b/src/main/java/org/apache/commons/math3/dfp/DfpDec.java
new file mode 100644
index 0000000..5571c2d
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/dfp/DfpDec.java
@@ -0,0 +1,388 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math3.dfp;
+
+/**
+ * Subclass of {@link Dfp} which hides the radix-10000 artifacts of the superclass. This should give
+ * outward appearances of being a decimal number with DIGITS*4-3 decimal digits. This class can be
+ * subclassed to appear to be an arbitrary number of decimal digits less than DIGITS*4-3.
+ *
+ * @since 2.2
+ */
+public class DfpDec extends Dfp {
+
+ /**
+ * Makes an instance with a value of zero.
+ *
+ * @param factory factory linked to this instance
+ */
+ protected DfpDec(final DfpField factory) {
+ super(factory);
+ }
+
+ /**
+ * Create an instance from a byte value.
+ *
+ * @param factory factory linked to this instance
+ * @param x value to convert to an instance
+ */
+ protected DfpDec(final DfpField factory, byte x) {
+ super(factory, x);
+ }
+
+ /**
+ * Create an instance from an int value.
+ *
+ * @param factory factory linked to this instance
+ * @param x value to convert to an instance
+ */
+ protected DfpDec(final DfpField factory, int x) {
+ super(factory, x);
+ }
+
+ /**
+ * Create an instance from a long value.
+ *
+ * @param factory factory linked to this instance
+ * @param x value to convert to an instance
+ */
+ protected DfpDec(final DfpField factory, long x) {
+ super(factory, x);
+ }
+
+ /**
+ * Create an instance from a double value.
+ *
+ * @param factory factory linked to this instance
+ * @param x value to convert to an instance
+ */
+ protected DfpDec(final DfpField factory, double x) {
+ super(factory, x);
+ round(0);
+ }
+
+ /**
+ * Copy constructor.
+ *
+ * @param d instance to copy
+ */
+ public DfpDec(final Dfp d) {
+ super(d);
+ round(0);
+ }
+
+ /**
+ * Create an instance from a String representation.
+ *
+ * @param factory factory linked to this instance
+ * @param s string representation of the instance
+ */
+ protected DfpDec(final DfpField factory, final String s) {
+ super(factory, s);
+ round(0);
+ }
+
+ /**
+ * Creates an instance with a non-finite value.
+ *
+ * @param factory factory linked to this instance
+ * @param sign sign of the Dfp to create
+ * @param nans code of the value, must be one of {@link #INFINITE}, {@link #SNAN}, {@link #QNAN}
+ */
+ protected DfpDec(final DfpField factory, final byte sign, final byte nans) {
+ super(factory, sign, nans);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public Dfp newInstance() {
+ return new DfpDec(getField());
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public Dfp newInstance(final byte x) {
+ return new DfpDec(getField(), x);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public Dfp newInstance(final int x) {
+ return new DfpDec(getField(), x);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public Dfp newInstance(final long x) {
+ return new DfpDec(getField(), x);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public Dfp newInstance(final double x) {
+ return new DfpDec(getField(), x);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public Dfp newInstance(final Dfp d) {
+
+ // make sure we don't mix number with different precision
+ if (getField().getRadixDigits() != d.getField().getRadixDigits()) {
+ getField().setIEEEFlagsBits(DfpField.FLAG_INVALID);
+ final Dfp result = newInstance(getZero());
+ result.nans = QNAN;
+ return dotrap(DfpField.FLAG_INVALID, "newInstance", d, result);
+ }
+
+ return new DfpDec(d);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public Dfp newInstance(final String s) {
+ return new DfpDec(getField(), s);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public Dfp newInstance(final byte sign, final byte nans) {
+ return new DfpDec(getField(), sign, nans);
+ }
+
+ /**
+ * Get the number of decimal digits this class is going to represent. Default implementation
+ * returns {@link #getRadixDigits()}*4-3. Subclasses can override this to return something less.
+ *
+ * @return number of decimal digits this class is going to represent
+ */
+ protected int getDecimalDigits() {
+ return getRadixDigits() * 4 - 3;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ protected int round(int in) {
+
+ int msb = mant[mant.length - 1];
+ if (msb == 0) {
+ // special case -- this == zero
+ return 0;
+ }
+
+ int cmaxdigits = mant.length * 4;
+ int lsbthreshold = 1000;
+ while (lsbthreshold > msb) {
+ lsbthreshold /= 10;
+ cmaxdigits--;
+ }
+
+ final int digits = getDecimalDigits();
+ final int lsbshift = cmaxdigits - digits;
+ final int lsd = lsbshift / 4;
+
+ lsbthreshold = 1;
+ for (int i = 0; i < lsbshift % 4; i++) {
+ lsbthreshold *= 10;
+ }
+
+ final int lsb = mant[lsd];
+
+ if (lsbthreshold <= 1 && digits == 4 * mant.length - 3) {
+ return super.round(in);
+ }
+
+ int discarded = in; // not looking at this after this point
+ final int n;
+ if (lsbthreshold == 1) {
+ // look to the next digit for rounding
+ n = (mant[lsd - 1] / 1000) % 10;
+ mant[lsd - 1] %= 1000;
+ discarded |= mant[lsd - 1];
+ } else {
+ n = (lsb * 10 / lsbthreshold) % 10;
+ discarded |= lsb % (lsbthreshold / 10);
+ }
+
+ for (int i = 0; i < lsd; i++) {
+ discarded |= mant[i]; // need to know if there are any discarded bits
+ mant[i] = 0;
+ }
+
+ mant[lsd] = lsb / lsbthreshold * lsbthreshold;
+
+ final boolean inc;
+ switch (getField().getRoundingMode()) {
+ case ROUND_DOWN:
+ inc = false;
+ break;
+
+ case ROUND_UP:
+ inc = (n != 0) || (discarded != 0); // round up if n!=0
+ break;
+
+ case ROUND_HALF_UP:
+ inc = n >= 5; // round half up
+ break;
+
+ case ROUND_HALF_DOWN:
+ inc = n > 5; // round half down
+ break;
+
+ case ROUND_HALF_EVEN:
+ inc =
+ (n > 5)
+ || (n == 5 && discarded != 0)
+ || (n == 5
+ && discarded == 0
+ && ((lsb / lsbthreshold) & 1) == 1); // round half-even
+ break;
+
+ case ROUND_HALF_ODD:
+ inc =
+ (n > 5)
+ || (n == 5 && discarded != 0)
+ || (n == 5
+ && discarded == 0
+ && ((lsb / lsbthreshold) & 1) == 0); // round half-odd
+ break;
+
+ case ROUND_CEIL:
+ inc = (sign == 1) && (n != 0 || discarded != 0); // round ceil
+ break;
+
+ case ROUND_FLOOR:
+ default:
+ inc = (sign == -1) && (n != 0 || discarded != 0); // round floor
+ break;
+ }
+
+ if (inc) {
+ // increment if necessary
+ int rh = lsbthreshold;
+ for (int i = lsd; i < mant.length; i++) {
+ final int r = mant[i] + rh;
+ rh = r / RADIX;
+ mant[i] = r % RADIX;
+ }
+
+ if (rh != 0) {
+ shiftRight();
+ mant[mant.length - 1] = rh;
+ }
+ }
+
+ // Check for exceptional cases and raise signals if necessary
+ if (exp < MIN_EXP) {
+ // Gradual Underflow
+ getField().setIEEEFlagsBits(DfpField.FLAG_UNDERFLOW);
+ return DfpField.FLAG_UNDERFLOW;
+ }
+
+ if (exp > MAX_EXP) {
+ // Overflow
+ getField().setIEEEFlagsBits(DfpField.FLAG_OVERFLOW);
+ return DfpField.FLAG_OVERFLOW;
+ }
+
+ if (n != 0 || discarded != 0) {
+ // Inexact
+ getField().setIEEEFlagsBits(DfpField.FLAG_INEXACT);
+ return DfpField.FLAG_INEXACT;
+ }
+ return 0;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public Dfp nextAfter(Dfp x) {
+
+ final String trapName = "nextAfter";
+
+ // make sure we don't mix number with different precision
+ if (getField().getRadixDigits() != x.getField().getRadixDigits()) {
+ getField().setIEEEFlagsBits(DfpField.FLAG_INVALID);
+ final Dfp result = newInstance(getZero());
+ result.nans = QNAN;
+ return dotrap(DfpField.FLAG_INVALID, trapName, x, result);
+ }
+
+ boolean up = false;
+ Dfp result;
+ Dfp inc;
+
+ // if this is greater than x
+ if (this.lessThan(x)) {
+ up = true;
+ }
+
+ if (equals(x)) {
+ return newInstance(x);
+ }
+
+ if (lessThan(getZero())) {
+ up = !up;
+ }
+
+ if (up) {
+ inc = power10(intLog10() - getDecimalDigits() + 1);
+ inc = copysign(inc, this);
+
+ if (this.equals(getZero())) {
+ inc = power10K(MIN_EXP - mant.length - 1);
+ }
+
+ if (inc.equals(getZero())) {
+ result = copysign(newInstance(getZero()), this);
+ } else {
+ result = add(inc);
+ }
+ } else {
+ inc = power10(intLog10());
+ inc = copysign(inc, this);
+
+ if (this.equals(inc)) {
+ inc = inc.divide(power10(getDecimalDigits()));
+ } else {
+ inc = inc.divide(power10(getDecimalDigits() - 1));
+ }
+
+ if (this.equals(getZero())) {
+ inc = power10K(MIN_EXP - mant.length - 1);
+ }
+
+ if (inc.equals(getZero())) {
+ result = copysign(newInstance(getZero()), this);
+ } else {
+ result = subtract(inc);
+ }
+ }
+
+ if (result.classify() == INFINITE && this.classify() != INFINITE) {
+ getField().setIEEEFlagsBits(DfpField.FLAG_INEXACT);
+ result = dotrap(DfpField.FLAG_INEXACT, trapName, x, result);
+ }
+
+ if (result.equals(getZero()) && this.equals(getZero()) == false) {
+ getField().setIEEEFlagsBits(DfpField.FLAG_INEXACT);
+ result = dotrap(DfpField.FLAG_INEXACT, trapName, x, result);
+ }
+
+ return result;
+ }
+}
diff --git a/src/main/java/org/apache/commons/math3/dfp/DfpField.java b/src/main/java/org/apache/commons/math3/dfp/DfpField.java
new file mode 100644
index 0000000..bd5a7eb
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/dfp/DfpField.java
@@ -0,0 +1,826 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math3.dfp;
+
+import org.apache.commons.math3.Field;
+import org.apache.commons.math3.FieldElement;
+
+/**
+ * Field for Decimal floating point instances.
+ *
+ * @since 2.2
+ */
+public class DfpField implements Field<Dfp> {
+
+ /** Enumerate for rounding modes. */
+ public enum RoundingMode {
+
+ /** Rounds toward zero (truncation). */
+ ROUND_DOWN,
+
+ /** Rounds away from zero if discarded digit is non-zero. */
+ ROUND_UP,
+
+ /**
+ * Rounds towards nearest unless both are equidistant in which case it rounds away from
+ * zero.
+ */
+ ROUND_HALF_UP,
+
+ /**
+ * Rounds towards nearest unless both are equidistant in which case it rounds toward zero.
+ */
+ ROUND_HALF_DOWN,
+
+ /**
+ * Rounds towards nearest unless both are equidistant in which case it rounds toward the
+ * even neighbor. This is the default as specified by IEEE 854-1987
+ */
+ ROUND_HALF_EVEN,
+
+ /**
+ * Rounds towards nearest unless both are equidistant in which case it rounds toward the odd
+ * neighbor.
+ */
+ ROUND_HALF_ODD,
+
+ /** Rounds towards positive infinity. */
+ ROUND_CEIL,
+
+ /** Rounds towards negative infinity. */
+ ROUND_FLOOR;
+ }
+
+ /** IEEE 854-1987 flag for invalid operation. */
+ public static final int FLAG_INVALID = 1;
+
+ /** IEEE 854-1987 flag for division by zero. */
+ public static final int FLAG_DIV_ZERO = 2;
+
+ /** IEEE 854-1987 flag for overflow. */
+ public static final int FLAG_OVERFLOW = 4;
+
+ /** IEEE 854-1987 flag for underflow. */
+ public static final int FLAG_UNDERFLOW = 8;
+
+ /** IEEE 854-1987 flag for inexact result. */
+ public static final int FLAG_INEXACT = 16;
+
+ /** High precision string representation of &radic;2. */
+ private static String sqr2String;
+
+ // Note: the static strings are set up (once) by the ctor and @GuardedBy("DfpField.class")
+
+ /** High precision string representation of &radic;2 / 2. */
+ private static String sqr2ReciprocalString;
+
+ /** High precision string representation of &radic;3. */
+ private static String sqr3String;
+
+ /** High precision string representation of &radic;3 / 3. */
+ private static String sqr3ReciprocalString;
+
+ /** High precision string representation of &pi;. */
+ private static String piString;
+
+ /** High precision string representation of e. */
+ private static String eString;
+
+ /** High precision string representation of ln(2). */
+ private static String ln2String;
+
+ /** High precision string representation of ln(5). */
+ private static String ln5String;
+
+ /** High precision string representation of ln(10). */
+ private static String ln10String;
+
+ /**
+ * The number of radix digits. Note these depend on the radix which is 10000 digits, so each one
+ * is equivalent to 4 decimal digits.
+ */
+ private final int radixDigits;
+
+ /** A {@link Dfp} with value 0. */
+ private final Dfp zero;
+
+ /** A {@link Dfp} with value 1. */
+ private final Dfp one;
+
+ /** A {@link Dfp} with value 2. */
+ private final Dfp two;
+
+ /** A {@link Dfp} with value &radic;2. */
+ private final Dfp sqr2;
+
+ /** A two elements {@link Dfp} array with value &radic;2 split in two pieces. */
+ private final Dfp[] sqr2Split;
+
+ /** A {@link Dfp} with value &radic;2 / 2. */
+ private final Dfp sqr2Reciprocal;
+
+ /** A {@link Dfp} with value &radic;3. */
+ private final Dfp sqr3;
+
+ /** A {@link Dfp} with value &radic;3 / 3. */
+ private final Dfp sqr3Reciprocal;
+
+ /** A {@link Dfp} with value &pi;. */
+ private final Dfp pi;
+
+ /** A two elements {@link Dfp} array with value &pi; split in two pieces. */
+ private final Dfp[] piSplit;
+
+ /** A {@link Dfp} with value e. */
+ private final Dfp e;
+
+ /** A two elements {@link Dfp} array with value e split in two pieces. */
+ private final Dfp[] eSplit;
+
+ /** A {@link Dfp} with value ln(2). */
+ private final Dfp ln2;
+
+ /** A two elements {@link Dfp} array with value ln(2) split in two pieces. */
+ private final Dfp[] ln2Split;
+
+ /** A {@link Dfp} with value ln(5). */
+ private final Dfp ln5;
+
+ /** A two elements {@link Dfp} array with value ln(5) split in two pieces. */
+ private final Dfp[] ln5Split;
+
+ /** A {@link Dfp} with value ln(10). */
+ private final Dfp ln10;
+
+ /** Current rounding mode. */
+ private RoundingMode rMode;
+
+ /** IEEE 854-1987 signals. */
+ private int ieeeFlags;
+
+ /**
+ * Create a factory for the specified number of radix digits.
+ *
+ * <p>Note that since the {@link Dfp} class uses 10000 as its radix, each radix digit is
+ * equivalent to 4 decimal digits. This implies that asking for 13, 14, 15 or 16 decimal digits
+ * will really lead to a 4 radix 10000 digits in all cases.
+ *
+ * @param decimalDigits minimal number of decimal digits.
+ */
+ public DfpField(final int decimalDigits) {
+ this(decimalDigits, true);
+ }
+
+ /**
+ * Create a factory for the specified number of radix digits.
+ *
+ * <p>Note that since the {@link Dfp} class uses 10000 as its radix, each radix digit is
+ * equivalent to 4 decimal digits. This implies that asking for 13, 14, 15 or 16 decimal digits
+ * will really lead to a 4 radix 10000 digits in all cases.
+ *
+ * @param decimalDigits minimal number of decimal digits
+ * @param computeConstants if true, the transcendental constants for the given precision must be
+ * computed (setting this flag to false is RESERVED for the internal recursive call)
+ */
+ private DfpField(final int decimalDigits, final boolean computeConstants) {
+
+ this.radixDigits = (decimalDigits < 13) ? 4 : (decimalDigits + 3) / 4;
+ this.rMode = RoundingMode.ROUND_HALF_EVEN;
+ this.ieeeFlags = 0;
+ this.zero = new Dfp(this, 0);
+ this.one = new Dfp(this, 1);
+ this.two = new Dfp(this, 2);
+
+ if (computeConstants) {
+ // set up transcendental constants
+ synchronized (DfpField.class) {
+
+ // as a heuristic to circumvent Table-Maker's Dilemma, we set the string
+ // representation of the constants to be at least 3 times larger than the
+ // number of decimal digits, also as an attempt to really compute these
+ // constants only once, we set a minimum number of digits
+ computeStringConstants((decimalDigits < 67) ? 200 : (3 * decimalDigits));
+
+ // set up the constants at current field accuracy
+ sqr2 = new Dfp(this, sqr2String);
+ sqr2Split = split(sqr2String);
+ sqr2Reciprocal = new Dfp(this, sqr2ReciprocalString);
+ sqr3 = new Dfp(this, sqr3String);
+ sqr3Reciprocal = new Dfp(this, sqr3ReciprocalString);
+ pi = new Dfp(this, piString);
+ piSplit = split(piString);
+ e = new Dfp(this, eString);
+ eSplit = split(eString);
+ ln2 = new Dfp(this, ln2String);
+ ln2Split = split(ln2String);
+ ln5 = new Dfp(this, ln5String);
+ ln5Split = split(ln5String);
+ ln10 = new Dfp(this, ln10String);
+ }
+ } else {
+ // dummy settings for unused constants
+ sqr2 = null;
+ sqr2Split = null;
+ sqr2Reciprocal = null;
+ sqr3 = null;
+ sqr3Reciprocal = null;
+ pi = null;
+ piSplit = null;
+ e = null;
+ eSplit = null;
+ ln2 = null;
+ ln2Split = null;
+ ln5 = null;
+ ln5Split = null;
+ ln10 = null;
+ }
+ }
+
+ /**
+ * Get the number of radix digits of the {@link Dfp} instances built by this factory.
+ *
+ * @return number of radix digits
+ */
+ public int getRadixDigits() {
+ return radixDigits;
+ }
+
+ /**
+ * Set the rounding mode. If not set, the default value is {@link RoundingMode#ROUND_HALF_EVEN}.
+ *
+ * @param mode desired rounding mode Note that the rounding mode is common to all {@link Dfp}
+ * instances belonging to the current {@link DfpField} in the system and will affect all
+ * future calculations.
+ */
+ public void setRoundingMode(final RoundingMode mode) {
+ rMode = mode;
+ }
+
+ /**
+ * Get the current rounding mode.
+ *
+ * @return current rounding mode
+ */
+ public RoundingMode getRoundingMode() {
+ return rMode;
+ }
+
+ /**
+ * Get the IEEE 854 status flags.
+ *
+ * @return IEEE 854 status flags
+ * @see #clearIEEEFlags()
+ * @see #setIEEEFlags(int)
+ * @see #setIEEEFlagsBits(int)
+ * @see #FLAG_INVALID
+ * @see #FLAG_DIV_ZERO
+ * @see #FLAG_OVERFLOW
+ * @see #FLAG_UNDERFLOW
+ * @see #FLAG_INEXACT
+ */
+ public int getIEEEFlags() {
+ return ieeeFlags;
+ }
+
+ /**
+ * Clears the IEEE 854 status flags.
+ *
+ * @see #getIEEEFlags()
+ * @see #setIEEEFlags(int)
+ * @see #setIEEEFlagsBits(int)
+ * @see #FLAG_INVALID
+ * @see #FLAG_DIV_ZERO
+ * @see #FLAG_OVERFLOW
+ * @see #FLAG_UNDERFLOW
+ * @see #FLAG_INEXACT
+ */
+ public void clearIEEEFlags() {
+ ieeeFlags = 0;
+ }
+
+ /**
+ * Sets the IEEE 854 status flags.
+ *
+ * @param flags desired value for the flags
+ * @see #getIEEEFlags()
+ * @see #clearIEEEFlags()
+ * @see #setIEEEFlagsBits(int)
+ * @see #FLAG_INVALID
+ * @see #FLAG_DIV_ZERO
+ * @see #FLAG_OVERFLOW
+ * @see #FLAG_UNDERFLOW
+ * @see #FLAG_INEXACT
+ */
+ public void setIEEEFlags(final int flags) {
+ ieeeFlags =
+ flags
+ & (FLAG_INVALID
+ | FLAG_DIV_ZERO
+ | FLAG_OVERFLOW
+ | FLAG_UNDERFLOW
+ | FLAG_INEXACT);
+ }
+
+ /**
+ * Sets some bits in the IEEE 854 status flags, without changing the already set bits.
+ *
+ * <p>Calling this method is equivalent to call {@code setIEEEFlags(getIEEEFlags() | bits)}
+ *
+ * @param bits bits to set
+ * @see #getIEEEFlags()
+ * @see #clearIEEEFlags()
+ * @see #setIEEEFlags(int)
+ * @see #FLAG_INVALID
+ * @see #FLAG_DIV_ZERO
+ * @see #FLAG_OVERFLOW
+ * @see #FLAG_UNDERFLOW
+ * @see #FLAG_INEXACT
+ */
+ public void setIEEEFlagsBits(final int bits) {
+ ieeeFlags |=
+ bits
+ & (FLAG_INVALID
+ | FLAG_DIV_ZERO
+ | FLAG_OVERFLOW
+ | FLAG_UNDERFLOW
+ | FLAG_INEXACT);
+ }
+
+ /**
+ * Makes a {@link Dfp} with a value of 0.
+ *
+ * @return a new {@link Dfp} with a value of 0
+ */
+ public Dfp newDfp() {
+ return new Dfp(this);
+ }
+
+ /**
+ * Create an instance from a byte value.
+ *
+ * @param x value to convert to an instance
+ * @return a new {@link Dfp} with the same value as x
+ */
+ public Dfp newDfp(final byte x) {
+ return new Dfp(this, x);
+ }
+
+ /**
+ * Create an instance from an int value.
+ *
+ * @param x value to convert to an instance
+ * @return a new {@link Dfp} with the same value as x
+ */
+ public Dfp newDfp(final int x) {
+ return new Dfp(this, x);
+ }
+
+ /**
+ * Create an instance from a long value.
+ *
+ * @param x value to convert to an instance
+ * @return a new {@link Dfp} with the same value as x
+ */
+ public Dfp newDfp(final long x) {
+ return new Dfp(this, x);
+ }
+
+ /**
+ * Create an instance from a double value.
+ *
+ * @param x value to convert to an instance
+ * @return a new {@link Dfp} with the same value as x
+ */
+ public Dfp newDfp(final double x) {
+ return new Dfp(this, x);
+ }
+
+ /**
+ * Copy constructor.
+ *
+ * @param d instance to copy
+ * @return a new {@link Dfp} with the same value as d
+ */
+ public Dfp newDfp(Dfp d) {
+ return new Dfp(d);
+ }
+
+ /**
+ * Create a {@link Dfp} given a String representation.
+ *
+ * @param s string representation of the instance
+ * @return a new {@link Dfp} parsed from specified string
+ */
+ public Dfp newDfp(final String s) {
+ return new Dfp(this, s);
+ }
+
+ /**
+ * Creates a {@link Dfp} with a non-finite value.
+ *
+ * @param sign sign of the Dfp to create
+ * @param nans code of the value, must be one of {@link Dfp#INFINITE}, {@link Dfp#SNAN}, {@link
+ * Dfp#QNAN}
+ * @return a new {@link Dfp} with a non-finite value
+ */
+ public Dfp newDfp(final byte sign, final byte nans) {
+ return new Dfp(this, sign, nans);
+ }
+
+ /**
+ * Get the constant 0.
+ *
+ * @return a {@link Dfp} with value 0
+ */
+ public Dfp getZero() {
+ return zero;
+ }
+
+ /**
+ * Get the constant 1.
+ *
+ * @return a {@link Dfp} with value 1
+ */
+ public Dfp getOne() {
+ return one;
+ }
+
+ /** {@inheritDoc} */
+ public Class<? extends FieldElement<Dfp>> getRuntimeClass() {
+ return Dfp.class;
+ }
+
+ /**
+ * Get the constant 2.
+ *
+ * @return a {@link Dfp} with value 2
+ */
+ public Dfp getTwo() {
+ return two;
+ }
+
+ /**
+ * Get the constant &radic;2.
+ *
+ * @return a {@link Dfp} with value &radic;2
+ */
+ public Dfp getSqr2() {
+ return sqr2;
+ }
+
+ /**
+ * Get the constant &radic;2 split in two pieces.
+ *
+ * @return a {@link Dfp} with value &radic;2 split in two pieces
+ */
+ public Dfp[] getSqr2Split() {
+ return sqr2Split.clone();
+ }
+
+ /**
+ * Get the constant &radic;2 / 2.
+ *
+ * @return a {@link Dfp} with value &radic;2 / 2
+ */
+ public Dfp getSqr2Reciprocal() {
+ return sqr2Reciprocal;
+ }
+
+ /**
+ * Get the constant &radic;3.
+ *
+ * @return a {@link Dfp} with value &radic;3
+ */
+ public Dfp getSqr3() {
+ return sqr3;
+ }
+
+ /**
+ * Get the constant &radic;3 / 3.
+ *
+ * @return a {@link Dfp} with value &radic;3 / 3
+ */
+ public Dfp getSqr3Reciprocal() {
+ return sqr3Reciprocal;
+ }
+
+ /**
+ * Get the constant &pi;.
+ *
+ * @return a {@link Dfp} with value &pi;
+ */
+ public Dfp getPi() {
+ return pi;
+ }
+
+ /**
+ * Get the constant &pi; split in two pieces.
+ *
+ * @return a {@link Dfp} with value &pi; split in two pieces
+ */
+ public Dfp[] getPiSplit() {
+ return piSplit.clone();
+ }
+
+ /**
+ * Get the constant e.
+ *
+ * @return a {@link Dfp} with value e
+ */
+ public Dfp getE() {
+ return e;
+ }
+
+ /**
+ * Get the constant e split in two pieces.
+ *
+ * @return a {@link Dfp} with value e split in two pieces
+ */
+ public Dfp[] getESplit() {
+ return eSplit.clone();
+ }
+
+ /**
+ * Get the constant ln(2).
+ *
+ * @return a {@link Dfp} with value ln(2)
+ */
+ public Dfp getLn2() {
+ return ln2;
+ }
+
+ /**
+ * Get the constant ln(2) split in two pieces.
+ *
+ * @return a {@link Dfp} with value ln(2) split in two pieces
+ */
+ public Dfp[] getLn2Split() {
+ return ln2Split.clone();
+ }
+
+ /**
+ * Get the constant ln(5).
+ *
+ * @return a {@link Dfp} with value ln(5)
+ */
+ public Dfp getLn5() {
+ return ln5;
+ }
+
+ /**
+ * Get the constant ln(5) split in two pieces.
+ *
+ * @return a {@link Dfp} with value ln(5) split in two pieces
+ */
+ public Dfp[] getLn5Split() {
+ return ln5Split.clone();
+ }
+
+ /**
+ * Get the constant ln(10).
+ *
+ * @return a {@link Dfp} with value ln(10)
+ */
+ public Dfp getLn10() {
+ return ln10;
+ }
+
+ /**
+ * Breaks a string representation up into two {@link Dfp}'s. The split is such that the sum of
+ * them is equivalent to the input string, but has higher precision than using a single Dfp.
+ *
+ * @param a string representation of the number to split
+ * @return an array of two {@link Dfp Dfp} instances which sum equals a
+ */
+ private Dfp[] split(final String a) {
+ Dfp result[] = new Dfp[2];
+ boolean leading = true;
+ int sp = 0;
+ int sig = 0;
+
+ char[] buf = new char[a.length()];
+
+ for (int i = 0; i < buf.length; i++) {
+ buf[i] = a.charAt(i);
+
+ if (buf[i] >= '1' && buf[i] <= '9') {
+ leading = false;
+ }
+
+ if (buf[i] == '.') {
+ sig += (400 - sig) % 4;
+ leading = false;
+ }
+
+ if (sig == (radixDigits / 2) * 4) {
+ sp = i;
+ break;
+ }
+
+ if (buf[i] >= '0' && buf[i] <= '9' && !leading) {
+ sig++;
+ }
+ }
+
+ result[0] = new Dfp(this, new String(buf, 0, sp));
+
+ for (int i = 0; i < buf.length; i++) {
+ buf[i] = a.charAt(i);
+ if (buf[i] >= '0' && buf[i] <= '9' && i < sp) {
+ buf[i] = '0';
+ }
+ }
+
+ result[1] = new Dfp(this, new String(buf));
+
+ return result;
+ }
+
+ /**
+ * Recompute the high precision string constants.
+ *
+ * @param highPrecisionDecimalDigits precision at which the string constants mus be computed
+ */
+ private static void computeStringConstants(final int highPrecisionDecimalDigits) {
+ if (sqr2String == null || sqr2String.length() < highPrecisionDecimalDigits - 3) {
+
+ // recompute the string representation of the transcendental constants
+ final DfpField highPrecisionField = new DfpField(highPrecisionDecimalDigits, false);
+ final Dfp highPrecisionOne = new Dfp(highPrecisionField, 1);
+ final Dfp highPrecisionTwo = new Dfp(highPrecisionField, 2);
+ final Dfp highPrecisionThree = new Dfp(highPrecisionField, 3);
+
+ final Dfp highPrecisionSqr2 = highPrecisionTwo.sqrt();
+ sqr2String = highPrecisionSqr2.toString();
+ sqr2ReciprocalString = highPrecisionOne.divide(highPrecisionSqr2).toString();
+
+ final Dfp highPrecisionSqr3 = highPrecisionThree.sqrt();
+ sqr3String = highPrecisionSqr3.toString();
+ sqr3ReciprocalString = highPrecisionOne.divide(highPrecisionSqr3).toString();
+
+ piString = computePi(highPrecisionOne, highPrecisionTwo, highPrecisionThree).toString();
+ eString = computeExp(highPrecisionOne, highPrecisionOne).toString();
+ ln2String = computeLn(highPrecisionTwo, highPrecisionOne, highPrecisionTwo).toString();
+ ln5String =
+ computeLn(new Dfp(highPrecisionField, 5), highPrecisionOne, highPrecisionTwo)
+ .toString();
+ ln10String =
+ computeLn(new Dfp(highPrecisionField, 10), highPrecisionOne, highPrecisionTwo)
+ .toString();
+ }
+ }
+
+ /**
+ * Compute &pi; using Jonathan and Peter Borwein quartic formula.
+ *
+ * @param one constant with value 1 at desired precision
+ * @param two constant with value 2 at desired precision
+ * @param three constant with value 3 at desired precision
+ * @return &pi;
+ */
+ private static Dfp computePi(final Dfp one, final Dfp two, final Dfp three) {
+
+ Dfp sqrt2 = two.sqrt();
+ Dfp yk = sqrt2.subtract(one);
+ Dfp four = two.add(two);
+ Dfp two2kp3 = two;
+ Dfp ak = two.multiply(three.subtract(two.multiply(sqrt2)));
+
+ // The formula converges quartically. This means the number of correct
+ // digits is multiplied by 4 at each iteration! Five iterations are
+ // sufficient for about 160 digits, eight iterations give about
+ // 10000 digits (this has been checked) and 20 iterations more than
+ // 160 billions of digits (this has NOT been checked).
+ // So the limit here is considered sufficient for most purposes ...
+ for (int i = 1; i < 20; i++) {
+ final Dfp ykM1 = yk;
+
+ final Dfp y2 = yk.multiply(yk);
+ final Dfp oneMinusY4 = one.subtract(y2.multiply(y2));
+ final Dfp s = oneMinusY4.sqrt().sqrt();
+ yk = one.subtract(s).divide(one.add(s));
+
+ two2kp3 = two2kp3.multiply(four);
+
+ final Dfp p = one.add(yk);
+ final Dfp p2 = p.multiply(p);
+ ak =
+ ak.multiply(p2.multiply(p2))
+ .subtract(
+ two2kp3.multiply(yk)
+ .multiply(one.add(yk).add(yk.multiply(yk))));
+
+ if (yk.equals(ykM1)) {
+ break;
+ }
+ }
+
+ return one.divide(ak);
+ }
+
+ /**
+ * Compute exp(a).
+ *
+ * @param a number for which we want the exponential
+ * @param one constant with value 1 at desired precision
+ * @return exp(a)
+ */
+ public static Dfp computeExp(final Dfp a, final Dfp one) {
+
+ Dfp y = new Dfp(one);
+ Dfp py = new Dfp(one);
+ Dfp f = new Dfp(one);
+ Dfp fi = new Dfp(one);
+ Dfp x = new Dfp(one);
+
+ for (int i = 0; i < 10000; i++) {
+ x = x.multiply(a);
+ y = y.add(x.divide(f));
+ fi = fi.add(one);
+ f = f.multiply(fi);
+ if (y.equals(py)) {
+ break;
+ }
+ py = new Dfp(y);
+ }
+
+ return y;
+ }
+
+ /**
+ * Compute ln(a).
+ *
+ * <p>Let f(x) = ln(x),
+ *
+ * <p>We know that f'(x) = 1/x, thus from Taylor's theorem we have:
+ *
+ * <p>----- n+1 n f(x) = \ (-1) (x - 1) / ---------------- for 1 <= n <= infinity ----- n
+ *
+ * <p>or 2 3 4 (x-1) (x-1) (x-1) ln(x) = (x-1) - ----- + ------ - ------ + ... 2 3 4
+ *
+ * <p>alternatively,
+ *
+ * <p>2 3 4 x x x ln(x+1) = x - - + - - - + ... 2 3 4
+ *
+ * <p>This series can be used to compute ln(x), but it converges too slowly.
+ *
+ * <p>If we substitute -x for x above, we get
+ *
+ * <p>2 3 4 x x x ln(1-x) = -x - - - - - - + ... 2 3 4
+ *
+ * <p>Note that all terms are now negative. Because the even powered ones absorbed the sign.
+ * Now, subtract the series above from the previous one to get ln(x+1) - ln(1-x). Note the even
+ * terms cancel out leaving only the odd ones
+ *
+ * <p>3 5 7 2x 2x 2x ln(x+1) - ln(x-1) = 2x + --- + --- + ---- + ... 3 5 7
+ *
+ * <p>By the property of logarithms that ln(a) - ln(b) = ln (a/b) we have:
+ *
+ * <p>3 5 7 x+1 / x x x \ ln ----- = 2 * | x + ---- + ---- + ---- + ... | x-1 \ 3 5 7 /
+ *
+ * <p>But now we want to find ln(a), so we need to find the value of x such that a =
+ * (x+1)/(x-1). This is easily solved to find that x = (a-1)/(a+1).
+ *
+ * @param a number for which we want the exponential
+ * @param one constant with value 1 at desired precision
+ * @param two constant with value 2 at desired precision
+ * @return ln(a)
+ */
+ public static Dfp computeLn(final Dfp a, final Dfp one, final Dfp two) {
+
+ int den = 1;
+ Dfp x = a.add(new Dfp(a.getField(), -1)).divide(a.add(one));
+
+ Dfp y = new Dfp(x);
+ Dfp num = new Dfp(x);
+ Dfp py = new Dfp(y);
+ for (int i = 0; i < 10000; i++) {
+ num = num.multiply(x);
+ num = num.multiply(x);
+ den += 2;
+ Dfp t = num.divide(den);
+ y = y.add(t);
+ if (y.equals(py)) {
+ break;
+ }
+ py = new Dfp(y);
+ }
+
+ return y.multiply(two);
+ }
+}
diff --git a/src/main/java/org/apache/commons/math3/dfp/DfpMath.java b/src/main/java/org/apache/commons/math3/dfp/DfpMath.java
new file mode 100644
index 0000000..ba50d05
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/dfp/DfpMath.java
@@ -0,0 +1,970 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math3.dfp;
+
+/**
+ * Mathematical routines for use with {@link Dfp}. The constants are defined in {@link DfpField}
+ *
+ * @since 2.2
+ */
+public class DfpMath {
+
+ /** Name for traps triggered by pow. */
+ private static final String POW_TRAP = "pow";
+
+ /** Private Constructor. */
+ private DfpMath() {}
+
+ /**
+ * Breaks a string representation up into two dfp's.
+ *
+ * <p>The two dfp are such that the sum of them is equivalent to the input string, but has
+ * higher precision than using a single dfp. This is useful for improving accuracy of
+ * exponentiation and critical multiplies.
+ *
+ * @param field field to which the Dfp must belong
+ * @param a string representation to split
+ * @return an array of two {@link Dfp} which sum is a
+ */
+ protected static Dfp[] split(final DfpField field, final String a) {
+ Dfp result[] = new Dfp[2];
+ char[] buf;
+ boolean leading = true;
+ int sp = 0;
+ int sig = 0;
+
+ buf = new char[a.length()];
+
+ for (int i = 0; i < buf.length; i++) {
+ buf[i] = a.charAt(i);
+
+ if (buf[i] >= '1' && buf[i] <= '9') {
+ leading = false;
+ }
+
+ if (buf[i] == '.') {
+ sig += (400 - sig) % 4;
+ leading = false;
+ }
+
+ if (sig == (field.getRadixDigits() / 2) * 4) {
+ sp = i;
+ break;
+ }
+
+ if (buf[i] >= '0' && buf[i] <= '9' && !leading) {
+ sig++;
+ }
+ }
+
+ result[0] = field.newDfp(new String(buf, 0, sp));
+
+ for (int i = 0; i < buf.length; i++) {
+ buf[i] = a.charAt(i);
+ if (buf[i] >= '0' && buf[i] <= '9' && i < sp) {
+ buf[i] = '0';
+ }
+ }
+
+ result[1] = field.newDfp(new String(buf));
+
+ return result;
+ }
+
+ /**
+ * Splits a {@link Dfp} into 2 {@link Dfp}'s such that their sum is equal to the input {@link
+ * Dfp}.
+ *
+ * @param a number to split
+ * @return two elements array containing the split number
+ */
+ protected static Dfp[] split(final Dfp a) {
+ final Dfp[] result = new Dfp[2];
+ final Dfp shift = a.multiply(a.power10K(a.getRadixDigits() / 2));
+ result[0] = a.add(shift).subtract(shift);
+ result[1] = a.subtract(result[0]);
+ return result;
+ }
+
+ /**
+ * Multiply two numbers that are split in to two pieces that are meant to be added together. Use
+ * binomial multiplication so ab = a0 b0 + a0 b1 + a1 b0 + a1 b1 Store the first term in
+ * result0, the rest in result1
+ *
+ * @param a first factor of the multiplication, in split form
+ * @param b second factor of the multiplication, in split form
+ * @return a &times; b, in split form
+ */
+ protected static Dfp[] splitMult(final Dfp[] a, final Dfp[] b) {
+ final Dfp[] result = new Dfp[2];
+
+ result[1] = a[0].getZero();
+ result[0] = a[0].multiply(b[0]);
+
+ /* If result[0] is infinite or zero, don't compute result[1].
+ * Attempting to do so may produce NaNs.
+ */
+
+ if (result[0].classify() == Dfp.INFINITE || result[0].equals(result[1])) {
+ return result;
+ }
+
+ result[1] = a[0].multiply(b[1]).add(a[1].multiply(b[0])).add(a[1].multiply(b[1]));
+
+ return result;
+ }
+
+ /**
+ * Divide two numbers that are split in to two pieces that are meant to be added together.
+ * Inverse of split multiply above: (a+b) / (c+d) = (a/c) + ( (bc-ad)/(c**2+cd) )
+ *
+ * @param a dividend, in split form
+ * @param b divisor, in split form
+ * @return a / b, in split form
+ */
+ protected static Dfp[] splitDiv(final Dfp[] a, final Dfp[] b) {
+ final Dfp[] result;
+
+ result = new Dfp[2];
+
+ result[0] = a[0].divide(b[0]);
+ result[1] = a[1].multiply(b[0]).subtract(a[0].multiply(b[1]));
+ result[1] = result[1].divide(b[0].multiply(b[0]).add(b[0].multiply(b[1])));
+
+ return result;
+ }
+
+ /**
+ * Raise a split base to the a power.
+ *
+ * @param base number to raise
+ * @param a power
+ * @return base<sup>a</sup>
+ */
+ protected static Dfp splitPow(final Dfp[] base, int a) {
+ boolean invert = false;
+
+ Dfp[] r = new Dfp[2];
+
+ Dfp[] result = new Dfp[2];
+ result[0] = base[0].getOne();
+ result[1] = base[0].getZero();
+
+ if (a == 0) {
+ // Special case a = 0
+ return result[0].add(result[1]);
+ }
+
+ if (a < 0) {
+ // If a is less than zero
+ invert = true;
+ a = -a;
+ }
+
+ // Exponentiate by successive squaring
+ do {
+ r[0] = new Dfp(base[0]);
+ r[1] = new Dfp(base[1]);
+ int trial = 1;
+
+ int prevtrial;
+ while (true) {
+ prevtrial = trial;
+ trial *= 2;
+ if (trial > a) {
+ break;
+ }
+ r = splitMult(r, r);
+ }
+
+ trial = prevtrial;
+
+ a -= trial;
+ result = splitMult(result, r);
+
+ } while (a >= 1);
+
+ result[0] = result[0].add(result[1]);
+
+ if (invert) {
+ result[0] = base[0].getOne().divide(result[0]);
+ }
+
+ return result[0];
+ }
+
+ /**
+ * Raises base to the power a by successive squaring.
+ *
+ * @param base number to raise
+ * @param a power
+ * @return base<sup>a</sup>
+ */
+ public static Dfp pow(Dfp base, int a) {
+ boolean invert = false;
+
+ Dfp result = base.getOne();
+
+ if (a == 0) {
+ // Special case
+ return result;
+ }
+
+ if (a < 0) {
+ invert = true;
+ a = -a;
+ }
+
+ // Exponentiate by successive squaring
+ do {
+ Dfp r = new Dfp(base);
+ Dfp prevr;
+ int trial = 1;
+ int prevtrial;
+
+ do {
+ prevr = new Dfp(r);
+ prevtrial = trial;
+ r = r.multiply(r);
+ trial *= 2;
+ } while (a > trial);
+
+ r = prevr;
+ trial = prevtrial;
+
+ a -= trial;
+ result = result.multiply(r);
+
+ } while (a >= 1);
+
+ if (invert) {
+ result = base.getOne().divide(result);
+ }
+
+ return base.newInstance(result);
+ }
+
+ /**
+ * Computes e to the given power. a is broken into two parts, such that a = n+m where n is an
+ * integer. We use pow() to compute e<sup>n</sup> and a Taylor series to compute e<sup>m</sup>.
+ * We return e*<sup>n</sup> &times; e<sup>m</sup>
+ *
+ * @param a power at which e should be raised
+ * @return e<sup>a</sup>
+ */
+ public static Dfp exp(final Dfp a) {
+
+ final Dfp inta = a.rint();
+ final Dfp fraca = a.subtract(inta);
+
+ final int ia = inta.intValue();
+ if (ia > 2147483646) {
+ // return +Infinity
+ return a.newInstance((byte) 1, Dfp.INFINITE);
+ }
+
+ if (ia < -2147483646) {
+ // return 0;
+ return a.newInstance();
+ }
+
+ final Dfp einta = splitPow(a.getField().getESplit(), ia);
+ final Dfp efraca = expInternal(fraca);
+
+ return einta.multiply(efraca);
+ }
+
+ /**
+ * Computes e to the given power. Where -1 < a < 1. Use the classic Taylor series. 1 + x**2/2! +
+ * x**3/3! + x**4/4! ...
+ *
+ * @param a power at which e should be raised
+ * @return e<sup>a</sup>
+ */
+ protected static Dfp expInternal(final Dfp a) {
+ Dfp y = a.getOne();
+ Dfp x = a.getOne();
+ Dfp fact = a.getOne();
+ Dfp py = new Dfp(y);
+
+ for (int i = 1; i < 90; i++) {
+ x = x.multiply(a);
+ fact = fact.divide(i);
+ y = y.add(x.multiply(fact));
+ if (y.equals(py)) {
+ break;
+ }
+ py = new Dfp(y);
+ }
+
+ return y;
+ }
+
+ /**
+ * Returns the natural logarithm of a. a is first split into three parts such that a =
+ * (10000^h)(2^j)k. ln(a) is computed by ln(a) = ln(5)*h + ln(2)*(h+j) + ln(k) k is in the range
+ * 2/3 < k <4/3 and is passed on to a series expansion.
+ *
+ * @param a number from which logarithm is requested
+ * @return log(a)
+ */
+ public static Dfp log(Dfp a) {
+ int lr;
+ Dfp x;
+ int ix;
+ int p2 = 0;
+
+ // Check the arguments somewhat here
+ if (a.equals(a.getZero()) || a.lessThan(a.getZero()) || a.isNaN()) {
+ // negative, zero or NaN
+ a.getField().setIEEEFlagsBits(DfpField.FLAG_INVALID);
+ return a.dotrap(DfpField.FLAG_INVALID, "ln", a, a.newInstance((byte) 1, Dfp.QNAN));
+ }
+
+ if (a.classify() == Dfp.INFINITE) {
+ return a;
+ }
+
+ x = new Dfp(a);
+ lr = x.log10K();
+
+ x = x.divide(pow(a.newInstance(10000), lr)); /* This puts x in the range 0-10000 */
+ ix = x.floor().intValue();
+
+ while (ix > 2) {
+ ix >>= 1;
+ p2++;
+ }
+
+ Dfp[] spx = split(x);
+ Dfp[] spy = new Dfp[2];
+ spy[0] = pow(a.getTwo(), p2); // use spy[0] temporarily as a divisor
+ spx[0] = spx[0].divide(spy[0]);
+ spx[1] = spx[1].divide(spy[0]);
+
+ spy[0] = a.newInstance("1.33333"); // Use spy[0] for comparison
+ while (spx[0].add(spx[1]).greaterThan(spy[0])) {
+ spx[0] = spx[0].divide(2);
+ spx[1] = spx[1].divide(2);
+ p2++;
+ }
+
+ // X is now in the range of 2/3 < x < 4/3
+ Dfp[] spz = logInternal(spx);
+
+ spx[0] = a.newInstance(new StringBuilder().append(p2 + 4 * lr).toString());
+ spx[1] = a.getZero();
+ spy = splitMult(a.getField().getLn2Split(), spx);
+
+ spz[0] = spz[0].add(spy[0]);
+ spz[1] = spz[1].add(spy[1]);
+
+ spx[0] = a.newInstance(new StringBuilder().append(4 * lr).toString());
+ spx[1] = a.getZero();
+ spy = splitMult(a.getField().getLn5Split(), spx);
+
+ spz[0] = spz[0].add(spy[0]);
+ spz[1] = spz[1].add(spy[1]);
+
+ return a.newInstance(spz[0].add(spz[1]));
+ }
+
+ /**
+ * Computes the natural log of a number between 0 and 2. Let f(x) = ln(x),
+ *
+ * <p>We know that f'(x) = 1/x, thus from Taylor's theorum we have:
+ *
+ * <p>----- n+1 n f(x) = \ (-1) (x - 1) / ---------------- for 1 <= n <= infinity ----- n
+ *
+ * <p>or 2 3 4 (x-1) (x-1) (x-1) ln(x) = (x-1) - ----- + ------ - ------ + ... 2 3 4
+ *
+ * <p>alternatively,
+ *
+ * <p>2 3 4 x x x ln(x+1) = x - - + - - - + ... 2 3 4
+ *
+ * <p>This series can be used to compute ln(x), but it converges too slowly.
+ *
+ * <p>If we substitute -x for x above, we get
+ *
+ * <p>2 3 4 x x x ln(1-x) = -x - - - - - - + ... 2 3 4
+ *
+ * <p>Note that all terms are now negative. Because the even powered ones absorbed the sign.
+ * Now, subtract the series above from the previous one to get ln(x+1) - ln(1-x). Note the even
+ * terms cancel out leaving only the odd ones
+ *
+ * <p>3 5 7 2x 2x 2x ln(x+1) - ln(x-1) = 2x + --- + --- + ---- + ... 3 5 7
+ *
+ * <p>By the property of logarithms that ln(a) - ln(b) = ln (a/b) we have:
+ *
+ * <p>3 5 7 x+1 / x x x \ ln ----- = 2 * | x + ---- + ---- + ---- + ... | x-1 \ 3 5 7 /
+ *
+ * <p>But now we want to find ln(a), so we need to find the value of x such that a =
+ * (x+1)/(x-1). This is easily solved to find that x = (a-1)/(a+1).
+ *
+ * @param a number from which logarithm is requested, in split form
+ * @return log(a)
+ */
+ protected static Dfp[] logInternal(final Dfp a[]) {
+
+ /* Now we want to compute x = (a-1)/(a+1) but this is prone to
+ * loss of precision. So instead, compute x = (a/4 - 1/4) / (a/4 + 1/4)
+ */
+ Dfp t = a[0].divide(4).add(a[1].divide(4));
+ Dfp x = t.add(a[0].newInstance("-0.25")).divide(t.add(a[0].newInstance("0.25")));
+
+ Dfp y = new Dfp(x);
+ Dfp num = new Dfp(x);
+ Dfp py = new Dfp(y);
+ int den = 1;
+ for (int i = 0; i < 10000; i++) {
+ num = num.multiply(x);
+ num = num.multiply(x);
+ den += 2;
+ t = num.divide(den);
+ y = y.add(t);
+ if (y.equals(py)) {
+ break;
+ }
+ py = new Dfp(y);
+ }
+
+ y = y.multiply(a[0].getTwo());
+
+ return split(y);
+ }
+
+ /**
+ * Computes x to the y power.
+ *
+ * <p>Uses the following method:
+ *
+ * <p>
+ *
+ * <ol>
+ * <li>Set u = rint(y), v = y-u
+ * <li>Compute a = v * ln(x)
+ * <li>Compute b = rint( a/ln(2) )
+ * <li>Compute c = a - b*ln(2)
+ * <li>x<sup>y</sup> = x<sup>u</sup> * 2<sup>b</sup> * e<sup>c</sup>
+ * </ol>
+ *
+ * if |y| > 1e8, then we compute by exp(y*ln(x))
+ *
+ * <p><b>Special Cases</b>
+ *
+ * <p>
+ *
+ * <ul>
+ * <li>if y is 0.0 or -0.0 then result is 1.0
+ * <li>if y is 1.0 then result is x
+ * <li>if y is NaN then result is NaN
+ * <li>if x is NaN and y is not zero then result is NaN
+ * <li>if |x| > 1.0 and y is +Infinity then result is +Infinity
+ * <li>if |x| < 1.0 and y is -Infinity then result is +Infinity
+ * <li>if |x| > 1.0 and y is -Infinity then result is +0
+ * <li>if |x| < 1.0 and y is +Infinity then result is +0
+ * <li>if |x| = 1.0 and y is +/-Infinity then result is NaN
+ * <li>if x = +0 and y > 0 then result is +0
+ * <li>if x = +Inf and y < 0 then result is +0
+ * <li>if x = +0 and y < 0 then result is +Inf
+ * <li>if x = +Inf and y > 0 then result is +Inf
+ * <li>if x = -0 and y > 0, finite, not odd integer then result is +0
+ * <li>if x = -0 and y < 0, finite, and odd integer then result is -Inf
+ * <li>if x = -Inf and y > 0, finite, and odd integer then result is -Inf
+ * <li>if x = -0 and y < 0, not finite odd integer then result is +Inf
+ * <li>if x = -Inf and y > 0, not finite odd integer then result is +Inf
+ * <li>if x < 0 and y > 0, finite, and odd integer then result is -(|x|<sup>y</sup>)
+ * <li>if x < 0 and y > 0, finite, and not integer then result is NaN
+ * </ul>
+ *
+ * @param x base to be raised
+ * @param y power to which base should be raised
+ * @return x<sup>y</sup>
+ */
+ public static Dfp pow(Dfp x, final Dfp y) {
+
+ // make sure we don't mix number with different precision
+ if (x.getField().getRadixDigits() != y.getField().getRadixDigits()) {
+ x.getField().setIEEEFlagsBits(DfpField.FLAG_INVALID);
+ final Dfp result = x.newInstance(x.getZero());
+ result.nans = Dfp.QNAN;
+ return x.dotrap(DfpField.FLAG_INVALID, POW_TRAP, x, result);
+ }
+
+ final Dfp zero = x.getZero();
+ final Dfp one = x.getOne();
+ final Dfp two = x.getTwo();
+ boolean invert = false;
+ int ui;
+
+ /* Check for special cases */
+ if (y.equals(zero)) {
+ return x.newInstance(one);
+ }
+
+ if (y.equals(one)) {
+ if (x.isNaN()) {
+ // Test for NaNs
+ x.getField().setIEEEFlagsBits(DfpField.FLAG_INVALID);
+ return x.dotrap(DfpField.FLAG_INVALID, POW_TRAP, x, x);
+ }
+ return x;
+ }
+
+ if (x.isNaN() || y.isNaN()) {
+ // Test for NaNs
+ x.getField().setIEEEFlagsBits(DfpField.FLAG_INVALID);
+ return x.dotrap(DfpField.FLAG_INVALID, POW_TRAP, x, x.newInstance((byte) 1, Dfp.QNAN));
+ }
+
+ // X == 0
+ if (x.equals(zero)) {
+ if (Dfp.copysign(one, x).greaterThan(zero)) {
+ // X == +0
+ if (y.greaterThan(zero)) {
+ return x.newInstance(zero);
+ } else {
+ return x.newInstance(x.newInstance((byte) 1, Dfp.INFINITE));
+ }
+ } else {
+ // X == -0
+ if (y.classify() == Dfp.FINITE
+ && y.rint().equals(y)
+ && !y.remainder(two).equals(zero)) {
+ // If y is odd integer
+ if (y.greaterThan(zero)) {
+ return x.newInstance(zero.negate());
+ } else {
+ return x.newInstance(x.newInstance((byte) -1, Dfp.INFINITE));
+ }
+ } else {
+ // Y is not odd integer
+ if (y.greaterThan(zero)) {
+ return x.newInstance(zero);
+ } else {
+ return x.newInstance(x.newInstance((byte) 1, Dfp.INFINITE));
+ }
+ }
+ }
+ }
+
+ if (x.lessThan(zero)) {
+ // Make x positive, but keep track of it
+ x = x.negate();
+ invert = true;
+ }
+
+ if (x.greaterThan(one) && y.classify() == Dfp.INFINITE) {
+ if (y.greaterThan(zero)) {
+ return y;
+ } else {
+ return x.newInstance(zero);
+ }
+ }
+
+ if (x.lessThan(one) && y.classify() == Dfp.INFINITE) {
+ if (y.greaterThan(zero)) {
+ return x.newInstance(zero);
+ } else {
+ return x.newInstance(Dfp.copysign(y, one));
+ }
+ }
+
+ if (x.equals(one) && y.classify() == Dfp.INFINITE) {
+ x.getField().setIEEEFlagsBits(DfpField.FLAG_INVALID);
+ return x.dotrap(DfpField.FLAG_INVALID, POW_TRAP, x, x.newInstance((byte) 1, Dfp.QNAN));
+ }
+
+ if (x.classify() == Dfp.INFINITE) {
+ // x = +/- inf
+ if (invert) {
+ // negative infinity
+ if (y.classify() == Dfp.FINITE
+ && y.rint().equals(y)
+ && !y.remainder(two).equals(zero)) {
+ // If y is odd integer
+ if (y.greaterThan(zero)) {
+ return x.newInstance(x.newInstance((byte) -1, Dfp.INFINITE));
+ } else {
+ return x.newInstance(zero.negate());
+ }
+ } else {
+ // Y is not odd integer
+ if (y.greaterThan(zero)) {
+ return x.newInstance(x.newInstance((byte) 1, Dfp.INFINITE));
+ } else {
+ return x.newInstance(zero);
+ }
+ }
+ } else {
+ // positive infinity
+ if (y.greaterThan(zero)) {
+ return x;
+ } else {
+ return x.newInstance(zero);
+ }
+ }
+ }
+
+ if (invert && !y.rint().equals(y)) {
+ x.getField().setIEEEFlagsBits(DfpField.FLAG_INVALID);
+ return x.dotrap(DfpField.FLAG_INVALID, POW_TRAP, x, x.newInstance((byte) 1, Dfp.QNAN));
+ }
+
+ // End special cases
+
+ Dfp r;
+ if (y.lessThan(x.newInstance(100000000)) && y.greaterThan(x.newInstance(-100000000))) {
+ final Dfp u = y.rint();
+ ui = u.intValue();
+
+ final Dfp v = y.subtract(u);
+
+ if (v.unequal(zero)) {
+ final Dfp a = v.multiply(log(x));
+ final Dfp b = a.divide(x.getField().getLn2()).rint();
+
+ final Dfp c = a.subtract(b.multiply(x.getField().getLn2()));
+ r = splitPow(split(x), ui);
+ r = r.multiply(pow(two, b.intValue()));
+ r = r.multiply(exp(c));
+ } else {
+ r = splitPow(split(x), ui);
+ }
+ } else {
+ // very large exponent. |y| > 1e8
+ r = exp(log(x).multiply(y));
+ }
+
+ if (invert && y.rint().equals(y) && !y.remainder(two).equals(zero)) {
+ // if y is odd integer
+ r = r.negate();
+ }
+
+ return x.newInstance(r);
+ }
+
+ /**
+ * Computes sin(a) Used when 0 < a < pi/4. Uses the classic Taylor series. x - x**3/3! + x**5/5!
+ * ...
+ *
+ * @param a number from which sine is desired, in split form
+ * @return sin(a)
+ */
+ protected static Dfp sinInternal(Dfp a[]) {
+
+ Dfp c = a[0].add(a[1]);
+ Dfp y = c;
+ c = c.multiply(c);
+ Dfp x = y;
+ Dfp fact = a[0].getOne();
+ Dfp py = new Dfp(y);
+
+ for (int i = 3; i < 90; i += 2) {
+ x = x.multiply(c);
+ x = x.negate();
+
+ fact = fact.divide((i - 1) * i); // 1 over fact
+ y = y.add(x.multiply(fact));
+ if (y.equals(py)) {
+ break;
+ }
+ py = new Dfp(y);
+ }
+
+ return y;
+ }
+
+ /**
+ * Computes cos(a) Used when 0 < a < pi/4. Uses the classic Taylor series for cosine. 1 -
+ * x**2/2! + x**4/4! ...
+ *
+ * @param a number from which cosine is desired, in split form
+ * @return cos(a)
+ */
+ protected static Dfp cosInternal(Dfp a[]) {
+ final Dfp one = a[0].getOne();
+
+ Dfp x = one;
+ Dfp y = one;
+ Dfp c = a[0].add(a[1]);
+ c = c.multiply(c);
+
+ Dfp fact = one;
+ Dfp py = new Dfp(y);
+
+ for (int i = 2; i < 90; i += 2) {
+ x = x.multiply(c);
+ x = x.negate();
+
+ fact = fact.divide((i - 1) * i); // 1 over fact
+
+ y = y.add(x.multiply(fact));
+ if (y.equals(py)) {
+ break;
+ }
+ py = new Dfp(y);
+ }
+
+ return y;
+ }
+
+ /**
+ * computes the sine of the argument.
+ *
+ * @param a number from which sine is desired
+ * @return sin(a)
+ */
+ public static Dfp sin(final Dfp a) {
+ final Dfp pi = a.getField().getPi();
+ final Dfp zero = a.getField().getZero();
+ boolean neg = false;
+
+ /* First reduce the argument to the range of +/- PI */
+ Dfp x = a.remainder(pi.multiply(2));
+
+ /* if x < 0 then apply identity sin(-x) = -sin(x) */
+ /* This puts x in the range 0 < x < PI */
+ if (x.lessThan(zero)) {
+ x = x.negate();
+ neg = true;
+ }
+
+ /* Since sine(x) = sine(pi - x) we can reduce the range to
+ * 0 < x < pi/2
+ */
+
+ if (x.greaterThan(pi.divide(2))) {
+ x = pi.subtract(x);
+ }
+
+ Dfp y;
+ if (x.lessThan(pi.divide(4))) {
+ y = sinInternal(split(x));
+ } else {
+ final Dfp c[] = new Dfp[2];
+ final Dfp[] piSplit = a.getField().getPiSplit();
+ c[0] = piSplit[0].divide(2).subtract(x);
+ c[1] = piSplit[1].divide(2);
+ y = cosInternal(c);
+ }
+
+ if (neg) {
+ y = y.negate();
+ }
+
+ return a.newInstance(y);
+ }
+
+ /**
+ * computes the cosine of the argument.
+ *
+ * @param a number from which cosine is desired
+ * @return cos(a)
+ */
+ public static Dfp cos(Dfp a) {
+ final Dfp pi = a.getField().getPi();
+ final Dfp zero = a.getField().getZero();
+ boolean neg = false;
+
+ /* First reduce the argument to the range of +/- PI */
+ Dfp x = a.remainder(pi.multiply(2));
+
+ /* if x < 0 then apply identity cos(-x) = cos(x) */
+ /* This puts x in the range 0 < x < PI */
+ if (x.lessThan(zero)) {
+ x = x.negate();
+ }
+
+ /* Since cos(x) = -cos(pi - x) we can reduce the range to
+ * 0 < x < pi/2
+ */
+
+ if (x.greaterThan(pi.divide(2))) {
+ x = pi.subtract(x);
+ neg = true;
+ }
+
+ Dfp y;
+ if (x.lessThan(pi.divide(4))) {
+ Dfp c[] = new Dfp[2];
+ c[0] = x;
+ c[1] = zero;
+
+ y = cosInternal(c);
+ } else {
+ final Dfp c[] = new Dfp[2];
+ final Dfp[] piSplit = a.getField().getPiSplit();
+ c[0] = piSplit[0].divide(2).subtract(x);
+ c[1] = piSplit[1].divide(2);
+ y = sinInternal(c);
+ }
+
+ if (neg) {
+ y = y.negate();
+ }
+
+ return a.newInstance(y);
+ }
+
+ /**
+ * computes the tangent of the argument.
+ *
+ * @param a number from which tangent is desired
+ * @return tan(a)
+ */
+ public static Dfp tan(final Dfp a) {
+ return sin(a).divide(cos(a));
+ }
+
+ /**
+ * computes the arc-tangent of the argument.
+ *
+ * @param a number from which arc-tangent is desired
+ * @return atan(a)
+ */
+ protected static Dfp atanInternal(final Dfp a) {
+
+ Dfp y = new Dfp(a);
+ Dfp x = new Dfp(y);
+ Dfp py = new Dfp(y);
+
+ for (int i = 3; i < 90; i += 2) {
+ x = x.multiply(a);
+ x = x.multiply(a);
+ x = x.negate();
+ y = y.add(x.divide(i));
+ if (y.equals(py)) {
+ break;
+ }
+ py = new Dfp(y);
+ }
+
+ return y;
+ }
+
+ /**
+ * computes the arc tangent of the argument
+ *
+ * <p>Uses the typical taylor series
+ *
+ * <p>but may reduce arguments using the following identity tan(x+y) = (tan(x) + tan(y)) / (1 -
+ * tan(x)*tan(y))
+ *
+ * <p>since tan(PI/8) = sqrt(2)-1,
+ *
+ * <p>atan(x) = atan( (x - sqrt(2) + 1) / (1+x*sqrt(2) - x) + PI/8.0
+ *
+ * @param a number from which arc-tangent is desired
+ * @return atan(a)
+ */
+ public static Dfp atan(final Dfp a) {
+ final Dfp zero = a.getField().getZero();
+ final Dfp one = a.getField().getOne();
+ final Dfp[] sqr2Split = a.getField().getSqr2Split();
+ final Dfp[] piSplit = a.getField().getPiSplit();
+ boolean recp = false;
+ boolean neg = false;
+ boolean sub = false;
+
+ final Dfp ty = sqr2Split[0].subtract(one).add(sqr2Split[1]);
+
+ Dfp x = new Dfp(a);
+ if (x.lessThan(zero)) {
+ neg = true;
+ x = x.negate();
+ }
+
+ if (x.greaterThan(one)) {
+ recp = true;
+ x = one.divide(x);
+ }
+
+ if (x.greaterThan(ty)) {
+ Dfp sty[] = new Dfp[2];
+ sub = true;
+
+ sty[0] = sqr2Split[0].subtract(one);
+ sty[1] = sqr2Split[1];
+
+ Dfp[] xs = split(x);
+
+ Dfp[] ds = splitMult(xs, sty);
+ ds[0] = ds[0].add(one);
+
+ xs[0] = xs[0].subtract(sty[0]);
+ xs[1] = xs[1].subtract(sty[1]);
+
+ xs = splitDiv(xs, ds);
+ x = xs[0].add(xs[1]);
+
+ // x = x.subtract(ty).divide(dfp.one.add(x.multiply(ty)));
+ }
+
+ Dfp y = atanInternal(x);
+
+ if (sub) {
+ y = y.add(piSplit[0].divide(8)).add(piSplit[1].divide(8));
+ }
+
+ if (recp) {
+ y = piSplit[0].divide(2).subtract(y).add(piSplit[1].divide(2));
+ }
+
+ if (neg) {
+ y = y.negate();
+ }
+
+ return a.newInstance(y);
+ }
+
+ /**
+ * computes the arc-sine of the argument.
+ *
+ * @param a number from which arc-sine is desired
+ * @return asin(a)
+ */
+ public static Dfp asin(final Dfp a) {
+ return atan(a.divide(a.getOne().subtract(a.multiply(a)).sqrt()));
+ }
+
+ /**
+ * computes the arc-cosine of the argument.
+ *
+ * @param a number from which arc-cosine is desired
+ * @return acos(a)
+ */
+ public static Dfp acos(Dfp a) {
+ Dfp result;
+ boolean negative = false;
+
+ if (a.lessThan(a.getZero())) {
+ negative = true;
+ }
+
+ a = Dfp.copysign(a, a.getOne()); // absolute value
+
+ result = atan(a.getOne().subtract(a.multiply(a)).sqrt().divide(a));
+
+ if (negative) {
+ result = a.getField().getPi().subtract(result);
+ }
+
+ return a.newInstance(result);
+ }
+}
diff --git a/src/main/java/org/apache/commons/math3/dfp/UnivariateDfpFunction.java b/src/main/java/org/apache/commons/math3/dfp/UnivariateDfpFunction.java
new file mode 100644
index 0000000..4507b41
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/dfp/UnivariateDfpFunction.java
@@ -0,0 +1,40 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math3.dfp;
+
+/**
+ * An interface representing a univariate {@link Dfp} function.
+ *
+ * @deprecated as of 3.6, replaced with {@link
+ * org.apache.commons.math3.analysis.RealFieldUnivariateFunction}
+ */
+@Deprecated
+public interface UnivariateDfpFunction {
+
+ /**
+ * Compute the value of the function.
+ *
+ * @param x Point at which the function value should be computed.
+ * @return the value.
+ * @throws IllegalArgumentException when the activated method itself can ascertain that
+ * preconditions, specified in the API expressed at the level of the activated method, have
+ * been violated. In the vast majority of cases where Commons-Math throws
+ * IllegalArgumentException, it is the result of argument checking of actual parameters
+ * immediately passed to a method.
+ */
+ Dfp value(Dfp x);
+}
diff --git a/src/main/java/org/apache/commons/math3/dfp/package-info.java b/src/main/java/org/apache/commons/math3/dfp/package-info.java
new file mode 100644
index 0000000..f4a7ca8
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/dfp/package-info.java
@@ -0,0 +1,80 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/**
+ * Decimal floating point library for Java
+ *
+ * <p>Another floating point class. This one is built using radix 10000 which is 10<sup>4</sup>, so
+ * its almost decimal.
+ *
+ * <p>The design goals here are:
+ *
+ * <ol>
+ * <li>Decimal math, or close to it
+ * <li>Settable precision (but no mix between numbers using different settings)
+ * <li>Portability. Code should be keep as portable as possible.
+ * <li>Performance
+ * <li>Accuracy - Results should always be +/- 1 ULP for basic algebraic operation
+ * <li>Comply with IEEE 854-1987 as much as possible. (See IEEE 854-1987 notes below)
+ * </ol>
+ *
+ * <p>Trade offs:
+ *
+ * <ol>
+ * <li>Memory foot print. I'm using more memory than necessary to represent numbers to get better
+ * performance.
+ * <li>Digits are bigger, so rounding is a greater loss. So, if you really need 12 decimal digits,
+ * better use 4 base 10000 digits there can be one partially filled.
+ * </ol>
+ *
+ * <p>Numbers are represented in the following form:
+ *
+ * <pre>
+ * n = sign &times; mant &times; (radix)<sup>exp</sup>;</p>
+ * </pre>
+ *
+ * where sign is &plusmn;1, mantissa represents a fractional number between zero and one. mant[0] is
+ * the least significant digit. exp is in the range of -32767 to 32768
+ *
+ * <p>IEEE 854-1987 Notes and differences
+ *
+ * <p>IEEE 854 requires the radix to be either 2 or 10. The radix here is 10000, so that requirement
+ * is not met, but it is possible that a subclassed can be made to make it behave as a radix 10
+ * number. It is my opinion that if it looks and behaves as a radix 10 number then it is one and
+ * that requirement would be met.
+ *
+ * <p>The radix of 10000 was chosen because it should be faster to operate on 4 decimal digits at
+ * once instead of one at a time. Radix 10 behavior can be realized by add an additional rounding
+ * step to ensure that the number of decimal digits represented is constant.
+ *
+ * <p>The IEEE standard specifically leaves out internal data encoding, so it is reasonable to
+ * conclude that such a subclass of this radix 10000 system is merely an encoding of a radix 10
+ * system.
+ *
+ * <p>IEEE 854 also specifies the existence of "sub-normal" numbers. This class does not contain any
+ * such entities. The most significant radix 10000 digit is always non-zero. Instead, we support
+ * "gradual underflow" by raising the underflow flag for numbers less with exponent less than
+ * expMin, but don't flush to zero until the exponent reaches MIN_EXP-digits. Thus the smallest
+ * number we can represent would be: 1E(-(MIN_EXP-digits-1)&lowast;4), eg, for digits=5,
+ * MIN_EXP=-32767, that would be 1e-131092.
+ *
+ * <p>IEEE 854 defines that the implied radix point lies just to the right of the most significant
+ * digit and to the left of the remaining digits. This implementation puts the implied radix point
+ * to the left of all digits including the most significant one. The most significant digit here is
+ * the one just to the right of the radix point. This is a fine detail and is really only a matter
+ * of definition. Any side effects of this can be rendered invisible by a subclass.
+ */
+package org.apache.commons.math3.dfp;
diff --git a/src/main/java/org/apache/commons/math3/distribution/AbstractIntegerDistribution.java b/src/main/java/org/apache/commons/math3/distribution/AbstractIntegerDistribution.java
new file mode 100644
index 0000000..102700e
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/distribution/AbstractIntegerDistribution.java
@@ -0,0 +1,250 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math3.distribution;
+
+import org.apache.commons.math3.exception.MathInternalError;
+import org.apache.commons.math3.exception.NotStrictlyPositiveException;
+import org.apache.commons.math3.exception.NumberIsTooLargeException;
+import org.apache.commons.math3.exception.OutOfRangeException;
+import org.apache.commons.math3.exception.util.LocalizedFormats;
+import org.apache.commons.math3.random.RandomGenerator;
+import org.apache.commons.math3.util.FastMath;
+
+import java.io.Serializable;
+
+/**
+ * Base class for integer-valued discrete distributions. Default implementations are provided for
+ * some of the methods that do not vary from distribution to distribution.
+ */
+public abstract class AbstractIntegerDistribution implements IntegerDistribution, Serializable {
+
+ /** Serializable version identifier */
+ private static final long serialVersionUID = -1146319659338487221L;
+
+ /**
+ * RandomData instance used to generate samples from the distribution.
+ *
+ * @deprecated As of 3.1, to be removed in 4.0. Please use the {@link #random} instance variable
+ * instead.
+ */
+ @Deprecated
+ protected final org.apache.commons.math3.random.RandomDataImpl randomData =
+ new org.apache.commons.math3.random.RandomDataImpl();
+
+ /**
+ * RNG instance used to generate samples from the distribution.
+ *
+ * @since 3.1
+ */
+ protected final RandomGenerator random;
+
+ /**
+ * @deprecated As of 3.1, to be removed in 4.0. Please use {@link
+ * #AbstractIntegerDistribution(RandomGenerator)} instead.
+ */
+ @Deprecated
+ protected AbstractIntegerDistribution() {
+ // Legacy users are only allowed to access the deprecated "randomData".
+ // New users are forbidden to use this constructor.
+ random = null;
+ }
+
+ /**
+ * @param rng Random number generator.
+ * @since 3.1
+ */
+ protected AbstractIntegerDistribution(RandomGenerator rng) {
+ random = rng;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * <p>The default implementation uses the identity
+ *
+ * <p>{@code P(x0 < X <= x1) = P(X <= x1) - P(X <= x0)}
+ */
+ public double cumulativeProbability(int x0, int x1) throws NumberIsTooLargeException {
+ if (x1 < x0) {
+ throw new NumberIsTooLargeException(
+ LocalizedFormats.LOWER_ENDPOINT_ABOVE_UPPER_ENDPOINT, x0, x1, true);
+ }
+ return cumulativeProbability(x1) - cumulativeProbability(x0);
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * <p>The default implementation returns
+ *
+ * <ul>
+ * <li>{@link #getSupportLowerBound()} for {@code p = 0},
+ * <li>{@link #getSupportUpperBound()} for {@code p = 1}, and
+ * <li>{@link #solveInverseCumulativeProbability(double, int, int)} for {@code 0 < p < 1}.
+ * </ul>
+ */
+ public int inverseCumulativeProbability(final double p) throws OutOfRangeException {
+ if (p < 0.0 || p > 1.0) {
+ throw new OutOfRangeException(p, 0, 1);
+ }
+
+ int lower = getSupportLowerBound();
+ if (p == 0.0) {
+ return lower;
+ }
+ if (lower == Integer.MIN_VALUE) {
+ if (checkedCumulativeProbability(lower) >= p) {
+ return lower;
+ }
+ } else {
+ lower -= 1; // this ensures cumulativeProbability(lower) < p, which
+ // is important for the solving step
+ }
+
+ int upper = getSupportUpperBound();
+ if (p == 1.0) {
+ return upper;
+ }
+
+ // use the one-sided Chebyshev inequality to narrow the bracket
+ // cf. AbstractRealDistribution.inverseCumulativeProbability(double)
+ final double mu = getNumericalMean();
+ final double sigma = FastMath.sqrt(getNumericalVariance());
+ final boolean chebyshevApplies =
+ !(Double.isInfinite(mu)
+ || Double.isNaN(mu)
+ || Double.isInfinite(sigma)
+ || Double.isNaN(sigma)
+ || sigma == 0.0);
+ if (chebyshevApplies) {
+ double k = FastMath.sqrt((1.0 - p) / p);
+ double tmp = mu - k * sigma;
+ if (tmp > lower) {
+ lower = ((int) FastMath.ceil(tmp)) - 1;
+ }
+ k = 1.0 / k;
+ tmp = mu + k * sigma;
+ if (tmp < upper) {
+ upper = ((int) FastMath.ceil(tmp)) - 1;
+ }
+ }
+
+ return solveInverseCumulativeProbability(p, lower, upper);
+ }
+
+ /**
+ * This is a utility function used by {@link #inverseCumulativeProbability(double)}. It assumes
+ * {@code 0 < p < 1} and that the inverse cumulative probability lies in the bracket {@code
+ * (lower, upper]}. The implementation does simple bisection to find the smallest {@code
+ * p}-quantile <code>inf{x in Z | P(X<=x) >= p}</code>.
+ *
+ * @param p the cumulative probability
+ * @param lower a value satisfying {@code cumulativeProbability(lower) < p}
+ * @param upper a value satisfying {@code p <= cumulativeProbability(upper)}
+ * @return the smallest {@code p}-quantile of this distribution
+ */
+ protected int solveInverseCumulativeProbability(final double p, int lower, int upper) {
+ while (lower + 1 < upper) {
+ int xm = (lower + upper) / 2;
+ if (xm < lower || xm > upper) {
+ /*
+ * Overflow.
+ * There will never be an overflow in both calculation methods
+ * for xm at the same time
+ */
+ xm = lower + (upper - lower) / 2;
+ }
+
+ double pm = checkedCumulativeProbability(xm);
+ if (pm >= p) {
+ upper = xm;
+ } else {
+ lower = xm;
+ }
+ }
+ return upper;
+ }
+
+ /** {@inheritDoc} */
+ public void reseedRandomGenerator(long seed) {
+ random.setSeed(seed);
+ randomData.reSeed(seed);
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * <p>The default implementation uses the <a
+ * href="http://en.wikipedia.org/wiki/Inverse_transform_sampling">inversion method</a>.
+ */
+ public int sample() {
+ return inverseCumulativeProbability(random.nextDouble());
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * <p>The default implementation generates the sample by calling {@link #sample()} in a loop.
+ */
+ public int[] sample(int sampleSize) {
+ if (sampleSize <= 0) {
+ throw new NotStrictlyPositiveException(LocalizedFormats.NUMBER_OF_SAMPLES, sampleSize);
+ }
+ int[] out = new int[sampleSize];
+ for (int i = 0; i < sampleSize; i++) {
+ out[i] = sample();
+ }
+ return out;
+ }
+
+ /**
+ * Computes the cumulative probability function and checks for {@code NaN} values returned.
+ * Throws {@code MathInternalError} if the value is {@code NaN}. Rethrows any exception
+ * encountered evaluating the cumulative probability function. Throws {@code MathInternalError}
+ * if the cumulative probability function returns {@code NaN}.
+ *
+ * @param argument input value
+ * @return the cumulative probability
+ * @throws MathInternalError if the cumulative probability is {@code NaN}
+ */
+ private double checkedCumulativeProbability(int argument) throws MathInternalError {
+ double result = Double.NaN;
+ result = cumulativeProbability(argument);
+ if (Double.isNaN(result)) {
+ throw new MathInternalError(
+ LocalizedFormats.DISCRETE_CUMULATIVE_PROBABILITY_RETURNED_NAN, argument);
+ }
+ return result;
+ }
+
+ /**
+ * For a random variable {@code X} whose values are distributed according to this distribution,
+ * this method returns {@code log(P(X = x))}, where {@code log} is the natural logarithm. In
+ * other words, this method represents the logarithm of the probability mass function (PMF) for
+ * the distribution. Note that due to the floating point precision and under/overflow issues,
+ * this method will for some distributions be more precise and faster than computing the
+ * logarithm of {@link #probability(int)}.
+ *
+ * <p>The default implementation simply computes the logarithm of {@code probability(x)}.
+ *
+ * @param x the point at which the PMF is evaluated
+ * @return the logarithm of the value of the probability mass function at {@code x}
+ */
+ public double logProbability(int x) {
+ return FastMath.log(probability(x));
+ }
+}
diff --git a/src/main/java/org/apache/commons/math3/distribution/AbstractMultivariateRealDistribution.java b/src/main/java/org/apache/commons/math3/distribution/AbstractMultivariateRealDistribution.java
new file mode 100644
index 0000000..98e9348
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/distribution/AbstractMultivariateRealDistribution.java
@@ -0,0 +1,68 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math3.distribution;
+
+import org.apache.commons.math3.exception.NotStrictlyPositiveException;
+import org.apache.commons.math3.exception.util.LocalizedFormats;
+import org.apache.commons.math3.random.RandomGenerator;
+
+/**
+ * Base class for multivariate probability distributions.
+ *
+ * @since 3.1
+ */
+public abstract class AbstractMultivariateRealDistribution implements MultivariateRealDistribution {
+ /** RNG instance used to generate samples from the distribution. */
+ protected final RandomGenerator random;
+
+ /** The number of dimensions or columns in the multivariate distribution. */
+ private final int dimension;
+
+ /**
+ * @param rng Random number generator.
+ * @param n Number of dimensions.
+ */
+ protected AbstractMultivariateRealDistribution(RandomGenerator rng, int n) {
+ random = rng;
+ dimension = n;
+ }
+
+ /** {@inheritDoc} */
+ public void reseedRandomGenerator(long seed) {
+ random.setSeed(seed);
+ }
+
+ /** {@inheritDoc} */
+ public int getDimension() {
+ return dimension;
+ }
+
+ /** {@inheritDoc} */
+ public abstract double[] sample();
+
+ /** {@inheritDoc} */
+ public double[][] sample(final int sampleSize) {
+ if (sampleSize <= 0) {
+ throw new NotStrictlyPositiveException(LocalizedFormats.NUMBER_OF_SAMPLES, sampleSize);
+ }
+ final double[][] out = new double[sampleSize][dimension];
+ for (int i = 0; i < sampleSize; i++) {
+ out[i] = sample();
+ }
+ return out;
+ }
+}
diff --git a/src/main/java/org/apache/commons/math3/distribution/AbstractRealDistribution.java b/src/main/java/org/apache/commons/math3/distribution/AbstractRealDistribution.java
new file mode 100644
index 0000000..b9e5bca
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/distribution/AbstractRealDistribution.java
@@ -0,0 +1,306 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math3.distribution;
+
+import org.apache.commons.math3.analysis.UnivariateFunction;
+import org.apache.commons.math3.analysis.solvers.UnivariateSolverUtils;
+import org.apache.commons.math3.exception.NotStrictlyPositiveException;
+import org.apache.commons.math3.exception.NumberIsTooLargeException;
+import org.apache.commons.math3.exception.OutOfRangeException;
+import org.apache.commons.math3.exception.util.LocalizedFormats;
+import org.apache.commons.math3.random.RandomGenerator;
+import org.apache.commons.math3.util.FastMath;
+
+import java.io.Serializable;
+
+/**
+ * Base class for probability distributions on the reals. Default implementations are provided for
+ * some of the methods that do not vary from distribution to distribution.
+ *
+ * @since 3.0
+ */
+public abstract class AbstractRealDistribution implements RealDistribution, Serializable {
+ /** Default accuracy. */
+ public static final double SOLVER_DEFAULT_ABSOLUTE_ACCURACY = 1e-6;
+
+ /** Serializable version identifier */
+ private static final long serialVersionUID = -38038050983108802L;
+
+ /**
+ * RandomData instance used to generate samples from the distribution.
+ *
+ * @deprecated As of 3.1, to be removed in 4.0. Please use the {@link #random} instance variable
+ * instead.
+ */
+ @Deprecated
+ protected org.apache.commons.math3.random.RandomDataImpl randomData =
+ new org.apache.commons.math3.random.RandomDataImpl();
+
+ /**
+ * RNG instance used to generate samples from the distribution.
+ *
+ * @since 3.1
+ */
+ protected final RandomGenerator random;
+
+ /** Solver absolute accuracy for inverse cumulative computation */
+ private double solverAbsoluteAccuracy = SOLVER_DEFAULT_ABSOLUTE_ACCURACY;
+
+ /**
+ * @deprecated As of 3.1, to be removed in 4.0. Please use {@link
+ * #AbstractRealDistribution(RandomGenerator)} instead.
+ */
+ @Deprecated
+ protected AbstractRealDistribution() {
+ // Legacy users are only allowed to access the deprecated "randomData".
+ // New users are forbidden to use this constructor.
+ random = null;
+ }
+
+ /**
+ * @param rng Random number generator.
+ * @since 3.1
+ */
+ protected AbstractRealDistribution(RandomGenerator rng) {
+ random = rng;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * <p>The default implementation uses the identity
+ *
+ * <p>{@code P(x0 < X <= x1) = P(X <= x1) - P(X <= x0)}
+ *
+ * @deprecated As of 3.1 (to be removed in 4.0). Please use {@link #probability(double,double)}
+ * instead.
+ */
+ @Deprecated
+ public double cumulativeProbability(double x0, double x1) throws NumberIsTooLargeException {
+ return probability(x0, x1);
+ }
+
+ /**
+ * For a random variable {@code X} whose values are distributed according to this distribution,
+ * this method returns {@code P(x0 < X <= x1)}.
+ *
+ * @param x0 Lower bound (excluded).
+ * @param x1 Upper bound (included).
+ * @return the probability that a random variable with this distribution takes a value between
+ * {@code x0} and {@code x1}, excluding the lower and including the upper endpoint.
+ * @throws NumberIsTooLargeException if {@code x0 > x1}.
+ * <p>The default implementation uses the identity {@code P(x0 < X <= x1) = P(X <= x1) - P(X
+ * <= x0)}
+ * @since 3.1
+ */
+ public double probability(double x0, double x1) {
+ if (x0 > x1) {
+ throw new NumberIsTooLargeException(
+ LocalizedFormats.LOWER_ENDPOINT_ABOVE_UPPER_ENDPOINT, x0, x1, true);
+ }
+ return cumulativeProbability(x1) - cumulativeProbability(x0);
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * <p>The default implementation returns
+ *
+ * <ul>
+ * <li>{@link #getSupportLowerBound()} for {@code p = 0},
+ * <li>{@link #getSupportUpperBound()} for {@code p = 1}.
+ * </ul>
+ */
+ public double inverseCumulativeProbability(final double p) throws OutOfRangeException {
+ /*
+ * IMPLEMENTATION NOTES
+ * --------------------
+ * Where applicable, use is made of the one-sided Chebyshev inequality
+ * to bracket the root. This inequality states that
+ * P(X - mu >= k * sig) <= 1 / (1 + k^2),
+ * mu: mean, sig: standard deviation. Equivalently
+ * 1 - P(X < mu + k * sig) <= 1 / (1 + k^2),
+ * F(mu + k * sig) >= k^2 / (1 + k^2).
+ *
+ * For k = sqrt(p / (1 - p)), we find
+ * F(mu + k * sig) >= p,
+ * and (mu + k * sig) is an upper-bound for the root.
+ *
+ * Then, introducing Y = -X, mean(Y) = -mu, sd(Y) = sig, and
+ * P(Y >= -mu + k * sig) <= 1 / (1 + k^2),
+ * P(-X >= -mu + k * sig) <= 1 / (1 + k^2),
+ * P(X <= mu - k * sig) <= 1 / (1 + k^2),
+ * F(mu - k * sig) <= 1 / (1 + k^2).
+ *
+ * For k = sqrt((1 - p) / p), we find
+ * F(mu - k * sig) <= p,
+ * and (mu - k * sig) is a lower-bound for the root.
+ *
+ * In cases where the Chebyshev inequality does not apply, geometric
+ * progressions 1, 2, 4, ... and -1, -2, -4, ... are used to bracket
+ * the root.
+ */
+ if (p < 0.0 || p > 1.0) {
+ throw new OutOfRangeException(p, 0, 1);
+ }
+
+ double lowerBound = getSupportLowerBound();
+ if (p == 0.0) {
+ return lowerBound;
+ }
+
+ double upperBound = getSupportUpperBound();
+ if (p == 1.0) {
+ return upperBound;
+ }
+
+ final double mu = getNumericalMean();
+ final double sig = FastMath.sqrt(getNumericalVariance());
+ final boolean chebyshevApplies;
+ chebyshevApplies =
+ !(Double.isInfinite(mu)
+ || Double.isNaN(mu)
+ || Double.isInfinite(sig)
+ || Double.isNaN(sig));
+
+ if (lowerBound == Double.NEGATIVE_INFINITY) {
+ if (chebyshevApplies) {
+ lowerBound = mu - sig * FastMath.sqrt((1. - p) / p);
+ } else {
+ lowerBound = -1.0;
+ while (cumulativeProbability(lowerBound) >= p) {
+ lowerBound *= 2.0;
+ }
+ }
+ }
+
+ if (upperBound == Double.POSITIVE_INFINITY) {
+ if (chebyshevApplies) {
+ upperBound = mu + sig * FastMath.sqrt(p / (1. - p));
+ } else {
+ upperBound = 1.0;
+ while (cumulativeProbability(upperBound) < p) {
+ upperBound *= 2.0;
+ }
+ }
+ }
+
+ final UnivariateFunction toSolve =
+ new UnivariateFunction() {
+ /** {@inheritDoc} */
+ public double value(final double x) {
+ return cumulativeProbability(x) - p;
+ }
+ };
+
+ double x =
+ UnivariateSolverUtils.solve(
+ toSolve, lowerBound, upperBound, getSolverAbsoluteAccuracy());
+
+ if (!isSupportConnected()) {
+ /* Test for plateau. */
+ final double dx = getSolverAbsoluteAccuracy();
+ if (x - dx >= getSupportLowerBound()) {
+ double px = cumulativeProbability(x);
+ if (cumulativeProbability(x - dx) == px) {
+ upperBound = x;
+ while (upperBound - lowerBound > dx) {
+ final double midPoint = 0.5 * (lowerBound + upperBound);
+ if (cumulativeProbability(midPoint) < px) {
+ lowerBound = midPoint;
+ } else {
+ upperBound = midPoint;
+ }
+ }
+ return upperBound;
+ }
+ }
+ }
+ return x;
+ }
+
+ /**
+ * Returns the solver absolute accuracy for inverse cumulative computation. You can override
+ * this method in order to use a Brent solver with an absolute accuracy different from the
+ * default.
+ *
+ * @return the maximum absolute error in inverse cumulative probability estimates
+ */
+ protected double getSolverAbsoluteAccuracy() {
+ return solverAbsoluteAccuracy;
+ }
+
+ /** {@inheritDoc} */
+ public void reseedRandomGenerator(long seed) {
+ random.setSeed(seed);
+ randomData.reSeed(seed);
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * <p>The default implementation uses the <a
+ * href="http://en.wikipedia.org/wiki/Inverse_transform_sampling">inversion method. </a>
+ */
+ public double sample() {
+ return inverseCumulativeProbability(random.nextDouble());
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * <p>The default implementation generates the sample by calling {@link #sample()} in a loop.
+ */
+ public double[] sample(int sampleSize) {
+ if (sampleSize <= 0) {
+ throw new NotStrictlyPositiveException(LocalizedFormats.NUMBER_OF_SAMPLES, sampleSize);
+ }
+ double[] out = new double[sampleSize];
+ for (int i = 0; i < sampleSize; i++) {
+ out[i] = sample();
+ }
+ return out;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @return zero.
+ * @since 3.1
+ */
+ public double probability(double x) {
+ return 0d;
+ }
+
+ /**
+ * Returns the natural logarithm of the probability density function (PDF) of this distribution
+ * evaluated at the specified point {@code x}. In general, the PDF is the derivative of the
+ * {@link #cumulativeProbability(double) CDF}. If the derivative does not exist at {@code x},
+ * then an appropriate replacement should be returned, e.g. {@code Double.POSITIVE_INFINITY},
+ * {@code Double.NaN}, or the limit inferior or limit superior of the difference quotient. Note
+ * that due to the floating point precision and under/overflow issues, this method will for some
+ * distributions be more precise and faster than computing the logarithm of {@link
+ * #density(double)}. The default implementation simply computes the logarithm of {@code
+ * density(x)}.
+ *
+ * @param x the point at which the PDF is evaluated
+ * @return the logarithm of the value of the probability density function at point {@code x}
+ */
+ public double logDensity(double x) {
+ return FastMath.log(density(x));
+ }
+}
diff --git a/src/main/java/org/apache/commons/math3/distribution/BetaDistribution.java b/src/main/java/org/apache/commons/math3/distribution/BetaDistribution.java
new file mode 100644
index 0000000..c7c2663
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/distribution/BetaDistribution.java
@@ -0,0 +1,417 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math3.distribution;
+
+import org.apache.commons.math3.exception.NumberIsTooSmallException;
+import org.apache.commons.math3.exception.util.LocalizedFormats;
+import org.apache.commons.math3.random.RandomGenerator;
+import org.apache.commons.math3.random.Well19937c;
+import org.apache.commons.math3.special.Beta;
+import org.apache.commons.math3.special.Gamma;
+import org.apache.commons.math3.util.FastMath;
+import org.apache.commons.math3.util.Precision;
+
+/**
+ * Implements the Beta distribution.
+ *
+ * @see <a href="http://en.wikipedia.org/wiki/Beta_distribution">Beta distribution</a>
+ * @since 2.0 (changed to concrete class in 3.0)
+ */
+public class BetaDistribution extends AbstractRealDistribution {
+ /**
+ * Default inverse cumulative probability accuracy.
+ *
+ * @since 2.1
+ */
+ public static final double DEFAULT_INVERSE_ABSOLUTE_ACCURACY = 1e-9;
+
+ /** Serializable version identifier. */
+ private static final long serialVersionUID = -1221965979403477668L;
+
+ /** First shape parameter. */
+ private final double alpha;
+
+ /** Second shape parameter. */
+ private final double beta;
+
+ /**
+ * Normalizing factor used in density computations. updated whenever alpha or beta are changed.
+ */
+ private double z;
+
+ /** Inverse cumulative probability accuracy. */
+ private final double solverAbsoluteAccuracy;
+
+ /**
+ * Build a new instance.
+ *
+ * <p><b>Note:</b> this constructor will implicitly create an instance of {@link Well19937c} as
+ * random generator to be used for sampling only (see {@link #sample()} and {@link
+ * #sample(int)}). In case no sampling is needed for the created distribution, it is advised to
+ * pass {@code null} as random generator via the appropriate constructors to avoid the
+ * additional initialisation overhead.
+ *
+ * @param alpha First shape parameter (must be positive).
+ * @param beta Second shape parameter (must be positive).
+ */
+ public BetaDistribution(double alpha, double beta) {
+ this(alpha, beta, DEFAULT_INVERSE_ABSOLUTE_ACCURACY);
+ }
+
+ /**
+ * Build a new instance.
+ *
+ * <p><b>Note:</b> this constructor will implicitly create an instance of {@link Well19937c} as
+ * random generator to be used for sampling only (see {@link #sample()} and {@link
+ * #sample(int)}). In case no sampling is needed for the created distribution, it is advised to
+ * pass {@code null} as random generator via the appropriate constructors to avoid the
+ * additional initialisation overhead.
+ *
+ * @param alpha First shape parameter (must be positive).
+ * @param beta Second shape parameter (must be positive).
+ * @param inverseCumAccuracy Maximum absolute error in inverse cumulative probability estimates
+ * (defaults to {@link #DEFAULT_INVERSE_ABSOLUTE_ACCURACY}).
+ * @since 2.1
+ */
+ public BetaDistribution(double alpha, double beta, double inverseCumAccuracy) {
+ this(new Well19937c(), alpha, beta, inverseCumAccuracy);
+ }
+
+ /**
+ * Creates a &beta; distribution.
+ *
+ * @param rng Random number generator.
+ * @param alpha First shape parameter (must be positive).
+ * @param beta Second shape parameter (must be positive).
+ * @since 3.3
+ */
+ public BetaDistribution(RandomGenerator rng, double alpha, double beta) {
+ this(rng, alpha, beta, DEFAULT_INVERSE_ABSOLUTE_ACCURACY);
+ }
+
+ /**
+ * Creates a &beta; distribution.
+ *
+ * @param rng Random number generator.
+ * @param alpha First shape parameter (must be positive).
+ * @param beta Second shape parameter (must be positive).
+ * @param inverseCumAccuracy Maximum absolute error in inverse cumulative probability estimates
+ * (defaults to {@link #DEFAULT_INVERSE_ABSOLUTE_ACCURACY}).
+ * @since 3.1
+ */
+ public BetaDistribution(
+ RandomGenerator rng, double alpha, double beta, double inverseCumAccuracy) {
+ super(rng);
+
+ this.alpha = alpha;
+ this.beta = beta;
+ z = Double.NaN;
+ solverAbsoluteAccuracy = inverseCumAccuracy;
+ }
+
+ /**
+ * Access the first shape parameter, {@code alpha}.
+ *
+ * @return the first shape parameter.
+ */
+ public double getAlpha() {
+ return alpha;
+ }
+
+ /**
+ * Access the second shape parameter, {@code beta}.
+ *
+ * @return the second shape parameter.
+ */
+ public double getBeta() {
+ return beta;
+ }
+
+ /** Recompute the normalization factor. */
+ private void recomputeZ() {
+ if (Double.isNaN(z)) {
+ z = Gamma.logGamma(alpha) + Gamma.logGamma(beta) - Gamma.logGamma(alpha + beta);
+ }
+ }
+
+ /** {@inheritDoc} */
+ public double density(double x) {
+ final double logDensity = logDensity(x);
+ return logDensity == Double.NEGATIVE_INFINITY ? 0 : FastMath.exp(logDensity);
+ }
+
+ /** {@inheritDoc} * */
+ @Override
+ public double logDensity(double x) {
+ recomputeZ();
+ if (x < 0 || x > 1) {
+ return Double.NEGATIVE_INFINITY;
+ } else if (x == 0) {
+ if (alpha < 1) {
+ throw new NumberIsTooSmallException(
+ LocalizedFormats.CANNOT_COMPUTE_BETA_DENSITY_AT_0_FOR_SOME_ALPHA,
+ alpha,
+ 1,
+ false);
+ }
+ return Double.NEGATIVE_INFINITY;
+ } else if (x == 1) {
+ if (beta < 1) {
+ throw new NumberIsTooSmallException(
+ LocalizedFormats.CANNOT_COMPUTE_BETA_DENSITY_AT_1_FOR_SOME_BETA,
+ beta,
+ 1,
+ false);
+ }
+ return Double.NEGATIVE_INFINITY;
+ } else {
+ double logX = FastMath.log(x);
+ double log1mX = FastMath.log1p(-x);
+ return (alpha - 1) * logX + (beta - 1) * log1mX - z;
+ }
+ }
+
+ /** {@inheritDoc} */
+ public double cumulativeProbability(double x) {
+ if (x <= 0) {
+ return 0;
+ } else if (x >= 1) {
+ return 1;
+ } else {
+ return Beta.regularizedBeta(x, alpha, beta);
+ }
+ }
+
+ /**
+ * Return the absolute accuracy setting of the solver used to estimate inverse cumulative
+ * probabilities.
+ *
+ * @return the solver absolute accuracy.
+ * @since 2.1
+ */
+ @Override
+ protected double getSolverAbsoluteAccuracy() {
+ return solverAbsoluteAccuracy;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * <p>For first shape parameter {@code alpha} and second shape parameter {@code beta}, the mean
+ * is {@code alpha / (alpha + beta)}.
+ */
+ public double getNumericalMean() {
+ final double a = getAlpha();
+ return a / (a + getBeta());
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * <p>For first shape parameter {@code alpha} and second shape parameter {@code beta}, the
+ * variance is {@code (alpha * beta) / [(alpha + beta)^2 * (alpha + beta + 1)]}.
+ */
+ public double getNumericalVariance() {
+ final double a = getAlpha();
+ final double b = getBeta();
+ final double alphabetasum = a + b;
+ return (a * b) / ((alphabetasum * alphabetasum) * (alphabetasum + 1));
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * <p>The lower bound of the support is always 0 no matter the parameters.
+ *
+ * @return lower bound of the support (always 0)
+ */
+ public double getSupportLowerBound() {
+ return 0;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * <p>The upper bound of the support is always 1 no matter the parameters.
+ *
+ * @return upper bound of the support (always 1)
+ */
+ public double getSupportUpperBound() {
+ return 1;
+ }
+
+ /** {@inheritDoc} */
+ public boolean isSupportLowerBoundInclusive() {
+ return false;
+ }
+
+ /** {@inheritDoc} */
+ public boolean isSupportUpperBoundInclusive() {
+ return false;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * <p>The support of this distribution is connected.
+ *
+ * @return {@code true}
+ */
+ public boolean isSupportConnected() {
+ return true;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * <p>Sampling is performed using Cheng algorithms:
+ *
+ * <p>R. C. H. Cheng, "Generating beta variates with nonintegral shape parameters.".
+ * Communications of the ACM, 21, 317–322, 1978.
+ */
+ @Override
+ public double sample() {
+ return ChengBetaSampler.sample(random, alpha, beta);
+ }
+
+ /**
+ * Utility class implementing Cheng's algorithms for beta distribution sampling.
+ *
+ * <p>R. C. H. Cheng, "Generating beta variates with nonintegral shape parameters.".
+ * Communications of the ACM, 21, 317–322, 1978.
+ *
+ * @since 3.6
+ */
+ private static final class ChengBetaSampler {
+
+ /**
+ * Returns one sample using Cheng's sampling algorithm.
+ *
+ * @param random random generator to use
+ * @param alpha distribution first shape parameter
+ * @param beta distribution second shape parameter
+ * @return sampled value
+ */
+ static double sample(RandomGenerator random, final double alpha, final double beta) {
+ final double a = FastMath.min(alpha, beta);
+ final double b = FastMath.max(alpha, beta);
+
+ if (a > 1) {
+ return algorithmBB(random, alpha, a, b);
+ } else {
+ return algorithmBC(random, alpha, b, a);
+ }
+ }
+
+ /**
+ * Returns one sample using Cheng's BB algorithm, when both &alpha; and &beta; are greater
+ * than 1.
+ *
+ * @param random random generator to use
+ * @param a0 distribution first shape parameter (&alpha;)
+ * @param a min(&alpha;, &beta;) where &alpha;, &beta; are the two distribution shape
+ * parameters
+ * @param b max(&alpha;, &beta;) where &alpha;, &beta; are the two distribution shape
+ * parameters
+ * @return sampled value
+ */
+ private static double algorithmBB(
+ RandomGenerator random, final double a0, final double a, final double b) {
+ final double alpha = a + b;
+ final double beta = FastMath.sqrt((alpha - 2.) / (2. * a * b - alpha));
+ final double gamma = a + 1. / beta;
+
+ double r;
+ double w;
+ double t;
+ do {
+ final double u1 = random.nextDouble();
+ final double u2 = random.nextDouble();
+ final double v = beta * (FastMath.log(u1) - FastMath.log1p(-u1));
+ w = a * FastMath.exp(v);
+ final double z = u1 * u1 * u2;
+ r = gamma * v - 1.3862944;
+ final double s = a + r - w;
+ if (s + 2.609438 >= 5 * z) {
+ break;
+ }
+
+ t = FastMath.log(z);
+ if (s >= t) {
+ break;
+ }
+ } while (r + alpha * (FastMath.log(alpha) - FastMath.log(b + w)) < t);
+
+ w = FastMath.min(w, Double.MAX_VALUE);
+ return Precision.equals(a, a0) ? w / (b + w) : b / (b + w);
+ }
+
+ /**
+ * Returns one sample using Cheng's BC algorithm, when at least one of &alpha; and &beta; is
+ * smaller than 1.
+ *
+ * @param random random generator to use
+ * @param a0 distribution first shape parameter (&alpha;)
+ * @param a max(&alpha;, &beta;) where &alpha;, &beta; are the two distribution shape
+ * parameters
+ * @param b min(&alpha;, &beta;) where &alpha;, &beta; are the two distribution shape
+ * parameters
+ * @return sampled value
+ */
+ private static double algorithmBC(
+ RandomGenerator random, final double a0, final double a, final double b) {
+ final double alpha = a + b;
+ final double beta = 1. / b;
+ final double delta = 1. + a - b;
+ final double k1 = delta * (0.0138889 + 0.0416667 * b) / (a * beta - 0.777778);
+ final double k2 = 0.25 + (0.5 + 0.25 / delta) * b;
+
+ double w;
+ for (; ; ) {
+ final double u1 = random.nextDouble();
+ final double u2 = random.nextDouble();
+ final double y = u1 * u2;
+ final double z = u1 * y;
+ if (u1 < 0.5) {
+ if (0.25 * u2 + z - y >= k1) {
+ continue;
+ }
+ } else {
+ if (z <= 0.25) {
+ final double v = beta * (FastMath.log(u1) - FastMath.log1p(-u1));
+ w = a * FastMath.exp(v);
+ break;
+ }
+
+ if (z >= k2) {
+ continue;
+ }
+ }
+
+ final double v = beta * (FastMath.log(u1) - FastMath.log1p(-u1));
+ w = a * FastMath.exp(v);
+ if (alpha * (FastMath.log(alpha) - FastMath.log(b + w) + v) - 1.3862944
+ >= FastMath.log(z)) {
+ break;
+ }
+ }
+
+ w = FastMath.min(w, Double.MAX_VALUE);
+ return Precision.equals(a, a0) ? w / (b + w) : b / (b + w);
+ }
+ }
+}
diff --git a/src/main/java/org/apache/commons/math3/distribution/BinomialDistribution.java b/src/main/java/org/apache/commons/math3/distribution/BinomialDistribution.java
new file mode 100644
index 0000000..611666a
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/distribution/BinomialDistribution.java
@@ -0,0 +1,195 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math3.distribution;
+
+import org.apache.commons.math3.exception.NotPositiveException;
+import org.apache.commons.math3.exception.OutOfRangeException;
+import org.apache.commons.math3.exception.util.LocalizedFormats;
+import org.apache.commons.math3.random.RandomGenerator;
+import org.apache.commons.math3.random.Well19937c;
+import org.apache.commons.math3.special.Beta;
+import org.apache.commons.math3.util.FastMath;
+
+/**
+ * Implementation of the binomial distribution.
+ *
+ * @see <a href="http://en.wikipedia.org/wiki/Binomial_distribution">Binomial distribution
+ * (Wikipedia)</a>
+ * @see <a href="http://mathworld.wolfram.com/BinomialDistribution.html">Binomial Distribution
+ * (MathWorld)</a>
+ */
+public class BinomialDistribution extends AbstractIntegerDistribution {
+ /** Serializable version identifier. */
+ private static final long serialVersionUID = 6751309484392813623L;
+
+ /** The number of trials. */
+ private final int numberOfTrials;
+
+ /** The probability of success. */
+ private final double probabilityOfSuccess;
+
+ /**
+ * Create a binomial distribution with the given number of trials and probability of success.
+ *
+ * <p><b>Note:</b> this constructor will implicitly create an instance of {@link Well19937c} as
+ * random generator to be used for sampling only (see {@link #sample()} and {@link
+ * #sample(int)}). In case no sampling is needed for the created distribution, it is advised to
+ * pass {@code null} as random generator via the appropriate constructors to avoid the
+ * additional initialisation overhead.
+ *
+ * @param trials Number of trials.
+ * @param p Probability of success.
+ * @throws NotPositiveException if {@code trials < 0}.
+ * @throws OutOfRangeException if {@code p < 0} or {@code p > 1}.
+ */
+ public BinomialDistribution(int trials, double p) {
+ this(new Well19937c(), trials, p);
+ }
+
+ /**
+ * Creates a binomial distribution.
+ *
+ * @param rng Random number generator.
+ * @param trials Number of trials.
+ * @param p Probability of success.
+ * @throws NotPositiveException if {@code trials < 0}.
+ * @throws OutOfRangeException if {@code p < 0} or {@code p > 1}.
+ * @since 3.1
+ */
+ public BinomialDistribution(RandomGenerator rng, int trials, double p) {
+ super(rng);
+
+ if (trials < 0) {
+ throw new NotPositiveException(LocalizedFormats.NUMBER_OF_TRIALS, trials);
+ }
+ if (p < 0 || p > 1) {
+ throw new OutOfRangeException(p, 0, 1);
+ }
+
+ probabilityOfSuccess = p;
+ numberOfTrials = trials;
+ }
+
+ /**
+ * Access the number of trials for this distribution.
+ *
+ * @return the number of trials.
+ */
+ public int getNumberOfTrials() {
+ return numberOfTrials;
+ }
+
+ /**
+ * Access the probability of success for this distribution.
+ *
+ * @return the probability of success.
+ */
+ public double getProbabilityOfSuccess() {
+ return probabilityOfSuccess;
+ }
+
+ /** {@inheritDoc} */
+ public double probability(int x) {
+ final double logProbability = logProbability(x);
+ return logProbability == Double.NEGATIVE_INFINITY ? 0 : FastMath.exp(logProbability);
+ }
+
+ /** {@inheritDoc} * */
+ @Override
+ public double logProbability(int x) {
+ if (numberOfTrials == 0) {
+ return (x == 0) ? 0. : Double.NEGATIVE_INFINITY;
+ }
+ double ret;
+ if (x < 0 || x > numberOfTrials) {
+ ret = Double.NEGATIVE_INFINITY;
+ } else {
+ ret =
+ SaddlePointExpansion.logBinomialProbability(
+ x, numberOfTrials, probabilityOfSuccess, 1.0 - probabilityOfSuccess);
+ }
+ return ret;
+ }
+
+ /** {@inheritDoc} */
+ public double cumulativeProbability(int x) {
+ double ret;
+ if (x < 0) {
+ ret = 0.0;
+ } else if (x >= numberOfTrials) {
+ ret = 1.0;
+ } else {
+ ret = 1.0 - Beta.regularizedBeta(probabilityOfSuccess, x + 1.0, numberOfTrials - x);
+ }
+ return ret;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * <p>For {@code n} trials and probability parameter {@code p}, the mean is {@code n * p}.
+ */
+ public double getNumericalMean() {
+ return numberOfTrials * probabilityOfSuccess;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * <p>For {@code n} trials and probability parameter {@code p}, the variance is {@code n * p *
+ * (1 - p)}.
+ */
+ public double getNumericalVariance() {
+ final double p = probabilityOfSuccess;
+ return numberOfTrials * p * (1 - p);
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * <p>The lower bound of the support is always 0 except for the probability parameter {@code p =
+ * 1}.
+ *
+ * @return lower bound of the support (0 or the number of trials)
+ */
+ public int getSupportLowerBound() {
+ return probabilityOfSuccess < 1.0 ? 0 : numberOfTrials;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * <p>The upper bound of the support is the number of trials except for the probability
+ * parameter {@code p = 0}.
+ *
+ * @return upper bound of the support (number of trials or 0)
+ */
+ public int getSupportUpperBound() {
+ return probabilityOfSuccess > 0.0 ? numberOfTrials : 0;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * <p>The support of this distribution is connected.
+ *
+ * @return {@code true}
+ */
+ public boolean isSupportConnected() {
+ return true;
+ }
+}
diff --git a/src/main/java/org/apache/commons/math3/distribution/CauchyDistribution.java b/src/main/java/org/apache/commons/math3/distribution/CauchyDistribution.java
new file mode 100644
index 0000000..8c235ea
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/distribution/CauchyDistribution.java
@@ -0,0 +1,251 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math3.distribution;
+
+import org.apache.commons.math3.exception.NotStrictlyPositiveException;
+import org.apache.commons.math3.exception.OutOfRangeException;
+import org.apache.commons.math3.exception.util.LocalizedFormats;
+import org.apache.commons.math3.random.RandomGenerator;
+import org.apache.commons.math3.random.Well19937c;
+import org.apache.commons.math3.util.FastMath;
+
+/**
+ * Implementation of the Cauchy distribution.
+ *
+ * @see <a href="http://en.wikipedia.org/wiki/Cauchy_distribution">Cauchy distribution
+ * (Wikipedia)</a>
+ * @see <a href="http://mathworld.wolfram.com/CauchyDistribution.html">Cauchy Distribution
+ * (MathWorld)</a>
+ * @since 1.1 (changed to concrete class in 3.0)
+ */
+public class CauchyDistribution extends AbstractRealDistribution {
+ /**
+ * Default inverse cumulative probability accuracy.
+ *
+ * @since 2.1
+ */
+ public static final double DEFAULT_INVERSE_ABSOLUTE_ACCURACY = 1e-9;
+
+ /** Serializable version identifier */
+ private static final long serialVersionUID = 8589540077390120676L;
+
+ /** The median of this distribution. */
+ private final double median;
+
+ /** The scale of this distribution. */
+ private final double scale;
+
+ /** Inverse cumulative probability accuracy */
+ private final double solverAbsoluteAccuracy;
+
+ /** Creates a Cauchy distribution with the median equal to zero and scale equal to one. */
+ public CauchyDistribution() {
+ this(0, 1);
+ }
+
+ /**
+ * Creates a Cauchy distribution using the given median and scale.
+ *
+ * <p><b>Note:</b> this constructor will implicitly create an instance of {@link Well19937c} as
+ * random generator to be used for sampling only (see {@link #sample()} and {@link
+ * #sample(int)}). In case no sampling is needed for the created distribution, it is advised to
+ * pass {@code null} as random generator via the appropriate constructors to avoid the
+ * additional initialisation overhead.
+ *
+ * @param median Median for this distribution.
+ * @param scale Scale parameter for this distribution.
+ */
+ public CauchyDistribution(double median, double scale) {
+ this(median, scale, DEFAULT_INVERSE_ABSOLUTE_ACCURACY);
+ }
+
+ /**
+ * Creates a Cauchy distribution using the given median and scale.
+ *
+ * <p><b>Note:</b> this constructor will implicitly create an instance of {@link Well19937c} as
+ * random generator to be used for sampling only (see {@link #sample()} and {@link
+ * #sample(int)}). In case no sampling is needed for the created distribution, it is advised to
+ * pass {@code null} as random generator via the appropriate constructors to avoid the
+ * additional initialisation overhead.
+ *
+ * @param median Median for this distribution.
+ * @param scale Scale parameter for this distribution.
+ * @param inverseCumAccuracy Maximum absolute error in inverse cumulative probability estimates
+ * (defaults to {@link #DEFAULT_INVERSE_ABSOLUTE_ACCURACY}).
+ * @throws NotStrictlyPositiveException if {@code scale <= 0}.
+ * @since 2.1
+ */
+ public CauchyDistribution(double median, double scale, double inverseCumAccuracy) {
+ this(new Well19937c(), median, scale, inverseCumAccuracy);
+ }
+
+ /**
+ * Creates a Cauchy distribution.
+ *
+ * @param rng Random number generator.
+ * @param median Median for this distribution.
+ * @param scale Scale parameter for this distribution.
+ * @throws NotStrictlyPositiveException if {@code scale <= 0}.
+ * @since 3.3
+ */
+ public CauchyDistribution(RandomGenerator rng, double median, double scale) {
+ this(rng, median, scale, DEFAULT_INVERSE_ABSOLUTE_ACCURACY);
+ }
+
+ /**
+ * Creates a Cauchy distribution.
+ *
+ * @param rng Random number generator.
+ * @param median Median for this distribution.
+ * @param scale Scale parameter for this distribution.
+ * @param inverseCumAccuracy Maximum absolute error in inverse cumulative probability estimates
+ * (defaults to {@link #DEFAULT_INVERSE_ABSOLUTE_ACCURACY}).
+ * @throws NotStrictlyPositiveException if {@code scale <= 0}.
+ * @since 3.1
+ */
+ public CauchyDistribution(
+ RandomGenerator rng, double median, double scale, double inverseCumAccuracy) {
+ super(rng);
+ if (scale <= 0) {
+ throw new NotStrictlyPositiveException(LocalizedFormats.SCALE, scale);
+ }
+ this.scale = scale;
+ this.median = median;
+ solverAbsoluteAccuracy = inverseCumAccuracy;
+ }
+
+ /** {@inheritDoc} */
+ public double cumulativeProbability(double x) {
+ return 0.5 + (FastMath.atan((x - median) / scale) / FastMath.PI);
+ }
+
+ /**
+ * Access the median.
+ *
+ * @return the median for this distribution.
+ */
+ public double getMedian() {
+ return median;
+ }
+
+ /**
+ * Access the scale parameter.
+ *
+ * @return the scale parameter for this distribution.
+ */
+ public double getScale() {
+ return scale;
+ }
+
+ /** {@inheritDoc} */
+ public double density(double x) {
+ final double dev = x - median;
+ return (1 / FastMath.PI) * (scale / (dev * dev + scale * scale));
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * <p>Returns {@code Double.NEGATIVE_INFINITY} when {@code p == 0} and {@code
+ * Double.POSITIVE_INFINITY} when {@code p == 1}.
+ */
+ @Override
+ public double inverseCumulativeProbability(double p) throws OutOfRangeException {
+ double ret;
+ if (p < 0 || p > 1) {
+ throw new OutOfRangeException(p, 0, 1);
+ } else if (p == 0) {
+ ret = Double.NEGATIVE_INFINITY;
+ } else if (p == 1) {
+ ret = Double.POSITIVE_INFINITY;
+ } else {
+ ret = median + scale * FastMath.tan(FastMath.PI * (p - .5));
+ }
+ return ret;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ protected double getSolverAbsoluteAccuracy() {
+ return solverAbsoluteAccuracy;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * <p>The mean is always undefined no matter the parameters.
+ *
+ * @return mean (always Double.NaN)
+ */
+ public double getNumericalMean() {
+ return Double.NaN;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * <p>The variance is always undefined no matter the parameters.
+ *
+ * @return variance (always Double.NaN)
+ */
+ public double getNumericalVariance() {
+ return Double.NaN;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * <p>The lower bound of the support is always negative infinity no matter the parameters.
+ *
+ * @return lower bound of the support (always Double.NEGATIVE_INFINITY)
+ */
+ public double getSupportLowerBound() {
+ return Double.NEGATIVE_INFINITY;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * <p>The upper bound of the support is always positive infinity no matter the parameters.
+ *
+ * @return upper bound of the support (always Double.POSITIVE_INFINITY)
+ */
+ public double getSupportUpperBound() {
+ return Double.POSITIVE_INFINITY;
+ }
+
+ /** {@inheritDoc} */
+ public boolean isSupportLowerBoundInclusive() {
+ return false;
+ }
+
+ /** {@inheritDoc} */
+ public boolean isSupportUpperBoundInclusive() {
+ return false;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * <p>The support of this distribution is connected.
+ *
+ * @return {@code true}
+ */
+ public boolean isSupportConnected() {
+ return true;
+ }
+}
diff --git a/src/main/java/org/apache/commons/math3/distribution/ChiSquaredDistribution.java b/src/main/java/org/apache/commons/math3/distribution/ChiSquaredDistribution.java
new file mode 100644
index 0000000..06af167
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/distribution/ChiSquaredDistribution.java
@@ -0,0 +1,196 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math3.distribution;
+
+import org.apache.commons.math3.random.RandomGenerator;
+import org.apache.commons.math3.random.Well19937c;
+
+/**
+ * Implementation of the chi-squared distribution.
+ *
+ * @see <a href="http://en.wikipedia.org/wiki/Chi-squared_distribution">Chi-squared distribution
+ * (Wikipedia)</a>
+ * @see <a href="http://mathworld.wolfram.com/Chi-SquaredDistribution.html">Chi-squared Distribution
+ * (MathWorld)</a>
+ */
+public class ChiSquaredDistribution extends AbstractRealDistribution {
+ /**
+ * Default inverse cumulative probability accuracy
+ *
+ * @since 2.1
+ */
+ public static final double DEFAULT_INVERSE_ABSOLUTE_ACCURACY = 1e-9;
+
+ /** Serializable version identifier */
+ private static final long serialVersionUID = -8352658048349159782L;
+
+ /** Internal Gamma distribution. */
+ private final GammaDistribution gamma;
+
+ /** Inverse cumulative probability accuracy */
+ private final double solverAbsoluteAccuracy;
+
+ /**
+ * Create a Chi-Squared distribution with the given degrees of freedom.
+ *
+ * @param degreesOfFreedom Degrees of freedom.
+ */
+ public ChiSquaredDistribution(double degreesOfFreedom) {
+ this(degreesOfFreedom, DEFAULT_INVERSE_ABSOLUTE_ACCURACY);
+ }
+
+ /**
+ * Create a Chi-Squared distribution with the given degrees of freedom and inverse cumulative
+ * probability accuracy.
+ *
+ * <p><b>Note:</b> this constructor will implicitly create an instance of {@link Well19937c} as
+ * random generator to be used for sampling only (see {@link #sample()} and {@link
+ * #sample(int)}). In case no sampling is needed for the created distribution, it is advised to
+ * pass {@code null} as random generator via the appropriate constructors to avoid the
+ * additional initialisation overhead.
+ *
+ * @param degreesOfFreedom Degrees of freedom.
+ * @param inverseCumAccuracy the maximum absolute error in inverse cumulative probability
+ * estimates (defaults to {@link #DEFAULT_INVERSE_ABSOLUTE_ACCURACY}).
+ * @since 2.1
+ */
+ public ChiSquaredDistribution(double degreesOfFreedom, double inverseCumAccuracy) {
+ this(new Well19937c(), degreesOfFreedom, inverseCumAccuracy);
+ }
+
+ /**
+ * Create a Chi-Squared distribution with the given degrees of freedom.
+ *
+ * @param rng Random number generator.
+ * @param degreesOfFreedom Degrees of freedom.
+ * @since 3.3
+ */
+ public ChiSquaredDistribution(RandomGenerator rng, double degreesOfFreedom) {
+ this(rng, degreesOfFreedom, DEFAULT_INVERSE_ABSOLUTE_ACCURACY);
+ }
+
+ /**
+ * Create a Chi-Squared distribution with the given degrees of freedom and inverse cumulative
+ * probability accuracy.
+ *
+ * @param rng Random number generator.
+ * @param degreesOfFreedom Degrees of freedom.
+ * @param inverseCumAccuracy the maximum absolute error in inverse cumulative probability
+ * estimates (defaults to {@link #DEFAULT_INVERSE_ABSOLUTE_ACCURACY}).
+ * @since 3.1
+ */
+ public ChiSquaredDistribution(
+ RandomGenerator rng, double degreesOfFreedom, double inverseCumAccuracy) {
+ super(rng);
+
+ gamma = new GammaDistribution(degreesOfFreedom / 2, 2);
+ solverAbsoluteAccuracy = inverseCumAccuracy;
+ }
+
+ /**
+ * Access the number of degrees of freedom.
+ *
+ * @return the degrees of freedom.
+ */
+ public double getDegreesOfFreedom() {
+ return gamma.getShape() * 2.0;
+ }
+
+ /** {@inheritDoc} */
+ public double density(double x) {
+ return gamma.density(x);
+ }
+
+ /** {@inheritDoc} * */
+ @Override
+ public double logDensity(double x) {
+ return gamma.logDensity(x);
+ }
+
+ /** {@inheritDoc} */
+ public double cumulativeProbability(double x) {
+ return gamma.cumulativeProbability(x);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ protected double getSolverAbsoluteAccuracy() {
+ return solverAbsoluteAccuracy;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * <p>For {@code k} degrees of freedom, the mean is {@code k}.
+ */
+ public double getNumericalMean() {
+ return getDegreesOfFreedom();
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @return {@code 2 * k}, where {@code k} is the number of degrees of freedom.
+ */
+ public double getNumericalVariance() {
+ return 2 * getDegreesOfFreedom();
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * <p>The lower bound of the support is always 0 no matter the degrees of freedom.
+ *
+ * @return zero.
+ */
+ public double getSupportLowerBound() {
+ return 0;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * <p>The upper bound of the support is always positive infinity no matter the degrees of
+ * freedom.
+ *
+ * @return {@code Double.POSITIVE_INFINITY}.
+ */
+ public double getSupportUpperBound() {
+ return Double.POSITIVE_INFINITY;
+ }
+
+ /** {@inheritDoc} */
+ public boolean isSupportLowerBoundInclusive() {
+ return true;
+ }
+
+ /** {@inheritDoc} */
+ public boolean isSupportUpperBoundInclusive() {
+ return false;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * <p>The support of this distribution is connected.
+ *
+ * @return {@code true}
+ */
+ public boolean isSupportConnected() {
+ return true;
+ }
+}
diff --git a/src/main/java/org/apache/commons/math3/distribution/ConstantRealDistribution.java b/src/main/java/org/apache/commons/math3/distribution/ConstantRealDistribution.java
new file mode 100644
index 0000000..93ba255
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/distribution/ConstantRealDistribution.java
@@ -0,0 +1,112 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math3.distribution;
+
+import org.apache.commons.math3.exception.OutOfRangeException;
+
+/**
+ * Implementation of the constant real distribution.
+ *
+ * @since 3.4
+ */
+public class ConstantRealDistribution extends AbstractRealDistribution {
+
+ /** Serialization ID */
+ private static final long serialVersionUID = -4157745166772046273L;
+
+ /** Constant value of the distribution */
+ private final double value;
+
+ /**
+ * Create a constant real distribution with the given value.
+ *
+ * @param value the constant value of this distribution
+ */
+ public ConstantRealDistribution(double value) {
+ super(null); // Avoid creating RandomGenerator
+ this.value = value;
+ }
+
+ /** {@inheritDoc} */
+ public double density(double x) {
+ return x == value ? 1 : 0;
+ }
+
+ /** {@inheritDoc} */
+ public double cumulativeProbability(double x) {
+ return x < value ? 0 : 1;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public double inverseCumulativeProbability(final double p) throws OutOfRangeException {
+ if (p < 0.0 || p > 1.0) {
+ throw new OutOfRangeException(p, 0, 1);
+ }
+ return value;
+ }
+
+ /** {@inheritDoc} */
+ public double getNumericalMean() {
+ return value;
+ }
+
+ /** {@inheritDoc} */
+ public double getNumericalVariance() {
+ return 0;
+ }
+
+ /** {@inheritDoc} */
+ public double getSupportLowerBound() {
+ return value;
+ }
+
+ /** {@inheritDoc} */
+ public double getSupportUpperBound() {
+ return value;
+ }
+
+ /** {@inheritDoc} */
+ public boolean isSupportLowerBoundInclusive() {
+ return true;
+ }
+
+ /** {@inheritDoc} */
+ public boolean isSupportUpperBoundInclusive() {
+ return true;
+ }
+
+ /** {@inheritDoc} */
+ public boolean isSupportConnected() {
+ return true;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public double sample() {
+ return value;
+ }
+
+ /**
+ * Override with no-op (there is no generator).
+ *
+ * @param seed (ignored)
+ */
+ @Override
+ public void reseedRandomGenerator(long seed) {}
+}
diff --git a/src/main/java/org/apache/commons/math3/distribution/EnumeratedDistribution.java b/src/main/java/org/apache/commons/math3/distribution/EnumeratedDistribution.java
new file mode 100644
index 0000000..991a9f8
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/distribution/EnumeratedDistribution.java
@@ -0,0 +1,283 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math3.distribution;
+
+import org.apache.commons.math3.exception.MathArithmeticException;
+import org.apache.commons.math3.exception.NotANumberException;
+import org.apache.commons.math3.exception.NotFiniteNumberException;
+import org.apache.commons.math3.exception.NotPositiveException;
+import org.apache.commons.math3.exception.NotStrictlyPositiveException;
+import org.apache.commons.math3.exception.NullArgumentException;
+import org.apache.commons.math3.exception.util.LocalizedFormats;
+import org.apache.commons.math3.random.RandomGenerator;
+import org.apache.commons.math3.random.Well19937c;
+import org.apache.commons.math3.util.MathArrays;
+import org.apache.commons.math3.util.Pair;
+
+import java.io.Serializable;
+import java.lang.reflect.Array;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+/**
+ * A generic implementation of a <a
+ * href="http://en.wikipedia.org/wiki/Probability_distribution#Discrete_probability_distribution">
+ * discrete probability distribution (Wikipedia)</a> over a finite sample space, based on an
+ * enumerated list of &lt;value, probability&gt; pairs. Input probabilities must all be
+ * non-negative, but zero values are allowed and their sum does not have to equal one. Constructors
+ * will normalize input probabilities to make them sum to one.
+ *
+ * <p>The list of <value, probability> pairs does not, strictly speaking, have to be a function and
+ * it can contain null values. The pmf created by the constructor will combine probabilities of
+ * equal values and will treat null values as equal. For example, if the list of pairs &lt;"dog",
+ * 0.2&gt;, &lt;null, 0.1&gt;, &lt;"pig", 0.2&gt;, &lt;"dog", 0.1&gt;, &lt;null, 0.4&gt; is provided
+ * to the constructor, the resulting pmf will assign mass of 0.5 to null, 0.3 to "dog" and 0.2 to
+ * null.
+ *
+ * @param <T> type of the elements in the sample space.
+ * @since 3.2
+ */
+public class EnumeratedDistribution<T> implements Serializable {
+
+ /** Serializable UID. */
+ private static final long serialVersionUID = 20123308L;
+
+ /** RNG instance used to generate samples from the distribution. */
+ protected final RandomGenerator random;
+
+ /** List of random variable values. */
+ private final List<T> singletons;
+
+ /**
+ * Probabilities of respective random variable values. For i = 0, ..., singletons.size() - 1,
+ * probability[i] is the probability that a random variable following this distribution takes
+ * the value singletons[i].
+ */
+ private final double[] probabilities;
+
+ /** Cumulative probabilities, cached to speed up sampling. */
+ private final double[] cumulativeProbabilities;
+
+ /**
+ * Create an enumerated distribution using the given probability mass function enumeration.
+ *
+ * <p><b>Note:</b> this constructor will implicitly create an instance of {@link Well19937c} as
+ * random generator to be used for sampling only (see {@link #sample()} and {@link
+ * #sample(int)}). In case no sampling is needed for the created distribution, it is advised to
+ * pass {@code null} as random generator via the appropriate constructors to avoid the
+ * additional initialisation overhead.
+ *
+ * @param pmf probability mass function enumerated as a list of <T, probability> pairs.
+ * @throws NotPositiveException if any of the probabilities are negative.
+ * @throws NotFiniteNumberException if any of the probabilities are infinite.
+ * @throws NotANumberException if any of the probabilities are NaN.
+ * @throws MathArithmeticException all of the probabilities are 0.
+ */
+ public EnumeratedDistribution(final List<Pair<T, Double>> pmf)
+ throws NotPositiveException,
+ MathArithmeticException,
+ NotFiniteNumberException,
+ NotANumberException {
+ this(new Well19937c(), pmf);
+ }
+
+ /**
+ * Create an enumerated distribution using the given random number generator and probability
+ * mass function enumeration.
+ *
+ * @param rng random number generator.
+ * @param pmf probability mass function enumerated as a list of <T, probability> pairs.
+ * @throws NotPositiveException if any of the probabilities are negative.
+ * @throws NotFiniteNumberException if any of the probabilities are infinite.
+ * @throws NotANumberException if any of the probabilities are NaN.
+ * @throws MathArithmeticException all of the probabilities are 0.
+ */
+ public EnumeratedDistribution(final RandomGenerator rng, final List<Pair<T, Double>> pmf)
+ throws NotPositiveException,
+ MathArithmeticException,
+ NotFiniteNumberException,
+ NotANumberException {
+ random = rng;
+
+ singletons = new ArrayList<T>(pmf.size());
+ final double[] probs = new double[pmf.size()];
+
+ for (int i = 0; i < pmf.size(); i++) {
+ final Pair<T, Double> sample = pmf.get(i);
+ singletons.add(sample.getKey());
+ final double p = sample.getValue();
+ if (p < 0) {
+ throw new NotPositiveException(sample.getValue());
+ }
+ if (Double.isInfinite(p)) {
+ throw new NotFiniteNumberException(p);
+ }
+ if (Double.isNaN(p)) {
+ throw new NotANumberException();
+ }
+ probs[i] = p;
+ }
+
+ probabilities = MathArrays.normalizeArray(probs, 1.0);
+
+ cumulativeProbabilities = new double[probabilities.length];
+ double sum = 0;
+ for (int i = 0; i < probabilities.length; i++) {
+ sum += probabilities[i];
+ cumulativeProbabilities[i] = sum;
+ }
+ }
+
+ /**
+ * Reseed the random generator used to generate samples.
+ *
+ * @param seed the new seed
+ */
+ public void reseedRandomGenerator(long seed) {
+ random.setSeed(seed);
+ }
+
+ /**
+ * For a random variable {@code X} whose values are distributed according to this distribution,
+ * this method returns {@code P(X = x)}. In other words, this method represents the probability
+ * mass function (PMF) for the distribution.
+ *
+ * <p>Note that if {@code x1} and {@code x2} satisfy {@code x1.equals(x2)}, or both are null,
+ * then {@code probability(x1) = probability(x2)}.
+ *
+ * @param x the point at which the PMF is evaluated
+ * @return the value of the probability mass function at {@code x}
+ */
+ double probability(final T x) {
+ double probability = 0;
+
+ for (int i = 0; i < probabilities.length; i++) {
+ if ((x == null && singletons.get(i) == null)
+ || (x != null && x.equals(singletons.get(i)))) {
+ probability += probabilities[i];
+ }
+ }
+
+ return probability;
+ }
+
+ /**
+ * Return the probability mass function as a list of <value, probability> pairs.
+ *
+ * <p>Note that if duplicate and / or null values were provided to the constructor when creating
+ * this EnumeratedDistribution, the returned list will contain these values. If duplicates
+ * values exist, what is returned will not represent a pmf (i.e., it is up to the caller to
+ * consolidate duplicate mass points).
+ *
+ * @return the probability mass function.
+ */
+ public List<Pair<T, Double>> getPmf() {
+ final List<Pair<T, Double>> samples = new ArrayList<Pair<T, Double>>(probabilities.length);
+
+ for (int i = 0; i < probabilities.length; i++) {
+ samples.add(new Pair<T, Double>(singletons.get(i), probabilities[i]));
+ }
+
+ return samples;
+ }
+
+ /**
+ * Generate a random value sampled from this distribution.
+ *
+ * @return a random value.
+ */
+ public T sample() {
+ final double randomValue = random.nextDouble();
+
+ int index = Arrays.binarySearch(cumulativeProbabilities, randomValue);
+ if (index < 0) {
+ index = -index - 1;
+ }
+
+ if (index >= 0
+ && index < probabilities.length
+ && randomValue < cumulativeProbabilities[index]) {
+ return singletons.get(index);
+ }
+
+ /* This should never happen, but it ensures we will return a correct
+ * object in case there is some floating point inequality problem
+ * wrt the cumulative probabilities. */
+ return singletons.get(singletons.size() - 1);
+ }
+
+ /**
+ * Generate a random sample from the distribution.
+ *
+ * @param sampleSize the number of random values to generate.
+ * @return an array representing the random sample.
+ * @throws NotStrictlyPositiveException if {@code sampleSize} is not positive.
+ */
+ public Object[] sample(int sampleSize) throws NotStrictlyPositiveException {
+ if (sampleSize <= 0) {
+ throw new NotStrictlyPositiveException(LocalizedFormats.NUMBER_OF_SAMPLES, sampleSize);
+ }
+
+ final Object[] out = new Object[sampleSize];
+
+ for (int i = 0; i < sampleSize; i++) {
+ out[i] = sample();
+ }
+
+ return out;
+ }
+
+ /**
+ * Generate a random sample from the distribution.
+ *
+ * <p>If the requested samples fit in the specified array, it is returned therein. Otherwise, a
+ * new array is allocated with the runtime type of the specified array and the size of this
+ * collection.
+ *
+ * @param sampleSize the number of random values to generate.
+ * @param array the array to populate.
+ * @return an array representing the random sample.
+ * @throws NotStrictlyPositiveException if {@code sampleSize} is not positive.
+ * @throws NullArgumentException if {@code array} is null
+ */
+ public T[] sample(int sampleSize, final T[] array) throws NotStrictlyPositiveException {
+ if (sampleSize <= 0) {
+ throw new NotStrictlyPositiveException(LocalizedFormats.NUMBER_OF_SAMPLES, sampleSize);
+ }
+
+ if (array == null) {
+ throw new NullArgumentException(LocalizedFormats.INPUT_ARRAY);
+ }
+
+ T[] out;
+ if (array.length < sampleSize) {
+ @SuppressWarnings("unchecked") // safe as both are of type T
+ final T[] unchecked =
+ (T[]) Array.newInstance(array.getClass().getComponentType(), sampleSize);
+ out = unchecked;
+ } else {
+ out = array;
+ }
+
+ for (int i = 0; i < sampleSize; i++) {
+ out[i] = sample();
+ }
+
+ return out;
+ }
+}
diff --git a/src/main/java/org/apache/commons/math3/distribution/EnumeratedIntegerDistribution.java b/src/main/java/org/apache/commons/math3/distribution/EnumeratedIntegerDistribution.java
new file mode 100644
index 0000000..37daf57
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/distribution/EnumeratedIntegerDistribution.java
@@ -0,0 +1,274 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math3.distribution;
+
+import org.apache.commons.math3.exception.DimensionMismatchException;
+import org.apache.commons.math3.exception.MathArithmeticException;
+import org.apache.commons.math3.exception.NotANumberException;
+import org.apache.commons.math3.exception.NotFiniteNumberException;
+import org.apache.commons.math3.exception.NotPositiveException;
+import org.apache.commons.math3.random.RandomGenerator;
+import org.apache.commons.math3.random.Well19937c;
+import org.apache.commons.math3.util.Pair;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+
+/**
+ * Implementation of an integer-valued {@link EnumeratedDistribution}.
+ *
+ * <p>Values with zero-probability are allowed but they do not extend the support.<br>
+ * Duplicate values are allowed. Probabilities of duplicate values are combined when computing
+ * cumulative probabilities and statistics.
+ *
+ * @since 3.2
+ */
+public class EnumeratedIntegerDistribution extends AbstractIntegerDistribution {
+
+ /** Serializable UID. */
+ private static final long serialVersionUID = 20130308L;
+
+ /**
+ * {@link EnumeratedDistribution} instance (using the {@link Integer} wrapper) used to generate
+ * the pmf.
+ */
+ protected final EnumeratedDistribution<Integer> innerDistribution;
+
+ /**
+ * Create a discrete distribution using the given probability mass function definition.
+ *
+ * <p><b>Note:</b> this constructor will implicitly create an instance of {@link Well19937c} as
+ * random generator to be used for sampling only (see {@link #sample()} and {@link
+ * #sample(int)}). In case no sampling is needed for the created distribution, it is advised to
+ * pass {@code null} as random generator via the appropriate constructors to avoid the
+ * additional initialisation overhead.
+ *
+ * @param singletons array of random variable values.
+ * @param probabilities array of probabilities.
+ * @throws DimensionMismatchException if {@code singletons.length != probabilities.length}
+ * @throws NotPositiveException if any of the probabilities are negative.
+ * @throws NotFiniteNumberException if any of the probabilities are infinite.
+ * @throws NotANumberException if any of the probabilities are NaN.
+ * @throws MathArithmeticException all of the probabilities are 0.
+ */
+ public EnumeratedIntegerDistribution(final int[] singletons, final double[] probabilities)
+ throws DimensionMismatchException,
+ NotPositiveException,
+ MathArithmeticException,
+ NotFiniteNumberException,
+ NotANumberException {
+ this(new Well19937c(), singletons, probabilities);
+ }
+
+ /**
+ * Create a discrete distribution using the given random number generator and probability mass
+ * function definition.
+ *
+ * @param rng random number generator.
+ * @param singletons array of random variable values.
+ * @param probabilities array of probabilities.
+ * @throws DimensionMismatchException if {@code singletons.length != probabilities.length}
+ * @throws NotPositiveException if any of the probabilities are negative.
+ * @throws NotFiniteNumberException if any of the probabilities are infinite.
+ * @throws NotANumberException if any of the probabilities are NaN.
+ * @throws MathArithmeticException all of the probabilities are 0.
+ */
+ public EnumeratedIntegerDistribution(
+ final RandomGenerator rng, final int[] singletons, final double[] probabilities)
+ throws DimensionMismatchException,
+ NotPositiveException,
+ MathArithmeticException,
+ NotFiniteNumberException,
+ NotANumberException {
+ super(rng);
+ innerDistribution =
+ new EnumeratedDistribution<Integer>(
+ rng, createDistribution(singletons, probabilities));
+ }
+
+ /**
+ * Create a discrete integer-valued distribution from the input data. Values are assigned mass
+ * based on their frequency.
+ *
+ * @param rng random number generator used for sampling
+ * @param data input dataset
+ * @since 3.6
+ */
+ public EnumeratedIntegerDistribution(final RandomGenerator rng, final int[] data) {
+ super(rng);
+ final Map<Integer, Integer> dataMap = new HashMap<Integer, Integer>();
+ for (int value : data) {
+ Integer count = dataMap.get(value);
+ if (count == null) {
+ count = 0;
+ }
+ dataMap.put(value, ++count);
+ }
+ final int massPoints = dataMap.size();
+ final double denom = data.length;
+ final int[] values = new int[massPoints];
+ final double[] probabilities = new double[massPoints];
+ int index = 0;
+ for (Entry<Integer, Integer> entry : dataMap.entrySet()) {
+ values[index] = entry.getKey();
+ probabilities[index] = entry.getValue().intValue() / denom;
+ index++;
+ }
+ innerDistribution =
+ new EnumeratedDistribution<Integer>(rng, createDistribution(values, probabilities));
+ }
+
+ /**
+ * Create a discrete integer-valued distribution from the input data. Values are assigned mass
+ * based on their frequency. For example, [0,1,1,2] as input creates a distribution with values
+ * 0, 1 and 2 having probability masses 0.25, 0.5 and 0.25 respectively,
+ *
+ * @param data input dataset
+ * @since 3.6
+ */
+ public EnumeratedIntegerDistribution(final int[] data) {
+ this(new Well19937c(), data);
+ }
+
+ /**
+ * Create the list of Pairs representing the distribution from singletons and probabilities.
+ *
+ * @param singletons values
+ * @param probabilities probabilities
+ * @return list of value/probability pairs
+ */
+ private static List<Pair<Integer, Double>> createDistribution(
+ int[] singletons, double[] probabilities) {
+ if (singletons.length != probabilities.length) {
+ throw new DimensionMismatchException(probabilities.length, singletons.length);
+ }
+
+ final List<Pair<Integer, Double>> samples =
+ new ArrayList<Pair<Integer, Double>>(singletons.length);
+
+ for (int i = 0; i < singletons.length; i++) {
+ samples.add(new Pair<Integer, Double>(singletons[i], probabilities[i]));
+ }
+ return samples;
+ }
+
+ /** {@inheritDoc} */
+ public double probability(final int x) {
+ return innerDistribution.probability(x);
+ }
+
+ /** {@inheritDoc} */
+ public double cumulativeProbability(final int x) {
+ double probability = 0;
+
+ for (final Pair<Integer, Double> sample : innerDistribution.getPmf()) {
+ if (sample.getKey() <= x) {
+ probability += sample.getValue();
+ }
+ }
+
+ return probability;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @return {@code sum(singletons[i] * probabilities[i])}
+ */
+ public double getNumericalMean() {
+ double mean = 0;
+
+ for (final Pair<Integer, Double> sample : innerDistribution.getPmf()) {
+ mean += sample.getValue() * sample.getKey();
+ }
+
+ return mean;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @return {@code sum((singletons[i] - mean) ^ 2 * probabilities[i])}
+ */
+ public double getNumericalVariance() {
+ double mean = 0;
+ double meanOfSquares = 0;
+
+ for (final Pair<Integer, Double> sample : innerDistribution.getPmf()) {
+ mean += sample.getValue() * sample.getKey();
+ meanOfSquares += sample.getValue() * sample.getKey() * sample.getKey();
+ }
+
+ return meanOfSquares - mean * mean;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * <p>Returns the lowest value with non-zero probability.
+ *
+ * @return the lowest value with non-zero probability.
+ */
+ public int getSupportLowerBound() {
+ int min = Integer.MAX_VALUE;
+ for (final Pair<Integer, Double> sample : innerDistribution.getPmf()) {
+ if (sample.getKey() < min && sample.getValue() > 0) {
+ min = sample.getKey();
+ }
+ }
+
+ return min;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * <p>Returns the highest value with non-zero probability.
+ *
+ * @return the highest value with non-zero probability.
+ */
+ public int getSupportUpperBound() {
+ int max = Integer.MIN_VALUE;
+ for (final Pair<Integer, Double> sample : innerDistribution.getPmf()) {
+ if (sample.getKey() > max && sample.getValue() > 0) {
+ max = sample.getKey();
+ }
+ }
+
+ return max;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * <p>The support of this distribution is connected.
+ *
+ * @return {@code true}
+ */
+ public boolean isSupportConnected() {
+ return true;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public int sample() {
+ return innerDistribution.sample();
+ }
+}
diff --git a/src/main/java/org/apache/commons/math3/distribution/EnumeratedRealDistribution.java b/src/main/java/org/apache/commons/math3/distribution/EnumeratedRealDistribution.java
new file mode 100644
index 0000000..2dd35ec
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/distribution/EnumeratedRealDistribution.java
@@ -0,0 +1,336 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math3.distribution;
+
+import org.apache.commons.math3.exception.DimensionMismatchException;
+import org.apache.commons.math3.exception.MathArithmeticException;
+import org.apache.commons.math3.exception.NotANumberException;
+import org.apache.commons.math3.exception.NotFiniteNumberException;
+import org.apache.commons.math3.exception.NotPositiveException;
+import org.apache.commons.math3.exception.OutOfRangeException;
+import org.apache.commons.math3.random.RandomGenerator;
+import org.apache.commons.math3.random.Well19937c;
+import org.apache.commons.math3.util.Pair;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+
+/**
+ * Implementation of a real-valued {@link EnumeratedDistribution}.
+ *
+ * <p>Values with zero-probability are allowed but they do not extend the support.<br>
+ * Duplicate values are allowed. Probabilities of duplicate values are combined when computing
+ * cumulative probabilities and statistics.
+ *
+ * @since 3.2
+ */
+public class EnumeratedRealDistribution extends AbstractRealDistribution {
+
+ /** Serializable UID. */
+ private static final long serialVersionUID = 20130308L;
+
+ /**
+ * {@link EnumeratedDistribution} (using the {@link Double} wrapper) used to generate the pmf.
+ */
+ protected final EnumeratedDistribution<Double> innerDistribution;
+
+ /**
+ * Create a discrete real-valued distribution using the given probability mass function
+ * enumeration.
+ *
+ * <p><b>Note:</b> this constructor will implicitly create an instance of {@link Well19937c} as
+ * random generator to be used for sampling only (see {@link #sample()} and {@link
+ * #sample(int)}). In case no sampling is needed for the created distribution, it is advised to
+ * pass {@code null} as random generator via the appropriate constructors to avoid the
+ * additional initialisation overhead.
+ *
+ * @param singletons array of random variable values.
+ * @param probabilities array of probabilities.
+ * @throws DimensionMismatchException if {@code singletons.length != probabilities.length}
+ * @throws NotPositiveException if any of the probabilities are negative.
+ * @throws NotFiniteNumberException if any of the probabilities are infinite.
+ * @throws NotANumberException if any of the probabilities are NaN.
+ * @throws MathArithmeticException all of the probabilities are 0.
+ */
+ public EnumeratedRealDistribution(final double[] singletons, final double[] probabilities)
+ throws DimensionMismatchException,
+ NotPositiveException,
+ MathArithmeticException,
+ NotFiniteNumberException,
+ NotANumberException {
+ this(new Well19937c(), singletons, probabilities);
+ }
+
+ /**
+ * Create a discrete real-valued distribution using the given random number generator and
+ * probability mass function enumeration.
+ *
+ * @param rng random number generator.
+ * @param singletons array of random variable values.
+ * @param probabilities array of probabilities.
+ * @throws DimensionMismatchException if {@code singletons.length != probabilities.length}
+ * @throws NotPositiveException if any of the probabilities are negative.
+ * @throws NotFiniteNumberException if any of the probabilities are infinite.
+ * @throws NotANumberException if any of the probabilities are NaN.
+ * @throws MathArithmeticException all of the probabilities are 0.
+ */
+ public EnumeratedRealDistribution(
+ final RandomGenerator rng, final double[] singletons, final double[] probabilities)
+ throws DimensionMismatchException,
+ NotPositiveException,
+ MathArithmeticException,
+ NotFiniteNumberException,
+ NotANumberException {
+ super(rng);
+
+ innerDistribution =
+ new EnumeratedDistribution<Double>(
+ rng, createDistribution(singletons, probabilities));
+ }
+
+ /**
+ * Create a discrete real-valued distribution from the input data. Values are assigned mass
+ * based on their frequency.
+ *
+ * @param rng random number generator used for sampling
+ * @param data input dataset
+ * @since 3.6
+ */
+ public EnumeratedRealDistribution(final RandomGenerator rng, final double[] data) {
+ super(rng);
+ final Map<Double, Integer> dataMap = new HashMap<Double, Integer>();
+ for (double value : data) {
+ Integer count = dataMap.get(value);
+ if (count == null) {
+ count = 0;
+ }
+ dataMap.put(value, ++count);
+ }
+ final int massPoints = dataMap.size();
+ final double denom = data.length;
+ final double[] values = new double[massPoints];
+ final double[] probabilities = new double[massPoints];
+ int index = 0;
+ for (Entry<Double, Integer> entry : dataMap.entrySet()) {
+ values[index] = entry.getKey();
+ probabilities[index] = entry.getValue().intValue() / denom;
+ index++;
+ }
+ innerDistribution =
+ new EnumeratedDistribution<Double>(rng, createDistribution(values, probabilities));
+ }
+
+ /**
+ * Create a discrete real-valued distribution from the input data. Values are assigned mass
+ * based on their frequency. For example, [0,1,1,2] as input creates a distribution with values
+ * 0, 1 and 2 having probability masses 0.25, 0.5 and 0.25 respectively,
+ *
+ * @param data input dataset
+ * @since 3.6
+ */
+ public EnumeratedRealDistribution(final double[] data) {
+ this(new Well19937c(), data);
+ }
+
+ /**
+ * Create the list of Pairs representing the distribution from singletons and probabilities.
+ *
+ * @param singletons values
+ * @param probabilities probabilities
+ * @return list of value/probability pairs
+ */
+ private static List<Pair<Double, Double>> createDistribution(
+ double[] singletons, double[] probabilities) {
+ if (singletons.length != probabilities.length) {
+ throw new DimensionMismatchException(probabilities.length, singletons.length);
+ }
+
+ final List<Pair<Double, Double>> samples =
+ new ArrayList<Pair<Double, Double>>(singletons.length);
+
+ for (int i = 0; i < singletons.length; i++) {
+ samples.add(new Pair<Double, Double>(singletons[i], probabilities[i]));
+ }
+ return samples;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public double probability(final double x) {
+ return innerDistribution.probability(x);
+ }
+
+ /**
+ * For a random variable {@code X} whose values are distributed according to this distribution,
+ * this method returns {@code P(X = x)}. In other words, this method represents the probability
+ * mass function (PMF) for the distribution.
+ *
+ * @param x the point at which the PMF is evaluated
+ * @return the value of the probability mass function at point {@code x}
+ */
+ public double density(final double x) {
+ return probability(x);
+ }
+
+ /** {@inheritDoc} */
+ public double cumulativeProbability(final double x) {
+ double probability = 0;
+
+ for (final Pair<Double, Double> sample : innerDistribution.getPmf()) {
+ if (sample.getKey() <= x) {
+ probability += sample.getValue();
+ }
+ }
+
+ return probability;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public double inverseCumulativeProbability(final double p) throws OutOfRangeException {
+ if (p < 0.0 || p > 1.0) {
+ throw new OutOfRangeException(p, 0, 1);
+ }
+
+ double probability = 0;
+ double x = getSupportLowerBound();
+ for (final Pair<Double, Double> sample : innerDistribution.getPmf()) {
+ if (sample.getValue() == 0.0) {
+ continue;
+ }
+
+ probability += sample.getValue();
+ x = sample.getKey();
+
+ if (probability >= p) {
+ break;
+ }
+ }
+
+ return x;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @return {@code sum(singletons[i] * probabilities[i])}
+ */
+ public double getNumericalMean() {
+ double mean = 0;
+
+ for (final Pair<Double, Double> sample : innerDistribution.getPmf()) {
+ mean += sample.getValue() * sample.getKey();
+ }
+
+ return mean;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @return {@code sum((singletons[i] - mean) ^ 2 * probabilities[i])}
+ */
+ public double getNumericalVariance() {
+ double mean = 0;
+ double meanOfSquares = 0;
+
+ for (final Pair<Double, Double> sample : innerDistribution.getPmf()) {
+ mean += sample.getValue() * sample.getKey();
+ meanOfSquares += sample.getValue() * sample.getKey() * sample.getKey();
+ }
+
+ return meanOfSquares - mean * mean;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * <p>Returns the lowest value with non-zero probability.
+ *
+ * @return the lowest value with non-zero probability.
+ */
+ public double getSupportLowerBound() {
+ double min = Double.POSITIVE_INFINITY;
+ for (final Pair<Double, Double> sample : innerDistribution.getPmf()) {
+ if (sample.getKey() < min && sample.getValue() > 0) {
+ min = sample.getKey();
+ }
+ }
+
+ return min;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * <p>Returns the highest value with non-zero probability.
+ *
+ * @return the highest value with non-zero probability.
+ */
+ public double getSupportUpperBound() {
+ double max = Double.NEGATIVE_INFINITY;
+ for (final Pair<Double, Double> sample : innerDistribution.getPmf()) {
+ if (sample.getKey() > max && sample.getValue() > 0) {
+ max = sample.getKey();
+ }
+ }
+
+ return max;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * <p>The support of this distribution includes the lower bound.
+ *
+ * @return {@code true}
+ */
+ public boolean isSupportLowerBoundInclusive() {
+ return true;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * <p>The support of this distribution includes the upper bound.
+ *
+ * @return {@code true}
+ */
+ public boolean isSupportUpperBoundInclusive() {
+ return true;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * <p>The support of this distribution is connected.
+ *
+ * @return {@code true}
+ */
+ public boolean isSupportConnected() {
+ return true;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public double sample() {
+ return innerDistribution.sample();
+ }
+}
diff --git a/src/main/java/org/apache/commons/math3/distribution/ExponentialDistribution.java b/src/main/java/org/apache/commons/math3/distribution/ExponentialDistribution.java
new file mode 100644
index 0000000..6ca5f1c
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/distribution/ExponentialDistribution.java
@@ -0,0 +1,342 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math3.distribution;
+
+import org.apache.commons.math3.exception.NotStrictlyPositiveException;
+import org.apache.commons.math3.exception.OutOfRangeException;
+import org.apache.commons.math3.exception.util.LocalizedFormats;
+import org.apache.commons.math3.random.RandomGenerator;
+import org.apache.commons.math3.random.Well19937c;
+import org.apache.commons.math3.util.CombinatoricsUtils;
+import org.apache.commons.math3.util.FastMath;
+import org.apache.commons.math3.util.ResizableDoubleArray;
+
+/**
+ * Implementation of the exponential distribution.
+ *
+ * @see <a href="http://en.wikipedia.org/wiki/Exponential_distribution">Exponential distribution
+ * (Wikipedia)</a>
+ * @see <a href="http://mathworld.wolfram.com/ExponentialDistribution.html">Exponential distribution
+ * (MathWorld)</a>
+ */
+public class ExponentialDistribution extends AbstractRealDistribution {
+ /**
+ * Default inverse cumulative probability accuracy.
+ *
+ * @since 2.1
+ */
+ public static final double DEFAULT_INVERSE_ABSOLUTE_ACCURACY = 1e-9;
+
+ /** Serializable version identifier */
+ private static final long serialVersionUID = 2401296428283614780L;
+
+ /**
+ * Used when generating Exponential samples. Table containing the constants q_i = sum_{j=1}^i
+ * (ln 2)^j/j! = ln 2 + (ln 2)^2/2 + ... + (ln 2)^i/i! until the largest representable fraction
+ * below 1 is exceeded.
+ *
+ * <p>Note that 1 = 2 - 1 = exp(ln 2) - 1 = sum_{n=1}^infty (ln 2)^n / n! thus q_i -> 1 as i ->
+ * +inf, so the higher i, the closer to one we get (the series is not alternating).
+ *
+ * <p>By trying, n = 16 in Java is enough to reach 1.0.
+ */
+ private static final double[] EXPONENTIAL_SA_QI;
+
+ /** The mean of this distribution. */
+ private final double mean;
+
+ /** The logarithm of the mean, stored to reduce computing time. * */
+ private final double logMean;
+
+ /** Inverse cumulative probability accuracy. */
+ private final double solverAbsoluteAccuracy;
+
+ /** Initialize tables. */
+ static {
+ /** Filling EXPONENTIAL_SA_QI table. Note that we don't want qi = 0 in the table. */
+ final double LN2 = FastMath.log(2);
+ double qi = 0;
+ int i = 1;
+
+ /**
+ * ArithmeticUtils provides factorials up to 20, so let's use that limit together with
+ * Precision.EPSILON to generate the following code (a priori, we know that there will be 16
+ * elements, but it is better to not hardcode it).
+ */
+ final ResizableDoubleArray ra = new ResizableDoubleArray(20);
+
+ while (qi < 1) {
+ qi += FastMath.pow(LN2, i) / CombinatoricsUtils.factorial(i);
+ ra.addElement(qi);
+ ++i;
+ }
+
+ EXPONENTIAL_SA_QI = ra.getElements();
+ }
+
+ /**
+ * Create an exponential distribution with the given mean.
+ *
+ * <p><b>Note:</b> this constructor will implicitly create an instance of {@link Well19937c} as
+ * random generator to be used for sampling only (see {@link #sample()} and {@link
+ * #sample(int)}). In case no sampling is needed for the created distribution, it is advised to
+ * pass {@code null} as random generator via the appropriate constructors to avoid the
+ * additional initialisation overhead.
+ *
+ * @param mean mean of this distribution.
+ */
+ public ExponentialDistribution(double mean) {
+ this(mean, DEFAULT_INVERSE_ABSOLUTE_ACCURACY);
+ }
+
+ /**
+ * Create an exponential distribution with the given mean.
+ *
+ * <p><b>Note:</b> this constructor will implicitly create an instance of {@link Well19937c} as
+ * random generator to be used for sampling only (see {@link #sample()} and {@link
+ * #sample(int)}). In case no sampling is needed for the created distribution, it is advised to
+ * pass {@code null} as random generator via the appropriate constructors to avoid the
+ * additional initialisation overhead.
+ *
+ * @param mean Mean of this distribution.
+ * @param inverseCumAccuracy Maximum absolute error in inverse cumulative probability estimates
+ * (defaults to {@link #DEFAULT_INVERSE_ABSOLUTE_ACCURACY}).
+ * @throws NotStrictlyPositiveException if {@code mean <= 0}.
+ * @since 2.1
+ */
+ public ExponentialDistribution(double mean, double inverseCumAccuracy) {
+ this(new Well19937c(), mean, inverseCumAccuracy);
+ }
+
+ /**
+ * Creates an exponential distribution.
+ *
+ * @param rng Random number generator.
+ * @param mean Mean of this distribution.
+ * @throws NotStrictlyPositiveException if {@code mean <= 0}.
+ * @since 3.3
+ */
+ public ExponentialDistribution(RandomGenerator rng, double mean)
+ throws NotStrictlyPositiveException {
+ this(rng, mean, DEFAULT_INVERSE_ABSOLUTE_ACCURACY);
+ }
+
+ /**
+ * Creates an exponential distribution.
+ *
+ * @param rng Random number generator.
+ * @param mean Mean of this distribution.
+ * @param inverseCumAccuracy Maximum absolute error in inverse cumulative probability estimates
+ * (defaults to {@link #DEFAULT_INVERSE_ABSOLUTE_ACCURACY}).
+ * @throws NotStrictlyPositiveException if {@code mean <= 0}.
+ * @since 3.1
+ */
+ public ExponentialDistribution(RandomGenerator rng, double mean, double inverseCumAccuracy)
+ throws NotStrictlyPositiveException {
+ super(rng);
+
+ if (mean <= 0) {
+ throw new NotStrictlyPositiveException(LocalizedFormats.MEAN, mean);
+ }
+ this.mean = mean;
+ logMean = FastMath.log(mean);
+ solverAbsoluteAccuracy = inverseCumAccuracy;
+ }
+
+ /**
+ * Access the mean.
+ *
+ * @return the mean.
+ */
+ public double getMean() {
+ return mean;
+ }
+
+ /** {@inheritDoc} */
+ public double density(double x) {
+ final double logDensity = logDensity(x);
+ return logDensity == Double.NEGATIVE_INFINITY ? 0 : FastMath.exp(logDensity);
+ }
+
+ /** {@inheritDoc} * */
+ @Override
+ public double logDensity(double x) {
+ if (x < 0) {
+ return Double.NEGATIVE_INFINITY;
+ }
+ return -x / mean - logMean;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * <p>The implementation of this method is based on:
+ *
+ * <ul>
+ * <li><a href="http://mathworld.wolfram.com/ExponentialDistribution.html">Exponential
+ * Distribution</a>, equation (1).
+ * </ul>
+ */
+ public double cumulativeProbability(double x) {
+ double ret;
+ if (x <= 0.0) {
+ ret = 0.0;
+ } else {
+ ret = 1.0 - FastMath.exp(-x / mean);
+ }
+ return ret;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * <p>Returns {@code 0} when {@code p= = 0} and {@code Double.POSITIVE_INFINITY} when {@code p
+ * == 1}.
+ */
+ @Override
+ public double inverseCumulativeProbability(double p) throws OutOfRangeException {
+ double ret;
+
+ if (p < 0.0 || p > 1.0) {
+ throw new OutOfRangeException(p, 0.0, 1.0);
+ } else if (p == 1.0) {
+ ret = Double.POSITIVE_INFINITY;
+ } else {
+ ret = -mean * FastMath.log(1.0 - p);
+ }
+
+ return ret;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * <p><strong>Algorithm Description</strong>: this implementation uses the <a
+ * href="http://www.jesus.ox.ac.uk/~clifford/a5/chap1/node5.html">Inversion Method</a> to
+ * generate exponentially distributed random values from uniform deviates.
+ *
+ * @return a random value.
+ * @since 2.2
+ */
+ @Override
+ public double sample() {
+ // Step 1:
+ double a = 0;
+ double u = random.nextDouble();
+
+ // Step 2 and 3:
+ while (u < 0.5) {
+ a += EXPONENTIAL_SA_QI[0];
+ u *= 2;
+ }
+
+ // Step 4 (now u >= 0.5):
+ u += u - 1;
+
+ // Step 5:
+ if (u <= EXPONENTIAL_SA_QI[0]) {
+ return mean * (a + u);
+ }
+
+ // Step 6:
+ int i = 0; // Should be 1, be we iterate before it in while using 0
+ double u2 = random.nextDouble();
+ double umin = u2;
+
+ // Step 7 and 8:
+ do {
+ ++i;
+ u2 = random.nextDouble();
+
+ if (u2 < umin) {
+ umin = u2;
+ }
+
+ // Step 8:
+ } while (u > EXPONENTIAL_SA_QI[i]); // Ensured to exit since EXPONENTIAL_SA_QI[MAX] = 1
+
+ return mean * (a + umin * EXPONENTIAL_SA_QI[0]);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ protected double getSolverAbsoluteAccuracy() {
+ return solverAbsoluteAccuracy;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * <p>For mean parameter {@code k}, the mean is {@code k}.
+ */
+ public double getNumericalMean() {
+ return getMean();
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * <p>For mean parameter {@code k}, the variance is {@code k^2}.
+ */
+ public double getNumericalVariance() {
+ final double m = getMean();
+ return m * m;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * <p>The lower bound of the support is always 0 no matter the mean parameter.
+ *
+ * @return lower bound of the support (always 0)
+ */
+ public double getSupportLowerBound() {
+ return 0;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * <p>The upper bound of the support is always positive infinity no matter the mean parameter.
+ *
+ * @return upper bound of the support (always Double.POSITIVE_INFINITY)
+ */
+ public double getSupportUpperBound() {
+ return Double.POSITIVE_INFINITY;
+ }
+
+ /** {@inheritDoc} */
+ public boolean isSupportLowerBoundInclusive() {
+ return true;
+ }
+
+ /** {@inheritDoc} */
+ public boolean isSupportUpperBoundInclusive() {
+ return false;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * <p>The support of this distribution is connected.
+ *
+ * @return {@code true}
+ */
+ public boolean isSupportConnected() {
+ return true;
+ }
+}
diff --git a/src/main/java/org/apache/commons/math3/distribution/FDistribution.java b/src/main/java/org/apache/commons/math3/distribution/FDistribution.java
new file mode 100644
index 0000000..3269f8d
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/distribution/FDistribution.java
@@ -0,0 +1,341 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math3.distribution;
+
+import org.apache.commons.math3.exception.NotStrictlyPositiveException;
+import org.apache.commons.math3.exception.util.LocalizedFormats;
+import org.apache.commons.math3.random.RandomGenerator;
+import org.apache.commons.math3.random.Well19937c;
+import org.apache.commons.math3.special.Beta;
+import org.apache.commons.math3.util.FastMath;
+
+/**
+ * Implementation of the F-distribution.
+ *
+ * @see <a href="http://en.wikipedia.org/wiki/F-distribution">F-distribution (Wikipedia)</a>
+ * @see <a href="http://mathworld.wolfram.com/F-Distribution.html">F-distribution (MathWorld)</a>
+ */
+public class FDistribution extends AbstractRealDistribution {
+ /**
+ * Default inverse cumulative probability accuracy.
+ *
+ * @since 2.1
+ */
+ public static final double DEFAULT_INVERSE_ABSOLUTE_ACCURACY = 1e-9;
+
+ /** Serializable version identifier. */
+ private static final long serialVersionUID = -8516354193418641566L;
+
+ /** The numerator degrees of freedom. */
+ private final double numeratorDegreesOfFreedom;
+
+ /** The numerator degrees of freedom. */
+ private final double denominatorDegreesOfFreedom;
+
+ /** Inverse cumulative probability accuracy. */
+ private final double solverAbsoluteAccuracy;
+
+ /** Cached numerical variance */
+ private double numericalVariance = Double.NaN;
+
+ /** Whether or not the numerical variance has been calculated */
+ private boolean numericalVarianceIsCalculated = false;
+
+ /**
+ * Creates an F distribution using the given degrees of freedom.
+ *
+ * <p><b>Note:</b> this constructor will implicitly create an instance of {@link Well19937c} as
+ * random generator to be used for sampling only (see {@link #sample()} and {@link
+ * #sample(int)}). In case no sampling is needed for the created distribution, it is advised to
+ * pass {@code null} as random generator via the appropriate constructors to avoid the
+ * additional initialisation overhead.
+ *
+ * @param numeratorDegreesOfFreedom Numerator degrees of freedom.
+ * @param denominatorDegreesOfFreedom Denominator degrees of freedom.
+ * @throws NotStrictlyPositiveException if {@code numeratorDegreesOfFreedom <= 0} or {@code
+ * denominatorDegreesOfFreedom <= 0}.
+ */
+ public FDistribution(double numeratorDegreesOfFreedom, double denominatorDegreesOfFreedom)
+ throws NotStrictlyPositiveException {
+ this(
+ numeratorDegreesOfFreedom,
+ denominatorDegreesOfFreedom,
+ DEFAULT_INVERSE_ABSOLUTE_ACCURACY);
+ }
+
+ /**
+ * Creates an F distribution using the given degrees of freedom and inverse cumulative
+ * probability accuracy.
+ *
+ * <p><b>Note:</b> this constructor will implicitly create an instance of {@link Well19937c} as
+ * random generator to be used for sampling only (see {@link #sample()} and {@link
+ * #sample(int)}). In case no sampling is needed for the created distribution, it is advised to
+ * pass {@code null} as random generator via the appropriate constructors to avoid the
+ * additional initialisation overhead.
+ *
+ * @param numeratorDegreesOfFreedom Numerator degrees of freedom.
+ * @param denominatorDegreesOfFreedom Denominator degrees of freedom.
+ * @param inverseCumAccuracy the maximum absolute error in inverse cumulative probability
+ * estimates.
+ * @throws NotStrictlyPositiveException if {@code numeratorDegreesOfFreedom <= 0} or {@code
+ * denominatorDegreesOfFreedom <= 0}.
+ * @since 2.1
+ */
+ public FDistribution(
+ double numeratorDegreesOfFreedom,
+ double denominatorDegreesOfFreedom,
+ double inverseCumAccuracy)
+ throws NotStrictlyPositiveException {
+ this(
+ new Well19937c(),
+ numeratorDegreesOfFreedom,
+ denominatorDegreesOfFreedom,
+ inverseCumAccuracy);
+ }
+
+ /**
+ * Creates an F distribution.
+ *
+ * @param rng Random number generator.
+ * @param numeratorDegreesOfFreedom Numerator degrees of freedom.
+ * @param denominatorDegreesOfFreedom Denominator degrees of freedom.
+ * @throws NotStrictlyPositiveException if {@code numeratorDegreesOfFreedom <= 0} or {@code
+ * denominatorDegreesOfFreedom <= 0}.
+ * @since 3.3
+ */
+ public FDistribution(
+ RandomGenerator rng,
+ double numeratorDegreesOfFreedom,
+ double denominatorDegreesOfFreedom)
+ throws NotStrictlyPositiveException {
+ this(
+ rng,
+ numeratorDegreesOfFreedom,
+ denominatorDegreesOfFreedom,
+ DEFAULT_INVERSE_ABSOLUTE_ACCURACY);
+ }
+
+ /**
+ * Creates an F distribution.
+ *
+ * @param rng Random number generator.
+ * @param numeratorDegreesOfFreedom Numerator degrees of freedom.
+ * @param denominatorDegreesOfFreedom Denominator degrees of freedom.
+ * @param inverseCumAccuracy the maximum absolute error in inverse cumulative probability
+ * estimates.
+ * @throws NotStrictlyPositiveException if {@code numeratorDegreesOfFreedom <= 0} or {@code
+ * denominatorDegreesOfFreedom <= 0}.
+ * @since 3.1
+ */
+ public FDistribution(
+ RandomGenerator rng,
+ double numeratorDegreesOfFreedom,
+ double denominatorDegreesOfFreedom,
+ double inverseCumAccuracy)
+ throws NotStrictlyPositiveException {
+ super(rng);
+
+ if (numeratorDegreesOfFreedom <= 0) {
+ throw new NotStrictlyPositiveException(
+ LocalizedFormats.DEGREES_OF_FREEDOM, numeratorDegreesOfFreedom);
+ }
+ if (denominatorDegreesOfFreedom <= 0) {
+ throw new NotStrictlyPositiveException(
+ LocalizedFormats.DEGREES_OF_FREEDOM, denominatorDegreesOfFreedom);
+ }
+ this.numeratorDegreesOfFreedom = numeratorDegreesOfFreedom;
+ this.denominatorDegreesOfFreedom = denominatorDegreesOfFreedom;
+ solverAbsoluteAccuracy = inverseCumAccuracy;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @since 2.1
+ */
+ public double density(double x) {
+ return FastMath.exp(logDensity(x));
+ }
+
+ /** {@inheritDoc} * */
+ @Override
+ public double logDensity(double x) {
+ final double nhalf = numeratorDegreesOfFreedom / 2;
+ final double mhalf = denominatorDegreesOfFreedom / 2;
+ final double logx = FastMath.log(x);
+ final double logn = FastMath.log(numeratorDegreesOfFreedom);
+ final double logm = FastMath.log(denominatorDegreesOfFreedom);
+ final double lognxm =
+ FastMath.log(numeratorDegreesOfFreedom * x + denominatorDegreesOfFreedom);
+ return nhalf * logn
+ + nhalf * logx
+ - logx
+ + mhalf * logm
+ - nhalf * lognxm
+ - mhalf * lognxm
+ - Beta.logBeta(nhalf, mhalf);
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * <p>The implementation of this method is based on
+ *
+ * <ul>
+ * <li><a href="http://mathworld.wolfram.com/F-Distribution.html">F-Distribution</a>, equation
+ * (4).
+ * </ul>
+ */
+ public double cumulativeProbability(double x) {
+ double ret;
+ if (x <= 0) {
+ ret = 0;
+ } else {
+ double n = numeratorDegreesOfFreedom;
+ double m = denominatorDegreesOfFreedom;
+
+ ret = Beta.regularizedBeta((n * x) / (m + n * x), 0.5 * n, 0.5 * m);
+ }
+ return ret;
+ }
+
+ /**
+ * Access the numerator degrees of freedom.
+ *
+ * @return the numerator degrees of freedom.
+ */
+ public double getNumeratorDegreesOfFreedom() {
+ return numeratorDegreesOfFreedom;
+ }
+
+ /**
+ * Access the denominator degrees of freedom.
+ *
+ * @return the denominator degrees of freedom.
+ */
+ public double getDenominatorDegreesOfFreedom() {
+ return denominatorDegreesOfFreedom;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ protected double getSolverAbsoluteAccuracy() {
+ return solverAbsoluteAccuracy;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * <p>For denominator degrees of freedom parameter {@code b}, the mean is
+ *
+ * <ul>
+ * <li>if {@code b > 2} then {@code b / (b - 2)},
+ * <li>else undefined ({@code Double.NaN}).
+ * </ul>
+ */
+ public double getNumericalMean() {
+ final double denominatorDF = getDenominatorDegreesOfFreedom();
+
+ if (denominatorDF > 2) {
+ return denominatorDF / (denominatorDF - 2);
+ }
+
+ return Double.NaN;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * <p>For numerator degrees of freedom parameter {@code a} and denominator degrees of freedom
+ * parameter {@code b}, the variance is
+ *
+ * <ul>
+ * <li>if {@code b > 4} then {@code [2 * b^2 * (a + b - 2)] / [a * (b - 2)^2 * (b - 4)]},
+ * <li>else undefined ({@code Double.NaN}).
+ * </ul>
+ */
+ public double getNumericalVariance() {
+ if (!numericalVarianceIsCalculated) {
+ numericalVariance = calculateNumericalVariance();
+ numericalVarianceIsCalculated = true;
+ }
+ return numericalVariance;
+ }
+
+ /**
+ * used by {@link #getNumericalVariance()}
+ *
+ * @return the variance of this distribution
+ */
+ protected double calculateNumericalVariance() {
+ final double denominatorDF = getDenominatorDegreesOfFreedom();
+
+ if (denominatorDF > 4) {
+ final double numeratorDF = getNumeratorDegreesOfFreedom();
+ final double denomDFMinusTwo = denominatorDF - 2;
+
+ return (2 * (denominatorDF * denominatorDF) * (numeratorDF + denominatorDF - 2))
+ / ((numeratorDF * (denomDFMinusTwo * denomDFMinusTwo) * (denominatorDF - 4)));
+ }
+
+ return Double.NaN;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * <p>The lower bound of the support is always 0 no matter the parameters.
+ *
+ * @return lower bound of the support (always 0)
+ */
+ public double getSupportLowerBound() {
+ return 0;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * <p>The upper bound of the support is always positive infinity no matter the parameters.
+ *
+ * @return upper bound of the support (always Double.POSITIVE_INFINITY)
+ */
+ public double getSupportUpperBound() {
+ return Double.POSITIVE_INFINITY;
+ }
+
+ /** {@inheritDoc} */
+ public boolean isSupportLowerBoundInclusive() {
+ return false;
+ }
+
+ /** {@inheritDoc} */
+ public boolean isSupportUpperBoundInclusive() {
+ return false;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * <p>The support of this distribution is connected.
+ *
+ * @return {@code true}
+ */
+ public boolean isSupportConnected() {
+ return true;
+ }
+}
diff --git a/src/main/java/org/apache/commons/math3/distribution/GammaDistribution.java b/src/main/java/org/apache/commons/math3/distribution/GammaDistribution.java
new file mode 100644
index 0000000..f062fd2
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/distribution/GammaDistribution.java
@@ -0,0 +1,505 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math3.distribution;
+
+import org.apache.commons.math3.exception.NotStrictlyPositiveException;
+import org.apache.commons.math3.exception.util.LocalizedFormats;
+import org.apache.commons.math3.random.RandomGenerator;
+import org.apache.commons.math3.random.Well19937c;
+import org.apache.commons.math3.special.Gamma;
+import org.apache.commons.math3.util.FastMath;
+
+/**
+ * Implementation of the Gamma distribution.
+ *
+ * @see <a href="http://en.wikipedia.org/wiki/Gamma_distribution">Gamma distribution (Wikipedia)</a>
+ * @see <a href="http://mathworld.wolfram.com/GammaDistribution.html">Gamma distribution
+ * (MathWorld)</a>
+ */
+public class GammaDistribution extends AbstractRealDistribution {
+ /**
+ * Default inverse cumulative probability accuracy.
+ *
+ * @since 2.1
+ */
+ public static final double DEFAULT_INVERSE_ABSOLUTE_ACCURACY = 1e-9;
+
+ /** Serializable version identifier. */
+ private static final long serialVersionUID = 20120524L;
+
+ /** The shape parameter. */
+ private final double shape;
+
+ /** The scale parameter. */
+ private final double scale;
+
+ /**
+ * The constant value of {@code shape + g + 0.5}, where {@code g} is the Lanczos constant {@link
+ * Gamma#LANCZOS_G}.
+ */
+ private final double shiftedShape;
+
+ /**
+ * The constant value of {@code shape / scale * sqrt(e / (2 * pi * (shape + g + 0.5))) /
+ * L(shape)}, where {@code L(shape)} is the Lanczos approximation returned by {@link
+ * Gamma#lanczos(double)}. This prefactor is used in {@link #density(double)}, when no overflow
+ * occurs with the natural calculation.
+ */
+ private final double densityPrefactor1;
+
+ /**
+ * The constant value of {@code log(shape / scale * sqrt(e / (2 * pi * (shape + g + 0.5))) /
+ * L(shape))}, where {@code L(shape)} is the Lanczos approximation returned by {@link
+ * Gamma#lanczos(double)}. This prefactor is used in {@link #logDensity(double)}, when no
+ * overflow occurs with the natural calculation.
+ */
+ private final double logDensityPrefactor1;
+
+ /**
+ * The constant value of {@code shape * sqrt(e / (2 * pi * (shape + g + 0.5))) / L(shape)},
+ * where {@code L(shape)} is the Lanczos approximation returned by {@link
+ * Gamma#lanczos(double)}. This prefactor is used in {@link #density(double)}, when overflow
+ * occurs with the natural calculation.
+ */
+ private final double densityPrefactor2;
+
+ /**
+ * The constant value of {@code log(shape * sqrt(e / (2 * pi * (shape + g + 0.5))) / L(shape))},
+ * where {@code L(shape)} is the Lanczos approximation returned by {@link
+ * Gamma#lanczos(double)}. This prefactor is used in {@link #logDensity(double)}, when overflow
+ * occurs with the natural calculation.
+ */
+ private final double logDensityPrefactor2;
+
+ /**
+ * Lower bound on {@code y = x / scale} for the selection of the computation method in {@link
+ * #density(double)}. For {@code y <= minY}, the natural calculation overflows.
+ */
+ private final double minY;
+
+ /**
+ * Upper bound on {@code log(y)} ({@code y = x / scale}) for the selection of the computation
+ * method in {@link #density(double)}. For {@code log(y) >= maxLogY}, the natural calculation
+ * overflows.
+ */
+ private final double maxLogY;
+
+ /** Inverse cumulative probability accuracy. */
+ private final double solverAbsoluteAccuracy;
+
+ /**
+ * Creates a new gamma distribution with specified values of the shape and scale parameters.
+ *
+ * <p><b>Note:</b> this constructor will implicitly create an instance of {@link Well19937c} as
+ * random generator to be used for sampling only (see {@link #sample()} and {@link
+ * #sample(int)}). In case no sampling is needed for the created distribution, it is advised to
+ * pass {@code null} as random generator via the appropriate constructors to avoid the
+ * additional initialisation overhead.
+ *
+ * @param shape the shape parameter
+ * @param scale the scale parameter
+ * @throws NotStrictlyPositiveException if {@code shape <= 0} or {@code scale <= 0}.
+ */
+ public GammaDistribution(double shape, double scale) throws NotStrictlyPositiveException {
+ this(shape, scale, DEFAULT_INVERSE_ABSOLUTE_ACCURACY);
+ }
+
+ /**
+ * Creates a new gamma distribution with specified values of the shape and scale parameters.
+ *
+ * <p><b>Note:</b> this constructor will implicitly create an instance of {@link Well19937c} as
+ * random generator to be used for sampling only (see {@link #sample()} and {@link
+ * #sample(int)}). In case no sampling is needed for the created distribution, it is advised to
+ * pass {@code null} as random generator via the appropriate constructors to avoid the
+ * additional initialisation overhead.
+ *
+ * @param shape the shape parameter
+ * @param scale the scale parameter
+ * @param inverseCumAccuracy the maximum absolute error in inverse cumulative probability
+ * estimates (defaults to {@link #DEFAULT_INVERSE_ABSOLUTE_ACCURACY}).
+ * @throws NotStrictlyPositiveException if {@code shape <= 0} or {@code scale <= 0}.
+ * @since 2.1
+ */
+ public GammaDistribution(double shape, double scale, double inverseCumAccuracy)
+ throws NotStrictlyPositiveException {
+ this(new Well19937c(), shape, scale, inverseCumAccuracy);
+ }
+
+ /**
+ * Creates a Gamma distribution.
+ *
+ * @param rng Random number generator.
+ * @param shape the shape parameter
+ * @param scale the scale parameter
+ * @throws NotStrictlyPositiveException if {@code shape <= 0} or {@code scale <= 0}.
+ * @since 3.3
+ */
+ public GammaDistribution(RandomGenerator rng, double shape, double scale)
+ throws NotStrictlyPositiveException {
+ this(rng, shape, scale, DEFAULT_INVERSE_ABSOLUTE_ACCURACY);
+ }
+
+ /**
+ * Creates a Gamma distribution.
+ *
+ * @param rng Random number generator.
+ * @param shape the shape parameter
+ * @param scale the scale parameter
+ * @param inverseCumAccuracy the maximum absolute error in inverse cumulative probability
+ * estimates (defaults to {@link #DEFAULT_INVERSE_ABSOLUTE_ACCURACY}).
+ * @throws NotStrictlyPositiveException if {@code shape <= 0} or {@code scale <= 0}.
+ * @since 3.1
+ */
+ public GammaDistribution(
+ RandomGenerator rng, double shape, double scale, double inverseCumAccuracy)
+ throws NotStrictlyPositiveException {
+ super(rng);
+
+ if (shape <= 0) {
+ throw new NotStrictlyPositiveException(LocalizedFormats.SHAPE, shape);
+ }
+ if (scale <= 0) {
+ throw new NotStrictlyPositiveException(LocalizedFormats.SCALE, scale);
+ }
+
+ this.shape = shape;
+ this.scale = scale;
+ this.solverAbsoluteAccuracy = inverseCumAccuracy;
+ this.shiftedShape = shape + Gamma.LANCZOS_G + 0.5;
+ final double aux = FastMath.E / (2.0 * FastMath.PI * shiftedShape);
+ this.densityPrefactor2 = shape * FastMath.sqrt(aux) / Gamma.lanczos(shape);
+ this.logDensityPrefactor2 =
+ FastMath.log(shape) + 0.5 * FastMath.log(aux) - FastMath.log(Gamma.lanczos(shape));
+ this.densityPrefactor1 =
+ this.densityPrefactor2
+ / scale
+ * FastMath.pow(shiftedShape, -shape)
+ * FastMath.exp(shape + Gamma.LANCZOS_G);
+ this.logDensityPrefactor1 =
+ this.logDensityPrefactor2
+ - FastMath.log(scale)
+ - FastMath.log(shiftedShape) * shape
+ + shape
+ + Gamma.LANCZOS_G;
+ this.minY = shape + Gamma.LANCZOS_G - FastMath.log(Double.MAX_VALUE);
+ this.maxLogY = FastMath.log(Double.MAX_VALUE) / (shape - 1.0);
+ }
+
+ /**
+ * Returns the shape parameter of {@code this} distribution.
+ *
+ * @return the shape parameter
+ * @deprecated as of version 3.1, {@link #getShape()} should be preferred. This method will be
+ * removed in version 4.0.
+ */
+ @Deprecated
+ public double getAlpha() {
+ return shape;
+ }
+
+ /**
+ * Returns the shape parameter of {@code this} distribution.
+ *
+ * @return the shape parameter
+ * @since 3.1
+ */
+ public double getShape() {
+ return shape;
+ }
+
+ /**
+ * Returns the scale parameter of {@code this} distribution.
+ *
+ * @return the scale parameter
+ * @deprecated as of version 3.1, {@link #getScale()} should be preferred. This method will be
+ * removed in version 4.0.
+ */
+ @Deprecated
+ public double getBeta() {
+ return scale;
+ }
+
+ /**
+ * Returns the scale parameter of {@code this} distribution.
+ *
+ * @return the scale parameter
+ * @since 3.1
+ */
+ public double getScale() {
+ return scale;
+ }
+
+ /** {@inheritDoc} */
+ public double density(double x) {
+ /* The present method must return the value of
+ *
+ * 1 x a - x
+ * ---------- (-) exp(---)
+ * x Gamma(a) b b
+ *
+ * where a is the shape parameter, and b the scale parameter.
+ * Substituting the Lanczos approximation of Gamma(a) leads to the
+ * following expression of the density
+ *
+ * a e 1 y a
+ * - sqrt(------------------) ---- (-----------) exp(a - y + g),
+ * x 2 pi (a + g + 0.5) L(a) a + g + 0.5
+ *
+ * where y = x / b. The above formula is the "natural" computation, which
+ * is implemented when no overflow is likely to occur. If overflow occurs
+ * with the natural computation, the following identity is used. It is
+ * based on the BOOST library
+ * http://www.boost.org/doc/libs/1_35_0/libs/math/doc/sf_and_dist/html/math_toolkit/special/sf_gamma/igamma.html
+ * Formula (15) needs adaptations, which are detailed below.
+ *
+ * y a
+ * (-----------) exp(a - y + g)
+ * a + g + 0.5
+ * y - a - g - 0.5 y (g + 0.5)
+ * = exp(a log1pm(---------------) - ----------- + g),
+ * a + g + 0.5 a + g + 0.5
+ *
+ * where log1pm(z) = log(1 + z) - z. Therefore, the value to be
+ * returned is
+ *
+ * a e 1
+ * - sqrt(------------------) ----
+ * x 2 pi (a + g + 0.5) L(a)
+ * y - a - g - 0.5 y (g + 0.5)
+ * * exp(a log1pm(---------------) - ----------- + g).
+ * a + g + 0.5 a + g + 0.5
+ */
+ if (x < 0) {
+ return 0;
+ }
+ final double y = x / scale;
+ if ((y <= minY) || (FastMath.log(y) >= maxLogY)) {
+ /*
+ * Overflow.
+ */
+ final double aux1 = (y - shiftedShape) / shiftedShape;
+ final double aux2 = shape * (FastMath.log1p(aux1) - aux1);
+ final double aux3 =
+ -y * (Gamma.LANCZOS_G + 0.5) / shiftedShape + Gamma.LANCZOS_G + aux2;
+ return densityPrefactor2 / x * FastMath.exp(aux3);
+ }
+ /*
+ * Natural calculation.
+ */
+ return densityPrefactor1 * FastMath.exp(-y) * FastMath.pow(y, shape - 1);
+ }
+
+ /** {@inheritDoc} * */
+ @Override
+ public double logDensity(double x) {
+ /*
+ * see the comment in {@link #density(double)} for computation details
+ */
+ if (x < 0) {
+ return Double.NEGATIVE_INFINITY;
+ }
+ final double y = x / scale;
+ if ((y <= minY) || (FastMath.log(y) >= maxLogY)) {
+ /*
+ * Overflow.
+ */
+ final double aux1 = (y - shiftedShape) / shiftedShape;
+ final double aux2 = shape * (FastMath.log1p(aux1) - aux1);
+ final double aux3 =
+ -y * (Gamma.LANCZOS_G + 0.5) / shiftedShape + Gamma.LANCZOS_G + aux2;
+ return logDensityPrefactor2 - FastMath.log(x) + aux3;
+ }
+ /*
+ * Natural calculation.
+ */
+ return logDensityPrefactor1 - y + FastMath.log(y) * (shape - 1);
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * <p>The implementation of this method is based on:
+ *
+ * <ul>
+ * <li><a href="http://mathworld.wolfram.com/Chi-SquaredDistribution.html">Chi-Squared
+ * Distribution</a>, equation (9).
+ * <li>Casella, G., & Berger, R. (1990). <i>Statistical Inference</i>. Belmont, CA: Duxbury
+ * Press.
+ * </ul>
+ */
+ public double cumulativeProbability(double x) {
+ double ret;
+
+ if (x <= 0) {
+ ret = 0;
+ } else {
+ ret = Gamma.regularizedGammaP(shape, x / scale);
+ }
+
+ return ret;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ protected double getSolverAbsoluteAccuracy() {
+ return solverAbsoluteAccuracy;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * <p>For shape parameter {@code alpha} and scale parameter {@code beta}, the mean is {@code
+ * alpha * beta}.
+ */
+ public double getNumericalMean() {
+ return shape * scale;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * <p>For shape parameter {@code alpha} and scale parameter {@code beta}, the variance is {@code
+ * alpha * beta^2}.
+ *
+ * @return {@inheritDoc}
+ */
+ public double getNumericalVariance() {
+ return shape * scale * scale;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * <p>The lower bound of the support is always 0 no matter the parameters.
+ *
+ * @return lower bound of the support (always 0)
+ */
+ public double getSupportLowerBound() {
+ return 0;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * <p>The upper bound of the support is always positive infinity no matter the parameters.
+ *
+ * @return upper bound of the support (always Double.POSITIVE_INFINITY)
+ */
+ public double getSupportUpperBound() {
+ return Double.POSITIVE_INFINITY;
+ }
+
+ /** {@inheritDoc} */
+ public boolean isSupportLowerBoundInclusive() {
+ return true;
+ }
+
+ /** {@inheritDoc} */
+ public boolean isSupportUpperBoundInclusive() {
+ return false;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * <p>The support of this distribution is connected.
+ *
+ * @return {@code true}
+ */
+ public boolean isSupportConnected() {
+ return true;
+ }
+
+ /**
+ * This implementation uses the following algorithms:
+ *
+ * <p>For 0 < shape < 1: <br>
+ * Ahrens, J. H. and Dieter, U., <i>Computer methods for sampling from gamma, beta, Poisson and
+ * binomial distributions.</i> Computing, 12, 223-246, 1974.
+ *
+ * <p>For shape >= 1: <br>
+ * Marsaglia and Tsang, <i>A Simple Method for Generating Gamma Variables.</i> ACM Transactions
+ * on Mathematical Software, Volume 26 Issue 3, September, 2000.
+ *
+ * @return random value sampled from the Gamma(shape, scale) distribution
+ */
+ @Override
+ public double sample() {
+ if (shape < 1) {
+ // [1]: p. 228, Algorithm GS
+
+ while (true) {
+ // Step 1:
+ final double u = random.nextDouble();
+ final double bGS = 1 + shape / FastMath.E;
+ final double p = bGS * u;
+
+ if (p <= 1) {
+ // Step 2:
+
+ final double x = FastMath.pow(p, 1 / shape);
+ final double u2 = random.nextDouble();
+
+ if (u2 > FastMath.exp(-x)) {
+ // Reject
+ continue;
+ } else {
+ return scale * x;
+ }
+ } else {
+ // Step 3:
+
+ final double x = -1 * FastMath.log((bGS - p) / shape);
+ final double u2 = random.nextDouble();
+
+ if (u2 > FastMath.pow(x, shape - 1)) {
+ // Reject
+ continue;
+ } else {
+ return scale * x;
+ }
+ }
+ }
+ }
+
+ // Now shape >= 1
+
+ final double d = shape - 0.333333333333333333;
+ final double c = 1 / (3 * FastMath.sqrt(d));
+
+ while (true) {
+ final double x = random.nextGaussian();
+ final double v = (1 + c * x) * (1 + c * x) * (1 + c * x);
+
+ if (v <= 0) {
+ continue;
+ }
+
+ final double x2 = x * x;
+ final double u = random.nextDouble();
+
+ // Squeeze
+ if (u < 1 - 0.0331 * x2 * x2) {
+ return scale * d * v;
+ }
+
+ if (FastMath.log(u) < 0.5 * x2 + d * (1 - v + FastMath.log(v))) {
+ return scale * d * v;
+ }
+ }
+ }
+}
diff --git a/src/main/java/org/apache/commons/math3/distribution/GeometricDistribution.java b/src/main/java/org/apache/commons/math3/distribution/GeometricDistribution.java
new file mode 100644
index 0000000..89c0a59
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/distribution/GeometricDistribution.java
@@ -0,0 +1,186 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math3.distribution;
+
+import org.apache.commons.math3.exception.OutOfRangeException;
+import org.apache.commons.math3.exception.util.LocalizedFormats;
+import org.apache.commons.math3.random.RandomGenerator;
+import org.apache.commons.math3.random.Well19937c;
+import org.apache.commons.math3.util.FastMath;
+
+/**
+ * Implementation of the geometric distribution.
+ *
+ * @see <a href="http://en.wikipedia.org/wiki/Geometric_distribution">Geometric distribution
+ * (Wikipedia)</a>
+ * @see <a href="http://mathworld.wolfram.com/GeometricDistribution.html">Geometric Distribution
+ * (MathWorld)</a>
+ * @since 3.3
+ */
+public class GeometricDistribution extends AbstractIntegerDistribution {
+
+ /** Serializable version identifier. */
+ private static final long serialVersionUID = 20130507L;
+
+ /** The probability of success. */
+ private final double probabilityOfSuccess;
+
+ /** {@code log(p)} where p is the probability of success. */
+ private final double logProbabilityOfSuccess;
+
+ /** {@code log(1 - p)} where p is the probability of success. */
+ private final double log1mProbabilityOfSuccess;
+
+ /**
+ * Create a geometric distribution with the given probability of success.
+ *
+ * <p><b>Note:</b> this constructor will implicitly create an instance of {@link Well19937c} as
+ * random generator to be used for sampling only (see {@link #sample()} and {@link
+ * #sample(int)}). In case no sampling is needed for the created distribution, it is advised to
+ * pass {@code null} as random generator via the appropriate constructors to avoid the
+ * additional initialisation overhead.
+ *
+ * @param p probability of success.
+ * @throws OutOfRangeException if {@code p <= 0} or {@code p > 1}.
+ */
+ public GeometricDistribution(double p) {
+ this(new Well19937c(), p);
+ }
+
+ /**
+ * Creates a geometric distribution.
+ *
+ * @param rng Random number generator.
+ * @param p Probability of success.
+ * @throws OutOfRangeException if {@code p <= 0} or {@code p > 1}.
+ */
+ public GeometricDistribution(RandomGenerator rng, double p) {
+ super(rng);
+
+ if (p <= 0 || p > 1) {
+ throw new OutOfRangeException(LocalizedFormats.OUT_OF_RANGE_LEFT, p, 0, 1);
+ }
+
+ probabilityOfSuccess = p;
+ logProbabilityOfSuccess = FastMath.log(p);
+ log1mProbabilityOfSuccess = FastMath.log1p(-p);
+ }
+
+ /**
+ * Access the probability of success for this distribution.
+ *
+ * @return the probability of success.
+ */
+ public double getProbabilityOfSuccess() {
+ return probabilityOfSuccess;
+ }
+
+ /** {@inheritDoc} */
+ public double probability(int x) {
+ if (x < 0) {
+ return 0.0;
+ } else {
+ return FastMath.exp(log1mProbabilityOfSuccess * x) * probabilityOfSuccess;
+ }
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public double logProbability(int x) {
+ if (x < 0) {
+ return Double.NEGATIVE_INFINITY;
+ } else {
+ return x * log1mProbabilityOfSuccess + logProbabilityOfSuccess;
+ }
+ }
+
+ /** {@inheritDoc} */
+ public double cumulativeProbability(int x) {
+ if (x < 0) {
+ return 0.0;
+ } else {
+ return -FastMath.expm1(log1mProbabilityOfSuccess * (x + 1));
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * <p>For probability parameter {@code p}, the mean is {@code (1 - p) / p}.
+ */
+ public double getNumericalMean() {
+ return (1 - probabilityOfSuccess) / probabilityOfSuccess;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * <p>For probability parameter {@code p}, the variance is {@code (1 - p) / (p * p)}.
+ */
+ public double getNumericalVariance() {
+ return (1 - probabilityOfSuccess) / (probabilityOfSuccess * probabilityOfSuccess);
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * <p>The lower bound of the support is always 0.
+ *
+ * @return lower bound of the support (always 0)
+ */
+ public int getSupportLowerBound() {
+ return 0;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * <p>The upper bound of the support is infinite (which we approximate as {@code
+ * Integer.MAX_VALUE}).
+ *
+ * @return upper bound of the support (always Integer.MAX_VALUE)
+ */
+ public int getSupportUpperBound() {
+ return Integer.MAX_VALUE;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * <p>The support of this distribution is connected.
+ *
+ * @return {@code true}
+ */
+ public boolean isSupportConnected() {
+ return true;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public int inverseCumulativeProbability(double p) throws OutOfRangeException {
+ if (p < 0 || p > 1) {
+ throw new OutOfRangeException(p, 0, 1);
+ }
+ if (p == 1) {
+ return Integer.MAX_VALUE;
+ }
+ if (p == 0) {
+ return 0;
+ }
+ return Math.max(0, (int) Math.ceil(FastMath.log1p(-p) / log1mProbabilityOfSuccess - 1));
+ }
+}
diff --git a/src/main/java/org/apache/commons/math3/distribution/GumbelDistribution.java b/src/main/java/org/apache/commons/math3/distribution/GumbelDistribution.java
new file mode 100644
index 0000000..78280a5
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/distribution/GumbelDistribution.java
@@ -0,0 +1,167 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math3.distribution;
+
+import org.apache.commons.math3.exception.NotStrictlyPositiveException;
+import org.apache.commons.math3.exception.OutOfRangeException;
+import org.apache.commons.math3.exception.util.LocalizedFormats;
+import org.apache.commons.math3.random.RandomGenerator;
+import org.apache.commons.math3.random.Well19937c;
+import org.apache.commons.math3.util.FastMath;
+import org.apache.commons.math3.util.MathUtils;
+
+/**
+ * This class implements the Gumbel distribution.
+ *
+ * @see <a href="http://en.wikipedia.org/wiki/Gumbel_distribution">Gumbel Distribution
+ * (Wikipedia)</a>
+ * @see <a href="http://mathworld.wolfram.com/GumbelDistribution.html">Gumbel Distribution
+ * (Mathworld)</a>
+ * @since 3.4
+ */
+public class GumbelDistribution extends AbstractRealDistribution {
+
+ /** Serializable version identifier. */
+ private static final long serialVersionUID = 20141003;
+
+ /**
+ * Approximation of Euler's constant see
+ * http://mathworld.wolfram.com/Euler-MascheroniConstantApproximations.html
+ */
+ private static final double EULER = FastMath.PI / (2 * FastMath.E);
+
+ /** The location parameter. */
+ private final double mu;
+
+ /** The scale parameter. */
+ private final double beta;
+
+ /**
+ * Build a new instance.
+ *
+ * <p><b>Note:</b> this constructor will implicitly create an instance of {@link Well19937c} as
+ * random generator to be used for sampling only (see {@link #sample()} and {@link
+ * #sample(int)}). In case no sampling is needed for the created distribution, it is advised to
+ * pass {@code null} as random generator via the appropriate constructors to avoid the
+ * additional initialisation overhead.
+ *
+ * @param mu location parameter
+ * @param beta scale parameter (must be positive)
+ * @throws NotStrictlyPositiveException if {@code beta <= 0}
+ */
+ public GumbelDistribution(double mu, double beta) {
+ this(new Well19937c(), mu, beta);
+ }
+
+ /**
+ * Build a new instance.
+ *
+ * @param rng Random number generator
+ * @param mu location parameter
+ * @param beta scale parameter (must be positive)
+ * @throws NotStrictlyPositiveException if {@code beta <= 0}
+ */
+ public GumbelDistribution(RandomGenerator rng, double mu, double beta) {
+ super(rng);
+
+ if (beta <= 0) {
+ throw new NotStrictlyPositiveException(LocalizedFormats.SCALE, beta);
+ }
+
+ this.beta = beta;
+ this.mu = mu;
+ }
+
+ /**
+ * Access the location parameter, {@code mu}.
+ *
+ * @return the location parameter.
+ */
+ public double getLocation() {
+ return mu;
+ }
+
+ /**
+ * Access the scale parameter, {@code beta}.
+ *
+ * @return the scale parameter.
+ */
+ public double getScale() {
+ return beta;
+ }
+
+ /** {@inheritDoc} */
+ public double density(double x) {
+ final double z = (x - mu) / beta;
+ final double t = FastMath.exp(-z);
+ return FastMath.exp(-z - t) / beta;
+ }
+
+ /** {@inheritDoc} */
+ public double cumulativeProbability(double x) {
+ final double z = (x - mu) / beta;
+ return FastMath.exp(-FastMath.exp(-z));
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public double inverseCumulativeProbability(double p) throws OutOfRangeException {
+ if (p < 0.0 || p > 1.0) {
+ throw new OutOfRangeException(p, 0.0, 1.0);
+ } else if (p == 0) {
+ return Double.NEGATIVE_INFINITY;
+ } else if (p == 1) {
+ return Double.POSITIVE_INFINITY;
+ }
+ return mu - FastMath.log(-FastMath.log(p)) * beta;
+ }
+
+ /** {@inheritDoc} */
+ public double getNumericalMean() {
+ return mu + EULER * beta;
+ }
+
+ /** {@inheritDoc} */
+ public double getNumericalVariance() {
+ return (MathUtils.PI_SQUARED) / 6.0 * (beta * beta);
+ }
+
+ /** {@inheritDoc} */
+ public double getSupportLowerBound() {
+ return Double.NEGATIVE_INFINITY;
+ }
+
+ /** {@inheritDoc} */
+ public double getSupportUpperBound() {
+ return Double.POSITIVE_INFINITY;
+ }
+
+ /** {@inheritDoc} */
+ public boolean isSupportLowerBoundInclusive() {
+ return false;
+ }
+
+ /** {@inheritDoc} */
+ public boolean isSupportUpperBoundInclusive() {
+ return false;
+ }
+
+ /** {@inheritDoc} */
+ public boolean isSupportConnected() {
+ return true;
+ }
+}
diff --git a/src/main/java/org/apache/commons/math3/distribution/HypergeometricDistribution.java b/src/main/java/org/apache/commons/math3/distribution/HypergeometricDistribution.java
new file mode 100644
index 0000000..dece6c8
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/distribution/HypergeometricDistribution.java
@@ -0,0 +1,347 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math3.distribution;
+
+import org.apache.commons.math3.exception.NotPositiveException;
+import org.apache.commons.math3.exception.NotStrictlyPositiveException;
+import org.apache.commons.math3.exception.NumberIsTooLargeException;
+import org.apache.commons.math3.exception.util.LocalizedFormats;
+import org.apache.commons.math3.random.RandomGenerator;
+import org.apache.commons.math3.random.Well19937c;
+import org.apache.commons.math3.util.FastMath;
+
+/**
+ * Implementation of the hypergeometric distribution.
+ *
+ * @see <a href="http://en.wikipedia.org/wiki/Hypergeometric_distribution">Hypergeometric
+ * distribution (Wikipedia)</a>
+ * @see <a href="http://mathworld.wolfram.com/HypergeometricDistribution.html">Hypergeometric
+ * distribution (MathWorld)</a>
+ */
+public class HypergeometricDistribution extends AbstractIntegerDistribution {
+ /** Serializable version identifier. */
+ private static final long serialVersionUID = -436928820673516179L;
+
+ /** The number of successes in the population. */
+ private final int numberOfSuccesses;
+
+ /** The population size. */
+ private final int populationSize;
+
+ /** The sample size. */
+ private final int sampleSize;
+
+ /** Cached numerical variance */
+ private double numericalVariance = Double.NaN;
+
+ /** Whether or not the numerical variance has been calculated */
+ private boolean numericalVarianceIsCalculated = false;
+
+ /**
+ * Construct a new hypergeometric distribution with the specified population size, number of
+ * successes in the population, and sample size.
+ *
+ * <p><b>Note:</b> this constructor will implicitly create an instance of {@link Well19937c} as
+ * random generator to be used for sampling only (see {@link #sample()} and {@link
+ * #sample(int)}). In case no sampling is needed for the created distribution, it is advised to
+ * pass {@code null} as random generator via the appropriate constructors to avoid the
+ * additional initialisation overhead.
+ *
+ * @param populationSize Population size.
+ * @param numberOfSuccesses Number of successes in the population.
+ * @param sampleSize Sample size.
+ * @throws NotPositiveException if {@code numberOfSuccesses < 0}.
+ * @throws NotStrictlyPositiveException if {@code populationSize <= 0}.
+ * @throws NumberIsTooLargeException if {@code numberOfSuccesses > populationSize}, or {@code
+ * sampleSize > populationSize}.
+ */
+ public HypergeometricDistribution(int populationSize, int numberOfSuccesses, int sampleSize)
+ throws NotPositiveException, NotStrictlyPositiveException, NumberIsTooLargeException {
+ this(new Well19937c(), populationSize, numberOfSuccesses, sampleSize);
+ }
+
+ /**
+ * Creates a new hypergeometric distribution.
+ *
+ * @param rng Random number generator.
+ * @param populationSize Population size.
+ * @param numberOfSuccesses Number of successes in the population.
+ * @param sampleSize Sample size.
+ * @throws NotPositiveException if {@code numberOfSuccesses < 0}.
+ * @throws NotStrictlyPositiveException if {@code populationSize <= 0}.
+ * @throws NumberIsTooLargeException if {@code numberOfSuccesses > populationSize}, or {@code
+ * sampleSize > populationSize}.
+ * @since 3.1
+ */
+ public HypergeometricDistribution(
+ RandomGenerator rng, int populationSize, int numberOfSuccesses, int sampleSize)
+ throws NotPositiveException, NotStrictlyPositiveException, NumberIsTooLargeException {
+ super(rng);
+
+ if (populationSize <= 0) {
+ throw new NotStrictlyPositiveException(
+ LocalizedFormats.POPULATION_SIZE, populationSize);
+ }
+ if (numberOfSuccesses < 0) {
+ throw new NotPositiveException(LocalizedFormats.NUMBER_OF_SUCCESSES, numberOfSuccesses);
+ }
+ if (sampleSize < 0) {
+ throw new NotPositiveException(LocalizedFormats.NUMBER_OF_SAMPLES, sampleSize);
+ }
+
+ if (numberOfSuccesses > populationSize) {
+ throw new NumberIsTooLargeException(
+ LocalizedFormats.NUMBER_OF_SUCCESS_LARGER_THAN_POPULATION_SIZE,
+ numberOfSuccesses,
+ populationSize,
+ true);
+ }
+ if (sampleSize > populationSize) {
+ throw new NumberIsTooLargeException(
+ LocalizedFormats.SAMPLE_SIZE_LARGER_THAN_POPULATION_SIZE,
+ sampleSize,
+ populationSize,
+ true);
+ }
+
+ this.numberOfSuccesses = numberOfSuccesses;
+ this.populationSize = populationSize;
+ this.sampleSize = sampleSize;
+ }
+
+ /** {@inheritDoc} */
+ public double cumulativeProbability(int x) {
+ double ret;
+
+ int[] domain = getDomain(populationSize, numberOfSuccesses, sampleSize);
+ if (x < domain[0]) {
+ ret = 0.0;
+ } else if (x >= domain[1]) {
+ ret = 1.0;
+ } else {
+ ret = innerCumulativeProbability(domain[0], x, 1);
+ }
+
+ return ret;
+ }
+
+ /**
+ * Return the domain for the given hypergeometric distribution parameters.
+ *
+ * @param n Population size.
+ * @param m Number of successes in the population.
+ * @param k Sample size.
+ * @return a two element array containing the lower and upper bounds of the hypergeometric
+ * distribution.
+ */
+ private int[] getDomain(int n, int m, int k) {
+ return new int[] {getLowerDomain(n, m, k), getUpperDomain(m, k)};
+ }
+
+ /**
+ * Return the lowest domain value for the given hypergeometric distribution parameters.
+ *
+ * @param n Population size.
+ * @param m Number of successes in the population.
+ * @param k Sample size.
+ * @return the lowest domain value of the hypergeometric distribution.
+ */
+ private int getLowerDomain(int n, int m, int k) {
+ return FastMath.max(0, m - (n - k));
+ }
+
+ /**
+ * Access the number of successes.
+ *
+ * @return the number of successes.
+ */
+ public int getNumberOfSuccesses() {
+ return numberOfSuccesses;
+ }
+
+ /**
+ * Access the population size.
+ *
+ * @return the population size.
+ */
+ public int getPopulationSize() {
+ return populationSize;
+ }
+
+ /**
+ * Access the sample size.
+ *
+ * @return the sample size.
+ */
+ public int getSampleSize() {
+ return sampleSize;
+ }
+
+ /**
+ * Return the highest domain value for the given hypergeometric distribution parameters.
+ *
+ * @param m Number of successes in the population.
+ * @param k Sample size.
+ * @return the highest domain value of the hypergeometric distribution.
+ */
+ private int getUpperDomain(int m, int k) {
+ return FastMath.min(k, m);
+ }
+
+ /** {@inheritDoc} */
+ public double probability(int x) {
+ final double logProbability = logProbability(x);
+ return logProbability == Double.NEGATIVE_INFINITY ? 0 : FastMath.exp(logProbability);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public double logProbability(int x) {
+ double ret;
+
+ int[] domain = getDomain(populationSize, numberOfSuccesses, sampleSize);
+ if (x < domain[0] || x > domain[1]) {
+ ret = Double.NEGATIVE_INFINITY;
+ } else {
+ double p = (double) sampleSize / (double) populationSize;
+ double q = (double) (populationSize - sampleSize) / (double) populationSize;
+ double p1 = SaddlePointExpansion.logBinomialProbability(x, numberOfSuccesses, p, q);
+ double p2 =
+ SaddlePointExpansion.logBinomialProbability(
+ sampleSize - x, populationSize - numberOfSuccesses, p, q);
+ double p3 =
+ SaddlePointExpansion.logBinomialProbability(sampleSize, populationSize, p, q);
+ ret = p1 + p2 - p3;
+ }
+
+ return ret;
+ }
+
+ /**
+ * For this distribution, {@code X}, this method returns {@code P(X >= x)}.
+ *
+ * @param x Value at which the CDF is evaluated.
+ * @return the upper tail CDF for this distribution.
+ * @since 1.1
+ */
+ public double upperCumulativeProbability(int x) {
+ double ret;
+
+ final int[] domain = getDomain(populationSize, numberOfSuccesses, sampleSize);
+ if (x <= domain[0]) {
+ ret = 1.0;
+ } else if (x > domain[1]) {
+ ret = 0.0;
+ } else {
+ ret = innerCumulativeProbability(domain[1], x, -1);
+ }
+
+ return ret;
+ }
+
+ /**
+ * For this distribution, {@code X}, this method returns {@code P(x0 <= X <= x1)}. This
+ * probability is computed by summing the point probabilities for the values {@code x0, x0 + 1,
+ * x0 + 2, ..., x1}, in the order directed by {@code dx}.
+ *
+ * @param x0 Inclusive lower bound.
+ * @param x1 Inclusive upper bound.
+ * @param dx Direction of summation (1 indicates summing from x0 to x1, and 0 indicates summing
+ * from x1 to x0).
+ * @return {@code P(x0 <= X <= x1)}.
+ */
+ private double innerCumulativeProbability(int x0, int x1, int dx) {
+ double ret = probability(x0);
+ while (x0 != x1) {
+ x0 += dx;
+ ret += probability(x0);
+ }
+ return ret;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * <p>For population size {@code N}, number of successes {@code m}, and sample size {@code n},
+ * the mean is {@code n * m / N}.
+ */
+ public double getNumericalMean() {
+ return getSampleSize() * (getNumberOfSuccesses() / (double) getPopulationSize());
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * <p>For population size {@code N}, number of successes {@code m}, and sample size {@code n},
+ * the variance is {@code [n * m * (N - n) * (N - m)] / [N^2 * (N - 1)]}.
+ */
+ public double getNumericalVariance() {
+ if (!numericalVarianceIsCalculated) {
+ numericalVariance = calculateNumericalVariance();
+ numericalVarianceIsCalculated = true;
+ }
+ return numericalVariance;
+ }
+
+ /**
+ * Used by {@link #getNumericalVariance()}.
+ *
+ * @return the variance of this distribution
+ */
+ protected double calculateNumericalVariance() {
+ final double N = getPopulationSize();
+ final double m = getNumberOfSuccesses();
+ final double n = getSampleSize();
+ return (n * m * (N - n) * (N - m)) / (N * N * (N - 1));
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * <p>For population size {@code N}, number of successes {@code m}, and sample size {@code n},
+ * the lower bound of the support is {@code max(0, n + m - N)}.
+ *
+ * @return lower bound of the support
+ */
+ public int getSupportLowerBound() {
+ return FastMath.max(0, getSampleSize() + getNumberOfSuccesses() - getPopulationSize());
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * <p>For number of successes {@code m} and sample size {@code n}, the upper bound of the
+ * support is {@code min(m, n)}.
+ *
+ * @return upper bound of the support
+ */
+ public int getSupportUpperBound() {
+ return FastMath.min(getNumberOfSuccesses(), getSampleSize());
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * <p>The support of this distribution is connected.
+ *
+ * @return {@code true}
+ */
+ public boolean isSupportConnected() {
+ return true;
+ }
+}
diff --git a/src/main/java/org/apache/commons/math3/distribution/IntegerDistribution.java b/src/main/java/org/apache/commons/math3/distribution/IntegerDistribution.java
new file mode 100644
index 0000000..c188a78
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/distribution/IntegerDistribution.java
@@ -0,0 +1,145 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math3.distribution;
+
+import org.apache.commons.math3.exception.NumberIsTooLargeException;
+import org.apache.commons.math3.exception.OutOfRangeException;
+
+/** Interface for distributions on the integers. */
+public interface IntegerDistribution {
+ /**
+ * For a random variable {@code X} whose values are distributed according to this distribution,
+ * this method returns {@code P(X = x)}. In other words, this method represents the probability
+ * mass function (PMF) for the distribution.
+ *
+ * @param x the point at which the PMF is evaluated
+ * @return the value of the probability mass function at {@code x}
+ */
+ double probability(int x);
+
+ /**
+ * For a random variable {@code X} whose values are distributed according to this distribution,
+ * this method returns {@code P(X <= x)}. In other words, this method represents the
+ * (cumulative) distribution function (CDF) for this distribution.
+ *
+ * @param x the point at which the CDF is evaluated
+ * @return the probability that a random variable with this distribution takes a value less than
+ * or equal to {@code x}
+ */
+ double cumulativeProbability(int x);
+
+ /**
+ * For a random variable {@code X} whose values are distributed according to this distribution,
+ * this method returns {@code P(x0 < X <= x1)}.
+ *
+ * @param x0 the exclusive lower bound
+ * @param x1 the inclusive upper bound
+ * @return the probability that a random variable with this distribution will take a value
+ * between {@code x0} and {@code x1}, excluding the lower and including the upper endpoint
+ * @throws NumberIsTooLargeException if {@code x0 > x1}
+ */
+ double cumulativeProbability(int x0, int x1) throws NumberIsTooLargeException;
+
+ /**
+ * Computes the quantile function of this distribution. For a random variable {@code X}
+ * distributed according to this distribution, the returned value is
+ *
+ * <ul>
+ * <li><code>inf{x in Z | P(X<=x) >= p}</code> for {@code 0 < p <= 1},
+ * <li><code>inf{x in Z | P(X<=x) > 0}</code> for {@code p = 0}.
+ * </ul>
+ *
+ * If the result exceeds the range of the data type {@code int}, then {@code Integer.MIN_VALUE}
+ * or {@code Integer.MAX_VALUE} is returned.
+ *
+ * @param p the cumulative probability
+ * @return the smallest {@code p}-quantile of this distribution (largest 0-quantile for {@code p
+ * = 0})
+ * @throws OutOfRangeException if {@code p < 0} or {@code p > 1}
+ */
+ int inverseCumulativeProbability(double p) throws OutOfRangeException;
+
+ /**
+ * Use this method to get the numerical value of the mean of this distribution.
+ *
+ * @return the mean or {@code Double.NaN} if it is not defined
+ */
+ double getNumericalMean();
+
+ /**
+ * Use this method to get the numerical value of the variance of this distribution.
+ *
+ * @return the variance (possibly {@code Double.POSITIVE_INFINITY} or {@code Double.NaN} if it
+ * is not defined)
+ */
+ double getNumericalVariance();
+
+ /**
+ * Access the lower bound of the support. This method must return the same value as {@code
+ * inverseCumulativeProbability(0)}. In other words, this method must return
+ *
+ * <p><code>inf {x in Z | P(X <= x) > 0}</code>.
+ *
+ * @return lower bound of the support ({@code Integer.MIN_VALUE} for negative infinity)
+ */
+ int getSupportLowerBound();
+
+ /**
+ * Access the upper bound of the support. This method must return the same value as {@code
+ * inverseCumulativeProbability(1)}. In other words, this method must return
+ *
+ * <p><code>inf {x in R | P(X <= x) = 1}</code>.
+ *
+ * @return upper bound of the support ({@code Integer.MAX_VALUE} for positive infinity)
+ */
+ int getSupportUpperBound();
+
+ /**
+ * Use this method to get information about whether the support is connected, i.e. whether all
+ * integers between the lower and upper bound of the support are included in the support.
+ *
+ * @return whether the support is connected or not
+ */
+ boolean isSupportConnected();
+
+ /**
+ * Reseed the random generator used to generate samples.
+ *
+ * @param seed the new seed
+ * @since 3.0
+ */
+ void reseedRandomGenerator(long seed);
+
+ /**
+ * Generate a random value sampled from this distribution.
+ *
+ * @return a random value
+ * @since 3.0
+ */
+ int sample();
+
+ /**
+ * Generate a random sample from the distribution.
+ *
+ * @param sampleSize the number of random values to generate
+ * @return an array representing the random sample
+ * @throws org.apache.commons.math3.exception.NotStrictlyPositiveException if {@code sampleSize}
+ * is not positive
+ * @since 3.0
+ */
+ int[] sample(int sampleSize);
+}
diff --git a/src/main/java/org/apache/commons/math3/distribution/KolmogorovSmirnovDistribution.java b/src/main/java/org/apache/commons/math3/distribution/KolmogorovSmirnovDistribution.java
new file mode 100644
index 0000000..3ee007f
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/distribution/KolmogorovSmirnovDistribution.java
@@ -0,0 +1,338 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math3.distribution;
+
+import org.apache.commons.math3.exception.MathArithmeticException;
+import org.apache.commons.math3.exception.NotStrictlyPositiveException;
+import org.apache.commons.math3.exception.NumberIsTooLargeException;
+import org.apache.commons.math3.exception.util.LocalizedFormats;
+import org.apache.commons.math3.fraction.BigFraction;
+import org.apache.commons.math3.fraction.BigFractionField;
+import org.apache.commons.math3.fraction.FractionConversionException;
+import org.apache.commons.math3.linear.Array2DRowFieldMatrix;
+import org.apache.commons.math3.linear.Array2DRowRealMatrix;
+import org.apache.commons.math3.linear.FieldMatrix;
+import org.apache.commons.math3.linear.RealMatrix;
+import org.apache.commons.math3.util.FastMath;
+
+import java.io.Serializable;
+import java.math.BigDecimal;
+
+/**
+ * Implementation of the Kolmogorov-Smirnov distribution.
+ *
+ * <p>Treats the distribution of the two-sided {@code P(D_n < d)} where {@code D_n = sup_x |G(x) -
+ * G_n (x)|} for the theoretical cdf {@code G} and the empirical cdf {@code G_n}.
+ *
+ * <p>This implementation is based on [1] with certain quick decisions for extreme values given in
+ * [2].
+ *
+ * <p>In short, when wanting to evaluate {@code P(D_n < d)}, the method in [1] is to write {@code d
+ * = (k - h) / n} for positive integer {@code k} and {@code 0 <= h < 1}. Then {@code P(D_n < d) =
+ * (n! / n^n) * t_kk}, where {@code t_kk} is the {@code (k, k)}'th entry in the special matrix
+ * {@code H^n}, i.e. {@code H} to the {@code n}'th power.
+ *
+ * <p>References:
+ *
+ * <ul>
+ * <li>[1] <a href="http://www.jstatsoft.org/v08/i18/">Evaluating Kolmogorov's Distribution</a> by
+ * George Marsaglia, Wai Wan Tsang, and Jingbo Wang
+ * <li>[2] <a href="http://www.jstatsoft.org/v39/i11/">Computing the Two-Sided Kolmogorov-Smirnov
+ * Distribution</a> by Richard Simard and Pierre L'Ecuyer
+ * </ul>
+ *
+ * Note that [1] contains an error in computing h, refer to <a
+ * href="https://issues.apache.org/jira/browse/MATH-437">MATH-437</a> for details.
+ *
+ * @see <a href="http://en.wikipedia.org/wiki/Kolmogorov-Smirnov_test">Kolmogorov-Smirnov test
+ * (Wikipedia)</a>
+ * @deprecated to be removed in version 4.0 - use {@link
+ * org.apache.commons.math3.stat.inference.KolmogorovSmirnovTest}
+ */
+public class KolmogorovSmirnovDistribution implements Serializable {
+
+ /** Serializable version identifier. */
+ private static final long serialVersionUID = -4670676796862967187L;
+
+ /** Number of observations. */
+ private int n;
+
+ /**
+ * @param n Number of observations
+ * @throws NotStrictlyPositiveException if {@code n <= 0}
+ */
+ public KolmogorovSmirnovDistribution(int n) throws NotStrictlyPositiveException {
+ if (n <= 0) {
+ throw new NotStrictlyPositiveException(
+ LocalizedFormats.NOT_POSITIVE_NUMBER_OF_SAMPLES, n);
+ }
+
+ this.n = n;
+ }
+
+ /**
+ * Calculates {@code P(D_n < d)} using method described in [1] with quick decisions for extreme
+ * values given in [2] (see above). The result is not exact as with {@link
+ * KolmogorovSmirnovDistribution#cdfExact(double)} because calculations are based on {@code
+ * double} rather than {@link org.apache.commons.math3.fraction.BigFraction}.
+ *
+ * @param d statistic
+ * @return the two-sided probability of {@code P(D_n < d)}
+ * @throws MathArithmeticException if algorithm fails to convert {@code h} to a {@link
+ * org.apache.commons.math3.fraction.BigFraction} in expressing {@code d} as {@code (k - h)
+ * / m} for integer {@code k, m} and {@code 0 <= h < 1}.
+ */
+ public double cdf(double d) throws MathArithmeticException {
+ return this.cdf(d, false);
+ }
+
+ /**
+ * Calculates {@code P(D_n < d)} using method described in [1] with quick decisions for extreme
+ * values given in [2] (see above). The result is exact in the sense that BigFraction/BigReal is
+ * used everywhere at the expense of very slow execution time. Almost never choose this in real
+ * applications unless you are very sure; this is almost solely for verification purposes.
+ * Normally, you would choose {@link KolmogorovSmirnovDistribution#cdf(double)}
+ *
+ * @param d statistic
+ * @return the two-sided probability of {@code P(D_n < d)}
+ * @throws MathArithmeticException if algorithm fails to convert {@code h} to a {@link
+ * org.apache.commons.math3.fraction.BigFraction} in expressing {@code d} as {@code (k - h)
+ * / m} for integer {@code k, m} and {@code 0 <= h < 1}.
+ */
+ public double cdfExact(double d) throws MathArithmeticException {
+ return this.cdf(d, true);
+ }
+
+ /**
+ * Calculates {@code P(D_n < d)} using method described in [1] with quick decisions for extreme
+ * values given in [2] (see above).
+ *
+ * @param d statistic
+ * @param exact whether the probability should be calculated exact using {@link
+ * org.apache.commons.math3.fraction.BigFraction} everywhere at the expense of very slow
+ * execution time, or if {@code double} should be used convenient places to gain speed.
+ * Almost never choose {@code true} in real applications unless you are very sure; {@code
+ * true} is almost solely for verification purposes.
+ * @return the two-sided probability of {@code P(D_n < d)}
+ * @throws MathArithmeticException if algorithm fails to convert {@code h} to a {@link
+ * org.apache.commons.math3.fraction.BigFraction} in expressing {@code d} as {@code (k - h)
+ * / m} for integer {@code k, m} and {@code 0 <= h < 1}.
+ */
+ public double cdf(double d, boolean exact) throws MathArithmeticException {
+
+ final double ninv = 1 / ((double) n);
+ final double ninvhalf = 0.5 * ninv;
+
+ if (d <= ninvhalf) {
+
+ return 0;
+
+ } else if (ninvhalf < d && d <= ninv) {
+
+ double res = 1;
+ double f = 2 * d - ninv;
+
+ // n! f^n = n*f * (n-1)*f * ... * 1*x
+ for (int i = 1; i <= n; ++i) {
+ res *= i * f;
+ }
+
+ return res;
+
+ } else if (1 - ninv <= d && d < 1) {
+
+ return 1 - 2 * FastMath.pow(1 - d, n);
+
+ } else if (1 <= d) {
+
+ return 1;
+ }
+
+ return exact ? exactK(d) : roundedK(d);
+ }
+
+ /**
+ * Calculates the exact value of {@code P(D_n < d)} using method described in [1] and {@link
+ * org.apache.commons.math3.fraction.BigFraction} (see above).
+ *
+ * @param d statistic
+ * @return the two-sided probability of {@code P(D_n < d)}
+ * @throws MathArithmeticException if algorithm fails to convert {@code h} to a {@link
+ * org.apache.commons.math3.fraction.BigFraction} in expressing {@code d} as {@code (k - h)
+ * / m} for integer {@code k, m} and {@code 0 <= h < 1}.
+ */
+ private double exactK(double d) throws MathArithmeticException {
+
+ final int k = (int) FastMath.ceil(n * d);
+
+ final FieldMatrix<BigFraction> H = this.createH(d);
+ final FieldMatrix<BigFraction> Hpower = H.power(n);
+
+ BigFraction pFrac = Hpower.getEntry(k - 1, k - 1);
+
+ for (int i = 1; i <= n; ++i) {
+ pFrac = pFrac.multiply(i).divide(n);
+ }
+
+ /*
+ * BigFraction.doubleValue converts numerator to double and the
+ * denominator to double and divides afterwards. That gives NaN quite
+ * easy. This does not (scale is the number of digits):
+ */
+ return pFrac.bigDecimalValue(20, BigDecimal.ROUND_HALF_UP).doubleValue();
+ }
+
+ /**
+ * Calculates {@code P(D_n < d)} using method described in [1] and doubles (see above).
+ *
+ * @param d statistic
+ * @return the two-sided probability of {@code P(D_n < d)}
+ * @throws MathArithmeticException if algorithm fails to convert {@code h} to a {@link
+ * org.apache.commons.math3.fraction.BigFraction} in expressing {@code d} as {@code (k - h)
+ * / m} for integer {@code k, m} and {@code 0 <= h < 1}.
+ */
+ private double roundedK(double d) throws MathArithmeticException {
+
+ final int k = (int) FastMath.ceil(n * d);
+ final FieldMatrix<BigFraction> HBigFraction = this.createH(d);
+ final int m = HBigFraction.getRowDimension();
+
+ /*
+ * Here the rounding part comes into play: use
+ * RealMatrix instead of FieldMatrix<BigFraction>
+ */
+ final RealMatrix H = new Array2DRowRealMatrix(m, m);
+
+ for (int i = 0; i < m; ++i) {
+ for (int j = 0; j < m; ++j) {
+ H.setEntry(i, j, HBigFraction.getEntry(i, j).doubleValue());
+ }
+ }
+
+ final RealMatrix Hpower = H.power(n);
+
+ double pFrac = Hpower.getEntry(k - 1, k - 1);
+
+ for (int i = 1; i <= n; ++i) {
+ pFrac *= (double) i / (double) n;
+ }
+
+ return pFrac;
+ }
+
+ /***
+ * Creates {@code H} of size {@code m x m} as described in [1] (see above).
+ *
+ * @param d statistic
+ * @return H matrix
+ * @throws NumberIsTooLargeException if fractional part is greater than 1
+ * @throws FractionConversionException if algorithm fails to convert
+ * {@code h} to a {@link org.apache.commons.math3.fraction.BigFraction} in
+ * expressing {@code d} as {@code (k - h) / m} for integer {@code k, m} and
+ * {@code 0 <= h < 1}.
+ */
+ private FieldMatrix<BigFraction> createH(double d)
+ throws NumberIsTooLargeException, FractionConversionException {
+
+ int k = (int) FastMath.ceil(n * d);
+
+ int m = 2 * k - 1;
+ double hDouble = k - n * d;
+
+ if (hDouble >= 1) {
+ throw new NumberIsTooLargeException(hDouble, 1.0, false);
+ }
+
+ BigFraction h = null;
+
+ try {
+ h = new BigFraction(hDouble, 1.0e-20, 10000);
+ } catch (FractionConversionException e1) {
+ try {
+ h = new BigFraction(hDouble, 1.0e-10, 10000);
+ } catch (FractionConversionException e2) {
+ h = new BigFraction(hDouble, 1.0e-5, 10000);
+ }
+ }
+
+ final BigFraction[][] Hdata = new BigFraction[m][m];
+
+ /*
+ * Start by filling everything with either 0 or 1.
+ */
+ for (int i = 0; i < m; ++i) {
+ for (int j = 0; j < m; ++j) {
+ if (i - j + 1 < 0) {
+ Hdata[i][j] = BigFraction.ZERO;
+ } else {
+ Hdata[i][j] = BigFraction.ONE;
+ }
+ }
+ }
+
+ /*
+ * Setting up power-array to avoid calculating the same value twice:
+ * hPowers[0] = h^1 ... hPowers[m-1] = h^m
+ */
+ final BigFraction[] hPowers = new BigFraction[m];
+ hPowers[0] = h;
+ for (int i = 1; i < m; ++i) {
+ hPowers[i] = h.multiply(hPowers[i - 1]);
+ }
+
+ /*
+ * First column and last row has special values (each other reversed).
+ */
+ for (int i = 0; i < m; ++i) {
+ Hdata[i][0] = Hdata[i][0].subtract(hPowers[i]);
+ Hdata[m - 1][i] = Hdata[m - 1][i].subtract(hPowers[m - i - 1]);
+ }
+
+ /*
+ * [1] states: "For 1/2 < h < 1 the bottom left element of the matrix
+ * should be (1 - 2*h^m + (2h - 1)^m )/m!" Since 0 <= h < 1, then if h >
+ * 1/2 is sufficient to check:
+ */
+ if (h.compareTo(BigFraction.ONE_HALF) == 1) {
+ Hdata[m - 1][0] = Hdata[m - 1][0].add(h.multiply(2).subtract(1).pow(m));
+ }
+
+ /*
+ * Aside from the first column and last row, the (i, j)-th element is
+ * 1/(i - j + 1)! if i - j + 1 >= 0, else 0. 1's and 0's are already
+ * put, so only division with (i - j + 1)! is needed in the elements
+ * that have 1's. There is no need to calculate (i - j + 1)! and then
+ * divide - small steps avoid overflows.
+ *
+ * Note that i - j + 1 > 0 <=> i + 1 > j instead of j'ing all the way to
+ * m. Also note that it is started at g = 2 because dividing by 1 isn't
+ * really necessary.
+ */
+ for (int i = 0; i < m; ++i) {
+ for (int j = 0; j < i + 1; ++j) {
+ if (i - j + 1 > 0) {
+ for (int g = 2; g <= i - j + 1; ++g) {
+ Hdata[i][j] = Hdata[i][j].divide(g);
+ }
+ }
+ }
+ }
+
+ return new Array2DRowFieldMatrix<BigFraction>(BigFractionField.getInstance(), Hdata);
+ }
+}
diff --git a/src/main/java/org/apache/commons/math3/distribution/LaplaceDistribution.java b/src/main/java/org/apache/commons/math3/distribution/LaplaceDistribution.java
new file mode 100644
index 0000000..2ce36fc
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/distribution/LaplaceDistribution.java
@@ -0,0 +1,160 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math3.distribution;
+
+import org.apache.commons.math3.exception.NotStrictlyPositiveException;
+import org.apache.commons.math3.exception.OutOfRangeException;
+import org.apache.commons.math3.exception.util.LocalizedFormats;
+import org.apache.commons.math3.random.RandomGenerator;
+import org.apache.commons.math3.random.Well19937c;
+import org.apache.commons.math3.util.FastMath;
+
+/**
+ * This class implements the Laplace distribution.
+ *
+ * @see <a href="http://en.wikipedia.org/wiki/Laplace_distribution">Laplace distribution
+ * (Wikipedia)</a>
+ * @since 3.4
+ */
+public class LaplaceDistribution extends AbstractRealDistribution {
+
+ /** Serializable version identifier. */
+ private static final long serialVersionUID = 20141003;
+
+ /** The location parameter. */
+ private final double mu;
+
+ /** The scale parameter. */
+ private final double beta;
+
+ /**
+ * Build a new instance.
+ *
+ * <p><b>Note:</b> this constructor will implicitly create an instance of {@link Well19937c} as
+ * random generator to be used for sampling only (see {@link #sample()} and {@link
+ * #sample(int)}). In case no sampling is needed for the created distribution, it is advised to
+ * pass {@code null} as random generator via the appropriate constructors to avoid the
+ * additional initialisation overhead.
+ *
+ * @param mu location parameter
+ * @param beta scale parameter (must be positive)
+ * @throws NotStrictlyPositiveException if {@code beta <= 0}
+ */
+ public LaplaceDistribution(double mu, double beta) {
+ this(new Well19937c(), mu, beta);
+ }
+
+ /**
+ * Build a new instance.
+ *
+ * @param rng Random number generator
+ * @param mu location parameter
+ * @param beta scale parameter (must be positive)
+ * @throws NotStrictlyPositiveException if {@code beta <= 0}
+ */
+ public LaplaceDistribution(RandomGenerator rng, double mu, double beta) {
+ super(rng);
+
+ if (beta <= 0.0) {
+ throw new NotStrictlyPositiveException(LocalizedFormats.NOT_POSITIVE_SCALE, beta);
+ }
+
+ this.mu = mu;
+ this.beta = beta;
+ }
+
+ /**
+ * Access the location parameter, {@code mu}.
+ *
+ * @return the location parameter.
+ */
+ public double getLocation() {
+ return mu;
+ }
+
+ /**
+ * Access the scale parameter, {@code beta}.
+ *
+ * @return the scale parameter.
+ */
+ public double getScale() {
+ return beta;
+ }
+
+ /** {@inheritDoc} */
+ public double density(double x) {
+ return FastMath.exp(-FastMath.abs(x - mu) / beta) / (2.0 * beta);
+ }
+
+ /** {@inheritDoc} */
+ public double cumulativeProbability(double x) {
+ if (x <= mu) {
+ return FastMath.exp((x - mu) / beta) / 2.0;
+ } else {
+ return 1.0 - FastMath.exp((mu - x) / beta) / 2.0;
+ }
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public double inverseCumulativeProbability(double p) throws OutOfRangeException {
+ if (p < 0.0 || p > 1.0) {
+ throw new OutOfRangeException(p, 0.0, 1.0);
+ } else if (p == 0) {
+ return Double.NEGATIVE_INFINITY;
+ } else if (p == 1) {
+ return Double.POSITIVE_INFINITY;
+ }
+ double x = (p > 0.5) ? -Math.log(2.0 - 2.0 * p) : Math.log(2.0 * p);
+ return mu + beta * x;
+ }
+
+ /** {@inheritDoc} */
+ public double getNumericalMean() {
+ return mu;
+ }
+
+ /** {@inheritDoc} */
+ public double getNumericalVariance() {
+ return 2.0 * beta * beta;
+ }
+
+ /** {@inheritDoc} */
+ public double getSupportLowerBound() {
+ return Double.NEGATIVE_INFINITY;
+ }
+
+ /** {@inheritDoc} */
+ public double getSupportUpperBound() {
+ return Double.POSITIVE_INFINITY;
+ }
+
+ /** {@inheritDoc} */
+ public boolean isSupportLowerBoundInclusive() {
+ return false;
+ }
+
+ /** {@inheritDoc} */
+ public boolean isSupportUpperBoundInclusive() {
+ return false;
+ }
+
+ /** {@inheritDoc} */
+ public boolean isSupportConnected() {
+ return true;
+ }
+}
diff --git a/src/main/java/org/apache/commons/math3/distribution/LevyDistribution.java b/src/main/java/org/apache/commons/math3/distribution/LevyDistribution.java
new file mode 100644
index 0000000..d76e993
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/distribution/LevyDistribution.java
@@ -0,0 +1,197 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math3.distribution;
+
+import org.apache.commons.math3.exception.OutOfRangeException;
+import org.apache.commons.math3.random.RandomGenerator;
+import org.apache.commons.math3.random.Well19937c;
+import org.apache.commons.math3.special.Erf;
+import org.apache.commons.math3.util.FastMath;
+
+/**
+ * This class implements the <a href="http://en.wikipedia.org/wiki/L%C3%A9vy_distribution">
+ * L&eacute;vy distribution</a>.
+ *
+ * @since 3.2
+ */
+public class LevyDistribution extends AbstractRealDistribution {
+
+ /** Serializable UID. */
+ private static final long serialVersionUID = 20130314L;
+
+ /** Location parameter. */
+ private final double mu;
+
+ /** Scale parameter. */
+ private final double c; // Setting this to 1 returns a cumProb of 1.0
+
+ /** Half of c (for calculations). */
+ private final double halfC;
+
+ /**
+ * Build a new instance.
+ *
+ * <p><b>Note:</b> this constructor will implicitly create an instance of {@link Well19937c} as
+ * random generator to be used for sampling only (see {@link #sample()} and {@link
+ * #sample(int)}). In case no sampling is needed for the created distribution, it is advised to
+ * pass {@code null} as random generator via the appropriate constructors to avoid the
+ * additional initialisation overhead.
+ *
+ * @param mu location parameter
+ * @param c scale parameter
+ * @since 3.4
+ */
+ public LevyDistribution(final double mu, final double c) {
+ this(new Well19937c(), mu, c);
+ }
+
+ /**
+ * Creates a LevyDistribution.
+ *
+ * @param rng random generator to be used for sampling
+ * @param mu location
+ * @param c scale parameter
+ */
+ public LevyDistribution(final RandomGenerator rng, final double mu, final double c) {
+ super(rng);
+ this.mu = mu;
+ this.c = c;
+ this.halfC = 0.5 * c;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * <p>From Wikipedia: The probability density function of the L&eacute;vy distribution over the
+ * domain is
+ *
+ * <pre>
+ * f(x; &mu;, c) = &radic;(c / 2&pi;) * e<sup>-c / 2 (x - &mu;)</sup> / (x - &mu;)<sup>3/2</sup>
+ * </pre>
+ *
+ * <p>For this distribution, {@code X}, this method returns {@code P(X < x)}. If {@code x} is
+ * less than location parameter &mu;, {@code Double.NaN} is returned, as in these cases the
+ * distribution is not defined.
+ */
+ public double density(final double x) {
+ if (x < mu) {
+ return Double.NaN;
+ }
+
+ final double delta = x - mu;
+ final double f = halfC / delta;
+ return FastMath.sqrt(f / FastMath.PI) * FastMath.exp(-f) / delta;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * <p>See documentation of {@link #density(double)} for computation details.
+ */
+ @Override
+ public double logDensity(double x) {
+ if (x < mu) {
+ return Double.NaN;
+ }
+
+ final double delta = x - mu;
+ final double f = halfC / delta;
+ return 0.5 * FastMath.log(f / FastMath.PI) - f - FastMath.log(delta);
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * <p>From Wikipedia: the cumulative distribution function is
+ *
+ * <pre>
+ * f(x; u, c) = erfc (&radic; (c / 2 (x - u )))
+ * </pre>
+ */
+ public double cumulativeProbability(final double x) {
+ if (x < mu) {
+ return Double.NaN;
+ }
+ return Erf.erfc(FastMath.sqrt(halfC / (x - mu)));
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public double inverseCumulativeProbability(final double p) throws OutOfRangeException {
+ if (p < 0.0 || p > 1.0) {
+ throw new OutOfRangeException(p, 0, 1);
+ }
+ final double t = Erf.erfcInv(p);
+ return mu + halfC / (t * t);
+ }
+
+ /**
+ * Get the scale parameter of the distribution.
+ *
+ * @return scale parameter of the distribution
+ */
+ public double getScale() {
+ return c;
+ }
+
+ /**
+ * Get the location parameter of the distribution.
+ *
+ * @return location parameter of the distribution
+ */
+ public double getLocation() {
+ return mu;
+ }
+
+ /** {@inheritDoc} */
+ public double getNumericalMean() {
+ return Double.POSITIVE_INFINITY;
+ }
+
+ /** {@inheritDoc} */
+ public double getNumericalVariance() {
+ return Double.POSITIVE_INFINITY;
+ }
+
+ /** {@inheritDoc} */
+ public double getSupportLowerBound() {
+ return mu;
+ }
+
+ /** {@inheritDoc} */
+ public double getSupportUpperBound() {
+ return Double.POSITIVE_INFINITY;
+ }
+
+ /** {@inheritDoc} */
+ public boolean isSupportLowerBoundInclusive() {
+ // there is a division by x-mu in the computation, so density
+ // is not finite at lower bound, bound must be excluded
+ return false;
+ }
+
+ /** {@inheritDoc} */
+ public boolean isSupportUpperBoundInclusive() {
+ // upper bound is infinite, so it must be excluded
+ return false;
+ }
+
+ /** {@inheritDoc} */
+ public boolean isSupportConnected() {
+ return true;
+ }
+}
diff --git a/src/main/java/org/apache/commons/math3/distribution/LogNormalDistribution.java b/src/main/java/org/apache/commons/math3/distribution/LogNormalDistribution.java
new file mode 100644
index 0000000..e6a6deb
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/distribution/LogNormalDistribution.java
@@ -0,0 +1,349 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math3.distribution;
+
+import org.apache.commons.math3.exception.NotStrictlyPositiveException;
+import org.apache.commons.math3.exception.NumberIsTooLargeException;
+import org.apache.commons.math3.exception.util.LocalizedFormats;
+import org.apache.commons.math3.random.RandomGenerator;
+import org.apache.commons.math3.random.Well19937c;
+import org.apache.commons.math3.special.Erf;
+import org.apache.commons.math3.util.FastMath;
+
+/**
+ * Implementation of the log-normal (gaussian) distribution.
+ *
+ * <p><strong>Parameters:</strong> {@code X} is log-normally distributed if its natural logarithm
+ * {@code log(X)} is normally distributed. The probability distribution function of {@code X} is
+ * given by (for {@code x > 0})
+ *
+ * <p>{@code exp(-0.5 * ((ln(x) - m) / s)^2) / (s * sqrt(2 * pi) * x)}
+ *
+ * <ul>
+ * <li>{@code m} is the <em>scale</em> parameter: this is the mean of the normally distributed
+ * natural logarithm of this distribution,
+ * <li>{@code s} is the <em>shape</em> parameter: this is the standard deviation of the normally
+ * distributed natural logarithm of this distribution.
+ * </ul>
+ *
+ * @see <a href="http://en.wikipedia.org/wiki/Log-normal_distribution">Log-normal distribution
+ * (Wikipedia)</a>
+ * @see <a href="http://mathworld.wolfram.com/LogNormalDistribution.html">Log Normal distribution
+ * (MathWorld)</a>
+ * @since 3.0
+ */
+public class LogNormalDistribution extends AbstractRealDistribution {
+ /** Default inverse cumulative probability accuracy. */
+ public static final double DEFAULT_INVERSE_ABSOLUTE_ACCURACY = 1e-9;
+
+ /** Serializable version identifier. */
+ private static final long serialVersionUID = 20120112;
+
+ /** &radic;(2 &pi;) */
+ private static final double SQRT2PI = FastMath.sqrt(2 * FastMath.PI);
+
+ /** &radic;(2) */
+ private static final double SQRT2 = FastMath.sqrt(2.0);
+
+ /** The scale parameter of this distribution. */
+ private final double scale;
+
+ /** The shape parameter of this distribution. */
+ private final double shape;
+
+ /** The value of {@code log(shape) + 0.5 * log(2*PI)} stored for faster computation. */
+ private final double logShapePlusHalfLog2Pi;
+
+ /** Inverse cumulative probability accuracy. */
+ private final double solverAbsoluteAccuracy;
+
+ /**
+ * Create a log-normal distribution, where the mean and standard deviation of the {@link
+ * NormalDistribution normally distributed} natural logarithm of the log-normal distribution are
+ * equal to zero and one respectively. In other words, the scale of the returned distribution is
+ * {@code 0}, while its shape is {@code 1}.
+ *
+ * <p><b>Note:</b> this constructor will implicitly create an instance of {@link Well19937c} as
+ * random generator to be used for sampling only (see {@link #sample()} and {@link
+ * #sample(int)}). In case no sampling is needed for the created distribution, it is advised to
+ * pass {@code null} as random generator via the appropriate constructors to avoid the
+ * additional initialisation overhead.
+ */
+ public LogNormalDistribution() {
+ this(0, 1);
+ }
+
+ /**
+ * Create a log-normal distribution using the specified scale and shape.
+ *
+ * <p><b>Note:</b> this constructor will implicitly create an instance of {@link Well19937c} as
+ * random generator to be used for sampling only (see {@link #sample()} and {@link
+ * #sample(int)}). In case no sampling is needed for the created distribution, it is advised to
+ * pass {@code null} as random generator via the appropriate constructors to avoid the
+ * additional initialisation overhead.
+ *
+ * @param scale the scale parameter of this distribution
+ * @param shape the shape parameter of this distribution
+ * @throws NotStrictlyPositiveException if {@code shape <= 0}.
+ */
+ public LogNormalDistribution(double scale, double shape) throws NotStrictlyPositiveException {
+ this(scale, shape, DEFAULT_INVERSE_ABSOLUTE_ACCURACY);
+ }
+
+ /**
+ * Create a log-normal distribution using the specified scale, shape and inverse cumulative
+ * distribution accuracy.
+ *
+ * <p><b>Note:</b> this constructor will implicitly create an instance of {@link Well19937c} as
+ * random generator to be used for sampling only (see {@link #sample()} and {@link
+ * #sample(int)}). In case no sampling is needed for the created distribution, it is advised to
+ * pass {@code null} as random generator via the appropriate constructors to avoid the
+ * additional initialisation overhead.
+ *
+ * @param scale the scale parameter of this distribution
+ * @param shape the shape parameter of this distribution
+ * @param inverseCumAccuracy Inverse cumulative probability accuracy.
+ * @throws NotStrictlyPositiveException if {@code shape <= 0}.
+ */
+ public LogNormalDistribution(double scale, double shape, double inverseCumAccuracy)
+ throws NotStrictlyPositiveException {
+ this(new Well19937c(), scale, shape, inverseCumAccuracy);
+ }
+
+ /**
+ * Creates a log-normal distribution.
+ *
+ * @param rng Random number generator.
+ * @param scale Scale parameter of this distribution.
+ * @param shape Shape parameter of this distribution.
+ * @throws NotStrictlyPositiveException if {@code shape <= 0}.
+ * @since 3.3
+ */
+ public LogNormalDistribution(RandomGenerator rng, double scale, double shape)
+ throws NotStrictlyPositiveException {
+ this(rng, scale, shape, DEFAULT_INVERSE_ABSOLUTE_ACCURACY);
+ }
+
+ /**
+ * Creates a log-normal distribution.
+ *
+ * @param rng Random number generator.
+ * @param scale Scale parameter of this distribution.
+ * @param shape Shape parameter of this distribution.
+ * @param inverseCumAccuracy Inverse cumulative probability accuracy.
+ * @throws NotStrictlyPositiveException if {@code shape <= 0}.
+ * @since 3.1
+ */
+ public LogNormalDistribution(
+ RandomGenerator rng, double scale, double shape, double inverseCumAccuracy)
+ throws NotStrictlyPositiveException {
+ super(rng);
+
+ if (shape <= 0) {
+ throw new NotStrictlyPositiveException(LocalizedFormats.SHAPE, shape);
+ }
+
+ this.scale = scale;
+ this.shape = shape;
+ this.logShapePlusHalfLog2Pi = FastMath.log(shape) + 0.5 * FastMath.log(2 * FastMath.PI);
+ this.solverAbsoluteAccuracy = inverseCumAccuracy;
+ }
+
+ /**
+ * Returns the scale parameter of this distribution.
+ *
+ * @return the scale parameter
+ */
+ public double getScale() {
+ return scale;
+ }
+
+ /**
+ * Returns the shape parameter of this distribution.
+ *
+ * @return the shape parameter
+ */
+ public double getShape() {
+ return shape;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * <p>For scale {@code m}, and shape {@code s} of this distribution, the PDF is given by
+ *
+ * <ul>
+ * <li>{@code 0} if {@code x <= 0},
+ * <li>{@code exp(-0.5 * ((ln(x) - m) / s)^2) / (s * sqrt(2 * pi) * x)} otherwise.
+ * </ul>
+ */
+ public double density(double x) {
+ if (x <= 0) {
+ return 0;
+ }
+ final double x0 = FastMath.log(x) - scale;
+ final double x1 = x0 / shape;
+ return FastMath.exp(-0.5 * x1 * x1) / (shape * SQRT2PI * x);
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * <p>See documentation of {@link #density(double)} for computation details.
+ */
+ @Override
+ public double logDensity(double x) {
+ if (x <= 0) {
+ return Double.NEGATIVE_INFINITY;
+ }
+ final double logX = FastMath.log(x);
+ final double x0 = logX - scale;
+ final double x1 = x0 / shape;
+ return -0.5 * x1 * x1 - (logShapePlusHalfLog2Pi + logX);
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * <p>For scale {@code m}, and shape {@code s} of this distribution, the CDF is given by
+ *
+ * <ul>
+ * <li>{@code 0} if {@code x <= 0},
+ * <li>{@code 0} if {@code ln(x) - m < 0} and {@code m - ln(x) > 40 * s}, as in these cases
+ * the actual value is within {@code Double.MIN_VALUE} of 0,
+ * <li>{@code 1} if {@code ln(x) - m >= 0} and {@code ln(x) - m > 40 * s}, as in these cases
+ * the actual value is within {@code Double.MIN_VALUE} of 1,
+ * <li>{@code 0.5 + 0.5 * erf((ln(x) - m) / (s * sqrt(2))} otherwise.
+ * </ul>
+ */
+ public double cumulativeProbability(double x) {
+ if (x <= 0) {
+ return 0;
+ }
+ final double dev = FastMath.log(x) - scale;
+ if (FastMath.abs(dev) > 40 * shape) {
+ return dev < 0 ? 0.0d : 1.0d;
+ }
+ return 0.5 + 0.5 * Erf.erf(dev / (shape * SQRT2));
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @deprecated See {@link RealDistribution#cumulativeProbability(double,double)}
+ */
+ @Override
+ @Deprecated
+ public double cumulativeProbability(double x0, double x1) throws NumberIsTooLargeException {
+ return probability(x0, x1);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public double probability(double x0, double x1) throws NumberIsTooLargeException {
+ if (x0 > x1) {
+ throw new NumberIsTooLargeException(
+ LocalizedFormats.LOWER_ENDPOINT_ABOVE_UPPER_ENDPOINT, x0, x1, true);
+ }
+ if (x0 <= 0 || x1 <= 0) {
+ return super.probability(x0, x1);
+ }
+ final double denom = shape * SQRT2;
+ final double v0 = (FastMath.log(x0) - scale) / denom;
+ final double v1 = (FastMath.log(x1) - scale) / denom;
+ return 0.5 * Erf.erf(v0, v1);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ protected double getSolverAbsoluteAccuracy() {
+ return solverAbsoluteAccuracy;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * <p>For scale {@code m} and shape {@code s}, the mean is {@code exp(m + s^2 / 2)}.
+ */
+ public double getNumericalMean() {
+ double s = shape;
+ return FastMath.exp(scale + (s * s / 2));
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * <p>For scale {@code m} and shape {@code s}, the variance is {@code (exp(s^2) - 1) * exp(2 * m
+ * + s^2)}.
+ */
+ public double getNumericalVariance() {
+ final double s = shape;
+ final double ss = s * s;
+ return (FastMath.expm1(ss)) * FastMath.exp(2 * scale + ss);
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * <p>The lower bound of the support is always 0 no matter the parameters.
+ *
+ * @return lower bound of the support (always 0)
+ */
+ public double getSupportLowerBound() {
+ return 0;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * <p>The upper bound of the support is always positive infinity no matter the parameters.
+ *
+ * @return upper bound of the support (always {@code Double.POSITIVE_INFINITY})
+ */
+ public double getSupportUpperBound() {
+ return Double.POSITIVE_INFINITY;
+ }
+
+ /** {@inheritDoc} */
+ public boolean isSupportLowerBoundInclusive() {
+ return true;
+ }
+
+ /** {@inheritDoc} */
+ public boolean isSupportUpperBoundInclusive() {
+ return false;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * <p>The support of this distribution is connected.
+ *
+ * @return {@code true}
+ */
+ public boolean isSupportConnected() {
+ return true;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public double sample() {
+ final double n = random.nextGaussian();
+ return FastMath.exp(scale + shape * n);
+ }
+}
diff --git a/src/main/java/org/apache/commons/math3/distribution/LogisticDistribution.java b/src/main/java/org/apache/commons/math3/distribution/LogisticDistribution.java
new file mode 100644
index 0000000..d2e7504
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/distribution/LogisticDistribution.java
@@ -0,0 +1,161 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math3.distribution;
+
+import org.apache.commons.math3.exception.NotStrictlyPositiveException;
+import org.apache.commons.math3.exception.OutOfRangeException;
+import org.apache.commons.math3.exception.util.LocalizedFormats;
+import org.apache.commons.math3.random.RandomGenerator;
+import org.apache.commons.math3.random.Well19937c;
+import org.apache.commons.math3.util.FastMath;
+import org.apache.commons.math3.util.MathUtils;
+
+/**
+ * This class implements the Logistic distribution.
+ *
+ * @see <a href="http://en.wikipedia.org/wiki/Logistic_distribution">Logistic Distribution
+ * (Wikipedia)</a>
+ * @see <a href="http://mathworld.wolfram.com/LogisticDistribution.html">Logistic Distribution
+ * (Mathworld)</a>
+ * @since 3.4
+ */
+public class LogisticDistribution extends AbstractRealDistribution {
+
+ /** Serializable version identifier. */
+ private static final long serialVersionUID = 20141003;
+
+ /** The location parameter. */
+ private final double mu;
+
+ /** The scale parameter. */
+ private final double s;
+
+ /**
+ * Build a new instance.
+ *
+ * <p><b>Note:</b> this constructor will implicitly create an instance of {@link Well19937c} as
+ * random generator to be used for sampling only (see {@link #sample()} and {@link
+ * #sample(int)}). In case no sampling is needed for the created distribution, it is advised to
+ * pass {@code null} as random generator via the appropriate constructors to avoid the
+ * additional initialisation overhead.
+ *
+ * @param mu location parameter
+ * @param s scale parameter (must be positive)
+ * @throws NotStrictlyPositiveException if {@code beta <= 0}
+ */
+ public LogisticDistribution(double mu, double s) {
+ this(new Well19937c(), mu, s);
+ }
+
+ /**
+ * Build a new instance.
+ *
+ * @param rng Random number generator
+ * @param mu location parameter
+ * @param s scale parameter (must be positive)
+ * @throws NotStrictlyPositiveException if {@code beta <= 0}
+ */
+ public LogisticDistribution(RandomGenerator rng, double mu, double s) {
+ super(rng);
+
+ if (s <= 0.0) {
+ throw new NotStrictlyPositiveException(LocalizedFormats.NOT_POSITIVE_SCALE, s);
+ }
+
+ this.mu = mu;
+ this.s = s;
+ }
+
+ /**
+ * Access the location parameter, {@code mu}.
+ *
+ * @return the location parameter.
+ */
+ public double getLocation() {
+ return mu;
+ }
+
+ /**
+ * Access the scale parameter, {@code s}.
+ *
+ * @return the scale parameter.
+ */
+ public double getScale() {
+ return s;
+ }
+
+ /** {@inheritDoc} */
+ public double density(double x) {
+ double z = (x - mu) / s;
+ double v = FastMath.exp(-z);
+ return 1 / s * v / ((1.0 + v) * (1.0 + v));
+ }
+
+ /** {@inheritDoc} */
+ public double cumulativeProbability(double x) {
+ double z = 1 / s * (x - mu);
+ return 1.0 / (1.0 + FastMath.exp(-z));
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public double inverseCumulativeProbability(double p) throws OutOfRangeException {
+ if (p < 0.0 || p > 1.0) {
+ throw new OutOfRangeException(p, 0.0, 1.0);
+ } else if (p == 0) {
+ return 0.0;
+ } else if (p == 1) {
+ return Double.POSITIVE_INFINITY;
+ }
+ return s * Math.log(p / (1.0 - p)) + mu;
+ }
+
+ /** {@inheritDoc} */
+ public double getNumericalMean() {
+ return mu;
+ }
+
+ /** {@inheritDoc} */
+ public double getNumericalVariance() {
+ return (MathUtils.PI_SQUARED / 3.0) * (1.0 / (s * s));
+ }
+
+ /** {@inheritDoc} */
+ public double getSupportLowerBound() {
+ return Double.NEGATIVE_INFINITY;
+ }
+
+ /** {@inheritDoc} */
+ public double getSupportUpperBound() {
+ return Double.POSITIVE_INFINITY;
+ }
+
+ /** {@inheritDoc} */
+ public boolean isSupportLowerBoundInclusive() {
+ return false;
+ }
+
+ /** {@inheritDoc} */
+ public boolean isSupportUpperBoundInclusive() {
+ return false;
+ }
+
+ /** {@inheritDoc} */
+ public boolean isSupportConnected() {
+ return true;
+ }
+}
diff --git a/src/main/java/org/apache/commons/math3/distribution/MixtureMultivariateNormalDistribution.java b/src/main/java/org/apache/commons/math3/distribution/MixtureMultivariateNormalDistribution.java
new file mode 100644
index 0000000..547d349
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/distribution/MixtureMultivariateNormalDistribution.java
@@ -0,0 +1,104 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math3.distribution;
+
+import org.apache.commons.math3.exception.DimensionMismatchException;
+import org.apache.commons.math3.exception.NotPositiveException;
+import org.apache.commons.math3.random.RandomGenerator;
+import org.apache.commons.math3.util.Pair;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Multivariate normal mixture distribution. This class is mainly syntactic sugar.
+ *
+ * @see MixtureMultivariateRealDistribution
+ * @since 3.2
+ */
+public class MixtureMultivariateNormalDistribution
+ extends MixtureMultivariateRealDistribution<MultivariateNormalDistribution> {
+
+ /**
+ * Creates a multivariate normal mixture distribution.
+ *
+ * <p><b>Note:</b> this constructor will implicitly create an instance of {@link
+ * org.apache.commons.math3.random.Well19937c Well19937c} as random generator to be used for
+ * sampling only (see {@link #sample()} and {@link #sample(int)}). In case no sampling is needed
+ * for the created distribution, it is advised to pass {@code null} as random generator via the
+ * appropriate constructors to avoid the additional initialisation overhead.
+ *
+ * @param weights Weights of each component.
+ * @param means Mean vector for each component.
+ * @param covariances Covariance matrix for each component.
+ */
+ public MixtureMultivariateNormalDistribution(
+ double[] weights, double[][] means, double[][][] covariances) {
+ super(createComponents(weights, means, covariances));
+ }
+
+ /**
+ * Creates a mixture model from a list of distributions and their associated weights.
+ *
+ * <p><b>Note:</b> this constructor will implicitly create an instance of {@link
+ * org.apache.commons.math3.random.Well19937c Well19937c} as random generator to be used for
+ * sampling only (see {@link #sample()} and {@link #sample(int)}). In case no sampling is needed
+ * for the created distribution, it is advised to pass {@code null} as random generator via the
+ * appropriate constructors to avoid the additional initialisation overhead.
+ *
+ * @param components List of (weight, distribution) pairs from which to sample.
+ */
+ public MixtureMultivariateNormalDistribution(
+ List<Pair<Double, MultivariateNormalDistribution>> components) {
+ super(components);
+ }
+
+ /**
+ * Creates a mixture model from a list of distributions and their associated weights.
+ *
+ * @param rng Random number generator.
+ * @param components Distributions from which to sample.
+ * @throws NotPositiveException if any of the weights is negative.
+ * @throws DimensionMismatchException if not all components have the same number of variables.
+ */
+ public MixtureMultivariateNormalDistribution(
+ RandomGenerator rng, List<Pair<Double, MultivariateNormalDistribution>> components)
+ throws NotPositiveException, DimensionMismatchException {
+ super(rng, components);
+ }
+
+ /**
+ * @param weights Weights of each component.
+ * @param means Mean vector for each component.
+ * @param covariances Covariance matrix for each component.
+ * @return the list of components.
+ */
+ private static List<Pair<Double, MultivariateNormalDistribution>> createComponents(
+ double[] weights, double[][] means, double[][][] covariances) {
+ final List<Pair<Double, MultivariateNormalDistribution>> mvns =
+ new ArrayList<Pair<Double, MultivariateNormalDistribution>>(weights.length);
+
+ for (int i = 0; i < weights.length; i++) {
+ final MultivariateNormalDistribution dist =
+ new MultivariateNormalDistribution(means[i], covariances[i]);
+
+ mvns.add(new Pair<Double, MultivariateNormalDistribution>(weights[i], dist));
+ }
+
+ return mvns;
+ }
+}
diff --git a/src/main/java/org/apache/commons/math3/distribution/MixtureMultivariateRealDistribution.java b/src/main/java/org/apache/commons/math3/distribution/MixtureMultivariateRealDistribution.java
new file mode 100644
index 0000000..4c65b75
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/distribution/MixtureMultivariateRealDistribution.java
@@ -0,0 +1,167 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math3.distribution;
+
+import org.apache.commons.math3.exception.DimensionMismatchException;
+import org.apache.commons.math3.exception.MathArithmeticException;
+import org.apache.commons.math3.exception.NotPositiveException;
+import org.apache.commons.math3.exception.util.LocalizedFormats;
+import org.apache.commons.math3.random.RandomGenerator;
+import org.apache.commons.math3.random.Well19937c;
+import org.apache.commons.math3.util.Pair;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Class for representing <a href="http://en.wikipedia.org/wiki/Mixture_model">mixture model</a>
+ * distributions.
+ *
+ * @param <T> Type of the mixture components.
+ * @since 3.1
+ */
+public class MixtureMultivariateRealDistribution<T extends MultivariateRealDistribution>
+ extends AbstractMultivariateRealDistribution {
+ /** Normalized weight of each mixture component. */
+ private final double[] weight;
+
+ /** Mixture components. */
+ private final List<T> distribution;
+
+ /**
+ * Creates a mixture model from a list of distributions and their associated weights.
+ *
+ * <p><b>Note:</b> this constructor will implicitly create an instance of {@link Well19937c} as
+ * random generator to be used for sampling only (see {@link #sample()} and {@link
+ * #sample(int)}). In case no sampling is needed for the created distribution, it is advised to
+ * pass {@code null} as random generator via the appropriate constructors to avoid the
+ * additional initialisation overhead.
+ *
+ * @param components List of (weight, distribution) pairs from which to sample.
+ */
+ public MixtureMultivariateRealDistribution(List<Pair<Double, T>> components) {
+ this(new Well19937c(), components);
+ }
+
+ /**
+ * Creates a mixture model from a list of distributions and their associated weights.
+ *
+ * @param rng Random number generator.
+ * @param components Distributions from which to sample.
+ * @throws NotPositiveException if any of the weights is negative.
+ * @throws DimensionMismatchException if not all components have the same number of variables.
+ */
+ public MixtureMultivariateRealDistribution(
+ RandomGenerator rng, List<Pair<Double, T>> components) {
+ super(rng, components.get(0).getSecond().getDimension());
+
+ final int numComp = components.size();
+ final int dim = getDimension();
+ double weightSum = 0;
+ for (int i = 0; i < numComp; i++) {
+ final Pair<Double, T> comp = components.get(i);
+ if (comp.getSecond().getDimension() != dim) {
+ throw new DimensionMismatchException(comp.getSecond().getDimension(), dim);
+ }
+ if (comp.getFirst() < 0) {
+ throw new NotPositiveException(comp.getFirst());
+ }
+ weightSum += comp.getFirst();
+ }
+
+ // Check for overflow.
+ if (Double.isInfinite(weightSum)) {
+ throw new MathArithmeticException(LocalizedFormats.OVERFLOW);
+ }
+
+ // Store each distribution and its normalized weight.
+ distribution = new ArrayList<T>();
+ weight = new double[numComp];
+ for (int i = 0; i < numComp; i++) {
+ final Pair<Double, T> comp = components.get(i);
+ weight[i] = comp.getFirst() / weightSum;
+ distribution.add(comp.getSecond());
+ }
+ }
+
+ /** {@inheritDoc} */
+ public double density(final double[] values) {
+ double p = 0;
+ for (int i = 0; i < weight.length; i++) {
+ p += weight[i] * distribution.get(i).density(values);
+ }
+ return p;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public double[] sample() {
+ // Sampled values.
+ double[] vals = null;
+
+ // Determine which component to sample from.
+ final double randomValue = random.nextDouble();
+ double sum = 0;
+
+ for (int i = 0; i < weight.length; i++) {
+ sum += weight[i];
+ if (randomValue <= sum) {
+ // pick model i
+ vals = distribution.get(i).sample();
+ break;
+ }
+ }
+
+ if (vals == null) {
+ // This should never happen, but it ensures we won't return a null in
+ // case the loop above has some floating point inequality problem on
+ // the final iteration.
+ vals = distribution.get(weight.length - 1).sample();
+ }
+
+ return vals;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void reseedRandomGenerator(long seed) {
+ // Seed needs to be propagated to underlying components
+ // in order to maintain consistency between runs.
+ super.reseedRandomGenerator(seed);
+
+ for (int i = 0; i < distribution.size(); i++) {
+ // Make each component's seed different in order to avoid
+ // using the same sequence of random numbers.
+ distribution.get(i).reseedRandomGenerator(i + 1 + seed);
+ }
+ }
+
+ /**
+ * Gets the distributions that make up the mixture model.
+ *
+ * @return the component distributions and associated weights.
+ */
+ public List<Pair<Double, T>> getComponents() {
+ final List<Pair<Double, T>> list = new ArrayList<Pair<Double, T>>(weight.length);
+
+ for (int i = 0; i < weight.length; i++) {
+ list.add(new Pair<Double, T>(weight[i], distribution.get(i)));
+ }
+
+ return list;
+ }
+}
diff --git a/src/main/java/org/apache/commons/math3/distribution/MultivariateNormalDistribution.java b/src/main/java/org/apache/commons/math3/distribution/MultivariateNormalDistribution.java
new file mode 100644
index 0000000..388761a
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/distribution/MultivariateNormalDistribution.java
@@ -0,0 +1,237 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math3.distribution;
+
+import org.apache.commons.math3.exception.DimensionMismatchException;
+import org.apache.commons.math3.linear.Array2DRowRealMatrix;
+import org.apache.commons.math3.linear.EigenDecomposition;
+import org.apache.commons.math3.linear.NonPositiveDefiniteMatrixException;
+import org.apache.commons.math3.linear.RealMatrix;
+import org.apache.commons.math3.linear.SingularMatrixException;
+import org.apache.commons.math3.random.RandomGenerator;
+import org.apache.commons.math3.random.Well19937c;
+import org.apache.commons.math3.util.FastMath;
+import org.apache.commons.math3.util.MathArrays;
+
+/**
+ * Implementation of the multivariate normal (Gaussian) distribution.
+ *
+ * @see <a href="http://en.wikipedia.org/wiki/Multivariate_normal_distribution">Multivariate normal
+ * distribution (Wikipedia)</a>
+ * @see <a href="http://mathworld.wolfram.com/MultivariateNormalDistribution.html">Multivariate
+ * normal distribution (MathWorld)</a>
+ * @since 3.1
+ */
+public class MultivariateNormalDistribution extends AbstractMultivariateRealDistribution {
+ /** Vector of means. */
+ private final double[] means;
+
+ /** Covariance matrix. */
+ private final RealMatrix covarianceMatrix;
+
+ /** The matrix inverse of the covariance matrix. */
+ private final RealMatrix covarianceMatrixInverse;
+
+ /** The determinant of the covariance matrix. */
+ private final double covarianceMatrixDeterminant;
+
+ /** Matrix used in computation of samples. */
+ private final RealMatrix samplingMatrix;
+
+ /**
+ * Creates a multivariate normal distribution with the given mean vector and covariance matrix.
+ * <br>
+ * The number of dimensions is equal to the length of the mean vector and to the number of rows
+ * and columns of the covariance matrix. It is frequently written as "p" in formulae.
+ *
+ * <p><b>Note:</b> this constructor will implicitly create an instance of {@link Well19937c} as
+ * random generator to be used for sampling only (see {@link #sample()} and {@link
+ * #sample(int)}). In case no sampling is needed for the created distribution, it is advised to
+ * pass {@code null} as random generator via the appropriate constructors to avoid the
+ * additional initialisation overhead.
+ *
+ * @param means Vector of means.
+ * @param covariances Covariance matrix.
+ * @throws DimensionMismatchException if the arrays length are inconsistent.
+ * @throws SingularMatrixException if the eigenvalue decomposition cannot be performed on the
+ * provided covariance matrix.
+ * @throws NonPositiveDefiniteMatrixException if any of the eigenvalues is negative.
+ */
+ public MultivariateNormalDistribution(final double[] means, final double[][] covariances)
+ throws SingularMatrixException,
+ DimensionMismatchException,
+ NonPositiveDefiniteMatrixException {
+ this(new Well19937c(), means, covariances);
+ }
+
+ /**
+ * Creates a multivariate normal distribution with the given mean vector and covariance matrix.
+ * <br>
+ * The number of dimensions is equal to the length of the mean vector and to the number of rows
+ * and columns of the covariance matrix. It is frequently written as "p" in formulae.
+ *
+ * @param rng Random Number Generator.
+ * @param means Vector of means.
+ * @param covariances Covariance matrix.
+ * @throws DimensionMismatchException if the arrays length are inconsistent.
+ * @throws SingularMatrixException if the eigenvalue decomposition cannot be performed on the
+ * provided covariance matrix.
+ * @throws NonPositiveDefiniteMatrixException if any of the eigenvalues is negative.
+ */
+ public MultivariateNormalDistribution(
+ RandomGenerator rng, final double[] means, final double[][] covariances)
+ throws SingularMatrixException,
+ DimensionMismatchException,
+ NonPositiveDefiniteMatrixException {
+ super(rng, means.length);
+
+ final int dim = means.length;
+
+ if (covariances.length != dim) {
+ throw new DimensionMismatchException(covariances.length, dim);
+ }
+
+ for (int i = 0; i < dim; i++) {
+ if (dim != covariances[i].length) {
+ throw new DimensionMismatchException(covariances[i].length, dim);
+ }
+ }
+
+ this.means = MathArrays.copyOf(means);
+
+ covarianceMatrix = new Array2DRowRealMatrix(covariances);
+
+ // Covariance matrix eigen decomposition.
+ final EigenDecomposition covMatDec = new EigenDecomposition(covarianceMatrix);
+
+ // Compute and store the inverse.
+ covarianceMatrixInverse = covMatDec.getSolver().getInverse();
+ // Compute and store the determinant.
+ covarianceMatrixDeterminant = covMatDec.getDeterminant();
+
+ // Eigenvalues of the covariance matrix.
+ final double[] covMatEigenvalues = covMatDec.getRealEigenvalues();
+
+ for (int i = 0; i < covMatEigenvalues.length; i++) {
+ if (covMatEigenvalues[i] < 0) {
+ throw new NonPositiveDefiniteMatrixException(covMatEigenvalues[i], i, 0);
+ }
+ }
+
+ // Matrix where each column is an eigenvector of the covariance matrix.
+ final Array2DRowRealMatrix covMatEigenvectors = new Array2DRowRealMatrix(dim, dim);
+ for (int v = 0; v < dim; v++) {
+ final double[] evec = covMatDec.getEigenvector(v).toArray();
+ covMatEigenvectors.setColumn(v, evec);
+ }
+
+ final RealMatrix tmpMatrix = covMatEigenvectors.transpose();
+
+ // Scale each eigenvector by the square root of its eigenvalue.
+ for (int row = 0; row < dim; row++) {
+ final double factor = FastMath.sqrt(covMatEigenvalues[row]);
+ for (int col = 0; col < dim; col++) {
+ tmpMatrix.multiplyEntry(row, col, factor);
+ }
+ }
+
+ samplingMatrix = covMatEigenvectors.multiply(tmpMatrix);
+ }
+
+ /**
+ * Gets the mean vector.
+ *
+ * @return the mean vector.
+ */
+ public double[] getMeans() {
+ return MathArrays.copyOf(means);
+ }
+
+ /**
+ * Gets the covariance matrix.
+ *
+ * @return the covariance matrix.
+ */
+ public RealMatrix getCovariances() {
+ return covarianceMatrix.copy();
+ }
+
+ /** {@inheritDoc} */
+ public double density(final double[] vals) throws DimensionMismatchException {
+ final int dim = getDimension();
+ if (vals.length != dim) {
+ throw new DimensionMismatchException(vals.length, dim);
+ }
+
+ return FastMath.pow(2 * FastMath.PI, -0.5 * dim)
+ * FastMath.pow(covarianceMatrixDeterminant, -0.5)
+ * getExponentTerm(vals);
+ }
+
+ /**
+ * Gets the square root of each element on the diagonal of the covariance matrix.
+ *
+ * @return the standard deviations.
+ */
+ public double[] getStandardDeviations() {
+ final int dim = getDimension();
+ final double[] std = new double[dim];
+ final double[][] s = covarianceMatrix.getData();
+ for (int i = 0; i < dim; i++) {
+ std[i] = FastMath.sqrt(s[i][i]);
+ }
+ return std;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public double[] sample() {
+ final int dim = getDimension();
+ final double[] normalVals = new double[dim];
+
+ for (int i = 0; i < dim; i++) {
+ normalVals[i] = random.nextGaussian();
+ }
+
+ final double[] vals = samplingMatrix.operate(normalVals);
+
+ for (int i = 0; i < dim; i++) {
+ vals[i] += means[i];
+ }
+
+ return vals;
+ }
+
+ /**
+ * Computes the term used in the exponent (see definition of the distribution).
+ *
+ * @param values Values at which to compute density.
+ * @return the multiplication factor of density calculations.
+ */
+ private double getExponentTerm(final double[] values) {
+ final double[] centered = new double[values.length];
+ for (int i = 0; i < centered.length; i++) {
+ centered[i] = values[i] - getMeans()[i];
+ }
+ final double[] preMultiplied = covarianceMatrixInverse.preMultiply(centered);
+ double sum = 0;
+ for (int i = 0; i < preMultiplied.length; i++) {
+ sum += preMultiplied[i] * centered[i];
+ }
+ return FastMath.exp(-0.5 * sum);
+ }
+}
diff --git a/src/main/java/org/apache/commons/math3/distribution/MultivariateRealDistribution.java b/src/main/java/org/apache/commons/math3/distribution/MultivariateRealDistribution.java
new file mode 100644
index 0000000..050cfd5
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/distribution/MultivariateRealDistribution.java
@@ -0,0 +1,75 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math3.distribution;
+
+import org.apache.commons.math3.exception.NotStrictlyPositiveException;
+
+/**
+ * Base interface for multivariate distributions on the reals.
+ *
+ * <p>This is based largely on the RealDistribution interface, but cumulative distribution functions
+ * are not required because they are often quite difficult to compute for multivariate
+ * distributions.
+ *
+ * @since 3.1
+ */
+public interface MultivariateRealDistribution {
+ /**
+ * Returns the probability density function (PDF) of this distribution evaluated at the
+ * specified point {@code x}. In general, the PDF is the derivative of the cumulative
+ * distribution function. If the derivative does not exist at {@code x}, then an appropriate
+ * replacement should be returned, e.g. {@code Double.POSITIVE_INFINITY}, {@code Double.NaN}, or
+ * the limit inferior or limit superior of the difference quotient.
+ *
+ * @param x Point at which the PDF is evaluated.
+ * @return the value of the probability density function at point {@code x}.
+ */
+ double density(double[] x);
+
+ /**
+ * Reseeds the random generator used to generate samples.
+ *
+ * @param seed Seed with which to initialize the random number generator.
+ */
+ void reseedRandomGenerator(long seed);
+
+ /**
+ * Gets the number of random variables of the distribution. It is the size of the array returned
+ * by the {@link #sample() sample} method.
+ *
+ * @return the number of variables.
+ */
+ int getDimension();
+
+ /**
+ * Generates a random value vector sampled from this distribution.
+ *
+ * @return a random value vector.
+ */
+ double[] sample();
+
+ /**
+ * Generates a list of a random value vectors from the distribution.
+ *
+ * @param sampleSize the number of random vectors to generate.
+ * @return an array representing the random samples.
+ * @throws org.apache.commons.math3.exception.NotStrictlyPositiveException if {@code sampleSize}
+ * is not positive.
+ * @see #sample()
+ */
+ double[][] sample(int sampleSize) throws NotStrictlyPositiveException;
+}
diff --git a/src/main/java/org/apache/commons/math3/distribution/NakagamiDistribution.java b/src/main/java/org/apache/commons/math3/distribution/NakagamiDistribution.java
new file mode 100644
index 0000000..298cb30
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/distribution/NakagamiDistribution.java
@@ -0,0 +1,192 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math3.distribution;
+
+import org.apache.commons.math3.exception.NotStrictlyPositiveException;
+import org.apache.commons.math3.exception.NumberIsTooSmallException;
+import org.apache.commons.math3.exception.util.LocalizedFormats;
+import org.apache.commons.math3.random.RandomGenerator;
+import org.apache.commons.math3.random.Well19937c;
+import org.apache.commons.math3.special.Gamma;
+import org.apache.commons.math3.util.FastMath;
+
+/**
+ * This class implements the Nakagami distribution.
+ *
+ * @see <a href="http://en.wikipedia.org/wiki/Nakagami_distribution">Nakagami Distribution
+ * (Wikipedia)</a>
+ * @since 3.4
+ */
+public class NakagamiDistribution extends AbstractRealDistribution {
+
+ /** Default inverse cumulative probability accuracy. */
+ public static final double DEFAULT_INVERSE_ABSOLUTE_ACCURACY = 1e-9;
+
+ /** Serializable version identifier. */
+ private static final long serialVersionUID = 20141003;
+
+ /** The shape parameter. */
+ private final double mu;
+
+ /** The scale parameter. */
+ private final double omega;
+
+ /** Inverse cumulative probability accuracy. */
+ private final double inverseAbsoluteAccuracy;
+
+ /**
+ * Build a new instance.
+ *
+ * <p><b>Note:</b> this constructor will implicitly create an instance of {@link Well19937c} as
+ * random generator to be used for sampling only (see {@link #sample()} and {@link
+ * #sample(int)}). In case no sampling is needed for the created distribution, it is advised to
+ * pass {@code null} as random generator via the appropriate constructors to avoid the
+ * additional initialisation overhead.
+ *
+ * @param mu shape parameter
+ * @param omega scale parameter (must be positive)
+ * @throws NumberIsTooSmallException if {@code mu < 0.5}
+ * @throws NotStrictlyPositiveException if {@code omega <= 0}
+ */
+ public NakagamiDistribution(double mu, double omega) {
+ this(mu, omega, DEFAULT_INVERSE_ABSOLUTE_ACCURACY);
+ }
+
+ /**
+ * Build a new instance.
+ *
+ * <p><b>Note:</b> this constructor will implicitly create an instance of {@link Well19937c} as
+ * random generator to be used for sampling only (see {@link #sample()} and {@link
+ * #sample(int)}). In case no sampling is needed for the created distribution, it is advised to
+ * pass {@code null} as random generator via the appropriate constructors to avoid the
+ * additional initialisation overhead.
+ *
+ * @param mu shape parameter
+ * @param omega scale parameter (must be positive)
+ * @param inverseAbsoluteAccuracy the maximum absolute error in inverse cumulative probability
+ * estimates (defaults to {@link #DEFAULT_INVERSE_ABSOLUTE_ACCURACY}).
+ * @throws NumberIsTooSmallException if {@code mu < 0.5}
+ * @throws NotStrictlyPositiveException if {@code omega <= 0}
+ */
+ public NakagamiDistribution(double mu, double omega, double inverseAbsoluteAccuracy) {
+ this(new Well19937c(), mu, omega, inverseAbsoluteAccuracy);
+ }
+
+ /**
+ * Build a new instance.
+ *
+ * @param rng Random number generator
+ * @param mu shape parameter
+ * @param omega scale parameter (must be positive)
+ * @param inverseAbsoluteAccuracy the maximum absolute error in inverse cumulative probability
+ * estimates (defaults to {@link #DEFAULT_INVERSE_ABSOLUTE_ACCURACY}).
+ * @throws NumberIsTooSmallException if {@code mu < 0.5}
+ * @throws NotStrictlyPositiveException if {@code omega <= 0}
+ */
+ public NakagamiDistribution(
+ RandomGenerator rng, double mu, double omega, double inverseAbsoluteAccuracy) {
+ super(rng);
+
+ if (mu < 0.5) {
+ throw new NumberIsTooSmallException(mu, 0.5, true);
+ }
+ if (omega <= 0) {
+ throw new NotStrictlyPositiveException(LocalizedFormats.NOT_POSITIVE_SCALE, omega);
+ }
+
+ this.mu = mu;
+ this.omega = omega;
+ this.inverseAbsoluteAccuracy = inverseAbsoluteAccuracy;
+ }
+
+ /**
+ * Access the shape parameter, {@code mu}.
+ *
+ * @return the shape parameter.
+ */
+ public double getShape() {
+ return mu;
+ }
+
+ /**
+ * Access the scale parameter, {@code omega}.
+ *
+ * @return the scale parameter.
+ */
+ public double getScale() {
+ return omega;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ protected double getSolverAbsoluteAccuracy() {
+ return inverseAbsoluteAccuracy;
+ }
+
+ /** {@inheritDoc} */
+ public double density(double x) {
+ if (x <= 0) {
+ return 0.0;
+ }
+ return 2.0
+ * FastMath.pow(mu, mu)
+ / (Gamma.gamma(mu) * FastMath.pow(omega, mu))
+ * FastMath.pow(x, 2 * mu - 1)
+ * FastMath.exp(-mu * x * x / omega);
+ }
+
+ /** {@inheritDoc} */
+ public double cumulativeProbability(double x) {
+ return Gamma.regularizedGammaP(mu, mu * x * x / omega);
+ }
+
+ /** {@inheritDoc} */
+ public double getNumericalMean() {
+ return Gamma.gamma(mu + 0.5) / Gamma.gamma(mu) * FastMath.sqrt(omega / mu);
+ }
+
+ /** {@inheritDoc} */
+ public double getNumericalVariance() {
+ double v = Gamma.gamma(mu + 0.5) / Gamma.gamma(mu);
+ return omega * (1 - 1 / mu * v * v);
+ }
+
+ /** {@inheritDoc} */
+ public double getSupportLowerBound() {
+ return 0;
+ }
+
+ /** {@inheritDoc} */
+ public double getSupportUpperBound() {
+ return Double.POSITIVE_INFINITY;
+ }
+
+ /** {@inheritDoc} */
+ public boolean isSupportLowerBoundInclusive() {
+ return true;
+ }
+
+ /** {@inheritDoc} */
+ public boolean isSupportUpperBoundInclusive() {
+ return false;
+ }
+
+ /** {@inheritDoc} */
+ public boolean isSupportConnected() {
+ return true;
+ }
+}
diff --git a/src/main/java/org/apache/commons/math3/distribution/NormalDistribution.java b/src/main/java/org/apache/commons/math3/distribution/NormalDistribution.java
new file mode 100644
index 0000000..a2bab56
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/distribution/NormalDistribution.java
@@ -0,0 +1,308 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math3.distribution;
+
+import org.apache.commons.math3.exception.NotStrictlyPositiveException;
+import org.apache.commons.math3.exception.NumberIsTooLargeException;
+import org.apache.commons.math3.exception.OutOfRangeException;
+import org.apache.commons.math3.exception.util.LocalizedFormats;
+import org.apache.commons.math3.random.RandomGenerator;
+import org.apache.commons.math3.random.Well19937c;
+import org.apache.commons.math3.special.Erf;
+import org.apache.commons.math3.util.FastMath;
+
+/**
+ * Implementation of the normal (gaussian) distribution.
+ *
+ * @see <a href="http://en.wikipedia.org/wiki/Normal_distribution">Normal distribution
+ * (Wikipedia)</a>
+ * @see <a href="http://mathworld.wolfram.com/NormalDistribution.html">Normal distribution
+ * (MathWorld)</a>
+ */
+public class NormalDistribution extends AbstractRealDistribution {
+ /**
+ * Default inverse cumulative probability accuracy.
+ *
+ * @since 2.1
+ */
+ public static final double DEFAULT_INVERSE_ABSOLUTE_ACCURACY = 1e-9;
+
+ /** Serializable version identifier. */
+ private static final long serialVersionUID = 8589540077390120676L;
+
+ /** &radic;(2) */
+ private static final double SQRT2 = FastMath.sqrt(2.0);
+
+ /** Mean of this distribution. */
+ private final double mean;
+
+ /** Standard deviation of this distribution. */
+ private final double standardDeviation;
+
+ /** The value of {@code log(sd) + 0.5*log(2*pi)} stored for faster computation. */
+ private final double logStandardDeviationPlusHalfLog2Pi;
+
+ /** Inverse cumulative probability accuracy. */
+ private final double solverAbsoluteAccuracy;
+
+ /**
+ * Create a normal distribution with mean equal to zero and standard deviation equal to one.
+ *
+ * <p><b>Note:</b> this constructor will implicitly create an instance of {@link Well19937c} as
+ * random generator to be used for sampling only (see {@link #sample()} and {@link
+ * #sample(int)}). In case no sampling is needed for the created distribution, it is advised to
+ * pass {@code null} as random generator via the appropriate constructors to avoid the
+ * additional initialisation overhead.
+ */
+ public NormalDistribution() {
+ this(0, 1);
+ }
+
+ /**
+ * Create a normal distribution using the given mean and standard deviation.
+ *
+ * <p><b>Note:</b> this constructor will implicitly create an instance of {@link Well19937c} as
+ * random generator to be used for sampling only (see {@link #sample()} and {@link
+ * #sample(int)}). In case no sampling is needed for the created distribution, it is advised to
+ * pass {@code null} as random generator via the appropriate constructors to avoid the
+ * additional initialisation overhead.
+ *
+ * @param mean Mean for this distribution.
+ * @param sd Standard deviation for this distribution.
+ * @throws NotStrictlyPositiveException if {@code sd <= 0}.
+ */
+ public NormalDistribution(double mean, double sd) throws NotStrictlyPositiveException {
+ this(mean, sd, DEFAULT_INVERSE_ABSOLUTE_ACCURACY);
+ }
+
+ /**
+ * Create a normal distribution using the given mean, standard deviation and inverse cumulative
+ * distribution accuracy.
+ *
+ * <p><b>Note:</b> this constructor will implicitly create an instance of {@link Well19937c} as
+ * random generator to be used for sampling only (see {@link #sample()} and {@link
+ * #sample(int)}). In case no sampling is needed for the created distribution, it is advised to
+ * pass {@code null} as random generator via the appropriate constructors to avoid the
+ * additional initialisation overhead.
+ *
+ * @param mean Mean for this distribution.
+ * @param sd Standard deviation for this distribution.
+ * @param inverseCumAccuracy Inverse cumulative probability accuracy.
+ * @throws NotStrictlyPositiveException if {@code sd <= 0}.
+ * @since 2.1
+ */
+ public NormalDistribution(double mean, double sd, double inverseCumAccuracy)
+ throws NotStrictlyPositiveException {
+ this(new Well19937c(), mean, sd, inverseCumAccuracy);
+ }
+
+ /**
+ * Creates a normal distribution.
+ *
+ * @param rng Random number generator.
+ * @param mean Mean for this distribution.
+ * @param sd Standard deviation for this distribution.
+ * @throws NotStrictlyPositiveException if {@code sd <= 0}.
+ * @since 3.3
+ */
+ public NormalDistribution(RandomGenerator rng, double mean, double sd)
+ throws NotStrictlyPositiveException {
+ this(rng, mean, sd, DEFAULT_INVERSE_ABSOLUTE_ACCURACY);
+ }
+
+ /**
+ * Creates a normal distribution.
+ *
+ * @param rng Random number generator.
+ * @param mean Mean for this distribution.
+ * @param sd Standard deviation for this distribution.
+ * @param inverseCumAccuracy Inverse cumulative probability accuracy.
+ * @throws NotStrictlyPositiveException if {@code sd <= 0}.
+ * @since 3.1
+ */
+ public NormalDistribution(
+ RandomGenerator rng, double mean, double sd, double inverseCumAccuracy)
+ throws NotStrictlyPositiveException {
+ super(rng);
+
+ if (sd <= 0) {
+ throw new NotStrictlyPositiveException(LocalizedFormats.STANDARD_DEVIATION, sd);
+ }
+
+ this.mean = mean;
+ standardDeviation = sd;
+ logStandardDeviationPlusHalfLog2Pi = FastMath.log(sd) + 0.5 * FastMath.log(2 * FastMath.PI);
+ solverAbsoluteAccuracy = inverseCumAccuracy;
+ }
+
+ /**
+ * Access the mean.
+ *
+ * @return the mean for this distribution.
+ */
+ public double getMean() {
+ return mean;
+ }
+
+ /**
+ * Access the standard deviation.
+ *
+ * @return the standard deviation for this distribution.
+ */
+ public double getStandardDeviation() {
+ return standardDeviation;
+ }
+
+ /** {@inheritDoc} */
+ public double density(double x) {
+ return FastMath.exp(logDensity(x));
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public double logDensity(double x) {
+ final double x0 = x - mean;
+ final double x1 = x0 / standardDeviation;
+ return -0.5 * x1 * x1 - logStandardDeviationPlusHalfLog2Pi;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * <p>If {@code x} is more than 40 standard deviations from the mean, 0 or 1 is returned, as in
+ * these cases the actual value is within {@code Double.MIN_VALUE} of 0 or 1.
+ */
+ public double cumulativeProbability(double x) {
+ final double dev = x - mean;
+ if (FastMath.abs(dev) > 40 * standardDeviation) {
+ return dev < 0 ? 0.0d : 1.0d;
+ }
+ return 0.5 * Erf.erfc(-dev / (standardDeviation * SQRT2));
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @since 3.2
+ */
+ @Override
+ public double inverseCumulativeProbability(final double p) throws OutOfRangeException {
+ if (p < 0.0 || p > 1.0) {
+ throw new OutOfRangeException(p, 0, 1);
+ }
+ return mean + standardDeviation * SQRT2 * Erf.erfInv(2 * p - 1);
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @deprecated See {@link RealDistribution#cumulativeProbability(double,double)}
+ */
+ @Override
+ @Deprecated
+ public double cumulativeProbability(double x0, double x1) throws NumberIsTooLargeException {
+ return probability(x0, x1);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public double probability(double x0, double x1) throws NumberIsTooLargeException {
+ if (x0 > x1) {
+ throw new NumberIsTooLargeException(
+ LocalizedFormats.LOWER_ENDPOINT_ABOVE_UPPER_ENDPOINT, x0, x1, true);
+ }
+ final double denom = standardDeviation * SQRT2;
+ final double v0 = (x0 - mean) / denom;
+ final double v1 = (x1 - mean) / denom;
+ return 0.5 * Erf.erf(v0, v1);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ protected double getSolverAbsoluteAccuracy() {
+ return solverAbsoluteAccuracy;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * <p>For mean parameter {@code mu}, the mean is {@code mu}.
+ */
+ public double getNumericalMean() {
+ return getMean();
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * <p>For standard deviation parameter {@code s}, the variance is {@code s^2}.
+ */
+ public double getNumericalVariance() {
+ final double s = getStandardDeviation();
+ return s * s;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * <p>The lower bound of the support is always negative infinity no matter the parameters.
+ *
+ * @return lower bound of the support (always {@code Double.NEGATIVE_INFINITY})
+ */
+ public double getSupportLowerBound() {
+ return Double.NEGATIVE_INFINITY;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * <p>The upper bound of the support is always positive infinity no matter the parameters.
+ *
+ * @return upper bound of the support (always {@code Double.POSITIVE_INFINITY})
+ */
+ public double getSupportUpperBound() {
+ return Double.POSITIVE_INFINITY;
+ }
+
+ /** {@inheritDoc} */
+ public boolean isSupportLowerBoundInclusive() {
+ return false;
+ }
+
+ /** {@inheritDoc} */
+ public boolean isSupportUpperBoundInclusive() {
+ return false;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * <p>The support of this distribution is connected.
+ *
+ * @return {@code true}
+ */
+ public boolean isSupportConnected() {
+ return true;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public double sample() {
+ return standardDeviation * random.nextGaussian() + mean;
+ }
+}
diff --git a/src/main/java/org/apache/commons/math3/distribution/ParetoDistribution.java b/src/main/java/org/apache/commons/math3/distribution/ParetoDistribution.java
new file mode 100644
index 0000000..c4d5d58
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/distribution/ParetoDistribution.java
@@ -0,0 +1,315 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math3.distribution;
+
+import org.apache.commons.math3.exception.NotStrictlyPositiveException;
+import org.apache.commons.math3.exception.NumberIsTooLargeException;
+import org.apache.commons.math3.exception.util.LocalizedFormats;
+import org.apache.commons.math3.random.RandomGenerator;
+import org.apache.commons.math3.random.Well19937c;
+import org.apache.commons.math3.util.FastMath;
+
+/**
+ * Implementation of the Pareto distribution.
+ *
+ * <p><strong>Parameters:</strong> The probability distribution function of {@code X} is given by
+ * (for {@code x >= k}):
+ *
+ * <pre>
+ * α * k^α / x^(α + 1)
+ * </pre>
+ *
+ * <p>
+ *
+ * <ul>
+ * <li>{@code k} is the <em>scale</em> parameter: this is the minimum possible value of {@code X},
+ * <li>{@code α} is the <em>shape</em> parameter: this is the Pareto index
+ * </ul>
+ *
+ * @see <a href="http://en.wikipedia.org/wiki/Pareto_distribution">Pareto distribution
+ * (Wikipedia)</a>
+ * @see <a href="http://mathworld.wolfram.com/ParetoDistribution.html">Pareto distribution
+ * (MathWorld)</a>
+ * @since 3.3
+ */
+public class ParetoDistribution extends AbstractRealDistribution {
+
+ /** Default inverse cumulative probability accuracy. */
+ public static final double DEFAULT_INVERSE_ABSOLUTE_ACCURACY = 1e-9;
+
+ /** Serializable version identifier. */
+ private static final long serialVersionUID = 20130424;
+
+ /** The scale parameter of this distribution. */
+ private final double scale;
+
+ /** The shape parameter of this distribution. */
+ private final double shape;
+
+ /** Inverse cumulative probability accuracy. */
+ private final double solverAbsoluteAccuracy;
+
+ /** Create a Pareto distribution with a scale of {@code 1} and a shape of {@code 1}. */
+ public ParetoDistribution() {
+ this(1, 1);
+ }
+
+ /**
+ * Create a Pareto distribution using the specified scale and shape.
+ *
+ * <p><b>Note:</b> this constructor will implicitly create an instance of {@link Well19937c} as
+ * random generator to be used for sampling only (see {@link #sample()} and {@link
+ * #sample(int)}). In case no sampling is needed for the created distribution, it is advised to
+ * pass {@code null} as random generator via the appropriate constructors to avoid the
+ * additional initialisation overhead.
+ *
+ * @param scale the scale parameter of this distribution
+ * @param shape the shape parameter of this distribution
+ * @throws NotStrictlyPositiveException if {@code scale <= 0} or {@code shape <= 0}.
+ */
+ public ParetoDistribution(double scale, double shape) throws NotStrictlyPositiveException {
+ this(scale, shape, DEFAULT_INVERSE_ABSOLUTE_ACCURACY);
+ }
+
+ /**
+ * Create a Pareto distribution using the specified scale, shape and inverse cumulative
+ * distribution accuracy.
+ *
+ * <p><b>Note:</b> this constructor will implicitly create an instance of {@link Well19937c} as
+ * random generator to be used for sampling only (see {@link #sample()} and {@link
+ * #sample(int)}). In case no sampling is needed for the created distribution, it is advised to
+ * pass {@code null} as random generator via the appropriate constructors to avoid the
+ * additional initialisation overhead.
+ *
+ * @param scale the scale parameter of this distribution
+ * @param shape the shape parameter of this distribution
+ * @param inverseCumAccuracy Inverse cumulative probability accuracy.
+ * @throws NotStrictlyPositiveException if {@code scale <= 0} or {@code shape <= 0}.
+ */
+ public ParetoDistribution(double scale, double shape, double inverseCumAccuracy)
+ throws NotStrictlyPositiveException {
+ this(new Well19937c(), scale, shape, inverseCumAccuracy);
+ }
+
+ /**
+ * Creates a Pareto distribution.
+ *
+ * @param rng Random number generator.
+ * @param scale Scale parameter of this distribution.
+ * @param shape Shape parameter of this distribution.
+ * @throws NotStrictlyPositiveException if {@code scale <= 0} or {@code shape <= 0}.
+ */
+ public ParetoDistribution(RandomGenerator rng, double scale, double shape)
+ throws NotStrictlyPositiveException {
+ this(rng, scale, shape, DEFAULT_INVERSE_ABSOLUTE_ACCURACY);
+ }
+
+ /**
+ * Creates a Pareto distribution.
+ *
+ * @param rng Random number generator.
+ * @param scale Scale parameter of this distribution.
+ * @param shape Shape parameter of this distribution.
+ * @param inverseCumAccuracy Inverse cumulative probability accuracy.
+ * @throws NotStrictlyPositiveException if {@code scale <= 0} or {@code shape <= 0}.
+ */
+ public ParetoDistribution(
+ RandomGenerator rng, double scale, double shape, double inverseCumAccuracy)
+ throws NotStrictlyPositiveException {
+ super(rng);
+
+ if (scale <= 0) {
+ throw new NotStrictlyPositiveException(LocalizedFormats.SCALE, scale);
+ }
+
+ if (shape <= 0) {
+ throw new NotStrictlyPositiveException(LocalizedFormats.SHAPE, shape);
+ }
+
+ this.scale = scale;
+ this.shape = shape;
+ this.solverAbsoluteAccuracy = inverseCumAccuracy;
+ }
+
+ /**
+ * Returns the scale parameter of this distribution.
+ *
+ * @return the scale parameter
+ */
+ public double getScale() {
+ return scale;
+ }
+
+ /**
+ * Returns the shape parameter of this distribution.
+ *
+ * @return the shape parameter
+ */
+ public double getShape() {
+ return shape;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * <p>For scale {@code k}, and shape {@code α} of this distribution, the PDF is given by
+ *
+ * <ul>
+ * <li>{@code 0} if {@code x < k},
+ * <li>{@code α * k^α / x^(α + 1)} otherwise.
+ * </ul>
+ */
+ public double density(double x) {
+ if (x < scale) {
+ return 0;
+ }
+ return FastMath.pow(scale, shape) / FastMath.pow(x, shape + 1) * shape;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * <p>See documentation of {@link #density(double)} for computation details.
+ */
+ @Override
+ public double logDensity(double x) {
+ if (x < scale) {
+ return Double.NEGATIVE_INFINITY;
+ }
+ return FastMath.log(scale) * shape - FastMath.log(x) * (shape + 1) + FastMath.log(shape);
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * <p>For scale {@code k}, and shape {@code α} of this distribution, the CDF is given by
+ *
+ * <ul>
+ * <li>{@code 0} if {@code x < k},
+ * <li>{@code 1 - (k / x)^α} otherwise.
+ * </ul>
+ */
+ public double cumulativeProbability(double x) {
+ if (x <= scale) {
+ return 0;
+ }
+ return 1 - FastMath.pow(scale / x, shape);
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @deprecated See {@link RealDistribution#cumulativeProbability(double,double)}
+ */
+ @Override
+ @Deprecated
+ public double cumulativeProbability(double x0, double x1) throws NumberIsTooLargeException {
+ return probability(x0, x1);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ protected double getSolverAbsoluteAccuracy() {
+ return solverAbsoluteAccuracy;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * <p>For scale {@code k} and shape {@code α}, the mean is given by
+ *
+ * <ul>
+ * <li>{@code ∞} if {@code α <= 1},
+ * <li>{@code α * k / (α - 1)} otherwise.
+ * </ul>
+ */
+ public double getNumericalMean() {
+ if (shape <= 1) {
+ return Double.POSITIVE_INFINITY;
+ }
+ return shape * scale / (shape - 1);
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * <p>For scale {@code k} and shape {@code α}, the variance is given by
+ *
+ * <ul>
+ * <li>{@code ∞} if {@code 1 < α <= 2},
+ * <li>{@code k^2 * α / ((α - 1)^2 * (α - 2))} otherwise.
+ * </ul>
+ */
+ public double getNumericalVariance() {
+ if (shape <= 2) {
+ return Double.POSITIVE_INFINITY;
+ }
+ double s = shape - 1;
+ return scale * scale * shape / (s * s) / (shape - 2);
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * <p>The lower bound of the support is equal to the scale parameter {@code k}.
+ *
+ * @return lower bound of the support
+ */
+ public double getSupportLowerBound() {
+ return scale;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * <p>The upper bound of the support is always positive infinity no matter the parameters.
+ *
+ * @return upper bound of the support (always {@code Double.POSITIVE_INFINITY})
+ */
+ public double getSupportUpperBound() {
+ return Double.POSITIVE_INFINITY;
+ }
+
+ /** {@inheritDoc} */
+ public boolean isSupportLowerBoundInclusive() {
+ return true;
+ }
+
+ /** {@inheritDoc} */
+ public boolean isSupportUpperBoundInclusive() {
+ return false;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * <p>The support of this distribution is connected.
+ *
+ * @return {@code true}
+ */
+ public boolean isSupportConnected() {
+ return true;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public double sample() {
+ final double n = random.nextDouble();
+ return scale / FastMath.pow(n, 1 / shape);
+ }
+}
diff --git a/src/main/java/org/apache/commons/math3/distribution/PascalDistribution.java b/src/main/java/org/apache/commons/math3/distribution/PascalDistribution.java
new file mode 100644
index 0000000..c850f8f
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/distribution/PascalDistribution.java
@@ -0,0 +1,240 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math3.distribution;
+
+import org.apache.commons.math3.exception.NotStrictlyPositiveException;
+import org.apache.commons.math3.exception.OutOfRangeException;
+import org.apache.commons.math3.exception.util.LocalizedFormats;
+import org.apache.commons.math3.random.RandomGenerator;
+import org.apache.commons.math3.random.Well19937c;
+import org.apache.commons.math3.special.Beta;
+import org.apache.commons.math3.util.CombinatoricsUtils;
+import org.apache.commons.math3.util.FastMath;
+
+/**
+ * Implementation of the Pascal distribution. The Pascal distribution is a special case of the
+ * Negative Binomial distribution where the number of successes parameter is an integer.
+ *
+ * <p>There are various ways to express the probability mass and distribution functions for the
+ * Pascal distribution. The present implementation represents the distribution of the number of
+ * failures before {@code r} successes occur. This is the convention adopted in e.g. <a
+ * href="http://mathworld.wolfram.com/NegativeBinomialDistribution.html">MathWorld</a>, but
+ * <em>not</em> in <a
+ * href="http://en.wikipedia.org/wiki/Negative_binomial_distribution">Wikipedia</a>.
+ *
+ * <p>For a random variable {@code X} whose values are distributed according to this distribution,
+ * the probability mass function is given by<br>
+ * {@code P(X = k) = C(k + r - 1, r - 1) * p^r * (1 - p)^k,}<br>
+ * where {@code r} is the number of successes, {@code p} is the probability of success, and {@code
+ * X} is the total number of failures. {@code C(n, k)} is the binomial coefficient ({@code n} choose
+ * {@code k}). The mean and variance of {@code X} are<br>
+ * {@code E(X) = (1 - p) * r / p, var(X) = (1 - p) * r / p^2.}<br>
+ * Finally, the cumulative distribution function is given by<br>
+ * {@code P(X <= k) = I(p, r, k + 1)}, where I is the regularized incomplete Beta function.
+ *
+ * @see <a href="http://en.wikipedia.org/wiki/Negative_binomial_distribution">Negative binomial
+ * distribution (Wikipedia)</a>
+ * @see <a href="http://mathworld.wolfram.com/NegativeBinomialDistribution.html">Negative binomial
+ * distribution (MathWorld)</a>
+ * @since 1.2 (changed to concrete class in 3.0)
+ */
+public class PascalDistribution extends AbstractIntegerDistribution {
+ /** Serializable version identifier. */
+ private static final long serialVersionUID = 6751309484392813623L;
+
+ /** The number of successes. */
+ private final int numberOfSuccesses;
+
+ /** The probability of success. */
+ private final double probabilityOfSuccess;
+
+ /**
+ * The value of {@code log(p)}, where {@code p} is the probability of success, stored for faster
+ * computation.
+ */
+ private final double logProbabilityOfSuccess;
+
+ /**
+ * The value of {@code log(1-p)}, where {@code p} is the probability of success, stored for
+ * faster computation.
+ */
+ private final double log1mProbabilityOfSuccess;
+
+ /**
+ * Create a Pascal distribution with the given number of successes and probability of success.
+ *
+ * <p><b>Note:</b> this constructor will implicitly create an instance of {@link Well19937c} as
+ * random generator to be used for sampling only (see {@link #sample()} and {@link
+ * #sample(int)}). In case no sampling is needed for the created distribution, it is advised to
+ * pass {@code null} as random generator via the appropriate constructors to avoid the
+ * additional initialisation overhead.
+ *
+ * @param r Number of successes.
+ * @param p Probability of success.
+ * @throws NotStrictlyPositiveException if the number of successes is not positive
+ * @throws OutOfRangeException if the probability of success is not in the range {@code [0, 1]}.
+ */
+ public PascalDistribution(int r, double p)
+ throws NotStrictlyPositiveException, OutOfRangeException {
+ this(new Well19937c(), r, p);
+ }
+
+ /**
+ * Create a Pascal distribution with the given number of successes and probability of success.
+ *
+ * @param rng Random number generator.
+ * @param r Number of successes.
+ * @param p Probability of success.
+ * @throws NotStrictlyPositiveException if the number of successes is not positive
+ * @throws OutOfRangeException if the probability of success is not in the range {@code [0, 1]}.
+ * @since 3.1
+ */
+ public PascalDistribution(RandomGenerator rng, int r, double p)
+ throws NotStrictlyPositiveException, OutOfRangeException {
+ super(rng);
+
+ if (r <= 0) {
+ throw new NotStrictlyPositiveException(LocalizedFormats.NUMBER_OF_SUCCESSES, r);
+ }
+ if (p < 0 || p > 1) {
+ throw new OutOfRangeException(p, 0, 1);
+ }
+
+ numberOfSuccesses = r;
+ probabilityOfSuccess = p;
+ logProbabilityOfSuccess = FastMath.log(p);
+ log1mProbabilityOfSuccess = FastMath.log1p(-p);
+ }
+
+ /**
+ * Access the number of successes for this distribution.
+ *
+ * @return the number of successes.
+ */
+ public int getNumberOfSuccesses() {
+ return numberOfSuccesses;
+ }
+
+ /**
+ * Access the probability of success for this distribution.
+ *
+ * @return the probability of success.
+ */
+ public double getProbabilityOfSuccess() {
+ return probabilityOfSuccess;
+ }
+
+ /** {@inheritDoc} */
+ public double probability(int x) {
+ double ret;
+ if (x < 0) {
+ ret = 0.0;
+ } else {
+ ret =
+ CombinatoricsUtils.binomialCoefficientDouble(
+ x + numberOfSuccesses - 1, numberOfSuccesses - 1)
+ * FastMath.pow(probabilityOfSuccess, numberOfSuccesses)
+ * FastMath.pow(1.0 - probabilityOfSuccess, x);
+ }
+ return ret;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public double logProbability(int x) {
+ double ret;
+ if (x < 0) {
+ ret = Double.NEGATIVE_INFINITY;
+ } else {
+ ret =
+ CombinatoricsUtils.binomialCoefficientLog(
+ x + numberOfSuccesses - 1, numberOfSuccesses - 1)
+ + logProbabilityOfSuccess * numberOfSuccesses
+ + log1mProbabilityOfSuccess * x;
+ }
+ return ret;
+ }
+
+ /** {@inheritDoc} */
+ public double cumulativeProbability(int x) {
+ double ret;
+ if (x < 0) {
+ ret = 0.0;
+ } else {
+ ret = Beta.regularizedBeta(probabilityOfSuccess, numberOfSuccesses, x + 1.0);
+ }
+ return ret;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * <p>For number of successes {@code r} and probability of success {@code p}, the mean is {@code
+ * r * (1 - p) / p}.
+ */
+ public double getNumericalMean() {
+ final double p = getProbabilityOfSuccess();
+ final double r = getNumberOfSuccesses();
+ return (r * (1 - p)) / p;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * <p>For number of successes {@code r} and probability of success {@code p}, the variance is
+ * {@code r * (1 - p) / p^2}.
+ */
+ public double getNumericalVariance() {
+ final double p = getProbabilityOfSuccess();
+ final double r = getNumberOfSuccesses();
+ return r * (1 - p) / (p * p);
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * <p>The lower bound of the support is always 0 no matter the parameters.
+ *
+ * @return lower bound of the support (always 0)
+ */
+ public int getSupportLowerBound() {
+ return 0;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * <p>The upper bound of the support is always positive infinity no matter the parameters.
+ * Positive infinity is symbolized by {@code Integer.MAX_VALUE}.
+ *
+ * @return upper bound of the support (always {@code Integer.MAX_VALUE} for positive infinity)
+ */
+ public int getSupportUpperBound() {
+ return Integer.MAX_VALUE;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * <p>The support of this distribution is connected.
+ *
+ * @return {@code true}
+ */
+ public boolean isSupportConnected() {
+ return true;
+ }
+}
diff --git a/src/main/java/org/apache/commons/math3/distribution/PoissonDistribution.java b/src/main/java/org/apache/commons/math3/distribution/PoissonDistribution.java
new file mode 100644
index 0000000..7d9eab3
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/distribution/PoissonDistribution.java
@@ -0,0 +1,394 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math3.distribution;
+
+import org.apache.commons.math3.exception.NotStrictlyPositiveException;
+import org.apache.commons.math3.exception.util.LocalizedFormats;
+import org.apache.commons.math3.random.RandomGenerator;
+import org.apache.commons.math3.random.Well19937c;
+import org.apache.commons.math3.special.Gamma;
+import org.apache.commons.math3.util.CombinatoricsUtils;
+import org.apache.commons.math3.util.FastMath;
+import org.apache.commons.math3.util.MathUtils;
+
+/**
+ * Implementation of the Poisson distribution.
+ *
+ * @see <a href="http://en.wikipedia.org/wiki/Poisson_distribution">Poisson distribution
+ * (Wikipedia)</a>
+ * @see <a href="http://mathworld.wolfram.com/PoissonDistribution.html">Poisson distribution
+ * (MathWorld)</a>
+ */
+public class PoissonDistribution extends AbstractIntegerDistribution {
+ /**
+ * Default maximum number of iterations for cumulative probability calculations.
+ *
+ * @since 2.1
+ */
+ public static final int DEFAULT_MAX_ITERATIONS = 10000000;
+
+ /**
+ * Default convergence criterion.
+ *
+ * @since 2.1
+ */
+ public static final double DEFAULT_EPSILON = 1e-12;
+
+ /** Serializable version identifier. */
+ private static final long serialVersionUID = -3349935121172596109L;
+
+ /** Distribution used to compute normal approximation. */
+ private final NormalDistribution normal;
+
+ /** Distribution needed for the {@link #sample()} method. */
+ private final ExponentialDistribution exponential;
+
+ /** Mean of the distribution. */
+ private final double mean;
+
+ /**
+ * Maximum number of iterations for cumulative probability. Cumulative probabilities are
+ * estimated using either Lanczos series approximation of {@link Gamma#regularizedGammaP(double,
+ * double, double, int)} or continued fraction approximation of {@link
+ * Gamma#regularizedGammaQ(double, double, double, int)}.
+ */
+ private final int maxIterations;
+
+ /** Convergence criterion for cumulative probability. */
+ private final double epsilon;
+
+ /**
+ * Creates a new Poisson distribution with specified mean.
+ *
+ * <p><b>Note:</b> this constructor will implicitly create an instance of {@link Well19937c} as
+ * random generator to be used for sampling only (see {@link #sample()} and {@link
+ * #sample(int)}). In case no sampling is needed for the created distribution, it is advised to
+ * pass {@code null} as random generator via the appropriate constructors to avoid the
+ * additional initialisation overhead.
+ *
+ * @param p the Poisson mean
+ * @throws NotStrictlyPositiveException if {@code p <= 0}.
+ */
+ public PoissonDistribution(double p) throws NotStrictlyPositiveException {
+ this(p, DEFAULT_EPSILON, DEFAULT_MAX_ITERATIONS);
+ }
+
+ /**
+ * Creates a new Poisson distribution with specified mean, convergence criterion and maximum
+ * number of iterations.
+ *
+ * <p><b>Note:</b> this constructor will implicitly create an instance of {@link Well19937c} as
+ * random generator to be used for sampling only (see {@link #sample()} and {@link
+ * #sample(int)}). In case no sampling is needed for the created distribution, it is advised to
+ * pass {@code null} as random generator via the appropriate constructors to avoid the
+ * additional initialisation overhead.
+ *
+ * @param p Poisson mean.
+ * @param epsilon Convergence criterion for cumulative probabilities.
+ * @param maxIterations the maximum number of iterations for cumulative probabilities.
+ * @throws NotStrictlyPositiveException if {@code p <= 0}.
+ * @since 2.1
+ */
+ public PoissonDistribution(double p, double epsilon, int maxIterations)
+ throws NotStrictlyPositiveException {
+ this(new Well19937c(), p, epsilon, maxIterations);
+ }
+
+ /**
+ * Creates a new Poisson distribution with specified mean, convergence criterion and maximum
+ * number of iterations.
+ *
+ * @param rng Random number generator.
+ * @param p Poisson mean.
+ * @param epsilon Convergence criterion for cumulative probabilities.
+ * @param maxIterations the maximum number of iterations for cumulative probabilities.
+ * @throws NotStrictlyPositiveException if {@code p <= 0}.
+ * @since 3.1
+ */
+ public PoissonDistribution(RandomGenerator rng, double p, double epsilon, int maxIterations)
+ throws NotStrictlyPositiveException {
+ super(rng);
+
+ if (p <= 0) {
+ throw new NotStrictlyPositiveException(LocalizedFormats.MEAN, p);
+ }
+ mean = p;
+ this.epsilon = epsilon;
+ this.maxIterations = maxIterations;
+
+ // Use the same RNG instance as the parent class.
+ normal =
+ new NormalDistribution(
+ rng,
+ p,
+ FastMath.sqrt(p),
+ NormalDistribution.DEFAULT_INVERSE_ABSOLUTE_ACCURACY);
+ exponential =
+ new ExponentialDistribution(
+ rng, 1, ExponentialDistribution.DEFAULT_INVERSE_ABSOLUTE_ACCURACY);
+ }
+
+ /**
+ * Creates a new Poisson distribution with the specified mean and convergence criterion.
+ *
+ * @param p Poisson mean.
+ * @param epsilon Convergence criterion for cumulative probabilities.
+ * @throws NotStrictlyPositiveException if {@code p <= 0}.
+ * @since 2.1
+ */
+ public PoissonDistribution(double p, double epsilon) throws NotStrictlyPositiveException {
+ this(p, epsilon, DEFAULT_MAX_ITERATIONS);
+ }
+
+ /**
+ * Creates a new Poisson distribution with the specified mean and maximum number of iterations.
+ *
+ * @param p Poisson mean.
+ * @param maxIterations Maximum number of iterations for cumulative probabilities.
+ * @since 2.1
+ */
+ public PoissonDistribution(double p, int maxIterations) {
+ this(p, DEFAULT_EPSILON, maxIterations);
+ }
+
+ /**
+ * Get the mean for the distribution.
+ *
+ * @return the mean for the distribution.
+ */
+ public double getMean() {
+ return mean;
+ }
+
+ /** {@inheritDoc} */
+ public double probability(int x) {
+ final double logProbability = logProbability(x);
+ return logProbability == Double.NEGATIVE_INFINITY ? 0 : FastMath.exp(logProbability);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public double logProbability(int x) {
+ double ret;
+ if (x < 0 || x == Integer.MAX_VALUE) {
+ ret = Double.NEGATIVE_INFINITY;
+ } else if (x == 0) {
+ ret = -mean;
+ } else {
+ ret =
+ -SaddlePointExpansion.getStirlingError(x)
+ - SaddlePointExpansion.getDeviancePart(x, mean)
+ - 0.5 * FastMath.log(MathUtils.TWO_PI)
+ - 0.5 * FastMath.log(x);
+ }
+ return ret;
+ }
+
+ /** {@inheritDoc} */
+ public double cumulativeProbability(int x) {
+ if (x < 0) {
+ return 0;
+ }
+ if (x == Integer.MAX_VALUE) {
+ return 1;
+ }
+ return Gamma.regularizedGammaQ((double) x + 1, mean, epsilon, maxIterations);
+ }
+
+ /**
+ * Calculates the Poisson distribution function using a normal approximation. The {@code N(mean,
+ * sqrt(mean))} distribution is used to approximate the Poisson distribution. The computation
+ * uses "half-correction" (evaluating the normal distribution function at {@code x + 0.5}).
+ *
+ * @param x Upper bound, inclusive.
+ * @return the distribution function value calculated using a normal approximation.
+ */
+ public double normalApproximateProbability(int x) {
+ // calculate the probability using half-correction
+ return normal.cumulativeProbability(x + 0.5);
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * <p>For mean parameter {@code p}, the mean is {@code p}.
+ */
+ public double getNumericalMean() {
+ return getMean();
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * <p>For mean parameter {@code p}, the variance is {@code p}.
+ */
+ public double getNumericalVariance() {
+ return getMean();
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * <p>The lower bound of the support is always 0 no matter the mean parameter.
+ *
+ * @return lower bound of the support (always 0)
+ */
+ public int getSupportLowerBound() {
+ return 0;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * <p>The upper bound of the support is positive infinity, regardless of the parameter values.
+ * There is no integer infinity, so this method returns {@code Integer.MAX_VALUE}.
+ *
+ * @return upper bound of the support (always {@code Integer.MAX_VALUE} for positive infinity)
+ */
+ public int getSupportUpperBound() {
+ return Integer.MAX_VALUE;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * <p>The support of this distribution is connected.
+ *
+ * @return {@code true}
+ */
+ public boolean isSupportConnected() {
+ return true;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * <p><strong>Algorithm Description</strong>:
+ *
+ * <ul>
+ * <li>For small means, uses simulation of a Poisson process using Uniform deviates, as
+ * described <a href="http://mathaa.epfl.ch/cours/PMMI2001/interactive/rng7.htm">here</a>.
+ * The Poisson process (and hence value returned) is bounded by 1000 * mean.
+ * <li>For large means, uses the rejection algorithm described in
+ * <blockquote>
+ * Devroye, Luc. (1981).<i>The Computer Generation of Poisson Random Variables</i><br>
+ * <strong>Computing</strong> vol. 26 pp. 197-207.<br>
+ * </blockquote>
+ * </ul>
+ *
+ * @return a random value.
+ * @since 2.2
+ */
+ @Override
+ public int sample() {
+ return (int) FastMath.min(nextPoisson(mean), Integer.MAX_VALUE);
+ }
+
+ /**
+ * @param meanPoisson Mean of the Poisson distribution.
+ * @return the next sample.
+ */
+ private long nextPoisson(double meanPoisson) {
+ final double pivot = 40.0d;
+ if (meanPoisson < pivot) {
+ double p = FastMath.exp(-meanPoisson);
+ long n = 0;
+ double r = 1.0d;
+ double rnd = 1.0d;
+
+ while (n < 1000 * meanPoisson) {
+ rnd = random.nextDouble();
+ r *= rnd;
+ if (r >= p) {
+ n++;
+ } else {
+ return n;
+ }
+ }
+ return n;
+ } else {
+ final double lambda = FastMath.floor(meanPoisson);
+ final double lambdaFractional = meanPoisson - lambda;
+ final double logLambda = FastMath.log(lambda);
+ final double logLambdaFactorial = CombinatoricsUtils.factorialLog((int) lambda);
+ final long y2 = lambdaFractional < Double.MIN_VALUE ? 0 : nextPoisson(lambdaFractional);
+ final double delta =
+ FastMath.sqrt(lambda * FastMath.log(32 * lambda / FastMath.PI + 1));
+ final double halfDelta = delta / 2;
+ final double twolpd = 2 * lambda + delta;
+ final double a1 = FastMath.sqrt(FastMath.PI * twolpd) * FastMath.exp(1 / (8 * lambda));
+ final double a2 = (twolpd / delta) * FastMath.exp(-delta * (1 + delta) / twolpd);
+ final double aSum = a1 + a2 + 1;
+ final double p1 = a1 / aSum;
+ final double p2 = a2 / aSum;
+ final double c1 = 1 / (8 * lambda);
+
+ double x = 0;
+ double y = 0;
+ double v = 0;
+ int a = 0;
+ double t = 0;
+ double qr = 0;
+ double qa = 0;
+ for (; ; ) {
+ final double u = random.nextDouble();
+ if (u <= p1) {
+ final double n = random.nextGaussian();
+ x = n * FastMath.sqrt(lambda + halfDelta) - 0.5d;
+ if (x > delta || x < -lambda) {
+ continue;
+ }
+ y = x < 0 ? FastMath.floor(x) : FastMath.ceil(x);
+ final double e = exponential.sample();
+ v = -e - (n * n / 2) + c1;
+ } else {
+ if (u > p1 + p2) {
+ y = lambda;
+ break;
+ } else {
+ x = delta + (twolpd / delta) * exponential.sample();
+ y = FastMath.ceil(x);
+ v = -exponential.sample() - delta * (x + 1) / twolpd;
+ }
+ }
+ a = x < 0 ? 1 : 0;
+ t = y * (y + 1) / (2 * lambda);
+ if (v < -t && a == 0) {
+ y = lambda + y;
+ break;
+ }
+ qr = t * ((2 * y + 1) / (6 * lambda) - 1);
+ qa = qr - (t * t) / (3 * (lambda + a * (y + 1)));
+ if (v < qa) {
+ y = lambda + y;
+ break;
+ }
+ if (v > qr) {
+ continue;
+ }
+ if (v
+ < y * logLambda
+ - CombinatoricsUtils.factorialLog((int) (y + lambda))
+ + logLambdaFactorial) {
+ y = lambda + y;
+ break;
+ }
+ }
+ return y2 + (long) y;
+ }
+ }
+}
diff --git a/src/main/java/org/apache/commons/math3/distribution/RealDistribution.java b/src/main/java/org/apache/commons/math3/distribution/RealDistribution.java
new file mode 100644
index 0000000..bee70a3
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/distribution/RealDistribution.java
@@ -0,0 +1,182 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math3.distribution;
+
+import org.apache.commons.math3.exception.NumberIsTooLargeException;
+import org.apache.commons.math3.exception.OutOfRangeException;
+
+/**
+ * Base interface for distributions on the reals.
+ *
+ * @since 3.0
+ */
+public interface RealDistribution {
+ /**
+ * For a random variable {@code X} whose values are distributed according to this distribution,
+ * this method returns {@code P(X = x)}. In other words, this method represents the probability
+ * mass function (PMF) for the distribution.
+ *
+ * @param x the point at which the PMF is evaluated
+ * @return the value of the probability mass function at point {@code x}
+ */
+ double probability(double x);
+
+ /**
+ * Returns the probability density function (PDF) of this distribution evaluated at the
+ * specified point {@code x}. In general, the PDF is the derivative of the {@link
+ * #cumulativeProbability(double) CDF}. If the derivative does not exist at {@code x}, then an
+ * appropriate replacement should be returned, e.g. {@code Double.POSITIVE_INFINITY}, {@code
+ * Double.NaN}, or the limit inferior or limit superior of the difference quotient.
+ *
+ * @param x the point at which the PDF is evaluated
+ * @return the value of the probability density function at point {@code x}
+ */
+ double density(double x);
+
+ /**
+ * For a random variable {@code X} whose values are distributed according to this distribution,
+ * this method returns {@code P(X <= x)}. In other words, this method represents the
+ * (cumulative) distribution function (CDF) for this distribution.
+ *
+ * @param x the point at which the CDF is evaluated
+ * @return the probability that a random variable with this distribution takes a value less than
+ * or equal to {@code x}
+ */
+ double cumulativeProbability(double x);
+
+ /**
+ * For a random variable {@code X} whose values are distributed according to this distribution,
+ * this method returns {@code P(x0 < X <= x1)}.
+ *
+ * @param x0 the exclusive lower bound
+ * @param x1 the inclusive upper bound
+ * @return the probability that a random variable with this distribution takes a value between
+ * {@code x0} and {@code x1}, excluding the lower and including the upper endpoint
+ * @throws NumberIsTooLargeException if {@code x0 > x1}
+ * @deprecated As of 3.1. In 4.0, this method will be renamed {@code probability(double x0,
+ * double x1)}.
+ */
+ @Deprecated
+ double cumulativeProbability(double x0, double x1) throws NumberIsTooLargeException;
+
+ /**
+ * Computes the quantile function of this distribution. For a random variable {@code X}
+ * distributed according to this distribution, the returned value is
+ *
+ * <ul>
+ * <li><code>inf{x in R | P(X<=x) >= p}</code> for {@code 0 < p <= 1},
+ * <li><code>inf{x in R | P(X<=x) > 0}</code> for {@code p = 0}.
+ * </ul>
+ *
+ * @param p the cumulative probability
+ * @return the smallest {@code p}-quantile of this distribution (largest 0-quantile for {@code p
+ * = 0})
+ * @throws OutOfRangeException if {@code p < 0} or {@code p > 1}
+ */
+ double inverseCumulativeProbability(double p) throws OutOfRangeException;
+
+ /**
+ * Use this method to get the numerical value of the mean of this distribution.
+ *
+ * @return the mean or {@code Double.NaN} if it is not defined
+ */
+ double getNumericalMean();
+
+ /**
+ * Use this method to get the numerical value of the variance of this distribution.
+ *
+ * @return the variance (possibly {@code Double.POSITIVE_INFINITY} as for certain cases in
+ * {@link TDistribution}) or {@code Double.NaN} if it is not defined
+ */
+ double getNumericalVariance();
+
+ /**
+ * Access the lower bound of the support. This method must return the same value as {@code
+ * inverseCumulativeProbability(0)}. In other words, this method must return
+ *
+ * <p><code>inf {x in R | P(X <= x) > 0}</code>.
+ *
+ * @return lower bound of the support (might be {@code Double.NEGATIVE_INFINITY})
+ */
+ double getSupportLowerBound();
+
+ /**
+ * Access the upper bound of the support. This method must return the same value as {@code
+ * inverseCumulativeProbability(1)}. In other words, this method must return
+ *
+ * <p><code>inf {x in R | P(X <= x) = 1}</code>.
+ *
+ * @return upper bound of the support (might be {@code Double.POSITIVE_INFINITY})
+ */
+ double getSupportUpperBound();
+
+ /**
+ * Whether or not the lower bound of support is in the domain of the density function. Returns
+ * true iff {@code getSupporLowerBound()} is finite and {@code density(getSupportLowerBound())}
+ * returns a non-NaN, non-infinite value.
+ *
+ * @return true if the lower bound of support is finite and the density function returns a
+ * non-NaN, non-infinite value there
+ * @deprecated to be removed in 4.0
+ */
+ @Deprecated
+ boolean isSupportLowerBoundInclusive();
+
+ /**
+ * Whether or not the upper bound of support is in the domain of the density function. Returns
+ * true iff {@code getSupportUpperBound()} is finite and {@code density(getSupportUpperBound())}
+ * returns a non-NaN, non-infinite value.
+ *
+ * @return true if the upper bound of support is finite and the density function returns a
+ * non-NaN, non-infinite value there
+ * @deprecated to be removed in 4.0
+ */
+ @Deprecated
+ boolean isSupportUpperBoundInclusive();
+
+ /**
+ * Use this method to get information about whether the support is connected, i.e. whether all
+ * values between the lower and upper bound of the support are included in the support.
+ *
+ * @return whether the support is connected or not
+ */
+ boolean isSupportConnected();
+
+ /**
+ * Reseed the random generator used to generate samples.
+ *
+ * @param seed the new seed
+ */
+ void reseedRandomGenerator(long seed);
+
+ /**
+ * Generate a random value sampled from this distribution.
+ *
+ * @return a random value.
+ */
+ double sample();
+
+ /**
+ * Generate a random sample from the distribution.
+ *
+ * @param sampleSize the number of random values to generate
+ * @return an array representing the random sample
+ * @throws org.apache.commons.math3.exception.NotStrictlyPositiveException if {@code sampleSize}
+ * is not positive
+ */
+ double[] sample(int sampleSize);
+}
diff --git a/src/main/java/org/apache/commons/math3/distribution/SaddlePointExpansion.java b/src/main/java/org/apache/commons/math3/distribution/SaddlePointExpansion.java
new file mode 100644
index 0000000..9dbceec
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/distribution/SaddlePointExpansion.java
@@ -0,0 +1,199 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math3.distribution;
+
+import org.apache.commons.math3.special.Gamma;
+import org.apache.commons.math3.util.FastMath;
+import org.apache.commons.math3.util.MathUtils;
+
+/**
+ * Utility class used by various distributions to accurately compute their respective probability
+ * mass functions. The implementation for this class is based on the Catherine Loader's <a
+ * target="_blank" href="http://www.herine.net/stat/software/dbinom.html">dbinom</a> routines.
+ *
+ * <p>This class is not intended to be called directly.
+ *
+ * <p>References:
+ *
+ * <ol>
+ * <li>Catherine Loader (2000). "Fast and Accurate Computation of Binomial Probabilities.". <a
+ * target="_blank" href="http://www.herine.net/stat/papers/dbinom.pdf">
+ * http://www.herine.net/stat/papers/dbinom.pdf</a>
+ * </ol>
+ *
+ * @since 2.1
+ */
+final class SaddlePointExpansion {
+
+ /** 1/2 * log(2 &#960;). */
+ private static final double HALF_LOG_2_PI = 0.5 * FastMath.log(MathUtils.TWO_PI);
+
+ /** exact Stirling expansion error for certain values. */
+ private static final double[] EXACT_STIRLING_ERRORS = {
+ 0.0, /* 0.0 */
+ 0.1534264097200273452913848, /* 0.5 */
+ 0.0810614667953272582196702, /* 1.0 */
+ 0.0548141210519176538961390, /* 1.5 */
+ 0.0413406959554092940938221, /* 2.0 */
+ 0.03316287351993628748511048, /* 2.5 */
+ 0.02767792568499833914878929, /* 3.0 */
+ 0.02374616365629749597132920, /* 3.5 */
+ 0.02079067210376509311152277, /* 4.0 */
+ 0.01848845053267318523077934, /* 4.5 */
+ 0.01664469118982119216319487, /* 5.0 */
+ 0.01513497322191737887351255, /* 5.5 */
+ 0.01387612882307074799874573, /* 6.0 */
+ 0.01281046524292022692424986, /* 6.5 */
+ 0.01189670994589177009505572, /* 7.0 */
+ 0.01110455975820691732662991, /* 7.5 */
+ 0.010411265261972096497478567, /* 8.0 */
+ 0.009799416126158803298389475, /* 8.5 */
+ 0.009255462182712732917728637, /* 9.0 */
+ 0.008768700134139385462952823, /* 9.5 */
+ 0.008330563433362871256469318, /* 10.0 */
+ 0.007934114564314020547248100, /* 10.5 */
+ 0.007573675487951840794972024, /* 11.0 */
+ 0.007244554301320383179543912, /* 11.5 */
+ 0.006942840107209529865664152, /* 12.0 */
+ 0.006665247032707682442354394, /* 12.5 */
+ 0.006408994188004207068439631, /* 13.0 */
+ 0.006171712263039457647532867, /* 13.5 */
+ 0.005951370112758847735624416, /* 14.0 */
+ 0.005746216513010115682023589, /* 14.5 */
+ 0.005554733551962801371038690 /* 15.0 */
+ };
+
+ /** Default constructor. */
+ private SaddlePointExpansion() {
+ super();
+ }
+
+ /**
+ * Compute the error of Stirling's series at the given value.
+ *
+ * <p>References:
+ *
+ * <ol>
+ * <li>Eric W. Weisstein. "Stirling's Series." From MathWorld--A Wolfram Web Resource. <a
+ * target="_blank" href="http://mathworld.wolfram.com/StirlingsSeries.html">
+ * http://mathworld.wolfram.com/StirlingsSeries.html</a>
+ * </ol>
+ *
+ * @param z the value.
+ * @return the Striling's series error.
+ */
+ static double getStirlingError(double z) {
+ double ret;
+ if (z < 15.0) {
+ double z2 = 2.0 * z;
+ if (FastMath.floor(z2) == z2) {
+ ret = EXACT_STIRLING_ERRORS[(int) z2];
+ } else {
+ ret = Gamma.logGamma(z + 1.0) - (z + 0.5) * FastMath.log(z) + z - HALF_LOG_2_PI;
+ }
+ } else {
+ double z2 = z * z;
+ ret =
+ (0.083333333333333333333
+ - (0.00277777777777777777778
+ - (0.00079365079365079365079365
+ - (0.000595238095238095238095238
+ - 0.0008417508417508417508417508
+ / z2)
+ / z2)
+ / z2)
+ / z2)
+ / z;
+ }
+ return ret;
+ }
+
+ /**
+ * A part of the deviance portion of the saddle point approximation.
+ *
+ * <p>References:
+ *
+ * <ol>
+ * <li>Catherine Loader (2000). "Fast and Accurate Computation of Binomial Probabilities.". <a
+ * target="_blank" href="http://www.herine.net/stat/papers/dbinom.pdf">
+ * http://www.herine.net/stat/papers/dbinom.pdf</a>
+ * </ol>
+ *
+ * @param x the x value.
+ * @param mu the average.
+ * @return a part of the deviance.
+ */
+ static double getDeviancePart(double x, double mu) {
+ double ret;
+ if (FastMath.abs(x - mu) < 0.1 * (x + mu)) {
+ double d = x - mu;
+ double v = d / (x + mu);
+ double s1 = v * d;
+ double s = Double.NaN;
+ double ej = 2.0 * x * v;
+ v *= v;
+ int j = 1;
+ while (s1 != s) {
+ s = s1;
+ ej *= v;
+ s1 = s + ej / ((j * 2) + 1);
+ ++j;
+ }
+ ret = s1;
+ } else {
+ ret = x * FastMath.log(x / mu) + mu - x;
+ }
+ return ret;
+ }
+
+ /**
+ * Compute the logarithm of the PMF for a binomial distribution using the saddle point
+ * expansion.
+ *
+ * @param x the value at which the probability is evaluated.
+ * @param n the number of trials.
+ * @param p the probability of success.
+ * @param q the probability of failure (1 - p).
+ * @return log(p(x)).
+ */
+ static double logBinomialProbability(int x, int n, double p, double q) {
+ double ret;
+ if (x == 0) {
+ if (p < 0.1) {
+ ret = -getDeviancePart(n, n * q) - n * p;
+ } else {
+ ret = n * FastMath.log(q);
+ }
+ } else if (x == n) {
+ if (q < 0.1) {
+ ret = -getDeviancePart(n, n * p) - n * q;
+ } else {
+ ret = n * FastMath.log(p);
+ }
+ } else {
+ ret =
+ getStirlingError(n)
+ - getStirlingError(x)
+ - getStirlingError(n - x)
+ - getDeviancePart(x, n * p)
+ - getDeviancePart(n - x, n * q);
+ double f = (MathUtils.TWO_PI * x * (n - x)) / n;
+ ret = -0.5 * FastMath.log(f) + ret;
+ }
+ return ret;
+ }
+}
diff --git a/src/main/java/org/apache/commons/math3/distribution/TDistribution.java b/src/main/java/org/apache/commons/math3/distribution/TDistribution.java
new file mode 100644
index 0000000..8e6053a
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/distribution/TDistribution.java
@@ -0,0 +1,270 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math3.distribution;
+
+import org.apache.commons.math3.exception.NotStrictlyPositiveException;
+import org.apache.commons.math3.exception.util.LocalizedFormats;
+import org.apache.commons.math3.random.RandomGenerator;
+import org.apache.commons.math3.random.Well19937c;
+import org.apache.commons.math3.special.Beta;
+import org.apache.commons.math3.special.Gamma;
+import org.apache.commons.math3.util.FastMath;
+
+/**
+ * Implementation of Student's t-distribution.
+ *
+ * @see "<a href='http://en.wikipedia.org/wiki/Student&apos;s_t-distribution'>Student's
+ * t-distribution (Wikipedia)</a>"
+ * @see "<a href='http://mathworld.wolfram.com/Studentst-Distribution.html'>Student's t-distribution
+ * (MathWorld)</a>"
+ */
+public class TDistribution extends AbstractRealDistribution {
+ /**
+ * Default inverse cumulative probability accuracy.
+ *
+ * @since 2.1
+ */
+ public static final double DEFAULT_INVERSE_ABSOLUTE_ACCURACY = 1e-9;
+
+ /** Serializable version identifier */
+ private static final long serialVersionUID = -5852615386664158222L;
+
+ /** The degrees of freedom. */
+ private final double degreesOfFreedom;
+
+ /** Inverse cumulative probability accuracy. */
+ private final double solverAbsoluteAccuracy;
+
+ /** Static computation factor based on degreesOfFreedom. */
+ private final double factor;
+
+ /**
+ * Create a t distribution using the given degrees of freedom.
+ *
+ * <p><b>Note:</b> this constructor will implicitly create an instance of {@link Well19937c} as
+ * random generator to be used for sampling only (see {@link #sample()} and {@link
+ * #sample(int)}). In case no sampling is needed for the created distribution, it is advised to
+ * pass {@code null} as random generator via the appropriate constructors to avoid the
+ * additional initialisation overhead.
+ *
+ * @param degreesOfFreedom Degrees of freedom.
+ * @throws NotStrictlyPositiveException if {@code degreesOfFreedom <= 0}
+ */
+ public TDistribution(double degreesOfFreedom) throws NotStrictlyPositiveException {
+ this(degreesOfFreedom, DEFAULT_INVERSE_ABSOLUTE_ACCURACY);
+ }
+
+ /**
+ * Create a t distribution using the given degrees of freedom and the specified inverse
+ * cumulative probability absolute accuracy.
+ *
+ * <p><b>Note:</b> this constructor will implicitly create an instance of {@link Well19937c} as
+ * random generator to be used for sampling only (see {@link #sample()} and {@link
+ * #sample(int)}). In case no sampling is needed for the created distribution, it is advised to
+ * pass {@code null} as random generator via the appropriate constructors to avoid the
+ * additional initialisation overhead.
+ *
+ * @param degreesOfFreedom Degrees of freedom.
+ * @param inverseCumAccuracy the maximum absolute error in inverse cumulative probability
+ * estimates (defaults to {@link #DEFAULT_INVERSE_ABSOLUTE_ACCURACY}).
+ * @throws NotStrictlyPositiveException if {@code degreesOfFreedom <= 0}
+ * @since 2.1
+ */
+ public TDistribution(double degreesOfFreedom, double inverseCumAccuracy)
+ throws NotStrictlyPositiveException {
+ this(new Well19937c(), degreesOfFreedom, inverseCumAccuracy);
+ }
+
+ /**
+ * Creates a t distribution.
+ *
+ * @param rng Random number generator.
+ * @param degreesOfFreedom Degrees of freedom.
+ * @throws NotStrictlyPositiveException if {@code degreesOfFreedom <= 0}
+ * @since 3.3
+ */
+ public TDistribution(RandomGenerator rng, double degreesOfFreedom)
+ throws NotStrictlyPositiveException {
+ this(rng, degreesOfFreedom, DEFAULT_INVERSE_ABSOLUTE_ACCURACY);
+ }
+
+ /**
+ * Creates a t distribution.
+ *
+ * @param rng Random number generator.
+ * @param degreesOfFreedom Degrees of freedom.
+ * @param inverseCumAccuracy the maximum absolute error in inverse cumulative probability
+ * estimates (defaults to {@link #DEFAULT_INVERSE_ABSOLUTE_ACCURACY}).
+ * @throws NotStrictlyPositiveException if {@code degreesOfFreedom <= 0}
+ * @since 3.1
+ */
+ public TDistribution(RandomGenerator rng, double degreesOfFreedom, double inverseCumAccuracy)
+ throws NotStrictlyPositiveException {
+ super(rng);
+
+ if (degreesOfFreedom <= 0) {
+ throw new NotStrictlyPositiveException(
+ LocalizedFormats.DEGREES_OF_FREEDOM, degreesOfFreedom);
+ }
+ this.degreesOfFreedom = degreesOfFreedom;
+ solverAbsoluteAccuracy = inverseCumAccuracy;
+
+ final double n = degreesOfFreedom;
+ final double nPlus1Over2 = (n + 1) / 2;
+ factor =
+ Gamma.logGamma(nPlus1Over2)
+ - 0.5 * (FastMath.log(FastMath.PI) + FastMath.log(n))
+ - Gamma.logGamma(n / 2);
+ }
+
+ /**
+ * Access the degrees of freedom.
+ *
+ * @return the degrees of freedom.
+ */
+ public double getDegreesOfFreedom() {
+ return degreesOfFreedom;
+ }
+
+ /** {@inheritDoc} */
+ public double density(double x) {
+ return FastMath.exp(logDensity(x));
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public double logDensity(double x) {
+ final double n = degreesOfFreedom;
+ final double nPlus1Over2 = (n + 1) / 2;
+ return factor - nPlus1Over2 * FastMath.log(1 + x * x / n);
+ }
+
+ /** {@inheritDoc} */
+ public double cumulativeProbability(double x) {
+ double ret;
+ if (x == 0) {
+ ret = 0.5;
+ } else {
+ double t =
+ Beta.regularizedBeta(
+ degreesOfFreedom / (degreesOfFreedom + (x * x)),
+ 0.5 * degreesOfFreedom,
+ 0.5);
+ if (x < 0.0) {
+ ret = 0.5 * t;
+ } else {
+ ret = 1.0 - 0.5 * t;
+ }
+ }
+
+ return ret;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ protected double getSolverAbsoluteAccuracy() {
+ return solverAbsoluteAccuracy;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * <p>For degrees of freedom parameter {@code df}, the mean is
+ *
+ * <ul>
+ * <li>if {@code df > 1} then {@code 0},
+ * <li>else undefined ({@code Double.NaN}).
+ * </ul>
+ */
+ public double getNumericalMean() {
+ final double df = getDegreesOfFreedom();
+
+ if (df > 1) {
+ return 0;
+ }
+
+ return Double.NaN;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * <p>For degrees of freedom parameter {@code df}, the variance is
+ *
+ * <ul>
+ * <li>if {@code df > 2} then {@code df / (df - 2)},
+ * <li>if {@code 1 < df <= 2} then positive infinity ({@code Double.POSITIVE_INFINITY}),
+ * <li>else undefined ({@code Double.NaN}).
+ * </ul>
+ */
+ public double getNumericalVariance() {
+ final double df = getDegreesOfFreedom();
+
+ if (df > 2) {
+ return df / (df - 2);
+ }
+
+ if (df > 1 && df <= 2) {
+ return Double.POSITIVE_INFINITY;
+ }
+
+ return Double.NaN;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * <p>The lower bound of the support is always negative infinity no matter the parameters.
+ *
+ * @return lower bound of the support (always {@code Double.NEGATIVE_INFINITY})
+ */
+ public double getSupportLowerBound() {
+ return Double.NEGATIVE_INFINITY;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * <p>The upper bound of the support is always positive infinity no matter the parameters.
+ *
+ * @return upper bound of the support (always {@code Double.POSITIVE_INFINITY})
+ */
+ public double getSupportUpperBound() {
+ return Double.POSITIVE_INFINITY;
+ }
+
+ /** {@inheritDoc} */
+ public boolean isSupportLowerBoundInclusive() {
+ return false;
+ }
+
+ /** {@inheritDoc} */
+ public boolean isSupportUpperBoundInclusive() {
+ return false;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * <p>The support of this distribution is connected.
+ *
+ * @return {@code true}
+ */
+ public boolean isSupportConnected() {
+ return true;
+ }
+}
diff --git a/src/main/java/org/apache/commons/math3/distribution/TriangularDistribution.java b/src/main/java/org/apache/commons/math3/distribution/TriangularDistribution.java
new file mode 100644
index 0000000..a7feadc
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/distribution/TriangularDistribution.java
@@ -0,0 +1,274 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math3.distribution;
+
+import org.apache.commons.math3.exception.NumberIsTooLargeException;
+import org.apache.commons.math3.exception.NumberIsTooSmallException;
+import org.apache.commons.math3.exception.OutOfRangeException;
+import org.apache.commons.math3.exception.util.LocalizedFormats;
+import org.apache.commons.math3.random.RandomGenerator;
+import org.apache.commons.math3.random.Well19937c;
+import org.apache.commons.math3.util.FastMath;
+
+/**
+ * Implementation of the triangular real distribution.
+ *
+ * @see <a href="http://en.wikipedia.org/wiki/Triangular_distribution">Triangular distribution
+ * (Wikipedia)</a>
+ * @since 3.0
+ */
+public class TriangularDistribution extends AbstractRealDistribution {
+ /** Serializable version identifier. */
+ private static final long serialVersionUID = 20120112L;
+
+ /** Lower limit of this distribution (inclusive). */
+ private final double a;
+
+ /** Upper limit of this distribution (inclusive). */
+ private final double b;
+
+ /** Mode of this distribution. */
+ private final double c;
+
+ /** Inverse cumulative probability accuracy. */
+ private final double solverAbsoluteAccuracy;
+
+ /**
+ * Creates a triangular real distribution using the given lower limit, upper limit, and mode.
+ *
+ * <p><b>Note:</b> this constructor will implicitly create an instance of {@link Well19937c} as
+ * random generator to be used for sampling only (see {@link #sample()} and {@link
+ * #sample(int)}). In case no sampling is needed for the created distribution, it is advised to
+ * pass {@code null} as random generator via the appropriate constructors to avoid the
+ * additional initialisation overhead.
+ *
+ * @param a Lower limit of this distribution (inclusive).
+ * @param b Upper limit of this distribution (inclusive).
+ * @param c Mode of this distribution.
+ * @throws NumberIsTooLargeException if {@code a >= b} or if {@code c > b}.
+ * @throws NumberIsTooSmallException if {@code c < a}.
+ */
+ public TriangularDistribution(double a, double c, double b)
+ throws NumberIsTooLargeException, NumberIsTooSmallException {
+ this(new Well19937c(), a, c, b);
+ }
+
+ /**
+ * Creates a triangular distribution.
+ *
+ * @param rng Random number generator.
+ * @param a Lower limit of this distribution (inclusive).
+ * @param b Upper limit of this distribution (inclusive).
+ * @param c Mode of this distribution.
+ * @throws NumberIsTooLargeException if {@code a >= b} or if {@code c > b}.
+ * @throws NumberIsTooSmallException if {@code c < a}.
+ * @since 3.1
+ */
+ public TriangularDistribution(RandomGenerator rng, double a, double c, double b)
+ throws NumberIsTooLargeException, NumberIsTooSmallException {
+ super(rng);
+
+ if (a >= b) {
+ throw new NumberIsTooLargeException(
+ LocalizedFormats.LOWER_BOUND_NOT_BELOW_UPPER_BOUND, a, b, false);
+ }
+ if (c < a) {
+ throw new NumberIsTooSmallException(LocalizedFormats.NUMBER_TOO_SMALL, c, a, true);
+ }
+ if (c > b) {
+ throw new NumberIsTooLargeException(LocalizedFormats.NUMBER_TOO_LARGE, c, b, true);
+ }
+
+ this.a = a;
+ this.c = c;
+ this.b = b;
+ solverAbsoluteAccuracy = FastMath.max(FastMath.ulp(a), FastMath.ulp(b));
+ }
+
+ /**
+ * Returns the mode {@code c} of this distribution.
+ *
+ * @return the mode {@code c} of this distribution
+ */
+ public double getMode() {
+ return c;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * <p>For this distribution, the returned value is not really meaningful, since exact formulas
+ * are implemented for the computation of the {@link #inverseCumulativeProbability(double)} (no
+ * solver is invoked).
+ *
+ * <p>For lower limit {@code a} and upper limit {@code b}, the current implementation returns
+ * {@code max(ulp(a), ulp(b)}.
+ */
+ @Override
+ protected double getSolverAbsoluteAccuracy() {
+ return solverAbsoluteAccuracy;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * <p>For lower limit {@code a}, upper limit {@code b} and mode {@code c}, the PDF is given by
+ *
+ * <ul>
+ * <li>{@code 2 * (x - a) / [(b - a) * (c - a)]} if {@code a <= x < c},
+ * <li>{@code 2 / (b - a)} if {@code x = c},
+ * <li>{@code 2 * (b - x) / [(b - a) * (b - c)]} if {@code c < x <= b},
+ * <li>{@code 0} otherwise.
+ * </ul>
+ */
+ public double density(double x) {
+ if (x < a) {
+ return 0;
+ }
+ if (a <= x && x < c) {
+ double divident = 2 * (x - a);
+ double divisor = (b - a) * (c - a);
+ return divident / divisor;
+ }
+ if (x == c) {
+ return 2 / (b - a);
+ }
+ if (c < x && x <= b) {
+ double divident = 2 * (b - x);
+ double divisor = (b - a) * (b - c);
+ return divident / divisor;
+ }
+ return 0;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * <p>For lower limit {@code a}, upper limit {@code b} and mode {@code c}, the CDF is given by
+ *
+ * <ul>
+ * <li>{@code 0} if {@code x < a},
+ * <li>{@code (x - a)^2 / [(b - a) * (c - a)]} if {@code a <= x < c},
+ * <li>{@code (c - a) / (b - a)} if {@code x = c},
+ * <li>{@code 1 - (b - x)^2 / [(b - a) * (b - c)]} if {@code c < x <= b},
+ * <li>{@code 1} if {@code x > b}.
+ * </ul>
+ */
+ public double cumulativeProbability(double x) {
+ if (x < a) {
+ return 0;
+ }
+ if (a <= x && x < c) {
+ double divident = (x - a) * (x - a);
+ double divisor = (b - a) * (c - a);
+ return divident / divisor;
+ }
+ if (x == c) {
+ return (c - a) / (b - a);
+ }
+ if (c < x && x <= b) {
+ double divident = (b - x) * (b - x);
+ double divisor = (b - a) * (b - c);
+ return 1 - (divident / divisor);
+ }
+ return 1;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * <p>For lower limit {@code a}, upper limit {@code b}, and mode {@code c}, the mean is {@code
+ * (a + b + c) / 3}.
+ */
+ public double getNumericalMean() {
+ return (a + b + c) / 3;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * <p>For lower limit {@code a}, upper limit {@code b}, and mode {@code c}, the variance is
+ * {@code (a^2 + b^2 + c^2 - a * b - a * c - b * c) / 18}.
+ */
+ public double getNumericalVariance() {
+ return (a * a + b * b + c * c - a * b - a * c - b * c) / 18;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * <p>The lower bound of the support is equal to the lower limit parameter {@code a} of the
+ * distribution.
+ *
+ * @return lower bound of the support
+ */
+ public double getSupportLowerBound() {
+ return a;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * <p>The upper bound of the support is equal to the upper limit parameter {@code b} of the
+ * distribution.
+ *
+ * @return upper bound of the support
+ */
+ public double getSupportUpperBound() {
+ return b;
+ }
+
+ /** {@inheritDoc} */
+ public boolean isSupportLowerBoundInclusive() {
+ return true;
+ }
+
+ /** {@inheritDoc} */
+ public boolean isSupportUpperBoundInclusive() {
+ return true;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * <p>The support of this distribution is connected.
+ *
+ * @return {@code true}
+ */
+ public boolean isSupportConnected() {
+ return true;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public double inverseCumulativeProbability(double p) throws OutOfRangeException {
+ if (p < 0 || p > 1) {
+ throw new OutOfRangeException(p, 0, 1);
+ }
+ if (p == 0) {
+ return a;
+ }
+ if (p == 1) {
+ return b;
+ }
+ if (p < (c - a) / (b - a)) {
+ return a + FastMath.sqrt(p * (b - a) * (c - a));
+ }
+ return b - FastMath.sqrt((1 - p) * (b - a) * (b - c));
+ }
+}
diff --git a/src/main/java/org/apache/commons/math3/distribution/UniformIntegerDistribution.java b/src/main/java/org/apache/commons/math3/distribution/UniformIntegerDistribution.java
new file mode 100644
index 0000000..8a3a98b
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/distribution/UniformIntegerDistribution.java
@@ -0,0 +1,174 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math3.distribution;
+
+import org.apache.commons.math3.exception.NumberIsTooLargeException;
+import org.apache.commons.math3.exception.util.LocalizedFormats;
+import org.apache.commons.math3.random.RandomGenerator;
+import org.apache.commons.math3.random.Well19937c;
+
+/**
+ * Implementation of the uniform integer distribution.
+ *
+ * @see <a href="http://en.wikipedia.org/wiki/Uniform_distribution_(discrete)" >Uniform distribution
+ * (discrete), at Wikipedia</a>
+ * @since 3.0
+ */
+public class UniformIntegerDistribution extends AbstractIntegerDistribution {
+ /** Serializable version identifier. */
+ private static final long serialVersionUID = 20120109L;
+
+ /** Lower bound (inclusive) of this distribution. */
+ private final int lower;
+
+ /** Upper bound (inclusive) of this distribution. */
+ private final int upper;
+
+ /**
+ * Creates a new uniform integer distribution using the given lower and upper bounds (both
+ * inclusive).
+ *
+ * <p><b>Note:</b> this constructor will implicitly create an instance of {@link Well19937c} as
+ * random generator to be used for sampling only (see {@link #sample()} and {@link
+ * #sample(int)}). In case no sampling is needed for the created distribution, it is advised to
+ * pass {@code null} as random generator via the appropriate constructors to avoid the
+ * additional initialisation overhead.
+ *
+ * @param lower Lower bound (inclusive) of this distribution.
+ * @param upper Upper bound (inclusive) of this distribution.
+ * @throws NumberIsTooLargeException if {@code lower >= upper}.
+ */
+ public UniformIntegerDistribution(int lower, int upper) throws NumberIsTooLargeException {
+ this(new Well19937c(), lower, upper);
+ }
+
+ /**
+ * Creates a new uniform integer distribution using the given lower and upper bounds (both
+ * inclusive).
+ *
+ * @param rng Random number generator.
+ * @param lower Lower bound (inclusive) of this distribution.
+ * @param upper Upper bound (inclusive) of this distribution.
+ * @throws NumberIsTooLargeException if {@code lower > upper}.
+ * @since 3.1
+ */
+ public UniformIntegerDistribution(RandomGenerator rng, int lower, int upper)
+ throws NumberIsTooLargeException {
+ super(rng);
+
+ if (lower > upper) {
+ throw new NumberIsTooLargeException(
+ LocalizedFormats.LOWER_BOUND_NOT_BELOW_UPPER_BOUND, lower, upper, true);
+ }
+ this.lower = lower;
+ this.upper = upper;
+ }
+
+ /** {@inheritDoc} */
+ public double probability(int x) {
+ if (x < lower || x > upper) {
+ return 0;
+ }
+ return 1.0 / (upper - lower + 1);
+ }
+
+ /** {@inheritDoc} */
+ public double cumulativeProbability(int x) {
+ if (x < lower) {
+ return 0;
+ }
+ if (x > upper) {
+ return 1;
+ }
+ return (x - lower + 1.0) / (upper - lower + 1.0);
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * <p>For lower bound {@code lower} and upper bound {@code upper}, the mean is {@code 0.5 *
+ * (lower + upper)}.
+ */
+ public double getNumericalMean() {
+ return 0.5 * (lower + upper);
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * <p>For lower bound {@code lower} and upper bound {@code upper}, and {@code n = upper - lower
+ * + 1}, the variance is {@code (n^2 - 1) / 12}.
+ */
+ public double getNumericalVariance() {
+ double n = upper - lower + 1;
+ return (n * n - 1) / 12.0;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * <p>The lower bound of the support is equal to the lower bound parameter of the distribution.
+ *
+ * @return lower bound of the support
+ */
+ public int getSupportLowerBound() {
+ return lower;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * <p>The upper bound of the support is equal to the upper bound parameter of the distribution.
+ *
+ * @return upper bound of the support
+ */
+ public int getSupportUpperBound() {
+ return upper;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * <p>The support of this distribution is connected.
+ *
+ * @return {@code true}
+ */
+ public boolean isSupportConnected() {
+ return true;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public int sample() {
+ final int max = (upper - lower) + 1;
+ if (max <= 0) {
+ // The range is too wide to fit in a positive int (larger
+ // than 2^31); as it covers more than half the integer range,
+ // we use a simple rejection method.
+ while (true) {
+ final int r = random.nextInt();
+ if (r >= lower && r <= upper) {
+ return r;
+ }
+ }
+ } else {
+ // We can shift the range and directly generate a positive int.
+ return lower + random.nextInt(max);
+ }
+ }
+}
diff --git a/src/main/java/org/apache/commons/math3/distribution/UniformRealDistribution.java b/src/main/java/org/apache/commons/math3/distribution/UniformRealDistribution.java
new file mode 100644
index 0000000..a3ccd97
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/distribution/UniformRealDistribution.java
@@ -0,0 +1,234 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math3.distribution;
+
+import org.apache.commons.math3.exception.NumberIsTooLargeException;
+import org.apache.commons.math3.exception.OutOfRangeException;
+import org.apache.commons.math3.exception.util.LocalizedFormats;
+import org.apache.commons.math3.random.RandomGenerator;
+import org.apache.commons.math3.random.Well19937c;
+
+/**
+ * Implementation of the uniform real distribution.
+ *
+ * @see <a href="http://en.wikipedia.org/wiki/Uniform_distribution_(continuous)" >Uniform
+ * distribution (continuous), at Wikipedia</a>
+ * @since 3.0
+ */
+public class UniformRealDistribution extends AbstractRealDistribution {
+ /**
+ * Default inverse cumulative probability accuracy.
+ *
+ * @deprecated as of 3.2 not used anymore, will be removed in 4.0
+ */
+ @Deprecated public static final double DEFAULT_INVERSE_ABSOLUTE_ACCURACY = 1e-9;
+
+ /** Serializable version identifier. */
+ private static final long serialVersionUID = 20120109L;
+
+ /** Lower bound of this distribution (inclusive). */
+ private final double lower;
+
+ /** Upper bound of this distribution (exclusive). */
+ private final double upper;
+
+ /**
+ * Create a standard uniform real distribution with lower bound (inclusive) equal to zero and
+ * upper bound (exclusive) equal to one.
+ *
+ * <p><b>Note:</b> this constructor will implicitly create an instance of {@link Well19937c} as
+ * random generator to be used for sampling only (see {@link #sample()} and {@link
+ * #sample(int)}). In case no sampling is needed for the created distribution, it is advised to
+ * pass {@code null} as random generator via the appropriate constructors to avoid the
+ * additional initialisation overhead.
+ */
+ public UniformRealDistribution() {
+ this(0, 1);
+ }
+
+ /**
+ * Create a uniform real distribution using the given lower and upper bounds.
+ *
+ * <p><b>Note:</b> this constructor will implicitly create an instance of {@link Well19937c} as
+ * random generator to be used for sampling only (see {@link #sample()} and {@link
+ * #sample(int)}). In case no sampling is needed for the created distribution, it is advised to
+ * pass {@code null} as random generator via the appropriate constructors to avoid the
+ * additional initialisation overhead.
+ *
+ * @param lower Lower bound of this distribution (inclusive).
+ * @param upper Upper bound of this distribution (exclusive).
+ * @throws NumberIsTooLargeException if {@code lower >= upper}.
+ */
+ public UniformRealDistribution(double lower, double upper) throws NumberIsTooLargeException {
+ this(new Well19937c(), lower, upper);
+ }
+
+ /**
+ * Create a uniform distribution.
+ *
+ * @param lower Lower bound of this distribution (inclusive).
+ * @param upper Upper bound of this distribution (exclusive).
+ * @param inverseCumAccuracy Inverse cumulative probability accuracy.
+ * @throws NumberIsTooLargeException if {@code lower >= upper}.
+ * @deprecated as of 3.2, inverse CDF is now calculated analytically, use {@link
+ * #UniformRealDistribution(double, double)} instead.
+ */
+ @Deprecated
+ public UniformRealDistribution(double lower, double upper, double inverseCumAccuracy)
+ throws NumberIsTooLargeException {
+ this(new Well19937c(), lower, upper);
+ }
+
+ /**
+ * Creates a uniform distribution.
+ *
+ * @param rng Random number generator.
+ * @param lower Lower bound of this distribution (inclusive).
+ * @param upper Upper bound of this distribution (exclusive).
+ * @param inverseCumAccuracy Inverse cumulative probability accuracy.
+ * @throws NumberIsTooLargeException if {@code lower >= upper}.
+ * @since 3.1
+ * @deprecated as of 3.2, inverse CDF is now calculated analytically, use {@link
+ * #UniformRealDistribution(RandomGenerator, double, double)} instead.
+ */
+ @Deprecated
+ public UniformRealDistribution(
+ RandomGenerator rng, double lower, double upper, double inverseCumAccuracy) {
+ this(rng, lower, upper);
+ }
+
+ /**
+ * Creates a uniform distribution.
+ *
+ * @param rng Random number generator.
+ * @param lower Lower bound of this distribution (inclusive).
+ * @param upper Upper bound of this distribution (exclusive).
+ * @throws NumberIsTooLargeException if {@code lower >= upper}.
+ * @since 3.1
+ */
+ public UniformRealDistribution(RandomGenerator rng, double lower, double upper)
+ throws NumberIsTooLargeException {
+ super(rng);
+ if (lower >= upper) {
+ throw new NumberIsTooLargeException(
+ LocalizedFormats.LOWER_BOUND_NOT_BELOW_UPPER_BOUND, lower, upper, false);
+ }
+
+ this.lower = lower;
+ this.upper = upper;
+ }
+
+ /** {@inheritDoc} */
+ public double density(double x) {
+ if (x < lower || x > upper) {
+ return 0.0;
+ }
+ return 1 / (upper - lower);
+ }
+
+ /** {@inheritDoc} */
+ public double cumulativeProbability(double x) {
+ if (x <= lower) {
+ return 0;
+ }
+ if (x >= upper) {
+ return 1;
+ }
+ return (x - lower) / (upper - lower);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public double inverseCumulativeProbability(final double p) throws OutOfRangeException {
+ if (p < 0.0 || p > 1.0) {
+ throw new OutOfRangeException(p, 0, 1);
+ }
+ return p * (upper - lower) + lower;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * <p>For lower bound {@code lower} and upper bound {@code upper}, the mean is {@code 0.5 *
+ * (lower + upper)}.
+ */
+ public double getNumericalMean() {
+ return 0.5 * (lower + upper);
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * <p>For lower bound {@code lower} and upper bound {@code upper}, the variance is {@code (upper
+ * - lower)^2 / 12}.
+ */
+ public double getNumericalVariance() {
+ double ul = upper - lower;
+ return ul * ul / 12;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * <p>The lower bound of the support is equal to the lower bound parameter of the distribution.
+ *
+ * @return lower bound of the support
+ */
+ public double getSupportLowerBound() {
+ return lower;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * <p>The upper bound of the support is equal to the upper bound parameter of the distribution.
+ *
+ * @return upper bound of the support
+ */
+ public double getSupportUpperBound() {
+ return upper;
+ }
+
+ /** {@inheritDoc} */
+ public boolean isSupportLowerBoundInclusive() {
+ return true;
+ }
+
+ /** {@inheritDoc} */
+ public boolean isSupportUpperBoundInclusive() {
+ return true;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * <p>The support of this distribution is connected.
+ *
+ * @return {@code true}
+ */
+ public boolean isSupportConnected() {
+ return true;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public double sample() {
+ final double u = random.nextDouble();
+ return u * upper + (1 - u) * lower;
+ }
+}
diff --git a/src/main/java/org/apache/commons/math3/distribution/WeibullDistribution.java b/src/main/java/org/apache/commons/math3/distribution/WeibullDistribution.java
new file mode 100644
index 0000000..b7d2953
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/distribution/WeibullDistribution.java
@@ -0,0 +1,346 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math3.distribution;
+
+import org.apache.commons.math3.exception.NotStrictlyPositiveException;
+import org.apache.commons.math3.exception.OutOfRangeException;
+import org.apache.commons.math3.exception.util.LocalizedFormats;
+import org.apache.commons.math3.random.RandomGenerator;
+import org.apache.commons.math3.random.Well19937c;
+import org.apache.commons.math3.special.Gamma;
+import org.apache.commons.math3.util.FastMath;
+
+/**
+ * Implementation of the Weibull distribution. This implementation uses the two parameter form of
+ * the distribution defined by <a href="http://mathworld.wolfram.com/WeibullDistribution.html">
+ * Weibull Distribution</a>, equations (1) and (2).
+ *
+ * @see <a href="http://en.wikipedia.org/wiki/Weibull_distribution">Weibull distribution
+ * (Wikipedia)</a>
+ * @see <a href="http://mathworld.wolfram.com/WeibullDistribution.html">Weibull distribution
+ * (MathWorld)</a>
+ * @since 1.1 (changed to concrete class in 3.0)
+ */
+public class WeibullDistribution extends AbstractRealDistribution {
+ /**
+ * Default inverse cumulative probability accuracy.
+ *
+ * @since 2.1
+ */
+ public static final double DEFAULT_INVERSE_ABSOLUTE_ACCURACY = 1e-9;
+
+ /** Serializable version identifier. */
+ private static final long serialVersionUID = 8589540077390120676L;
+
+ /** The shape parameter. */
+ private final double shape;
+
+ /** The scale parameter. */
+ private final double scale;
+
+ /** Inverse cumulative probability accuracy. */
+ private final double solverAbsoluteAccuracy;
+
+ /** Cached numerical mean */
+ private double numericalMean = Double.NaN;
+
+ /** Whether or not the numerical mean has been calculated */
+ private boolean numericalMeanIsCalculated = false;
+
+ /** Cached numerical variance */
+ private double numericalVariance = Double.NaN;
+
+ /** Whether or not the numerical variance has been calculated */
+ private boolean numericalVarianceIsCalculated = false;
+
+ /**
+ * Create a Weibull distribution with the given shape and scale and a location equal to zero.
+ *
+ * <p><b>Note:</b> this constructor will implicitly create an instance of {@link Well19937c} as
+ * random generator to be used for sampling only (see {@link #sample()} and {@link
+ * #sample(int)}). In case no sampling is needed for the created distribution, it is advised to
+ * pass {@code null} as random generator via the appropriate constructors to avoid the
+ * additional initialisation overhead.
+ *
+ * @param alpha Shape parameter.
+ * @param beta Scale parameter.
+ * @throws NotStrictlyPositiveException if {@code alpha <= 0} or {@code beta <= 0}.
+ */
+ public WeibullDistribution(double alpha, double beta) throws NotStrictlyPositiveException {
+ this(alpha, beta, DEFAULT_INVERSE_ABSOLUTE_ACCURACY);
+ }
+
+ /**
+ * Create a Weibull distribution with the given shape, scale and inverse cumulative probability
+ * accuracy and a location equal to zero.
+ *
+ * <p><b>Note:</b> this constructor will implicitly create an instance of {@link Well19937c} as
+ * random generator to be used for sampling only (see {@link #sample()} and {@link
+ * #sample(int)}). In case no sampling is needed for the created distribution, it is advised to
+ * pass {@code null} as random generator via the appropriate constructors to avoid the
+ * additional initialisation overhead.
+ *
+ * @param alpha Shape parameter.
+ * @param beta Scale parameter.
+ * @param inverseCumAccuracy Maximum absolute error in inverse cumulative probability estimates
+ * (defaults to {@link #DEFAULT_INVERSE_ABSOLUTE_ACCURACY}).
+ * @throws NotStrictlyPositiveException if {@code alpha <= 0} or {@code beta <= 0}.
+ * @since 2.1
+ */
+ public WeibullDistribution(double alpha, double beta, double inverseCumAccuracy) {
+ this(new Well19937c(), alpha, beta, inverseCumAccuracy);
+ }
+
+ /**
+ * Creates a Weibull distribution.
+ *
+ * @param rng Random number generator.
+ * @param alpha Shape parameter.
+ * @param beta Scale parameter.
+ * @throws NotStrictlyPositiveException if {@code alpha <= 0} or {@code beta <= 0}.
+ * @since 3.3
+ */
+ public WeibullDistribution(RandomGenerator rng, double alpha, double beta)
+ throws NotStrictlyPositiveException {
+ this(rng, alpha, beta, DEFAULT_INVERSE_ABSOLUTE_ACCURACY);
+ }
+
+ /**
+ * Creates a Weibull distribution.
+ *
+ * @param rng Random number generator.
+ * @param alpha Shape parameter.
+ * @param beta Scale parameter.
+ * @param inverseCumAccuracy Maximum absolute error in inverse cumulative probability estimates
+ * (defaults to {@link #DEFAULT_INVERSE_ABSOLUTE_ACCURACY}).
+ * @throws NotStrictlyPositiveException if {@code alpha <= 0} or {@code beta <= 0}.
+ * @since 3.1
+ */
+ public WeibullDistribution(
+ RandomGenerator rng, double alpha, double beta, double inverseCumAccuracy)
+ throws NotStrictlyPositiveException {
+ super(rng);
+
+ if (alpha <= 0) {
+ throw new NotStrictlyPositiveException(LocalizedFormats.SHAPE, alpha);
+ }
+ if (beta <= 0) {
+ throw new NotStrictlyPositiveException(LocalizedFormats.SCALE, beta);
+ }
+ scale = beta;
+ shape = alpha;
+ solverAbsoluteAccuracy = inverseCumAccuracy;
+ }
+
+ /**
+ * Access the shape parameter, {@code alpha}.
+ *
+ * @return the shape parameter, {@code alpha}.
+ */
+ public double getShape() {
+ return shape;
+ }
+
+ /**
+ * Access the scale parameter, {@code beta}.
+ *
+ * @return the scale parameter, {@code beta}.
+ */
+ public double getScale() {
+ return scale;
+ }
+
+ /** {@inheritDoc} */
+ public double density(double x) {
+ if (x < 0) {
+ return 0;
+ }
+
+ final double xscale = x / scale;
+ final double xscalepow = FastMath.pow(xscale, shape - 1);
+
+ /*
+ * FastMath.pow(x / scale, shape) =
+ * FastMath.pow(xscale, shape) =
+ * FastMath.pow(xscale, shape - 1) * xscale
+ */
+ final double xscalepowshape = xscalepow * xscale;
+
+ return (shape / scale) * xscalepow * FastMath.exp(-xscalepowshape);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public double logDensity(double x) {
+ if (x < 0) {
+ return Double.NEGATIVE_INFINITY;
+ }
+
+ final double xscale = x / scale;
+ final double logxscalepow = FastMath.log(xscale) * (shape - 1);
+
+ /*
+ * FastMath.pow(x / scale, shape) =
+ * FastMath.pow(xscale, shape) =
+ * FastMath.pow(xscale, shape - 1) * xscale
+ */
+ final double xscalepowshape = FastMath.exp(logxscalepow) * xscale;
+
+ return FastMath.log(shape / scale) + logxscalepow - xscalepowshape;
+ }
+
+ /** {@inheritDoc} */
+ public double cumulativeProbability(double x) {
+ double ret;
+ if (x <= 0.0) {
+ ret = 0.0;
+ } else {
+ ret = 1.0 - FastMath.exp(-FastMath.pow(x / scale, shape));
+ }
+ return ret;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * <p>Returns {@code 0} when {@code p == 0} and {@code Double.POSITIVE_INFINITY} when {@code p
+ * == 1}.
+ */
+ @Override
+ public double inverseCumulativeProbability(double p) {
+ double ret;
+ if (p < 0.0 || p > 1.0) {
+ throw new OutOfRangeException(p, 0.0, 1.0);
+ } else if (p == 0) {
+ ret = 0.0;
+ } else if (p == 1) {
+ ret = Double.POSITIVE_INFINITY;
+ } else {
+ ret = scale * FastMath.pow(-FastMath.log1p(-p), 1.0 / shape);
+ }
+ return ret;
+ }
+
+ /**
+ * Return the absolute accuracy setting of the solver used to estimate inverse cumulative
+ * probabilities.
+ *
+ * @return the solver absolute accuracy.
+ * @since 2.1
+ */
+ @Override
+ protected double getSolverAbsoluteAccuracy() {
+ return solverAbsoluteAccuracy;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * <p>The mean is {@code scale * Gamma(1 + (1 / shape))}, where {@code Gamma()} is the
+ * Gamma-function.
+ */
+ public double getNumericalMean() {
+ if (!numericalMeanIsCalculated) {
+ numericalMean = calculateNumericalMean();
+ numericalMeanIsCalculated = true;
+ }
+ return numericalMean;
+ }
+
+ /**
+ * used by {@link #getNumericalMean()}
+ *
+ * @return the mean of this distribution
+ */
+ protected double calculateNumericalMean() {
+ final double sh = getShape();
+ final double sc = getScale();
+
+ return sc * FastMath.exp(Gamma.logGamma(1 + (1 / sh)));
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * <p>The variance is {@code scale^2 * Gamma(1 + (2 / shape)) - mean^2} where {@code Gamma()} is
+ * the Gamma-function.
+ */
+ public double getNumericalVariance() {
+ if (!numericalVarianceIsCalculated) {
+ numericalVariance = calculateNumericalVariance();
+ numericalVarianceIsCalculated = true;
+ }
+ return numericalVariance;
+ }
+
+ /**
+ * used by {@link #getNumericalVariance()}
+ *
+ * @return the variance of this distribution
+ */
+ protected double calculateNumericalVariance() {
+ final double sh = getShape();
+ final double sc = getScale();
+ final double mn = getNumericalMean();
+
+ return (sc * sc) * FastMath.exp(Gamma.logGamma(1 + (2 / sh))) - (mn * mn);
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * <p>The lower bound of the support is always 0 no matter the parameters.
+ *
+ * @return lower bound of the support (always 0)
+ */
+ public double getSupportLowerBound() {
+ return 0;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * <p>The upper bound of the support is always positive infinity no matter the parameters.
+ *
+ * @return upper bound of the support (always {@code Double.POSITIVE_INFINITY})
+ */
+ public double getSupportUpperBound() {
+ return Double.POSITIVE_INFINITY;
+ }
+
+ /** {@inheritDoc} */
+ public boolean isSupportLowerBoundInclusive() {
+ return true;
+ }
+
+ /** {@inheritDoc} */
+ public boolean isSupportUpperBoundInclusive() {
+ return false;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * <p>The support of this distribution is connected.
+ *
+ * @return {@code true}
+ */
+ public boolean isSupportConnected() {
+ return true;
+ }
+}
diff --git a/src/main/java/org/apache/commons/math3/distribution/ZipfDistribution.java b/src/main/java/org/apache/commons/math3/distribution/ZipfDistribution.java
new file mode 100644
index 0000000..d452122
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/distribution/ZipfDistribution.java
@@ -0,0 +1,502 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math3.distribution;
+
+import org.apache.commons.math3.exception.NotStrictlyPositiveException;
+import org.apache.commons.math3.exception.util.LocalizedFormats;
+import org.apache.commons.math3.random.RandomGenerator;
+import org.apache.commons.math3.random.Well19937c;
+import org.apache.commons.math3.util.FastMath;
+
+/**
+ * Implementation of the Zipf distribution.
+ *
+ * <p><strong>Parameters:</strong> For a random variable {@code X} whose values are distributed
+ * according to this distribution, the probability mass function is given by
+ *
+ * <pre>
+ * P(X = k) = H(N,s) * 1 / k^s for {@code k = 1,2,...,N}.
+ * </pre>
+ *
+ * {@code H(N,s)} is the normalizing constant which corresponds to the generalized harmonic number
+ * of order N of s.
+ *
+ * <p>
+ *
+ * <ul>
+ * <li>{@code N} is the number of elements
+ * <li>{@code s} is the exponent
+ * </ul>
+ *
+ * @see <a href="https://en.wikipedia.org/wiki/Zipf's_law">Zipf's law (Wikipedia)</a>
+ * @see <a
+ * href="https://en.wikipedia.org/wiki/Harmonic_number#Generalized_harmonic_numbers">Generalized
+ * harmonic numbers</a>
+ */
+public class ZipfDistribution extends AbstractIntegerDistribution {
+ /** Serializable version identifier. */
+ private static final long serialVersionUID = -140627372283420404L;
+
+ /** Number of elements. */
+ private final int numberOfElements;
+
+ /** Exponent parameter of the distribution. */
+ private final double exponent;
+
+ /** Cached numerical mean */
+ private double numericalMean = Double.NaN;
+
+ /** Whether or not the numerical mean has been calculated */
+ private boolean numericalMeanIsCalculated = false;
+
+ /** Cached numerical variance */
+ private double numericalVariance = Double.NaN;
+
+ /** Whether or not the numerical variance has been calculated */
+ private boolean numericalVarianceIsCalculated = false;
+
+ /** The sampler to be used for the sample() method */
+ private transient ZipfRejectionInversionSampler sampler;
+
+ /**
+ * Create a new Zipf distribution with the given number of elements and exponent.
+ *
+ * <p><b>Note:</b> this constructor will implicitly create an instance of {@link Well19937c} as
+ * random generator to be used for sampling only (see {@link #sample()} and {@link
+ * #sample(int)}). In case no sampling is needed for the created distribution, it is advised to
+ * pass {@code null} as random generator via the appropriate constructors to avoid the
+ * additional initialisation overhead.
+ *
+ * @param numberOfElements Number of elements.
+ * @param exponent Exponent.
+ * @exception NotStrictlyPositiveException if {@code numberOfElements <= 0} or {@code exponent
+ * <= 0}.
+ */
+ public ZipfDistribution(final int numberOfElements, final double exponent) {
+ this(new Well19937c(), numberOfElements, exponent);
+ }
+
+ /**
+ * Creates a Zipf distribution.
+ *
+ * @param rng Random number generator.
+ * @param numberOfElements Number of elements.
+ * @param exponent Exponent.
+ * @exception NotStrictlyPositiveException if {@code numberOfElements <= 0} or {@code exponent
+ * <= 0}.
+ * @since 3.1
+ */
+ public ZipfDistribution(RandomGenerator rng, int numberOfElements, double exponent)
+ throws NotStrictlyPositiveException {
+ super(rng);
+
+ if (numberOfElements <= 0) {
+ throw new NotStrictlyPositiveException(LocalizedFormats.DIMENSION, numberOfElements);
+ }
+ if (exponent <= 0) {
+ throw new NotStrictlyPositiveException(LocalizedFormats.EXPONENT, exponent);
+ }
+
+ this.numberOfElements = numberOfElements;
+ this.exponent = exponent;
+ }
+
+ /**
+ * Get the number of elements (e.g. corpus size) for the distribution.
+ *
+ * @return the number of elements
+ */
+ public int getNumberOfElements() {
+ return numberOfElements;
+ }
+
+ /**
+ * Get the exponent characterizing the distribution.
+ *
+ * @return the exponent
+ */
+ public double getExponent() {
+ return exponent;
+ }
+
+ /** {@inheritDoc} */
+ public double probability(final int x) {
+ if (x <= 0 || x > numberOfElements) {
+ return 0.0;
+ }
+
+ return (1.0 / FastMath.pow(x, exponent)) / generalizedHarmonic(numberOfElements, exponent);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public double logProbability(int x) {
+ if (x <= 0 || x > numberOfElements) {
+ return Double.NEGATIVE_INFINITY;
+ }
+
+ return -FastMath.log(x) * exponent
+ - FastMath.log(generalizedHarmonic(numberOfElements, exponent));
+ }
+
+ /** {@inheritDoc} */
+ public double cumulativeProbability(final int x) {
+ if (x <= 0) {
+ return 0.0;
+ } else if (x >= numberOfElements) {
+ return 1.0;
+ }
+
+ return generalizedHarmonic(x, exponent) / generalizedHarmonic(numberOfElements, exponent);
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * <p>For number of elements {@code N} and exponent {@code s}, the mean is {@code Hs1 / Hs},
+ * where
+ *
+ * <ul>
+ * <li>{@code Hs1 = generalizedHarmonic(N, s - 1)},
+ * <li>{@code Hs = generalizedHarmonic(N, s)}.
+ * </ul>
+ */
+ public double getNumericalMean() {
+ if (!numericalMeanIsCalculated) {
+ numericalMean = calculateNumericalMean();
+ numericalMeanIsCalculated = true;
+ }
+ return numericalMean;
+ }
+
+ /**
+ * Used by {@link #getNumericalMean()}.
+ *
+ * @return the mean of this distribution
+ */
+ protected double calculateNumericalMean() {
+ final int N = getNumberOfElements();
+ final double s = getExponent();
+
+ final double Hs1 = generalizedHarmonic(N, s - 1);
+ final double Hs = generalizedHarmonic(N, s);
+
+ return Hs1 / Hs;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * <p>For number of elements {@code N} and exponent {@code s}, the mean is {@code (Hs2 / Hs) -
+ * (Hs1^2 / Hs^2)}, where
+ *
+ * <ul>
+ * <li>{@code Hs2 = generalizedHarmonic(N, s - 2)},
+ * <li>{@code Hs1 = generalizedHarmonic(N, s - 1)},
+ * <li>{@code Hs = generalizedHarmonic(N, s)}.
+ * </ul>
+ */
+ public double getNumericalVariance() {
+ if (!numericalVarianceIsCalculated) {
+ numericalVariance = calculateNumericalVariance();
+ numericalVarianceIsCalculated = true;
+ }
+ return numericalVariance;
+ }
+
+ /**
+ * Used by {@link #getNumericalVariance()}.
+ *
+ * @return the variance of this distribution
+ */
+ protected double calculateNumericalVariance() {
+ final int N = getNumberOfElements();
+ final double s = getExponent();
+
+ final double Hs2 = generalizedHarmonic(N, s - 2);
+ final double Hs1 = generalizedHarmonic(N, s - 1);
+ final double Hs = generalizedHarmonic(N, s);
+
+ return (Hs2 / Hs) - ((Hs1 * Hs1) / (Hs * Hs));
+ }
+
+ /**
+ * Calculates the Nth generalized harmonic number. See <a
+ * href="http://mathworld.wolfram.com/HarmonicSeries.html">Harmonic Series</a>.
+ *
+ * @param n Term in the series to calculate (must be larger than 1)
+ * @param m Exponent (special case {@code m = 1} is the harmonic series).
+ * @return the n<sup>th</sup> generalized harmonic number.
+ */
+ private double generalizedHarmonic(final int n, final double m) {
+ double value = 0;
+ for (int k = n; k > 0; --k) {
+ value += 1.0 / FastMath.pow(k, m);
+ }
+ return value;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * <p>The lower bound of the support is always 1 no matter the parameters.
+ *
+ * @return lower bound of the support (always 1)
+ */
+ public int getSupportLowerBound() {
+ return 1;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * <p>The upper bound of the support is the number of elements.
+ *
+ * @return upper bound of the support
+ */
+ public int getSupportUpperBound() {
+ return getNumberOfElements();
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * <p>The support of this distribution is connected.
+ *
+ * @return {@code true}
+ */
+ public boolean isSupportConnected() {
+ return true;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public int sample() {
+ if (sampler == null) {
+ sampler = new ZipfRejectionInversionSampler(numberOfElements, exponent);
+ }
+ return sampler.sample(random);
+ }
+
+ /**
+ * Utility class implementing a rejection inversion sampling method for a discrete, bounded Zipf
+ * distribution that is based on the method described in
+ *
+ * <p>Wolfgang Hörmann and Gerhard Derflinger "Rejection-inversion to generate variates from
+ * monotone discrete distributions." ACM Transactions on Modeling and Computer Simulation
+ * (TOMACS) 6.3 (1996): 169-184.
+ *
+ * <p>The paper describes an algorithm for exponents larger than 1 (Algorithm ZRI). The original
+ * method uses {@code H(x) := (v + x)^(1 - q) / (1 - q)} as the integral of the hat function.
+ * This function is undefined for q = 1, which is the reason for the limitation of the exponent.
+ * If instead the integral function {@code H(x) := ((v + x)^(1 - q) - 1) / (1 - q)} is used, for
+ * which a meaningful limit exists for q = 1, the method works for all positive exponents.
+ *
+ * <p>The following implementation uses v := 0 and generates integral numbers in the range [1,
+ * numberOfElements]. This is different to the original method where v is defined to be positive
+ * and numbers are taken from [0, i_max]. This explains why the implementation looks slightly
+ * different.
+ *
+ * @since 3.6
+ */
+ static final class ZipfRejectionInversionSampler {
+
+ /** Exponent parameter of the distribution. */
+ private final double exponent;
+
+ /** Number of elements. */
+ private final int numberOfElements;
+
+ /** Constant equal to {@code hIntegral(1.5) - 1}. */
+ private final double hIntegralX1;
+
+ /** Constant equal to {@code hIntegral(numberOfElements + 0.5)}. */
+ private final double hIntegralNumberOfElements;
+
+ /** Constant equal to {@code 2 - hIntegralInverse(hIntegral(2.5) - h(2)}. */
+ private final double s;
+
+ /**
+ * Simple constructor.
+ *
+ * @param numberOfElements number of elements
+ * @param exponent exponent parameter of the distribution
+ */
+ ZipfRejectionInversionSampler(final int numberOfElements, final double exponent) {
+ this.exponent = exponent;
+ this.numberOfElements = numberOfElements;
+ this.hIntegralX1 = hIntegral(1.5) - 1d;
+ this.hIntegralNumberOfElements = hIntegral(numberOfElements + 0.5);
+ this.s = 2d - hIntegralInverse(hIntegral(2.5) - h(2));
+ }
+
+ /**
+ * Generate one integral number in the range [1, numberOfElements].
+ *
+ * @param random random generator to use
+ * @return generated integral number in the range [1, numberOfElements]
+ */
+ int sample(final RandomGenerator random) {
+ while (true) {
+
+ final double u =
+ hIntegralNumberOfElements
+ + random.nextDouble() * (hIntegralX1 - hIntegralNumberOfElements);
+ // u is uniformly distributed in (hIntegralX1, hIntegralNumberOfElements]
+
+ double x = hIntegralInverse(u);
+
+ int k = (int) (x + 0.5);
+
+ // Limit k to the range [1, numberOfElements]
+ // (k could be outside due to numerical inaccuracies)
+ if (k < 1) {
+ k = 1;
+ } else if (k > numberOfElements) {
+ k = numberOfElements;
+ }
+
+ // Here, the distribution of k is given by:
+ //
+ // P(k = 1) = C * (hIntegral(1.5) - hIntegralX1) = C
+ // P(k = m) = C * (hIntegral(m + 1/2) - hIntegral(m - 1/2)) for m >= 2
+ //
+ // where C := 1 / (hIntegralNumberOfElements - hIntegralX1)
+
+ if (k - x <= s || u >= hIntegral(k + 0.5) - h(k)) {
+
+ // Case k = 1:
+ //
+ // The right inequality is always true, because replacing k by 1 gives
+ // u >= hIntegral(1.5) - h(1) = hIntegralX1 and u is taken from
+ // (hIntegralX1, hIntegralNumberOfElements].
+ //
+ // Therefore, the acceptance rate for k = 1 is P(accepted | k = 1) = 1
+ // and the probability that 1 is returned as random value is
+ // P(k = 1 and accepted) = P(accepted | k = 1) * P(k = 1) = C = C / 1^exponent
+ //
+ // Case k >= 2:
+ //
+ // The left inequality (k - x <= s) is just a short cut
+ // to avoid the more expensive evaluation of the right inequality
+ // (u >= hIntegral(k + 0.5) - h(k)) in many cases.
+ //
+ // If the left inequality is true, the right inequality is also true:
+ // Theorem 2 in the paper is valid for all positive exponents, because
+ // the requirements h'(x) = -exponent/x^(exponent + 1) < 0 and
+ // (-1/hInverse'(x))'' = (1+1/exponent) * x^(1/exponent-1) >= 0
+ // are both fulfilled.
+ // Therefore, f(x) := x - hIntegralInverse(hIntegral(x + 0.5) - h(x))
+ // is a non-decreasing function. If k - x <= s holds,
+ // k - x <= s + f(k) - f(2) is obviously also true which is equivalent to
+ // -x <= -hIntegralInverse(hIntegral(k + 0.5) - h(k)),
+ // -hIntegralInverse(u) <= -hIntegralInverse(hIntegral(k + 0.5) - h(k)),
+ // and finally u >= hIntegral(k + 0.5) - h(k).
+ //
+ // Hence, the right inequality determines the acceptance rate:
+ // P(accepted | k = m) = h(m) / (hIntegrated(m+1/2) - hIntegrated(m-1/2))
+ // The probability that m is returned is given by
+ // P(k = m and accepted) = P(accepted | k = m) * P(k = m) = C * h(m) = C /
+ // m^exponent.
+ //
+ // In both cases the probabilities are proportional to the probability mass
+ // function
+ // of the Zipf distribution.
+
+ return k;
+ }
+ }
+ }
+
+ /**
+ * {@code H(x) :=}
+ *
+ * <ul>
+ * <li>{@code (x^(1-exponent) - 1)/(1 - exponent)}, if {@code exponent != 1}
+ * <li>{@code log(x)}, if {@code exponent == 1}
+ * </ul>
+ *
+ * H(x) is an integral function of h(x), the derivative of H(x) is h(x).
+ *
+ * @param x free parameter
+ * @return {@code H(x)}
+ */
+ private double hIntegral(final double x) {
+ final double logX = FastMath.log(x);
+ return helper2((1d - exponent) * logX) * logX;
+ }
+
+ /**
+ * {@code h(x) := 1/x^exponent}
+ *
+ * @param x free parameter
+ * @return h(x)
+ */
+ private double h(final double x) {
+ return FastMath.exp(-exponent * FastMath.log(x));
+ }
+
+ /**
+ * The inverse function of H(x).
+ *
+ * @param x free parameter
+ * @return y for which {@code H(y) = x}
+ */
+ private double hIntegralInverse(final double x) {
+ double t = x * (1d - exponent);
+ if (t < -1d) {
+ // Limit value to the range [-1, +inf).
+ // t could be smaller than -1 in some rare cases due to numerical errors.
+ t = -1;
+ }
+ return FastMath.exp(helper1(t) * x);
+ }
+
+ /**
+ * Helper function that calculates {@code log(1+x)/x}.
+ *
+ * <p>A Taylor series expansion is used, if x is close to 0.
+ *
+ * @param x a value larger than or equal to -1
+ * @return {@code log(1+x)/x}
+ */
+ static double helper1(final double x) {
+ if (FastMath.abs(x) > 1e-8) {
+ return FastMath.log1p(x) / x;
+ } else {
+ return 1. - x * ((1. / 2.) - x * ((1. / 3.) - x * (1. / 4.)));
+ }
+ }
+
+ /**
+ * Helper function to calculate {@code (exp(x)-1)/x}.
+ *
+ * <p>A Taylor series expansion is used, if x is close to 0.
+ *
+ * @param x free parameter
+ * @return {@code (exp(x)-1)/x} if x is non-zero, or 1 if x=0
+ */
+ static double helper2(final double x) {
+ if (FastMath.abs(x) > 1e-8) {
+ return FastMath.expm1(x) / x;
+ } else {
+ return 1. + x * (1. / 2.) * (1. + x * (1. / 3.) * (1. + x * (1. / 4.)));
+ }
+ }
+ }
+}
diff --git a/src/main/java/org/apache/commons/math3/distribution/fitting/MultivariateNormalMixtureExpectationMaximization.java b/src/main/java/org/apache/commons/math3/distribution/fitting/MultivariateNormalMixtureExpectationMaximization.java
new file mode 100644
index 0000000..0b4ac0d
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/distribution/fitting/MultivariateNormalMixtureExpectationMaximization.java
@@ -0,0 +1,454 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math3.distribution.fitting;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+import org.apache.commons.math3.distribution.MultivariateNormalDistribution;
+import org.apache.commons.math3.distribution.MixtureMultivariateNormalDistribution;
+import org.apache.commons.math3.exception.ConvergenceException;
+import org.apache.commons.math3.exception.DimensionMismatchException;
+import org.apache.commons.math3.exception.NotStrictlyPositiveException;
+import org.apache.commons.math3.exception.NumberIsTooSmallException;
+import org.apache.commons.math3.exception.NumberIsTooLargeException;
+import org.apache.commons.math3.exception.util.LocalizedFormats;
+import org.apache.commons.math3.linear.Array2DRowRealMatrix;
+import org.apache.commons.math3.linear.RealMatrix;
+import org.apache.commons.math3.linear.SingularMatrixException;
+import org.apache.commons.math3.stat.correlation.Covariance;
+import org.apache.commons.math3.util.FastMath;
+import org.apache.commons.math3.util.MathArrays;
+import org.apache.commons.math3.util.Pair;
+
+/**
+ * Expectation-Maximization</a> algorithm for fitting the parameters of
+ * multivariate normal mixture model distributions.
+ *
+ * This implementation is pure original code based on <a
+ * href="https://www.ee.washington.edu/techsite/papers/documents/UWEETR-2010-0002.pdf">
+ * EM Demystified: An Expectation-Maximization Tutorial</a> by Yihua Chen and Maya R. Gupta,
+ * Department of Electrical Engineering, University of Washington, Seattle, WA 98195.
+ * It was verified using external tools like <a
+ * href="http://cran.r-project.org/web/packages/mixtools/index.html">CRAN Mixtools</a>
+ * (see the JUnit test cases) but it is <strong>not</strong> based on Mixtools code at all.
+ * The discussion of the origin of this class can be seen in the comments of the <a
+ * href="https://issues.apache.org/jira/browse/MATH-817">MATH-817</a> JIRA issue.
+ * @since 3.2
+ */
+public class MultivariateNormalMixtureExpectationMaximization {
+ /**
+ * Default maximum number of iterations allowed per fitting process.
+ */
+ private static final int DEFAULT_MAX_ITERATIONS = 1000;
+ /**
+ * Default convergence threshold for fitting.
+ */
+ private static final double DEFAULT_THRESHOLD = 1E-5;
+ /**
+ * The data to fit.
+ */
+ private final double[][] data;
+ /**
+ * The model fit against the data.
+ */
+ private MixtureMultivariateNormalDistribution fittedModel;
+ /**
+ * The log likelihood of the data given the fitted model.
+ */
+ private double logLikelihood = 0d;
+
+ /**
+ * Creates an object to fit a multivariate normal mixture model to data.
+ *
+ * @param data Data to use in fitting procedure
+ * @throws NotStrictlyPositiveException if data has no rows
+ * @throws DimensionMismatchException if rows of data have different numbers
+ * of columns
+ * @throws NumberIsTooSmallException if the number of columns in the data is
+ * less than 2
+ */
+ public MultivariateNormalMixtureExpectationMaximization(double[][] data)
+ throws NotStrictlyPositiveException,
+ DimensionMismatchException,
+ NumberIsTooSmallException {
+ if (data.length < 1) {
+ throw new NotStrictlyPositiveException(data.length);
+ }
+
+ this.data = new double[data.length][data[0].length];
+
+ for (int i = 0; i < data.length; i++) {
+ if (data[i].length != data[0].length) {
+ // Jagged arrays not allowed
+ throw new DimensionMismatchException(data[i].length,
+ data[0].length);
+ }
+ if (data[i].length < 2) {
+ throw new NumberIsTooSmallException(LocalizedFormats.NUMBER_TOO_SMALL,
+ data[i].length, 2, true);
+ }
+ this.data[i] = MathArrays.copyOf(data[i], data[i].length);
+ }
+ }
+
+ /**
+ * Fit a mixture model to the data supplied to the constructor.
+ *
+ * The quality of the fit depends on the concavity of the data provided to
+ * the constructor and the initial mixture provided to this function. If the
+ * data has many local optima, multiple runs of the fitting function with
+ * different initial mixtures may be required to find the optimal solution.
+ * If a SingularMatrixException is encountered, it is possible that another
+ * initialization would work.
+ *
+ * @param initialMixture Model containing initial values of weights and
+ * multivariate normals
+ * @param maxIterations Maximum iterations allowed for fit
+ * @param threshold Convergence threshold computed as difference in
+ * logLikelihoods between successive iterations
+ * @throws SingularMatrixException if any component's covariance matrix is
+ * singular during fitting
+ * @throws NotStrictlyPositiveException if numComponents is less than one
+ * or threshold is less than Double.MIN_VALUE
+ * @throws DimensionMismatchException if initialMixture mean vector and data
+ * number of columns are not equal
+ */
+ public void fit(final MixtureMultivariateNormalDistribution initialMixture,
+ final int maxIterations,
+ final double threshold)
+ throws SingularMatrixException,
+ NotStrictlyPositiveException,
+ DimensionMismatchException {
+ if (maxIterations < 1) {
+ throw new NotStrictlyPositiveException(maxIterations);
+ }
+
+ if (threshold < Double.MIN_VALUE) {
+ throw new NotStrictlyPositiveException(threshold);
+ }
+
+ final int n = data.length;
+
+ // Number of data columns. Jagged data already rejected in constructor,
+ // so we can assume the lengths of each row are equal.
+ final int numCols = data[0].length;
+ final int k = initialMixture.getComponents().size();
+
+ final int numMeanColumns
+ = initialMixture.getComponents().get(0).getSecond().getMeans().length;
+
+ if (numMeanColumns != numCols) {
+ throw new DimensionMismatchException(numMeanColumns, numCols);
+ }
+
+ int numIterations = 0;
+ double previousLogLikelihood = 0d;
+
+ logLikelihood = Double.NEGATIVE_INFINITY;
+
+ // Initialize model to fit to initial mixture.
+ fittedModel = new MixtureMultivariateNormalDistribution(initialMixture.getComponents());
+
+ while (numIterations++ <= maxIterations &&
+ FastMath.abs(previousLogLikelihood - logLikelihood) > threshold) {
+ previousLogLikelihood = logLikelihood;
+ double sumLogLikelihood = 0d;
+
+ // Mixture components
+ final List<Pair<Double, MultivariateNormalDistribution>> components
+ = fittedModel.getComponents();
+
+ // Weight and distribution of each component
+ final double[] weights = new double[k];
+
+ final MultivariateNormalDistribution[] mvns = new MultivariateNormalDistribution[k];
+
+ for (int j = 0; j < k; j++) {
+ weights[j] = components.get(j).getFirst();
+ mvns[j] = components.get(j).getSecond();
+ }
+
+ // E-step: compute the data dependent parameters of the expectation
+ // function.
+ // The percentage of row's total density between a row and a
+ // component
+ final double[][] gamma = new double[n][k];
+
+ // Sum of gamma for each component
+ final double[] gammaSums = new double[k];
+
+ // Sum of gamma times its row for each each component
+ final double[][] gammaDataProdSums = new double[k][numCols];
+
+ for (int i = 0; i < n; i++) {
+ final double rowDensity = fittedModel.density(data[i]);
+ sumLogLikelihood += FastMath.log(rowDensity);
+
+ for (int j = 0; j < k; j++) {
+ gamma[i][j] = weights[j] * mvns[j].density(data[i]) / rowDensity;
+ gammaSums[j] += gamma[i][j];
+
+ for (int col = 0; col < numCols; col++) {
+ gammaDataProdSums[j][col] += gamma[i][j] * data[i][col];
+ }
+ }
+ }
+
+ logLikelihood = sumLogLikelihood / n;
+
+ // M-step: compute the new parameters based on the expectation
+ // function.
+ final double[] newWeights = new double[k];
+ final double[][] newMeans = new double[k][numCols];
+
+ for (int j = 0; j < k; j++) {
+ newWeights[j] = gammaSums[j] / n;
+ for (int col = 0; col < numCols; col++) {
+ newMeans[j][col] = gammaDataProdSums[j][col] / gammaSums[j];
+ }
+ }
+
+ // Compute new covariance matrices
+ final RealMatrix[] newCovMats = new RealMatrix[k];
+ for (int j = 0; j < k; j++) {
+ newCovMats[j] = new Array2DRowRealMatrix(numCols, numCols);
+ }
+ for (int i = 0; i < n; i++) {
+ for (int j = 0; j < k; j++) {
+ final RealMatrix vec
+ = new Array2DRowRealMatrix(MathArrays.ebeSubtract(data[i], newMeans[j]));
+ final RealMatrix dataCov
+ = vec.multiply(vec.transpose()).scalarMultiply(gamma[i][j]);
+ newCovMats[j] = newCovMats[j].add(dataCov);
+ }
+ }
+
+ // Converting to arrays for use by fitted model
+ final double[][][] newCovMatArrays = new double[k][numCols][numCols];
+ for (int j = 0; j < k; j++) {
+ newCovMats[j] = newCovMats[j].scalarMultiply(1d / gammaSums[j]);
+ newCovMatArrays[j] = newCovMats[j].getData();
+ }
+
+ // Update current model
+ fittedModel = new MixtureMultivariateNormalDistribution(newWeights,
+ newMeans,
+ newCovMatArrays);
+ }
+
+ if (FastMath.abs(previousLogLikelihood - logLikelihood) > threshold) {
+ // Did not converge before the maximum number of iterations
+ throw new ConvergenceException();
+ }
+ }
+
+ /**
+ * Fit a mixture model to the data supplied to the constructor.
+ *
+ * The quality of the fit depends on the concavity of the data provided to
+ * the constructor and the initial mixture provided to this function. If the
+ * data has many local optima, multiple runs of the fitting function with
+ * different initial mixtures may be required to find the optimal solution.
+ * If a SingularMatrixException is encountered, it is possible that another
+ * initialization would work.
+ *
+ * @param initialMixture Model containing initial values of weights and
+ * multivariate normals
+ * @throws SingularMatrixException if any component's covariance matrix is
+ * singular during fitting
+ * @throws NotStrictlyPositiveException if numComponents is less than one or
+ * threshold is less than Double.MIN_VALUE
+ */
+ public void fit(MixtureMultivariateNormalDistribution initialMixture)
+ throws SingularMatrixException,
+ NotStrictlyPositiveException {
+ fit(initialMixture, DEFAULT_MAX_ITERATIONS, DEFAULT_THRESHOLD);
+ }
+
+ /**
+ * Helper method to create a multivariate normal mixture model which can be
+ * used to initialize {@link #fit(MixtureMultivariateNormalDistribution)}.
+ *
+ * This method uses the data supplied to the constructor to try to determine
+ * a good mixture model at which to start the fit, but it is not guaranteed
+ * to supply a model which will find the optimal solution or even converge.
+ *
+ * @param data Data to estimate distribution
+ * @param numComponents Number of components for estimated mixture
+ * @return Multivariate normal mixture model estimated from the data
+ * @throws NumberIsTooLargeException if {@code numComponents} is greater
+ * than the number of data rows.
+ * @throws NumberIsTooSmallException if {@code numComponents < 2}.
+ * @throws NotStrictlyPositiveException if data has less than 2 rows
+ * @throws DimensionMismatchException if rows of data have different numbers
+ * of columns
+ */
+ public static MixtureMultivariateNormalDistribution estimate(final double[][] data,
+ final int numComponents)
+ throws NotStrictlyPositiveException,
+ DimensionMismatchException {
+ if (data.length < 2) {
+ throw new NotStrictlyPositiveException(data.length);
+ }
+ if (numComponents < 2) {
+ throw new NumberIsTooSmallException(numComponents, 2, true);
+ }
+ if (numComponents > data.length) {
+ throw new NumberIsTooLargeException(numComponents, data.length, true);
+ }
+
+ final int numRows = data.length;
+ final int numCols = data[0].length;
+
+ // sort the data
+ final DataRow[] sortedData = new DataRow[numRows];
+ for (int i = 0; i < numRows; i++) {
+ sortedData[i] = new DataRow(data[i]);
+ }
+ Arrays.sort(sortedData);
+
+ // uniform weight for each bin
+ final double weight = 1d / numComponents;
+
+ // components of mixture model to be created
+ final List<Pair<Double, MultivariateNormalDistribution>> components =
+ new ArrayList<Pair<Double, MultivariateNormalDistribution>>(numComponents);
+
+ // create a component based on data in each bin
+ for (int binIndex = 0; binIndex < numComponents; binIndex++) {
+ // minimum index (inclusive) from sorted data for this bin
+ final int minIndex = (binIndex * numRows) / numComponents;
+
+ // maximum index (exclusive) from sorted data for this bin
+ final int maxIndex = ((binIndex + 1) * numRows) / numComponents;
+
+ // number of data records that will be in this bin
+ final int numBinRows = maxIndex - minIndex;
+
+ // data for this bin
+ final double[][] binData = new double[numBinRows][numCols];
+
+ // mean of each column for the data in the this bin
+ final double[] columnMeans = new double[numCols];
+
+ // populate bin and create component
+ for (int i = minIndex, iBin = 0; i < maxIndex; i++, iBin++) {
+ for (int j = 0; j < numCols; j++) {
+ final double val = sortedData[i].getRow()[j];
+ columnMeans[j] += val;
+ binData[iBin][j] = val;
+ }
+ }
+
+ MathArrays.scaleInPlace(1d / numBinRows, columnMeans);
+
+ // covariance matrix for this bin
+ final double[][] covMat
+ = new Covariance(binData).getCovarianceMatrix().getData();
+ final MultivariateNormalDistribution mvn
+ = new MultivariateNormalDistribution(columnMeans, covMat);
+
+ components.add(new Pair<Double, MultivariateNormalDistribution>(weight, mvn));
+ }
+
+ return new MixtureMultivariateNormalDistribution(components);
+ }
+
+ /**
+ * Gets the log likelihood of the data under the fitted model.
+ *
+ * @return Log likelihood of data or zero of no data has been fit
+ */
+ public double getLogLikelihood() {
+ return logLikelihood;
+ }
+
+ /**
+ * Gets the fitted model.
+ *
+ * @return fitted model or {@code null} if no fit has been performed yet.
+ */
+ public MixtureMultivariateNormalDistribution getFittedModel() {
+ return new MixtureMultivariateNormalDistribution(fittedModel.getComponents());
+ }
+
+ /**
+ * Class used for sorting user-supplied data.
+ */
+ private static class DataRow implements Comparable<DataRow> {
+ /** One data row. */
+ private final double[] row;
+ /** Mean of the data row. */
+ private Double mean;
+
+ /**
+ * Create a data row.
+ * @param data Data to use for the row
+ */
+ DataRow(final double[] data) {
+ // Store reference.
+ row = data;
+ // Compute mean.
+ mean = 0d;
+ for (int i = 0; i < data.length; i++) {
+ mean += data[i];
+ }
+ mean /= data.length;
+ }
+
+ /**
+ * Compare two data rows.
+ * @param other The other row
+ * @return int for sorting
+ */
+ public int compareTo(final DataRow other) {
+ return mean.compareTo(other.mean);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public boolean equals(Object other) {
+
+ if (this == other) {
+ return true;
+ }
+
+ if (other instanceof DataRow) {
+ return MathArrays.equals(row, ((DataRow) other).row);
+ }
+
+ return false;
+
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public int hashCode() {
+ return Arrays.hashCode(row);
+ }
+ /**
+ * Get a data row.
+ * @return data row array
+ */
+ public double[] getRow() {
+ return row;
+ }
+ }
+}
+
diff --git a/src/main/java/org/apache/commons/math3/distribution/fitting/package-info.java b/src/main/java/org/apache/commons/math3/distribution/fitting/package-info.java
new file mode 100644
index 0000000..aa95c6d
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/distribution/fitting/package-info.java
@@ -0,0 +1,20 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/**
+ * Fitting of parameters against distributions.
+ */
+package org.apache.commons.math3.distribution.fitting;
diff --git a/src/main/java/org/apache/commons/math3/distribution/package-info.java b/src/main/java/org/apache/commons/math3/distribution/package-info.java
new file mode 100644
index 0000000..3a9fbc9
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/distribution/package-info.java
@@ -0,0 +1,18 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/** Implementations of common discrete and continuous distributions. */
+package org.apache.commons.math3.distribution;
diff --git a/src/main/java/org/apache/commons/math3/exception/ConvergenceException.java b/src/main/java/org/apache/commons/math3/exception/ConvergenceException.java
new file mode 100644
index 0000000..034a83a
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/exception/ConvergenceException.java
@@ -0,0 +1,46 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math3.exception;
+
+import org.apache.commons.math3.exception.util.Localizable;
+import org.apache.commons.math3.exception.util.LocalizedFormats;
+
+/**
+ * Error thrown when a numerical computation can not be performed because the numerical result
+ * failed to converge to a finite value.
+ *
+ * @since 2.2
+ */
+public class ConvergenceException extends MathIllegalStateException {
+ /** Serializable version Id. */
+ private static final long serialVersionUID = 4330003017885151975L;
+
+ /** Construct the exception. */
+ public ConvergenceException() {
+ this(LocalizedFormats.CONVERGENCE_FAILED);
+ }
+
+ /**
+ * Construct the exception with a specific context and arguments.
+ *
+ * @param pattern Message pattern providing the specific context of the error.
+ * @param args Arguments.
+ */
+ public ConvergenceException(Localizable pattern, Object... args) {
+ getContext().addMessage(pattern, args);
+ }
+}
diff --git a/src/main/java/org/apache/commons/math3/exception/DimensionMismatchException.java b/src/main/java/org/apache/commons/math3/exception/DimensionMismatchException.java
new file mode 100644
index 0000000..a0f8dae
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/exception/DimensionMismatchException.java
@@ -0,0 +1,62 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math3.exception;
+
+import org.apache.commons.math3.exception.util.Localizable;
+import org.apache.commons.math3.exception.util.LocalizedFormats;
+
+/**
+ * Exception to be thrown when two dimensions differ.
+ *
+ * @since 2.2
+ */
+public class DimensionMismatchException extends MathIllegalNumberException {
+ /** Serializable version Id. */
+ private static final long serialVersionUID = -8415396756375798143L;
+
+ /** Correct dimension. */
+ private final int dimension;
+
+ /**
+ * Construct an exception from the mismatched dimensions.
+ *
+ * @param specific Specific context information pattern.
+ * @param wrong Wrong dimension.
+ * @param expected Expected dimension.
+ */
+ public DimensionMismatchException(Localizable specific, int wrong, int expected) {
+ super(specific, Integer.valueOf(wrong), Integer.valueOf(expected));
+ dimension = expected;
+ }
+
+ /**
+ * Construct an exception from the mismatched dimensions.
+ *
+ * @param wrong Wrong dimension.
+ * @param expected Expected dimension.
+ */
+ public DimensionMismatchException(int wrong, int expected) {
+ this(LocalizedFormats.DIMENSIONS_MISMATCH_SIMPLE, wrong, expected);
+ }
+
+ /**
+ * @return the expected dimension.
+ */
+ public int getDimension() {
+ return dimension;
+ }
+}
diff --git a/src/main/java/org/apache/commons/math3/exception/InsufficientDataException.java b/src/main/java/org/apache/commons/math3/exception/InsufficientDataException.java
new file mode 100644
index 0000000..59fc6da
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/exception/InsufficientDataException.java
@@ -0,0 +1,46 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math3.exception;
+
+import org.apache.commons.math3.exception.util.Localizable;
+import org.apache.commons.math3.exception.util.LocalizedFormats;
+
+/**
+ * Exception to be thrown when there is insufficient data to perform a computation.
+ *
+ * @since 3.3
+ */
+public class InsufficientDataException extends MathIllegalArgumentException {
+
+ /** Serializable version Id. */
+ private static final long serialVersionUID = -2629324471511903359L;
+
+ /** Construct the exception. */
+ public InsufficientDataException() {
+ this(LocalizedFormats.INSUFFICIENT_DATA);
+ }
+
+ /**
+ * Construct the exception with a specific context.
+ *
+ * @param pattern Message pattern providing the specific context of the error.
+ * @param arguments Values for replacing the placeholders in {@code pattern}.
+ */
+ public InsufficientDataException(Localizable pattern, Object... arguments) {
+ super(pattern, arguments);
+ }
+}
diff --git a/src/main/java/org/apache/commons/math3/exception/MathArithmeticException.java b/src/main/java/org/apache/commons/math3/exception/MathArithmeticException.java
new file mode 100644
index 0000000..d8bd723
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/exception/MathArithmeticException.java
@@ -0,0 +1,71 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math3.exception;
+
+import org.apache.commons.math3.exception.util.ExceptionContext;
+import org.apache.commons.math3.exception.util.ExceptionContextProvider;
+import org.apache.commons.math3.exception.util.Localizable;
+import org.apache.commons.math3.exception.util.LocalizedFormats;
+
+/**
+ * Base class for arithmetic exceptions. It is used for all the exceptions that have the semantics
+ * of the standard {@link ArithmeticException}, but must also provide a localized message.
+ *
+ * @since 3.0
+ */
+public class MathArithmeticException extends ArithmeticException
+ implements ExceptionContextProvider {
+ /** Serializable version Id. */
+ private static final long serialVersionUID = -6024911025449780478L;
+
+ /** Context. */
+ private final ExceptionContext context;
+
+ /** Default constructor. */
+ public MathArithmeticException() {
+ context = new ExceptionContext(this);
+ context.addMessage(LocalizedFormats.ARITHMETIC_EXCEPTION);
+ }
+
+ /**
+ * Constructor with a specific message.
+ *
+ * @param pattern Message pattern providing the specific context of the error.
+ * @param args Arguments.
+ */
+ public MathArithmeticException(Localizable pattern, Object... args) {
+ context = new ExceptionContext(this);
+ context.addMessage(pattern, args);
+ }
+
+ /** {@inheritDoc} */
+ public ExceptionContext getContext() {
+ return context;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public String getMessage() {
+ return context.getMessage();
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public String getLocalizedMessage() {
+ return context.getLocalizedMessage();
+ }
+}
diff --git a/src/main/java/org/apache/commons/math3/exception/MathIllegalArgumentException.java b/src/main/java/org/apache/commons/math3/exception/MathIllegalArgumentException.java
new file mode 100644
index 0000000..73baf13
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/exception/MathIllegalArgumentException.java
@@ -0,0 +1,63 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math3.exception;
+
+import org.apache.commons.math3.exception.util.ExceptionContext;
+import org.apache.commons.math3.exception.util.ExceptionContextProvider;
+import org.apache.commons.math3.exception.util.Localizable;
+
+/**
+ * Base class for all preconditions violation exceptions. In most cases, this class should not be
+ * instantiated directly: it should serve as a base class to create all the exceptions that have the
+ * semantics of the standard {@link IllegalArgumentException}.
+ *
+ * @since 2.2
+ */
+public class MathIllegalArgumentException extends IllegalArgumentException
+ implements ExceptionContextProvider {
+ /** Serializable version Id. */
+ private static final long serialVersionUID = -6024911025449780478L;
+
+ /** Context. */
+ private final ExceptionContext context;
+
+ /**
+ * @param pattern Message pattern explaining the cause of the error.
+ * @param args Arguments.
+ */
+ public MathIllegalArgumentException(Localizable pattern, Object... args) {
+ context = new ExceptionContext(this);
+ context.addMessage(pattern, args);
+ }
+
+ /** {@inheritDoc} */
+ public ExceptionContext getContext() {
+ return context;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public String getMessage() {
+ return context.getMessage();
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public String getLocalizedMessage() {
+ return context.getLocalizedMessage();
+ }
+}
diff --git a/src/main/java/org/apache/commons/math3/exception/MathIllegalNumberException.java b/src/main/java/org/apache/commons/math3/exception/MathIllegalNumberException.java
new file mode 100644
index 0000000..a0d632b
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/exception/MathIllegalNumberException.java
@@ -0,0 +1,57 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math3.exception;
+
+import org.apache.commons.math3.exception.util.Localizable;
+
+/**
+ * Base class for exceptions raised by a wrong number. This class is not intended to be instantiated
+ * directly: it should serve as a base class to create all the exceptions that are raised because
+ * some precondition is violated by a number argument.
+ *
+ * @since 2.2
+ */
+public class MathIllegalNumberException extends MathIllegalArgumentException {
+
+ /** Helper to avoid boxing warnings. @since 3.3 */
+ protected static final Integer INTEGER_ZERO = Integer.valueOf(0);
+
+ /** Serializable version Id. */
+ private static final long serialVersionUID = -7447085893598031110L;
+
+ /** Requested. */
+ private final Number argument;
+
+ /**
+ * Construct an exception.
+ *
+ * @param pattern Localizable pattern.
+ * @param wrong Wrong number.
+ * @param arguments Arguments.
+ */
+ protected MathIllegalNumberException(Localizable pattern, Number wrong, Object... arguments) {
+ super(pattern, wrong, arguments);
+ argument = wrong;
+ }
+
+ /**
+ * @return the requested value.
+ */
+ public Number getArgument() {
+ return argument;
+ }
+}
diff --git a/src/main/java/org/apache/commons/math3/exception/MathIllegalStateException.java b/src/main/java/org/apache/commons/math3/exception/MathIllegalStateException.java
new file mode 100644
index 0000000..fa7bf60
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/exception/MathIllegalStateException.java
@@ -0,0 +1,83 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math3.exception;
+
+import org.apache.commons.math3.exception.util.ExceptionContext;
+import org.apache.commons.math3.exception.util.ExceptionContextProvider;
+import org.apache.commons.math3.exception.util.Localizable;
+import org.apache.commons.math3.exception.util.LocalizedFormats;
+
+/**
+ * Base class for all exceptions that signal that the process throwing the exception is in a state
+ * that does not comply with the set of states that it is designed to be in.
+ *
+ * @since 2.2
+ */
+public class MathIllegalStateException extends IllegalStateException
+ implements ExceptionContextProvider {
+ /** Serializable version Id. */
+ private static final long serialVersionUID = -6024911025449780478L;
+
+ /** Context. */
+ private final ExceptionContext context;
+
+ /**
+ * Simple constructor.
+ *
+ * @param pattern Message pattern explaining the cause of the error.
+ * @param args Arguments.
+ */
+ public MathIllegalStateException(Localizable pattern, Object... args) {
+ context = new ExceptionContext(this);
+ context.addMessage(pattern, args);
+ }
+
+ /**
+ * Simple constructor.
+ *
+ * @param cause Root cause.
+ * @param pattern Message pattern explaining the cause of the error.
+ * @param args Arguments.
+ */
+ public MathIllegalStateException(Throwable cause, Localizable pattern, Object... args) {
+ super(cause);
+ context = new ExceptionContext(this);
+ context.addMessage(pattern, args);
+ }
+
+ /** Default constructor. */
+ public MathIllegalStateException() {
+ this(LocalizedFormats.ILLEGAL_STATE);
+ }
+
+ /** {@inheritDoc} */
+ public ExceptionContext getContext() {
+ return context;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public String getMessage() {
+ return context.getMessage();
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public String getLocalizedMessage() {
+ return context.getLocalizedMessage();
+ }
+}
diff --git a/src/main/java/org/apache/commons/math3/exception/MathInternalError.java b/src/main/java/org/apache/commons/math3/exception/MathInternalError.java
new file mode 100644
index 0000000..bf1b06c
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/exception/MathInternalError.java
@@ -0,0 +1,57 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math3.exception;
+
+import org.apache.commons.math3.exception.util.Localizable;
+import org.apache.commons.math3.exception.util.LocalizedFormats;
+
+/**
+ * Exception triggered when something that shouldn't happen does happen.
+ *
+ * @since 2.2
+ */
+public class MathInternalError extends MathIllegalStateException {
+ /** Serializable version Id. */
+ private static final long serialVersionUID = -6276776513966934846L;
+
+ /** URL for reporting problems. */
+ private static final String REPORT_URL = "https://issues.apache.org/jira/browse/MATH";
+
+ /** Simple constructor. */
+ public MathInternalError() {
+ getContext().addMessage(LocalizedFormats.INTERNAL_ERROR, REPORT_URL);
+ }
+
+ /**
+ * Simple constructor.
+ *
+ * @param cause root cause
+ */
+ public MathInternalError(final Throwable cause) {
+ super(cause, LocalizedFormats.INTERNAL_ERROR, REPORT_URL);
+ }
+
+ /**
+ * Constructor accepting a localized message.
+ *
+ * @param pattern Message pattern explaining the cause of the error.
+ * @param args Arguments.
+ */
+ public MathInternalError(Localizable pattern, Object... args) {
+ super(pattern, args);
+ }
+}
diff --git a/src/main/java/org/apache/commons/math3/exception/MathParseException.java b/src/main/java/org/apache/commons/math3/exception/MathParseException.java
new file mode 100644
index 0000000..0bf7948
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/exception/MathParseException.java
@@ -0,0 +1,53 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math3.exception;
+
+import org.apache.commons.math3.exception.util.ExceptionContextProvider;
+import org.apache.commons.math3.exception.util.LocalizedFormats;
+
+/**
+ * Class to signal parse failures.
+ *
+ * @since 2.2
+ */
+public class MathParseException extends MathIllegalStateException
+ implements ExceptionContextProvider {
+ /** Serializable version Id. */
+ private static final long serialVersionUID = -6024911025449780478L;
+
+ /**
+ * @param wrong Bad string representation of the object.
+ * @param position Index, in the {@code wrong} string, that caused the parsing to fail.
+ * @param type Class of the object supposedly represented by the {@code wrong} string.
+ */
+ public MathParseException(String wrong, int position, Class<?> type) {
+ getContext()
+ .addMessage(
+ LocalizedFormats.CANNOT_PARSE_AS_TYPE,
+ wrong,
+ Integer.valueOf(position),
+ type.getName());
+ }
+
+ /**
+ * @param wrong Bad string representation of the object.
+ * @param position Index, in the {@code wrong} string, that caused the parsing to fail.
+ */
+ public MathParseException(String wrong, int position) {
+ getContext().addMessage(LocalizedFormats.CANNOT_PARSE, wrong, Integer.valueOf(position));
+ }
+}
diff --git a/src/main/java/org/apache/commons/math3/exception/MathRuntimeException.java b/src/main/java/org/apache/commons/math3/exception/MathRuntimeException.java
new file mode 100644
index 0000000..9bd2bd0
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/exception/MathRuntimeException.java
@@ -0,0 +1,63 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math3.exception;
+
+import org.apache.commons.math3.exception.util.ExceptionContext;
+import org.apache.commons.math3.exception.util.ExceptionContextProvider;
+import org.apache.commons.math3.exception.util.Localizable;
+
+/**
+ * As of release 4.0, all exceptions thrown by the Commons Math code (except {@link
+ * NullArgumentException}) inherit from this class. In most cases, this class should not be
+ * instantiated directly: it should serve as a base class for implementing exception classes that
+ * describe a specific "problem".
+ *
+ * @since 3.1
+ */
+public class MathRuntimeException extends RuntimeException implements ExceptionContextProvider {
+ /** Serializable version Id. */
+ private static final long serialVersionUID = 20120926L;
+
+ /** Context. */
+ private final ExceptionContext context;
+
+ /**
+ * @param pattern Message pattern explaining the cause of the error.
+ * @param args Arguments.
+ */
+ public MathRuntimeException(Localizable pattern, Object... args) {
+ context = new ExceptionContext(this);
+ context.addMessage(pattern, args);
+ }
+
+ /** {@inheritDoc} */
+ public ExceptionContext getContext() {
+ return context;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public String getMessage() {
+ return context.getMessage();
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public String getLocalizedMessage() {
+ return context.getLocalizedMessage();
+ }
+}
diff --git a/src/main/java/org/apache/commons/math3/exception/MathUnsupportedOperationException.java b/src/main/java/org/apache/commons/math3/exception/MathUnsupportedOperationException.java
new file mode 100644
index 0000000..992527f
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/exception/MathUnsupportedOperationException.java
@@ -0,0 +1,69 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math3.exception;
+
+import org.apache.commons.math3.exception.util.ExceptionContext;
+import org.apache.commons.math3.exception.util.ExceptionContextProvider;
+import org.apache.commons.math3.exception.util.Localizable;
+import org.apache.commons.math3.exception.util.LocalizedFormats;
+
+/**
+ * Base class for all unsupported features. It is used for all the exceptions that have the
+ * semantics of the standard {@link UnsupportedOperationException}, but must also provide a
+ * localized message.
+ *
+ * @since 2.2
+ */
+public class MathUnsupportedOperationException extends UnsupportedOperationException
+ implements ExceptionContextProvider {
+ /** Serializable version Id. */
+ private static final long serialVersionUID = -6024911025449780478L;
+
+ /** Context. */
+ private final ExceptionContext context;
+
+ /** Default constructor. */
+ public MathUnsupportedOperationException() {
+ this(LocalizedFormats.UNSUPPORTED_OPERATION);
+ }
+
+ /**
+ * @param pattern Message pattern providing the specific context of the error.
+ * @param args Arguments.
+ */
+ public MathUnsupportedOperationException(Localizable pattern, Object... args) {
+ context = new ExceptionContext(this);
+ context.addMessage(pattern, args);
+ }
+
+ /** {@inheritDoc} */
+ public ExceptionContext getContext() {
+ return context;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public String getMessage() {
+ return context.getMessage();
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public String getLocalizedMessage() {
+ return context.getLocalizedMessage();
+ }
+}
diff --git a/src/main/java/org/apache/commons/math3/exception/MaxCountExceededException.java b/src/main/java/org/apache/commons/math3/exception/MaxCountExceededException.java
new file mode 100644
index 0000000..bc97c84
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/exception/MaxCountExceededException.java
@@ -0,0 +1,61 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math3.exception;
+
+import org.apache.commons.math3.exception.util.Localizable;
+import org.apache.commons.math3.exception.util.LocalizedFormats;
+
+/**
+ * Exception to be thrown when some counter maximum value is exceeded.
+ *
+ * @since 3.0
+ */
+public class MaxCountExceededException extends MathIllegalStateException {
+ /** Serializable version Id. */
+ private static final long serialVersionUID = 4330003017885151975L;
+
+ /** Maximum number of evaluations. */
+ private final Number max;
+
+ /**
+ * Construct the exception.
+ *
+ * @param max Maximum.
+ */
+ public MaxCountExceededException(Number max) {
+ this(LocalizedFormats.MAX_COUNT_EXCEEDED, max);
+ }
+
+ /**
+ * Construct the exception with a specific context.
+ *
+ * @param specific Specific context pattern.
+ * @param max Maximum.
+ * @param args Additional arguments.
+ */
+ public MaxCountExceededException(Localizable specific, Number max, Object... args) {
+ getContext().addMessage(specific, max, args);
+ this.max = max;
+ }
+
+ /**
+ * @return the maximum number of evaluations.
+ */
+ public Number getMax() {
+ return max;
+ }
+}
diff --git a/src/main/java/org/apache/commons/math3/exception/MultiDimensionMismatchException.java b/src/main/java/org/apache/commons/math3/exception/MultiDimensionMismatchException.java
new file mode 100644
index 0000000..1db0971
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/exception/MultiDimensionMismatchException.java
@@ -0,0 +1,90 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math3.exception;
+
+import org.apache.commons.math3.exception.util.Localizable;
+import org.apache.commons.math3.exception.util.LocalizedFormats;
+
+/**
+ * Exception to be thrown when two sets of dimensions differ.
+ *
+ * @since 3.0
+ */
+public class MultiDimensionMismatchException extends MathIllegalArgumentException {
+ /** Serializable version Id. */
+ private static final long serialVersionUID = -8415396756375798143L;
+
+ /** Wrong dimensions. */
+ private final Integer[] wrong;
+
+ /** Correct dimensions. */
+ private final Integer[] expected;
+
+ /**
+ * Construct an exception from the mismatched dimensions.
+ *
+ * @param wrong Wrong dimensions.
+ * @param expected Expected dimensions.
+ */
+ public MultiDimensionMismatchException(Integer[] wrong, Integer[] expected) {
+ this(LocalizedFormats.DIMENSIONS_MISMATCH, wrong, expected);
+ }
+
+ /**
+ * Construct an exception from the mismatched dimensions.
+ *
+ * @param specific Message pattern providing the specific context of the error.
+ * @param wrong Wrong dimensions.
+ * @param expected Expected dimensions.
+ */
+ public MultiDimensionMismatchException(
+ Localizable specific, Integer[] wrong, Integer[] expected) {
+ super(specific, wrong, expected);
+ this.wrong = wrong.clone();
+ this.expected = expected.clone();
+ }
+
+ /**
+ * @return an array containing the wrong dimensions.
+ */
+ public Integer[] getWrongDimensions() {
+ return wrong.clone();
+ }
+
+ /**
+ * @return an array containing the expected dimensions.
+ */
+ public Integer[] getExpectedDimensions() {
+ return expected.clone();
+ }
+
+ /**
+ * @param index Dimension index.
+ * @return the wrong dimension stored at {@code index}.
+ */
+ public int getWrongDimension(int index) {
+ return wrong[index].intValue();
+ }
+
+ /**
+ * @param index Dimension index.
+ * @return the expected dimension stored at {@code index}.
+ */
+ public int getExpectedDimension(int index) {
+ return expected[index].intValue();
+ }
+}
diff --git a/src/main/java/org/apache/commons/math3/exception/NoBracketingException.java b/src/main/java/org/apache/commons/math3/exception/NoBracketingException.java
new file mode 100644
index 0000000..f0ff514
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/exception/NoBracketingException.java
@@ -0,0 +1,115 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math3.exception;
+
+import org.apache.commons.math3.exception.util.Localizable;
+import org.apache.commons.math3.exception.util.LocalizedFormats;
+
+/**
+ * Exception to be thrown when function values have the same sign at both ends of an interval.
+ *
+ * @since 3.0
+ */
+public class NoBracketingException extends MathIllegalArgumentException {
+ /** Serializable version Id. */
+ private static final long serialVersionUID = -3629324471511904459L;
+
+ /** Lower end of the interval. */
+ private final double lo;
+
+ /** Higher end of the interval. */
+ private final double hi;
+
+ /** Value at lower end of the interval. */
+ private final double fLo;
+
+ /** Value at higher end of the interval. */
+ private final double fHi;
+
+ /**
+ * Construct the exception.
+ *
+ * @param lo Lower end of the interval.
+ * @param hi Higher end of the interval.
+ * @param fLo Value at lower end of the interval.
+ * @param fHi Value at higher end of the interval.
+ */
+ public NoBracketingException(double lo, double hi, double fLo, double fHi) {
+ this(LocalizedFormats.SAME_SIGN_AT_ENDPOINTS, lo, hi, fLo, fHi);
+ }
+
+ /**
+ * Construct the exception with a specific context.
+ *
+ * @param specific Contextual information on what caused the exception.
+ * @param lo Lower end of the interval.
+ * @param hi Higher end of the interval.
+ * @param fLo Value at lower end of the interval.
+ * @param fHi Value at higher end of the interval.
+ * @param args Additional arguments.
+ */
+ public NoBracketingException(
+ Localizable specific, double lo, double hi, double fLo, double fHi, Object... args) {
+ super(
+ specific,
+ Double.valueOf(lo),
+ Double.valueOf(hi),
+ Double.valueOf(fLo),
+ Double.valueOf(fHi),
+ args);
+ this.lo = lo;
+ this.hi = hi;
+ this.fLo = fLo;
+ this.fHi = fHi;
+ }
+
+ /**
+ * Get the lower end of the interval.
+ *
+ * @return the lower end.
+ */
+ public double getLo() {
+ return lo;
+ }
+
+ /**
+ * Get the higher end of the interval.
+ *
+ * @return the higher end.
+ */
+ public double getHi() {
+ return hi;
+ }
+
+ /**
+ * Get the value at the lower end of the interval.
+ *
+ * @return the value at the lower end.
+ */
+ public double getFLo() {
+ return fLo;
+ }
+
+ /**
+ * Get the value at the higher end of the interval.
+ *
+ * @return the value at the higher end.
+ */
+ public double getFHi() {
+ return fHi;
+ }
+}
diff --git a/src/main/java/org/apache/commons/math3/exception/NoDataException.java b/src/main/java/org/apache/commons/math3/exception/NoDataException.java
new file mode 100644
index 0000000..e475877
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/exception/NoDataException.java
@@ -0,0 +1,45 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math3.exception;
+
+import org.apache.commons.math3.exception.util.Localizable;
+import org.apache.commons.math3.exception.util.LocalizedFormats;
+
+/**
+ * Exception to be thrown when the required data is missing.
+ *
+ * @since 2.2
+ */
+public class NoDataException extends MathIllegalArgumentException {
+
+ /** Serializable version Id. */
+ private static final long serialVersionUID = -3629324471511904459L;
+
+ /** Construct the exception. */
+ public NoDataException() {
+ this(LocalizedFormats.NO_DATA);
+ }
+
+ /**
+ * Construct the exception with a specific context.
+ *
+ * @param specific Contextual information on what caused the exception.
+ */
+ public NoDataException(Localizable specific) {
+ super(specific);
+ }
+}
diff --git a/src/main/java/org/apache/commons/math3/exception/NonMonotonicSequenceException.java b/src/main/java/org/apache/commons/math3/exception/NonMonotonicSequenceException.java
new file mode 100644
index 0000000..a2e37f4
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/exception/NonMonotonicSequenceException.java
@@ -0,0 +1,120 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math3.exception;
+
+import org.apache.commons.math3.exception.util.LocalizedFormats;
+import org.apache.commons.math3.util.MathArrays;
+
+/**
+ * Exception to be thrown when the a sequence of values is not monotonically increasing or
+ * decreasing.
+ *
+ * @since 2.2 (name changed to "NonMonotonicSequenceException" in 3.0)
+ */
+public class NonMonotonicSequenceException extends MathIllegalNumberException {
+ /** Serializable version Id. */
+ private static final long serialVersionUID = 3596849179428944575L;
+
+ /** Direction (positive for increasing, negative for decreasing). */
+ private final MathArrays.OrderDirection direction;
+
+ /** Whether the sequence must be strictly increasing or decreasing. */
+ private final boolean strict;
+
+ /** Index of the wrong value. */
+ private final int index;
+
+ /** Previous value. */
+ private final Number previous;
+
+ /**
+ * Construct the exception. This constructor uses default values assuming that the sequence
+ * should have been strictly increasing.
+ *
+ * @param wrong Value that did not match the requirements.
+ * @param previous Previous value in the sequence.
+ * @param index Index of the value that did not match the requirements.
+ */
+ public NonMonotonicSequenceException(Number wrong, Number previous, int index) {
+ this(wrong, previous, index, MathArrays.OrderDirection.INCREASING, true);
+ }
+
+ /**
+ * Construct the exception.
+ *
+ * @param wrong Value that did not match the requirements.
+ * @param previous Previous value in the sequence.
+ * @param index Index of the value that did not match the requirements.
+ * @param direction Strictly positive for a sequence required to be increasing, negative (or
+ * zero) for a decreasing sequence.
+ * @param strict Whether the sequence must be strictly increasing or decreasing.
+ */
+ public NonMonotonicSequenceException(
+ Number wrong,
+ Number previous,
+ int index,
+ MathArrays.OrderDirection direction,
+ boolean strict) {
+ super(
+ direction == MathArrays.OrderDirection.INCREASING
+ ? (strict
+ ? LocalizedFormats.NOT_STRICTLY_INCREASING_SEQUENCE
+ : LocalizedFormats.NOT_INCREASING_SEQUENCE)
+ : (strict
+ ? LocalizedFormats.NOT_STRICTLY_DECREASING_SEQUENCE
+ : LocalizedFormats.NOT_DECREASING_SEQUENCE),
+ wrong,
+ previous,
+ Integer.valueOf(index),
+ Integer.valueOf(index - 1));
+
+ this.direction = direction;
+ this.strict = strict;
+ this.index = index;
+ this.previous = previous;
+ }
+
+ /**
+ * @return the order direction.
+ */
+ public MathArrays.OrderDirection getDirection() {
+ return direction;
+ }
+
+ /**
+ * @return {@code true} is the sequence should be strictly monotonic.
+ */
+ public boolean getStrict() {
+ return strict;
+ }
+
+ /**
+ * Get the index of the wrong value.
+ *
+ * @return the current index.
+ */
+ public int getIndex() {
+ return index;
+ }
+
+ /**
+ * @return the previous value.
+ */
+ public Number getPrevious() {
+ return previous;
+ }
+}
diff --git a/src/main/java/org/apache/commons/math3/exception/NotANumberException.java b/src/main/java/org/apache/commons/math3/exception/NotANumberException.java
new file mode 100644
index 0000000..60d3cff
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/exception/NotANumberException.java
@@ -0,0 +1,34 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math3.exception;
+
+import org.apache.commons.math3.exception.util.LocalizedFormats;
+
+/**
+ * Exception to be thrown when a number is not a number.
+ *
+ * @since 3.1
+ */
+public class NotANumberException extends MathIllegalNumberException {
+ /** Serializable version Id. */
+ private static final long serialVersionUID = 20120906L;
+
+ /** Construct the exception. */
+ public NotANumberException() {
+ super(LocalizedFormats.NAN_NOT_ALLOWED, Double.valueOf(Double.NaN));
+ }
+}
diff --git a/src/main/java/org/apache/commons/math3/exception/NotFiniteNumberException.java b/src/main/java/org/apache/commons/math3/exception/NotFiniteNumberException.java
new file mode 100644
index 0000000..5774256
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/exception/NotFiniteNumberException.java
@@ -0,0 +1,51 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math3.exception;
+
+import org.apache.commons.math3.exception.util.Localizable;
+import org.apache.commons.math3.exception.util.LocalizedFormats;
+
+/**
+ * Exception to be thrown when a number is not finite.
+ *
+ * @since 3.0
+ */
+public class NotFiniteNumberException extends MathIllegalNumberException {
+ /** Serializable version Id. */
+ private static final long serialVersionUID = -6100997100383932834L;
+
+ /**
+ * Construct the exception.
+ *
+ * @param wrong Value that is infinite or NaN.
+ * @param args Optional arguments.
+ */
+ public NotFiniteNumberException(Number wrong, Object... args) {
+ this(LocalizedFormats.NOT_FINITE_NUMBER, wrong, args);
+ }
+
+ /**
+ * Construct the exception with a specific context.
+ *
+ * @param specific Specific context pattern.
+ * @param wrong Value that is infinite or NaN.
+ * @param args Optional arguments.
+ */
+ public NotFiniteNumberException(Localizable specific, Number wrong, Object... args) {
+ super(specific, wrong, args);
+ }
+}
diff --git a/src/main/java/org/apache/commons/math3/exception/NotPositiveException.java b/src/main/java/org/apache/commons/math3/exception/NotPositiveException.java
new file mode 100644
index 0000000..32c601d
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/exception/NotPositiveException.java
@@ -0,0 +1,48 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math3.exception;
+
+import org.apache.commons.math3.exception.util.Localizable;
+
+/**
+ * Exception to be thrown when the argument is negative.
+ *
+ * @since 2.2
+ */
+public class NotPositiveException extends NumberIsTooSmallException {
+ /** Serializable version Id. */
+ private static final long serialVersionUID = -2250556892093726375L;
+
+ /**
+ * Construct the exception.
+ *
+ * @param value Argument.
+ */
+ public NotPositiveException(Number value) {
+ super(value, INTEGER_ZERO, true);
+ }
+
+ /**
+ * Construct the exception with a specific context.
+ *
+ * @param specific Specific context where the error occurred.
+ * @param value Argument.
+ */
+ public NotPositiveException(Localizable specific, Number value) {
+ super(specific, value, INTEGER_ZERO, true);
+ }
+}
diff --git a/src/main/java/org/apache/commons/math3/exception/NotStrictlyPositiveException.java b/src/main/java/org/apache/commons/math3/exception/NotStrictlyPositiveException.java
new file mode 100644
index 0000000..28283ec
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/exception/NotStrictlyPositiveException.java
@@ -0,0 +1,49 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math3.exception;
+
+import org.apache.commons.math3.exception.util.Localizable;
+
+/**
+ * Exception to be thrown when the argument is not greater than 0.
+ *
+ * @since 2.2
+ */
+public class NotStrictlyPositiveException extends NumberIsTooSmallException {
+
+ /** Serializable version Id. */
+ private static final long serialVersionUID = -7824848630829852237L;
+
+ /**
+ * Construct the exception.
+ *
+ * @param value Argument.
+ */
+ public NotStrictlyPositiveException(Number value) {
+ super(value, INTEGER_ZERO, false);
+ }
+
+ /**
+ * Construct the exception with a specific context.
+ *
+ * @param specific Specific context where the error occurred.
+ * @param value Argument.
+ */
+ public NotStrictlyPositiveException(Localizable specific, Number value) {
+ super(specific, value, INTEGER_ZERO, false);
+ }
+}
diff --git a/src/main/java/org/apache/commons/math3/exception/NullArgumentException.java b/src/main/java/org/apache/commons/math3/exception/NullArgumentException.java
new file mode 100644
index 0000000..301b7df
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/exception/NullArgumentException.java
@@ -0,0 +1,46 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math3.exception;
+
+import org.apache.commons.math3.exception.util.Localizable;
+import org.apache.commons.math3.exception.util.LocalizedFormats;
+
+/**
+ * All conditions checks that fail due to a {@code null} argument must throw this exception. This
+ * class is meant to signal a precondition violation ("null is an illegal argument") and so does not
+ * extend the standard {@code NullPointerException}. Propagation of {@code NullPointerException}
+ * from within Commons-Math is construed to be a bug.
+ *
+ * @since 2.2
+ */
+public class NullArgumentException extends MathIllegalArgumentException {
+ /** Serializable version Id. */
+ private static final long serialVersionUID = -6024911025449780478L;
+
+ /** Default constructor. */
+ public NullArgumentException() {
+ this(LocalizedFormats.NULL_NOT_ALLOWED);
+ }
+
+ /**
+ * @param pattern Message pattern providing the specific context of the error.
+ * @param arguments Values for replacing the placeholders in {@code pattern}.
+ */
+ public NullArgumentException(Localizable pattern, Object... arguments) {
+ super(pattern, arguments);
+ }
+}
diff --git a/src/main/java/org/apache/commons/math3/exception/NumberIsTooLargeException.java b/src/main/java/org/apache/commons/math3/exception/NumberIsTooLargeException.java
new file mode 100644
index 0000000..49d1a53
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/exception/NumberIsTooLargeException.java
@@ -0,0 +1,83 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math3.exception;
+
+import org.apache.commons.math3.exception.util.Localizable;
+import org.apache.commons.math3.exception.util.LocalizedFormats;
+
+/**
+ * Exception to be thrown when a number is too large.
+ *
+ * @since 2.2
+ */
+public class NumberIsTooLargeException extends MathIllegalNumberException {
+ /** Serializable version Id. */
+ private static final long serialVersionUID = 4330003017885151975L;
+
+ /** Higher bound. */
+ private final Number max;
+
+ /** Whether the maximum is included in the allowed range. */
+ private final boolean boundIsAllowed;
+
+ /**
+ * Construct the exception.
+ *
+ * @param wrong Value that is larger than the maximum.
+ * @param max Maximum.
+ * @param boundIsAllowed if true the maximum is included in the allowed range.
+ */
+ public NumberIsTooLargeException(Number wrong, Number max, boolean boundIsAllowed) {
+ this(
+ boundIsAllowed
+ ? LocalizedFormats.NUMBER_TOO_LARGE
+ : LocalizedFormats.NUMBER_TOO_LARGE_BOUND_EXCLUDED,
+ wrong,
+ max,
+ boundIsAllowed);
+ }
+
+ /**
+ * Construct the exception with a specific context.
+ *
+ * @param specific Specific context pattern.
+ * @param wrong Value that is larger than the maximum.
+ * @param max Maximum.
+ * @param boundIsAllowed if true the maximum is included in the allowed range.
+ */
+ public NumberIsTooLargeException(
+ Localizable specific, Number wrong, Number max, boolean boundIsAllowed) {
+ super(specific, wrong, max);
+
+ this.max = max;
+ this.boundIsAllowed = boundIsAllowed;
+ }
+
+ /**
+ * @return {@code true} if the maximum is included in the allowed range.
+ */
+ public boolean getBoundIsAllowed() {
+ return boundIsAllowed;
+ }
+
+ /**
+ * @return the maximum.
+ */
+ public Number getMax() {
+ return max;
+ }
+}
diff --git a/src/main/java/org/apache/commons/math3/exception/NumberIsTooSmallException.java b/src/main/java/org/apache/commons/math3/exception/NumberIsTooSmallException.java
new file mode 100644
index 0000000..ec5b986
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/exception/NumberIsTooSmallException.java
@@ -0,0 +1,83 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math3.exception;
+
+import org.apache.commons.math3.exception.util.Localizable;
+import org.apache.commons.math3.exception.util.LocalizedFormats;
+
+/**
+ * Exception to be thrown when a number is too small.
+ *
+ * @since 2.2
+ */
+public class NumberIsTooSmallException extends MathIllegalNumberException {
+ /** Serializable version Id. */
+ private static final long serialVersionUID = -6100997100383932834L;
+
+ /** Higher bound. */
+ private final Number min;
+
+ /** Whether the maximum is included in the allowed range. */
+ private final boolean boundIsAllowed;
+
+ /**
+ * Construct the exception.
+ *
+ * @param wrong Value that is smaller than the minimum.
+ * @param min Minimum.
+ * @param boundIsAllowed Whether {@code min} is included in the allowed range.
+ */
+ public NumberIsTooSmallException(Number wrong, Number min, boolean boundIsAllowed) {
+ this(
+ boundIsAllowed
+ ? LocalizedFormats.NUMBER_TOO_SMALL
+ : LocalizedFormats.NUMBER_TOO_SMALL_BOUND_EXCLUDED,
+ wrong,
+ min,
+ boundIsAllowed);
+ }
+
+ /**
+ * Construct the exception with a specific context.
+ *
+ * @param specific Specific context pattern.
+ * @param wrong Value that is smaller than the minimum.
+ * @param min Minimum.
+ * @param boundIsAllowed Whether {@code min} is included in the allowed range.
+ */
+ public NumberIsTooSmallException(
+ Localizable specific, Number wrong, Number min, boolean boundIsAllowed) {
+ super(specific, wrong, min);
+
+ this.min = min;
+ this.boundIsAllowed = boundIsAllowed;
+ }
+
+ /**
+ * @return {@code true} if the minimum is included in the allowed range.
+ */
+ public boolean getBoundIsAllowed() {
+ return boundIsAllowed;
+ }
+
+ /**
+ * @return the minimum.
+ */
+ public Number getMin() {
+ return min;
+ }
+}
diff --git a/src/main/java/org/apache/commons/math3/exception/OutOfRangeException.java b/src/main/java/org/apache/commons/math3/exception/OutOfRangeException.java
new file mode 100644
index 0000000..b064346
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/exception/OutOfRangeException.java
@@ -0,0 +1,75 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math3.exception;
+
+import org.apache.commons.math3.exception.util.Localizable;
+import org.apache.commons.math3.exception.util.LocalizedFormats;
+
+/**
+ * Exception to be thrown when some argument is out of range.
+ *
+ * @since 2.2
+ */
+public class OutOfRangeException extends MathIllegalNumberException {
+ /** Serializable version Id. */
+ private static final long serialVersionUID = 111601815794403609L;
+
+ /** Lower bound. */
+ private final Number lo;
+
+ /** Higher bound. */
+ private final Number hi;
+
+ /**
+ * Construct an exception from the mismatched dimensions.
+ *
+ * @param wrong Requested value.
+ * @param lo Lower bound.
+ * @param hi Higher bound.
+ */
+ public OutOfRangeException(Number wrong, Number lo, Number hi) {
+ this(LocalizedFormats.OUT_OF_RANGE_SIMPLE, wrong, lo, hi);
+ }
+
+ /**
+ * Construct an exception from the mismatched dimensions with a specific context information.
+ *
+ * @param specific Context information.
+ * @param wrong Requested value.
+ * @param lo Lower bound.
+ * @param hi Higher bound.
+ */
+ public OutOfRangeException(Localizable specific, Number wrong, Number lo, Number hi) {
+ super(specific, wrong, lo, hi);
+ this.lo = lo;
+ this.hi = hi;
+ }
+
+ /**
+ * @return the lower bound.
+ */
+ public Number getLo() {
+ return lo;
+ }
+
+ /**
+ * @return the higher bound.
+ */
+ public Number getHi() {
+ return hi;
+ }
+}
diff --git a/src/main/java/org/apache/commons/math3/exception/TooManyEvaluationsException.java b/src/main/java/org/apache/commons/math3/exception/TooManyEvaluationsException.java
new file mode 100644
index 0000000..09da0a0
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/exception/TooManyEvaluationsException.java
@@ -0,0 +1,39 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math3.exception;
+
+import org.apache.commons.math3.exception.util.LocalizedFormats;
+
+/**
+ * Exception to be thrown when the maximal number of evaluations is exceeded.
+ *
+ * @since 3.0
+ */
+public class TooManyEvaluationsException extends MaxCountExceededException {
+ /** Serializable version Id. */
+ private static final long serialVersionUID = 4330003017885151975L;
+
+ /**
+ * Construct the exception.
+ *
+ * @param max Maximum number of evaluations.
+ */
+ public TooManyEvaluationsException(Number max) {
+ super(max);
+ getContext().addMessage(LocalizedFormats.EVALUATIONS);
+ }
+}
diff --git a/src/main/java/org/apache/commons/math3/exception/TooManyIterationsException.java b/src/main/java/org/apache/commons/math3/exception/TooManyIterationsException.java
new file mode 100644
index 0000000..c49f405
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/exception/TooManyIterationsException.java
@@ -0,0 +1,39 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math3.exception;
+
+import org.apache.commons.math3.exception.util.LocalizedFormats;
+
+/**
+ * Exception to be thrown when the maximal number of iterations is exceeded.
+ *
+ * @since 3.1
+ */
+public class TooManyIterationsException extends MaxCountExceededException {
+ /** Serializable version Id. */
+ private static final long serialVersionUID = 20121211L;
+
+ /**
+ * Construct the exception.
+ *
+ * @param max Maximum number of evaluations.
+ */
+ public TooManyIterationsException(Number max) {
+ super(max);
+ getContext().addMessage(LocalizedFormats.ITERATIONS);
+ }
+}
diff --git a/src/main/java/org/apache/commons/math3/exception/ZeroException.java b/src/main/java/org/apache/commons/math3/exception/ZeroException.java
new file mode 100644
index 0000000..90933ae
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/exception/ZeroException.java
@@ -0,0 +1,46 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math3.exception;
+
+import org.apache.commons.math3.exception.util.Localizable;
+import org.apache.commons.math3.exception.util.LocalizedFormats;
+
+/**
+ * Exception to be thrown when zero is provided where it is not allowed.
+ *
+ * @since 2.2
+ */
+public class ZeroException extends MathIllegalNumberException {
+
+ /** Serializable version identifier */
+ private static final long serialVersionUID = -1960874856936000015L;
+
+ /** Construct the exception. */
+ public ZeroException() {
+ this(LocalizedFormats.ZERO_NOT_ALLOWED);
+ }
+
+ /**
+ * Construct the exception with a specific context.
+ *
+ * @param specific Specific context pattern.
+ * @param arguments Arguments.
+ */
+ public ZeroException(Localizable specific, Object... arguments) {
+ super(specific, INTEGER_ZERO, arguments);
+ }
+}
diff --git a/src/main/java/org/apache/commons/math3/exception/package-info.java b/src/main/java/org/apache/commons/math3/exception/package-info.java
new file mode 100644
index 0000000..65cc05b
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/exception/package-info.java
@@ -0,0 +1,21 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/**
+ * Specialized exceptions for algorithms errors. The exceptions can be localized using simple java
+ * properties.
+ */
+package org.apache.commons.math3.exception;
diff --git a/src/main/java/org/apache/commons/math3/exception/util/ArgUtils.java b/src/main/java/org/apache/commons/math3/exception/util/ArgUtils.java
new file mode 100644
index 0000000..74214cc
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/exception/util/ArgUtils.java
@@ -0,0 +1,55 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math3.exception.util;
+
+import java.util.List;
+import java.util.ArrayList;
+
+/**
+ * Utility class for transforming the list of arguments passed to
+ * constructors of exceptions.
+ *
+ */
+public class ArgUtils {
+ /**
+ * Class contains only static methods.
+ */
+ private ArgUtils() {}
+
+ /**
+ * Transform a multidimensional array into a one-dimensional list.
+ *
+ * @param array Array (possibly multidimensional).
+ * @return a list of all the {@code Object} instances contained in
+ * {@code array}.
+ */
+ public static Object[] flatten(Object[] array) {
+ final List<Object> list = new ArrayList<Object>();
+ if (array != null) {
+ for (Object o : array) {
+ if (o instanceof Object[]) {
+ for (Object oR : flatten((Object[]) o)) {
+ list.add(oR);
+ }
+ } else {
+ list.add(o);
+ }
+ }
+ }
+ return list.toArray();
+ }
+}
diff --git a/src/main/java/org/apache/commons/math3/exception/util/DummyLocalizable.java b/src/main/java/org/apache/commons/math3/exception/util/DummyLocalizable.java
new file mode 100644
index 0000000..cd56708
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/exception/util/DummyLocalizable.java
@@ -0,0 +1,57 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math3.exception.util;
+
+import java.util.Locale;
+
+/**
+ * Dummy implementation of the {@link Localizable} interface, without localization.
+ *
+ * @since 2.2
+ */
+public class DummyLocalizable implements Localizable {
+
+ /** Serializable version identifier. */
+ private static final long serialVersionUID = 8843275624471387299L;
+
+ /** Source string. */
+ private final String source;
+
+ /** Simple constructor.
+ * @param source source text
+ */
+ public DummyLocalizable(final String source) {
+ this.source = source;
+ }
+
+ /** {@inheritDoc} */
+ public String getSourceString() {
+ return source;
+ }
+
+ /** {@inheritDoc} */
+ public String getLocalizedString(Locale locale) {
+ return source;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public String toString() {
+ return source;
+ }
+
+}
diff --git a/src/main/java/org/apache/commons/math3/exception/util/ExceptionContext.java b/src/main/java/org/apache/commons/math3/exception/util/ExceptionContext.java
new file mode 100644
index 0000000..1a78bd3
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/exception/util/ExceptionContext.java
@@ -0,0 +1,334 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math3.exception.util;
+
+import java.util.List;
+import java.util.ArrayList;
+import java.util.Set;
+import java.util.Map;
+import java.io.IOException;
+import java.io.Serializable;
+import java.io.ObjectOutputStream;
+import java.io.ObjectInputStream;
+import java.util.HashMap;
+import java.text.MessageFormat;
+import java.util.Locale;
+
+/**
+ * Class that contains the actual implementation of the functionality mandated
+ * by the {@link ExceptionContext} interface.
+ * All Commons Math exceptions delegate the interface's methods to this class.
+ *
+ * @since 3.0
+ */
+public class ExceptionContext implements Serializable {
+ /** Serializable version Id. */
+ private static final long serialVersionUID = -6024911025449780478L;
+ /**
+ * The throwable to which this context refers to.
+ */
+ private Throwable throwable;
+ /**
+ * Various informations that enrich the informative message.
+ */
+ private List<Localizable> msgPatterns;
+ /**
+ * Various informations that enrich the informative message.
+ * The arguments will replace the corresponding place-holders in
+ * {@link #msgPatterns}.
+ */
+ private List<Object[]> msgArguments;
+ /**
+ * Arbitrary context information.
+ */
+ private Map<String, Object> context;
+
+ /** Simple constructor.
+ * @param throwable the exception this context refers too
+ */
+ public ExceptionContext(final Throwable throwable) {
+ this.throwable = throwable;
+ msgPatterns = new ArrayList<Localizable>();
+ msgArguments = new ArrayList<Object[]>();
+ context = new HashMap<String, Object>();
+ }
+
+ /** Get a reference to the exception to which the context relates.
+ * @return a reference to the exception to which the context relates
+ */
+ public Throwable getThrowable() {
+ return throwable;
+ }
+
+ /**
+ * Adds a message.
+ *
+ * @param pattern Message pattern.
+ * @param arguments Values for replacing the placeholders in the message
+ * pattern.
+ */
+ public void addMessage(Localizable pattern,
+ Object ... arguments) {
+ msgPatterns.add(pattern);
+ msgArguments.add(ArgUtils.flatten(arguments));
+ }
+
+ /**
+ * Sets the context (key, value) pair.
+ * Keys are assumed to be unique within an instance. If the same key is
+ * assigned a new value, the previous one will be lost.
+ *
+ * @param key Context key (not null).
+ * @param value Context value.
+ */
+ public void setValue(String key, Object value) {
+ context.put(key, value);
+ }
+
+ /**
+ * Gets the value associated to the given context key.
+ *
+ * @param key Context key.
+ * @return the context value or {@code null} if the key does not exist.
+ */
+ public Object getValue(String key) {
+ return context.get(key);
+ }
+
+ /**
+ * Gets all the keys stored in the exception
+ *
+ * @return the set of keys.
+ */
+ public Set<String> getKeys() {
+ return context.keySet();
+ }
+
+ /**
+ * Gets the default message.
+ *
+ * @return the message.
+ */
+ public String getMessage() {
+ return getMessage(Locale.US);
+ }
+
+ /**
+ * Gets the message in the default locale.
+ *
+ * @return the localized message.
+ */
+ public String getLocalizedMessage() {
+ return getMessage(Locale.getDefault());
+ }
+
+ /**
+ * Gets the message in a specified locale.
+ *
+ * @param locale Locale in which the message should be translated.
+ * @return the localized message.
+ */
+ public String getMessage(final Locale locale) {
+ return buildMessage(locale, ": ");
+ }
+
+ /**
+ * Gets the message in a specified locale.
+ *
+ * @param locale Locale in which the message should be translated.
+ * @param separator Separator inserted between the message parts.
+ * @return the localized message.
+ */
+ public String getMessage(final Locale locale,
+ final String separator) {
+ return buildMessage(locale, separator);
+ }
+
+ /**
+ * Builds a message string.
+ *
+ * @param locale Locale in which the message should be translated.
+ * @param separator Message separator.
+ * @return a localized message string.
+ */
+ private String buildMessage(Locale locale,
+ String separator) {
+ final StringBuilder sb = new StringBuilder();
+ int count = 0;
+ final int len = msgPatterns.size();
+ for (int i = 0; i < len; i++) {
+ final Localizable pat = msgPatterns.get(i);
+ final Object[] args = msgArguments.get(i);
+ final MessageFormat fmt = new MessageFormat(pat.getLocalizedString(locale),
+ locale);
+ sb.append(fmt.format(args));
+ if (++count < len) {
+ // Add a separator if there are other messages.
+ sb.append(separator);
+ }
+ }
+
+ return sb.toString();
+ }
+
+ /**
+ * Serialize this object to the given stream.
+ *
+ * @param out Stream.
+ * @throws IOException This should never happen.
+ */
+ private void writeObject(ObjectOutputStream out)
+ throws IOException {
+ out.writeObject(throwable);
+ serializeMessages(out);
+ serializeContext(out);
+ }
+ /**
+ * Deserialize this object from the given stream.
+ *
+ * @param in Stream.
+ * @throws IOException This should never happen.
+ * @throws ClassNotFoundException This should never happen.
+ */
+ private void readObject(ObjectInputStream in)
+ throws IOException,
+ ClassNotFoundException {
+ throwable = (Throwable) in.readObject();
+ deSerializeMessages(in);
+ deSerializeContext(in);
+ }
+
+ /**
+ * Serialize {@link #msgPatterns} and {@link #msgArguments}.
+ *
+ * @param out Stream.
+ * @throws IOException This should never happen.
+ */
+ private void serializeMessages(ObjectOutputStream out)
+ throws IOException {
+ // Step 1.
+ final int len = msgPatterns.size();
+ out.writeInt(len);
+ // Step 2.
+ for (int i = 0; i < len; i++) {
+ final Localizable pat = msgPatterns.get(i);
+ // Step 3.
+ out.writeObject(pat);
+ final Object[] args = msgArguments.get(i);
+ final int aLen = args.length;
+ // Step 4.
+ out.writeInt(aLen);
+ for (int j = 0; j < aLen; j++) {
+ if (args[j] instanceof Serializable) {
+ // Step 5a.
+ out.writeObject(args[j]);
+ } else {
+ // Step 5b.
+ out.writeObject(nonSerializableReplacement(args[j]));
+ }
+ }
+ }
+ }
+
+ /**
+ * Deserialize {@link #msgPatterns} and {@link #msgArguments}.
+ *
+ * @param in Stream.
+ * @throws IOException This should never happen.
+ * @throws ClassNotFoundException This should never happen.
+ */
+ private void deSerializeMessages(ObjectInputStream in)
+ throws IOException,
+ ClassNotFoundException {
+ // Step 1.
+ final int len = in.readInt();
+ msgPatterns = new ArrayList<Localizable>(len);
+ msgArguments = new ArrayList<Object[]>(len);
+ // Step 2.
+ for (int i = 0; i < len; i++) {
+ // Step 3.
+ final Localizable pat = (Localizable) in.readObject();
+ msgPatterns.add(pat);
+ // Step 4.
+ final int aLen = in.readInt();
+ final Object[] args = new Object[aLen];
+ for (int j = 0; j < aLen; j++) {
+ // Step 5.
+ args[j] = in.readObject();
+ }
+ msgArguments.add(args);
+ }
+ }
+
+ /**
+ * Serialize {@link #context}.
+ *
+ * @param out Stream.
+ * @throws IOException This should never happen.
+ */
+ private void serializeContext(ObjectOutputStream out)
+ throws IOException {
+ // Step 1.
+ final int len = context.size();
+ out.writeInt(len);
+ for (Map.Entry<String, Object> entry : context.entrySet()) {
+ // Step 2.
+ out.writeObject(entry.getKey());
+ final Object value = entry.getValue();
+ if (value instanceof Serializable) {
+ // Step 3a.
+ out.writeObject(value);
+ } else {
+ // Step 3b.
+ out.writeObject(nonSerializableReplacement(value));
+ }
+ }
+ }
+
+ /**
+ * Deserialize {@link #context}.
+ *
+ * @param in Stream.
+ * @throws IOException This should never happen.
+ * @throws ClassNotFoundException This should never happen.
+ */
+ private void deSerializeContext(ObjectInputStream in)
+ throws IOException,
+ ClassNotFoundException {
+ // Step 1.
+ final int len = in.readInt();
+ context = new HashMap<String, Object>();
+ for (int i = 0; i < len; i++) {
+ // Step 2.
+ final String key = (String) in.readObject();
+ // Step 3.
+ final Object value = in.readObject();
+ context.put(key, value);
+ }
+ }
+
+ /**
+ * Replaces a non-serializable object with an error message string.
+ *
+ * @param obj Object that does not implement the {@code Serializable}
+ * interface.
+ * @return a string that mentions which class could not be serialized.
+ */
+ private String nonSerializableReplacement(Object obj) {
+ return "[Object could not be serialized: " + obj.getClass().getName() + "]";
+ }
+}
diff --git a/src/main/java/org/apache/commons/math3/exception/util/ExceptionContextProvider.java b/src/main/java/org/apache/commons/math3/exception/util/ExceptionContextProvider.java
new file mode 100644
index 0000000..913f66a
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/exception/util/ExceptionContextProvider.java
@@ -0,0 +1,33 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math3.exception.util;
+
+/**
+ * Interface for accessing the context data structure stored in Commons Math
+ * exceptions.
+ *
+ */
+public interface ExceptionContextProvider {
+ /**
+ * Gets a reference to the "rich context" data structure that allows to
+ * customize error messages and store key, value pairs in exceptions.
+ *
+ * @return a reference to the exception context.
+ */
+ ExceptionContext getContext();
+
+}
diff --git a/src/main/java/org/apache/commons/math3/exception/util/Localizable.java b/src/main/java/org/apache/commons/math3/exception/util/Localizable.java
new file mode 100644
index 0000000..9758bc2
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/exception/util/Localizable.java
@@ -0,0 +1,43 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math3.exception.util;
+
+import java.io.Serializable;
+import java.util.Locale;
+
+/**
+ * Interface for localizable strings.
+ *
+ * @since 2.2
+ */
+public interface Localizable extends Serializable {
+ /**
+ * Gets the source (non-localized) string.
+ *
+ * @return the source string.
+ */
+ String getSourceString();
+
+ /**
+ * Gets the localized string.
+ *
+ * @param locale locale into which to get the string.
+ * @return the localized string or the source string if no
+ * localized version is available.
+ */
+ String getLocalizedString(Locale locale);
+}
diff --git a/src/main/java/org/apache/commons/math3/exception/util/LocalizedFormats.java b/src/main/java/org/apache/commons/math3/exception/util/LocalizedFormats.java
new file mode 100644
index 0000000..8be9639
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/exception/util/LocalizedFormats.java
@@ -0,0 +1,414 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math3.exception.util;
+
+import java.util.Locale;
+import java.util.MissingResourceException;
+import java.util.ResourceBundle;
+
+/**
+ * Enumeration for localized messages formats used in exceptions messages.
+ * <p>
+ * The constants in this enumeration represent the available
+ * formats as localized strings. These formats are intended to be
+ * localized using simple properties files, using the constant
+ * name as the key and the property value as the message format.
+ * The source English format is provided in the constants themselves
+ * to serve both as a reminder for developers to understand the parameters
+ * needed by each format, as a basis for translators to create
+ * localized properties files, and as a default format if some
+ * translation is missing.
+ * </p>
+ * @since 2.2
+ */
+public enum LocalizedFormats implements Localizable {
+
+ // CHECKSTYLE: stop MultipleVariableDeclarations
+ // CHECKSTYLE: stop JavadocVariable
+
+ ARGUMENT_OUTSIDE_DOMAIN("Argument {0} outside domain [{1} ; {2}]"),
+ ARRAY_SIZE_EXCEEDS_MAX_VARIABLES("array size cannot be greater than {0}"),
+ ARRAY_SIZES_SHOULD_HAVE_DIFFERENCE_1("array sizes should have difference 1 ({0} != {1} + 1)"),
+ ARRAY_SUMS_TO_ZERO("array sums to zero"),
+ ASSYMETRIC_EIGEN_NOT_SUPPORTED("eigen decomposition of assymetric matrices not supported yet"),
+ AT_LEAST_ONE_COLUMN("matrix must have at least one column"),
+ AT_LEAST_ONE_ROW("matrix must have at least one row"),
+ BANDWIDTH("bandwidth ({0})"),
+ BESSEL_FUNCTION_BAD_ARGUMENT("Bessel function of order {0} cannot be computed for x = {1}"),
+ BESSEL_FUNCTION_FAILED_CONVERGENCE("Bessel function of order {0} failed to converge for x = {1}"),
+ BINOMIAL_INVALID_PARAMETERS_ORDER("must have n >= k for binomial coefficient (n, k), got k = {0}, n = {1}"),
+ BINOMIAL_NEGATIVE_PARAMETER("must have n >= 0 for binomial coefficient (n, k), got n = {0}"),
+ CANNOT_CLEAR_STATISTIC_CONSTRUCTED_FROM_EXTERNAL_MOMENTS("statistics constructed from external moments cannot be cleared"),
+ CANNOT_COMPUTE_0TH_ROOT_OF_UNITY("cannot compute 0-th root of unity, indefinite result"),
+ CANNOT_COMPUTE_BETA_DENSITY_AT_0_FOR_SOME_ALPHA("cannot compute beta density at 0 when alpha = {0,number}"),
+ CANNOT_COMPUTE_BETA_DENSITY_AT_1_FOR_SOME_BETA("cannot compute beta density at 1 when beta = %.3g"),
+ CANNOT_COMPUTE_NTH_ROOT_FOR_NEGATIVE_N("cannot compute nth root for null or negative n: {0}"),
+ CANNOT_DISCARD_NEGATIVE_NUMBER_OF_ELEMENTS("cannot discard a negative number of elements ({0})"),
+ CANNOT_FORMAT_INSTANCE_AS_3D_VECTOR("cannot format a {0} instance as a 3D vector"),
+ CANNOT_FORMAT_INSTANCE_AS_COMPLEX("cannot format a {0} instance as a complex number"),
+ CANNOT_FORMAT_INSTANCE_AS_REAL_VECTOR("cannot format a {0} instance as a real vector"),
+ CANNOT_FORMAT_OBJECT_TO_FRACTION("cannot format given object as a fraction number"),
+ CANNOT_INCREMENT_STATISTIC_CONSTRUCTED_FROM_EXTERNAL_MOMENTS("statistics constructed from external moments cannot be incremented"),
+ CANNOT_NORMALIZE_A_ZERO_NORM_VECTOR("cannot normalize a zero norm vector"),
+ CANNOT_RETRIEVE_AT_NEGATIVE_INDEX("elements cannot be retrieved from a negative array index {0}"),
+ CANNOT_SET_AT_NEGATIVE_INDEX("cannot set an element at a negative index {0}"),
+ CANNOT_SUBSTITUTE_ELEMENT_FROM_EMPTY_ARRAY("cannot substitute an element from an empty array"),
+ CANNOT_TRANSFORM_TO_DOUBLE("Conversion Exception in Transformation: {0}"),
+ CARDAN_ANGLES_SINGULARITY("Cardan angles singularity"),
+ CLASS_DOESNT_IMPLEMENT_COMPARABLE("class ({0}) does not implement Comparable"),
+ CLOSE_VERTICES("too close vertices near point ({0}, {1}, {2})"),
+ CLOSEST_ORTHOGONAL_MATRIX_HAS_NEGATIVE_DETERMINANT("the closest orthogonal matrix has a negative determinant {0}"),
+ COLUMN_INDEX_OUT_OF_RANGE("column index {0} out of allowed range [{1}, {2}]"),
+ COLUMN_INDEX("column index ({0})"), /* keep */
+ CONSTRAINT("constraint"), /* keep */
+ CONTINUED_FRACTION_INFINITY_DIVERGENCE("Continued fraction convergents diverged to +/- infinity for value {0}"),
+ CONTINUED_FRACTION_NAN_DIVERGENCE("Continued fraction diverged to NaN for value {0}"),
+ CONTRACTION_CRITERIA_SMALLER_THAN_EXPANSION_FACTOR("contraction criteria ({0}) smaller than the expansion factor ({1}). This would lead to a never ending loop of expansion and contraction as a newly expanded internal storage array would immediately satisfy the criteria for contraction."),
+ CONTRACTION_CRITERIA_SMALLER_THAN_ONE("contraction criteria smaller than one ({0}). This would lead to a never ending loop of expansion and contraction as an internal storage array length equal to the number of elements would satisfy the contraction criteria."),
+ CONVERGENCE_FAILED("convergence failed"), /* keep */
+ CROSSING_BOUNDARY_LOOPS("some outline boundary loops cross each other"),
+ CROSSOVER_RATE("crossover rate ({0})"),
+ CUMULATIVE_PROBABILITY_RETURNED_NAN("Cumulative probability function returned NaN for argument {0} p = {1}"),
+ DIFFERENT_ROWS_LENGTHS("some rows have length {0} while others have length {1}"),
+ DIFFERENT_ORIG_AND_PERMUTED_DATA("original and permuted data must contain the same elements"),
+ DIGEST_NOT_INITIALIZED("digest not initialized"),
+ DIMENSIONS_MISMATCH_2x2("got {0}x{1} but expected {2}x{3}"), /* keep */
+ DIMENSIONS_MISMATCH_SIMPLE("{0} != {1}"), /* keep */
+ DIMENSIONS_MISMATCH("dimensions mismatch"), /* keep */
+ DISCRETE_CUMULATIVE_PROBABILITY_RETURNED_NAN("Discrete cumulative probability function returned NaN for argument {0}"),
+ DISTRIBUTION_NOT_LOADED("distribution not loaded"),
+ DUPLICATED_ABSCISSA_DIVISION_BY_ZERO("duplicated abscissa {0} causes division by zero"),
+ EDGE_CONNECTED_TO_ONE_FACET("edge joining points ({0}, {1}, {2}) and ({3}, {4}, {5}) is connected to one facet only"),
+ ELITISM_RATE("elitism rate ({0})"),
+ EMPTY_CLUSTER_IN_K_MEANS("empty cluster in k-means"),
+ EMPTY_INTERPOLATION_SAMPLE("sample for interpolation is empty"),
+ EMPTY_POLYNOMIALS_COEFFICIENTS_ARRAY("empty polynomials coefficients array"), /* keep */
+ EMPTY_SELECTED_COLUMN_INDEX_ARRAY("empty selected column index array"),
+ EMPTY_SELECTED_ROW_INDEX_ARRAY("empty selected row index array"),
+ EMPTY_STRING_FOR_IMAGINARY_CHARACTER("empty string for imaginary character"),
+ ENDPOINTS_NOT_AN_INTERVAL("endpoints do not specify an interval: [{0}, {1}]"),
+ EQUAL_VERTICES_IN_SIMPLEX("equal vertices {0} and {1} in simplex configuration"),
+ EULER_ANGLES_SINGULARITY("Euler angles singularity"),
+ EVALUATION("evaluation"), /* keep */
+ EXPANSION_FACTOR_SMALLER_THAN_ONE("expansion factor smaller than one ({0})"),
+ FACET_ORIENTATION_MISMATCH("facets orientation mismatch around edge joining points ({0}, {1}, {2}) and ({3}, {4}, {5})"),
+ FACTORIAL_NEGATIVE_PARAMETER("must have n >= 0 for n!, got n = {0}"),
+ FAILED_BRACKETING("number of iterations={4}, maximum iterations={5}, initial={6}, lower bound={7}, upper bound={8}, final a value={0}, final b value={1}, f(a)={2}, f(b)={3}"),
+ FAILED_FRACTION_CONVERSION("Unable to convert {0} to fraction after {1} iterations"),
+ FIRST_COLUMNS_NOT_INITIALIZED_YET("first {0} columns are not initialized yet"),
+ FIRST_ELEMENT_NOT_ZERO("first element is not 0: {0}"),
+ FIRST_ROWS_NOT_INITIALIZED_YET("first {0} rows are not initialized yet"),
+ FRACTION_CONVERSION_OVERFLOW("Overflow trying to convert {0} to fraction ({1}/{2})"),
+ FUNCTION_NOT_DIFFERENTIABLE("function is not differentiable"),
+ FUNCTION_NOT_POLYNOMIAL("function is not polynomial"),
+ GCD_OVERFLOW_32_BITS("overflow: gcd({0}, {1}) is 2^31"),
+ GCD_OVERFLOW_64_BITS("overflow: gcd({0}, {1}) is 2^63"),
+ HOLE_BETWEEN_MODELS_TIME_RANGES("{0} wide hole between models time ranges"),
+ ILL_CONDITIONED_OPERATOR("condition number {1} is too high "),
+ INCONSISTENT_STATE_AT_2_PI_WRAPPING("inconsistent state at 2\u03c0 wrapping"),
+ INDEX_LARGER_THAN_MAX("the index specified: {0} is larger than the current maximal index {1}"),
+ INDEX_NOT_POSITIVE("index ({0}) is not positive"),
+ INDEX_OUT_OF_RANGE("index {0} out of allowed range [{1}, {2}]"),
+ INDEX("index ({0})"), /* keep */
+ NOT_FINITE_NUMBER("{0} is not a finite number"), /* keep */
+ INFINITE_BOUND("interval bounds must be finite"),
+ ARRAY_ELEMENT("value {0} at index {1}"), /* keep */
+ INFINITE_ARRAY_ELEMENT("Array contains an infinite element, {0} at index {1}"),
+ INFINITE_VALUE_CONVERSION("cannot convert infinite value"),
+ INITIAL_CAPACITY_NOT_POSITIVE("initial capacity ({0}) is not positive"),
+ INITIAL_COLUMN_AFTER_FINAL_COLUMN("initial column {1} after final column {0}"),
+ INITIAL_ROW_AFTER_FINAL_ROW("initial row {1} after final row {0}"),
+ @Deprecated
+ INPUT_DATA_FROM_UNSUPPORTED_DATASOURCE("input data comes from unsupported datasource: {0}, supported sources: {1}, {2}"),
+ INSTANCES_NOT_COMPARABLE_TO_EXISTING_VALUES("instance of class {0} not comparable to existing values"),
+ INSUFFICIENT_DATA("insufficient data"),
+ INSUFFICIENT_DATA_FOR_T_STATISTIC("insufficient data for t statistic, needs at least 2, got {0}"),
+ INSUFFICIENT_DIMENSION("insufficient dimension {0}, must be at least {1}"),
+ DIMENSION("dimension ({0})"), /* keep */
+ INSUFFICIENT_OBSERVED_POINTS_IN_SAMPLE("sample contains {0} observed points, at least {1} are required"),
+ INSUFFICIENT_ROWS_AND_COLUMNS("insufficient data: only {0} rows and {1} columns."),
+ INTEGRATION_METHOD_NEEDS_AT_LEAST_TWO_PREVIOUS_POINTS("multistep method needs at least {0} previous steps, got {1}"),
+ INTERNAL_ERROR("internal error, please fill a bug report at {0}"),
+ INVALID_BINARY_DIGIT("invalid binary digit: {0}"),
+ INVALID_BINARY_CHROMOSOME("binary mutation works on BinaryChromosome only"),
+ INVALID_BRACKETING_PARAMETERS("invalid bracketing parameters: lower bound={0}, initial={1}, upper bound={2}"),
+ INVALID_FIXED_LENGTH_CHROMOSOME("one-point crossover only works with fixed-length chromosomes"),
+ INVALID_IMPLEMENTATION("required functionality is missing in {0}"),
+ INVALID_INTERVAL_INITIAL_VALUE_PARAMETERS("invalid interval, initial value parameters: lower={0}, initial={1}, upper={2}"),
+ INVALID_ITERATIONS_LIMITS("invalid iteration limits: min={0}, max={1}"),
+ INVALID_MAX_ITERATIONS("bad value for maximum iterations number: {0}"),
+ NOT_ENOUGH_DATA_REGRESSION("the number of observations is not sufficient to conduct regression"),
+ INVALID_REGRESSION_ARRAY("input data array length = {0} does not match the number of observations = {1} and the number of regressors = {2}"),
+ INVALID_REGRESSION_OBSERVATION("length of regressor array = {0} does not match the number of variables = {1} in the model"),
+ INVALID_ROUNDING_METHOD("invalid rounding method {0}, valid methods: {1} ({2}), {3} ({4}), {5} ({6}), {7} ({8}), {9} ({10}), {11} ({12}), {13} ({14}), {15} ({16})"),
+ ITERATOR_EXHAUSTED("iterator exhausted"),
+ ITERATIONS("iterations"), /* keep */
+ LCM_OVERFLOW_32_BITS("overflow: lcm({0}, {1}) is 2^31"),
+ LCM_OVERFLOW_64_BITS("overflow: lcm({0}, {1}) is 2^63"),
+ LIST_OF_CHROMOSOMES_BIGGER_THAN_POPULATION_SIZE("list of chromosomes bigger than maxPopulationSize"),
+ LOESS_EXPECTS_AT_LEAST_ONE_POINT("Loess expects at least 1 point"),
+ LOWER_BOUND_NOT_BELOW_UPPER_BOUND("lower bound ({0}) must be strictly less than upper bound ({1})"), /* keep */
+ LOWER_ENDPOINT_ABOVE_UPPER_ENDPOINT("lower endpoint ({0}) must be less than or equal to upper endpoint ({1})"),
+ MAP_MODIFIED_WHILE_ITERATING("map has been modified while iterating"),
+ MULTISTEP_STARTER_STOPPED_EARLY("multistep integrator starter stopped early, maybe too large step size"),
+ EVALUATIONS("evaluations"), /* keep */
+ MAX_COUNT_EXCEEDED("maximal count ({0}) exceeded"), /* keep */
+ MAX_ITERATIONS_EXCEEDED("maximal number of iterations ({0}) exceeded"),
+ MINIMAL_STEPSIZE_REACHED_DURING_INTEGRATION("minimal step size ({1,number,0.00E00}) reached, integration needs {0,number,0.00E00}"),
+ MISMATCHED_LOESS_ABSCISSA_ORDINATE_ARRAYS("Loess expects the abscissa and ordinate arrays to be of the same size, but got {0} abscissae and {1} ordinatae"),
+ MUTATION_RATE("mutation rate ({0})"),
+ NAN_ELEMENT_AT_INDEX("element {0} is NaN"),
+ NAN_VALUE_CONVERSION("cannot convert NaN value"),
+ NEGATIVE_BRIGHTNESS_EXPONENT("brightness exponent should be positive or null, but got {0}"),
+ NEGATIVE_COMPLEX_MODULE("negative complex module {0}"),
+ NEGATIVE_ELEMENT_AT_2D_INDEX("element ({0}, {1}) is negative: {2}"),
+ NEGATIVE_ELEMENT_AT_INDEX("element {0} is negative: {1}"),
+ NEGATIVE_NUMBER_OF_SUCCESSES("number of successes must be non-negative ({0})"),
+ NUMBER_OF_SUCCESSES("number of successes ({0})"), /* keep */
+ NEGATIVE_NUMBER_OF_TRIALS("number of trials must be non-negative ({0})"),
+ NUMBER_OF_INTERPOLATION_POINTS("number of interpolation points ({0})"), /* keep */
+ NUMBER_OF_TRIALS("number of trials ({0})"),
+ NOT_CONVEX("vertices do not form a convex hull in CCW winding"),
+ NOT_CONVEX_HYPERPLANES("hyperplanes do not define a convex region"),
+ ROBUSTNESS_ITERATIONS("number of robustness iterations ({0})"),
+ START_POSITION("start position ({0})"), /* keep */
+ NON_CONVERGENT_CONTINUED_FRACTION("Continued fraction convergents failed to converge (in less than {0} iterations) for value {1}"),
+ NON_INVERTIBLE_TRANSFORM("non-invertible affine transform collapses some lines into single points"),
+ NON_POSITIVE_MICROSPHERE_ELEMENTS("number of microsphere elements must be positive, but got {0}"),
+ NON_POSITIVE_POLYNOMIAL_DEGREE("polynomial degree must be positive: degree={0}"),
+ NON_REAL_FINITE_ABSCISSA("all abscissae must be finite real numbers, but {0}-th is {1}"),
+ NON_REAL_FINITE_ORDINATE("all ordinatae must be finite real numbers, but {0}-th is {1}"),
+ NON_REAL_FINITE_WEIGHT("all weights must be finite real numbers, but {0}-th is {1}"),
+ NON_SQUARE_MATRIX("non square ({0}x{1}) matrix"),
+ NORM("Norm ({0})"), /* keep */
+ NORMALIZE_INFINITE("Cannot normalize to an infinite value"),
+ NORMALIZE_NAN("Cannot normalize to NaN"),
+ NOT_ADDITION_COMPATIBLE_MATRICES("{0}x{1} and {2}x{3} matrices are not addition compatible"),
+ NOT_DECREASING_NUMBER_OF_POINTS("points {0} and {1} are not decreasing ({2} < {3})"),
+ NOT_DECREASING_SEQUENCE("points {3} and {2} are not decreasing ({1} < {0})"), /* keep */
+ NOT_ENOUGH_DATA_FOR_NUMBER_OF_PREDICTORS("not enough data ({0} rows) for this many predictors ({1} predictors)"),
+ NOT_ENOUGH_POINTS_IN_SPLINE_PARTITION("spline partition must have at least {0} points, got {1}"),
+ NOT_INCREASING_NUMBER_OF_POINTS("points {0} and {1} are not increasing ({2} > {3})"),
+ NOT_INCREASING_SEQUENCE("points {3} and {2} are not increasing ({1} > {0})"), /* keep */
+ NOT_MULTIPLICATION_COMPATIBLE_MATRICES("{0}x{1} and {2}x{3} matrices are not multiplication compatible"),
+ NOT_POSITIVE_DEFINITE_MATRIX("not positive definite matrix"), /* keep */
+ NON_POSITIVE_DEFINITE_MATRIX("not positive definite matrix: diagonal element at ({1},{1}) is smaller than {2} ({0})"),
+ NON_POSITIVE_DEFINITE_OPERATOR("non positive definite linear operator"), /* keep */
+ NON_SELF_ADJOINT_OPERATOR("non self-adjoint linear operator"), /* keep */
+ NON_SQUARE_OPERATOR("non square ({0}x{1}) linear operator"), /* keep */
+ DEGREES_OF_FREEDOM("degrees of freedom ({0})"), /* keep */
+ NOT_POSITIVE_DEGREES_OF_FREEDOM("degrees of freedom must be positive ({0})"),
+ NOT_POSITIVE_ELEMENT_AT_INDEX("element {0} is not positive: {1}"),
+ NOT_POSITIVE_EXPONENT("invalid exponent {0} (must be positive)"),
+ NUMBER_OF_ELEMENTS_SHOULD_BE_POSITIVE("number of elements should be positive ({0})"),
+ BASE("base ({0})"), /* keep */
+ EXPONENT("exponent ({0})"), /* keep */
+ NOT_POSITIVE_LENGTH("length must be positive ({0})"),
+ LENGTH("length ({0})"), /* keep */
+ NOT_POSITIVE_MEAN("mean must be positive ({0})"),
+ MEAN("mean ({0})"), /* keep */
+ NOT_POSITIVE_NUMBER_OF_SAMPLES("number of sample is not positive: {0}"),
+ NUMBER_OF_SAMPLES("number of samples ({0})"), /* keep */
+ NOT_POSITIVE_PERMUTATION("permutation k ({0}) must be positive"),
+ PERMUTATION_SIZE("permutation size ({0}"), /* keep */
+ NOT_POSITIVE_POISSON_MEAN("the Poisson mean must be positive ({0})"),
+ NOT_POSITIVE_POPULATION_SIZE("population size must be positive ({0})"),
+ POPULATION_SIZE("population size ({0})"), /* keep */
+ NOT_POSITIVE_ROW_DIMENSION("invalid row dimension: {0} (must be positive)"),
+ NOT_POSITIVE_SAMPLE_SIZE("sample size must be positive ({0})"),
+ NOT_POSITIVE_SCALE("scale must be positive ({0})"),
+ SCALE("scale ({0})"), /* keep */
+ NOT_POSITIVE_SHAPE("shape must be positive ({0})"),
+ SHAPE("shape ({0})"), /* keep */
+ NOT_POSITIVE_STANDARD_DEVIATION("standard deviation must be positive ({0})"),
+ STANDARD_DEVIATION("standard deviation ({0})"), /* keep */
+ NOT_POSITIVE_UPPER_BOUND("upper bound must be positive ({0})"),
+ NOT_POSITIVE_WINDOW_SIZE("window size must be positive ({0})"),
+ NOT_POWER_OF_TWO("{0} is not a power of 2"),
+ NOT_POWER_OF_TWO_CONSIDER_PADDING("{0} is not a power of 2, consider padding for fix"),
+ NOT_POWER_OF_TWO_PLUS_ONE("{0} is not a power of 2 plus one"),
+ NOT_STRICTLY_DECREASING_NUMBER_OF_POINTS("points {0} and {1} are not strictly decreasing ({2} <= {3})"),
+ NOT_STRICTLY_DECREASING_SEQUENCE("points {3} and {2} are not strictly decreasing ({1} <= {0})"), /* keep */
+ NOT_STRICTLY_INCREASING_KNOT_VALUES("knot values must be strictly increasing"),
+ NOT_STRICTLY_INCREASING_NUMBER_OF_POINTS("points {0} and {1} are not strictly increasing ({2} >= {3})"),
+ NOT_STRICTLY_INCREASING_SEQUENCE("points {3} and {2} are not strictly increasing ({1} >= {0})"), /* keep */
+ NOT_SUBTRACTION_COMPATIBLE_MATRICES("{0}x{1} and {2}x{3} matrices are not subtraction compatible"),
+ NOT_SUPPORTED_IN_DIMENSION_N("method not supported in dimension {0}"),
+ NOT_SYMMETRIC_MATRIX("not symmetric matrix"),
+ NON_SYMMETRIC_MATRIX("non symmetric matrix: the difference between entries at ({0},{1}) and ({1},{0}) is larger than {2}"), /* keep */
+ NO_BIN_SELECTED("no bin selected"),
+ NO_CONVERGENCE_WITH_ANY_START_POINT("none of the {0} start points lead to convergence"), /* keep */
+ NO_DATA("no data"), /* keep */
+ NO_DEGREES_OF_FREEDOM("no degrees of freedom ({0} measurements, {1} parameters)"),
+ NO_DENSITY_FOR_THIS_DISTRIBUTION("This distribution does not have a density function implemented"),
+ NO_FEASIBLE_SOLUTION("no feasible solution"),
+ NO_OPTIMUM_COMPUTED_YET("no optimum computed yet"), /* keep */
+ NO_REGRESSORS("Regression model must include at least one regressor"),
+ NO_RESULT_AVAILABLE("no result available"),
+ NO_SUCH_MATRIX_ENTRY("no entry at indices ({0}, {1}) in a {2}x{3} matrix"),
+ NAN_NOT_ALLOWED("NaN is not allowed"),
+ NULL_NOT_ALLOWED("null is not allowed"), /* keep */
+ ARRAY_ZERO_LENGTH_OR_NULL_NOT_ALLOWED("a null or zero length array not allowed"),
+ COVARIANCE_MATRIX("covariance matrix"), /* keep */
+ DENOMINATOR("denominator"), /* keep */
+ DENOMINATOR_FORMAT("denominator format"), /* keep */
+ FRACTION("fraction"), /* keep */
+ FUNCTION("function"), /* keep */
+ IMAGINARY_FORMAT("imaginary format"), /* keep */
+ INPUT_ARRAY("input array"), /* keep */
+ NUMERATOR("numerator"), /* keep */
+ NUMERATOR_FORMAT("numerator format"), /* keep */
+ OBJECT_TRANSFORMATION("conversion exception in transformation"), /* keep */
+ REAL_FORMAT("real format"), /* keep */
+ WHOLE_FORMAT("whole format"), /* keep */
+ NUMBER_TOO_LARGE("{0} is larger than the maximum ({1})"), /* keep */
+ NUMBER_TOO_SMALL("{0} is smaller than the minimum ({1})"), /* keep */
+ NUMBER_TOO_LARGE_BOUND_EXCLUDED("{0} is larger than, or equal to, the maximum ({1})"), /* keep */
+ NUMBER_TOO_SMALL_BOUND_EXCLUDED("{0} is smaller than, or equal to, the minimum ({1})"), /* keep */
+ NUMBER_OF_SUCCESS_LARGER_THAN_POPULATION_SIZE("number of successes ({0}) must be less than or equal to population size ({1})"),
+ NUMERATOR_OVERFLOW_AFTER_MULTIPLY("overflow, numerator too large after multiply: {0}"),
+ N_POINTS_GAUSS_LEGENDRE_INTEGRATOR_NOT_SUPPORTED("{0} points Legendre-Gauss integrator not supported, number of points must be in the {1}-{2} range"),
+ OBSERVED_COUNTS_ALL_ZERO("observed counts are all 0 in observed array {0}"),
+ OBSERVED_COUNTS_BOTTH_ZERO_FOR_ENTRY("observed counts are both zero for entry {0}"),
+ BOBYQA_BOUND_DIFFERENCE_CONDITION("the difference between the upper and lower bound must be larger than twice the initial trust region radius ({0})"),
+ OUT_OF_BOUNDS_QUANTILE_VALUE("out of bounds quantile value: {0}, must be in (0, 100]"),
+ OUT_OF_BOUNDS_CONFIDENCE_LEVEL("out of bounds confidence level {0}, must be between {1} and {2}"),
+ OUT_OF_BOUND_SIGNIFICANCE_LEVEL("out of bounds significance level {0}, must be between {1} and {2}"),
+ SIGNIFICANCE_LEVEL("significance level ({0})"), /* keep */
+ OUT_OF_ORDER_ABSCISSA_ARRAY("the abscissae array must be sorted in a strictly increasing order, but the {0}-th element is {1} whereas {2}-th is {3}"),
+ OUT_OF_PLANE("point ({0}, {1}, {2}) is out of plane"),
+ OUT_OF_RANGE_ROOT_OF_UNITY_INDEX("out of range root of unity index {0} (must be in [{1};{2}])"),
+ OUT_OF_RANGE("out of range"), /* keep */
+ OUT_OF_RANGE_SIMPLE("{0} out of [{1}, {2}] range"), /* keep */
+ OUT_OF_RANGE_LEFT("{0} out of ({1}, {2}] range"),
+ OUT_OF_RANGE_RIGHT("{0} out of [{1}, {2}) range"),
+ OUTLINE_BOUNDARY_LOOP_OPEN("an outline boundary loop is open"),
+ OVERFLOW("overflow"), /* keep */
+ OVERFLOW_IN_FRACTION("overflow in fraction {0}/{1}, cannot negate"),
+ OVERFLOW_IN_ADDITION("overflow in addition: {0} + {1}"),
+ OVERFLOW_IN_SUBTRACTION("overflow in subtraction: {0} - {1}"),
+ OVERFLOW_IN_MULTIPLICATION("overflow in multiplication: {0} * {1}"),
+ PERCENTILE_IMPLEMENTATION_CANNOT_ACCESS_METHOD("cannot access {0} method in percentile implementation {1}"),
+ PERCENTILE_IMPLEMENTATION_UNSUPPORTED_METHOD("percentile implementation {0} does not support {1}"),
+ PERMUTATION_EXCEEDS_N("permutation size ({0}) exceeds permuation domain ({1})"), /* keep */
+ POLYNOMIAL("polynomial"), /* keep */
+ POLYNOMIAL_INTERPOLANTS_MISMATCH_SEGMENTS("number of polynomial interpolants must match the number of segments ({0} != {1} - 1)"),
+ POPULATION_LIMIT_NOT_POSITIVE("population limit has to be positive"),
+ POWER_NEGATIVE_PARAMETERS("cannot raise an integral value to a negative power ({0}^{1})"),
+ PROPAGATION_DIRECTION_MISMATCH("propagation direction mismatch"),
+ RANDOMKEY_MUTATION_WRONG_CLASS("RandomKeyMutation works only with RandomKeys, not {0}"),
+ ROOTS_OF_UNITY_NOT_COMPUTED_YET("roots of unity have not been computed yet"),
+ ROTATION_MATRIX_DIMENSIONS("a {0}x{1} matrix cannot be a rotation matrix"),
+ ROW_INDEX_OUT_OF_RANGE("row index {0} out of allowed range [{1}, {2}]"),
+ ROW_INDEX("row index ({0})"), /* keep */
+ SAME_SIGN_AT_ENDPOINTS("function values at endpoints do not have different signs, endpoints: [{0}, {1}], values: [{2}, {3}]"),
+ SAMPLE_SIZE_EXCEEDS_COLLECTION_SIZE("sample size ({0}) exceeds collection size ({1})"), /* keep */
+ SAMPLE_SIZE_LARGER_THAN_POPULATION_SIZE("sample size ({0}) must be less than or equal to population size ({1})"),
+ SIMPLEX_NEED_ONE_POINT("simplex must contain at least one point"),
+ SIMPLE_MESSAGE("{0}"),
+ SINGULAR_MATRIX("matrix is singular"), /* keep */
+ SINGULAR_OPERATOR("operator is singular"),
+ SUBARRAY_ENDS_AFTER_ARRAY_END("subarray ends after array end"),
+ TOO_LARGE_CUTOFF_SINGULAR_VALUE("cutoff singular value is {0}, should be at most {1}"),
+ TOO_LARGE_TOURNAMENT_ARITY("tournament arity ({0}) cannot be bigger than population size ({1})"),
+ TOO_MANY_ELEMENTS_TO_DISCARD_FROM_ARRAY("cannot discard {0} elements from a {1} elements array"),
+ TOO_MANY_REGRESSORS("too many regressors ({0}) specified, only {1} in the model"),
+ TOO_SMALL_COST_RELATIVE_TOLERANCE("cost relative tolerance is too small ({0}), no further reduction in the sum of squares is possible"),
+ TOO_SMALL_INTEGRATION_INTERVAL("too small integration interval: length = {0}"),
+ TOO_SMALL_ORTHOGONALITY_TOLERANCE("orthogonality tolerance is too small ({0}), solution is orthogonal to the jacobian"),
+ TOO_SMALL_PARAMETERS_RELATIVE_TOLERANCE("parameters relative tolerance is too small ({0}), no further improvement in the approximate solution is possible"),
+ TRUST_REGION_STEP_FAILED("trust region step has failed to reduce Q"),
+ TWO_OR_MORE_CATEGORIES_REQUIRED("two or more categories required, got {0}"),
+ TWO_OR_MORE_VALUES_IN_CATEGORY_REQUIRED("two or more values required in each category, one has {0}"),
+ UNABLE_TO_BRACKET_OPTIMUM_IN_LINE_SEARCH("unable to bracket optimum in line search"),
+ UNABLE_TO_COMPUTE_COVARIANCE_SINGULAR_PROBLEM("unable to compute covariances: singular problem"),
+ UNABLE_TO_FIRST_GUESS_HARMONIC_COEFFICIENTS("unable to first guess the harmonic coefficients"),
+ UNABLE_TO_ORTHOGONOLIZE_MATRIX("unable to orthogonalize matrix in {0} iterations"),
+ UNABLE_TO_PERFORM_QR_DECOMPOSITION_ON_JACOBIAN("unable to perform Q.R decomposition on the {0}x{1} jacobian matrix"),
+ UNABLE_TO_SOLVE_SINGULAR_PROBLEM("unable to solve: singular problem"),
+ UNBOUNDED_SOLUTION("unbounded solution"),
+ UNKNOWN_MODE("unknown mode {0}, known modes: {1} ({2}), {3} ({4}), {5} ({6}), {7} ({8}), {9} ({10}) and {11} ({12})"),
+ UNKNOWN_PARAMETER("unknown parameter {0}"),
+ UNMATCHED_ODE_IN_EXPANDED_SET("ode does not match the main ode set in the extended set"),
+ CANNOT_PARSE_AS_TYPE("string \"{0}\" unparseable (from position {1}) as an object of type {2}"), /* keep */
+ CANNOT_PARSE("string \"{0}\" unparseable (from position {1})"), /* keep */
+ UNPARSEABLE_3D_VECTOR("unparseable 3D vector: \"{0}\""),
+ UNPARSEABLE_COMPLEX_NUMBER("unparseable complex number: \"{0}\""),
+ UNPARSEABLE_REAL_VECTOR("unparseable real vector: \"{0}\""),
+ UNSUPPORTED_EXPANSION_MODE("unsupported expansion mode {0}, supported modes are {1} ({2}) and {3} ({4})"),
+ UNSUPPORTED_OPERATION("unsupported operation"), /* keep */
+ ARITHMETIC_EXCEPTION("arithmetic exception"), /* keep */
+ ILLEGAL_STATE("illegal state"), /* keep */
+ USER_EXCEPTION("exception generated in user code"), /* keep */
+ URL_CONTAINS_NO_DATA("URL {0} contains no data"),
+ VALUES_ADDED_BEFORE_CONFIGURING_STATISTIC("{0} values have been added before statistic is configured"),
+ VECTOR_LENGTH_MISMATCH("vector length mismatch: got {0} but expected {1}"),
+ VECTOR_MUST_HAVE_AT_LEAST_ONE_ELEMENT("vector must have at least one element"),
+ WEIGHT_AT_LEAST_ONE_NON_ZERO("weigth array must contain at least one non-zero value"),
+ WRONG_BLOCK_LENGTH("wrong array shape (block length = {0}, expected {1})"),
+ WRONG_NUMBER_OF_POINTS("{0} points are required, got only {1}"),
+ NUMBER_OF_POINTS("number of points ({0})"), /* keep */
+ ZERO_DENOMINATOR("denominator must be different from 0"), /* keep */
+ ZERO_DENOMINATOR_IN_FRACTION("zero denominator in fraction {0}/{1}"),
+ ZERO_FRACTION_TO_DIVIDE_BY("the fraction to divide by must not be zero: {0}/{1}"),
+ ZERO_NORM("zero norm"),
+ ZERO_NORM_FOR_ROTATION_AXIS("zero norm for rotation axis"),
+ ZERO_NORM_FOR_ROTATION_DEFINING_VECTOR("zero norm for rotation defining vector"),
+ ZERO_NOT_ALLOWED("zero not allowed here");
+
+ // CHECKSTYLE: resume JavadocVariable
+ // CHECKSTYLE: resume MultipleVariableDeclarations
+
+
+ /** Source English format. */
+ private final String sourceFormat;
+
+ /** Simple constructor.
+ * @param sourceFormat source English format to use when no
+ * localized version is available
+ */
+ LocalizedFormats(final String sourceFormat) {
+ this.sourceFormat = sourceFormat;
+ }
+
+ /** {@inheritDoc} */
+ public String getSourceString() {
+ return sourceFormat;
+ }
+
+ /** {@inheritDoc} */
+ public String getLocalizedString(final Locale locale) {
+ try {
+ final String path = LocalizedFormats.class.getName().replaceAll("\\.", "/");
+ ResourceBundle bundle =
+ ResourceBundle.getBundle("assets/" + path, locale);
+ if (bundle.getLocale().getLanguage().equals(locale.getLanguage())) {
+ // the value of the resource is the translated format
+ return bundle.getString(toString());
+ }
+
+ } catch (MissingResourceException mre) { // NOPMD
+ // do nothing here
+ }
+
+ // either the locale is not supported or the resource is unknown
+ // don't translate and fall back to using the source format
+ return sourceFormat;
+
+ }
+
+}
diff --git a/src/main/java/org/apache/commons/math3/exception/util/package-info.java b/src/main/java/org/apache/commons/math3/exception/util/package-info.java
new file mode 100644
index 0000000..6439a8d
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/exception/util/package-info.java
@@ -0,0 +1,22 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/**
+ *
+ * Classes supporting exception localization.
+ *
+ */
+package org.apache.commons.math3.exception.util;
diff --git a/src/main/java/org/apache/commons/math3/filter/DefaultMeasurementModel.java b/src/main/java/org/apache/commons/math3/filter/DefaultMeasurementModel.java
new file mode 100644
index 0000000..d0b4440
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/filter/DefaultMeasurementModel.java
@@ -0,0 +1,77 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math3.filter;
+
+import org.apache.commons.math3.exception.DimensionMismatchException;
+import org.apache.commons.math3.exception.NoDataException;
+import org.apache.commons.math3.exception.NullArgumentException;
+import org.apache.commons.math3.linear.Array2DRowRealMatrix;
+import org.apache.commons.math3.linear.RealMatrix;
+
+/**
+ * Default implementation of a {@link MeasurementModel} for the use with a {@link KalmanFilter}.
+ *
+ * @since 3.0
+ */
+public class DefaultMeasurementModel implements MeasurementModel {
+
+ /**
+ * The measurement matrix, used to associate the measurement vector to the internal state
+ * estimation vector.
+ */
+ private RealMatrix measurementMatrix;
+
+ /** The measurement noise covariance matrix. */
+ private RealMatrix measurementNoise;
+
+ /**
+ * Create a new {@link MeasurementModel}, taking double arrays as input parameters for the
+ * respective measurement matrix and noise.
+ *
+ * @param measMatrix the measurement matrix
+ * @param measNoise the measurement noise matrix
+ * @throws NullArgumentException if any of the input matrices is {@code null}
+ * @throws NoDataException if any row / column dimension of the input matrices is zero
+ * @throws DimensionMismatchException if any of the input matrices is non-rectangular
+ */
+ public DefaultMeasurementModel(final double[][] measMatrix, final double[][] measNoise)
+ throws NullArgumentException, NoDataException, DimensionMismatchException {
+ this(new Array2DRowRealMatrix(measMatrix), new Array2DRowRealMatrix(measNoise));
+ }
+
+ /**
+ * Create a new {@link MeasurementModel}, taking {@link RealMatrix} objects as input parameters
+ * for the respective measurement matrix and noise.
+ *
+ * @param measMatrix the measurement matrix
+ * @param measNoise the measurement noise matrix
+ */
+ public DefaultMeasurementModel(final RealMatrix measMatrix, final RealMatrix measNoise) {
+ this.measurementMatrix = measMatrix;
+ this.measurementNoise = measNoise;
+ }
+
+ /** {@inheritDoc} */
+ public RealMatrix getMeasurementMatrix() {
+ return measurementMatrix;
+ }
+
+ /** {@inheritDoc} */
+ public RealMatrix getMeasurementNoise() {
+ return measurementNoise;
+ }
+}
diff --git a/src/main/java/org/apache/commons/math3/filter/DefaultProcessModel.java b/src/main/java/org/apache/commons/math3/filter/DefaultProcessModel.java
new file mode 100644
index 0000000..24ae97a
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/filter/DefaultProcessModel.java
@@ -0,0 +1,151 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math3.filter;
+
+import org.apache.commons.math3.exception.DimensionMismatchException;
+import org.apache.commons.math3.exception.NoDataException;
+import org.apache.commons.math3.exception.NullArgumentException;
+import org.apache.commons.math3.linear.Array2DRowRealMatrix;
+import org.apache.commons.math3.linear.ArrayRealVector;
+import org.apache.commons.math3.linear.RealMatrix;
+import org.apache.commons.math3.linear.RealVector;
+
+/**
+ * Default implementation of a {@link ProcessModel} for the use with a {@link KalmanFilter}.
+ *
+ * @since 3.0
+ */
+public class DefaultProcessModel implements ProcessModel {
+ /**
+ * The state transition matrix, used to advance the internal state estimation each time-step.
+ */
+ private RealMatrix stateTransitionMatrix;
+
+ /** The control matrix, used to integrate a control input into the state estimation. */
+ private RealMatrix controlMatrix;
+
+ /** The process noise covariance matrix. */
+ private RealMatrix processNoiseCovMatrix;
+
+ /** The initial state estimation of the observed process. */
+ private RealVector initialStateEstimateVector;
+
+ /** The initial error covariance matrix of the observed process. */
+ private RealMatrix initialErrorCovMatrix;
+
+ /**
+ * Create a new {@link ProcessModel}, taking double arrays as input parameters.
+ *
+ * @param stateTransition the state transition matrix
+ * @param control the control matrix
+ * @param processNoise the process noise matrix
+ * @param initialStateEstimate the initial state estimate vector
+ * @param initialErrorCovariance the initial error covariance matrix
+ * @throws NullArgumentException if any of the input arrays is {@code null}
+ * @throws NoDataException if any row / column dimension of the input matrices is zero
+ * @throws DimensionMismatchException if any of the input matrices is non-rectangular
+ */
+ public DefaultProcessModel(
+ final double[][] stateTransition,
+ final double[][] control,
+ final double[][] processNoise,
+ final double[] initialStateEstimate,
+ final double[][] initialErrorCovariance)
+ throws NullArgumentException, NoDataException, DimensionMismatchException {
+
+ this(
+ new Array2DRowRealMatrix(stateTransition),
+ new Array2DRowRealMatrix(control),
+ new Array2DRowRealMatrix(processNoise),
+ new ArrayRealVector(initialStateEstimate),
+ new Array2DRowRealMatrix(initialErrorCovariance));
+ }
+
+ /**
+ * Create a new {@link ProcessModel}, taking double arrays as input parameters.
+ *
+ * <p>The initial state estimate and error covariance are omitted and will be initialized by the
+ * {@link KalmanFilter} to default values.
+ *
+ * @param stateTransition the state transition matrix
+ * @param control the control matrix
+ * @param processNoise the process noise matrix
+ * @throws NullArgumentException if any of the input arrays is {@code null}
+ * @throws NoDataException if any row / column dimension of the input matrices is zero
+ * @throws DimensionMismatchException if any of the input matrices is non-rectangular
+ */
+ public DefaultProcessModel(
+ final double[][] stateTransition,
+ final double[][] control,
+ final double[][] processNoise)
+ throws NullArgumentException, NoDataException, DimensionMismatchException {
+
+ this(
+ new Array2DRowRealMatrix(stateTransition),
+ new Array2DRowRealMatrix(control),
+ new Array2DRowRealMatrix(processNoise),
+ null,
+ null);
+ }
+
+ /**
+ * Create a new {@link ProcessModel}, taking double arrays as input parameters.
+ *
+ * @param stateTransition the state transition matrix
+ * @param control the control matrix
+ * @param processNoise the process noise matrix
+ * @param initialStateEstimate the initial state estimate vector
+ * @param initialErrorCovariance the initial error covariance matrix
+ */
+ public DefaultProcessModel(
+ final RealMatrix stateTransition,
+ final RealMatrix control,
+ final RealMatrix processNoise,
+ final RealVector initialStateEstimate,
+ final RealMatrix initialErrorCovariance) {
+ this.stateTransitionMatrix = stateTransition;
+ this.controlMatrix = control;
+ this.processNoiseCovMatrix = processNoise;
+ this.initialStateEstimateVector = initialStateEstimate;
+ this.initialErrorCovMatrix = initialErrorCovariance;
+ }
+
+ /** {@inheritDoc} */
+ public RealMatrix getStateTransitionMatrix() {
+ return stateTransitionMatrix;
+ }
+
+ /** {@inheritDoc} */
+ public RealMatrix getControlMatrix() {
+ return controlMatrix;
+ }
+
+ /** {@inheritDoc} */
+ public RealMatrix getProcessNoise() {
+ return processNoiseCovMatrix;
+ }
+
+ /** {@inheritDoc} */
+ public RealVector getInitialStateEstimate() {
+ return initialStateEstimateVector;
+ }
+
+ /** {@inheritDoc} */
+ public RealMatrix getInitialErrorCovariance() {
+ return initialErrorCovMatrix;
+ }
+}
diff --git a/src/main/java/org/apache/commons/math3/filter/KalmanFilter.java b/src/main/java/org/apache/commons/math3/filter/KalmanFilter.java
new file mode 100644
index 0000000..7b2e63d
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/filter/KalmanFilter.java
@@ -0,0 +1,385 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math3.filter;
+
+import org.apache.commons.math3.exception.DimensionMismatchException;
+import org.apache.commons.math3.exception.NullArgumentException;
+import org.apache.commons.math3.linear.Array2DRowRealMatrix;
+import org.apache.commons.math3.linear.ArrayRealVector;
+import org.apache.commons.math3.linear.CholeskyDecomposition;
+import org.apache.commons.math3.linear.MatrixDimensionMismatchException;
+import org.apache.commons.math3.linear.MatrixUtils;
+import org.apache.commons.math3.linear.NonSquareMatrixException;
+import org.apache.commons.math3.linear.RealMatrix;
+import org.apache.commons.math3.linear.RealVector;
+import org.apache.commons.math3.linear.SingularMatrixException;
+import org.apache.commons.math3.util.MathUtils;
+
+/**
+ * Implementation of a Kalman filter to estimate the state <i>x<sub>k</sub></i> of a discrete-time
+ * controlled process that is governed by the linear stochastic difference equation:
+ *
+ * <pre>
+ * <i>x<sub>k</sub></i> = <b>A</b><i>x<sub>k-1</sub></i> + <b>B</b><i>u<sub>k-1</sub></i> + <i>w<sub>k-1</sub></i>
+ * </pre>
+ *
+ * with a measurement <i>x<sub>k</sub></i> that is
+ *
+ * <pre>
+ * <i>z<sub>k</sub></i> = <b>H</b><i>x<sub>k</sub></i> + <i>v<sub>k</sub></i>.
+ * </pre>
+ *
+ * <p>The random variables <i>w<sub>k</sub></i> and <i>v<sub>k</sub></i> represent the process and
+ * measurement noise and are assumed to be independent of each other and distributed with normal
+ * probability (white noise).
+ *
+ * <p>The Kalman filter cycle involves the following steps:
+ *
+ * <ol>
+ * <li>predict: project the current state estimate ahead in time
+ * <li>correct: adjust the projected estimate by an actual measurement
+ * </ol>
+ *
+ * <p>The Kalman filter is initialized with a {@link ProcessModel} and a {@link MeasurementModel},
+ * which contain the corresponding transformation and noise covariance matrices. The parameter names
+ * used in the respective models correspond to the following names commonly used in the mathematical
+ * literature:
+ *
+ * <ul>
+ * <li>A - state transition matrix
+ * <li>B - control input matrix
+ * <li>H - measurement matrix
+ * <li>Q - process noise covariance matrix
+ * <li>R - measurement noise covariance matrix
+ * <li>P - error covariance matrix
+ * </ul>
+ *
+ * @see <a href="http://www.cs.unc.edu/~welch/kalman/">Kalman filter resources</a>
+ * @see <a href="http://www.cs.unc.edu/~welch/media/pdf/kalman_intro.pdf">An introduction to the
+ * Kalman filter by Greg Welch and Gary Bishop</a>
+ * @see <a href="http://academic.csuohio.edu/simond/courses/eec644/kalman.pdf">Kalman filter example
+ * by Dan Simon</a>
+ * @see ProcessModel
+ * @see MeasurementModel
+ * @since 3.0
+ */
+public class KalmanFilter {
+ /** The process model used by this filter instance. */
+ private final ProcessModel processModel;
+
+ /** The measurement model used by this filter instance. */
+ private final MeasurementModel measurementModel;
+
+ /** The transition matrix, equivalent to A. */
+ private RealMatrix transitionMatrix;
+
+ /** The transposed transition matrix. */
+ private RealMatrix transitionMatrixT;
+
+ /** The control matrix, equivalent to B. */
+ private RealMatrix controlMatrix;
+
+ /** The measurement matrix, equivalent to H. */
+ private RealMatrix measurementMatrix;
+
+ /** The transposed measurement matrix. */
+ private RealMatrix measurementMatrixT;
+
+ /** The internal state estimation vector, equivalent to x hat. */
+ private RealVector stateEstimation;
+
+ /** The error covariance matrix, equivalent to P. */
+ private RealMatrix errorCovariance;
+
+ /**
+ * Creates a new Kalman filter with the given process and measurement models.
+ *
+ * @param process the model defining the underlying process dynamics
+ * @param measurement the model defining the given measurement characteristics
+ * @throws NullArgumentException if any of the given inputs is null (except for the control
+ * matrix)
+ * @throws NonSquareMatrixException if the transition matrix is non square
+ * @throws DimensionMismatchException if the column dimension of the transition matrix does not
+ * match the dimension of the initial state estimation vector
+ * @throws MatrixDimensionMismatchException if the matrix dimensions do not fit together
+ */
+ public KalmanFilter(final ProcessModel process, final MeasurementModel measurement)
+ throws NullArgumentException,
+ NonSquareMatrixException,
+ DimensionMismatchException,
+ MatrixDimensionMismatchException {
+
+ MathUtils.checkNotNull(process);
+ MathUtils.checkNotNull(measurement);
+
+ this.processModel = process;
+ this.measurementModel = measurement;
+
+ transitionMatrix = processModel.getStateTransitionMatrix();
+ MathUtils.checkNotNull(transitionMatrix);
+ transitionMatrixT = transitionMatrix.transpose();
+
+ // create an empty matrix if no control matrix was given
+ if (processModel.getControlMatrix() == null) {
+ controlMatrix = new Array2DRowRealMatrix();
+ } else {
+ controlMatrix = processModel.getControlMatrix();
+ }
+
+ measurementMatrix = measurementModel.getMeasurementMatrix();
+ MathUtils.checkNotNull(measurementMatrix);
+ measurementMatrixT = measurementMatrix.transpose();
+
+ // check that the process and measurement noise matrices are not null
+ // they will be directly accessed from the model as they may change
+ // over time
+ RealMatrix processNoise = processModel.getProcessNoise();
+ MathUtils.checkNotNull(processNoise);
+ RealMatrix measNoise = measurementModel.getMeasurementNoise();
+ MathUtils.checkNotNull(measNoise);
+
+ // set the initial state estimate to a zero vector if it is not
+ // available from the process model
+ if (processModel.getInitialStateEstimate() == null) {
+ stateEstimation = new ArrayRealVector(transitionMatrix.getColumnDimension());
+ } else {
+ stateEstimation = processModel.getInitialStateEstimate();
+ }
+
+ if (transitionMatrix.getColumnDimension() != stateEstimation.getDimension()) {
+ throw new DimensionMismatchException(
+ transitionMatrix.getColumnDimension(), stateEstimation.getDimension());
+ }
+
+ // initialize the error covariance to the process noise if it is not
+ // available from the process model
+ if (processModel.getInitialErrorCovariance() == null) {
+ errorCovariance = processNoise.copy();
+ } else {
+ errorCovariance = processModel.getInitialErrorCovariance();
+ }
+
+ // sanity checks, the control matrix B may be null
+
+ // A must be a square matrix
+ if (!transitionMatrix.isSquare()) {
+ throw new NonSquareMatrixException(
+ transitionMatrix.getRowDimension(), transitionMatrix.getColumnDimension());
+ }
+
+ // row dimension of B must be equal to A
+ // if no control matrix is available, the row and column dimension will be 0
+ if (controlMatrix != null
+ && controlMatrix.getRowDimension() > 0
+ && controlMatrix.getColumnDimension() > 0
+ && controlMatrix.getRowDimension() != transitionMatrix.getRowDimension()) {
+ throw new MatrixDimensionMismatchException(
+ controlMatrix.getRowDimension(),
+ controlMatrix.getColumnDimension(),
+ transitionMatrix.getRowDimension(),
+ controlMatrix.getColumnDimension());
+ }
+
+ // Q must be equal to A
+ MatrixUtils.checkAdditionCompatible(transitionMatrix, processNoise);
+
+ // column dimension of H must be equal to row dimension of A
+ if (measurementMatrix.getColumnDimension() != transitionMatrix.getRowDimension()) {
+ throw new MatrixDimensionMismatchException(
+ measurementMatrix.getRowDimension(),
+ measurementMatrix.getColumnDimension(),
+ measurementMatrix.getRowDimension(),
+ transitionMatrix.getRowDimension());
+ }
+
+ // row dimension of R must be equal to row dimension of H
+ if (measNoise.getRowDimension() != measurementMatrix.getRowDimension()) {
+ throw new MatrixDimensionMismatchException(
+ measNoise.getRowDimension(),
+ measNoise.getColumnDimension(),
+ measurementMatrix.getRowDimension(),
+ measNoise.getColumnDimension());
+ }
+ }
+
+ /**
+ * Returns the dimension of the state estimation vector.
+ *
+ * @return the state dimension
+ */
+ public int getStateDimension() {
+ return stateEstimation.getDimension();
+ }
+
+ /**
+ * Returns the dimension of the measurement vector.
+ *
+ * @return the measurement vector dimension
+ */
+ public int getMeasurementDimension() {
+ return measurementMatrix.getRowDimension();
+ }
+
+ /**
+ * Returns the current state estimation vector.
+ *
+ * @return the state estimation vector
+ */
+ public double[] getStateEstimation() {
+ return stateEstimation.toArray();
+ }
+
+ /**
+ * Returns a copy of the current state estimation vector.
+ *
+ * @return the state estimation vector
+ */
+ public RealVector getStateEstimationVector() {
+ return stateEstimation.copy();
+ }
+
+ /**
+ * Returns the current error covariance matrix.
+ *
+ * @return the error covariance matrix
+ */
+ public double[][] getErrorCovariance() {
+ return errorCovariance.getData();
+ }
+
+ /**
+ * Returns a copy of the current error covariance matrix.
+ *
+ * @return the error covariance matrix
+ */
+ public RealMatrix getErrorCovarianceMatrix() {
+ return errorCovariance.copy();
+ }
+
+ /** Predict the internal state estimation one time step ahead. */
+ public void predict() {
+ predict((RealVector) null);
+ }
+
+ /**
+ * Predict the internal state estimation one time step ahead.
+ *
+ * @param u the control vector
+ * @throws DimensionMismatchException if the dimension of the control vector does not fit
+ */
+ public void predict(final double[] u) throws DimensionMismatchException {
+ predict(new ArrayRealVector(u, false));
+ }
+
+ /**
+ * Predict the internal state estimation one time step ahead.
+ *
+ * @param u the control vector
+ * @throws DimensionMismatchException if the dimension of the control vector does not match
+ */
+ public void predict(final RealVector u) throws DimensionMismatchException {
+ // sanity checks
+ if (u != null && u.getDimension() != controlMatrix.getColumnDimension()) {
+ throw new DimensionMismatchException(
+ u.getDimension(), controlMatrix.getColumnDimension());
+ }
+
+ // project the state estimation ahead (a priori state)
+ // xHat(k)- = A * xHat(k-1) + B * u(k-1)
+ stateEstimation = transitionMatrix.operate(stateEstimation);
+
+ // add control input if it is available
+ if (u != null) {
+ stateEstimation = stateEstimation.add(controlMatrix.operate(u));
+ }
+
+ // project the error covariance ahead
+ // P(k)- = A * P(k-1) * A' + Q
+ errorCovariance =
+ transitionMatrix
+ .multiply(errorCovariance)
+ .multiply(transitionMatrixT)
+ .add(processModel.getProcessNoise());
+ }
+
+ /**
+ * Correct the current state estimate with an actual measurement.
+ *
+ * @param z the measurement vector
+ * @throws NullArgumentException if the measurement vector is {@code null}
+ * @throws DimensionMismatchException if the dimension of the measurement vector does not fit
+ * @throws SingularMatrixException if the covariance matrix could not be inverted
+ */
+ public void correct(final double[] z)
+ throws NullArgumentException, DimensionMismatchException, SingularMatrixException {
+ correct(new ArrayRealVector(z, false));
+ }
+
+ /**
+ * Correct the current state estimate with an actual measurement.
+ *
+ * @param z the measurement vector
+ * @throws NullArgumentException if the measurement vector is {@code null}
+ * @throws DimensionMismatchException if the dimension of the measurement vector does not fit
+ * @throws SingularMatrixException if the covariance matrix could not be inverted
+ */
+ public void correct(final RealVector z)
+ throws NullArgumentException, DimensionMismatchException, SingularMatrixException {
+
+ // sanity checks
+ MathUtils.checkNotNull(z);
+ if (z.getDimension() != measurementMatrix.getRowDimension()) {
+ throw new DimensionMismatchException(
+ z.getDimension(), measurementMatrix.getRowDimension());
+ }
+
+ // S = H * P(k) * H' + R
+ RealMatrix s =
+ measurementMatrix
+ .multiply(errorCovariance)
+ .multiply(measurementMatrixT)
+ .add(measurementModel.getMeasurementNoise());
+
+ // Inn = z(k) - H * xHat(k)-
+ RealVector innovation = z.subtract(measurementMatrix.operate(stateEstimation));
+
+ // calculate gain matrix
+ // K(k) = P(k)- * H' * (H * P(k)- * H' + R)^-1
+ // K(k) = P(k)- * H' * S^-1
+
+ // instead of calculating the inverse of S we can rearrange the formula,
+ // and then solve the linear equation A x X = B with A = S', X = K' and B = (H * P)'
+
+ // K(k) * S = P(k)- * H'
+ // S' * K(k)' = H * P(k)-'
+ RealMatrix kalmanGain =
+ new CholeskyDecomposition(s)
+ .getSolver()
+ .solve(measurementMatrix.multiply(errorCovariance.transpose()))
+ .transpose();
+
+ // update estimate with measurement z(k)
+ // xHat(k) = xHat(k)- + K * Inn
+ stateEstimation = stateEstimation.add(kalmanGain.operate(innovation));
+
+ // update covariance of prediction error
+ // P(k) = (I - K * H) * P(k)-
+ RealMatrix identity = MatrixUtils.createRealIdentityMatrix(kalmanGain.getRowDimension());
+ errorCovariance =
+ identity.subtract(kalmanGain.multiply(measurementMatrix)).multiply(errorCovariance);
+ }
+}
diff --git a/src/main/java/org/apache/commons/math3/filter/MeasurementModel.java b/src/main/java/org/apache/commons/math3/filter/MeasurementModel.java
new file mode 100644
index 0000000..2e0a379
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/filter/MeasurementModel.java
@@ -0,0 +1,44 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math3.filter;
+
+import org.apache.commons.math3.linear.RealMatrix;
+
+/**
+ * Defines the measurement model for the use with a {@link KalmanFilter}.
+ *
+ * @since 3.0
+ */
+public interface MeasurementModel {
+ /**
+ * Returns the measurement matrix.
+ *
+ * @return the measurement matrix
+ */
+ RealMatrix getMeasurementMatrix();
+
+ /**
+ * Returns the measurement noise matrix. This method is called by the {@link KalmanFilter} every
+ * correction step, so implementations of this interface may return a modified measurement noise
+ * depending on the current iteration step.
+ *
+ * @return the measurement noise matrix
+ * @see KalmanFilter#correct(double[])
+ * @see KalmanFilter#correct(org.apache.commons.math3.linear.RealVector)
+ */
+ RealMatrix getMeasurementNoise();
+}
diff --git a/src/main/java/org/apache/commons/math3/filter/ProcessModel.java b/src/main/java/org/apache/commons/math3/filter/ProcessModel.java
new file mode 100644
index 0000000..5e0b427
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/filter/ProcessModel.java
@@ -0,0 +1,73 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math3.filter;
+
+import org.apache.commons.math3.linear.RealMatrix;
+import org.apache.commons.math3.linear.RealVector;
+
+/**
+ * Defines the process dynamics model for the use with a {@link KalmanFilter}.
+ *
+ * @since 3.0
+ */
+public interface ProcessModel {
+ /**
+ * Returns the state transition matrix.
+ *
+ * @return the state transition matrix
+ */
+ RealMatrix getStateTransitionMatrix();
+
+ /**
+ * Returns the control matrix.
+ *
+ * @return the control matrix
+ */
+ RealMatrix getControlMatrix();
+
+ /**
+ * Returns the process noise matrix. This method is called by the {@link KalmanFilter} every
+ * prediction step, so implementations of this interface may return a modified process noise
+ * depending on the current iteration step.
+ *
+ * @return the process noise matrix
+ * @see KalmanFilter#predict()
+ * @see KalmanFilter#predict(double[])
+ * @see KalmanFilter#predict(RealVector)
+ */
+ RealMatrix getProcessNoise();
+
+ /**
+ * Returns the initial state estimation vector.
+ *
+ * <p><b>Note:</b> if the return value is zero, the Kalman filter will initialize the state
+ * estimation with a zero vector.
+ *
+ * @return the initial state estimation vector
+ */
+ RealVector getInitialStateEstimate();
+
+ /**
+ * Returns the initial error covariance matrix.
+ *
+ * <p><b>Note:</b> if the return value is zero, the Kalman filter will initialize the error
+ * covariance with the process noise matrix.
+ *
+ * @return the initial error covariance matrix
+ */
+ RealMatrix getInitialErrorCovariance();
+}
diff --git a/src/main/java/org/apache/commons/math3/filter/package-info.java b/src/main/java/org/apache/commons/math3/filter/package-info.java
new file mode 100644
index 0000000..159b133
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/filter/package-info.java
@@ -0,0 +1,18 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/** Implementations of common discrete-time linear filters. */
+package org.apache.commons.math3.filter;
diff --git a/src/main/java/org/apache/commons/math3/fitting/AbstractCurveFitter.java b/src/main/java/org/apache/commons/math3/fitting/AbstractCurveFitter.java
new file mode 100644
index 0000000..c3f7239
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/fitting/AbstractCurveFitter.java
@@ -0,0 +1,141 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math3.fitting;
+
+import org.apache.commons.math3.analysis.MultivariateMatrixFunction;
+import org.apache.commons.math3.analysis.MultivariateVectorFunction;
+import org.apache.commons.math3.analysis.ParametricUnivariateFunction;
+import org.apache.commons.math3.fitting.leastsquares.LeastSquaresOptimizer;
+import org.apache.commons.math3.fitting.leastsquares.LeastSquaresProblem;
+import org.apache.commons.math3.fitting.leastsquares.LevenbergMarquardtOptimizer;
+
+import java.util.Collection;
+
+/**
+ * Base class that contains common code for fitting parametric univariate real functions <code>
+ * y = f(p<sub>i</sub>;x)</code>, where {@code x} is the independent variable and the <code>
+ * p<sub>i</sub></code> are the <em>parameters</em>. <br>
+ * A fitter will find the optimal values of the parameters by <em>fitting</em> the curve so it
+ * remains very close to a set of {@code N} observed points <code>(x<sub>k</sub>, y<sub>k</sub>)
+ * </code>, {@code 0 <= k < N}. <br>
+ * An algorithm usually performs the fit by finding the parameter values that minimizes the
+ * objective function
+ *
+ * <pre><code>
+ * &sum;y<sub>k</sub> - f(x<sub>k</sub>)<sup>2</sup>,
+ * </code></pre>
+ *
+ * which is actually a least-squares problem. This class contains boilerplate code for calling the
+ * {@link #fit(Collection)} method for obtaining the parameters. The problem setup, such as the
+ * choice of optimization algorithm for fitting a specific function is delegated to subclasses.
+ *
+ * @since 3.3
+ */
+public abstract class AbstractCurveFitter {
+ /**
+ * Fits a curve. This method computes the coefficients of the curve that best fit the sample of
+ * observed points.
+ *
+ * @param points Observations.
+ * @return the fitted parameters.
+ */
+ public double[] fit(Collection<WeightedObservedPoint> points) {
+ // Perform the fit.
+ return getOptimizer().optimize(getProblem(points)).getPoint().toArray();
+ }
+
+ /**
+ * Creates an optimizer set up to fit the appropriate curve.
+ *
+ * <p>The default implementation uses a {@link LevenbergMarquardtOptimizer Levenberg-Marquardt}
+ * optimizer.
+ *
+ * @return the optimizer to use for fitting the curve to the given {@code points}.
+ */
+ protected LeastSquaresOptimizer getOptimizer() {
+ return new LevenbergMarquardtOptimizer();
+ }
+
+ /**
+ * Creates a least squares problem corresponding to the appropriate curve.
+ *
+ * @param points Sample points.
+ * @return the least squares problem to use for fitting the curve to the given {@code points}.
+ */
+ protected abstract LeastSquaresProblem getProblem(Collection<WeightedObservedPoint> points);
+
+ /** Vector function for computing function theoretical values. */
+ protected static class TheoreticalValuesFunction {
+ /** Function to fit. */
+ private final ParametricUnivariateFunction f;
+
+ /** Observations. */
+ private final double[] points;
+
+ /**
+ * @param f function to fit.
+ * @param observations Observations.
+ */
+ public TheoreticalValuesFunction(
+ final ParametricUnivariateFunction f,
+ final Collection<WeightedObservedPoint> observations) {
+ this.f = f;
+
+ final int len = observations.size();
+ this.points = new double[len];
+ int i = 0;
+ for (WeightedObservedPoint obs : observations) {
+ this.points[i++] = obs.getX();
+ }
+ }
+
+ /**
+ * @return the model function values.
+ */
+ public MultivariateVectorFunction getModelFunction() {
+ return new MultivariateVectorFunction() {
+ /** {@inheritDoc} */
+ public double[] value(double[] p) {
+ final int len = points.length;
+ final double[] values = new double[len];
+ for (int i = 0; i < len; i++) {
+ values[i] = f.value(points[i], p);
+ }
+
+ return values;
+ }
+ };
+ }
+
+ /**
+ * @return the model function Jacobian.
+ */
+ public MultivariateMatrixFunction getModelFunctionJacobian() {
+ return new MultivariateMatrixFunction() {
+ /** {@inheritDoc} */
+ public double[][] value(double[] p) {
+ final int len = points.length;
+ final double[][] jacobian = new double[len][];
+ for (int i = 0; i < len; i++) {
+ jacobian[i] = f.gradient(points[i], p);
+ }
+ return jacobian;
+ }
+ };
+ }
+ }
+}
diff --git a/src/main/java/org/apache/commons/math3/fitting/CurveFitter.java b/src/main/java/org/apache/commons/math3/fitting/CurveFitter.java
new file mode 100644
index 0000000..09dd7f2
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/fitting/CurveFitter.java
@@ -0,0 +1,235 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math3.fitting;
+
+import org.apache.commons.math3.analysis.MultivariateMatrixFunction;
+import org.apache.commons.math3.analysis.MultivariateVectorFunction;
+import org.apache.commons.math3.analysis.ParametricUnivariateFunction;
+import org.apache.commons.math3.optim.InitialGuess;
+import org.apache.commons.math3.optim.MaxEval;
+import org.apache.commons.math3.optim.PointVectorValuePair;
+import org.apache.commons.math3.optim.nonlinear.vector.ModelFunction;
+import org.apache.commons.math3.optim.nonlinear.vector.ModelFunctionJacobian;
+import org.apache.commons.math3.optim.nonlinear.vector.MultivariateVectorOptimizer;
+import org.apache.commons.math3.optim.nonlinear.vector.Target;
+import org.apache.commons.math3.optim.nonlinear.vector.Weight;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Fitter for parametric univariate real functions y = f(x). <br>
+ * When a univariate real function y = f(x) does depend on some unknown parameters p<sub>0</sub>,
+ * p<sub>1</sub> ... p<sub>n-1</sub>, this class can be used to find these parameters. It does this
+ * by <em>fitting</em> the curve so it remains very close to a set of observed points
+ * (x<sub>0</sub>, y<sub>0</sub>), (x<sub>1</sub>, y<sub>1</sub>) ... (x<sub>k-1</sub>,
+ * y<sub>k-1</sub>). This fitting is done by finding the parameters values that minimizes the
+ * objective function &sum;(y<sub>i</sub>-f(x<sub>i</sub>))<sup>2</sup>. This is really a least
+ * squares problem.
+ *
+ * @param <T> Function to use for the fit.
+ * @since 2.0
+ * @deprecated As of 3.3. Please use {@link AbstractCurveFitter} and {@link WeightedObservedPoints}
+ * instead.
+ */
+@Deprecated
+public class CurveFitter<T extends ParametricUnivariateFunction> {
+ /** Optimizer to use for the fitting. */
+ private final MultivariateVectorOptimizer optimizer;
+
+ /** Observed points. */
+ private final List<WeightedObservedPoint> observations;
+
+ /**
+ * Simple constructor.
+ *
+ * @param optimizer Optimizer to use for the fitting.
+ * @since 3.1
+ */
+ public CurveFitter(final MultivariateVectorOptimizer optimizer) {
+ this.optimizer = optimizer;
+ observations = new ArrayList<WeightedObservedPoint>();
+ }
+
+ /**
+ * Add an observed (x,y) point to the sample with unit weight.
+ *
+ * <p>Calling this method is equivalent to call {@code addObservedPoint(1.0, x, y)}.
+ *
+ * @param x abscissa of the point
+ * @param y observed value of the point at x, after fitting we should have f(x) as close as
+ * possible to this value
+ * @see #addObservedPoint(double, double, double)
+ * @see #addObservedPoint(WeightedObservedPoint)
+ * @see #getObservations()
+ */
+ public void addObservedPoint(double x, double y) {
+ addObservedPoint(1.0, x, y);
+ }
+
+ /**
+ * Add an observed weighted (x,y) point to the sample.
+ *
+ * @param weight weight of the observed point in the fit
+ * @param x abscissa of the point
+ * @param y observed value of the point at x, after fitting we should have f(x) as close as
+ * possible to this value
+ * @see #addObservedPoint(double, double)
+ * @see #addObservedPoint(WeightedObservedPoint)
+ * @see #getObservations()
+ */
+ public void addObservedPoint(double weight, double x, double y) {
+ observations.add(new WeightedObservedPoint(weight, x, y));
+ }
+
+ /**
+ * Add an observed weighted (x,y) point to the sample.
+ *
+ * @param observed observed point to add
+ * @see #addObservedPoint(double, double)
+ * @see #addObservedPoint(double, double, double)
+ * @see #getObservations()
+ */
+ public void addObservedPoint(WeightedObservedPoint observed) {
+ observations.add(observed);
+ }
+
+ /**
+ * Get the observed points.
+ *
+ * @return observed points
+ * @see #addObservedPoint(double, double)
+ * @see #addObservedPoint(double, double, double)
+ * @see #addObservedPoint(WeightedObservedPoint)
+ */
+ public WeightedObservedPoint[] getObservations() {
+ return observations.toArray(new WeightedObservedPoint[observations.size()]);
+ }
+
+ /** Remove all observations. */
+ public void clearObservations() {
+ observations.clear();
+ }
+
+ /**
+ * Fit a curve. This method compute the coefficients of the curve that best fit the sample of
+ * observed points previously given through calls to the {@link
+ * #addObservedPoint(WeightedObservedPoint) addObservedPoint} method.
+ *
+ * @param f parametric function to fit.
+ * @param initialGuess first guess of the function parameters.
+ * @return the fitted parameters.
+ * @throws org.apache.commons.math3.exception.DimensionMismatchException if the start point
+ * dimension is wrong.
+ */
+ public double[] fit(T f, final double[] initialGuess) {
+ return fit(Integer.MAX_VALUE, f, initialGuess);
+ }
+
+ /**
+ * Fit a curve. This method compute the coefficients of the curve that best fit the sample of
+ * observed points previously given through calls to the {@link
+ * #addObservedPoint(WeightedObservedPoint) addObservedPoint} method.
+ *
+ * @param f parametric function to fit.
+ * @param initialGuess first guess of the function parameters.
+ * @param maxEval Maximum number of function evaluations.
+ * @return the fitted parameters.
+ * @throws org.apache.commons.math3.exception.TooManyEvaluationsException if the number of
+ * allowed evaluations is exceeded.
+ * @throws org.apache.commons.math3.exception.DimensionMismatchException if the start point
+ * dimension is wrong.
+ * @since 3.0
+ */
+ public double[] fit(int maxEval, T f, final double[] initialGuess) {
+ // Prepare least squares problem.
+ double[] target = new double[observations.size()];
+ double[] weights = new double[observations.size()];
+ int i = 0;
+ for (WeightedObservedPoint point : observations) {
+ target[i] = point.getY();
+ weights[i] = point.getWeight();
+ ++i;
+ }
+
+ // Input to the optimizer: the model and its Jacobian.
+ final TheoreticalValuesFunction model = new TheoreticalValuesFunction(f);
+
+ // Perform the fit.
+ final PointVectorValuePair optimum =
+ optimizer.optimize(
+ new MaxEval(maxEval),
+ model.getModelFunction(),
+ model.getModelFunctionJacobian(),
+ new Target(target),
+ new Weight(weights),
+ new InitialGuess(initialGuess));
+ // Extract the coefficients.
+ return optimum.getPointRef();
+ }
+
+ /** Vectorial function computing function theoretical values. */
+ private class TheoreticalValuesFunction {
+ /** Function to fit. */
+ private final ParametricUnivariateFunction f;
+
+ /**
+ * @param f function to fit.
+ */
+ TheoreticalValuesFunction(final ParametricUnivariateFunction f) {
+ this.f = f;
+ }
+
+ /**
+ * @return the model function values.
+ */
+ public ModelFunction getModelFunction() {
+ return new ModelFunction(
+ new MultivariateVectorFunction() {
+ /** {@inheritDoc} */
+ public double[] value(double[] point) {
+ // compute the residuals
+ final double[] values = new double[observations.size()];
+ int i = 0;
+ for (WeightedObservedPoint observed : observations) {
+ values[i++] = f.value(observed.getX(), point);
+ }
+
+ return values;
+ }
+ });
+ }
+
+ /**
+ * @return the model function Jacobian.
+ */
+ public ModelFunctionJacobian getModelFunctionJacobian() {
+ return new ModelFunctionJacobian(
+ new MultivariateMatrixFunction() {
+ /** {@inheritDoc} */
+ public double[][] value(double[] point) {
+ final double[][] jacobian = new double[observations.size()][];
+ int i = 0;
+ for (WeightedObservedPoint observed : observations) {
+ jacobian[i++] = f.gradient(observed.getX(), point);
+ }
+ return jacobian;
+ }
+ });
+ }
+ }
+}
diff --git a/src/main/java/org/apache/commons/math3/fitting/GaussianCurveFitter.java b/src/main/java/org/apache/commons/math3/fitting/GaussianCurveFitter.java
new file mode 100644
index 0000000..685df28
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/fitting/GaussianCurveFitter.java
@@ -0,0 +1,425 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math3.fitting;
+
+import org.apache.commons.math3.analysis.function.Gaussian;
+import org.apache.commons.math3.exception.NotStrictlyPositiveException;
+import org.apache.commons.math3.exception.NullArgumentException;
+import org.apache.commons.math3.exception.NumberIsTooSmallException;
+import org.apache.commons.math3.exception.OutOfRangeException;
+import org.apache.commons.math3.exception.ZeroException;
+import org.apache.commons.math3.exception.util.LocalizedFormats;
+import org.apache.commons.math3.fitting.leastsquares.LeastSquaresBuilder;
+import org.apache.commons.math3.fitting.leastsquares.LeastSquaresProblem;
+import org.apache.commons.math3.linear.DiagonalMatrix;
+import org.apache.commons.math3.util.FastMath;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.List;
+
+/**
+ * Fits points to a {@link org.apache.commons.math3.analysis.function.Gaussian.Parametric Gaussian}
+ * function. <br>
+ * The {@link #withStartPoint(double[]) initial guess values} must be passed in the following order:
+ *
+ * <ul>
+ * <li>Normalization
+ * <li>Mean
+ * <li>Sigma
+ * </ul>
+ *
+ * The optimal values will be returned in the same order.
+ *
+ * <p>Usage example:
+ *
+ * <pre>
+ * WeightedObservedPoints obs = new WeightedObservedPoints();
+ * obs.add(4.0254623, 531026.0);
+ * obs.add(4.03128248, 984167.0);
+ * obs.add(4.03839603, 1887233.0);
+ * obs.add(4.04421621, 2687152.0);
+ * obs.add(4.05132976, 3461228.0);
+ * obs.add(4.05326982, 3580526.0);
+ * obs.add(4.05779662, 3439750.0);
+ * obs.add(4.0636168, 2877648.0);
+ * obs.add(4.06943698, 2175960.0);
+ * obs.add(4.07525716, 1447024.0);
+ * obs.add(4.08237071, 717104.0);
+ * obs.add(4.08366408, 620014.0);
+ * double[] parameters = GaussianCurveFitter.create().fit(obs.toList());
+ * </pre>
+ *
+ * @since 3.3
+ */
+public class GaussianCurveFitter extends AbstractCurveFitter {
+ /** Parametric function to be fitted. */
+ private static final Gaussian.Parametric FUNCTION =
+ new Gaussian.Parametric() {
+ /** {@inheritDoc} */
+ @Override
+ public double value(double x, double... p) {
+ double v = Double.POSITIVE_INFINITY;
+ try {
+ v = super.value(x, p);
+ } catch (NotStrictlyPositiveException e) { // NOPMD
+ // Do nothing.
+ }
+ return v;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public double[] gradient(double x, double... p) {
+ double[] v = {
+ Double.POSITIVE_INFINITY, Double.POSITIVE_INFINITY, Double.POSITIVE_INFINITY
+ };
+ try {
+ v = super.gradient(x, p);
+ } catch (NotStrictlyPositiveException e) { // NOPMD
+ // Do nothing.
+ }
+ return v;
+ }
+ };
+
+ /** Initial guess. */
+ private final double[] initialGuess;
+
+ /** Maximum number of iterations of the optimization algorithm. */
+ private final int maxIter;
+
+ /**
+ * Contructor used by the factory methods.
+ *
+ * @param initialGuess Initial guess. If set to {@code null}, the initial guess will be
+ * estimated using the {@link ParameterGuesser}.
+ * @param maxIter Maximum number of iterations of the optimization algorithm.
+ */
+ private GaussianCurveFitter(double[] initialGuess, int maxIter) {
+ this.initialGuess = initialGuess;
+ this.maxIter = maxIter;
+ }
+
+ /**
+ * Creates a default curve fitter. The initial guess for the parameters will be {@link
+ * ParameterGuesser} computed automatically, and the maximum number of iterations of the
+ * optimization algorithm is set to {@link Integer#MAX_VALUE}.
+ *
+ * @return a curve fitter.
+ * @see #withStartPoint(double[])
+ * @see #withMaxIterations(int)
+ */
+ public static GaussianCurveFitter create() {
+ return new GaussianCurveFitter(null, Integer.MAX_VALUE);
+ }
+
+ /**
+ * Configure the start point (initial guess).
+ *
+ * @param newStart new start point (initial guess)
+ * @return a new instance.
+ */
+ public GaussianCurveFitter withStartPoint(double[] newStart) {
+ return new GaussianCurveFitter(newStart.clone(), maxIter);
+ }
+
+ /**
+ * Configure the maximum number of iterations.
+ *
+ * @param newMaxIter maximum number of iterations
+ * @return a new instance.
+ */
+ public GaussianCurveFitter withMaxIterations(int newMaxIter) {
+ return new GaussianCurveFitter(initialGuess, newMaxIter);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ protected LeastSquaresProblem getProblem(Collection<WeightedObservedPoint> observations) {
+
+ // Prepare least-squares problem.
+ final int len = observations.size();
+ final double[] target = new double[len];
+ final double[] weights = new double[len];
+
+ int i = 0;
+ for (WeightedObservedPoint obs : observations) {
+ target[i] = obs.getY();
+ weights[i] = obs.getWeight();
+ ++i;
+ }
+
+ final AbstractCurveFitter.TheoreticalValuesFunction model =
+ new AbstractCurveFitter.TheoreticalValuesFunction(FUNCTION, observations);
+
+ final double[] startPoint =
+ initialGuess != null
+ ? initialGuess
+ :
+ // Compute estimation.
+ new ParameterGuesser(observations).guess();
+
+ // Return a new least squares problem set up to fit a Gaussian curve to the
+ // observed points.
+ return new LeastSquaresBuilder()
+ .maxEvaluations(Integer.MAX_VALUE)
+ .maxIterations(maxIter)
+ .start(startPoint)
+ .target(target)
+ .weight(new DiagonalMatrix(weights))
+ .model(model.getModelFunction(), model.getModelFunctionJacobian())
+ .build();
+ }
+
+ /**
+ * Guesses the parameters {@code norm}, {@code mean}, and {@code sigma} of a {@link
+ * org.apache.commons.math3.analysis.function.Gaussian.Parametric} based on the specified
+ * observed points.
+ */
+ public static class ParameterGuesser {
+ /** Normalization factor. */
+ private final double norm;
+
+ /** Mean. */
+ private final double mean;
+
+ /** Standard deviation. */
+ private final double sigma;
+
+ /**
+ * Constructs instance with the specified observed points.
+ *
+ * @param observations Observed points from which to guess the parameters of the Gaussian.
+ * @throws NullArgumentException if {@code observations} is {@code null}.
+ * @throws NumberIsTooSmallException if there are less than 3 observations.
+ */
+ public ParameterGuesser(Collection<WeightedObservedPoint> observations) {
+ if (observations == null) {
+ throw new NullArgumentException(LocalizedFormats.INPUT_ARRAY);
+ }
+ if (observations.size() < 3) {
+ throw new NumberIsTooSmallException(observations.size(), 3, true);
+ }
+
+ final List<WeightedObservedPoint> sorted = sortObservations(observations);
+ final double[] params = basicGuess(sorted.toArray(new WeightedObservedPoint[0]));
+
+ norm = params[0];
+ mean = params[1];
+ sigma = params[2];
+ }
+
+ /**
+ * Gets an estimation of the parameters.
+ *
+ * @return the guessed parameters, in the following order:
+ * <ul>
+ * <li>Normalization factor
+ * <li>Mean
+ * <li>Standard deviation
+ * </ul>
+ */
+ public double[] guess() {
+ return new double[] {norm, mean, sigma};
+ }
+
+ /**
+ * Sort the observations.
+ *
+ * @param unsorted Input observations.
+ * @return the input observations, sorted.
+ */
+ private List<WeightedObservedPoint> sortObservations(
+ Collection<WeightedObservedPoint> unsorted) {
+ final List<WeightedObservedPoint> observations =
+ new ArrayList<WeightedObservedPoint>(unsorted);
+
+ final Comparator<WeightedObservedPoint> cmp =
+ new Comparator<WeightedObservedPoint>() {
+ /** {@inheritDoc} */
+ public int compare(WeightedObservedPoint p1, WeightedObservedPoint p2) {
+ if (p1 == null && p2 == null) {
+ return 0;
+ }
+ if (p1 == null) {
+ return -1;
+ }
+ if (p2 == null) {
+ return 1;
+ }
+ final int cmpX = Double.compare(p1.getX(), p2.getX());
+ if (cmpX < 0) {
+ return -1;
+ }
+ if (cmpX > 0) {
+ return 1;
+ }
+ final int cmpY = Double.compare(p1.getY(), p2.getY());
+ if (cmpY < 0) {
+ return -1;
+ }
+ if (cmpY > 0) {
+ return 1;
+ }
+ final int cmpW = Double.compare(p1.getWeight(), p2.getWeight());
+ if (cmpW < 0) {
+ return -1;
+ }
+ if (cmpW > 0) {
+ return 1;
+ }
+ return 0;
+ }
+ };
+
+ Collections.sort(observations, cmp);
+ return observations;
+ }
+
+ /**
+ * Guesses the parameters based on the specified observed points.
+ *
+ * @param points Observed points, sorted.
+ * @return the guessed parameters (normalization factor, mean and sigma).
+ */
+ private double[] basicGuess(WeightedObservedPoint[] points) {
+ final int maxYIdx = findMaxY(points);
+ final double n = points[maxYIdx].getY();
+ final double m = points[maxYIdx].getX();
+
+ double fwhmApprox;
+ try {
+ final double halfY = n + ((m - n) / 2);
+ final double fwhmX1 = interpolateXAtY(points, maxYIdx, -1, halfY);
+ final double fwhmX2 = interpolateXAtY(points, maxYIdx, 1, halfY);
+ fwhmApprox = fwhmX2 - fwhmX1;
+ } catch (OutOfRangeException e) {
+ // TODO: Exceptions should not be used for flow control.
+ fwhmApprox = points[points.length - 1].getX() - points[0].getX();
+ }
+ final double s = fwhmApprox / (2 * FastMath.sqrt(2 * FastMath.log(2)));
+
+ return new double[] {n, m, s};
+ }
+
+ /**
+ * Finds index of point in specified points with the largest Y.
+ *
+ * @param points Points to search.
+ * @return the index in specified points array.
+ */
+ private int findMaxY(WeightedObservedPoint[] points) {
+ int maxYIdx = 0;
+ for (int i = 1; i < points.length; i++) {
+ if (points[i].getY() > points[maxYIdx].getY()) {
+ maxYIdx = i;
+ }
+ }
+ return maxYIdx;
+ }
+
+ /**
+ * Interpolates using the specified points to determine X at the specified Y.
+ *
+ * @param points Points to use for interpolation.
+ * @param startIdx Index within points from which to start the search for interpolation
+ * bounds points.
+ * @param idxStep Index step for searching interpolation bounds points.
+ * @param y Y value for which X should be determined.
+ * @return the value of X for the specified Y.
+ * @throws ZeroException if {@code idxStep} is 0.
+ * @throws OutOfRangeException if specified {@code y} is not within the range of the
+ * specified {@code points}.
+ */
+ private double interpolateXAtY(
+ WeightedObservedPoint[] points, int startIdx, int idxStep, double y)
+ throws OutOfRangeException {
+ if (idxStep == 0) {
+ throw new ZeroException();
+ }
+ final WeightedObservedPoint[] twoPoints =
+ getInterpolationPointsForY(points, startIdx, idxStep, y);
+ final WeightedObservedPoint p1 = twoPoints[0];
+ final WeightedObservedPoint p2 = twoPoints[1];
+ if (p1.getY() == y) {
+ return p1.getX();
+ }
+ if (p2.getY() == y) {
+ return p2.getX();
+ }
+ return p1.getX()
+ + (((y - p1.getY()) * (p2.getX() - p1.getX())) / (p2.getY() - p1.getY()));
+ }
+
+ /**
+ * Gets the two bounding interpolation points from the specified points suitable for
+ * determining X at the specified Y.
+ *
+ * @param points Points to use for interpolation.
+ * @param startIdx Index within points from which to start search for interpolation bounds
+ * points.
+ * @param idxStep Index step for search for interpolation bounds points.
+ * @param y Y value for which X should be determined.
+ * @return the array containing two points suitable for determining X at the specified Y.
+ * @throws ZeroException if {@code idxStep} is 0.
+ * @throws OutOfRangeException if specified {@code y} is not within the range of the
+ * specified {@code points}.
+ */
+ private WeightedObservedPoint[] getInterpolationPointsForY(
+ WeightedObservedPoint[] points, int startIdx, int idxStep, double y)
+ throws OutOfRangeException {
+ if (idxStep == 0) {
+ throw new ZeroException();
+ }
+ for (int i = startIdx;
+ idxStep < 0 ? i + idxStep >= 0 : i + idxStep < points.length;
+ i += idxStep) {
+ final WeightedObservedPoint p1 = points[i];
+ final WeightedObservedPoint p2 = points[i + idxStep];
+ if (isBetween(y, p1.getY(), p2.getY())) {
+ if (idxStep < 0) {
+ return new WeightedObservedPoint[] {p2, p1};
+ } else {
+ return new WeightedObservedPoint[] {p1, p2};
+ }
+ }
+ }
+
+ // Boundaries are replaced by dummy values because the raised
+ // exception is caught and the message never displayed.
+ // TODO: Exceptions should not be used for flow control.
+ throw new OutOfRangeException(y, Double.NEGATIVE_INFINITY, Double.POSITIVE_INFINITY);
+ }
+
+ /**
+ * Determines whether a value is between two other values.
+ *
+ * @param value Value to test whether it is between {@code boundary1} and {@code boundary2}.
+ * @param boundary1 One end of the range.
+ * @param boundary2 Other end of the range.
+ * @return {@code true} if {@code value} is between {@code boundary1} and {@code boundary2}
+ * (inclusive), {@code false} otherwise.
+ */
+ private boolean isBetween(double value, double boundary1, double boundary2) {
+ return (value >= boundary1 && value <= boundary2)
+ || (value >= boundary2 && value <= boundary1);
+ }
+ }
+}
diff --git a/src/main/java/org/apache/commons/math3/fitting/GaussianFitter.java b/src/main/java/org/apache/commons/math3/fitting/GaussianFitter.java
new file mode 100644
index 0000000..fe25c05
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/fitting/GaussianFitter.java
@@ -0,0 +1,362 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math3.fitting;
+
+import org.apache.commons.math3.analysis.function.Gaussian;
+import org.apache.commons.math3.exception.NotStrictlyPositiveException;
+import org.apache.commons.math3.exception.NullArgumentException;
+import org.apache.commons.math3.exception.NumberIsTooSmallException;
+import org.apache.commons.math3.exception.OutOfRangeException;
+import org.apache.commons.math3.exception.ZeroException;
+import org.apache.commons.math3.exception.util.LocalizedFormats;
+import org.apache.commons.math3.optim.nonlinear.vector.MultivariateVectorOptimizer;
+import org.apache.commons.math3.util.FastMath;
+
+import java.util.Arrays;
+import java.util.Comparator;
+
+/**
+ * Fits points to a {@link org.apache.commons.math3.analysis.function.Gaussian.Parametric Gaussian}
+ * function.
+ *
+ * <p>Usage example:
+ *
+ * <pre>
+ * GaussianFitter fitter = new GaussianFitter(
+ * new LevenbergMarquardtOptimizer());
+ * fitter.addObservedPoint(4.0254623, 531026.0);
+ * fitter.addObservedPoint(4.03128248, 984167.0);
+ * fitter.addObservedPoint(4.03839603, 1887233.0);
+ * fitter.addObservedPoint(4.04421621, 2687152.0);
+ * fitter.addObservedPoint(4.05132976, 3461228.0);
+ * fitter.addObservedPoint(4.05326982, 3580526.0);
+ * fitter.addObservedPoint(4.05779662, 3439750.0);
+ * fitter.addObservedPoint(4.0636168, 2877648.0);
+ * fitter.addObservedPoint(4.06943698, 2175960.0);
+ * fitter.addObservedPoint(4.07525716, 1447024.0);
+ * fitter.addObservedPoint(4.08237071, 717104.0);
+ * fitter.addObservedPoint(4.08366408, 620014.0);
+ * double[] parameters = fitter.fit();
+ * </pre>
+ *
+ * @since 2.2
+ * @deprecated As of 3.3. Please use {@link GaussianCurveFitter} and {@link WeightedObservedPoints}
+ * instead.
+ */
+@Deprecated
+public class GaussianFitter extends CurveFitter<Gaussian.Parametric> {
+ /**
+ * Constructs an instance using the specified optimizer.
+ *
+ * @param optimizer Optimizer to use for the fitting.
+ */
+ public GaussianFitter(MultivariateVectorOptimizer optimizer) {
+ super(optimizer);
+ }
+
+ /**
+ * Fits a Gaussian function to the observed points.
+ *
+ * @param initialGuess First guess values in the following order:
+ * <ul>
+ * <li>Norm
+ * <li>Mean
+ * <li>Sigma
+ * </ul>
+ *
+ * @return the parameters of the Gaussian function that best fits the observed points (in the
+ * same order as above).
+ * @since 3.0
+ */
+ public double[] fit(double[] initialGuess) {
+ final Gaussian.Parametric f =
+ new Gaussian.Parametric() {
+ /** {@inheritDoc} */
+ @Override
+ public double value(double x, double... p) {
+ double v = Double.POSITIVE_INFINITY;
+ try {
+ v = super.value(x, p);
+ } catch (NotStrictlyPositiveException e) { // NOPMD
+ // Do nothing.
+ }
+ return v;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public double[] gradient(double x, double... p) {
+ double[] v = {
+ Double.POSITIVE_INFINITY,
+ Double.POSITIVE_INFINITY,
+ Double.POSITIVE_INFINITY
+ };
+ try {
+ v = super.gradient(x, p);
+ } catch (NotStrictlyPositiveException e) { // NOPMD
+ // Do nothing.
+ }
+ return v;
+ }
+ };
+
+ return fit(f, initialGuess);
+ }
+
+ /**
+ * Fits a Gaussian function to the observed points.
+ *
+ * @return the parameters of the Gaussian function that best fits the observed points (in the
+ * same order as above).
+ */
+ public double[] fit() {
+ final double[] guess = (new ParameterGuesser(getObservations())).guess();
+ return fit(guess);
+ }
+
+ /**
+ * Guesses the parameters {@code norm}, {@code mean}, and {@code sigma} of a {@link
+ * org.apache.commons.math3.analysis.function.Gaussian.Parametric} based on the specified
+ * observed points.
+ */
+ public static class ParameterGuesser {
+ /** Normalization factor. */
+ private final double norm;
+
+ /** Mean. */
+ private final double mean;
+
+ /** Standard deviation. */
+ private final double sigma;
+
+ /**
+ * Constructs instance with the specified observed points.
+ *
+ * @param observations Observed points from which to guess the parameters of the Gaussian.
+ * @throws NullArgumentException if {@code observations} is {@code null}.
+ * @throws NumberIsTooSmallException if there are less than 3 observations.
+ */
+ public ParameterGuesser(WeightedObservedPoint[] observations) {
+ if (observations == null) {
+ throw new NullArgumentException(LocalizedFormats.INPUT_ARRAY);
+ }
+ if (observations.length < 3) {
+ throw new NumberIsTooSmallException(observations.length, 3, true);
+ }
+
+ final WeightedObservedPoint[] sorted = sortObservations(observations);
+ final double[] params = basicGuess(sorted);
+
+ norm = params[0];
+ mean = params[1];
+ sigma = params[2];
+ }
+
+ /**
+ * Gets an estimation of the parameters.
+ *
+ * @return the guessed parameters, in the following order:
+ * <ul>
+ * <li>Normalization factor
+ * <li>Mean
+ * <li>Standard deviation
+ * </ul>
+ */
+ public double[] guess() {
+ return new double[] {norm, mean, sigma};
+ }
+
+ /**
+ * Sort the observations.
+ *
+ * @param unsorted Input observations.
+ * @return the input observations, sorted.
+ */
+ private WeightedObservedPoint[] sortObservations(WeightedObservedPoint[] unsorted) {
+ final WeightedObservedPoint[] observations = unsorted.clone();
+ final Comparator<WeightedObservedPoint> cmp =
+ new Comparator<WeightedObservedPoint>() {
+ /** {@inheritDoc} */
+ public int compare(WeightedObservedPoint p1, WeightedObservedPoint p2) {
+ if (p1 == null && p2 == null) {
+ return 0;
+ }
+ if (p1 == null) {
+ return -1;
+ }
+ if (p2 == null) {
+ return 1;
+ }
+ final int cmpX = Double.compare(p1.getX(), p2.getX());
+ if (cmpX < 0) {
+ return -1;
+ }
+ if (cmpX > 0) {
+ return 1;
+ }
+ final int cmpY = Double.compare(p1.getY(), p2.getY());
+ if (cmpY < 0) {
+ return -1;
+ }
+ if (cmpY > 0) {
+ return 1;
+ }
+ final int cmpW = Double.compare(p1.getWeight(), p2.getWeight());
+ if (cmpW < 0) {
+ return -1;
+ }
+ if (cmpW > 0) {
+ return 1;
+ }
+ return 0;
+ }
+ };
+
+ Arrays.sort(observations, cmp);
+ return observations;
+ }
+
+ /**
+ * Guesses the parameters based on the specified observed points.
+ *
+ * @param points Observed points, sorted.
+ * @return the guessed parameters (normalization factor, mean and sigma).
+ */
+ private double[] basicGuess(WeightedObservedPoint[] points) {
+ final int maxYIdx = findMaxY(points);
+ final double n = points[maxYIdx].getY();
+ final double m = points[maxYIdx].getX();
+
+ double fwhmApprox;
+ try {
+ final double halfY = n + ((m - n) / 2);
+ final double fwhmX1 = interpolateXAtY(points, maxYIdx, -1, halfY);
+ final double fwhmX2 = interpolateXAtY(points, maxYIdx, 1, halfY);
+ fwhmApprox = fwhmX2 - fwhmX1;
+ } catch (OutOfRangeException e) {
+ // TODO: Exceptions should not be used for flow control.
+ fwhmApprox = points[points.length - 1].getX() - points[0].getX();
+ }
+ final double s = fwhmApprox / (2 * FastMath.sqrt(2 * FastMath.log(2)));
+
+ return new double[] {n, m, s};
+ }
+
+ /**
+ * Finds index of point in specified points with the largest Y.
+ *
+ * @param points Points to search.
+ * @return the index in specified points array.
+ */
+ private int findMaxY(WeightedObservedPoint[] points) {
+ int maxYIdx = 0;
+ for (int i = 1; i < points.length; i++) {
+ if (points[i].getY() > points[maxYIdx].getY()) {
+ maxYIdx = i;
+ }
+ }
+ return maxYIdx;
+ }
+
+ /**
+ * Interpolates using the specified points to determine X at the specified Y.
+ *
+ * @param points Points to use for interpolation.
+ * @param startIdx Index within points from which to start the search for interpolation
+ * bounds points.
+ * @param idxStep Index step for searching interpolation bounds points.
+ * @param y Y value for which X should be determined.
+ * @return the value of X for the specified Y.
+ * @throws ZeroException if {@code idxStep} is 0.
+ * @throws OutOfRangeException if specified {@code y} is not within the range of the
+ * specified {@code points}.
+ */
+ private double interpolateXAtY(
+ WeightedObservedPoint[] points, int startIdx, int idxStep, double y)
+ throws OutOfRangeException {
+ if (idxStep == 0) {
+ throw new ZeroException();
+ }
+ final WeightedObservedPoint[] twoPoints =
+ getInterpolationPointsForY(points, startIdx, idxStep, y);
+ final WeightedObservedPoint p1 = twoPoints[0];
+ final WeightedObservedPoint p2 = twoPoints[1];
+ if (p1.getY() == y) {
+ return p1.getX();
+ }
+ if (p2.getY() == y) {
+ return p2.getX();
+ }
+ return p1.getX()
+ + (((y - p1.getY()) * (p2.getX() - p1.getX())) / (p2.getY() - p1.getY()));
+ }
+
+ /**
+ * Gets the two bounding interpolation points from the specified points suitable for
+ * determining X at the specified Y.
+ *
+ * @param points Points to use for interpolation.
+ * @param startIdx Index within points from which to start search for interpolation bounds
+ * points.
+ * @param idxStep Index step for search for interpolation bounds points.
+ * @param y Y value for which X should be determined.
+ * @return the array containing two points suitable for determining X at the specified Y.
+ * @throws ZeroException if {@code idxStep} is 0.
+ * @throws OutOfRangeException if specified {@code y} is not within the range of the
+ * specified {@code points}.
+ */
+ private WeightedObservedPoint[] getInterpolationPointsForY(
+ WeightedObservedPoint[] points, int startIdx, int idxStep, double y)
+ throws OutOfRangeException {
+ if (idxStep == 0) {
+ throw new ZeroException();
+ }
+ for (int i = startIdx;
+ idxStep < 0 ? i + idxStep >= 0 : i + idxStep < points.length;
+ i += idxStep) {
+ final WeightedObservedPoint p1 = points[i];
+ final WeightedObservedPoint p2 = points[i + idxStep];
+ if (isBetween(y, p1.getY(), p2.getY())) {
+ if (idxStep < 0) {
+ return new WeightedObservedPoint[] {p2, p1};
+ } else {
+ return new WeightedObservedPoint[] {p1, p2};
+ }
+ }
+ }
+
+ // Boundaries are replaced by dummy values because the raised
+ // exception is caught and the message never displayed.
+ // TODO: Exceptions should not be used for flow control.
+ throw new OutOfRangeException(y, Double.NEGATIVE_INFINITY, Double.POSITIVE_INFINITY);
+ }
+
+ /**
+ * Determines whether a value is between two other values.
+ *
+ * @param value Value to test whether it is between {@code boundary1} and {@code boundary2}.
+ * @param boundary1 One end of the range.
+ * @param boundary2 Other end of the range.
+ * @return {@code true} if {@code value} is between {@code boundary1} and {@code boundary2}
+ * (inclusive), {@code false} otherwise.
+ */
+ private boolean isBetween(double value, double boundary1, double boundary2) {
+ return (value >= boundary1 && value <= boundary2)
+ || (value >= boundary2 && value <= boundary1);
+ }
+ }
+}
diff --git a/src/main/java/org/apache/commons/math3/fitting/HarmonicCurveFitter.java b/src/main/java/org/apache/commons/math3/fitting/HarmonicCurveFitter.java
new file mode 100644
index 0000000..29a49c7
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/fitting/HarmonicCurveFitter.java
@@ -0,0 +1,410 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math3.fitting;
+
+import org.apache.commons.math3.analysis.function.HarmonicOscillator;
+import org.apache.commons.math3.exception.MathIllegalStateException;
+import org.apache.commons.math3.exception.NumberIsTooSmallException;
+import org.apache.commons.math3.exception.ZeroException;
+import org.apache.commons.math3.exception.util.LocalizedFormats;
+import org.apache.commons.math3.fitting.leastsquares.LeastSquaresBuilder;
+import org.apache.commons.math3.fitting.leastsquares.LeastSquaresProblem;
+import org.apache.commons.math3.linear.DiagonalMatrix;
+import org.apache.commons.math3.util.FastMath;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+
+/**
+ * Fits points to a {@link org.apache.commons.math3.analysis.function.HarmonicOscillator.Parametric
+ * harmonic oscillator} function. <br>
+ * The {@link #withStartPoint(double[]) initial guess values} must be passed in the following order:
+ *
+ * <ul>
+ * <li>Amplitude
+ * <li>Angular frequency
+ * <li>phase
+ * </ul>
+ *
+ * The optimal values will be returned in the same order.
+ *
+ * @since 3.3
+ */
+public class HarmonicCurveFitter extends AbstractCurveFitter {
+ /** Parametric function to be fitted. */
+ private static final HarmonicOscillator.Parametric FUNCTION =
+ new HarmonicOscillator.Parametric();
+
+ /** Initial guess. */
+ private final double[] initialGuess;
+
+ /** Maximum number of iterations of the optimization algorithm. */
+ private final int maxIter;
+
+ /**
+ * Contructor used by the factory methods.
+ *
+ * @param initialGuess Initial guess. If set to {@code null}, the initial guess will be
+ * estimated using the {@link ParameterGuesser}.
+ * @param maxIter Maximum number of iterations of the optimization algorithm.
+ */
+ private HarmonicCurveFitter(double[] initialGuess, int maxIter) {
+ this.initialGuess = initialGuess;
+ this.maxIter = maxIter;
+ }
+
+ /**
+ * Creates a default curve fitter. The initial guess for the parameters will be {@link
+ * ParameterGuesser} computed automatically, and the maximum number of iterations of the
+ * optimization algorithm is set to {@link Integer#MAX_VALUE}.
+ *
+ * @return a curve fitter.
+ * @see #withStartPoint(double[])
+ * @see #withMaxIterations(int)
+ */
+ public static HarmonicCurveFitter create() {
+ return new HarmonicCurveFitter(null, Integer.MAX_VALUE);
+ }
+
+ /**
+ * Configure the start point (initial guess).
+ *
+ * @param newStart new start point (initial guess)
+ * @return a new instance.
+ */
+ public HarmonicCurveFitter withStartPoint(double[] newStart) {
+ return new HarmonicCurveFitter(newStart.clone(), maxIter);
+ }
+
+ /**
+ * Configure the maximum number of iterations.
+ *
+ * @param newMaxIter maximum number of iterations
+ * @return a new instance.
+ */
+ public HarmonicCurveFitter withMaxIterations(int newMaxIter) {
+ return new HarmonicCurveFitter(initialGuess, newMaxIter);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ protected LeastSquaresProblem getProblem(Collection<WeightedObservedPoint> observations) {
+ // Prepare least-squares problem.
+ final int len = observations.size();
+ final double[] target = new double[len];
+ final double[] weights = new double[len];
+
+ int i = 0;
+ for (WeightedObservedPoint obs : observations) {
+ target[i] = obs.getY();
+ weights[i] = obs.getWeight();
+ ++i;
+ }
+
+ final AbstractCurveFitter.TheoreticalValuesFunction model =
+ new AbstractCurveFitter.TheoreticalValuesFunction(FUNCTION, observations);
+
+ final double[] startPoint =
+ initialGuess != null
+ ? initialGuess
+ :
+ // Compute estimation.
+ new ParameterGuesser(observations).guess();
+
+ // Return a new optimizer set up to fit a Gaussian curve to the
+ // observed points.
+ return new LeastSquaresBuilder()
+ .maxEvaluations(Integer.MAX_VALUE)
+ .maxIterations(maxIter)
+ .start(startPoint)
+ .target(target)
+ .weight(new DiagonalMatrix(weights))
+ .model(model.getModelFunction(), model.getModelFunctionJacobian())
+ .build();
+ }
+
+ /**
+ * This class guesses harmonic coefficients from a sample.
+ *
+ * <p>The algorithm used to guess the coefficients is as follows:
+ *
+ * <p>We know \( f(t) \) at some sampling points \( t_i \) and want to find \( a \), \( \omega
+ * \) and \( \phi \) such that \( f(t) = a \cos (\omega t + \phi) \).
+ *
+ * <p>From the analytical expression, we can compute two primitives : \[ If2(t) = \int f^2 dt =
+ * a^2 (t + S(t)) / 2 \] \[ If'2(t) = \int f'^2 dt = a^2 \omega^2 (t - S(t)) / 2 \] where \(S(t)
+ * = \frac{\sin(2 (\omega t + \phi))}{2\omega}\)
+ *
+ * <p>We can remove \(S\) between these expressions : \[ If'2(t) = a^2 \omega^2 t - \omega^2
+ * If2(t) \]
+ *
+ * <p>The preceding expression shows that \(If'2 (t)\) is a linear combination of both \(t\) and
+ * \(If2(t)\): \[ If'2(t) = A t + B If2(t) \]
+ *
+ * <p>From the primitive, we can deduce the same form for definite integrals between \(t_1\) and
+ * \(t_i\) for each \(t_i\) : \[ If2(t_i) - If2(t_1) = A (t_i - t_1) + B (If2 (t_i) - If2(t_1))
+ * \]
+ *
+ * <p>We can find the coefficients \(A\) and \(B\) that best fit the sample to this linear
+ * expression by computing the definite integrals for each sample points.
+ *
+ * <p>For a bilinear expression \(z(x_i, y_i) = A x_i + B y_i\), the coefficients \(A\) and
+ * \(B\) that minimize a least-squares criterion \(\sum (z_i - z(x_i, y_i))^2\) are given by
+ * these expressions: \[ A = \frac{\sum y_i y_i \sum x_i z_i - \sum x_i y_i \sum y_i z_i} {\sum
+ * x_i x_i \sum y_i y_i - \sum x_i y_i \sum x_i y_i} \] \[ B = \frac{\sum x_i x_i \sum y_i z_i -
+ * \sum x_i y_i \sum x_i z_i} {\sum x_i x_i \sum y_i y_i - \sum x_i y_i \sum x_i y_i}
+ *
+ * <p>\]
+ *
+ * <p>In fact, we can assume that both \(a\) and \(\omega\) are positive and compute them
+ * directly, knowing that \(A = a^2 \omega^2\) and that \(B = -\omega^2\). The complete
+ * algorithm is therefore: For each \(t_i\) from \(t_1\) to \(t_{n-1}\), compute: \[ f(t_i) \]
+ * \[ f'(t_i) = \frac{f (t_{i+1}) - f(t_{i-1})}{t_{i+1} - t_{i-1}} \] \[ x_i = t_i - t_1 \] \[
+ * y_i = \int_{t_1}^{t_i} f^2(t) dt \] \[ z_i = \int_{t_1}^{t_i} f'^2(t) dt \] and update the
+ * sums: \[ \sum x_i x_i, \sum y_i y_i, \sum x_i y_i, \sum x_i z_i, \sum y_i z_i \]
+ *
+ * <p>Then: \[ a = \sqrt{\frac{\sum y_i y_i \sum x_i z_i - \sum x_i y_i \sum y_i z_i } {\sum x_i
+ * y_i \sum x_i z_i - \sum x_i x_i \sum y_i z_i }} \] \[ \omega = \sqrt{\frac{\sum x_i y_i \sum
+ * x_i z_i - \sum x_i x_i \sum y_i z_i} {\sum x_i x_i \sum y_i y_i - \sum x_i y_i \sum x_i y_i}}
+ * \]
+ *
+ * <p>Once we know \(\omega\) we can compute: \[ fc = \omega f(t) \cos(\omega t) - f'(t)
+ * \sin(\omega t) \] \[ fs = \omega f(t) \sin(\omega t) + f'(t) \cos(\omega t) \]
+ *
+ * <p>It appears that \(fc = a \omega \cos(\phi)\) and \(fs = -a \omega \sin(\phi)\), so we can
+ * use these expressions to compute \(\phi\). The best estimate over the sample is given by
+ * averaging these expressions.
+ *
+ * <p>Since integrals and means are involved in the preceding estimations, these operations run
+ * in \(O(n)\) time, where \(n\) is the number of measurements.
+ */
+ public static class ParameterGuesser {
+ /** Amplitude. */
+ private final double a;
+
+ /** Angular frequency. */
+ private final double omega;
+
+ /** Phase. */
+ private final double phi;
+
+ /**
+ * Simple constructor.
+ *
+ * @param observations Sampled observations.
+ * @throws NumberIsTooSmallException if the sample is too short.
+ * @throws ZeroException if the abscissa range is zero.
+ * @throws MathIllegalStateException when the guessing procedure cannot produce sensible
+ * results.
+ */
+ public ParameterGuesser(Collection<WeightedObservedPoint> observations) {
+ if (observations.size() < 4) {
+ throw new NumberIsTooSmallException(
+ LocalizedFormats.INSUFFICIENT_OBSERVED_POINTS_IN_SAMPLE,
+ observations.size(),
+ 4,
+ true);
+ }
+
+ final WeightedObservedPoint[] sorted =
+ sortObservations(observations).toArray(new WeightedObservedPoint[0]);
+
+ final double aOmega[] = guessAOmega(sorted);
+ a = aOmega[0];
+ omega = aOmega[1];
+
+ phi = guessPhi(sorted);
+ }
+
+ /**
+ * Gets an estimation of the parameters.
+ *
+ * @return the guessed parameters, in the following order:
+ * <ul>
+ * <li>Amplitude
+ * <li>Angular frequency
+ * <li>Phase
+ * </ul>
+ */
+ public double[] guess() {
+ return new double[] {a, omega, phi};
+ }
+
+ /**
+ * Sort the observations with respect to the abscissa.
+ *
+ * @param unsorted Input observations.
+ * @return the input observations, sorted.
+ */
+ private List<WeightedObservedPoint> sortObservations(
+ Collection<WeightedObservedPoint> unsorted) {
+ final List<WeightedObservedPoint> observations =
+ new ArrayList<WeightedObservedPoint>(unsorted);
+
+ // Since the samples are almost always already sorted, this
+ // method is implemented as an insertion sort that reorders the
+ // elements in place. Insertion sort is very efficient in this case.
+ WeightedObservedPoint curr = observations.get(0);
+ final int len = observations.size();
+ for (int j = 1; j < len; j++) {
+ WeightedObservedPoint prec = curr;
+ curr = observations.get(j);
+ if (curr.getX() < prec.getX()) {
+ // the current element should be inserted closer to the beginning
+ int i = j - 1;
+ WeightedObservedPoint mI = observations.get(i);
+ while ((i >= 0) && (curr.getX() < mI.getX())) {
+ observations.set(i + 1, mI);
+ if (i-- != 0) {
+ mI = observations.get(i);
+ }
+ }
+ observations.set(i + 1, curr);
+ curr = observations.get(j);
+ }
+ }
+
+ return observations;
+ }
+
+ /**
+ * Estimate a first guess of the amplitude and angular frequency.
+ *
+ * @param observations Observations, sorted w.r.t. abscissa.
+ * @throws ZeroException if the abscissa range is zero.
+ * @throws MathIllegalStateException when the guessing procedure cannot produce sensible
+ * results.
+ * @return the guessed amplitude (at index 0) and circular frequency (at index 1).
+ */
+ private double[] guessAOmega(WeightedObservedPoint[] observations) {
+ final double[] aOmega = new double[2];
+
+ // initialize the sums for the linear model between the two integrals
+ double sx2 = 0;
+ double sy2 = 0;
+ double sxy = 0;
+ double sxz = 0;
+ double syz = 0;
+
+ double currentX = observations[0].getX();
+ double currentY = observations[0].getY();
+ double f2Integral = 0;
+ double fPrime2Integral = 0;
+ final double startX = currentX;
+ for (int i = 1; i < observations.length; ++i) {
+ // one step forward
+ final double previousX = currentX;
+ final double previousY = currentY;
+ currentX = observations[i].getX();
+ currentY = observations[i].getY();
+
+ // update the integrals of f<sup>2</sup> and f'<sup>2</sup>
+ // considering a linear model for f (and therefore constant f')
+ final double dx = currentX - previousX;
+ final double dy = currentY - previousY;
+ final double f2StepIntegral =
+ dx
+ * (previousY * previousY
+ + previousY * currentY
+ + currentY * currentY)
+ / 3;
+ final double fPrime2StepIntegral = dy * dy / dx;
+
+ final double x = currentX - startX;
+ f2Integral += f2StepIntegral;
+ fPrime2Integral += fPrime2StepIntegral;
+
+ sx2 += x * x;
+ sy2 += f2Integral * f2Integral;
+ sxy += x * f2Integral;
+ sxz += x * fPrime2Integral;
+ syz += f2Integral * fPrime2Integral;
+ }
+
+ // compute the amplitude and pulsation coefficients
+ double c1 = sy2 * sxz - sxy * syz;
+ double c2 = sxy * sxz - sx2 * syz;
+ double c3 = sx2 * sy2 - sxy * sxy;
+ if ((c1 / c2 < 0) || (c2 / c3 < 0)) {
+ final int last = observations.length - 1;
+ // Range of the observations, assuming that the
+ // observations are sorted.
+ final double xRange = observations[last].getX() - observations[0].getX();
+ if (xRange == 0) {
+ throw new ZeroException();
+ }
+ aOmega[1] = 2 * Math.PI / xRange;
+
+ double yMin = Double.POSITIVE_INFINITY;
+ double yMax = Double.NEGATIVE_INFINITY;
+ for (int i = 1; i < observations.length; ++i) {
+ final double y = observations[i].getY();
+ if (y < yMin) {
+ yMin = y;
+ }
+ if (y > yMax) {
+ yMax = y;
+ }
+ }
+ aOmega[0] = 0.5 * (yMax - yMin);
+ } else {
+ if (c2 == 0) {
+ // In some ill-conditioned cases (cf. MATH-844), the guesser
+ // procedure cannot produce sensible results.
+ throw new MathIllegalStateException(LocalizedFormats.ZERO_DENOMINATOR);
+ }
+
+ aOmega[0] = FastMath.sqrt(c1 / c2);
+ aOmega[1] = FastMath.sqrt(c2 / c3);
+ }
+
+ return aOmega;
+ }
+
+ /**
+ * Estimate a first guess of the phase.
+ *
+ * @param observations Observations, sorted w.r.t. abscissa.
+ * @return the guessed phase.
+ */
+ private double guessPhi(WeightedObservedPoint[] observations) {
+ // initialize the means
+ double fcMean = 0;
+ double fsMean = 0;
+
+ double currentX = observations[0].getX();
+ double currentY = observations[0].getY();
+ for (int i = 1; i < observations.length; ++i) {
+ // one step forward
+ final double previousX = currentX;
+ final double previousY = currentY;
+ currentX = observations[i].getX();
+ currentY = observations[i].getY();
+ final double currentYPrime = (currentY - previousY) / (currentX - previousX);
+
+ double omegaX = omega * currentX;
+ double cosine = FastMath.cos(omegaX);
+ double sine = FastMath.sin(omegaX);
+ fcMean += omega * currentY * cosine - currentYPrime * sine;
+ fsMean += omega * currentY * sine + currentYPrime * cosine;
+ }
+
+ return FastMath.atan2(-fsMean, fcMean);
+ }
+ }
+}
diff --git a/src/main/java/org/apache/commons/math3/fitting/HarmonicFitter.java b/src/main/java/org/apache/commons/math3/fitting/HarmonicFitter.java
new file mode 100644
index 0000000..1a41398
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/fitting/HarmonicFitter.java
@@ -0,0 +1,386 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math3.fitting;
+
+import org.apache.commons.math3.analysis.function.HarmonicOscillator;
+import org.apache.commons.math3.exception.MathIllegalStateException;
+import org.apache.commons.math3.exception.NumberIsTooSmallException;
+import org.apache.commons.math3.exception.ZeroException;
+import org.apache.commons.math3.exception.util.LocalizedFormats;
+import org.apache.commons.math3.optim.nonlinear.vector.MultivariateVectorOptimizer;
+import org.apache.commons.math3.util.FastMath;
+
+/**
+ * Class that implements a curve fitting specialized for sinusoids.
+ *
+ * <p>Harmonic fitting is a very simple case of curve fitting. The estimated coefficients are the
+ * amplitude a, the pulsation &omega; and the phase &phi;: <code>f (t) = a cos (&omega; t + &phi;)
+ * </code>. They are searched by a least square estimator initialized with a rough guess based on
+ * integrals.
+ *
+ * @since 2.0
+ * @deprecated As of 3.3. Please use {@link HarmonicCurveFitter} and {@link WeightedObservedPoints}
+ * instead.
+ */
+@Deprecated
+public class HarmonicFitter extends CurveFitter<HarmonicOscillator.Parametric> {
+ /**
+ * Simple constructor.
+ *
+ * @param optimizer Optimizer to use for the fitting.
+ */
+ public HarmonicFitter(final MultivariateVectorOptimizer optimizer) {
+ super(optimizer);
+ }
+
+ /**
+ * Fit an harmonic function to the observed points.
+ *
+ * @param initialGuess First guess values in the following order:
+ * <ul>
+ * <li>Amplitude
+ * <li>Angular frequency
+ * <li>Phase
+ * </ul>
+ *
+ * @return the parameters of the harmonic function that best fits the observed points (in the
+ * same order as above).
+ */
+ public double[] fit(double[] initialGuess) {
+ return fit(new HarmonicOscillator.Parametric(), initialGuess);
+ }
+
+ /**
+ * Fit an harmonic function to the observed points. An initial guess will be automatically
+ * computed.
+ *
+ * @return the parameters of the harmonic function that best fits the observed points (see the
+ * other {@link #fit(double[]) fit} method.
+ * @throws NumberIsTooSmallException if the sample is too short for the the first guess to be
+ * computed.
+ * @throws ZeroException if the first guess cannot be computed because the abscissa range is
+ * zero.
+ */
+ public double[] fit() {
+ return fit((new ParameterGuesser(getObservations())).guess());
+ }
+
+ /**
+ * This class guesses harmonic coefficients from a sample.
+ *
+ * <p>The algorithm used to guess the coefficients is as follows:
+ *
+ * <p>We know f (t) at some sampling points t<sub>i</sub> and want to find a, &omega; and &phi;
+ * such that f (t) = a cos (&omega; t + &phi;).
+ *
+ * <p>From the analytical expression, we can compute two primitives :
+ *
+ * <pre>
+ * If2 (t) = &int; f<sup>2</sup> = a<sup>2</sup> &times; [t + S (t)] / 2
+ * If'2 (t) = &int; f'<sup>2</sup> = a<sup>2</sup> &omega;<sup>2</sup> &times; [t - S (t)] / 2
+ * where S (t) = sin (2 (&omega; t + &phi;)) / (2 &omega;)
+ * </pre>
+ *
+ * <p>We can remove S between these expressions :
+ *
+ * <pre>
+ * If'2 (t) = a<sup>2</sup> &omega;<sup>2</sup> t - &omega;<sup>2</sup> If2 (t)
+ * </pre>
+ *
+ * <p>The preceding expression shows that If'2 (t) is a linear combination of both t and If2
+ * (t): If'2 (t) = A &times; t + B &times; If2 (t)
+ *
+ * <p>From the primitive, we can deduce the same form for definite integrals between
+ * t<sub>1</sub> and t<sub>i</sub> for each t<sub>i</sub> :
+ *
+ * <pre>
+ * If2 (t<sub>i</sub>) - If2 (t<sub>1</sub>) = A &times; (t<sub>i</sub> - t<sub>1</sub>) + B &times; (If2 (t<sub>i</sub>) - If2 (t<sub>1</sub>))
+ * </pre>
+ *
+ * <p>We can find the coefficients A and B that best fit the sample to this linear expression by
+ * computing the definite integrals for each sample points.
+ *
+ * <p>For a bilinear expression z (x<sub>i</sub>, y<sub>i</sub>) = A &times; x<sub>i</sub> + B
+ * &times; y<sub>i</sub>, the coefficients A and B that minimize a least square criterion &sum;
+ * (z<sub>i</sub> - z (x<sub>i</sub>, y<sub>i</sub>))<sup>2</sup> are given by these
+ * expressions:
+ *
+ * <pre>
+ *
+ * &sum;y<sub>i</sub>y<sub>i</sub> &sum;x<sub>i</sub>z<sub>i</sub> - &sum;x<sub>i</sub>y<sub>i</sub> &sum;y<sub>i</sub>z<sub>i</sub>
+ * A = ------------------------
+ * &sum;x<sub>i</sub>x<sub>i</sub> &sum;y<sub>i</sub>y<sub>i</sub> - &sum;x<sub>i</sub>y<sub>i</sub> &sum;x<sub>i</sub>y<sub>i</sub>
+ *
+ * &sum;x<sub>i</sub>x<sub>i</sub> &sum;y<sub>i</sub>z<sub>i</sub> - &sum;x<sub>i</sub>y<sub>i</sub> &sum;x<sub>i</sub>z<sub>i</sub>
+ * B = ------------------------
+ * &sum;x<sub>i</sub>x<sub>i</sub> &sum;y<sub>i</sub>y<sub>i</sub> - &sum;x<sub>i</sub>y<sub>i</sub> &sum;x<sub>i</sub>y<sub>i</sub>
+ * </pre>
+ *
+ * <p>In fact, we can assume both a and &omega; are positive and compute them directly, knowing
+ * that A = a<sup>2</sup> &omega;<sup>2</sup> and that B = - &omega;<sup>2</sup>. The complete
+ * algorithm is therefore:
+ *
+ * <pre>
+ *
+ * for each t<sub>i</sub> from t<sub>1</sub> to t<sub>n-1</sub>, compute:
+ * f (t<sub>i</sub>)
+ * f' (t<sub>i</sub>) = (f (t<sub>i+1</sub>) - f(t<sub>i-1</sub>)) / (t<sub>i+1</sub> - t<sub>i-1</sub>)
+ * x<sub>i</sub> = t<sub>i</sub> - t<sub>1</sub>
+ * y<sub>i</sub> = &int; f<sup>2</sup> from t<sub>1</sub> to t<sub>i</sub>
+ * z<sub>i</sub> = &int; f'<sup>2</sup> from t<sub>1</sub> to t<sub>i</sub>
+ * update the sums &sum;x<sub>i</sub>x<sub>i</sub>, &sum;y<sub>i</sub>y<sub>i</sub>, &sum;x<sub>i</sub>y<sub>i</sub>, &sum;x<sub>i</sub>z<sub>i</sub> and &sum;y<sub>i</sub>z<sub>i</sub>
+ * end for
+ *
+ * |--------------------------
+ * \ | &sum;y<sub>i</sub>y<sub>i</sub> &sum;x<sub>i</sub>z<sub>i</sub> - &sum;x<sub>i</sub>y<sub>i</sub> &sum;y<sub>i</sub>z<sub>i</sub>
+ * a = \ | ------------------------
+ * \| &sum;x<sub>i</sub>y<sub>i</sub> &sum;x<sub>i</sub>z<sub>i</sub> - &sum;x<sub>i</sub>x<sub>i</sub> &sum;y<sub>i</sub>z<sub>i</sub>
+ *
+ *
+ * |--------------------------
+ * \ | &sum;x<sub>i</sub>y<sub>i</sub> &sum;x<sub>i</sub>z<sub>i</sub> - &sum;x<sub>i</sub>x<sub>i</sub> &sum;y<sub>i</sub>z<sub>i</sub>
+ * &omega; = \ | ------------------------
+ * \| &sum;x<sub>i</sub>x<sub>i</sub> &sum;y<sub>i</sub>y<sub>i</sub> - &sum;x<sub>i</sub>y<sub>i</sub> &sum;x<sub>i</sub>y<sub>i</sub>
+ *
+ * </pre>
+ *
+ * <p>Once we know &omega;, we can compute:
+ *
+ * <pre>
+ * fc = &omega; f (t) cos (&omega; t) - f' (t) sin (&omega; t)
+ * fs = &omega; f (t) sin (&omega; t) + f' (t) cos (&omega; t)
+ * </pre>
+ *
+ * <p>It appears that <code>fc = a &omega; cos (&phi;)</code> and <code>
+ * fs = -a &omega; sin (&phi;)</code>, so we can use these expressions to compute &phi;. The
+ * best estimate over the sample is given by averaging these expressions.
+ *
+ * <p>Since integrals and means are involved in the preceding estimations, these operations run
+ * in O(n) time, where n is the number of measurements.
+ */
+ public static class ParameterGuesser {
+ /** Amplitude. */
+ private final double a;
+
+ /** Angular frequency. */
+ private final double omega;
+
+ /** Phase. */
+ private final double phi;
+
+ /**
+ * Simple constructor.
+ *
+ * @param observations Sampled observations.
+ * @throws NumberIsTooSmallException if the sample is too short.
+ * @throws ZeroException if the abscissa range is zero.
+ * @throws MathIllegalStateException when the guessing procedure cannot produce sensible
+ * results.
+ */
+ public ParameterGuesser(WeightedObservedPoint[] observations) {
+ if (observations.length < 4) {
+ throw new NumberIsTooSmallException(
+ LocalizedFormats.INSUFFICIENT_OBSERVED_POINTS_IN_SAMPLE,
+ observations.length,
+ 4,
+ true);
+ }
+
+ final WeightedObservedPoint[] sorted = sortObservations(observations);
+
+ final double aOmega[] = guessAOmega(sorted);
+ a = aOmega[0];
+ omega = aOmega[1];
+
+ phi = guessPhi(sorted);
+ }
+
+ /**
+ * Gets an estimation of the parameters.
+ *
+ * @return the guessed parameters, in the following order:
+ * <ul>
+ * <li>Amplitude
+ * <li>Angular frequency
+ * <li>Phase
+ * </ul>
+ */
+ public double[] guess() {
+ return new double[] {a, omega, phi};
+ }
+
+ /**
+ * Sort the observations with respect to the abscissa.
+ *
+ * @param unsorted Input observations.
+ * @return the input observations, sorted.
+ */
+ private WeightedObservedPoint[] sortObservations(WeightedObservedPoint[] unsorted) {
+ final WeightedObservedPoint[] observations = unsorted.clone();
+
+ // Since the samples are almost always already sorted, this
+ // method is implemented as an insertion sort that reorders the
+ // elements in place. Insertion sort is very efficient in this case.
+ WeightedObservedPoint curr = observations[0];
+ for (int j = 1; j < observations.length; ++j) {
+ WeightedObservedPoint prec = curr;
+ curr = observations[j];
+ if (curr.getX() < prec.getX()) {
+ // the current element should be inserted closer to the beginning
+ int i = j - 1;
+ WeightedObservedPoint mI = observations[i];
+ while ((i >= 0) && (curr.getX() < mI.getX())) {
+ observations[i + 1] = mI;
+ if (i-- != 0) {
+ mI = observations[i];
+ }
+ }
+ observations[i + 1] = curr;
+ curr = observations[j];
+ }
+ }
+
+ return observations;
+ }
+
+ /**
+ * Estimate a first guess of the amplitude and angular frequency. This method assumes that
+ * the {@link #sortObservations(WeightedObservedPoint[])} method has been called previously.
+ *
+ * @param observations Observations, sorted w.r.t. abscissa.
+ * @throws ZeroException if the abscissa range is zero.
+ * @throws MathIllegalStateException when the guessing procedure cannot produce sensible
+ * results.
+ * @return the guessed amplitude (at index 0) and circular frequency (at index 1).
+ */
+ private double[] guessAOmega(WeightedObservedPoint[] observations) {
+ final double[] aOmega = new double[2];
+
+ // initialize the sums for the linear model between the two integrals
+ double sx2 = 0;
+ double sy2 = 0;
+ double sxy = 0;
+ double sxz = 0;
+ double syz = 0;
+
+ double currentX = observations[0].getX();
+ double currentY = observations[0].getY();
+ double f2Integral = 0;
+ double fPrime2Integral = 0;
+ final double startX = currentX;
+ for (int i = 1; i < observations.length; ++i) {
+ // one step forward
+ final double previousX = currentX;
+ final double previousY = currentY;
+ currentX = observations[i].getX();
+ currentY = observations[i].getY();
+
+ // update the integrals of f<sup>2</sup> and f'<sup>2</sup>
+ // considering a linear model for f (and therefore constant f')
+ final double dx = currentX - previousX;
+ final double dy = currentY - previousY;
+ final double f2StepIntegral =
+ dx
+ * (previousY * previousY
+ + previousY * currentY
+ + currentY * currentY)
+ / 3;
+ final double fPrime2StepIntegral = dy * dy / dx;
+
+ final double x = currentX - startX;
+ f2Integral += f2StepIntegral;
+ fPrime2Integral += fPrime2StepIntegral;
+
+ sx2 += x * x;
+ sy2 += f2Integral * f2Integral;
+ sxy += x * f2Integral;
+ sxz += x * fPrime2Integral;
+ syz += f2Integral * fPrime2Integral;
+ }
+
+ // compute the amplitude and pulsation coefficients
+ double c1 = sy2 * sxz - sxy * syz;
+ double c2 = sxy * sxz - sx2 * syz;
+ double c3 = sx2 * sy2 - sxy * sxy;
+ if ((c1 / c2 < 0) || (c2 / c3 < 0)) {
+ final int last = observations.length - 1;
+ // Range of the observations, assuming that the
+ // observations are sorted.
+ final double xRange = observations[last].getX() - observations[0].getX();
+ if (xRange == 0) {
+ throw new ZeroException();
+ }
+ aOmega[1] = 2 * Math.PI / xRange;
+
+ double yMin = Double.POSITIVE_INFINITY;
+ double yMax = Double.NEGATIVE_INFINITY;
+ for (int i = 1; i < observations.length; ++i) {
+ final double y = observations[i].getY();
+ if (y < yMin) {
+ yMin = y;
+ }
+ if (y > yMax) {
+ yMax = y;
+ }
+ }
+ aOmega[0] = 0.5 * (yMax - yMin);
+ } else {
+ if (c2 == 0) {
+ // In some ill-conditioned cases (cf. MATH-844), the guesser
+ // procedure cannot produce sensible results.
+ throw new MathIllegalStateException(LocalizedFormats.ZERO_DENOMINATOR);
+ }
+
+ aOmega[0] = FastMath.sqrt(c1 / c2);
+ aOmega[1] = FastMath.sqrt(c2 / c3);
+ }
+
+ return aOmega;
+ }
+
+ /**
+ * Estimate a first guess of the phase.
+ *
+ * @param observations Observations, sorted w.r.t. abscissa.
+ * @return the guessed phase.
+ */
+ private double guessPhi(WeightedObservedPoint[] observations) {
+ // initialize the means
+ double fcMean = 0;
+ double fsMean = 0;
+
+ double currentX = observations[0].getX();
+ double currentY = observations[0].getY();
+ for (int i = 1; i < observations.length; ++i) {
+ // one step forward
+ final double previousX = currentX;
+ final double previousY = currentY;
+ currentX = observations[i].getX();
+ currentY = observations[i].getY();
+ final double currentYPrime = (currentY - previousY) / (currentX - previousX);
+
+ double omegaX = omega * currentX;
+ double cosine = FastMath.cos(omegaX);
+ double sine = FastMath.sin(omegaX);
+ fcMean += omega * currentY * cosine - currentYPrime * sine;
+ fsMean += omega * currentY * sine + currentYPrime * cosine;
+ }
+
+ return FastMath.atan2(-fsMean, fcMean);
+ }
+ }
+}
diff --git a/src/main/java/org/apache/commons/math3/fitting/PolynomialCurveFitter.java b/src/main/java/org/apache/commons/math3/fitting/PolynomialCurveFitter.java
new file mode 100644
index 0000000..ab2b5ca
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/fitting/PolynomialCurveFitter.java
@@ -0,0 +1,127 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math3.fitting;
+
+import org.apache.commons.math3.analysis.polynomials.PolynomialFunction;
+import org.apache.commons.math3.exception.MathInternalError;
+import org.apache.commons.math3.fitting.leastsquares.LeastSquaresBuilder;
+import org.apache.commons.math3.fitting.leastsquares.LeastSquaresProblem;
+import org.apache.commons.math3.linear.DiagonalMatrix;
+
+import java.util.Collection;
+
+/**
+ * Fits points to a {@link
+ * org.apache.commons.math3.analysis.polynomials.PolynomialFunction.Parametric polynomial} function.
+ * <br>
+ * The size of the {@link #withStartPoint(double[]) initial guess} array defines the degree of the
+ * polynomial to be fitted. They must be sorted in increasing order of the polynomial's degree. The
+ * optimal values of the coefficients will be returned in the same order.
+ *
+ * @since 3.3
+ */
+public class PolynomialCurveFitter extends AbstractCurveFitter {
+ /** Parametric function to be fitted. */
+ private static final PolynomialFunction.Parametric FUNCTION =
+ new PolynomialFunction.Parametric();
+
+ /** Initial guess. */
+ private final double[] initialGuess;
+
+ /** Maximum number of iterations of the optimization algorithm. */
+ private final int maxIter;
+
+ /**
+ * Contructor used by the factory methods.
+ *
+ * @param initialGuess Initial guess.
+ * @param maxIter Maximum number of iterations of the optimization algorithm.
+ * @throws MathInternalError if {@code initialGuess} is {@code null}.
+ */
+ private PolynomialCurveFitter(double[] initialGuess, int maxIter) {
+ this.initialGuess = initialGuess;
+ this.maxIter = maxIter;
+ }
+
+ /**
+ * Creates a default curve fitter. Zero will be used as initial guess for the coefficients, and
+ * the maximum number of iterations of the optimization algorithm is set to {@link
+ * Integer#MAX_VALUE}.
+ *
+ * @param degree Degree of the polynomial to be fitted.
+ * @return a curve fitter.
+ * @see #withStartPoint(double[])
+ * @see #withMaxIterations(int)
+ */
+ public static PolynomialCurveFitter create(int degree) {
+ return new PolynomialCurveFitter(new double[degree + 1], Integer.MAX_VALUE);
+ }
+
+ /**
+ * Configure the start point (initial guess).
+ *
+ * @param newStart new start point (initial guess)
+ * @return a new instance.
+ */
+ public PolynomialCurveFitter withStartPoint(double[] newStart) {
+ return new PolynomialCurveFitter(newStart.clone(), maxIter);
+ }
+
+ /**
+ * Configure the maximum number of iterations.
+ *
+ * @param newMaxIter maximum number of iterations
+ * @return a new instance.
+ */
+ public PolynomialCurveFitter withMaxIterations(int newMaxIter) {
+ return new PolynomialCurveFitter(initialGuess, newMaxIter);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ protected LeastSquaresProblem getProblem(Collection<WeightedObservedPoint> observations) {
+ // Prepare least-squares problem.
+ final int len = observations.size();
+ final double[] target = new double[len];
+ final double[] weights = new double[len];
+
+ int i = 0;
+ for (WeightedObservedPoint obs : observations) {
+ target[i] = obs.getY();
+ weights[i] = obs.getWeight();
+ ++i;
+ }
+
+ final AbstractCurveFitter.TheoreticalValuesFunction model =
+ new AbstractCurveFitter.TheoreticalValuesFunction(FUNCTION, observations);
+
+ if (initialGuess == null) {
+ throw new MathInternalError();
+ }
+
+ // Return a new least squares problem set up to fit a polynomial curve to the
+ // observed points.
+ return new LeastSquaresBuilder()
+ .maxEvaluations(Integer.MAX_VALUE)
+ .maxIterations(maxIter)
+ .start(initialGuess)
+ .target(target)
+ .weight(new DiagonalMatrix(weights))
+ .model(model.getModelFunction(), model.getModelFunctionJacobian())
+ .build();
+ }
+}
diff --git a/src/main/java/org/apache/commons/math3/fitting/PolynomialFitter.java b/src/main/java/org/apache/commons/math3/fitting/PolynomialFitter.java
new file mode 100644
index 0000000..0dd17a4
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/fitting/PolynomialFitter.java
@@ -0,0 +1,71 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math3.fitting;
+
+import org.apache.commons.math3.analysis.polynomials.PolynomialFunction;
+import org.apache.commons.math3.optim.nonlinear.vector.MultivariateVectorOptimizer;
+
+/**
+ * Polynomial fitting is a very simple case of {@link CurveFitter curve fitting}. The estimated
+ * coefficients are the polynomial coefficients (see the {@link #fit(double[]) fit} method).
+ *
+ * @since 2.0
+ * @deprecated As of 3.3. Please use {@link PolynomialCurveFitter} and {@link
+ * WeightedObservedPoints} instead.
+ */
+@Deprecated
+public class PolynomialFitter extends CurveFitter<PolynomialFunction.Parametric> {
+ /**
+ * Simple constructor.
+ *
+ * @param optimizer Optimizer to use for the fitting.
+ */
+ public PolynomialFitter(MultivariateVectorOptimizer optimizer) {
+ super(optimizer);
+ }
+
+ /**
+ * Get the coefficients of the polynomial fitting the weighted data points. The degree of the
+ * fitting polynomial is {@code guess.length - 1}.
+ *
+ * @param guess First guess for the coefficients. They must be sorted in increasing order of the
+ * polynomial's degree.
+ * @param maxEval Maximum number of evaluations of the polynomial.
+ * @return the coefficients of the polynomial that best fits the observed points.
+ * @throws org.apache.commons.math3.exception.TooManyEvaluationsException if the number of
+ * evaluations exceeds {@code maxEval}.
+ * @throws org.apache.commons.math3.exception.ConvergenceException if the algorithm failed to
+ * converge.
+ */
+ public double[] fit(int maxEval, double[] guess) {
+ return fit(maxEval, new PolynomialFunction.Parametric(), guess);
+ }
+
+ /**
+ * Get the coefficients of the polynomial fitting the weighted data points. The degree of the
+ * fitting polynomial is {@code guess.length - 1}.
+ *
+ * @param guess First guess for the coefficients. They must be sorted in increasing order of the
+ * polynomial's degree.
+ * @return the coefficients of the polynomial that best fits the observed points.
+ * @throws org.apache.commons.math3.exception.ConvergenceException if the algorithm failed to
+ * converge.
+ */
+ public double[] fit(double[] guess) {
+ return fit(new PolynomialFunction.Parametric(), guess);
+ }
+}
diff --git a/src/main/java/org/apache/commons/math3/fitting/SimpleCurveFitter.java b/src/main/java/org/apache/commons/math3/fitting/SimpleCurveFitter.java
new file mode 100644
index 0000000..304f661
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/fitting/SimpleCurveFitter.java
@@ -0,0 +1,119 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math3.fitting;
+
+import org.apache.commons.math3.analysis.ParametricUnivariateFunction;
+import org.apache.commons.math3.fitting.leastsquares.LeastSquaresBuilder;
+import org.apache.commons.math3.fitting.leastsquares.LeastSquaresProblem;
+import org.apache.commons.math3.linear.DiagonalMatrix;
+
+import java.util.Collection;
+
+/**
+ * Fits points to a user-defined {@link ParametricUnivariateFunction function}.
+ *
+ * @since 3.4
+ */
+public class SimpleCurveFitter extends AbstractCurveFitter {
+ /** Function to fit. */
+ private final ParametricUnivariateFunction function;
+
+ /** Initial guess for the parameters. */
+ private final double[] initialGuess;
+
+ /** Maximum number of iterations of the optimization algorithm. */
+ private final int maxIter;
+
+ /**
+ * Contructor used by the factory methods.
+ *
+ * @param function Function to fit.
+ * @param initialGuess Initial guess. Cannot be {@code null}. Its length must be consistent with
+ * the number of parameters of the {@code function} to fit.
+ * @param maxIter Maximum number of iterations of the optimization algorithm.
+ */
+ private SimpleCurveFitter(
+ ParametricUnivariateFunction function, double[] initialGuess, int maxIter) {
+ this.function = function;
+ this.initialGuess = initialGuess;
+ this.maxIter = maxIter;
+ }
+
+ /**
+ * Creates a curve fitter. The maximum number of iterations of the optimization algorithm is set
+ * to {@link Integer#MAX_VALUE}.
+ *
+ * @param f Function to fit.
+ * @param start Initial guess for the parameters. Cannot be {@code null}. Its length must be
+ * consistent with the number of parameters of the function to fit.
+ * @return a curve fitter.
+ * @see #withStartPoint(double[])
+ * @see #withMaxIterations(int)
+ */
+ public static SimpleCurveFitter create(ParametricUnivariateFunction f, double[] start) {
+ return new SimpleCurveFitter(f, start, Integer.MAX_VALUE);
+ }
+
+ /**
+ * Configure the start point (initial guess).
+ *
+ * @param newStart new start point (initial guess)
+ * @return a new instance.
+ */
+ public SimpleCurveFitter withStartPoint(double[] newStart) {
+ return new SimpleCurveFitter(function, newStart.clone(), maxIter);
+ }
+
+ /**
+ * Configure the maximum number of iterations.
+ *
+ * @param newMaxIter maximum number of iterations
+ * @return a new instance.
+ */
+ public SimpleCurveFitter withMaxIterations(int newMaxIter) {
+ return new SimpleCurveFitter(function, initialGuess, newMaxIter);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ protected LeastSquaresProblem getProblem(Collection<WeightedObservedPoint> observations) {
+ // Prepare least-squares problem.
+ final int len = observations.size();
+ final double[] target = new double[len];
+ final double[] weights = new double[len];
+
+ int count = 0;
+ for (WeightedObservedPoint obs : observations) {
+ target[count] = obs.getY();
+ weights[count] = obs.getWeight();
+ ++count;
+ }
+
+ final AbstractCurveFitter.TheoreticalValuesFunction model =
+ new AbstractCurveFitter.TheoreticalValuesFunction(function, observations);
+
+ // Create an optimizer for fitting the curve to the observed points.
+ return new LeastSquaresBuilder()
+ .maxEvaluations(Integer.MAX_VALUE)
+ .maxIterations(maxIter)
+ .start(initialGuess)
+ .target(target)
+ .weight(new DiagonalMatrix(weights))
+ .model(model.getModelFunction(), model.getModelFunctionJacobian())
+ .build();
+ }
+}
diff --git a/src/main/java/org/apache/commons/math3/fitting/WeightedObservedPoint.java b/src/main/java/org/apache/commons/math3/fitting/WeightedObservedPoint.java
new file mode 100644
index 0000000..ec88747
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/fitting/WeightedObservedPoint.java
@@ -0,0 +1,81 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math3.fitting;
+
+import java.io.Serializable;
+
+/**
+ * This class is a simple container for weighted observed point in {@link CurveFitter curve
+ * fitting}.
+ *
+ * <p>Instances of this class are guaranteed to be immutable.
+ *
+ * @since 2.0
+ */
+public class WeightedObservedPoint implements Serializable {
+ /** Serializable version id. */
+ private static final long serialVersionUID = 5306874947404636157L;
+
+ /** Weight of the measurement in the fitting process. */
+ private final double weight;
+
+ /** Abscissa of the point. */
+ private final double x;
+
+ /** Observed value of the function at x. */
+ private final double y;
+
+ /**
+ * Simple constructor.
+ *
+ * @param weight Weight of the measurement in the fitting process.
+ * @param x Abscissa of the measurement.
+ * @param y Ordinate of the measurement.
+ */
+ public WeightedObservedPoint(final double weight, final double x, final double y) {
+ this.weight = weight;
+ this.x = x;
+ this.y = y;
+ }
+
+ /**
+ * Gets the weight of the measurement in the fitting process.
+ *
+ * @return the weight of the measurement in the fitting process.
+ */
+ public double getWeight() {
+ return weight;
+ }
+
+ /**
+ * Gets the abscissa of the point.
+ *
+ * @return the abscissa of the point.
+ */
+ public double getX() {
+ return x;
+ }
+
+ /**
+ * Gets the observed value of the function at x.
+ *
+ * @return the observed value of the function at x.
+ */
+ public double getY() {
+ return y;
+ }
+}
diff --git a/src/main/java/org/apache/commons/math3/fitting/WeightedObservedPoints.java b/src/main/java/org/apache/commons/math3/fitting/WeightedObservedPoints.java
new file mode 100644
index 0000000..69deaae
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/fitting/WeightedObservedPoints.java
@@ -0,0 +1,100 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math3.fitting;
+
+import java.io.Serializable;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Simple container for weighted observed points used in {@link AbstractCurveFitter curve fitting}
+ * algorithms.
+ *
+ * @since 3.3
+ */
+public class WeightedObservedPoints implements Serializable {
+ /** Serializable version id. */
+ private static final long serialVersionUID = 20130813L;
+
+ /** Observed points. */
+ private final List<WeightedObservedPoint> observations = new ArrayList<WeightedObservedPoint>();
+
+ /**
+ * Adds a point to the sample. Calling this method is equivalent to calling {@code add(1.0, x,
+ * y)}.
+ *
+ * @param x Abscissa of the point.
+ * @param y Observed value at {@code x}. After fitting we should have {@code f(x)} as close as
+ * possible to this value.
+ * @see #add(double, double, double)
+ * @see #add(WeightedObservedPoint)
+ * @see #toList()
+ */
+ public void add(double x, double y) {
+ add(1d, x, y);
+ }
+
+ /**
+ * Adds a point to the sample.
+ *
+ * @param weight Weight of the observed point.
+ * @param x Abscissa of the point.
+ * @param y Observed value at {@code x}. After fitting we should have {@code f(x)} as close as
+ * possible to this value.
+ * @see #add(double, double)
+ * @see #add(WeightedObservedPoint)
+ * @see #toList()
+ */
+ public void add(double weight, double x, double y) {
+ observations.add(new WeightedObservedPoint(weight, x, y));
+ }
+
+ /**
+ * Adds a point to the sample.
+ *
+ * @param observed Observed point to add.
+ * @see #add(double, double)
+ * @see #add(double, double, double)
+ * @see #toList()
+ */
+ public void add(WeightedObservedPoint observed) {
+ observations.add(observed);
+ }
+
+ /**
+ * Gets a <em>snapshot</em> of the observed points. The list of stored points is copied in order
+ * to ensure that modification of the returned instance does not affect this container.
+ * Conversely, further modification of this container (through the {@code add} or {@code clear}
+ * methods) will not affect the returned list.
+ *
+ * @return the observed points, in the order they were added to this container.
+ * @see #add(double, double)
+ * @see #add(double, double, double)
+ * @see #add(WeightedObservedPoint)
+ */
+ public List<WeightedObservedPoint> toList() {
+ // The copy is necessary to ensure thread-safety because of the
+ // "clear" method (which otherwise would be able to empty the
+ // list of points while it is being used by another thread).
+ return new ArrayList<WeightedObservedPoint>(observations);
+ }
+
+ /** Removes all observations from this container. */
+ public void clear() {
+ observations.clear();
+ }
+}
diff --git a/src/main/java/org/apache/commons/math3/fitting/leastsquares/AbstractEvaluation.java b/src/main/java/org/apache/commons/math3/fitting/leastsquares/AbstractEvaluation.java
new file mode 100644
index 0000000..b164380
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/fitting/leastsquares/AbstractEvaluation.java
@@ -0,0 +1,87 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math3.fitting.leastsquares;
+
+import org.apache.commons.math3.fitting.leastsquares.LeastSquaresProblem.Evaluation;
+import org.apache.commons.math3.linear.ArrayRealVector;
+import org.apache.commons.math3.linear.DecompositionSolver;
+import org.apache.commons.math3.linear.QRDecomposition;
+import org.apache.commons.math3.linear.RealMatrix;
+import org.apache.commons.math3.linear.RealVector;
+import org.apache.commons.math3.util.FastMath;
+
+/**
+ * An implementation of {@link Evaluation} that is designed for extension. All of the
+ * methods implemented here use the methods that are left unimplemented.
+ * <p/>
+ * TODO cache results?
+ *
+ * @since 3.3
+ */
+public abstract class AbstractEvaluation implements Evaluation {
+
+ /** number of observations */
+ private final int observationSize;
+
+ /**
+ * Constructor.
+ *
+ * @param observationSize the number of observation. Needed for {@link
+ * #getRMS()}.
+ */
+ AbstractEvaluation(final int observationSize) {
+ this.observationSize = observationSize;
+ }
+
+ /** {@inheritDoc} */
+ public RealMatrix getCovariances(double threshold) {
+ // Set up the Jacobian.
+ final RealMatrix j = this.getJacobian();
+
+ // Compute transpose(J)J.
+ final RealMatrix jTj = j.transpose().multiply(j);
+
+ // Compute the covariances matrix.
+ final DecompositionSolver solver
+ = new QRDecomposition(jTj, threshold).getSolver();
+ return solver.getInverse();
+ }
+
+ /** {@inheritDoc} */
+ public RealVector getSigma(double covarianceSingularityThreshold) {
+ final RealMatrix cov = this.getCovariances(covarianceSingularityThreshold);
+ final int nC = cov.getColumnDimension();
+ final RealVector sig = new ArrayRealVector(nC);
+ for (int i = 0; i < nC; ++i) {
+ sig.setEntry(i, FastMath.sqrt(cov.getEntry(i,i)));
+ }
+ return sig;
+ }
+
+ /** {@inheritDoc} */
+ public double getRMS() {
+ final double cost = this.getCost();
+ return FastMath.sqrt(cost * cost / this.observationSize);
+ }
+
+ /** {@inheritDoc} */
+ public double getCost() {
+ final ArrayRealVector r = new ArrayRealVector(this.getResiduals());
+ return FastMath.sqrt(r.dotProduct(r));
+ }
+
+}
diff --git a/src/main/java/org/apache/commons/math3/fitting/leastsquares/DenseWeightedEvaluation.java b/src/main/java/org/apache/commons/math3/fitting/leastsquares/DenseWeightedEvaluation.java
new file mode 100644
index 0000000..89f5f1f
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/fitting/leastsquares/DenseWeightedEvaluation.java
@@ -0,0 +1,68 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math3.fitting.leastsquares;
+
+import org.apache.commons.math3.fitting.leastsquares.LeastSquaresProblem.Evaluation;
+import org.apache.commons.math3.linear.RealMatrix;
+import org.apache.commons.math3.linear.RealVector;
+
+/**
+ * Applies a dense weight matrix to an evaluation.
+ *
+ * @since 3.3
+ */
+class DenseWeightedEvaluation extends AbstractEvaluation {
+
+ /** the unweighted evaluation */
+ private final Evaluation unweighted;
+ /** reference to the weight square root matrix */
+ private final RealMatrix weightSqrt;
+
+ /**
+ * Create a weighted evaluation from an unweighted one.
+ *
+ * @param unweighted the evalutation before weights are applied
+ * @param weightSqrt the matrix square root of the weight matrix
+ */
+ DenseWeightedEvaluation(final Evaluation unweighted,
+ final RealMatrix weightSqrt) {
+ // weight square root is square, nR=nC=number of observations
+ super(weightSqrt.getColumnDimension());
+ this.unweighted = unweighted;
+ this.weightSqrt = weightSqrt;
+ }
+
+ /* apply weights */
+
+ /** {@inheritDoc} */
+ public RealMatrix getJacobian() {
+ return weightSqrt.multiply(this.unweighted.getJacobian());
+ }
+
+ /** {@inheritDoc} */
+ public RealVector getResiduals() {
+ return this.weightSqrt.operate(this.unweighted.getResiduals());
+ }
+
+ /* delegate */
+
+ /** {@inheritDoc} */
+ public RealVector getPoint() {
+ return unweighted.getPoint();
+ }
+
+}
diff --git a/src/main/java/org/apache/commons/math3/fitting/leastsquares/EvaluationRmsChecker.java b/src/main/java/org/apache/commons/math3/fitting/leastsquares/EvaluationRmsChecker.java
new file mode 100644
index 0000000..ceb5988
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/fitting/leastsquares/EvaluationRmsChecker.java
@@ -0,0 +1,75 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math3.fitting.leastsquares;
+
+import org.apache.commons.math3.fitting.leastsquares.LeastSquaresProblem.Evaluation;
+import org.apache.commons.math3.optim.ConvergenceChecker;
+import org.apache.commons.math3.util.Precision;
+
+/**
+ * Check if an optimization has converged based on the change in computed RMS.
+ *
+ * @since 3.4
+ */
+public class EvaluationRmsChecker implements ConvergenceChecker<Evaluation> {
+
+ /** relative tolerance for comparisons. */
+ private final double relTol;
+ /** absolute tolerance for comparisons. */
+ private final double absTol;
+
+ /**
+ * Create a convergence checker for the RMS with the same relative and absolute
+ * tolerance.
+ *
+ * <p>Convenience constructor for when the relative and absolute tolerances are the
+ * same. Same as {@code new EvaluationRmsChecker(tol, tol)}.
+ *
+ * @param tol the relative and absolute tolerance.
+ * @see #EvaluationRmsChecker(double, double)
+ */
+ public EvaluationRmsChecker(final double tol) {
+ this(tol, tol);
+ }
+
+ /**
+ * Create a convergence checker for the RMS with a relative and absolute tolerance.
+ *
+ * <p>The optimization has converged when the RMS of consecutive evaluations are equal
+ * to within the given relative tolerance or absolute tolerance.
+ *
+ * @param relTol the relative tolerance.
+ * @param absTol the absolute tolerance.
+ * @see Precision#equals(double, double, double)
+ * @see Precision#equalsWithRelativeTolerance(double, double, double)
+ */
+ public EvaluationRmsChecker(final double relTol, final double absTol) {
+ this.relTol = relTol;
+ this.absTol = absTol;
+ }
+
+ /** {@inheritDoc} */
+ public boolean converged(final int iteration,
+ final Evaluation previous,
+ final Evaluation current) {
+ final double prevRms = previous.getRMS();
+ final double currRms = current.getRMS();
+ return Precision.equals(prevRms, currRms, this.absTol) ||
+ Precision.equalsWithRelativeTolerance(prevRms, currRms, this.relTol);
+ }
+
+}
diff --git a/src/main/java/org/apache/commons/math3/fitting/leastsquares/GaussNewtonOptimizer.java b/src/main/java/org/apache/commons/math3/fitting/leastsquares/GaussNewtonOptimizer.java
new file mode 100644
index 0000000..8157706
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/fitting/leastsquares/GaussNewtonOptimizer.java
@@ -0,0 +1,299 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math3.fitting.leastsquares;
+
+import org.apache.commons.math3.exception.ConvergenceException;
+import org.apache.commons.math3.exception.NullArgumentException;
+import org.apache.commons.math3.exception.util.LocalizedFormats;
+import org.apache.commons.math3.fitting.leastsquares.LeastSquaresProblem.Evaluation;
+import org.apache.commons.math3.linear.ArrayRealVector;
+import org.apache.commons.math3.linear.CholeskyDecomposition;
+import org.apache.commons.math3.linear.LUDecomposition;
+import org.apache.commons.math3.linear.MatrixUtils;
+import org.apache.commons.math3.linear.NonPositiveDefiniteMatrixException;
+import org.apache.commons.math3.linear.QRDecomposition;
+import org.apache.commons.math3.linear.RealMatrix;
+import org.apache.commons.math3.linear.RealVector;
+import org.apache.commons.math3.linear.SingularMatrixException;
+import org.apache.commons.math3.linear.SingularValueDecomposition;
+import org.apache.commons.math3.optim.ConvergenceChecker;
+import org.apache.commons.math3.util.Incrementor;
+import org.apache.commons.math3.util.Pair;
+
+/**
+ * Gauss-Newton least-squares solver.
+ * <p> This class solve a least-square problem by
+ * solving the normal equations of the linearized problem at each iteration. Either LU
+ * decomposition or Cholesky decomposition can be used to solve the normal equations,
+ * or QR decomposition or SVD decomposition can be used to solve the linear system. LU
+ * decomposition is faster but QR decomposition is more robust for difficult problems,
+ * and SVD can compute a solution for rank-deficient problems.
+ * </p>
+ *
+ * @since 3.3
+ */
+public class GaussNewtonOptimizer implements LeastSquaresOptimizer {
+
+ /** The decomposition algorithm to use to solve the normal equations. */
+ //TODO move to linear package and expand options?
+ public enum Decomposition {
+ /**
+ * Solve by forming the normal equations (J<sup>T</sup>Jx=J<sup>T</sup>r) and
+ * using the {@link LUDecomposition}.
+ *
+ * <p> Theoretically this method takes mn<sup>2</sup>/2 operations to compute the
+ * normal matrix and n<sup>3</sup>/3 operations (m > n) to solve the system using
+ * the LU decomposition. </p>
+ */
+ LU {
+ @Override
+ protected RealVector solve(final RealMatrix jacobian,
+ final RealVector residuals) {
+ try {
+ final Pair<RealMatrix, RealVector> normalEquation =
+ computeNormalMatrix(jacobian, residuals);
+ final RealMatrix normal = normalEquation.getFirst();
+ final RealVector jTr = normalEquation.getSecond();
+ return new LUDecomposition(normal, SINGULARITY_THRESHOLD)
+ .getSolver()
+ .solve(jTr);
+ } catch (SingularMatrixException e) {
+ throw new ConvergenceException(LocalizedFormats.UNABLE_TO_SOLVE_SINGULAR_PROBLEM, e);
+ }
+ }
+ },
+ /**
+ * Solve the linear least squares problem (Jx=r) using the {@link
+ * QRDecomposition}.
+ *
+ * <p> Theoretically this method takes mn<sup>2</sup> - n<sup>3</sup>/3 operations
+ * (m > n) and has better numerical accuracy than any method that forms the normal
+ * equations. </p>
+ */
+ QR {
+ @Override
+ protected RealVector solve(final RealMatrix jacobian,
+ final RealVector residuals) {
+ try {
+ return new QRDecomposition(jacobian, SINGULARITY_THRESHOLD)
+ .getSolver()
+ .solve(residuals);
+ } catch (SingularMatrixException e) {
+ throw new ConvergenceException(LocalizedFormats.UNABLE_TO_SOLVE_SINGULAR_PROBLEM, e);
+ }
+ }
+ },
+ /**
+ * Solve by forming the normal equations (J<sup>T</sup>Jx=J<sup>T</sup>r) and
+ * using the {@link CholeskyDecomposition}.
+ *
+ * <p> Theoretically this method takes mn<sup>2</sup>/2 operations to compute the
+ * normal matrix and n<sup>3</sup>/6 operations (m > n) to solve the system using
+ * the Cholesky decomposition. </p>
+ */
+ CHOLESKY {
+ @Override
+ protected RealVector solve(final RealMatrix jacobian,
+ final RealVector residuals) {
+ try {
+ final Pair<RealMatrix, RealVector> normalEquation =
+ computeNormalMatrix(jacobian, residuals);
+ final RealMatrix normal = normalEquation.getFirst();
+ final RealVector jTr = normalEquation.getSecond();
+ return new CholeskyDecomposition(
+ normal, SINGULARITY_THRESHOLD, SINGULARITY_THRESHOLD)
+ .getSolver()
+ .solve(jTr);
+ } catch (NonPositiveDefiniteMatrixException e) {
+ throw new ConvergenceException(LocalizedFormats.UNABLE_TO_SOLVE_SINGULAR_PROBLEM, e);
+ }
+ }
+ },
+ /**
+ * Solve the linear least squares problem using the {@link
+ * SingularValueDecomposition}.
+ *
+ * <p> This method is slower, but can provide a solution for rank deficient and
+ * nearly singular systems.
+ */
+ SVD {
+ @Override
+ protected RealVector solve(final RealMatrix jacobian,
+ final RealVector residuals) {
+ return new SingularValueDecomposition(jacobian)
+ .getSolver()
+ .solve(residuals);
+ }
+ };
+
+ /**
+ * Solve the linear least squares problem Jx=r.
+ *
+ * @param jacobian the Jacobian matrix, J. the number of rows >= the number or
+ * columns.
+ * @param residuals the computed residuals, r.
+ * @return the solution x, to the linear least squares problem Jx=r.
+ * @throws ConvergenceException if the matrix properties (e.g. singular) do not
+ * permit a solution.
+ */
+ protected abstract RealVector solve(RealMatrix jacobian,
+ RealVector residuals);
+ }
+
+ /**
+ * The singularity threshold for matrix decompositions. Determines when a {@link
+ * ConvergenceException} is thrown. The current value was the default value for {@link
+ * LUDecomposition}.
+ */
+ private static final double SINGULARITY_THRESHOLD = 1e-11;
+
+ /** Indicator for using LU decomposition. */
+ private final Decomposition decomposition;
+
+ /**
+ * Creates a Gauss Newton optimizer.
+ * <p/>
+ * The default for the algorithm is to solve the normal equations using QR
+ * decomposition.
+ */
+ public GaussNewtonOptimizer() {
+ this(Decomposition.QR);
+ }
+
+ /**
+ * Create a Gauss Newton optimizer that uses the given decomposition algorithm to
+ * solve the normal equations.
+ *
+ * @param decomposition the {@link Decomposition} algorithm.
+ */
+ public GaussNewtonOptimizer(final Decomposition decomposition) {
+ this.decomposition = decomposition;
+ }
+
+ /**
+ * Get the matrix decomposition algorithm used to solve the normal equations.
+ *
+ * @return the matrix {@link Decomposition} algoritm.
+ */
+ public Decomposition getDecomposition() {
+ return this.decomposition;
+ }
+
+ /**
+ * Configure the decomposition algorithm.
+ *
+ * @param newDecomposition the {@link Decomposition} algorithm to use.
+ * @return a new instance.
+ */
+ public GaussNewtonOptimizer withDecomposition(final Decomposition newDecomposition) {
+ return new GaussNewtonOptimizer(newDecomposition);
+ }
+
+ /** {@inheritDoc} */
+ public Optimum optimize(final LeastSquaresProblem lsp) {
+ //create local evaluation and iteration counts
+ final Incrementor evaluationCounter = lsp.getEvaluationCounter();
+ final Incrementor iterationCounter = lsp.getIterationCounter();
+ final ConvergenceChecker<Evaluation> checker
+ = lsp.getConvergenceChecker();
+
+ // Computation will be useless without a checker (see "for-loop").
+ if (checker == null) {
+ throw new NullArgumentException();
+ }
+
+ RealVector currentPoint = lsp.getStart();
+
+ // iterate until convergence is reached
+ Evaluation current = null;
+ while (true) {
+ iterationCounter.incrementCount();
+
+ // evaluate the objective function and its jacobian
+ Evaluation previous = current;
+ // Value of the objective function at "currentPoint".
+ evaluationCounter.incrementCount();
+ current = lsp.evaluate(currentPoint);
+ final RealVector currentResiduals = current.getResiduals();
+ final RealMatrix weightedJacobian = current.getJacobian();
+ currentPoint = current.getPoint();
+
+ // Check convergence.
+ if (previous != null &&
+ checker.converged(iterationCounter.getCount(), previous, current)) {
+ return new OptimumImpl(current,
+ evaluationCounter.getCount(),
+ iterationCounter.getCount());
+ }
+
+ // solve the linearized least squares problem
+ final RealVector dX = this.decomposition.solve(weightedJacobian, currentResiduals);
+ // update the estimated parameters
+ currentPoint = currentPoint.add(dX);
+ }
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public String toString() {
+ return "GaussNewtonOptimizer{" +
+ "decomposition=" + decomposition +
+ '}';
+ }
+
+ /**
+ * Compute the normal matrix, J<sup>T</sup>J.
+ *
+ * @param jacobian the m by n jacobian matrix, J. Input.
+ * @param residuals the m by 1 residual vector, r. Input.
+ * @return the n by n normal matrix and the n by 1 J<sup>Tr vector.
+ */
+ private static Pair<RealMatrix, RealVector> computeNormalMatrix(final RealMatrix jacobian,
+ final RealVector residuals) {
+ //since the normal matrix is symmetric, we only need to compute half of it.
+ final int nR = jacobian.getRowDimension();
+ final int nC = jacobian.getColumnDimension();
+ //allocate space for return values
+ final RealMatrix normal = MatrixUtils.createRealMatrix(nC, nC);
+ final RealVector jTr = new ArrayRealVector(nC);
+ //for each measurement
+ for (int i = 0; i < nR; ++i) {
+ //compute JTr for measurement i
+ for (int j = 0; j < nC; j++) {
+ jTr.setEntry(j, jTr.getEntry(j) +
+ residuals.getEntry(i) * jacobian.getEntry(i, j));
+ }
+
+ // add the the contribution to the normal matrix for measurement i
+ for (int k = 0; k < nC; ++k) {
+ //only compute the upper triangular part
+ for (int l = k; l < nC; ++l) {
+ normal.setEntry(k, l, normal.getEntry(k, l) +
+ jacobian.getEntry(i, k) * jacobian.getEntry(i, l));
+ }
+ }
+ }
+ //copy the upper triangular part to the lower triangular part.
+ for (int i = 0; i < nC; i++) {
+ for (int j = 0; j < i; j++) {
+ normal.setEntry(i, j, normal.getEntry(j, i));
+ }
+ }
+ return new Pair<RealMatrix, RealVector>(normal, jTr);
+ }
+
+}
diff --git a/src/main/java/org/apache/commons/math3/fitting/leastsquares/LeastSquaresAdapter.java b/src/main/java/org/apache/commons/math3/fitting/leastsquares/LeastSquaresAdapter.java
new file mode 100644
index 0000000..1c09874
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/fitting/leastsquares/LeastSquaresAdapter.java
@@ -0,0 +1,77 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math3.fitting.leastsquares;
+
+import org.apache.commons.math3.linear.RealVector;
+import org.apache.commons.math3.optim.ConvergenceChecker;
+import org.apache.commons.math3.util.Incrementor;
+
+/**
+ * An adapter that delegates to another implementation of {@link LeastSquaresProblem}.
+ *
+ * @since 3.3
+ */
+public class LeastSquaresAdapter implements LeastSquaresProblem {
+
+ /** the delegate problem */
+ private final LeastSquaresProblem problem;
+
+ /**
+ * Delegate the {@link LeastSquaresProblem} interface to the given implementation.
+ *
+ * @param problem the delegate
+ */
+ public LeastSquaresAdapter(final LeastSquaresProblem problem) {
+ this.problem = problem;
+ }
+
+ /** {@inheritDoc} */
+ public RealVector getStart() {
+ return problem.getStart();
+ }
+
+ /** {@inheritDoc} */
+ public int getObservationSize() {
+ return problem.getObservationSize();
+ }
+
+ /** {@inheritDoc} */
+ public int getParameterSize() {
+ return problem.getParameterSize();
+ }
+
+ /** {@inheritDoc}
+ * @param point*/
+ public Evaluation evaluate(final RealVector point) {
+ return problem.evaluate(point);
+ }
+
+ /** {@inheritDoc} */
+ public Incrementor getEvaluationCounter() {
+ return problem.getEvaluationCounter();
+ }
+
+ /** {@inheritDoc} */
+ public Incrementor getIterationCounter() {
+ return problem.getIterationCounter();
+ }
+
+ /** {@inheritDoc} */
+ public ConvergenceChecker<Evaluation> getConvergenceChecker() {
+ return problem.getConvergenceChecker();
+ }
+}
diff --git a/src/main/java/org/apache/commons/math3/fitting/leastsquares/LeastSquaresBuilder.java b/src/main/java/org/apache/commons/math3/fitting/leastsquares/LeastSquaresBuilder.java
new file mode 100644
index 0000000..7b14b37
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/fitting/leastsquares/LeastSquaresBuilder.java
@@ -0,0 +1,226 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math3.fitting.leastsquares;
+
+import org.apache.commons.math3.analysis.MultivariateMatrixFunction;
+import org.apache.commons.math3.analysis.MultivariateVectorFunction;
+import org.apache.commons.math3.fitting.leastsquares.LeastSquaresProblem.Evaluation;
+import org.apache.commons.math3.linear.ArrayRealVector;
+import org.apache.commons.math3.linear.RealMatrix;
+import org.apache.commons.math3.linear.RealVector;
+import org.apache.commons.math3.optim.ConvergenceChecker;
+import org.apache.commons.math3.optim.PointVectorValuePair;
+
+/**
+ * A mutable builder for {@link LeastSquaresProblem}s.
+ *
+ * @see LeastSquaresFactory
+ * @since 3.3
+ */
+public class LeastSquaresBuilder {
+
+ /** max evaluations */
+ private int maxEvaluations;
+ /** max iterations */
+ private int maxIterations;
+ /** convergence checker */
+ private ConvergenceChecker<Evaluation> checker;
+ /** model function */
+ private MultivariateJacobianFunction model;
+ /** observed values */
+ private RealVector target;
+ /** initial guess */
+ private RealVector start;
+ /** weight matrix */
+ private RealMatrix weight;
+ /**
+ * Lazy evaluation.
+ *
+ * @since 3.4
+ */
+ private boolean lazyEvaluation;
+ /** Validator.
+ *
+ * @since 3.4
+ */
+ private ParameterValidator paramValidator;
+
+
+ /**
+ * Construct a {@link LeastSquaresProblem} from the data in this builder.
+ *
+ * @return a new {@link LeastSquaresProblem}.
+ */
+ public LeastSquaresProblem build() {
+ return LeastSquaresFactory.create(model,
+ target,
+ start,
+ weight,
+ checker,
+ maxEvaluations,
+ maxIterations,
+ lazyEvaluation,
+ paramValidator);
+ }
+
+ /**
+ * Configure the max evaluations.
+ *
+ * @param newMaxEvaluations the maximum number of evaluations permitted.
+ * @return this
+ */
+ public LeastSquaresBuilder maxEvaluations(final int newMaxEvaluations) {
+ this.maxEvaluations = newMaxEvaluations;
+ return this;
+ }
+
+ /**
+ * Configure the max iterations.
+ *
+ * @param newMaxIterations the maximum number of iterations permitted.
+ * @return this
+ */
+ public LeastSquaresBuilder maxIterations(final int newMaxIterations) {
+ this.maxIterations = newMaxIterations;
+ return this;
+ }
+
+ /**
+ * Configure the convergence checker.
+ *
+ * @param newChecker the convergence checker.
+ * @return this
+ */
+ public LeastSquaresBuilder checker(final ConvergenceChecker<Evaluation> newChecker) {
+ this.checker = newChecker;
+ return this;
+ }
+
+ /**
+ * Configure the convergence checker.
+ * <p/>
+ * This function is an overloaded version of {@link #checker(ConvergenceChecker)}.
+ *
+ * @param newChecker the convergence checker.
+ * @return this
+ */
+ public LeastSquaresBuilder checkerPair(final ConvergenceChecker<PointVectorValuePair> newChecker) {
+ return this.checker(LeastSquaresFactory.evaluationChecker(newChecker));
+ }
+
+ /**
+ * Configure the model function.
+ *
+ * @param value the model function value
+ * @param jacobian the Jacobian of {@code value}
+ * @return this
+ */
+ public LeastSquaresBuilder model(final MultivariateVectorFunction value,
+ final MultivariateMatrixFunction jacobian) {
+ return model(LeastSquaresFactory.model(value, jacobian));
+ }
+
+ /**
+ * Configure the model function.
+ *
+ * @param newModel the model function value and Jacobian
+ * @return this
+ */
+ public LeastSquaresBuilder model(final MultivariateJacobianFunction newModel) {
+ this.model = newModel;
+ return this;
+ }
+
+ /**
+ * Configure the observed data.
+ *
+ * @param newTarget the observed data.
+ * @return this
+ */
+ public LeastSquaresBuilder target(final RealVector newTarget) {
+ this.target = newTarget;
+ return this;
+ }
+
+ /**
+ * Configure the observed data.
+ *
+ * @param newTarget the observed data.
+ * @return this
+ */
+ public LeastSquaresBuilder target(final double[] newTarget) {
+ return target(new ArrayRealVector(newTarget, false));
+ }
+
+ /**
+ * Configure the initial guess.
+ *
+ * @param newStart the initial guess.
+ * @return this
+ */
+ public LeastSquaresBuilder start(final RealVector newStart) {
+ this.start = newStart;
+ return this;
+ }
+
+ /**
+ * Configure the initial guess.
+ *
+ * @param newStart the initial guess.
+ * @return this
+ */
+ public LeastSquaresBuilder start(final double[] newStart) {
+ return start(new ArrayRealVector(newStart, false));
+ }
+
+ /**
+ * Configure the weight matrix.
+ *
+ * @param newWeight the weight matrix
+ * @return this
+ */
+ public LeastSquaresBuilder weight(final RealMatrix newWeight) {
+ this.weight = newWeight;
+ return this;
+ }
+
+ /**
+ * Configure whether evaluation will be lazy or not.
+ *
+ * @param newValue Whether to perform lazy evaluation.
+ * @return this object.
+ *
+ * @since 3.4
+ */
+ public LeastSquaresBuilder lazyEvaluation(final boolean newValue) {
+ lazyEvaluation = newValue;
+ return this;
+ }
+
+ /**
+ * Configure the validator of the model parameters.
+ *
+ * @param newValidator Parameter validator.
+ * @return this object.
+ *
+ * @since 3.4
+ */
+ public LeastSquaresBuilder parameterValidator(final ParameterValidator newValidator) {
+ paramValidator = newValidator;
+ return this;
+ }
+}
diff --git a/src/main/java/org/apache/commons/math3/fitting/leastsquares/LeastSquaresFactory.java b/src/main/java/org/apache/commons/math3/fitting/leastsquares/LeastSquaresFactory.java
new file mode 100644
index 0000000..42cdf89
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/fitting/leastsquares/LeastSquaresFactory.java
@@ -0,0 +1,532 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math3.fitting.leastsquares;
+
+import org.apache.commons.math3.exception.MathIllegalStateException;
+import org.apache.commons.math3.exception.util.LocalizedFormats;
+import org.apache.commons.math3.analysis.MultivariateMatrixFunction;
+import org.apache.commons.math3.analysis.MultivariateVectorFunction;
+import org.apache.commons.math3.fitting.leastsquares.LeastSquaresProblem.Evaluation;
+import org.apache.commons.math3.linear.Array2DRowRealMatrix;
+import org.apache.commons.math3.linear.ArrayRealVector;
+import org.apache.commons.math3.linear.DiagonalMatrix;
+import org.apache.commons.math3.linear.EigenDecomposition;
+import org.apache.commons.math3.linear.RealMatrix;
+import org.apache.commons.math3.linear.RealVector;
+import org.apache.commons.math3.optim.AbstractOptimizationProblem;
+import org.apache.commons.math3.optim.ConvergenceChecker;
+import org.apache.commons.math3.optim.PointVectorValuePair;
+import org.apache.commons.math3.util.FastMath;
+import org.apache.commons.math3.util.Incrementor;
+import org.apache.commons.math3.util.Pair;
+
+/**
+ * A Factory for creating {@link LeastSquaresProblem}s.
+ *
+ * @since 3.3
+ */
+public class LeastSquaresFactory {
+
+ /** Prevent instantiation. */
+ private LeastSquaresFactory() {}
+
+ /**
+ * Create a {@link org.apache.commons.math3.fitting.leastsquares.LeastSquaresProblem}
+ * from the given elements. There will be no weights applied (unit weights).
+ *
+ * @param model the model function. Produces the computed values.
+ * @param observed the observed (target) values
+ * @param start the initial guess.
+ * @param weight the weight matrix
+ * @param checker convergence checker
+ * @param maxEvaluations the maximum number of times to evaluate the model
+ * @param maxIterations the maximum number to times to iterate in the algorithm
+ * @param lazyEvaluation Whether the call to {@link Evaluation#evaluate(RealVector)}
+ * will defer the evaluation until access to the value is requested.
+ * @param paramValidator Model parameters validator.
+ * @return the specified General Least Squares problem.
+ *
+ * @since 3.4
+ */
+ public static LeastSquaresProblem create(final MultivariateJacobianFunction model,
+ final RealVector observed,
+ final RealVector start,
+ final RealMatrix weight,
+ final ConvergenceChecker<Evaluation> checker,
+ final int maxEvaluations,
+ final int maxIterations,
+ final boolean lazyEvaluation,
+ final ParameterValidator paramValidator) {
+ final LeastSquaresProblem p = new LocalLeastSquaresProblem(model,
+ observed,
+ start,
+ checker,
+ maxEvaluations,
+ maxIterations,
+ lazyEvaluation,
+ paramValidator);
+ if (weight != null) {
+ return weightMatrix(p, weight);
+ } else {
+ return p;
+ }
+ }
+
+ /**
+ * Create a {@link org.apache.commons.math3.fitting.leastsquares.LeastSquaresProblem}
+ * from the given elements. There will be no weights applied (unit weights).
+ *
+ * @param model the model function. Produces the computed values.
+ * @param observed the observed (target) values
+ * @param start the initial guess.
+ * @param checker convergence checker
+ * @param maxEvaluations the maximum number of times to evaluate the model
+ * @param maxIterations the maximum number to times to iterate in the algorithm
+ * @return the specified General Least Squares problem.
+ */
+ public static LeastSquaresProblem create(final MultivariateJacobianFunction model,
+ final RealVector observed,
+ final RealVector start,
+ final ConvergenceChecker<Evaluation> checker,
+ final int maxEvaluations,
+ final int maxIterations) {
+ return create(model,
+ observed,
+ start,
+ null,
+ checker,
+ maxEvaluations,
+ maxIterations,
+ false,
+ null);
+ }
+
+ /**
+ * Create a {@link org.apache.commons.math3.fitting.leastsquares.LeastSquaresProblem}
+ * from the given elements.
+ *
+ * @param model the model function. Produces the computed values.
+ * @param observed the observed (target) values
+ * @param start the initial guess.
+ * @param weight the weight matrix
+ * @param checker convergence checker
+ * @param maxEvaluations the maximum number of times to evaluate the model
+ * @param maxIterations the maximum number to times to iterate in the algorithm
+ * @return the specified General Least Squares problem.
+ */
+ public static LeastSquaresProblem create(final MultivariateJacobianFunction model,
+ final RealVector observed,
+ final RealVector start,
+ final RealMatrix weight,
+ final ConvergenceChecker<Evaluation> checker,
+ final int maxEvaluations,
+ final int maxIterations) {
+ return weightMatrix(create(model,
+ observed,
+ start,
+ checker,
+ maxEvaluations,
+ maxIterations),
+ weight);
+ }
+
+ /**
+ * Create a {@link org.apache.commons.math3.fitting.leastsquares.LeastSquaresProblem}
+ * from the given elements.
+ * <p>
+ * This factory method is provided for continuity with previous interfaces. Newer
+ * applications should use {@link #create(MultivariateJacobianFunction, RealVector,
+ * RealVector, ConvergenceChecker, int, int)}, or {@link #create(MultivariateJacobianFunction,
+ * RealVector, RealVector, RealMatrix, ConvergenceChecker, int, int)}.
+ *
+ * @param model the model function. Produces the computed values.
+ * @param jacobian the jacobian of the model with respect to the parameters
+ * @param observed the observed (target) values
+ * @param start the initial guess.
+ * @param weight the weight matrix
+ * @param checker convergence checker
+ * @param maxEvaluations the maximum number of times to evaluate the model
+ * @param maxIterations the maximum number to times to iterate in the algorithm
+ * @return the specified General Least Squares problem.
+ */
+ public static LeastSquaresProblem create(final MultivariateVectorFunction model,
+ final MultivariateMatrixFunction jacobian,
+ final double[] observed,
+ final double[] start,
+ final RealMatrix weight,
+ final ConvergenceChecker<Evaluation> checker,
+ final int maxEvaluations,
+ final int maxIterations) {
+ return create(model(model, jacobian),
+ new ArrayRealVector(observed, false),
+ new ArrayRealVector(start, false),
+ weight,
+ checker,
+ maxEvaluations,
+ maxIterations);
+ }
+
+ /**
+ * Apply a dense weight matrix to the {@link LeastSquaresProblem}.
+ *
+ * @param problem the unweighted problem
+ * @param weights the matrix of weights
+ * @return a new {@link LeastSquaresProblem} with the weights applied. The original
+ * {@code problem} is not modified.
+ */
+ public static LeastSquaresProblem weightMatrix(final LeastSquaresProblem problem,
+ final RealMatrix weights) {
+ final RealMatrix weightSquareRoot = squareRoot(weights);
+ return new LeastSquaresAdapter(problem) {
+ /** {@inheritDoc} */
+ @Override
+ public Evaluation evaluate(final RealVector point) {
+ return new DenseWeightedEvaluation(super.evaluate(point), weightSquareRoot);
+ }
+ };
+ }
+
+ /**
+ * Apply a diagonal weight matrix to the {@link LeastSquaresProblem}.
+ *
+ * @param problem the unweighted problem
+ * @param weights the diagonal of the weight matrix
+ * @return a new {@link LeastSquaresProblem} with the weights applied. The original
+ * {@code problem} is not modified.
+ */
+ public static LeastSquaresProblem weightDiagonal(final LeastSquaresProblem problem,
+ final RealVector weights) {
+ // TODO more efficient implementation
+ return weightMatrix(problem, new DiagonalMatrix(weights.toArray()));
+ }
+
+ /**
+ * Count the evaluations of a particular problem. The {@code counter} will be
+ * incremented every time {@link LeastSquaresProblem#evaluate(RealVector)} is called on
+ * the <em>returned</em> problem.
+ *
+ * @param problem the problem to track.
+ * @param counter the counter to increment.
+ * @return a least squares problem that tracks evaluations
+ */
+ public static LeastSquaresProblem countEvaluations(final LeastSquaresProblem problem,
+ final Incrementor counter) {
+ return new LeastSquaresAdapter(problem) {
+
+ /** {@inheritDoc} */
+ @Override
+ public Evaluation evaluate(final RealVector point) {
+ counter.incrementCount();
+ return super.evaluate(point);
+ }
+
+ // Delegate the rest.
+ };
+ }
+
+ /**
+ * View a convergence checker specified for a {@link PointVectorValuePair} as one
+ * specified for an {@link Evaluation}.
+ *
+ * @param checker the convergence checker to adapt.
+ * @return a convergence checker that delegates to {@code checker}.
+ */
+ public static ConvergenceChecker<Evaluation> evaluationChecker(final ConvergenceChecker<PointVectorValuePair> checker) {
+ return new ConvergenceChecker<Evaluation>() {
+ /** {@inheritDoc} */
+ public boolean converged(final int iteration,
+ final Evaluation previous,
+ final Evaluation current) {
+ return checker.converged(
+ iteration,
+ new PointVectorValuePair(
+ previous.getPoint().toArray(),
+ previous.getResiduals().toArray(),
+ false),
+ new PointVectorValuePair(
+ current.getPoint().toArray(),
+ current.getResiduals().toArray(),
+ false)
+ );
+ }
+ };
+ }
+
+ /**
+ * Computes the square-root of the weight matrix.
+ *
+ * @param m Symmetric, positive-definite (weight) matrix.
+ * @return the square-root of the weight matrix.
+ */
+ private static RealMatrix squareRoot(final RealMatrix m) {
+ if (m instanceof DiagonalMatrix) {
+ final int dim = m.getRowDimension();
+ final RealMatrix sqrtM = new DiagonalMatrix(dim);
+ for (int i = 0; i < dim; i++) {
+ sqrtM.setEntry(i, i, FastMath.sqrt(m.getEntry(i, i)));
+ }
+ return sqrtM;
+ } else {
+ final EigenDecomposition dec = new EigenDecomposition(m);
+ return dec.getSquareRoot();
+ }
+ }
+
+ /**
+ * Combine a {@link MultivariateVectorFunction} with a {@link
+ * MultivariateMatrixFunction} to produce a {@link MultivariateJacobianFunction}.
+ *
+ * @param value the vector value function
+ * @param jacobian the Jacobian function
+ * @return a function that computes both at the same time
+ */
+ public static MultivariateJacobianFunction model(final MultivariateVectorFunction value,
+ final MultivariateMatrixFunction jacobian) {
+ return new LocalValueAndJacobianFunction(value, jacobian);
+ }
+
+ /**
+ * Combine a {@link MultivariateVectorFunction} with a {@link
+ * MultivariateMatrixFunction} to produce a {@link MultivariateJacobianFunction}.
+ *
+ * @param value the vector value function
+ * @param jacobian the Jacobian function
+ * @return a function that computes both at the same time
+ */
+ private static class LocalValueAndJacobianFunction
+ implements ValueAndJacobianFunction {
+ /** Model. */
+ private final MultivariateVectorFunction value;
+ /** Model's Jacobian. */
+ private final MultivariateMatrixFunction jacobian;
+
+ /**
+ * @param value Model function.
+ * @param jacobian Model's Jacobian function.
+ */
+ LocalValueAndJacobianFunction(final MultivariateVectorFunction value,
+ final MultivariateMatrixFunction jacobian) {
+ this.value = value;
+ this.jacobian = jacobian;
+ }
+
+ /** {@inheritDoc} */
+ public Pair<RealVector, RealMatrix> value(final RealVector point) {
+ //TODO get array from RealVector without copying?
+ final double[] p = point.toArray();
+
+ // Evaluate.
+ return new Pair<RealVector, RealMatrix>(computeValue(p),
+ computeJacobian(p));
+ }
+
+ /** {@inheritDoc} */
+ public RealVector computeValue(final double[] params) {
+ return new ArrayRealVector(value.value(params), false);
+ }
+
+ /** {@inheritDoc} */
+ public RealMatrix computeJacobian(final double[] params) {
+ return new Array2DRowRealMatrix(jacobian.value(params), false);
+ }
+ }
+
+
+ /**
+ * A private, "field" immutable (not "real" immutable) implementation of {@link
+ * LeastSquaresProblem}.
+ * @since 3.3
+ */
+ private static class LocalLeastSquaresProblem
+ extends AbstractOptimizationProblem<Evaluation>
+ implements LeastSquaresProblem {
+
+ /** Target values for the model function at optimum. */
+ private final RealVector target;
+ /** Model function. */
+ private final MultivariateJacobianFunction model;
+ /** Initial guess. */
+ private final RealVector start;
+ /** Whether to use lazy evaluation. */
+ private final boolean lazyEvaluation;
+ /** Model parameters validator. */
+ private final ParameterValidator paramValidator;
+
+ /**
+ * Create a {@link LeastSquaresProblem} from the given data.
+ *
+ * @param model the model function
+ * @param target the observed data
+ * @param start the initial guess
+ * @param checker the convergence checker
+ * @param maxEvaluations the allowed evaluations
+ * @param maxIterations the allowed iterations
+ * @param lazyEvaluation Whether the call to {@link Evaluation#evaluate(RealVector)}
+ * will defer the evaluation until access to the value is requested.
+ * @param paramValidator Model parameters validator.
+ */
+ LocalLeastSquaresProblem(final MultivariateJacobianFunction model,
+ final RealVector target,
+ final RealVector start,
+ final ConvergenceChecker<Evaluation> checker,
+ final int maxEvaluations,
+ final int maxIterations,
+ final boolean lazyEvaluation,
+ final ParameterValidator paramValidator) {
+ super(maxEvaluations, maxIterations, checker);
+ this.target = target;
+ this.model = model;
+ this.start = start;
+ this.lazyEvaluation = lazyEvaluation;
+ this.paramValidator = paramValidator;
+
+ if (lazyEvaluation &&
+ !(model instanceof ValueAndJacobianFunction)) {
+ // Lazy evaluation requires that value and Jacobian
+ // can be computed separately.
+ throw new MathIllegalStateException(LocalizedFormats.INVALID_IMPLEMENTATION,
+ model.getClass().getName());
+ }
+ }
+
+ /** {@inheritDoc} */
+ public int getObservationSize() {
+ return target.getDimension();
+ }
+
+ /** {@inheritDoc} */
+ public int getParameterSize() {
+ return start.getDimension();
+ }
+
+ /** {@inheritDoc} */
+ public RealVector getStart() {
+ return start == null ? null : start.copy();
+ }
+
+ /** {@inheritDoc} */
+ public Evaluation evaluate(final RealVector point) {
+ // Copy so optimizer can change point without changing our instance.
+ final RealVector p = paramValidator == null ?
+ point.copy() :
+ paramValidator.validate(point.copy());
+
+ if (lazyEvaluation) {
+ return new LazyUnweightedEvaluation((ValueAndJacobianFunction) model,
+ target,
+ p);
+ } else {
+ // Evaluate value and jacobian in one function call.
+ final Pair<RealVector, RealMatrix> value = model.value(p);
+ return new UnweightedEvaluation(value.getFirst(),
+ value.getSecond(),
+ target,
+ p);
+ }
+ }
+
+ /**
+ * Container with the model evaluation at a particular point.
+ */
+ private static class UnweightedEvaluation extends AbstractEvaluation {
+ /** Point of evaluation. */
+ private final RealVector point;
+ /** Derivative at point. */
+ private final RealMatrix jacobian;
+ /** Computed residuals. */
+ private final RealVector residuals;
+
+ /**
+ * Create an {@link Evaluation} with no weights.
+ *
+ * @param values the computed function values
+ * @param jacobian the computed function Jacobian
+ * @param target the observed values
+ * @param point the abscissa
+ */
+ private UnweightedEvaluation(final RealVector values,
+ final RealMatrix jacobian,
+ final RealVector target,
+ final RealVector point) {
+ super(target.getDimension());
+ this.jacobian = jacobian;
+ this.point = point;
+ this.residuals = target.subtract(values);
+ }
+
+ /** {@inheritDoc} */
+ public RealMatrix getJacobian() {
+ return jacobian;
+ }
+
+ /** {@inheritDoc} */
+ public RealVector getPoint() {
+ return point;
+ }
+
+ /** {@inheritDoc} */
+ public RealVector getResiduals() {
+ return residuals;
+ }
+ }
+
+ /**
+ * Container with the model <em>lazy</em> evaluation at a particular point.
+ */
+ private static class LazyUnweightedEvaluation extends AbstractEvaluation {
+ /** Point of evaluation. */
+ private final RealVector point;
+ /** Model and Jacobian functions. */
+ private final ValueAndJacobianFunction model;
+ /** Target values for the model function at optimum. */
+ private final RealVector target;
+
+ /**
+ * Create an {@link Evaluation} with no weights.
+ *
+ * @param model the model function
+ * @param target the observed values
+ * @param point the abscissa
+ */
+ private LazyUnweightedEvaluation(final ValueAndJacobianFunction model,
+ final RealVector target,
+ final RealVector point) {
+ super(target.getDimension());
+ // Safe to cast as long as we control usage of this class.
+ this.model = model;
+ this.point = point;
+ this.target = target;
+ }
+
+ /** {@inheritDoc} */
+ public RealMatrix getJacobian() {
+ return model.computeJacobian(point.toArray());
+ }
+
+ /** {@inheritDoc} */
+ public RealVector getPoint() {
+ return point;
+ }
+
+ /** {@inheritDoc} */
+ public RealVector getResiduals() {
+ return target.subtract(model.computeValue(point.toArray()));
+ }
+ }
+ }
+}
+
diff --git a/src/main/java/org/apache/commons/math3/fitting/leastsquares/LeastSquaresOptimizer.java b/src/main/java/org/apache/commons/math3/fitting/leastsquares/LeastSquaresOptimizer.java
new file mode 100644
index 0000000..50d5b8a
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/fitting/leastsquares/LeastSquaresOptimizer.java
@@ -0,0 +1,62 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math3.fitting.leastsquares;
+
+/**
+ * An algorithm that can be applied to a non-linear least squares problem.
+ *
+ * @since 3.3
+ */
+public interface LeastSquaresOptimizer {
+
+ /**
+ * Solve the non-linear least squares problem.
+ *
+ *
+ * @param leastSquaresProblem the problem definition, including model function and
+ * convergence criteria.
+ * @return The optimum.
+ */
+ Optimum optimize(LeastSquaresProblem leastSquaresProblem);
+
+ /**
+ * The optimum found by the optimizer. This object contains the point, its value, and
+ * some metadata.
+ */
+ //TODO Solution?
+ interface Optimum extends LeastSquaresProblem.Evaluation {
+
+ /**
+ * Get the number of times the model was evaluated in order to produce this
+ * optimum.
+ *
+ * @return the number of model (objective) function evaluations
+ */
+ int getEvaluations();
+
+ /**
+ * Get the number of times the algorithm iterated in order to produce this
+ * optimum. In general least squares it is common to have one {@link
+ * #getEvaluations() evaluation} per iterations.
+ *
+ * @return the number of iterations
+ */
+ int getIterations();
+
+ }
+
+}
diff --git a/src/main/java/org/apache/commons/math3/fitting/leastsquares/LeastSquaresProblem.java b/src/main/java/org/apache/commons/math3/fitting/leastsquares/LeastSquaresProblem.java
new file mode 100644
index 0000000..097ff81
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/fitting/leastsquares/LeastSquaresProblem.java
@@ -0,0 +1,156 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math3.fitting.leastsquares;
+
+import org.apache.commons.math3.linear.RealMatrix;
+import org.apache.commons.math3.linear.RealVector;
+import org.apache.commons.math3.optim.OptimizationProblem;
+
+/**
+ * The data necessary to define a non-linear least squares problem.
+ * <p>
+ * Includes the observed values, computed model function, and
+ * convergence/divergence criteria. Weights are implicit in {@link
+ * Evaluation#getResiduals()} and {@link Evaluation#getJacobian()}.
+ * </p>
+ * <p>
+ * Instances are typically either created progressively using a {@link
+ * LeastSquaresBuilder builder} or created at once using a {@link LeastSquaresFactory
+ * factory}.
+ * </p>
+ * @see LeastSquaresBuilder
+ * @see LeastSquaresFactory
+ * @see LeastSquaresAdapter
+ *
+ * @since 3.3
+ */
+public interface LeastSquaresProblem extends OptimizationProblem<LeastSquaresProblem.Evaluation> {
+
+ /**
+ * Gets the initial guess.
+ *
+ * @return the initial guess values.
+ */
+ RealVector getStart();
+
+ /**
+ * Get the number of observations (rows in the Jacobian) in this problem.
+ *
+ * @return the number of scalar observations
+ */
+ int getObservationSize();
+
+ /**
+ * Get the number of parameters (columns in the Jacobian) in this problem.
+ *
+ * @return the number of scalar parameters
+ */
+ int getParameterSize();
+
+ /**
+ * Evaluate the model at the specified point.
+ *
+ *
+ * @param point the parameter values.
+ * @return the model's value and derivative at the given point.
+ * @throws org.apache.commons.math3.exception.TooManyEvaluationsException
+ * if the maximal number of evaluations (of the model vector function) is
+ * exceeded.
+ */
+ Evaluation evaluate(RealVector point);
+
+ /**
+ * An evaluation of a {@link LeastSquaresProblem} at a particular point. This class
+ * also computes several quantities derived from the value and its Jacobian.
+ */
+ public interface Evaluation {
+
+ /**
+ * Get the covariance matrix of the optimized parameters. <br/> Note that this
+ * operation involves the inversion of the <code>J<sup>T</sup>J</code> matrix,
+ * where {@code J} is the Jacobian matrix. The {@code threshold} parameter is a
+ * way for the caller to specify that the result of this computation should be
+ * considered meaningless, and thus trigger an exception.
+ *
+ *
+ * @param threshold Singularity threshold.
+ * @return the covariance matrix.
+ * @throws org.apache.commons.math3.linear.SingularMatrixException
+ * if the covariance matrix cannot be computed (singular problem).
+ */
+ RealMatrix getCovariances(double threshold);
+
+ /**
+ * Get an estimate of the standard deviation of the parameters. The returned
+ * values are the square root of the diagonal coefficients of the covariance
+ * matrix, {@code sd(a[i]) ~= sqrt(C[i][i])}, where {@code a[i]} is the optimized
+ * value of the {@code i}-th parameter, and {@code C} is the covariance matrix.
+ *
+ *
+ * @param covarianceSingularityThreshold Singularity threshold (see {@link
+ * #getCovariances(double) computeCovariances}).
+ * @return an estimate of the standard deviation of the optimized parameters
+ * @throws org.apache.commons.math3.linear.SingularMatrixException
+ * if the covariance matrix cannot be computed.
+ */
+ RealVector getSigma(double covarianceSingularityThreshold);
+
+ /**
+ * Get the normalized cost. It is the square-root of the sum of squared of
+ * the residuals, divided by the number of measurements.
+ *
+ * @return the cost.
+ */
+ double getRMS();
+
+ /**
+ * Get the weighted Jacobian matrix.
+ *
+ * @return the weighted Jacobian: W<sup>1/2</sup> J.
+ * @throws org.apache.commons.math3.exception.DimensionMismatchException
+ * if the Jacobian dimension does not match problem dimension.
+ */
+ RealMatrix getJacobian();
+
+ /**
+ * Get the cost.
+ *
+ * @return the cost.
+ * @see #getResiduals()
+ */
+ double getCost();
+
+ /**
+ * Get the weighted residuals. The residual is the difference between the
+ * observed (target) values and the model (objective function) value. There is one
+ * residual for each element of the vector-valued function. The raw residuals are
+ * then multiplied by the square root of the weight matrix.
+ *
+ * @return the weighted residuals: W<sup>1/2</sup> K.
+ * @throws org.apache.commons.math3.exception.DimensionMismatchException
+ * if the residuals have the wrong length.
+ */
+ RealVector getResiduals();
+
+ /**
+ * Get the abscissa (independent variables) of this evaluation.
+ *
+ * @return the point provided to {@link #evaluate(RealVector)}.
+ */
+ RealVector getPoint();
+ }
+}
diff --git a/src/main/java/org/apache/commons/math3/fitting/leastsquares/LevenbergMarquardtOptimizer.java b/src/main/java/org/apache/commons/math3/fitting/leastsquares/LevenbergMarquardtOptimizer.java
new file mode 100644
index 0000000..358d240
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/fitting/leastsquares/LevenbergMarquardtOptimizer.java
@@ -0,0 +1,1042 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math3.fitting.leastsquares;
+
+import java.util.Arrays;
+
+import org.apache.commons.math3.fitting.leastsquares.LeastSquaresProblem.Evaluation;
+import org.apache.commons.math3.linear.ArrayRealVector;
+import org.apache.commons.math3.linear.RealMatrix;
+import org.apache.commons.math3.exception.ConvergenceException;
+import org.apache.commons.math3.exception.util.LocalizedFormats;
+import org.apache.commons.math3.optim.ConvergenceChecker;
+import org.apache.commons.math3.util.Incrementor;
+import org.apache.commons.math3.util.Precision;
+import org.apache.commons.math3.util.FastMath;
+
+
+/**
+ * This class solves a least-squares problem using the Levenberg-Marquardt
+ * algorithm.
+ *
+ * <p>This implementation <em>should</em> work even for over-determined systems
+ * (i.e. systems having more point than equations). Over-determined systems
+ * are solved by ignoring the point which have the smallest impact according
+ * to their jacobian column norm. Only the rank of the matrix and some loop bounds
+ * are changed to implement this.</p>
+ *
+ * <p>The resolution engine is a simple translation of the MINPACK <a
+ * href="http://www.netlib.org/minpack/lmder.f">lmder</a> routine with minor
+ * changes. The changes include the over-determined resolution, the use of
+ * inherited convergence checker and the Q.R. decomposition which has been
+ * rewritten following the algorithm described in the
+ * P. Lascaux and R. Theodor book <i>Analyse num&eacute;rique matricielle
+ * appliqu&eacute;e &agrave; l'art de l'ing&eacute;nieur</i>, Masson 1986.</p>
+ * <p>The authors of the original fortran version are:
+ * <ul>
+ * <li>Argonne National Laboratory. MINPACK project. March 1980</li>
+ * <li>Burton S. Garbow</li>
+ * <li>Kenneth E. Hillstrom</li>
+ * <li>Jorge J. More</li>
+ * </ul>
+ * The redistribution policy for MINPACK is available <a
+ * href="http://www.netlib.org/minpack/disclaimer">here</a>, for convenience, it
+ * is reproduced below.</p>
+ *
+ * <table border="0" width="80%" cellpadding="10" align="center" bgcolor="#E0E0E0">
+ * <tr><td>
+ * Minpack Copyright Notice (1999) University of Chicago.
+ * All rights reserved
+ * </td></tr>
+ * <tr><td>
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * <ol>
+ * <li>Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.</li>
+ * <li>Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided
+ * with the distribution.</li>
+ * <li>The end-user documentation included with the redistribution, if any,
+ * must include the following acknowledgment:
+ * <code>This product includes software developed by the University of
+ * Chicago, as Operator of Argonne National Laboratory.</code>
+ * Alternately, this acknowledgment may appear in the software itself,
+ * if and wherever such third-party acknowledgments normally appear.</li>
+ * <li><strong>WARRANTY DISCLAIMER. THE SOFTWARE IS SUPPLIED "AS IS"
+ * WITHOUT WARRANTY OF ANY KIND. THE COPYRIGHT HOLDER, THE
+ * UNITED STATES, THE UNITED STATES DEPARTMENT OF ENERGY, AND
+ * THEIR EMPLOYEES: (1) DISCLAIM ANY WARRANTIES, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO ANY IMPLIED WARRANTIES
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, TITLE
+ * OR NON-INFRINGEMENT, (2) DO NOT ASSUME ANY LEGAL LIABILITY
+ * OR RESPONSIBILITY FOR THE ACCURACY, COMPLETENESS, OR
+ * USEFULNESS OF THE SOFTWARE, (3) DO NOT REPRESENT THAT USE OF
+ * THE SOFTWARE WOULD NOT INFRINGE PRIVATELY OWNED RIGHTS, (4)
+ * DO NOT WARRANT THAT THE SOFTWARE WILL FUNCTION
+ * UNINTERRUPTED, THAT IT IS ERROR-FREE OR THAT ANY ERRORS WILL
+ * BE CORRECTED.</strong></li>
+ * <li><strong>LIMITATION OF LIABILITY. IN NO EVENT WILL THE COPYRIGHT
+ * HOLDER, THE UNITED STATES, THE UNITED STATES DEPARTMENT OF
+ * ENERGY, OR THEIR EMPLOYEES: BE LIABLE FOR ANY INDIRECT,
+ * INCIDENTAL, CONSEQUENTIAL, SPECIAL OR PUNITIVE DAMAGES OF
+ * ANY KIND OR NATURE, INCLUDING BUT NOT LIMITED TO LOSS OF
+ * PROFITS OR LOSS OF DATA, FOR ANY REASON WHATSOEVER, WHETHER
+ * SUCH LIABILITY IS ASSERTED ON THE BASIS OF CONTRACT, TORT
+ * (INCLUDING NEGLIGENCE OR STRICT LIABILITY), OR OTHERWISE,
+ * EVEN IF ANY OF SAID PARTIES HAS BEEN WARNED OF THE
+ * POSSIBILITY OF SUCH LOSS OR DAMAGES.</strong></li>
+ * <ol></td></tr>
+ * </table>
+ *
+ * @since 3.3
+ */
+public class LevenbergMarquardtOptimizer implements LeastSquaresOptimizer {
+
+ /** Twice the "epsilon machine". */
+ private static final double TWO_EPS = 2 * Precision.EPSILON;
+
+ /* configuration parameters */
+ /** Positive input variable used in determining the initial step bound. */
+ private final double initialStepBoundFactor;
+ /** Desired relative error in the sum of squares. */
+ private final double costRelativeTolerance;
+ /** Desired relative error in the approximate solution parameters. */
+ private final double parRelativeTolerance;
+ /** Desired max cosine on the orthogonality between the function vector
+ * and the columns of the jacobian. */
+ private final double orthoTolerance;
+ /** Threshold for QR ranking. */
+ private final double qrRankingThreshold;
+
+ /** Default constructor.
+ * <p>
+ * The default values for the algorithm settings are:
+ * <ul>
+ * <li>Initial step bound factor: 100</li>
+ * <li>Cost relative tolerance: 1e-10</li>
+ * <li>Parameters relative tolerance: 1e-10</li>
+ * <li>Orthogonality tolerance: 1e-10</li>
+ * <li>QR ranking threshold: {@link Precision#SAFE_MIN}</li>
+ * </ul>
+ **/
+ public LevenbergMarquardtOptimizer() {
+ this(100, 1e-10, 1e-10, 1e-10, Precision.SAFE_MIN);
+ }
+
+ /**
+ * Construct an instance with all parameters specified.
+ *
+ * @param initialStepBoundFactor initial step bound factor
+ * @param costRelativeTolerance cost relative tolerance
+ * @param parRelativeTolerance parameters relative tolerance
+ * @param orthoTolerance orthogonality tolerance
+ * @param qrRankingThreshold threshold in the QR decomposition. Columns with a 2
+ * norm less than this threshold are considered to be
+ * all 0s.
+ */
+ public LevenbergMarquardtOptimizer(
+ final double initialStepBoundFactor,
+ final double costRelativeTolerance,
+ final double parRelativeTolerance,
+ final double orthoTolerance,
+ final double qrRankingThreshold) {
+ this.initialStepBoundFactor = initialStepBoundFactor;
+ this.costRelativeTolerance = costRelativeTolerance;
+ this.parRelativeTolerance = parRelativeTolerance;
+ this.orthoTolerance = orthoTolerance;
+ this.qrRankingThreshold = qrRankingThreshold;
+ }
+
+ /**
+ * @param newInitialStepBoundFactor Positive input variable used in
+ * determining the initial step bound. This bound is set to the
+ * product of initialStepBoundFactor and the euclidean norm of
+ * {@code diag * x} if non-zero, or else to {@code newInitialStepBoundFactor}
+ * itself. In most cases factor should lie in the interval
+ * {@code (0.1, 100.0)}. {@code 100} is a generally recommended value.
+ * of the matrix is reduced.
+ * @return a new instance.
+ */
+ public LevenbergMarquardtOptimizer withInitialStepBoundFactor(double newInitialStepBoundFactor) {
+ return new LevenbergMarquardtOptimizer(
+ newInitialStepBoundFactor,
+ costRelativeTolerance,
+ parRelativeTolerance,
+ orthoTolerance,
+ qrRankingThreshold);
+ }
+
+ /**
+ * @param newCostRelativeTolerance Desired relative error in the sum of squares.
+ * @return a new instance.
+ */
+ public LevenbergMarquardtOptimizer withCostRelativeTolerance(double newCostRelativeTolerance) {
+ return new LevenbergMarquardtOptimizer(
+ initialStepBoundFactor,
+ newCostRelativeTolerance,
+ parRelativeTolerance,
+ orthoTolerance,
+ qrRankingThreshold);
+ }
+
+ /**
+ * @param newParRelativeTolerance Desired relative error in the approximate solution
+ * parameters.
+ * @return a new instance.
+ */
+ public LevenbergMarquardtOptimizer withParameterRelativeTolerance(double newParRelativeTolerance) {
+ return new LevenbergMarquardtOptimizer(
+ initialStepBoundFactor,
+ costRelativeTolerance,
+ newParRelativeTolerance,
+ orthoTolerance,
+ qrRankingThreshold);
+ }
+
+ /**
+ * Modifies the given parameter.
+ *
+ * @param newOrthoTolerance Desired max cosine on the orthogonality between
+ * the function vector and the columns of the Jacobian.
+ * @return a new instance.
+ */
+ public LevenbergMarquardtOptimizer withOrthoTolerance(double newOrthoTolerance) {
+ return new LevenbergMarquardtOptimizer(
+ initialStepBoundFactor,
+ costRelativeTolerance,
+ parRelativeTolerance,
+ newOrthoTolerance,
+ qrRankingThreshold);
+ }
+
+ /**
+ * @param newQRRankingThreshold Desired threshold for QR ranking.
+ * If the squared norm of a column vector is smaller or equal to this
+ * threshold during QR decomposition, it is considered to be a zero vector
+ * and hence the rank of the matrix is reduced.
+ * @return a new instance.
+ */
+ public LevenbergMarquardtOptimizer withRankingThreshold(double newQRRankingThreshold) {
+ return new LevenbergMarquardtOptimizer(
+ initialStepBoundFactor,
+ costRelativeTolerance,
+ parRelativeTolerance,
+ orthoTolerance,
+ newQRRankingThreshold);
+ }
+
+ /**
+ * Gets the value of a tuning parameter.
+ * @see #withInitialStepBoundFactor(double)
+ *
+ * @return the parameter's value.
+ */
+ public double getInitialStepBoundFactor() {
+ return initialStepBoundFactor;
+ }
+
+ /**
+ * Gets the value of a tuning parameter.
+ * @see #withCostRelativeTolerance(double)
+ *
+ * @return the parameter's value.
+ */
+ public double getCostRelativeTolerance() {
+ return costRelativeTolerance;
+ }
+
+ /**
+ * Gets the value of a tuning parameter.
+ * @see #withParameterRelativeTolerance(double)
+ *
+ * @return the parameter's value.
+ */
+ public double getParameterRelativeTolerance() {
+ return parRelativeTolerance;
+ }
+
+ /**
+ * Gets the value of a tuning parameter.
+ * @see #withOrthoTolerance(double)
+ *
+ * @return the parameter's value.
+ */
+ public double getOrthoTolerance() {
+ return orthoTolerance;
+ }
+
+ /**
+ * Gets the value of a tuning parameter.
+ * @see #withRankingThreshold(double)
+ *
+ * @return the parameter's value.
+ */
+ public double getRankingThreshold() {
+ return qrRankingThreshold;
+ }
+
+ /** {@inheritDoc} */
+ public Optimum optimize(final LeastSquaresProblem problem) {
+ // Pull in relevant data from the problem as locals.
+ final int nR = problem.getObservationSize(); // Number of observed data.
+ final int nC = problem.getParameterSize(); // Number of parameters.
+ // Counters.
+ final Incrementor iterationCounter = problem.getIterationCounter();
+ final Incrementor evaluationCounter = problem.getEvaluationCounter();
+ // Convergence criterion.
+ final ConvergenceChecker<Evaluation> checker = problem.getConvergenceChecker();
+
+ // arrays shared with the other private methods
+ final int solvedCols = FastMath.min(nR, nC);
+ /* Parameters evolution direction associated with lmPar. */
+ double[] lmDir = new double[nC];
+ /* Levenberg-Marquardt parameter. */
+ double lmPar = 0;
+
+ // local point
+ double delta = 0;
+ double xNorm = 0;
+ double[] diag = new double[nC];
+ double[] oldX = new double[nC];
+ double[] oldRes = new double[nR];
+ double[] qtf = new double[nR];
+ double[] work1 = new double[nC];
+ double[] work2 = new double[nC];
+ double[] work3 = new double[nC];
+
+
+ // Evaluate the function at the starting point and calculate its norm.
+ evaluationCounter.incrementCount();
+ //value will be reassigned in the loop
+ Evaluation current = problem.evaluate(problem.getStart());
+ double[] currentResiduals = current.getResiduals().toArray();
+ double currentCost = current.getCost();
+ double[] currentPoint = current.getPoint().toArray();
+
+ // Outer loop.
+ boolean firstIteration = true;
+ while (true) {
+ iterationCounter.incrementCount();
+
+ final Evaluation previous = current;
+
+ // QR decomposition of the jacobian matrix
+ final InternalData internalData
+ = qrDecomposition(current.getJacobian(), solvedCols);
+ final double[][] weightedJacobian = internalData.weightedJacobian;
+ final int[] permutation = internalData.permutation;
+ final double[] diagR = internalData.diagR;
+ final double[] jacNorm = internalData.jacNorm;
+
+ //residuals already have weights applied
+ double[] weightedResidual = currentResiduals;
+ for (int i = 0; i < nR; i++) {
+ qtf[i] = weightedResidual[i];
+ }
+
+ // compute Qt.res
+ qTy(qtf, internalData);
+
+ // now we don't need Q anymore,
+ // so let jacobian contain the R matrix with its diagonal elements
+ for (int k = 0; k < solvedCols; ++k) {
+ int pk = permutation[k];
+ weightedJacobian[k][pk] = diagR[pk];
+ }
+
+ if (firstIteration) {
+ // scale the point according to the norms of the columns
+ // of the initial jacobian
+ xNorm = 0;
+ for (int k = 0; k < nC; ++k) {
+ double dk = jacNorm[k];
+ if (dk == 0) {
+ dk = 1.0;
+ }
+ double xk = dk * currentPoint[k];
+ xNorm += xk * xk;
+ diag[k] = dk;
+ }
+ xNorm = FastMath.sqrt(xNorm);
+
+ // initialize the step bound delta
+ delta = (xNorm == 0) ? initialStepBoundFactor : (initialStepBoundFactor * xNorm);
+ }
+
+ // check orthogonality between function vector and jacobian columns
+ double maxCosine = 0;
+ if (currentCost != 0) {
+ for (int j = 0; j < solvedCols; ++j) {
+ int pj = permutation[j];
+ double s = jacNorm[pj];
+ if (s != 0) {
+ double sum = 0;
+ for (int i = 0; i <= j; ++i) {
+ sum += weightedJacobian[i][pj] * qtf[i];
+ }
+ maxCosine = FastMath.max(maxCosine, FastMath.abs(sum) / (s * currentCost));
+ }
+ }
+ }
+ if (maxCosine <= orthoTolerance) {
+ // Convergence has been reached.
+ return new OptimumImpl(
+ current,
+ evaluationCounter.getCount(),
+ iterationCounter.getCount());
+ }
+
+ // rescale if necessary
+ for (int j = 0; j < nC; ++j) {
+ diag[j] = FastMath.max(diag[j], jacNorm[j]);
+ }
+
+ // Inner loop.
+ for (double ratio = 0; ratio < 1.0e-4;) {
+
+ // save the state
+ for (int j = 0; j < solvedCols; ++j) {
+ int pj = permutation[j];
+ oldX[pj] = currentPoint[pj];
+ }
+ final double previousCost = currentCost;
+ double[] tmpVec = weightedResidual;
+ weightedResidual = oldRes;
+ oldRes = tmpVec;
+
+ // determine the Levenberg-Marquardt parameter
+ lmPar = determineLMParameter(qtf, delta, diag,
+ internalData, solvedCols,
+ work1, work2, work3, lmDir, lmPar);
+
+ // compute the new point and the norm of the evolution direction
+ double lmNorm = 0;
+ for (int j = 0; j < solvedCols; ++j) {
+ int pj = permutation[j];
+ lmDir[pj] = -lmDir[pj];
+ currentPoint[pj] = oldX[pj] + lmDir[pj];
+ double s = diag[pj] * lmDir[pj];
+ lmNorm += s * s;
+ }
+ lmNorm = FastMath.sqrt(lmNorm);
+ // on the first iteration, adjust the initial step bound.
+ if (firstIteration) {
+ delta = FastMath.min(delta, lmNorm);
+ }
+
+ // Evaluate the function at x + p and calculate its norm.
+ evaluationCounter.incrementCount();
+ current = problem.evaluate(new ArrayRealVector(currentPoint));
+ currentResiduals = current.getResiduals().toArray();
+ currentCost = current.getCost();
+ currentPoint = current.getPoint().toArray();
+
+ // compute the scaled actual reduction
+ double actRed = -1.0;
+ if (0.1 * currentCost < previousCost) {
+ double r = currentCost / previousCost;
+ actRed = 1.0 - r * r;
+ }
+
+ // compute the scaled predicted reduction
+ // and the scaled directional derivative
+ for (int j = 0; j < solvedCols; ++j) {
+ int pj = permutation[j];
+ double dirJ = lmDir[pj];
+ work1[j] = 0;
+ for (int i = 0; i <= j; ++i) {
+ work1[i] += weightedJacobian[i][pj] * dirJ;
+ }
+ }
+ double coeff1 = 0;
+ for (int j = 0; j < solvedCols; ++j) {
+ coeff1 += work1[j] * work1[j];
+ }
+ double pc2 = previousCost * previousCost;
+ coeff1 /= pc2;
+ double coeff2 = lmPar * lmNorm * lmNorm / pc2;
+ double preRed = coeff1 + 2 * coeff2;
+ double dirDer = -(coeff1 + coeff2);
+
+ // ratio of the actual to the predicted reduction
+ ratio = (preRed == 0) ? 0 : (actRed / preRed);
+
+ // update the step bound
+ if (ratio <= 0.25) {
+ double tmp =
+ (actRed < 0) ? (0.5 * dirDer / (dirDer + 0.5 * actRed)) : 0.5;
+ if ((0.1 * currentCost >= previousCost) || (tmp < 0.1)) {
+ tmp = 0.1;
+ }
+ delta = tmp * FastMath.min(delta, 10.0 * lmNorm);
+ lmPar /= tmp;
+ } else if ((lmPar == 0) || (ratio >= 0.75)) {
+ delta = 2 * lmNorm;
+ lmPar *= 0.5;
+ }
+
+ // test for successful iteration.
+ if (ratio >= 1.0e-4) {
+ // successful iteration, update the norm
+ firstIteration = false;
+ xNorm = 0;
+ for (int k = 0; k < nC; ++k) {
+ double xK = diag[k] * currentPoint[k];
+ xNorm += xK * xK;
+ }
+ xNorm = FastMath.sqrt(xNorm);
+
+ // tests for convergence.
+ if (checker != null && checker.converged(iterationCounter.getCount(), previous, current)) {
+ return new OptimumImpl(current, evaluationCounter.getCount(), iterationCounter.getCount());
+ }
+ } else {
+ // failed iteration, reset the previous values
+ currentCost = previousCost;
+ for (int j = 0; j < solvedCols; ++j) {
+ int pj = permutation[j];
+ currentPoint[pj] = oldX[pj];
+ }
+ tmpVec = weightedResidual;
+ weightedResidual = oldRes;
+ oldRes = tmpVec;
+ // Reset "current" to previous values.
+ current = previous;
+ }
+
+ // Default convergence criteria.
+ if ((FastMath.abs(actRed) <= costRelativeTolerance &&
+ preRed <= costRelativeTolerance &&
+ ratio <= 2.0) ||
+ delta <= parRelativeTolerance * xNorm) {
+ return new OptimumImpl(current, evaluationCounter.getCount(), iterationCounter.getCount());
+ }
+
+ // tests for termination and stringent tolerances
+ if (FastMath.abs(actRed) <= TWO_EPS &&
+ preRed <= TWO_EPS &&
+ ratio <= 2.0) {
+ throw new ConvergenceException(LocalizedFormats.TOO_SMALL_COST_RELATIVE_TOLERANCE,
+ costRelativeTolerance);
+ } else if (delta <= TWO_EPS * xNorm) {
+ throw new ConvergenceException(LocalizedFormats.TOO_SMALL_PARAMETERS_RELATIVE_TOLERANCE,
+ parRelativeTolerance);
+ } else if (maxCosine <= TWO_EPS) {
+ throw new ConvergenceException(LocalizedFormats.TOO_SMALL_ORTHOGONALITY_TOLERANCE,
+ orthoTolerance);
+ }
+ }
+ }
+ }
+
+ /**
+ * Holds internal data.
+ * This structure was created so that all optimizer fields can be "final".
+ * Code should be further refactored in order to not pass around arguments
+ * that will modified in-place (cf. "work" arrays).
+ */
+ private static class InternalData {
+ /** Weighted Jacobian. */
+ private final double[][] weightedJacobian;
+ /** Columns permutation array. */
+ private final int[] permutation;
+ /** Rank of the Jacobian matrix. */
+ private final int rank;
+ /** Diagonal elements of the R matrix in the QR decomposition. */
+ private final double[] diagR;
+ /** Norms of the columns of the jacobian matrix. */
+ private final double[] jacNorm;
+ /** Coefficients of the Householder transforms vectors. */
+ private final double[] beta;
+
+ /**
+ * @param weightedJacobian Weighted Jacobian.
+ * @param permutation Columns permutation array.
+ * @param rank Rank of the Jacobian matrix.
+ * @param diagR Diagonal elements of the R matrix in the QR decomposition.
+ * @param jacNorm Norms of the columns of the jacobian matrix.
+ * @param beta Coefficients of the Householder transforms vectors.
+ */
+ InternalData(double[][] weightedJacobian,
+ int[] permutation,
+ int rank,
+ double[] diagR,
+ double[] jacNorm,
+ double[] beta) {
+ this.weightedJacobian = weightedJacobian;
+ this.permutation = permutation;
+ this.rank = rank;
+ this.diagR = diagR;
+ this.jacNorm = jacNorm;
+ this.beta = beta;
+ }
+ }
+
+ /**
+ * Determines the Levenberg-Marquardt parameter.
+ *
+ * <p>This implementation is a translation in Java of the MINPACK
+ * <a href="http://www.netlib.org/minpack/lmpar.f">lmpar</a>
+ * routine.</p>
+ * <p>This method sets the lmPar and lmDir attributes.</p>
+ * <p>The authors of the original fortran function are:</p>
+ * <ul>
+ * <li>Argonne National Laboratory. MINPACK project. March 1980</li>
+ * <li>Burton S. Garbow</li>
+ * <li>Kenneth E. Hillstrom</li>
+ * <li>Jorge J. More</li>
+ * </ul>
+ * <p>Luc Maisonobe did the Java translation.</p>
+ *
+ * @param qy Array containing qTy.
+ * @param delta Upper bound on the euclidean norm of diagR * lmDir.
+ * @param diag Diagonal matrix.
+ * @param internalData Data (modified in-place in this method).
+ * @param solvedCols Number of solved point.
+ * @param work1 work array
+ * @param work2 work array
+ * @param work3 work array
+ * @param lmDir the "returned" LM direction will be stored in this array.
+ * @param lmPar the value of the LM parameter from the previous iteration.
+ * @return the new LM parameter
+ */
+ private double determineLMParameter(double[] qy, double delta, double[] diag,
+ InternalData internalData, int solvedCols,
+ double[] work1, double[] work2, double[] work3,
+ double[] lmDir, double lmPar) {
+ final double[][] weightedJacobian = internalData.weightedJacobian;
+ final int[] permutation = internalData.permutation;
+ final int rank = internalData.rank;
+ final double[] diagR = internalData.diagR;
+
+ final int nC = weightedJacobian[0].length;
+
+ // compute and store in x the gauss-newton direction, if the
+ // jacobian is rank-deficient, obtain a least squares solution
+ for (int j = 0; j < rank; ++j) {
+ lmDir[permutation[j]] = qy[j];
+ }
+ for (int j = rank; j < nC; ++j) {
+ lmDir[permutation[j]] = 0;
+ }
+ for (int k = rank - 1; k >= 0; --k) {
+ int pk = permutation[k];
+ double ypk = lmDir[pk] / diagR[pk];
+ for (int i = 0; i < k; ++i) {
+ lmDir[permutation[i]] -= ypk * weightedJacobian[i][pk];
+ }
+ lmDir[pk] = ypk;
+ }
+
+ // evaluate the function at the origin, and test
+ // for acceptance of the Gauss-Newton direction
+ double dxNorm = 0;
+ for (int j = 0; j < solvedCols; ++j) {
+ int pj = permutation[j];
+ double s = diag[pj] * lmDir[pj];
+ work1[pj] = s;
+ dxNorm += s * s;
+ }
+ dxNorm = FastMath.sqrt(dxNorm);
+ double fp = dxNorm - delta;
+ if (fp <= 0.1 * delta) {
+ lmPar = 0;
+ return lmPar;
+ }
+
+ // if the jacobian is not rank deficient, the Newton step provides
+ // a lower bound, parl, for the zero of the function,
+ // otherwise set this bound to zero
+ double sum2;
+ double parl = 0;
+ if (rank == solvedCols) {
+ for (int j = 0; j < solvedCols; ++j) {
+ int pj = permutation[j];
+ work1[pj] *= diag[pj] / dxNorm;
+ }
+ sum2 = 0;
+ for (int j = 0; j < solvedCols; ++j) {
+ int pj = permutation[j];
+ double sum = 0;
+ for (int i = 0; i < j; ++i) {
+ sum += weightedJacobian[i][pj] * work1[permutation[i]];
+ }
+ double s = (work1[pj] - sum) / diagR[pj];
+ work1[pj] = s;
+ sum2 += s * s;
+ }
+ parl = fp / (delta * sum2);
+ }
+
+ // calculate an upper bound, paru, for the zero of the function
+ sum2 = 0;
+ for (int j = 0; j < solvedCols; ++j) {
+ int pj = permutation[j];
+ double sum = 0;
+ for (int i = 0; i <= j; ++i) {
+ sum += weightedJacobian[i][pj] * qy[i];
+ }
+ sum /= diag[pj];
+ sum2 += sum * sum;
+ }
+ double gNorm = FastMath.sqrt(sum2);
+ double paru = gNorm / delta;
+ if (paru == 0) {
+ paru = Precision.SAFE_MIN / FastMath.min(delta, 0.1);
+ }
+
+ // if the input par lies outside of the interval (parl,paru),
+ // set par to the closer endpoint
+ lmPar = FastMath.min(paru, FastMath.max(lmPar, parl));
+ if (lmPar == 0) {
+ lmPar = gNorm / dxNorm;
+ }
+
+ for (int countdown = 10; countdown >= 0; --countdown) {
+
+ // evaluate the function at the current value of lmPar
+ if (lmPar == 0) {
+ lmPar = FastMath.max(Precision.SAFE_MIN, 0.001 * paru);
+ }
+ double sPar = FastMath.sqrt(lmPar);
+ for (int j = 0; j < solvedCols; ++j) {
+ int pj = permutation[j];
+ work1[pj] = sPar * diag[pj];
+ }
+ determineLMDirection(qy, work1, work2, internalData, solvedCols, work3, lmDir);
+
+ dxNorm = 0;
+ for (int j = 0; j < solvedCols; ++j) {
+ int pj = permutation[j];
+ double s = diag[pj] * lmDir[pj];
+ work3[pj] = s;
+ dxNorm += s * s;
+ }
+ dxNorm = FastMath.sqrt(dxNorm);
+ double previousFP = fp;
+ fp = dxNorm - delta;
+
+ // if the function is small enough, accept the current value
+ // of lmPar, also test for the exceptional cases where parl is zero
+ if (FastMath.abs(fp) <= 0.1 * delta ||
+ (parl == 0 &&
+ fp <= previousFP &&
+ previousFP < 0)) {
+ return lmPar;
+ }
+
+ // compute the Newton correction
+ for (int j = 0; j < solvedCols; ++j) {
+ int pj = permutation[j];
+ work1[pj] = work3[pj] * diag[pj] / dxNorm;
+ }
+ for (int j = 0; j < solvedCols; ++j) {
+ int pj = permutation[j];
+ work1[pj] /= work2[j];
+ double tmp = work1[pj];
+ for (int i = j + 1; i < solvedCols; ++i) {
+ work1[permutation[i]] -= weightedJacobian[i][pj] * tmp;
+ }
+ }
+ sum2 = 0;
+ for (int j = 0; j < solvedCols; ++j) {
+ double s = work1[permutation[j]];
+ sum2 += s * s;
+ }
+ double correction = fp / (delta * sum2);
+
+ // depending on the sign of the function, update parl or paru.
+ if (fp > 0) {
+ parl = FastMath.max(parl, lmPar);
+ } else if (fp < 0) {
+ paru = FastMath.min(paru, lmPar);
+ }
+
+ // compute an improved estimate for lmPar
+ lmPar = FastMath.max(parl, lmPar + correction);
+ }
+
+ return lmPar;
+ }
+
+ /**
+ * Solve a*x = b and d*x = 0 in the least squares sense.
+ * <p>This implementation is a translation in Java of the MINPACK
+ * <a href="http://www.netlib.org/minpack/qrsolv.f">qrsolv</a>
+ * routine.</p>
+ * <p>This method sets the lmDir and lmDiag attributes.</p>
+ * <p>The authors of the original fortran function are:</p>
+ * <ul>
+ * <li>Argonne National Laboratory. MINPACK project. March 1980</li>
+ * <li>Burton S. Garbow</li>
+ * <li>Kenneth E. Hillstrom</li>
+ * <li>Jorge J. More</li>
+ * </ul>
+ * <p>Luc Maisonobe did the Java translation.</p>
+ *
+ * @param qy array containing qTy
+ * @param diag diagonal matrix
+ * @param lmDiag diagonal elements associated with lmDir
+ * @param internalData Data (modified in-place in this method).
+ * @param solvedCols Number of sloved point.
+ * @param work work array
+ * @param lmDir the "returned" LM direction is stored in this array
+ */
+ private void determineLMDirection(double[] qy, double[] diag,
+ double[] lmDiag,
+ InternalData internalData,
+ int solvedCols,
+ double[] work,
+ double[] lmDir) {
+ final int[] permutation = internalData.permutation;
+ final double[][] weightedJacobian = internalData.weightedJacobian;
+ final double[] diagR = internalData.diagR;
+
+ // copy R and Qty to preserve input and initialize s
+ // in particular, save the diagonal elements of R in lmDir
+ for (int j = 0; j < solvedCols; ++j) {
+ int pj = permutation[j];
+ for (int i = j + 1; i < solvedCols; ++i) {
+ weightedJacobian[i][pj] = weightedJacobian[j][permutation[i]];
+ }
+ lmDir[j] = diagR[pj];
+ work[j] = qy[j];
+ }
+
+ // eliminate the diagonal matrix d using a Givens rotation
+ for (int j = 0; j < solvedCols; ++j) {
+
+ // prepare the row of d to be eliminated, locating the
+ // diagonal element using p from the Q.R. factorization
+ int pj = permutation[j];
+ double dpj = diag[pj];
+ if (dpj != 0) {
+ Arrays.fill(lmDiag, j + 1, lmDiag.length, 0);
+ }
+ lmDiag[j] = dpj;
+
+ // the transformations to eliminate the row of d
+ // modify only a single element of Qty
+ // beyond the first n, which is initially zero.
+ double qtbpj = 0;
+ for (int k = j; k < solvedCols; ++k) {
+ int pk = permutation[k];
+
+ // determine a Givens rotation which eliminates the
+ // appropriate element in the current row of d
+ if (lmDiag[k] != 0) {
+
+ final double sin;
+ final double cos;
+ double rkk = weightedJacobian[k][pk];
+ if (FastMath.abs(rkk) < FastMath.abs(lmDiag[k])) {
+ final double cotan = rkk / lmDiag[k];
+ sin = 1.0 / FastMath.sqrt(1.0 + cotan * cotan);
+ cos = sin * cotan;
+ } else {
+ final double tan = lmDiag[k] / rkk;
+ cos = 1.0 / FastMath.sqrt(1.0 + tan * tan);
+ sin = cos * tan;
+ }
+
+ // compute the modified diagonal element of R and
+ // the modified element of (Qty,0)
+ weightedJacobian[k][pk] = cos * rkk + sin * lmDiag[k];
+ final double temp = cos * work[k] + sin * qtbpj;
+ qtbpj = -sin * work[k] + cos * qtbpj;
+ work[k] = temp;
+
+ // accumulate the tranformation in the row of s
+ for (int i = k + 1; i < solvedCols; ++i) {
+ double rik = weightedJacobian[i][pk];
+ final double temp2 = cos * rik + sin * lmDiag[i];
+ lmDiag[i] = -sin * rik + cos * lmDiag[i];
+ weightedJacobian[i][pk] = temp2;
+ }
+ }
+ }
+
+ // store the diagonal element of s and restore
+ // the corresponding diagonal element of R
+ lmDiag[j] = weightedJacobian[j][permutation[j]];
+ weightedJacobian[j][permutation[j]] = lmDir[j];
+ }
+
+ // solve the triangular system for z, if the system is
+ // singular, then obtain a least squares solution
+ int nSing = solvedCols;
+ for (int j = 0; j < solvedCols; ++j) {
+ if ((lmDiag[j] == 0) && (nSing == solvedCols)) {
+ nSing = j;
+ }
+ if (nSing < solvedCols) {
+ work[j] = 0;
+ }
+ }
+ if (nSing > 0) {
+ for (int j = nSing - 1; j >= 0; --j) {
+ int pj = permutation[j];
+ double sum = 0;
+ for (int i = j + 1; i < nSing; ++i) {
+ sum += weightedJacobian[i][pj] * work[i];
+ }
+ work[j] = (work[j] - sum) / lmDiag[j];
+ }
+ }
+
+ // permute the components of z back to components of lmDir
+ for (int j = 0; j < lmDir.length; ++j) {
+ lmDir[permutation[j]] = work[j];
+ }
+ }
+
+ /**
+ * Decompose a matrix A as A.P = Q.R using Householder transforms.
+ * <p>As suggested in the P. Lascaux and R. Theodor book
+ * <i>Analyse num&eacute;rique matricielle appliqu&eacute;e &agrave;
+ * l'art de l'ing&eacute;nieur</i> (Masson, 1986), instead of representing
+ * the Householder transforms with u<sub>k</sub> unit vectors such that:
+ * <pre>
+ * H<sub>k</sub> = I - 2u<sub>k</sub>.u<sub>k</sub><sup>t</sup>
+ * </pre>
+ * we use <sub>k</sub> non-unit vectors such that:
+ * <pre>
+ * H<sub>k</sub> = I - beta<sub>k</sub>v<sub>k</sub>.v<sub>k</sub><sup>t</sup>
+ * </pre>
+ * where v<sub>k</sub> = a<sub>k</sub> - alpha<sub>k</sub> e<sub>k</sub>.
+ * The beta<sub>k</sub> coefficients are provided upon exit as recomputing
+ * them from the v<sub>k</sub> vectors would be costly.</p>
+ * <p>This decomposition handles rank deficient cases since the tranformations
+ * are performed in non-increasing columns norms order thanks to columns
+ * pivoting. The diagonal elements of the R matrix are therefore also in
+ * non-increasing absolute values order.</p>
+ *
+ * @param jacobian Weighted Jacobian matrix at the current point.
+ * @param solvedCols Number of solved point.
+ * @return data used in other methods of this class.
+ * @throws ConvergenceException if the decomposition cannot be performed.
+ */
+ private InternalData qrDecomposition(RealMatrix jacobian,
+ int solvedCols) throws ConvergenceException {
+ // Code in this class assumes that the weighted Jacobian is -(W^(1/2) J),
+ // hence the multiplication by -1.
+ final double[][] weightedJacobian = jacobian.scalarMultiply(-1).getData();
+
+ final int nR = weightedJacobian.length;
+ final int nC = weightedJacobian[0].length;
+
+ final int[] permutation = new int[nC];
+ final double[] diagR = new double[nC];
+ final double[] jacNorm = new double[nC];
+ final double[] beta = new double[nC];
+
+ // initializations
+ for (int k = 0; k < nC; ++k) {
+ permutation[k] = k;
+ double norm2 = 0;
+ for (int i = 0; i < nR; ++i) {
+ double akk = weightedJacobian[i][k];
+ norm2 += akk * akk;
+ }
+ jacNorm[k] = FastMath.sqrt(norm2);
+ }
+
+ // transform the matrix column after column
+ for (int k = 0; k < nC; ++k) {
+
+ // select the column with the greatest norm on active components
+ int nextColumn = -1;
+ double ak2 = Double.NEGATIVE_INFINITY;
+ for (int i = k; i < nC; ++i) {
+ double norm2 = 0;
+ for (int j = k; j < nR; ++j) {
+ double aki = weightedJacobian[j][permutation[i]];
+ norm2 += aki * aki;
+ }
+ if (Double.isInfinite(norm2) || Double.isNaN(norm2)) {
+ throw new ConvergenceException(LocalizedFormats.UNABLE_TO_PERFORM_QR_DECOMPOSITION_ON_JACOBIAN,
+ nR, nC);
+ }
+ if (norm2 > ak2) {
+ nextColumn = i;
+ ak2 = norm2;
+ }
+ }
+ if (ak2 <= qrRankingThreshold) {
+ return new InternalData(weightedJacobian, permutation, k, diagR, jacNorm, beta);
+ }
+ int pk = permutation[nextColumn];
+ permutation[nextColumn] = permutation[k];
+ permutation[k] = pk;
+
+ // choose alpha such that Hk.u = alpha ek
+ double akk = weightedJacobian[k][pk];
+ double alpha = (akk > 0) ? -FastMath.sqrt(ak2) : FastMath.sqrt(ak2);
+ double betak = 1.0 / (ak2 - akk * alpha);
+ beta[pk] = betak;
+
+ // transform the current column
+ diagR[pk] = alpha;
+ weightedJacobian[k][pk] -= alpha;
+
+ // transform the remaining columns
+ for (int dk = nC - 1 - k; dk > 0; --dk) {
+ double gamma = 0;
+ for (int j = k; j < nR; ++j) {
+ gamma += weightedJacobian[j][pk] * weightedJacobian[j][permutation[k + dk]];
+ }
+ gamma *= betak;
+ for (int j = k; j < nR; ++j) {
+ weightedJacobian[j][permutation[k + dk]] -= gamma * weightedJacobian[j][pk];
+ }
+ }
+ }
+
+ return new InternalData(weightedJacobian, permutation, solvedCols, diagR, jacNorm, beta);
+ }
+
+ /**
+ * Compute the product Qt.y for some Q.R. decomposition.
+ *
+ * @param y vector to multiply (will be overwritten with the result)
+ * @param internalData Data.
+ */
+ private void qTy(double[] y,
+ InternalData internalData) {
+ final double[][] weightedJacobian = internalData.weightedJacobian;
+ final int[] permutation = internalData.permutation;
+ final double[] beta = internalData.beta;
+
+ final int nR = weightedJacobian.length;
+ final int nC = weightedJacobian[0].length;
+
+ for (int k = 0; k < nC; ++k) {
+ int pk = permutation[k];
+ double gamma = 0;
+ for (int i = k; i < nR; ++i) {
+ gamma += weightedJacobian[i][pk] * y[i];
+ }
+ gamma *= beta[pk];
+ for (int i = k; i < nR; ++i) {
+ y[i] -= gamma * weightedJacobian[i][pk];
+ }
+ }
+ }
+}
diff --git a/src/main/java/org/apache/commons/math3/fitting/leastsquares/MultivariateJacobianFunction.java b/src/main/java/org/apache/commons/math3/fitting/leastsquares/MultivariateJacobianFunction.java
new file mode 100644
index 0000000..e673855
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/fitting/leastsquares/MultivariateJacobianFunction.java
@@ -0,0 +1,39 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math3.fitting.leastsquares;
+
+import org.apache.commons.math3.linear.RealMatrix;
+import org.apache.commons.math3.linear.RealVector;
+import org.apache.commons.math3.util.Pair;
+
+/**
+ * A interface for functions that compute a vector of values and can compute their
+ * derivatives (Jacobian).
+ *
+ * @since 3.3
+ */
+public interface MultivariateJacobianFunction {
+
+ /**
+ * Compute the function value and its Jacobian.
+ *
+ * @param point the abscissae
+ * @return the values and their Jacobian of this vector valued function.
+ */
+ Pair<RealVector, RealMatrix> value(RealVector point);
+
+}
diff --git a/src/main/java/org/apache/commons/math3/fitting/leastsquares/OptimumImpl.java b/src/main/java/org/apache/commons/math3/fitting/leastsquares/OptimumImpl.java
new file mode 100644
index 0000000..698f86c
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/fitting/leastsquares/OptimumImpl.java
@@ -0,0 +1,97 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math3.fitting.leastsquares;
+
+import org.apache.commons.math3.fitting.leastsquares.LeastSquaresOptimizer.Optimum;
+import org.apache.commons.math3.fitting.leastsquares.LeastSquaresProblem.Evaluation;
+import org.apache.commons.math3.linear.RealMatrix;
+import org.apache.commons.math3.linear.RealVector;
+
+/**
+ * A pedantic implementation of {@link Optimum}.
+ *
+ * @since 3.3
+ */
+class OptimumImpl implements Optimum {
+
+ /** abscissa and ordinate */
+ private final Evaluation value;
+ /** number of evaluations to compute this optimum */
+ private final int evaluations;
+ /** number of iterations to compute this optimum */
+ private final int iterations;
+
+ /**
+ * Construct an optimum from an evaluation and the values of the counters.
+ *
+ * @param value the function value
+ * @param evaluations number of times the function was evaluated
+ * @param iterations number of iterations of the algorithm
+ */
+ OptimumImpl(final Evaluation value, final int evaluations, final int iterations) {
+ this.value = value;
+ this.evaluations = evaluations;
+ this.iterations = iterations;
+ }
+
+ /* auto-generated implementations */
+
+ /** {@inheritDoc} */
+ public int getEvaluations() {
+ return evaluations;
+ }
+
+ /** {@inheritDoc} */
+ public int getIterations() {
+ return iterations;
+ }
+
+ /** {@inheritDoc} */
+ public RealMatrix getCovariances(double threshold) {
+ return value.getCovariances(threshold);
+ }
+
+ /** {@inheritDoc} */
+ public RealVector getSigma(double covarianceSingularityThreshold) {
+ return value.getSigma(covarianceSingularityThreshold);
+ }
+
+ /** {@inheritDoc} */
+ public double getRMS() {
+ return value.getRMS();
+ }
+
+ /** {@inheritDoc} */
+ public RealMatrix getJacobian() {
+ return value.getJacobian();
+ }
+
+ /** {@inheritDoc} */
+ public double getCost() {
+ return value.getCost();
+ }
+
+ /** {@inheritDoc} */
+ public RealVector getResiduals() {
+ return value.getResiduals();
+ }
+
+ /** {@inheritDoc} */
+ public RealVector getPoint() {
+ return value.getPoint();
+ }
+}
diff --git a/src/main/java/org/apache/commons/math3/fitting/leastsquares/ParameterValidator.java b/src/main/java/org/apache/commons/math3/fitting/leastsquares/ParameterValidator.java
new file mode 100644
index 0000000..d5b8529
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/fitting/leastsquares/ParameterValidator.java
@@ -0,0 +1,34 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math3.fitting.leastsquares;
+
+import org.apache.commons.math3.linear.RealVector;
+
+/**
+ * Interface for validating a set of model parameters.
+ *
+ * @since 3.4
+ */
+public interface ParameterValidator {
+ /**
+ * Validates the set of parameters.
+ *
+ * @param params Input parameters.
+ * @return the validated values.
+ */
+ RealVector validate(RealVector params);
+}
diff --git a/src/main/java/org/apache/commons/math3/fitting/leastsquares/ValueAndJacobianFunction.java b/src/main/java/org/apache/commons/math3/fitting/leastsquares/ValueAndJacobianFunction.java
new file mode 100644
index 0000000..180e328
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/fitting/leastsquares/ValueAndJacobianFunction.java
@@ -0,0 +1,44 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math3.fitting.leastsquares;
+
+import org.apache.commons.math3.linear.RealMatrix;
+import org.apache.commons.math3.linear.RealVector;
+
+/**
+ * A interface for functions that compute a vector of values and can compute their
+ * derivatives (Jacobian).
+ *
+ * @since 3.4
+ */
+public interface ValueAndJacobianFunction extends MultivariateJacobianFunction {
+ /**
+ * Compute the value.
+ *
+ * @param params Point.
+ * @return the value at the given point.
+ */
+ RealVector computeValue(final double[] params);
+
+ /**
+ * Compute the Jacobian.
+ *
+ * @param params Point.
+ * @return the Jacobian at the given point.
+ */
+ RealMatrix computeJacobian(final double[] params);
+}
diff --git a/src/main/java/org/apache/commons/math3/fitting/leastsquares/package-info.java b/src/main/java/org/apache/commons/math3/fitting/leastsquares/package-info.java
new file mode 100644
index 0000000..98623b5
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/fitting/leastsquares/package-info.java
@@ -0,0 +1,39 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * This package provides algorithms that minimize the residuals
+ * between observations and model values.
+ * The {@link org.apache.commons.math3.fitting.leastsquares.LeastSquaresOptimizer
+ * least-squares optimizers} minimize the distance (called
+ * <em>cost</em> or <em>&chi;<sup>2</sup></em>) between model and
+ * observations.
+ *
+ * <br/>
+ * Algorithms in this category need access to a <em>problem</em>
+ * (represented by a {@link org.apache.commons.math3.fitting.leastsquares.LeastSquaresProblem
+ * LeastSquaresProblem}).
+ * Such a model predicts a set of values which the algorithm tries to match
+ * with a set of given set of observed values.
+ * <br/>
+ * The problem can be created progressively using a {@link
+ * org.apache.commons.math3.fitting.leastsquares.LeastSquaresBuilder builder} or it can
+ * be created at once using a {@link org.apache.commons.math3.fitting.leastsquares.LeastSquaresFactory
+ * factory}.
+ * @since 3.3
+ */
+package org.apache.commons.math3.fitting.leastsquares;
diff --git a/src/main/java/org/apache/commons/math3/fitting/package-info.java b/src/main/java/org/apache/commons/math3/fitting/package-info.java
new file mode 100644
index 0000000..af00a6a
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/fitting/package-info.java
@@ -0,0 +1,25 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/**
+ * Classes to perform curve fitting.
+ *
+ * <p>Curve fitting is a special case of a least-squares problem where the parameters are the
+ * coefficients of a function \( f \) whose graph \( y = f(x) \) should pass through sample points,
+ * and were the objective function is the squared sum of the residuals \( f(x_i) - y_i \) for
+ * observed points \( (x_i, y_i) \).
+ */
+package org.apache.commons.math3.fitting;
diff --git a/src/main/java/org/apache/commons/math3/fraction/AbstractFormat.java b/src/main/java/org/apache/commons/math3/fraction/AbstractFormat.java
new file mode 100644
index 0000000..c17d127
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/fraction/AbstractFormat.java
@@ -0,0 +1,216 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math3.fraction;
+
+import org.apache.commons.math3.exception.NullArgumentException;
+import org.apache.commons.math3.exception.util.LocalizedFormats;
+
+import java.io.Serializable;
+import java.text.FieldPosition;
+import java.text.NumberFormat;
+import java.text.ParsePosition;
+import java.util.Locale;
+
+/**
+ * Common part shared by both {@link FractionFormat} and {@link BigFractionFormat}.
+ *
+ * @since 2.0
+ */
+public abstract class AbstractFormat extends NumberFormat implements Serializable {
+
+ /** Serializable version identifier. */
+ private static final long serialVersionUID = -6981118387974191891L;
+
+ /** The format used for the denominator. */
+ private NumberFormat denominatorFormat;
+
+ /** The format used for the numerator. */
+ private NumberFormat numeratorFormat;
+
+ /**
+ * Create an improper formatting instance with the default number format for the numerator and
+ * denominator.
+ */
+ protected AbstractFormat() {
+ this(getDefaultNumberFormat());
+ }
+
+ /**
+ * Create an improper formatting instance with a custom number format for both the numerator and
+ * denominator.
+ *
+ * @param format the custom format for both the numerator and denominator.
+ */
+ protected AbstractFormat(final NumberFormat format) {
+ this(format, (NumberFormat) format.clone());
+ }
+
+ /**
+ * Create an improper formatting instance with a custom number format for the numerator and a
+ * custom number format for the denominator.
+ *
+ * @param numeratorFormat the custom format for the numerator.
+ * @param denominatorFormat the custom format for the denominator.
+ */
+ protected AbstractFormat(
+ final NumberFormat numeratorFormat, final NumberFormat denominatorFormat) {
+ this.numeratorFormat = numeratorFormat;
+ this.denominatorFormat = denominatorFormat;
+ }
+
+ /**
+ * Create a default number format. The default number format is based on {@link
+ * NumberFormat#getNumberInstance(java.util.Locale)}. The only customization is the maximum
+ * number of BigFraction digits, which is set to 0.
+ *
+ * @return the default number format.
+ */
+ protected static NumberFormat getDefaultNumberFormat() {
+ return getDefaultNumberFormat(Locale.getDefault());
+ }
+
+ /**
+ * Create a default number format. The default number format is based on {@link
+ * NumberFormat#getNumberInstance(java.util.Locale)}. The only customization is the maximum
+ * number of BigFraction digits, which is set to 0.
+ *
+ * @param locale the specific locale used by the format.
+ * @return the default number format specific to the given locale.
+ */
+ protected static NumberFormat getDefaultNumberFormat(final Locale locale) {
+ final NumberFormat nf = NumberFormat.getNumberInstance(locale);
+ nf.setMaximumFractionDigits(0);
+ nf.setParseIntegerOnly(true);
+ return nf;
+ }
+
+ /**
+ * Access the denominator format.
+ *
+ * @return the denominator format.
+ */
+ public NumberFormat getDenominatorFormat() {
+ return denominatorFormat;
+ }
+
+ /**
+ * Access the numerator format.
+ *
+ * @return the numerator format.
+ */
+ public NumberFormat getNumeratorFormat() {
+ return numeratorFormat;
+ }
+
+ /**
+ * Modify the denominator format.
+ *
+ * @param format the new denominator format value.
+ * @throws NullArgumentException if {@code format} is {@code null}.
+ */
+ public void setDenominatorFormat(final NumberFormat format) {
+ if (format == null) {
+ throw new NullArgumentException(LocalizedFormats.DENOMINATOR_FORMAT);
+ }
+ this.denominatorFormat = format;
+ }
+
+ /**
+ * Modify the numerator format.
+ *
+ * @param format the new numerator format value.
+ * @throws NullArgumentException if {@code format} is {@code null}.
+ */
+ public void setNumeratorFormat(final NumberFormat format) {
+ if (format == null) {
+ throw new NullArgumentException(LocalizedFormats.NUMERATOR_FORMAT);
+ }
+ this.numeratorFormat = format;
+ }
+
+ /**
+ * Parses <code>source</code> until a non-whitespace character is found.
+ *
+ * @param source the string to parse
+ * @param pos input/output parsing parameter. On output, <code>pos</code> holds the index of the
+ * next non-whitespace character.
+ */
+ protected static void parseAndIgnoreWhitespace(final String source, final ParsePosition pos) {
+ parseNextCharacter(source, pos);
+ pos.setIndex(pos.getIndex() - 1);
+ }
+
+ /**
+ * Parses <code>source</code> until a non-whitespace character is found.
+ *
+ * @param source the string to parse
+ * @param pos input/output parsing parameter.
+ * @return the first non-whitespace character.
+ */
+ protected static char parseNextCharacter(final String source, final ParsePosition pos) {
+ int index = pos.getIndex();
+ final int n = source.length();
+ char ret = 0;
+
+ if (index < n) {
+ char c;
+ do {
+ c = source.charAt(index++);
+ } while (Character.isWhitespace(c) && index < n);
+ pos.setIndex(index);
+
+ if (index < n) {
+ ret = c;
+ }
+ }
+
+ return ret;
+ }
+
+ /**
+ * Formats a double value as a fraction and appends the result to a StringBuffer.
+ *
+ * @param value the double value to format
+ * @param buffer StringBuffer to append to
+ * @param position On input: an alignment field, if desired. On output: the offsets of the
+ * alignment field
+ * @return a reference to the appended buffer
+ * @see #format(Object, StringBuffer, FieldPosition)
+ */
+ @Override
+ public StringBuffer format(
+ final double value, final StringBuffer buffer, final FieldPosition position) {
+ return format(Double.valueOf(value), buffer, position);
+ }
+
+ /**
+ * Formats a long value as a fraction and appends the result to a StringBuffer.
+ *
+ * @param value the long value to format
+ * @param buffer StringBuffer to append to
+ * @param position On input: an alignment field, if desired. On output: the offsets of the
+ * alignment field
+ * @return a reference to the appended buffer
+ * @see #format(Object, StringBuffer, FieldPosition)
+ */
+ @Override
+ public StringBuffer format(
+ final long value, final StringBuffer buffer, final FieldPosition position) {
+ return format(Long.valueOf(value), buffer, position);
+ }
+}
diff --git a/src/main/java/org/apache/commons/math3/fraction/BigFraction.java b/src/main/java/org/apache/commons/math3/fraction/BigFraction.java
new file mode 100644
index 0000000..56616b3
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/fraction/BigFraction.java
@@ -0,0 +1,1065 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math3.fraction;
+
+import org.apache.commons.math3.FieldElement;
+import org.apache.commons.math3.exception.MathArithmeticException;
+import org.apache.commons.math3.exception.MathIllegalArgumentException;
+import org.apache.commons.math3.exception.NullArgumentException;
+import org.apache.commons.math3.exception.ZeroException;
+import org.apache.commons.math3.exception.util.LocalizedFormats;
+import org.apache.commons.math3.util.ArithmeticUtils;
+import org.apache.commons.math3.util.FastMath;
+import org.apache.commons.math3.util.MathUtils;
+
+import java.io.Serializable;
+import java.math.BigDecimal;
+import java.math.BigInteger;
+
+/**
+ * Representation of a rational number without any overflow. This class is immutable.
+ *
+ * @since 2.0
+ */
+public class BigFraction extends Number
+ implements FieldElement<BigFraction>, Comparable<BigFraction>, Serializable {
+
+ /** A fraction representing "2 / 1". */
+ public static final BigFraction TWO = new BigFraction(2);
+
+ /** A fraction representing "1". */
+ public static final BigFraction ONE = new BigFraction(1);
+
+ /** A fraction representing "0". */
+ public static final BigFraction ZERO = new BigFraction(0);
+
+ /** A fraction representing "-1 / 1". */
+ public static final BigFraction MINUS_ONE = new BigFraction(-1);
+
+ /** A fraction representing "4/5". */
+ public static final BigFraction FOUR_FIFTHS = new BigFraction(4, 5);
+
+ /** A fraction representing "1/5". */
+ public static final BigFraction ONE_FIFTH = new BigFraction(1, 5);
+
+ /** A fraction representing "1/2". */
+ public static final BigFraction ONE_HALF = new BigFraction(1, 2);
+
+ /** A fraction representing "1/4". */
+ public static final BigFraction ONE_QUARTER = new BigFraction(1, 4);
+
+ /** A fraction representing "1/3". */
+ public static final BigFraction ONE_THIRD = new BigFraction(1, 3);
+
+ /** A fraction representing "3/5". */
+ public static final BigFraction THREE_FIFTHS = new BigFraction(3, 5);
+
+ /** A fraction representing "3/4". */
+ public static final BigFraction THREE_QUARTERS = new BigFraction(3, 4);
+
+ /** A fraction representing "2/5". */
+ public static final BigFraction TWO_FIFTHS = new BigFraction(2, 5);
+
+ /** A fraction representing "2/4". */
+ public static final BigFraction TWO_QUARTERS = new BigFraction(2, 4);
+
+ /** A fraction representing "2/3". */
+ public static final BigFraction TWO_THIRDS = new BigFraction(2, 3);
+
+ /** Serializable version identifier. */
+ private static final long serialVersionUID = -5630213147331578515L;
+
+ /** <code>BigInteger</code> representation of 100. */
+ private static final BigInteger ONE_HUNDRED = BigInteger.valueOf(100);
+
+ /** The numerator. */
+ private final BigInteger numerator;
+
+ /** The denominator. */
+ private final BigInteger denominator;
+
+ /**
+ * Create a {@link BigFraction} equivalent to the passed {@code BigInteger}, ie "num / 1".
+ *
+ * @param num the numerator.
+ */
+ public BigFraction(final BigInteger num) {
+ this(num, BigInteger.ONE);
+ }
+
+ /**
+ * Create a {@link BigFraction} given the numerator and denominator as {@code BigInteger}. The
+ * {@link BigFraction} is reduced to lowest terms.
+ *
+ * @param num the numerator, must not be {@code null}.
+ * @param den the denominator, must not be {@code null}.
+ * @throws ZeroException if the denominator is zero.
+ * @throws NullArgumentException if either of the arguments is null
+ */
+ public BigFraction(BigInteger num, BigInteger den) {
+ MathUtils.checkNotNull(num, LocalizedFormats.NUMERATOR);
+ MathUtils.checkNotNull(den, LocalizedFormats.DENOMINATOR);
+ if (den.signum() == 0) {
+ throw new ZeroException(LocalizedFormats.ZERO_DENOMINATOR);
+ }
+ if (num.signum() == 0) {
+ numerator = BigInteger.ZERO;
+ denominator = BigInteger.ONE;
+ } else {
+
+ // reduce numerator and denominator by greatest common denominator
+ final BigInteger gcd = num.gcd(den);
+ if (BigInteger.ONE.compareTo(gcd) < 0) {
+ num = num.divide(gcd);
+ den = den.divide(gcd);
+ }
+
+ // move sign to numerator
+ if (den.signum() == -1) {
+ num = num.negate();
+ den = den.negate();
+ }
+
+ // store the values in the final fields
+ numerator = num;
+ denominator = den;
+ }
+ }
+
+ /**
+ * Create a fraction given the double value.
+ *
+ * <p>This constructor behaves <em>differently</em> from {@link #BigFraction(double, double,
+ * int)}. It converts the double value exactly, considering its internal bits representation.
+ * This works for all values except NaN and infinities and does not requires any loop or
+ * convergence threshold.
+ *
+ * <p>Since this conversion is exact and since double numbers are sometimes approximated, the
+ * fraction created may seem strange in some cases. For example, calling <code>
+ * new BigFraction(1.0 / 3.0)</code> does <em>not</em> create the fraction 1/3, but the fraction
+ * 6004799503160661 / 18014398509481984 because the double number passed to the constructor is
+ * not exactly 1/3 (this number cannot be stored exactly in IEEE754).
+ *
+ * @see #BigFraction(double, double, int)
+ * @param value the double value to convert to a fraction.
+ * @exception MathIllegalArgumentException if value is NaN or infinite
+ */
+ public BigFraction(final double value) throws MathIllegalArgumentException {
+ if (Double.isNaN(value)) {
+ throw new MathIllegalArgumentException(LocalizedFormats.NAN_VALUE_CONVERSION);
+ }
+ if (Double.isInfinite(value)) {
+ throw new MathIllegalArgumentException(LocalizedFormats.INFINITE_VALUE_CONVERSION);
+ }
+
+ // compute m and k such that value = m * 2^k
+ final long bits = Double.doubleToLongBits(value);
+ final long sign = bits & 0x8000000000000000L;
+ final long exponent = bits & 0x7ff0000000000000L;
+ long m = bits & 0x000fffffffffffffL;
+ if (exponent != 0) {
+ // this was a normalized number, add the implicit most significant bit
+ m |= 0x0010000000000000L;
+ }
+ if (sign != 0) {
+ m = -m;
+ }
+ int k = ((int) (exponent >> 52)) - 1075;
+ while (((m & 0x001ffffffffffffeL) != 0) && ((m & 0x1) == 0)) {
+ m >>= 1;
+ ++k;
+ }
+
+ if (k < 0) {
+ numerator = BigInteger.valueOf(m);
+ denominator = BigInteger.ZERO.flipBit(-k);
+ } else {
+ numerator = BigInteger.valueOf(m).multiply(BigInteger.ZERO.flipBit(k));
+ denominator = BigInteger.ONE;
+ }
+ }
+
+ /**
+ * Create a fraction given the double value and maximum error allowed.
+ *
+ * <p>References:
+ *
+ * <ul>
+ * <li><a href="http://mathworld.wolfram.com/ContinuedFraction.html">Continued Fraction</a>
+ * equations (11) and (22)-(26)
+ * </ul>
+ *
+ * @param value the double value to convert to a fraction.
+ * @param epsilon maximum error allowed. The resulting fraction is within <code>epsilon</code>
+ * of <code>value</code>, in absolute terms.
+ * @param maxIterations maximum number of convergents.
+ * @throws FractionConversionException if the continued fraction failed to converge.
+ * @see #BigFraction(double)
+ */
+ public BigFraction(final double value, final double epsilon, final int maxIterations)
+ throws FractionConversionException {
+ this(value, epsilon, Integer.MAX_VALUE, maxIterations);
+ }
+
+ /**
+ * Create a fraction given the double value and either the maximum error allowed or the maximum
+ * number of denominator digits.
+ *
+ * <p>NOTE: This constructor is called with EITHER - a valid epsilon value and the
+ * maxDenominator set to Integer.MAX_VALUE (that way the maxDenominator has no effect). OR - a
+ * valid maxDenominator value and the epsilon value set to zero (that way epsilon only has
+ * effect if there is an exact match before the maxDenominator value is reached).
+ *
+ * <p>It has been done this way so that the same code can be (re)used for both scenarios.
+ * However this could be confusing to users if it were part of the public API and this
+ * constructor should therefore remain PRIVATE. See JIRA issue ticket MATH-181 for more details:
+ *
+ * <p>https://issues.apache.org/jira/browse/MATH-181
+ *
+ * @param value the double value to convert to a fraction.
+ * @param epsilon maximum error allowed. The resulting fraction is within <code>epsilon</code>
+ * of <code>value</code>, in absolute terms.
+ * @param maxDenominator maximum denominator value allowed.
+ * @param maxIterations maximum number of convergents.
+ * @throws FractionConversionException if the continued fraction failed to converge.
+ */
+ private BigFraction(
+ final double value, final double epsilon, final int maxDenominator, int maxIterations)
+ throws FractionConversionException {
+ long overflow = Integer.MAX_VALUE;
+ double r0 = value;
+ long a0 = (long) FastMath.floor(r0);
+
+ if (FastMath.abs(a0) > overflow) {
+ throw new FractionConversionException(value, a0, 1l);
+ }
+
+ // check for (almost) integer arguments, which should not go
+ // to iterations.
+ if (FastMath.abs(a0 - value) < epsilon) {
+ numerator = BigInteger.valueOf(a0);
+ denominator = BigInteger.ONE;
+ return;
+ }
+
+ long p0 = 1;
+ long q0 = 0;
+ long p1 = a0;
+ long q1 = 1;
+
+ long p2 = 0;
+ long q2 = 1;
+
+ int n = 0;
+ boolean stop = false;
+ do {
+ ++n;
+ final double r1 = 1.0 / (r0 - a0);
+ final long a1 = (long) FastMath.floor(r1);
+ p2 = (a1 * p1) + p0;
+ q2 = (a1 * q1) + q0;
+ if ((p2 > overflow) || (q2 > overflow)) {
+ // in maxDenominator mode, if the last fraction was very close to the actual value
+ // q2 may overflow in the next iteration; in this case return the last one.
+ if (epsilon == 0.0 && FastMath.abs(q1) < maxDenominator) {
+ break;
+ }
+ throw new FractionConversionException(value, p2, q2);
+ }
+
+ final double convergent = (double) p2 / (double) q2;
+ if ((n < maxIterations)
+ && (FastMath.abs(convergent - value) > epsilon)
+ && (q2 < maxDenominator)) {
+ p0 = p1;
+ p1 = p2;
+ q0 = q1;
+ q1 = q2;
+ a0 = a1;
+ r0 = r1;
+ } else {
+ stop = true;
+ }
+ } while (!stop);
+
+ if (n >= maxIterations) {
+ throw new FractionConversionException(value, maxIterations);
+ }
+
+ if (q2 < maxDenominator) {
+ numerator = BigInteger.valueOf(p2);
+ denominator = BigInteger.valueOf(q2);
+ } else {
+ numerator = BigInteger.valueOf(p1);
+ denominator = BigInteger.valueOf(q1);
+ }
+ }
+
+ /**
+ * Create a fraction given the double value and maximum denominator.
+ *
+ * <p>References:
+ *
+ * <ul>
+ * <li><a href="http://mathworld.wolfram.com/ContinuedFraction.html">Continued Fraction</a>
+ * equations (11) and (22)-(26)
+ * </ul>
+ *
+ * @param value the double value to convert to a fraction.
+ * @param maxDenominator The maximum allowed value for denominator.
+ * @throws FractionConversionException if the continued fraction failed to converge.
+ */
+ public BigFraction(final double value, final int maxDenominator)
+ throws FractionConversionException {
+ this(value, 0, maxDenominator, 100);
+ }
+
+ /**
+ * Create a {@link BigFraction} equivalent to the passed {@code int}, ie "num / 1".
+ *
+ * @param num the numerator.
+ */
+ public BigFraction(final int num) {
+ this(BigInteger.valueOf(num), BigInteger.ONE);
+ }
+
+ /**
+ * Create a {@link BigFraction} given the numerator and denominator as simple {@code int}. The
+ * {@link BigFraction} is reduced to lowest terms.
+ *
+ * @param num the numerator.
+ * @param den the denominator.
+ */
+ public BigFraction(final int num, final int den) {
+ this(BigInteger.valueOf(num), BigInteger.valueOf(den));
+ }
+
+ /**
+ * Create a {@link BigFraction} equivalent to the passed long, ie "num / 1".
+ *
+ * @param num the numerator.
+ */
+ public BigFraction(final long num) {
+ this(BigInteger.valueOf(num), BigInteger.ONE);
+ }
+
+ /**
+ * Create a {@link BigFraction} given the numerator and denominator as simple {@code long}. The
+ * {@link BigFraction} is reduced to lowest terms.
+ *
+ * @param num the numerator.
+ * @param den the denominator.
+ */
+ public BigFraction(final long num, final long den) {
+ this(BigInteger.valueOf(num), BigInteger.valueOf(den));
+ }
+
+ /**
+ * Creates a <code>BigFraction</code> instance with the 2 parts of a fraction Y/Z.
+ *
+ * <p>Any negative signs are resolved to be on the numerator.
+ *
+ * @param numerator the numerator, for example the three in 'three sevenths'.
+ * @param denominator the denominator, for example the seven in 'three sevenths'.
+ * @return a new fraction instance, with the numerator and denominator reduced.
+ * @throws ArithmeticException if the denominator is <code>zero</code>.
+ */
+ public static BigFraction getReducedFraction(final int numerator, final int denominator) {
+ if (numerator == 0) {
+ return ZERO; // normalize zero.
+ }
+
+ return new BigFraction(numerator, denominator);
+ }
+
+ /**
+ * Returns the absolute value of this {@link BigFraction}.
+ *
+ * @return the absolute value as a {@link BigFraction}.
+ */
+ public BigFraction abs() {
+ return (numerator.signum() == 1) ? this : negate();
+ }
+
+ /**
+ * Adds the value of this fraction to the passed {@link BigInteger}, returning the result in
+ * reduced form.
+ *
+ * @param bg the {@link BigInteger} to add, must'nt be <code>null</code>.
+ * @return a <code>BigFraction</code> instance with the resulting values.
+ * @throws NullArgumentException if the {@link BigInteger} is <code>null</code>.
+ */
+ public BigFraction add(final BigInteger bg) throws NullArgumentException {
+ MathUtils.checkNotNull(bg);
+
+ if (numerator.signum() == 0) {
+ return new BigFraction(bg);
+ }
+ if (bg.signum() == 0) {
+ return this;
+ }
+
+ return new BigFraction(numerator.add(denominator.multiply(bg)), denominator);
+ }
+
+ /**
+ * Adds the value of this fraction to the passed {@code integer}, returning the result in
+ * reduced form.
+ *
+ * @param i the {@code integer} to add.
+ * @return a <code>BigFraction</code> instance with the resulting values.
+ */
+ public BigFraction add(final int i) {
+ return add(BigInteger.valueOf(i));
+ }
+
+ /**
+ * Adds the value of this fraction to the passed {@code long}, returning the result in reduced
+ * form.
+ *
+ * @param l the {@code long} to add.
+ * @return a <code>BigFraction</code> instance with the resulting values.
+ */
+ public BigFraction add(final long l) {
+ return add(BigInteger.valueOf(l));
+ }
+
+ /**
+ * Adds the value of this fraction to another, returning the result in reduced form.
+ *
+ * @param fraction the {@link BigFraction} to add, must not be <code>null</code>.
+ * @return a {@link BigFraction} instance with the resulting values.
+ * @throws NullArgumentException if the {@link BigFraction} is {@code null}.
+ */
+ public BigFraction add(final BigFraction fraction) {
+ if (fraction == null) {
+ throw new NullArgumentException(LocalizedFormats.FRACTION);
+ }
+ if (fraction.numerator.signum() == 0) {
+ return this;
+ }
+ if (numerator.signum() == 0) {
+ return fraction;
+ }
+
+ BigInteger num = null;
+ BigInteger den = null;
+
+ if (denominator.equals(fraction.denominator)) {
+ num = numerator.add(fraction.numerator);
+ den = denominator;
+ } else {
+ num =
+ (numerator.multiply(fraction.denominator))
+ .add((fraction.numerator).multiply(denominator));
+ den = denominator.multiply(fraction.denominator);
+ }
+
+ if (num.signum() == 0) {
+ return ZERO;
+ }
+
+ return new BigFraction(num, den);
+ }
+
+ /**
+ * Gets the fraction as a <code>BigDecimal</code>. This calculates the fraction as the numerator
+ * divided by denominator.
+ *
+ * @return the fraction as a <code>BigDecimal</code>.
+ * @throws ArithmeticException if the exact quotient does not have a terminating decimal
+ * expansion.
+ * @see BigDecimal
+ */
+ public BigDecimal bigDecimalValue() {
+ return new BigDecimal(numerator).divide(new BigDecimal(denominator));
+ }
+
+ /**
+ * Gets the fraction as a <code>BigDecimal</code> following the passed rounding mode. This
+ * calculates the fraction as the numerator divided by denominator.
+ *
+ * @param roundingMode rounding mode to apply. see {@link BigDecimal} constants.
+ * @return the fraction as a <code>BigDecimal</code>.
+ * @throws IllegalArgumentException if {@code roundingMode} does not represent a valid rounding
+ * mode.
+ * @see BigDecimal
+ */
+ public BigDecimal bigDecimalValue(final int roundingMode) {
+ return new BigDecimal(numerator).divide(new BigDecimal(denominator), roundingMode);
+ }
+
+ /**
+ * Gets the fraction as a <code>BigDecimal</code> following the passed scale and rounding mode.
+ * This calculates the fraction as the numerator divided by denominator.
+ *
+ * @param scale scale of the <code>BigDecimal</code> quotient to be returned. see {@link
+ * BigDecimal} for more information.
+ * @param roundingMode rounding mode to apply. see {@link BigDecimal} constants.
+ * @return the fraction as a <code>BigDecimal</code>.
+ * @see BigDecimal
+ */
+ public BigDecimal bigDecimalValue(final int scale, final int roundingMode) {
+ return new BigDecimal(numerator).divide(new BigDecimal(denominator), scale, roundingMode);
+ }
+
+ /**
+ * Compares this object to another based on size.
+ *
+ * @param object the object to compare to, must not be <code>null</code>.
+ * @return -1 if this is less than {@code object}, +1 if this is greater than {@code object}, 0
+ * if they are equal.
+ * @see java.lang.Comparable#compareTo(java.lang.Object)
+ */
+ public int compareTo(final BigFraction object) {
+ int lhsSigNum = numerator.signum();
+ int rhsSigNum = object.numerator.signum();
+
+ if (lhsSigNum != rhsSigNum) {
+ return (lhsSigNum > rhsSigNum) ? 1 : -1;
+ }
+ if (lhsSigNum == 0) {
+ return 0;
+ }
+
+ BigInteger nOd = numerator.multiply(object.denominator);
+ BigInteger dOn = denominator.multiply(object.numerator);
+ return nOd.compareTo(dOn);
+ }
+
+ /**
+ * Divide the value of this fraction by the passed {@code BigInteger}, ie {@code this * 1 / bg},
+ * returning the result in reduced form.
+ *
+ * @param bg the {@code BigInteger} to divide by, must not be {@code null}
+ * @return a {@link BigFraction} instance with the resulting values
+ * @throws NullArgumentException if the {@code BigInteger} is {@code null}
+ * @throws MathArithmeticException if the fraction to divide by is zero
+ */
+ public BigFraction divide(final BigInteger bg) {
+ if (bg == null) {
+ throw new NullArgumentException(LocalizedFormats.FRACTION);
+ }
+ if (bg.signum() == 0) {
+ throw new MathArithmeticException(LocalizedFormats.ZERO_DENOMINATOR);
+ }
+ if (numerator.signum() == 0) {
+ return ZERO;
+ }
+ return new BigFraction(numerator, denominator.multiply(bg));
+ }
+
+ /**
+ * Divide the value of this fraction by the passed {@code int}, ie {@code this * 1 / i},
+ * returning the result in reduced form.
+ *
+ * @param i the {@code int} to divide by
+ * @return a {@link BigFraction} instance with the resulting values
+ * @throws MathArithmeticException if the fraction to divide by is zero
+ */
+ public BigFraction divide(final int i) {
+ return divide(BigInteger.valueOf(i));
+ }
+
+ /**
+ * Divide the value of this fraction by the passed {@code long}, ie {@code this * 1 / l},
+ * returning the result in reduced form.
+ *
+ * @param l the {@code long} to divide by
+ * @return a {@link BigFraction} instance with the resulting values
+ * @throws MathArithmeticException if the fraction to divide by is zero
+ */
+ public BigFraction divide(final long l) {
+ return divide(BigInteger.valueOf(l));
+ }
+
+ /**
+ * Divide the value of this fraction by another, returning the result in reduced form.
+ *
+ * @param fraction Fraction to divide by, must not be {@code null}.
+ * @return a {@link BigFraction} instance with the resulting values.
+ * @throws NullArgumentException if the {@code fraction} is {@code null}.
+ * @throws MathArithmeticException if the fraction to divide by is zero
+ */
+ public BigFraction divide(final BigFraction fraction) {
+ if (fraction == null) {
+ throw new NullArgumentException(LocalizedFormats.FRACTION);
+ }
+ if (fraction.numerator.signum() == 0) {
+ throw new MathArithmeticException(LocalizedFormats.ZERO_DENOMINATOR);
+ }
+ if (numerator.signum() == 0) {
+ return ZERO;
+ }
+
+ return multiply(fraction.reciprocal());
+ }
+
+ /**
+ * Gets the fraction as a {@code double}. This calculates the fraction as the numerator divided
+ * by denominator.
+ *
+ * @return the fraction as a {@code double}
+ * @see java.lang.Number#doubleValue()
+ */
+ @Override
+ public double doubleValue() {
+ double result = numerator.doubleValue() / denominator.doubleValue();
+ if (Double.isNaN(result)) {
+ // Numerator and/or denominator must be out of range:
+ // Calculate how far to shift them to put them in range.
+ int shift =
+ FastMath.max(numerator.bitLength(), denominator.bitLength())
+ - FastMath.getExponent(Double.MAX_VALUE);
+ result =
+ numerator.shiftRight(shift).doubleValue()
+ / denominator.shiftRight(shift).doubleValue();
+ }
+ return result;
+ }
+
+ /**
+ * Test for the equality of two fractions. If the lowest term numerator and denominators are the
+ * same for both fractions, the two fractions are considered to be equal.
+ *
+ * @param other fraction to test for equality to this fraction, can be <code>null</code>.
+ * @return true if two fractions are equal, false if object is <code>null</code>, not an
+ * instance of {@link BigFraction}, or not equal to this fraction instance.
+ * @see java.lang.Object#equals(java.lang.Object)
+ */
+ @Override
+ public boolean equals(final Object other) {
+ boolean ret = false;
+
+ if (this == other) {
+ ret = true;
+ } else if (other instanceof BigFraction) {
+ BigFraction rhs = ((BigFraction) other).reduce();
+ BigFraction thisOne = this.reduce();
+ ret =
+ thisOne.numerator.equals(rhs.numerator)
+ && thisOne.denominator.equals(rhs.denominator);
+ }
+
+ return ret;
+ }
+
+ /**
+ * Gets the fraction as a {@code float}. This calculates the fraction as the numerator divided
+ * by denominator.
+ *
+ * @return the fraction as a {@code float}.
+ * @see java.lang.Number#floatValue()
+ */
+ @Override
+ public float floatValue() {
+ float result = numerator.floatValue() / denominator.floatValue();
+ if (Double.isNaN(result)) {
+ // Numerator and/or denominator must be out of range:
+ // Calculate how far to shift them to put them in range.
+ int shift =
+ FastMath.max(numerator.bitLength(), denominator.bitLength())
+ - FastMath.getExponent(Float.MAX_VALUE);
+ result =
+ numerator.shiftRight(shift).floatValue()
+ / denominator.shiftRight(shift).floatValue();
+ }
+ return result;
+ }
+
+ /**
+ * Access the denominator as a <code>BigInteger</code>.
+ *
+ * @return the denominator as a <code>BigInteger</code>.
+ */
+ public BigInteger getDenominator() {
+ return denominator;
+ }
+
+ /**
+ * Access the denominator as a {@code int}.
+ *
+ * @return the denominator as a {@code int}.
+ */
+ public int getDenominatorAsInt() {
+ return denominator.intValue();
+ }
+
+ /**
+ * Access the denominator as a {@code long}.
+ *
+ * @return the denominator as a {@code long}.
+ */
+ public long getDenominatorAsLong() {
+ return denominator.longValue();
+ }
+
+ /**
+ * Access the numerator as a <code>BigInteger</code>.
+ *
+ * @return the numerator as a <code>BigInteger</code>.
+ */
+ public BigInteger getNumerator() {
+ return numerator;
+ }
+
+ /**
+ * Access the numerator as a {@code int}.
+ *
+ * @return the numerator as a {@code int}.
+ */
+ public int getNumeratorAsInt() {
+ return numerator.intValue();
+ }
+
+ /**
+ * Access the numerator as a {@code long}.
+ *
+ * @return the numerator as a {@code long}.
+ */
+ public long getNumeratorAsLong() {
+ return numerator.longValue();
+ }
+
+ /**
+ * Gets a hashCode for the fraction.
+ *
+ * @return a hash code value for this object.
+ * @see java.lang.Object#hashCode()
+ */
+ @Override
+ public int hashCode() {
+ return 37 * (37 * 17 + numerator.hashCode()) + denominator.hashCode();
+ }
+
+ /**
+ * Gets the fraction as an {@code int}. This returns the whole number part of the fraction.
+ *
+ * @return the whole number fraction part.
+ * @see java.lang.Number#intValue()
+ */
+ @Override
+ public int intValue() {
+ return numerator.divide(denominator).intValue();
+ }
+
+ /**
+ * Gets the fraction as a {@code long}. This returns the whole number part of the fraction.
+ *
+ * @return the whole number fraction part.
+ * @see java.lang.Number#longValue()
+ */
+ @Override
+ public long longValue() {
+ return numerator.divide(denominator).longValue();
+ }
+
+ /**
+ * Multiplies the value of this fraction by the passed <code>BigInteger</code>, returning the
+ * result in reduced form.
+ *
+ * @param bg the {@code BigInteger} to multiply by.
+ * @return a {@code BigFraction} instance with the resulting values.
+ * @throws NullArgumentException if {@code bg} is {@code null}.
+ */
+ public BigFraction multiply(final BigInteger bg) {
+ if (bg == null) {
+ throw new NullArgumentException();
+ }
+ if (numerator.signum() == 0 || bg.signum() == 0) {
+ return ZERO;
+ }
+ return new BigFraction(bg.multiply(numerator), denominator);
+ }
+
+ /**
+ * Multiply the value of this fraction by the passed {@code int}, returning the result in
+ * reduced form.
+ *
+ * @param i the {@code int} to multiply by.
+ * @return a {@link BigFraction} instance with the resulting values.
+ */
+ public BigFraction multiply(final int i) {
+ if (i == 0 || numerator.signum() == 0) {
+ return ZERO;
+ }
+
+ return multiply(BigInteger.valueOf(i));
+ }
+
+ /**
+ * Multiply the value of this fraction by the passed {@code long}, returning the result in
+ * reduced form.
+ *
+ * @param l the {@code long} to multiply by.
+ * @return a {@link BigFraction} instance with the resulting values.
+ */
+ public BigFraction multiply(final long l) {
+ if (l == 0 || numerator.signum() == 0) {
+ return ZERO;
+ }
+
+ return multiply(BigInteger.valueOf(l));
+ }
+
+ /**
+ * Multiplies the value of this fraction by another, returning the result in reduced form.
+ *
+ * @param fraction Fraction to multiply by, must not be {@code null}.
+ * @return a {@link BigFraction} instance with the resulting values.
+ * @throws NullArgumentException if {@code fraction} is {@code null}.
+ */
+ public BigFraction multiply(final BigFraction fraction) {
+ if (fraction == null) {
+ throw new NullArgumentException(LocalizedFormats.FRACTION);
+ }
+ if (numerator.signum() == 0 || fraction.numerator.signum() == 0) {
+ return ZERO;
+ }
+ return new BigFraction(
+ numerator.multiply(fraction.numerator), denominator.multiply(fraction.denominator));
+ }
+
+ /**
+ * Return the additive inverse of this fraction, returning the result in reduced form.
+ *
+ * @return the negation of this fraction.
+ */
+ public BigFraction negate() {
+ return new BigFraction(numerator.negate(), denominator);
+ }
+
+ /**
+ * Gets the fraction percentage as a {@code double}. This calculates the fraction as the
+ * numerator divided by denominator multiplied by 100.
+ *
+ * @return the fraction percentage as a {@code double}.
+ */
+ public double percentageValue() {
+ return multiply(ONE_HUNDRED).doubleValue();
+ }
+
+ /**
+ * Returns a {@code BigFraction} whose value is {@code (this<sup>exponent</sup>)}, returning the
+ * result in reduced form.
+ *
+ * @param exponent exponent to which this {@code BigFraction} is to be raised.
+ * @return <tt>this<sup>exponent</sup></tt>.
+ */
+ public BigFraction pow(final int exponent) {
+ if (exponent == 0) {
+ return ONE;
+ }
+ if (numerator.signum() == 0) {
+ return this;
+ }
+
+ if (exponent < 0) {
+ return new BigFraction(denominator.pow(-exponent), numerator.pow(-exponent));
+ }
+ return new BigFraction(numerator.pow(exponent), denominator.pow(exponent));
+ }
+
+ /**
+ * Returns a <code>BigFraction</code> whose value is <tt>(this<sup>exponent</sup>)</tt>,
+ * returning the result in reduced form.
+ *
+ * @param exponent exponent to which this <code>BigFraction</code> is to be raised.
+ * @return <tt>this<sup>exponent</sup></tt> as a <code>BigFraction</code>.
+ */
+ public BigFraction pow(final long exponent) {
+ if (exponent == 0) {
+ return ONE;
+ }
+ if (numerator.signum() == 0) {
+ return this;
+ }
+
+ if (exponent < 0) {
+ return new BigFraction(
+ ArithmeticUtils.pow(denominator, -exponent),
+ ArithmeticUtils.pow(numerator, -exponent));
+ }
+ return new BigFraction(
+ ArithmeticUtils.pow(numerator, exponent),
+ ArithmeticUtils.pow(denominator, exponent));
+ }
+
+ /**
+ * Returns a <code>BigFraction</code> whose value is <tt>(this<sup>exponent</sup>)</tt>,
+ * returning the result in reduced form.
+ *
+ * @param exponent exponent to which this <code>BigFraction</code> is to be raised.
+ * @return <tt>this<sup>exponent</sup></tt> as a <code>BigFraction</code>.
+ */
+ public BigFraction pow(final BigInteger exponent) {
+ if (exponent.signum() == 0) {
+ return ONE;
+ }
+ if (numerator.signum() == 0) {
+ return this;
+ }
+
+ if (exponent.signum() == -1) {
+ final BigInteger eNeg = exponent.negate();
+ return new BigFraction(
+ ArithmeticUtils.pow(denominator, eNeg), ArithmeticUtils.pow(numerator, eNeg));
+ }
+ return new BigFraction(
+ ArithmeticUtils.pow(numerator, exponent),
+ ArithmeticUtils.pow(denominator, exponent));
+ }
+
+ /**
+ * Returns a <code>double</code> whose value is <tt>(this<sup>exponent</sup>)</tt>, returning
+ * the result in reduced form.
+ *
+ * @param exponent exponent to which this <code>BigFraction</code> is to be raised.
+ * @return <tt>this<sup>exponent</sup></tt>.
+ */
+ public double pow(final double exponent) {
+ return FastMath.pow(numerator.doubleValue(), exponent)
+ / FastMath.pow(denominator.doubleValue(), exponent);
+ }
+
+ /**
+ * Return the multiplicative inverse of this fraction.
+ *
+ * @return the reciprocal fraction.
+ */
+ public BigFraction reciprocal() {
+ return new BigFraction(denominator, numerator);
+ }
+
+ /**
+ * Reduce this <code>BigFraction</code> to its lowest terms.
+ *
+ * @return the reduced <code>BigFraction</code>. It doesn't change anything if the fraction can
+ * be reduced.
+ */
+ public BigFraction reduce() {
+ final BigInteger gcd = numerator.gcd(denominator);
+
+ if (BigInteger.ONE.compareTo(gcd) < 0) {
+ return new BigFraction(numerator.divide(gcd), denominator.divide(gcd));
+ } else {
+ return this;
+ }
+ }
+
+ /**
+ * Subtracts the value of an {@link BigInteger} from the value of this {@code BigFraction},
+ * returning the result in reduced form.
+ *
+ * @param bg the {@link BigInteger} to subtract, cannot be {@code null}.
+ * @return a {@code BigFraction} instance with the resulting values.
+ * @throws NullArgumentException if the {@link BigInteger} is {@code null}.
+ */
+ public BigFraction subtract(final BigInteger bg) {
+ if (bg == null) {
+ throw new NullArgumentException();
+ }
+ if (bg.signum() == 0) {
+ return this;
+ }
+ if (numerator.signum() == 0) {
+ return new BigFraction(bg.negate());
+ }
+
+ return new BigFraction(numerator.subtract(denominator.multiply(bg)), denominator);
+ }
+
+ /**
+ * Subtracts the value of an {@code integer} from the value of this {@code BigFraction},
+ * returning the result in reduced form.
+ *
+ * @param i the {@code integer} to subtract.
+ * @return a {@code BigFraction} instance with the resulting values.
+ */
+ public BigFraction subtract(final int i) {
+ return subtract(BigInteger.valueOf(i));
+ }
+
+ /**
+ * Subtracts the value of a {@code long} from the value of this {@code BigFraction}, returning
+ * the result in reduced form.
+ *
+ * @param l the {@code long} to subtract.
+ * @return a {@code BigFraction} instance with the resulting values.
+ */
+ public BigFraction subtract(final long l) {
+ return subtract(BigInteger.valueOf(l));
+ }
+
+ /**
+ * Subtracts the value of another fraction from the value of this one, returning the result in
+ * reduced form.
+ *
+ * @param fraction {@link BigFraction} to subtract, must not be {@code null}.
+ * @return a {@link BigFraction} instance with the resulting values
+ * @throws NullArgumentException if the {@code fraction} is {@code null}.
+ */
+ public BigFraction subtract(final BigFraction fraction) {
+ if (fraction == null) {
+ throw new NullArgumentException(LocalizedFormats.FRACTION);
+ }
+ if (fraction.numerator.signum() == 0) {
+ return this;
+ }
+ if (numerator.signum() == 0) {
+ return fraction.negate();
+ }
+
+ BigInteger num = null;
+ BigInteger den = null;
+ if (denominator.equals(fraction.denominator)) {
+ num = numerator.subtract(fraction.numerator);
+ den = denominator;
+ } else {
+ num =
+ (numerator.multiply(fraction.denominator))
+ .subtract((fraction.numerator).multiply(denominator));
+ den = denominator.multiply(fraction.denominator);
+ }
+ return new BigFraction(num, den);
+ }
+
+ /**
+ * Returns the <code>String</code> representing this fraction, ie "num / dem" or just "num" if
+ * the denominator is one.
+ *
+ * @return a string representation of the fraction.
+ * @see java.lang.Object#toString()
+ */
+ @Override
+ public String toString() {
+ String str = null;
+ if (BigInteger.ONE.equals(denominator)) {
+ str = numerator.toString();
+ } else if (BigInteger.ZERO.equals(numerator)) {
+ str = "0";
+ } else {
+ str = numerator + " / " + denominator;
+ }
+ return str;
+ }
+
+ /** {@inheritDoc} */
+ public BigFractionField getField() {
+ return BigFractionField.getInstance();
+ }
+}
diff --git a/src/main/java/org/apache/commons/math3/fraction/BigFractionField.java b/src/main/java/org/apache/commons/math3/fraction/BigFractionField.java
new file mode 100644
index 0000000..763cea1
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/fraction/BigFractionField.java
@@ -0,0 +1,87 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math3.fraction;
+
+import org.apache.commons.math3.Field;
+import org.apache.commons.math3.FieldElement;
+
+import java.io.Serializable;
+
+/**
+ * Representation of the fractional numbers without any overflow field.
+ *
+ * <p>This class is a singleton.
+ *
+ * @see Fraction
+ * @since 2.0
+ */
+public class BigFractionField implements Field<BigFraction>, Serializable {
+
+ /** Serializable version identifier */
+ private static final long serialVersionUID = -1699294557189741703L;
+
+ /** Private constructor for the singleton. */
+ private BigFractionField() {}
+
+ /**
+ * Get the unique instance.
+ *
+ * @return the unique instance
+ */
+ public static BigFractionField getInstance() {
+ return LazyHolder.INSTANCE;
+ }
+
+ /** {@inheritDoc} */
+ public BigFraction getOne() {
+ return BigFraction.ONE;
+ }
+
+ /** {@inheritDoc} */
+ public BigFraction getZero() {
+ return BigFraction.ZERO;
+ }
+
+ /** {@inheritDoc} */
+ public Class<? extends FieldElement<BigFraction>> getRuntimeClass() {
+ return BigFraction.class;
+ }
+
+ // CHECKSTYLE: stop HideUtilityClassConstructor
+ /**
+ * Holder for the instance.
+ *
+ * <p>We use here the Initialization On Demand Holder Idiom.
+ */
+ private static class LazyHolder {
+ /** Cached field instance. */
+ private static final BigFractionField INSTANCE = new BigFractionField();
+ }
+
+ // CHECKSTYLE: resume HideUtilityClassConstructor
+
+ /**
+ * Handle deserialization of the singleton.
+ *
+ * @return the singleton instance
+ */
+ private Object readResolve() {
+ // return the singleton instance
+ return LazyHolder.INSTANCE;
+ }
+}
diff --git a/src/main/java/org/apache/commons/math3/fraction/BigFractionFormat.java b/src/main/java/org/apache/commons/math3/fraction/BigFractionFormat.java
new file mode 100644
index 0000000..aed9b26
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/fraction/BigFractionFormat.java
@@ -0,0 +1,288 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math3.fraction;
+
+import org.apache.commons.math3.exception.MathIllegalArgumentException;
+import org.apache.commons.math3.exception.MathParseException;
+import org.apache.commons.math3.exception.util.LocalizedFormats;
+
+import java.io.Serializable;
+import java.math.BigInteger;
+import java.text.FieldPosition;
+import java.text.NumberFormat;
+import java.text.ParsePosition;
+import java.util.Locale;
+
+/**
+ * Formats a BigFraction number in proper format or improper format.
+ *
+ * <p>The number format for each of the whole number, numerator and, denominator can be configured.
+ *
+ * @since 2.0
+ */
+public class BigFractionFormat extends AbstractFormat implements Serializable {
+
+ /** Serializable version identifier */
+ private static final long serialVersionUID = -2932167925527338976L;
+
+ /**
+ * Create an improper formatting instance with the default number format for the numerator and
+ * denominator.
+ */
+ public BigFractionFormat() {}
+
+ /**
+ * Create an improper formatting instance with a custom number format for both the numerator and
+ * denominator.
+ *
+ * @param format the custom format for both the numerator and denominator.
+ */
+ public BigFractionFormat(final NumberFormat format) {
+ super(format);
+ }
+
+ /**
+ * Create an improper formatting instance with a custom number format for the numerator and a
+ * custom number format for the denominator.
+ *
+ * @param numeratorFormat the custom format for the numerator.
+ * @param denominatorFormat the custom format for the denominator.
+ */
+ public BigFractionFormat(
+ final NumberFormat numeratorFormat, final NumberFormat denominatorFormat) {
+ super(numeratorFormat, denominatorFormat);
+ }
+
+ /**
+ * Get the set of locales for which complex formats are available. This is the same set as the
+ * {@link NumberFormat} set.
+ *
+ * @return available complex format locales.
+ */
+ public static Locale[] getAvailableLocales() {
+ return NumberFormat.getAvailableLocales();
+ }
+
+ /**
+ * This static method calls formatBigFraction() on a default instance of BigFractionFormat.
+ *
+ * @param f BigFraction object to format
+ * @return A formatted BigFraction in proper form.
+ */
+ public static String formatBigFraction(final BigFraction f) {
+ return getImproperInstance().format(f);
+ }
+
+ /**
+ * Returns the default complex format for the current locale.
+ *
+ * @return the default complex format.
+ */
+ public static BigFractionFormat getImproperInstance() {
+ return getImproperInstance(Locale.getDefault());
+ }
+
+ /**
+ * Returns the default complex format for the given locale.
+ *
+ * @param locale the specific locale used by the format.
+ * @return the complex format specific to the given locale.
+ */
+ public static BigFractionFormat getImproperInstance(final Locale locale) {
+ return new BigFractionFormat(getDefaultNumberFormat(locale));
+ }
+
+ /**
+ * Returns the default complex format for the current locale.
+ *
+ * @return the default complex format.
+ */
+ public static BigFractionFormat getProperInstance() {
+ return getProperInstance(Locale.getDefault());
+ }
+
+ /**
+ * Returns the default complex format for the given locale.
+ *
+ * @param locale the specific locale used by the format.
+ * @return the complex format specific to the given locale.
+ */
+ public static BigFractionFormat getProperInstance(final Locale locale) {
+ return new ProperBigFractionFormat(getDefaultNumberFormat(locale));
+ }
+
+ /**
+ * Formats a {@link BigFraction} object to produce a string. The BigFraction is output in
+ * improper format.
+ *
+ * @param BigFraction the object to format.
+ * @param toAppendTo where the text is to be appended
+ * @param pos On input: an alignment field, if desired. On output: the offsets of the alignment
+ * field
+ * @return the value passed in as toAppendTo.
+ */
+ public StringBuffer format(
+ final BigFraction BigFraction, final StringBuffer toAppendTo, final FieldPosition pos) {
+
+ pos.setBeginIndex(0);
+ pos.setEndIndex(0);
+
+ getNumeratorFormat().format(BigFraction.getNumerator(), toAppendTo, pos);
+ toAppendTo.append(" / ");
+ getDenominatorFormat().format(BigFraction.getDenominator(), toAppendTo, pos);
+
+ return toAppendTo;
+ }
+
+ /**
+ * Formats an object and appends the result to a StringBuffer. <code>obj</code> must be either a
+ * {@link BigFraction} object or a {@link BigInteger} object or a {@link Number} object. Any
+ * other type of object will result in an {@link IllegalArgumentException} being thrown.
+ *
+ * @param obj the object to format.
+ * @param toAppendTo where the text is to be appended
+ * @param pos On input: an alignment field, if desired. On output: the offsets of the alignment
+ * field
+ * @return the value passed in as toAppendTo.
+ * @see java.text.Format#format(java.lang.Object, java.lang.StringBuffer,
+ * java.text.FieldPosition)
+ * @throws MathIllegalArgumentException if <code>obj</code> is not a valid type.
+ */
+ @Override
+ public StringBuffer format(
+ final Object obj, final StringBuffer toAppendTo, final FieldPosition pos) {
+
+ final StringBuffer ret;
+ if (obj instanceof BigFraction) {
+ ret = format((BigFraction) obj, toAppendTo, pos);
+ } else if (obj instanceof BigInteger) {
+ ret = format(new BigFraction((BigInteger) obj), toAppendTo, pos);
+ } else if (obj instanceof Number) {
+ ret = format(new BigFraction(((Number) obj).doubleValue()), toAppendTo, pos);
+ } else {
+ throw new MathIllegalArgumentException(
+ LocalizedFormats.CANNOT_FORMAT_OBJECT_TO_FRACTION);
+ }
+
+ return ret;
+ }
+
+ /**
+ * Parses a string to produce a {@link BigFraction} object.
+ *
+ * @param source the string to parse
+ * @return the parsed {@link BigFraction} object.
+ * @exception MathParseException if the beginning of the specified string cannot be parsed.
+ */
+ @Override
+ public BigFraction parse(final String source) throws MathParseException {
+ final ParsePosition parsePosition = new ParsePosition(0);
+ final BigFraction result = parse(source, parsePosition);
+ if (parsePosition.getIndex() == 0) {
+ throw new MathParseException(source, parsePosition.getErrorIndex(), BigFraction.class);
+ }
+ return result;
+ }
+
+ /**
+ * Parses a string to produce a {@link BigFraction} object. This method expects the string to be
+ * formatted as an improper BigFraction.
+ *
+ * @param source the string to parse
+ * @param pos input/output parsing parameter.
+ * @return the parsed {@link BigFraction} object.
+ */
+ @Override
+ public BigFraction parse(final String source, final ParsePosition pos) {
+ final int initialIndex = pos.getIndex();
+
+ // parse whitespace
+ parseAndIgnoreWhitespace(source, pos);
+
+ // parse numerator
+ final BigInteger num = parseNextBigInteger(source, pos);
+ if (num == null) {
+ // invalid integer number
+ // set index back to initial, error index should already be set
+ // character examined.
+ pos.setIndex(initialIndex);
+ return null;
+ }
+
+ // parse '/'
+ final int startIndex = pos.getIndex();
+ final char c = parseNextCharacter(source, pos);
+ switch (c) {
+ case 0:
+ // no '/'
+ // return num as a BigFraction
+ return new BigFraction(num);
+ case '/':
+ // found '/', continue parsing denominator
+ break;
+ default:
+ // invalid '/'
+ // set index back to initial, error index should be the last
+ // character examined.
+ pos.setIndex(initialIndex);
+ pos.setErrorIndex(startIndex);
+ return null;
+ }
+
+ // parse whitespace
+ parseAndIgnoreWhitespace(source, pos);
+
+ // parse denominator
+ final BigInteger den = parseNextBigInteger(source, pos);
+ if (den == null) {
+ // invalid integer number
+ // set index back to initial, error index should already be set
+ // character examined.
+ pos.setIndex(initialIndex);
+ return null;
+ }
+
+ return new BigFraction(num, den);
+ }
+
+ /**
+ * Parses a string to produce a <code>BigInteger</code>.
+ *
+ * @param source the string to parse
+ * @param pos input/output parsing parameter.
+ * @return a parsed <code>BigInteger</code> or null if string does not contain a BigInteger at
+ * the specified position
+ */
+ protected BigInteger parseNextBigInteger(final String source, final ParsePosition pos) {
+
+ final int start = pos.getIndex();
+ int end = (source.charAt(start) == '-') ? (start + 1) : start;
+ while ((end < source.length()) && Character.isDigit(source.charAt(end))) {
+ ++end;
+ }
+
+ try {
+ BigInteger n = new BigInteger(source.substring(start, end));
+ pos.setIndex(end);
+ return n;
+ } catch (NumberFormatException nfe) {
+ pos.setErrorIndex(start);
+ return null;
+ }
+ }
+}
diff --git a/src/main/java/org/apache/commons/math3/fraction/Fraction.java b/src/main/java/org/apache/commons/math3/fraction/Fraction.java
new file mode 100644
index 0000000..9b04e12
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/fraction/Fraction.java
@@ -0,0 +1,669 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math3.fraction;
+
+import org.apache.commons.math3.FieldElement;
+import org.apache.commons.math3.exception.MathArithmeticException;
+import org.apache.commons.math3.exception.NullArgumentException;
+import org.apache.commons.math3.exception.util.LocalizedFormats;
+import org.apache.commons.math3.util.ArithmeticUtils;
+import org.apache.commons.math3.util.FastMath;
+
+import java.io.Serializable;
+import java.math.BigInteger;
+
+/**
+ * Representation of a rational number.
+ *
+ * <p>implements Serializable since 2.0
+ *
+ * @since 1.1
+ */
+public class Fraction extends Number
+ implements FieldElement<Fraction>, Comparable<Fraction>, Serializable {
+
+ /** A fraction representing "2 / 1". */
+ public static final Fraction TWO = new Fraction(2, 1);
+
+ /** A fraction representing "1". */
+ public static final Fraction ONE = new Fraction(1, 1);
+
+ /** A fraction representing "0". */
+ public static final Fraction ZERO = new Fraction(0, 1);
+
+ /** A fraction representing "4/5". */
+ public static final Fraction FOUR_FIFTHS = new Fraction(4, 5);
+
+ /** A fraction representing "1/5". */
+ public static final Fraction ONE_FIFTH = new Fraction(1, 5);
+
+ /** A fraction representing "1/2". */
+ public static final Fraction ONE_HALF = new Fraction(1, 2);
+
+ /** A fraction representing "1/4". */
+ public static final Fraction ONE_QUARTER = new Fraction(1, 4);
+
+ /** A fraction representing "1/3". */
+ public static final Fraction ONE_THIRD = new Fraction(1, 3);
+
+ /** A fraction representing "3/5". */
+ public static final Fraction THREE_FIFTHS = new Fraction(3, 5);
+
+ /** A fraction representing "3/4". */
+ public static final Fraction THREE_QUARTERS = new Fraction(3, 4);
+
+ /** A fraction representing "2/5". */
+ public static final Fraction TWO_FIFTHS = new Fraction(2, 5);
+
+ /** A fraction representing "2/4". */
+ public static final Fraction TWO_QUARTERS = new Fraction(2, 4);
+
+ /** A fraction representing "2/3". */
+ public static final Fraction TWO_THIRDS = new Fraction(2, 3);
+
+ /** A fraction representing "-1 / 1". */
+ public static final Fraction MINUS_ONE = new Fraction(-1, 1);
+
+ /** Serializable version identifier */
+ private static final long serialVersionUID = 3698073679419233275L;
+
+ /** The default epsilon used for convergence. */
+ private static final double DEFAULT_EPSILON = 1e-5;
+
+ /** The denominator. */
+ private final int denominator;
+
+ /** The numerator. */
+ private final int numerator;
+
+ /**
+ * Create a fraction given the double value.
+ *
+ * @param value the double value to convert to a fraction.
+ * @throws FractionConversionException if the continued fraction failed to converge.
+ */
+ public Fraction(double value) throws FractionConversionException {
+ this(value, DEFAULT_EPSILON, 100);
+ }
+
+ /**
+ * Create a fraction given the double value and maximum error allowed.
+ *
+ * <p>References:
+ *
+ * <ul>
+ * <li><a href="http://mathworld.wolfram.com/ContinuedFraction.html">Continued Fraction</a>
+ * equations (11) and (22)-(26)
+ * </ul>
+ *
+ * @param value the double value to convert to a fraction.
+ * @param epsilon maximum error allowed. The resulting fraction is within {@code epsilon} of
+ * {@code value}, in absolute terms.
+ * @param maxIterations maximum number of convergents
+ * @throws FractionConversionException if the continued fraction failed to converge.
+ */
+ public Fraction(double value, double epsilon, int maxIterations)
+ throws FractionConversionException {
+ this(value, epsilon, Integer.MAX_VALUE, maxIterations);
+ }
+
+ /**
+ * Create a fraction given the double value and maximum denominator.
+ *
+ * <p>References:
+ *
+ * <ul>
+ * <li><a href="http://mathworld.wolfram.com/ContinuedFraction.html">Continued Fraction</a>
+ * equations (11) and (22)-(26)
+ * </ul>
+ *
+ * @param value the double value to convert to a fraction.
+ * @param maxDenominator The maximum allowed value for denominator
+ * @throws FractionConversionException if the continued fraction failed to converge
+ */
+ public Fraction(double value, int maxDenominator) throws FractionConversionException {
+ this(value, 0, maxDenominator, 100);
+ }
+
+ /**
+ * Create a fraction given the double value and either the maximum error allowed or the maximum
+ * number of denominator digits.
+ *
+ * <p>NOTE: This constructor is called with EITHER - a valid epsilon value and the
+ * maxDenominator set to Integer.MAX_VALUE (that way the maxDenominator has no effect). OR - a
+ * valid maxDenominator value and the epsilon value set to zero (that way epsilon only has
+ * effect if there is an exact match before the maxDenominator value is reached).
+ *
+ * <p>It has been done this way so that the same code can be (re)used for both scenarios.
+ * However this could be confusing to users if it were part of the public API and this
+ * constructor should therefore remain PRIVATE. See JIRA issue ticket MATH-181 for more details:
+ *
+ * <p>https://issues.apache.org/jira/browse/MATH-181
+ *
+ * @param value the double value to convert to a fraction.
+ * @param epsilon maximum error allowed. The resulting fraction is within {@code epsilon} of
+ * {@code value}, in absolute terms.
+ * @param maxDenominator maximum denominator value allowed.
+ * @param maxIterations maximum number of convergents
+ * @throws FractionConversionException if the continued fraction failed to converge.
+ */
+ private Fraction(double value, double epsilon, int maxDenominator, int maxIterations)
+ throws FractionConversionException {
+ long overflow = Integer.MAX_VALUE;
+ double r0 = value;
+ long a0 = (long) FastMath.floor(r0);
+ if (FastMath.abs(a0) > overflow) {
+ throw new FractionConversionException(value, a0, 1l);
+ }
+
+ // check for (almost) integer arguments, which should not go to iterations.
+ if (FastMath.abs(a0 - value) < epsilon) {
+ this.numerator = (int) a0;
+ this.denominator = 1;
+ return;
+ }
+
+ long p0 = 1;
+ long q0 = 0;
+ long p1 = a0;
+ long q1 = 1;
+
+ long p2 = 0;
+ long q2 = 1;
+
+ int n = 0;
+ boolean stop = false;
+ do {
+ ++n;
+ double r1 = 1.0 / (r0 - a0);
+ long a1 = (long) FastMath.floor(r1);
+ p2 = (a1 * p1) + p0;
+ q2 = (a1 * q1) + q0;
+
+ if ((FastMath.abs(p2) > overflow) || (FastMath.abs(q2) > overflow)) {
+ // in maxDenominator mode, if the last fraction was very close to the actual value
+ // q2 may overflow in the next iteration; in this case return the last one.
+ if (epsilon == 0.0 && FastMath.abs(q1) < maxDenominator) {
+ break;
+ }
+ throw new FractionConversionException(value, p2, q2);
+ }
+
+ double convergent = (double) p2 / (double) q2;
+ if (n < maxIterations
+ && FastMath.abs(convergent - value) > epsilon
+ && q2 < maxDenominator) {
+ p0 = p1;
+ p1 = p2;
+ q0 = q1;
+ q1 = q2;
+ a0 = a1;
+ r0 = r1;
+ } else {
+ stop = true;
+ }
+ } while (!stop);
+
+ if (n >= maxIterations) {
+ throw new FractionConversionException(value, maxIterations);
+ }
+
+ if (q2 < maxDenominator) {
+ this.numerator = (int) p2;
+ this.denominator = (int) q2;
+ } else {
+ this.numerator = (int) p1;
+ this.denominator = (int) q1;
+ }
+ }
+
+ /**
+ * Create a fraction from an int. The fraction is num / 1.
+ *
+ * @param num the numerator.
+ */
+ public Fraction(int num) {
+ this(num, 1);
+ }
+
+ /**
+ * Create a fraction given the numerator and denominator. The fraction is reduced to lowest
+ * terms.
+ *
+ * @param num the numerator.
+ * @param den the denominator.
+ * @throws MathArithmeticException if the denominator is {@code zero}
+ */
+ public Fraction(int num, int den) {
+ if (den == 0) {
+ throw new MathArithmeticException(
+ LocalizedFormats.ZERO_DENOMINATOR_IN_FRACTION, num, den);
+ }
+ if (den < 0) {
+ if (num == Integer.MIN_VALUE || den == Integer.MIN_VALUE) {
+ throw new MathArithmeticException(LocalizedFormats.OVERFLOW_IN_FRACTION, num, den);
+ }
+ num = -num;
+ den = -den;
+ }
+ // reduce numerator and denominator by greatest common denominator.
+ final int d = ArithmeticUtils.gcd(num, den);
+ if (d > 1) {
+ num /= d;
+ den /= d;
+ }
+
+ // move sign to numerator.
+ if (den < 0) {
+ num = -num;
+ den = -den;
+ }
+ this.numerator = num;
+ this.denominator = den;
+ }
+
+ /**
+ * Returns the absolute value of this fraction.
+ *
+ * @return the absolute value.
+ */
+ public Fraction abs() {
+ Fraction ret;
+ if (numerator >= 0) {
+ ret = this;
+ } else {
+ ret = negate();
+ }
+ return ret;
+ }
+
+ /**
+ * Compares this object to another based on size.
+ *
+ * @param object the object to compare to
+ * @return -1 if this is less than {@code object}, +1 if this is greater than {@code object}, 0
+ * if they are equal.
+ */
+ public int compareTo(Fraction object) {
+ long nOd = ((long) numerator) * object.denominator;
+ long dOn = ((long) denominator) * object.numerator;
+ return (nOd < dOn) ? -1 : ((nOd > dOn) ? +1 : 0);
+ }
+
+ /**
+ * Gets the fraction as a {@code double}. This calculates the fraction as the numerator divided
+ * by denominator.
+ *
+ * @return the fraction as a {@code double}
+ */
+ @Override
+ public double doubleValue() {
+ return (double) numerator / (double) denominator;
+ }
+
+ /**
+ * Test for the equality of two fractions. If the lowest term numerator and denominators are the
+ * same for both fractions, the two fractions are considered to be equal.
+ *
+ * @param other fraction to test for equality to this fraction
+ * @return true if two fractions are equal, false if object is {@code null}, not an instance of
+ * {@link Fraction}, or not equal to this fraction instance.
+ */
+ @Override
+ public boolean equals(Object other) {
+ if (this == other) {
+ return true;
+ }
+ if (other instanceof Fraction) {
+ // since fractions are always in lowest terms, numerators and
+ // denominators can be compared directly for equality.
+ Fraction rhs = (Fraction) other;
+ return (numerator == rhs.numerator) && (denominator == rhs.denominator);
+ }
+ return false;
+ }
+
+ /**
+ * Gets the fraction as a {@code float}. This calculates the fraction as the numerator divided
+ * by denominator.
+ *
+ * @return the fraction as a {@code float}
+ */
+ @Override
+ public float floatValue() {
+ return (float) doubleValue();
+ }
+
+ /**
+ * Access the denominator.
+ *
+ * @return the denominator.
+ */
+ public int getDenominator() {
+ return denominator;
+ }
+
+ /**
+ * Access the numerator.
+ *
+ * @return the numerator.
+ */
+ public int getNumerator() {
+ return numerator;
+ }
+
+ /**
+ * Gets a hashCode for the fraction.
+ *
+ * @return a hash code value for this object
+ */
+ @Override
+ public int hashCode() {
+ return 37 * (37 * 17 + numerator) + denominator;
+ }
+
+ /**
+ * Gets the fraction as an {@code int}. This returns the whole number part of the fraction.
+ *
+ * @return the whole number fraction part
+ */
+ @Override
+ public int intValue() {
+ return (int) doubleValue();
+ }
+
+ /**
+ * Gets the fraction as a {@code long}. This returns the whole number part of the fraction.
+ *
+ * @return the whole number fraction part
+ */
+ @Override
+ public long longValue() {
+ return (long) doubleValue();
+ }
+
+ /**
+ * Return the additive inverse of this fraction.
+ *
+ * @return the negation of this fraction.
+ */
+ public Fraction negate() {
+ if (numerator == Integer.MIN_VALUE) {
+ throw new MathArithmeticException(
+ LocalizedFormats.OVERFLOW_IN_FRACTION, numerator, denominator);
+ }
+ return new Fraction(-numerator, denominator);
+ }
+
+ /**
+ * Return the multiplicative inverse of this fraction.
+ *
+ * @return the reciprocal fraction
+ */
+ public Fraction reciprocal() {
+ return new Fraction(denominator, numerator);
+ }
+
+ /**
+ * Adds the value of this fraction to another, returning the result in reduced form. The
+ * algorithm follows Knuth, 4.5.1.
+ *
+ * @param fraction the fraction to add, must not be {@code null}
+ * @return a {@code Fraction} instance with the resulting values
+ * @throws NullArgumentException if the fraction is {@code null}
+ * @throws MathArithmeticException if the resulting numerator or denominator exceeds {@code
+ * Integer.MAX_VALUE}
+ */
+ public Fraction add(Fraction fraction) {
+ return addSub(fraction, true /* add */);
+ }
+
+ /**
+ * Add an integer to the fraction.
+ *
+ * @param i the {@code integer} to add.
+ * @return this + i
+ */
+ public Fraction add(final int i) {
+ return new Fraction(numerator + i * denominator, denominator);
+ }
+
+ /**
+ * Subtracts the value of another fraction from the value of this one, returning the result in
+ * reduced form.
+ *
+ * @param fraction the fraction to subtract, must not be {@code null}
+ * @return a {@code Fraction} instance with the resulting values
+ * @throws NullArgumentException if the fraction is {@code null}
+ * @throws MathArithmeticException if the resulting numerator or denominator cannot be
+ * represented in an {@code int}.
+ */
+ public Fraction subtract(Fraction fraction) {
+ return addSub(fraction, false /* subtract */);
+ }
+
+ /**
+ * Subtract an integer from the fraction.
+ *
+ * @param i the {@code integer} to subtract.
+ * @return this - i
+ */
+ public Fraction subtract(final int i) {
+ return new Fraction(numerator - i * denominator, denominator);
+ }
+
+ /**
+ * Implement add and subtract using algorithm described in Knuth 4.5.1.
+ *
+ * @param fraction the fraction to subtract, must not be {@code null}
+ * @param isAdd true to add, false to subtract
+ * @return a {@code Fraction} instance with the resulting values
+ * @throws NullArgumentException if the fraction is {@code null}
+ * @throws MathArithmeticException if the resulting numerator or denominator cannot be
+ * represented in an {@code int}.
+ */
+ private Fraction addSub(Fraction fraction, boolean isAdd) {
+ if (fraction == null) {
+ throw new NullArgumentException(LocalizedFormats.FRACTION);
+ }
+ // zero is identity for addition.
+ if (numerator == 0) {
+ return isAdd ? fraction : fraction.negate();
+ }
+ if (fraction.numerator == 0) {
+ return this;
+ }
+ // if denominators are randomly distributed, d1 will be 1 about 61%
+ // of the time.
+ int d1 = ArithmeticUtils.gcd(denominator, fraction.denominator);
+ if (d1 == 1) {
+ // result is ( (u*v' +/- u'v) / u'v')
+ int uvp = ArithmeticUtils.mulAndCheck(numerator, fraction.denominator);
+ int upv = ArithmeticUtils.mulAndCheck(fraction.numerator, denominator);
+ return new Fraction(
+ isAdd
+ ? ArithmeticUtils.addAndCheck(uvp, upv)
+ : ArithmeticUtils.subAndCheck(uvp, upv),
+ ArithmeticUtils.mulAndCheck(denominator, fraction.denominator));
+ }
+ // the quantity 't' requires 65 bits of precision; see knuth 4.5.1
+ // exercise 7. we're going to use a BigInteger.
+ // t = u(v'/d1) +/- v(u'/d1)
+ BigInteger uvp =
+ BigInteger.valueOf(numerator)
+ .multiply(BigInteger.valueOf(fraction.denominator / d1));
+ BigInteger upv =
+ BigInteger.valueOf(fraction.numerator)
+ .multiply(BigInteger.valueOf(denominator / d1));
+ BigInteger t = isAdd ? uvp.add(upv) : uvp.subtract(upv);
+ // but d2 doesn't need extra precision because
+ // d2 = gcd(t,d1) = gcd(t mod d1, d1)
+ int tmodd1 = t.mod(BigInteger.valueOf(d1)).intValue();
+ int d2 = (tmodd1 == 0) ? d1 : ArithmeticUtils.gcd(tmodd1, d1);
+
+ // result is (t/d2) / (u'/d1)(v'/d2)
+ BigInteger w = t.divide(BigInteger.valueOf(d2));
+ if (w.bitLength() > 31) {
+ throw new MathArithmeticException(
+ LocalizedFormats.NUMERATOR_OVERFLOW_AFTER_MULTIPLY, w);
+ }
+ return new Fraction(
+ w.intValue(),
+ ArithmeticUtils.mulAndCheck(denominator / d1, fraction.denominator / d2));
+ }
+
+ /**
+ * Multiplies the value of this fraction by another, returning the result in reduced form.
+ *
+ * @param fraction the fraction to multiply by, must not be {@code null}
+ * @return a {@code Fraction} instance with the resulting values
+ * @throws NullArgumentException if the fraction is {@code null}
+ * @throws MathArithmeticException if the resulting numerator or denominator exceeds {@code
+ * Integer.MAX_VALUE}
+ */
+ public Fraction multiply(Fraction fraction) {
+ if (fraction == null) {
+ throw new NullArgumentException(LocalizedFormats.FRACTION);
+ }
+ if (numerator == 0 || fraction.numerator == 0) {
+ return ZERO;
+ }
+ // knuth 4.5.1
+ // make sure we don't overflow unless the result *must* overflow.
+ int d1 = ArithmeticUtils.gcd(numerator, fraction.denominator);
+ int d2 = ArithmeticUtils.gcd(fraction.numerator, denominator);
+ return getReducedFraction(
+ ArithmeticUtils.mulAndCheck(numerator / d1, fraction.numerator / d2),
+ ArithmeticUtils.mulAndCheck(denominator / d2, fraction.denominator / d1));
+ }
+
+ /**
+ * Multiply the fraction by an integer.
+ *
+ * @param i the {@code integer} to multiply by.
+ * @return this * i
+ */
+ public Fraction multiply(final int i) {
+ return multiply(new Fraction(i));
+ }
+
+ /**
+ * Divide the value of this fraction by another.
+ *
+ * @param fraction the fraction to divide by, must not be {@code null}
+ * @return a {@code Fraction} instance with the resulting values
+ * @throws IllegalArgumentException if the fraction is {@code null}
+ * @throws MathArithmeticException if the fraction to divide by is zero
+ * @throws MathArithmeticException if the resulting numerator or denominator exceeds {@code
+ * Integer.MAX_VALUE}
+ */
+ public Fraction divide(Fraction fraction) {
+ if (fraction == null) {
+ throw new NullArgumentException(LocalizedFormats.FRACTION);
+ }
+ if (fraction.numerator == 0) {
+ throw new MathArithmeticException(
+ LocalizedFormats.ZERO_FRACTION_TO_DIVIDE_BY,
+ fraction.numerator,
+ fraction.denominator);
+ }
+ return multiply(fraction.reciprocal());
+ }
+
+ /**
+ * Divide the fraction by an integer.
+ *
+ * @param i the {@code integer} to divide by.
+ * @return this * i
+ */
+ public Fraction divide(final int i) {
+ return divide(new Fraction(i));
+ }
+
+ /**
+ * Gets the fraction percentage as a {@code double}. This calculates the fraction as the
+ * numerator divided by denominator multiplied by 100.
+ *
+ * @return the fraction percentage as a {@code double}.
+ */
+ public double percentageValue() {
+ return 100 * doubleValue();
+ }
+
+ /**
+ * Creates a {@code Fraction} instance with the 2 parts of a fraction Y/Z.
+ *
+ * <p>Any negative signs are resolved to be on the numerator.
+ *
+ * @param numerator the numerator, for example the three in 'three sevenths'
+ * @param denominator the denominator, for example the seven in 'three sevenths'
+ * @return a new fraction instance, with the numerator and denominator reduced
+ * @throws MathArithmeticException if the denominator is {@code zero}
+ */
+ public static Fraction getReducedFraction(int numerator, int denominator) {
+ if (denominator == 0) {
+ throw new MathArithmeticException(
+ LocalizedFormats.ZERO_DENOMINATOR_IN_FRACTION, numerator, denominator);
+ }
+ if (numerator == 0) {
+ return ZERO; // normalize zero.
+ }
+ // allow 2^k/-2^31 as a valid fraction (where k>0)
+ if (denominator == Integer.MIN_VALUE && (numerator & 1) == 0) {
+ numerator /= 2;
+ denominator /= 2;
+ }
+ if (denominator < 0) {
+ if (numerator == Integer.MIN_VALUE || denominator == Integer.MIN_VALUE) {
+ throw new MathArithmeticException(
+ LocalizedFormats.OVERFLOW_IN_FRACTION, numerator, denominator);
+ }
+ numerator = -numerator;
+ denominator = -denominator;
+ }
+ // simplify fraction.
+ int gcd = ArithmeticUtils.gcd(numerator, denominator);
+ numerator /= gcd;
+ denominator /= gcd;
+ return new Fraction(numerator, denominator);
+ }
+
+ /**
+ * Returns the {@code String} representing this fraction, ie "num / dem" or just "num" if the
+ * denominator is one.
+ *
+ * @return a string representation of the fraction.
+ * @see java.lang.Object#toString()
+ */
+ @Override
+ public String toString() {
+ String str = null;
+ if (denominator == 1) {
+ str = Integer.toString(numerator);
+ } else if (numerator == 0) {
+ str = "0";
+ } else {
+ str = numerator + " / " + denominator;
+ }
+ return str;
+ }
+
+ /** {@inheritDoc} */
+ public FractionField getField() {
+ return FractionField.getInstance();
+ }
+}
diff --git a/src/main/java/org/apache/commons/math3/fraction/FractionConversionException.java b/src/main/java/org/apache/commons/math3/fraction/FractionConversionException.java
new file mode 100644
index 0000000..cc34cae
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/fraction/FractionConversionException.java
@@ -0,0 +1,56 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math3.fraction;
+
+import org.apache.commons.math3.exception.ConvergenceException;
+import org.apache.commons.math3.exception.util.LocalizedFormats;
+
+/**
+ * Error thrown when a double value cannot be converted to a fraction in the allowed number of
+ * iterations.
+ *
+ * @since 1.2
+ */
+public class FractionConversionException extends ConvergenceException {
+
+ /** Serializable version identifier. */
+ private static final long serialVersionUID = -4661812640132576263L;
+
+ /**
+ * Constructs an exception with specified formatted detail message. Message formatting is
+ * delegated to {@link java.text.MessageFormat}.
+ *
+ * @param value double value to convert
+ * @param maxIterations maximal number of iterations allowed
+ */
+ public FractionConversionException(double value, int maxIterations) {
+ super(LocalizedFormats.FAILED_FRACTION_CONVERSION, value, maxIterations);
+ }
+
+ /**
+ * Constructs an exception with specified formatted detail message. Message formatting is
+ * delegated to {@link java.text.MessageFormat}.
+ *
+ * @param value double value to convert
+ * @param p current numerator
+ * @param q current denominator
+ */
+ public FractionConversionException(double value, long p, long q) {
+ super(LocalizedFormats.FRACTION_CONVERSION_OVERFLOW, value, p, q);
+ }
+}
diff --git a/src/main/java/org/apache/commons/math3/fraction/FractionField.java b/src/main/java/org/apache/commons/math3/fraction/FractionField.java
new file mode 100644
index 0000000..bc00716
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/fraction/FractionField.java
@@ -0,0 +1,87 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math3.fraction;
+
+import org.apache.commons.math3.Field;
+import org.apache.commons.math3.FieldElement;
+
+import java.io.Serializable;
+
+/**
+ * Representation of the fractional numbers field.
+ *
+ * <p>This class is a singleton.
+ *
+ * @see Fraction
+ * @since 2.0
+ */
+public class FractionField implements Field<Fraction>, Serializable {
+
+ /** Serializable version identifier */
+ private static final long serialVersionUID = -1257768487499119313L;
+
+ /** Private constructor for the singleton. */
+ private FractionField() {}
+
+ /**
+ * Get the unique instance.
+ *
+ * @return the unique instance
+ */
+ public static FractionField getInstance() {
+ return LazyHolder.INSTANCE;
+ }
+
+ /** {@inheritDoc} */
+ public Fraction getOne() {
+ return Fraction.ONE;
+ }
+
+ /** {@inheritDoc} */
+ public Fraction getZero() {
+ return Fraction.ZERO;
+ }
+
+ /** {@inheritDoc} */
+ public Class<? extends FieldElement<Fraction>> getRuntimeClass() {
+ return Fraction.class;
+ }
+
+ // CHECKSTYLE: stop HideUtilityClassConstructor
+ /**
+ * Holder for the instance.
+ *
+ * <p>We use here the Initialization On Demand Holder Idiom.
+ */
+ private static class LazyHolder {
+ /** Cached field instance. */
+ private static final FractionField INSTANCE = new FractionField();
+ }
+
+ // CHECKSTYLE: resume HideUtilityClassConstructor
+
+ /**
+ * Handle deserialization of the singleton.
+ *
+ * @return the singleton instance
+ */
+ private Object readResolve() {
+ // return the singleton instance
+ return LazyHolder.INSTANCE;
+ }
+}
diff --git a/src/main/java/org/apache/commons/math3/fraction/FractionFormat.java b/src/main/java/org/apache/commons/math3/fraction/FractionFormat.java
new file mode 100644
index 0000000..181de7e
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/fraction/FractionFormat.java
@@ -0,0 +1,270 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math3.fraction;
+
+import org.apache.commons.math3.exception.MathIllegalArgumentException;
+import org.apache.commons.math3.exception.MathParseException;
+import org.apache.commons.math3.exception.util.LocalizedFormats;
+
+import java.text.FieldPosition;
+import java.text.NumberFormat;
+import java.text.ParsePosition;
+import java.util.Locale;
+
+/**
+ * Formats a Fraction number in proper format or improper format. The number format for each of the
+ * whole number, numerator and, denominator can be configured.
+ *
+ * @since 1.1
+ */
+public class FractionFormat extends AbstractFormat {
+
+ /** Serializable version identifier */
+ private static final long serialVersionUID = 3008655719530972611L;
+
+ /**
+ * Create an improper formatting instance with the default number format for the numerator and
+ * denominator.
+ */
+ public FractionFormat() {}
+
+ /**
+ * Create an improper formatting instance with a custom number format for both the numerator and
+ * denominator.
+ *
+ * @param format the custom format for both the numerator and denominator.
+ */
+ public FractionFormat(final NumberFormat format) {
+ super(format);
+ }
+
+ /**
+ * Create an improper formatting instance with a custom number format for the numerator and a
+ * custom number format for the denominator.
+ *
+ * @param numeratorFormat the custom format for the numerator.
+ * @param denominatorFormat the custom format for the denominator.
+ */
+ public FractionFormat(
+ final NumberFormat numeratorFormat, final NumberFormat denominatorFormat) {
+ super(numeratorFormat, denominatorFormat);
+ }
+
+ /**
+ * Get the set of locales for which complex formats are available. This is the same set as the
+ * {@link NumberFormat} set.
+ *
+ * @return available complex format locales.
+ */
+ public static Locale[] getAvailableLocales() {
+ return NumberFormat.getAvailableLocales();
+ }
+
+ /**
+ * This static method calls formatFraction() on a default instance of FractionFormat.
+ *
+ * @param f Fraction object to format
+ * @return a formatted fraction in proper form.
+ */
+ public static String formatFraction(Fraction f) {
+ return getImproperInstance().format(f);
+ }
+
+ /**
+ * Returns the default complex format for the current locale.
+ *
+ * @return the default complex format.
+ */
+ public static FractionFormat getImproperInstance() {
+ return getImproperInstance(Locale.getDefault());
+ }
+
+ /**
+ * Returns the default complex format for the given locale.
+ *
+ * @param locale the specific locale used by the format.
+ * @return the complex format specific to the given locale.
+ */
+ public static FractionFormat getImproperInstance(final Locale locale) {
+ return new FractionFormat(getDefaultNumberFormat(locale));
+ }
+
+ /**
+ * Returns the default complex format for the current locale.
+ *
+ * @return the default complex format.
+ */
+ public static FractionFormat getProperInstance() {
+ return getProperInstance(Locale.getDefault());
+ }
+
+ /**
+ * Returns the default complex format for the given locale.
+ *
+ * @param locale the specific locale used by the format.
+ * @return the complex format specific to the given locale.
+ */
+ public static FractionFormat getProperInstance(final Locale locale) {
+ return new ProperFractionFormat(getDefaultNumberFormat(locale));
+ }
+
+ /**
+ * Create a default number format. The default number format is based on {@link
+ * NumberFormat#getNumberInstance(java.util.Locale)} with the only customizing is the maximum
+ * number of fraction digits, which is set to 0.
+ *
+ * @return the default number format.
+ */
+ protected static NumberFormat getDefaultNumberFormat() {
+ return getDefaultNumberFormat(Locale.getDefault());
+ }
+
+ /**
+ * Formats a {@link Fraction} object to produce a string. The fraction is output in improper
+ * format.
+ *
+ * @param fraction the object to format.
+ * @param toAppendTo where the text is to be appended
+ * @param pos On input: an alignment field, if desired. On output: the offsets of the alignment
+ * field
+ * @return the value passed in as toAppendTo.
+ */
+ public StringBuffer format(
+ final Fraction fraction, final StringBuffer toAppendTo, final FieldPosition pos) {
+
+ pos.setBeginIndex(0);
+ pos.setEndIndex(0);
+
+ getNumeratorFormat().format(fraction.getNumerator(), toAppendTo, pos);
+ toAppendTo.append(" / ");
+ getDenominatorFormat().format(fraction.getDenominator(), toAppendTo, pos);
+
+ return toAppendTo;
+ }
+
+ /**
+ * Formats an object and appends the result to a StringBuffer. <code>obj</code> must be either a
+ * {@link Fraction} object or a {@link Number} object. Any other type of object will result in
+ * an {@link IllegalArgumentException} being thrown.
+ *
+ * @param obj the object to format.
+ * @param toAppendTo where the text is to be appended
+ * @param pos On input: an alignment field, if desired. On output: the offsets of the alignment
+ * field
+ * @return the value passed in as toAppendTo.
+ * @see java.text.Format#format(java.lang.Object, java.lang.StringBuffer,
+ * java.text.FieldPosition)
+ * @throws FractionConversionException if the number cannot be converted to a fraction
+ * @throws MathIllegalArgumentException if <code>obj</code> is not a valid type.
+ */
+ @Override
+ public StringBuffer format(
+ final Object obj, final StringBuffer toAppendTo, final FieldPosition pos)
+ throws FractionConversionException, MathIllegalArgumentException {
+ StringBuffer ret = null;
+
+ if (obj instanceof Fraction) {
+ ret = format((Fraction) obj, toAppendTo, pos);
+ } else if (obj instanceof Number) {
+ ret = format(new Fraction(((Number) obj).doubleValue()), toAppendTo, pos);
+ } else {
+ throw new MathIllegalArgumentException(
+ LocalizedFormats.CANNOT_FORMAT_OBJECT_TO_FRACTION);
+ }
+
+ return ret;
+ }
+
+ /**
+ * Parses a string to produce a {@link Fraction} object.
+ *
+ * @param source the string to parse
+ * @return the parsed {@link Fraction} object.
+ * @exception MathParseException if the beginning of the specified string cannot be parsed.
+ */
+ @Override
+ public Fraction parse(final String source) throws MathParseException {
+ final ParsePosition parsePosition = new ParsePosition(0);
+ final Fraction result = parse(source, parsePosition);
+ if (parsePosition.getIndex() == 0) {
+ throw new MathParseException(source, parsePosition.getErrorIndex(), Fraction.class);
+ }
+ return result;
+ }
+
+ /**
+ * Parses a string to produce a {@link Fraction} object. This method expects the string to be
+ * formatted as an improper fraction.
+ *
+ * @param source the string to parse
+ * @param pos input/output parsing parameter.
+ * @return the parsed {@link Fraction} object.
+ */
+ @Override
+ public Fraction parse(final String source, final ParsePosition pos) {
+ final int initialIndex = pos.getIndex();
+
+ // parse whitespace
+ parseAndIgnoreWhitespace(source, pos);
+
+ // parse numerator
+ final Number num = getNumeratorFormat().parse(source, pos);
+ if (num == null) {
+ // invalid integer number
+ // set index back to initial, error index should already be set
+ // character examined.
+ pos.setIndex(initialIndex);
+ return null;
+ }
+
+ // parse '/'
+ final int startIndex = pos.getIndex();
+ final char c = parseNextCharacter(source, pos);
+ switch (c) {
+ case 0:
+ // no '/'
+ // return num as a fraction
+ return new Fraction(num.intValue(), 1);
+ case '/':
+ // found '/', continue parsing denominator
+ break;
+ default:
+ // invalid '/'
+ // set index back to initial, error index should be the last
+ // character examined.
+ pos.setIndex(initialIndex);
+ pos.setErrorIndex(startIndex);
+ return null;
+ }
+
+ // parse whitespace
+ parseAndIgnoreWhitespace(source, pos);
+
+ // parse denominator
+ final Number den = getDenominatorFormat().parse(source, pos);
+ if (den == null) {
+ // invalid integer number
+ // set index back to initial, error index should already be set
+ // character examined.
+ pos.setIndex(initialIndex);
+ return null;
+ }
+
+ return new Fraction(num.intValue(), den.intValue());
+ }
+}
diff --git a/src/main/java/org/apache/commons/math3/fraction/ProperBigFractionFormat.java b/src/main/java/org/apache/commons/math3/fraction/ProperBigFractionFormat.java
new file mode 100644
index 0000000..6e034b5
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/fraction/ProperBigFractionFormat.java
@@ -0,0 +1,239 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math3.fraction;
+
+import org.apache.commons.math3.exception.NullArgumentException;
+import org.apache.commons.math3.exception.util.LocalizedFormats;
+
+import java.math.BigInteger;
+import java.text.FieldPosition;
+import java.text.NumberFormat;
+import java.text.ParsePosition;
+
+/**
+ * Formats a BigFraction number in proper format. The number format for each of the whole number,
+ * numerator and, denominator can be configured.
+ *
+ * <p>Minus signs are only allowed in the whole number part - i.e., "-3 1/2" is legitimate and
+ * denotes -7/2, but "-3 -1/2" is invalid and will result in a <code>ParseException</code>.
+ *
+ * @since 1.1
+ */
+public class ProperBigFractionFormat extends BigFractionFormat {
+
+ /** Serializable version identifier */
+ private static final long serialVersionUID = -6337346779577272307L;
+
+ /** The format used for the whole number. */
+ private NumberFormat wholeFormat;
+
+ /**
+ * Create a proper formatting instance with the default number format for the whole, numerator,
+ * and denominator.
+ */
+ public ProperBigFractionFormat() {
+ this(getDefaultNumberFormat());
+ }
+
+ /**
+ * Create a proper formatting instance with a custom number format for the whole, numerator, and
+ * denominator.
+ *
+ * @param format the custom format for the whole, numerator, and denominator.
+ */
+ public ProperBigFractionFormat(final NumberFormat format) {
+ this(format, (NumberFormat) format.clone(), (NumberFormat) format.clone());
+ }
+
+ /**
+ * Create a proper formatting instance with a custom number format for each of the whole,
+ * numerator, and denominator.
+ *
+ * @param wholeFormat the custom format for the whole.
+ * @param numeratorFormat the custom format for the numerator.
+ * @param denominatorFormat the custom format for the denominator.
+ */
+ public ProperBigFractionFormat(
+ final NumberFormat wholeFormat,
+ final NumberFormat numeratorFormat,
+ final NumberFormat denominatorFormat) {
+ super(numeratorFormat, denominatorFormat);
+ setWholeFormat(wholeFormat);
+ }
+
+ /**
+ * Formats a {@link BigFraction} object to produce a string. The BigFraction is output in proper
+ * format.
+ *
+ * @param fraction the object to format.
+ * @param toAppendTo where the text is to be appended
+ * @param pos On input: an alignment field, if desired. On output: the offsets of the alignment
+ * field
+ * @return the value passed in as toAppendTo.
+ */
+ @Override
+ public StringBuffer format(
+ final BigFraction fraction, final StringBuffer toAppendTo, final FieldPosition pos) {
+
+ pos.setBeginIndex(0);
+ pos.setEndIndex(0);
+
+ BigInteger num = fraction.getNumerator();
+ BigInteger den = fraction.getDenominator();
+ BigInteger whole = num.divide(den);
+ num = num.remainder(den);
+
+ if (!BigInteger.ZERO.equals(whole)) {
+ getWholeFormat().format(whole, toAppendTo, pos);
+ toAppendTo.append(' ');
+ if (num.compareTo(BigInteger.ZERO) < 0) {
+ num = num.negate();
+ }
+ }
+ getNumeratorFormat().format(num, toAppendTo, pos);
+ toAppendTo.append(" / ");
+ getDenominatorFormat().format(den, toAppendTo, pos);
+
+ return toAppendTo;
+ }
+
+ /**
+ * Access the whole format.
+ *
+ * @return the whole format.
+ */
+ public NumberFormat getWholeFormat() {
+ return wholeFormat;
+ }
+
+ /**
+ * Parses a string to produce a {@link BigFraction} object. This method expects the string to be
+ * formatted as a proper BigFraction.
+ *
+ * <p>Minus signs are only allowed in the whole number part - i.e., "-3 1/2" is legitimate and
+ * denotes -7/2, but "-3 -1/2" is invalid and will result in a <code>ParseException</code>.
+ *
+ * @param source the string to parse
+ * @param pos input/ouput parsing parameter.
+ * @return the parsed {@link BigFraction} object.
+ */
+ @Override
+ public BigFraction parse(final String source, final ParsePosition pos) {
+ // try to parse improper BigFraction
+ BigFraction ret = super.parse(source, pos);
+ if (ret != null) {
+ return ret;
+ }
+
+ final int initialIndex = pos.getIndex();
+
+ // parse whitespace
+ parseAndIgnoreWhitespace(source, pos);
+
+ // parse whole
+ BigInteger whole = parseNextBigInteger(source, pos);
+ if (whole == null) {
+ // invalid integer number
+ // set index back to initial, error index should already be set
+ // character examined.
+ pos.setIndex(initialIndex);
+ return null;
+ }
+
+ // parse whitespace
+ parseAndIgnoreWhitespace(source, pos);
+
+ // parse numerator
+ BigInteger num = parseNextBigInteger(source, pos);
+ if (num == null) {
+ // invalid integer number
+ // set index back to initial, error index should already be set
+ // character examined.
+ pos.setIndex(initialIndex);
+ return null;
+ }
+
+ if (num.compareTo(BigInteger.ZERO) < 0) {
+ // minus signs should be leading, invalid expression
+ pos.setIndex(initialIndex);
+ return null;
+ }
+
+ // parse '/'
+ final int startIndex = pos.getIndex();
+ final char c = parseNextCharacter(source, pos);
+ switch (c) {
+ case 0:
+ // no '/'
+ // return num as a BigFraction
+ return new BigFraction(num);
+ case '/':
+ // found '/', continue parsing denominator
+ break;
+ default:
+ // invalid '/'
+ // set index back to initial, error index should be the last
+ // character examined.
+ pos.setIndex(initialIndex);
+ pos.setErrorIndex(startIndex);
+ return null;
+ }
+
+ // parse whitespace
+ parseAndIgnoreWhitespace(source, pos);
+
+ // parse denominator
+ final BigInteger den = parseNextBigInteger(source, pos);
+ if (den == null) {
+ // invalid integer number
+ // set index back to initial, error index should already be set
+ // character examined.
+ pos.setIndex(initialIndex);
+ return null;
+ }
+
+ if (den.compareTo(BigInteger.ZERO) < 0) {
+ // minus signs must be leading, invalid
+ pos.setIndex(initialIndex);
+ return null;
+ }
+
+ boolean wholeIsNeg = whole.compareTo(BigInteger.ZERO) < 0;
+ if (wholeIsNeg) {
+ whole = whole.negate();
+ }
+ num = whole.multiply(den).add(num);
+ if (wholeIsNeg) {
+ num = num.negate();
+ }
+
+ return new BigFraction(num, den);
+ }
+
+ /**
+ * Modify the whole format.
+ *
+ * @param format The new whole format value.
+ * @throws NullArgumentException if {@code format} is {@code null}.
+ */
+ public void setWholeFormat(final NumberFormat format) {
+ if (format == null) {
+ throw new NullArgumentException(LocalizedFormats.WHOLE_FORMAT);
+ }
+ this.wholeFormat = format;
+ }
+}
diff --git a/src/main/java/org/apache/commons/math3/fraction/ProperFractionFormat.java b/src/main/java/org/apache/commons/math3/fraction/ProperFractionFormat.java
new file mode 100644
index 0000000..3f74f16
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/fraction/ProperFractionFormat.java
@@ -0,0 +1,231 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math3.fraction;
+
+import org.apache.commons.math3.exception.NullArgumentException;
+import org.apache.commons.math3.exception.util.LocalizedFormats;
+import org.apache.commons.math3.util.FastMath;
+import org.apache.commons.math3.util.MathUtils;
+
+import java.text.FieldPosition;
+import java.text.NumberFormat;
+import java.text.ParsePosition;
+
+/**
+ * Formats a Fraction number in proper format. The number format for each of the whole number,
+ * numerator and, denominator can be configured.
+ *
+ * <p>Minus signs are only allowed in the whole number part - i.e., "-3 1/2" is legitimate and
+ * denotes -7/2, but "-3 -1/2" is invalid and will result in a <code>ParseException</code>.
+ *
+ * @since 1.1
+ */
+public class ProperFractionFormat extends FractionFormat {
+
+ /** Serializable version identifier */
+ private static final long serialVersionUID = 760934726031766749L;
+
+ /** The format used for the whole number. */
+ private NumberFormat wholeFormat;
+
+ /**
+ * Create a proper formatting instance with the default number format for the whole, numerator,
+ * and denominator.
+ */
+ public ProperFractionFormat() {
+ this(getDefaultNumberFormat());
+ }
+
+ /**
+ * Create a proper formatting instance with a custom number format for the whole, numerator, and
+ * denominator.
+ *
+ * @param format the custom format for the whole, numerator, and denominator.
+ */
+ public ProperFractionFormat(NumberFormat format) {
+ this(format, (NumberFormat) format.clone(), (NumberFormat) format.clone());
+ }
+
+ /**
+ * Create a proper formatting instance with a custom number format for each of the whole,
+ * numerator, and denominator.
+ *
+ * @param wholeFormat the custom format for the whole.
+ * @param numeratorFormat the custom format for the numerator.
+ * @param denominatorFormat the custom format for the denominator.
+ */
+ public ProperFractionFormat(
+ NumberFormat wholeFormat,
+ NumberFormat numeratorFormat,
+ NumberFormat denominatorFormat) {
+ super(numeratorFormat, denominatorFormat);
+ setWholeFormat(wholeFormat);
+ }
+
+ /**
+ * Formats a {@link Fraction} object to produce a string. The fraction is output in proper
+ * format.
+ *
+ * @param fraction the object to format.
+ * @param toAppendTo where the text is to be appended
+ * @param pos On input: an alignment field, if desired. On output: the offsets of the alignment
+ * field
+ * @return the value passed in as toAppendTo.
+ */
+ @Override
+ public StringBuffer format(Fraction fraction, StringBuffer toAppendTo, FieldPosition pos) {
+
+ pos.setBeginIndex(0);
+ pos.setEndIndex(0);
+
+ int num = fraction.getNumerator();
+ int den = fraction.getDenominator();
+ int whole = num / den;
+ num %= den;
+
+ if (whole != 0) {
+ getWholeFormat().format(whole, toAppendTo, pos);
+ toAppendTo.append(' ');
+ num = FastMath.abs(num);
+ }
+ getNumeratorFormat().format(num, toAppendTo, pos);
+ toAppendTo.append(" / ");
+ getDenominatorFormat().format(den, toAppendTo, pos);
+
+ return toAppendTo;
+ }
+
+ /**
+ * Access the whole format.
+ *
+ * @return the whole format.
+ */
+ public NumberFormat getWholeFormat() {
+ return wholeFormat;
+ }
+
+ /**
+ * Parses a string to produce a {@link Fraction} object. This method expects the string to be
+ * formatted as a proper fraction.
+ *
+ * <p>Minus signs are only allowed in the whole number part - i.e., "-3 1/2" is legitimate and
+ * denotes -7/2, but "-3 -1/2" is invalid and will result in a <code>ParseException</code>.
+ *
+ * @param source the string to parse
+ * @param pos input/ouput parsing parameter.
+ * @return the parsed {@link Fraction} object.
+ */
+ @Override
+ public Fraction parse(String source, ParsePosition pos) {
+ // try to parse improper fraction
+ Fraction ret = super.parse(source, pos);
+ if (ret != null) {
+ return ret;
+ }
+
+ int initialIndex = pos.getIndex();
+
+ // parse whitespace
+ parseAndIgnoreWhitespace(source, pos);
+
+ // parse whole
+ Number whole = getWholeFormat().parse(source, pos);
+ if (whole == null) {
+ // invalid integer number
+ // set index back to initial, error index should already be set
+ // character examined.
+ pos.setIndex(initialIndex);
+ return null;
+ }
+
+ // parse whitespace
+ parseAndIgnoreWhitespace(source, pos);
+
+ // parse numerator
+ Number num = getNumeratorFormat().parse(source, pos);
+ if (num == null) {
+ // invalid integer number
+ // set index back to initial, error index should already be set
+ // character examined.
+ pos.setIndex(initialIndex);
+ return null;
+ }
+
+ if (num.intValue() < 0) {
+ // minus signs should be leading, invalid expression
+ pos.setIndex(initialIndex);
+ return null;
+ }
+
+ // parse '/'
+ int startIndex = pos.getIndex();
+ char c = parseNextCharacter(source, pos);
+ switch (c) {
+ case 0:
+ // no '/'
+ // return num as a fraction
+ return new Fraction(num.intValue(), 1);
+ case '/':
+ // found '/', continue parsing denominator
+ break;
+ default:
+ // invalid '/'
+ // set index back to initial, error index should be the last
+ // character examined.
+ pos.setIndex(initialIndex);
+ pos.setErrorIndex(startIndex);
+ return null;
+ }
+
+ // parse whitespace
+ parseAndIgnoreWhitespace(source, pos);
+
+ // parse denominator
+ Number den = getDenominatorFormat().parse(source, pos);
+ if (den == null) {
+ // invalid integer number
+ // set index back to initial, error index should already be set
+ // character examined.
+ pos.setIndex(initialIndex);
+ return null;
+ }
+
+ if (den.intValue() < 0) {
+ // minus signs must be leading, invalid
+ pos.setIndex(initialIndex);
+ return null;
+ }
+
+ int w = whole.intValue();
+ int n = num.intValue();
+ int d = den.intValue();
+ return new Fraction(((FastMath.abs(w) * d) + n) * MathUtils.copySign(1, w), d);
+ }
+
+ /**
+ * Modify the whole format.
+ *
+ * @param format The new whole format value.
+ * @throws NullArgumentException if {@code format} is {@code null}.
+ */
+ public void setWholeFormat(NumberFormat format) {
+ if (format == null) {
+ throw new NullArgumentException(LocalizedFormats.WHOLE_FORMAT);
+ }
+ this.wholeFormat = format;
+ }
+}
diff --git a/src/main/java/org/apache/commons/math3/fraction/package-info.java b/src/main/java/org/apache/commons/math3/fraction/package-info.java
new file mode 100644
index 0000000..9d05869
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/fraction/package-info.java
@@ -0,0 +1,18 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/** Fraction number type and fraction number formatting. */
+package org.apache.commons.math3.fraction;
diff --git a/src/main/java/org/apache/commons/math3/genetics/AbstractListChromosome.java b/src/main/java/org/apache/commons/math3/genetics/AbstractListChromosome.java
new file mode 100644
index 0000000..60d09c7
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/genetics/AbstractListChromosome.java
@@ -0,0 +1,119 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math3.genetics;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * Chromosome represented by an immutable list of a fixed length.
+ *
+ * @param <T> type of the representation list
+ * @since 2.0
+ */
+public abstract class AbstractListChromosome<T> extends Chromosome {
+
+ /** List representing the chromosome */
+ private final List<T> representation;
+
+ /**
+ * Constructor, copying the input representation.
+ *
+ * @param representation inner representation of the chromosome
+ * @throws InvalidRepresentationException iff the <code>representation</code> can not represent
+ * a valid chromosome
+ */
+ public AbstractListChromosome(final List<T> representation)
+ throws InvalidRepresentationException {
+ this(representation, true);
+ }
+
+ /**
+ * Constructor, copying the input representation.
+ *
+ * @param representation inner representation of the chromosome
+ * @throws InvalidRepresentationException iff the <code>representation</code> can not represent
+ * a valid chromosome
+ */
+ public AbstractListChromosome(final T[] representation) throws InvalidRepresentationException {
+ this(Arrays.asList(representation));
+ }
+
+ /**
+ * Constructor.
+ *
+ * @param representation inner representation of the chromosome
+ * @param copyList if {@code true}, the representation will be copied, otherwise it will be
+ * referenced.
+ * @since 3.3
+ */
+ public AbstractListChromosome(final List<T> representation, final boolean copyList) {
+ checkValidity(representation);
+ this.representation =
+ Collections.unmodifiableList(
+ copyList ? new ArrayList<T>(representation) : representation);
+ }
+
+ /**
+ * Asserts that <code>representation</code> can represent a valid chromosome.
+ *
+ * @param chromosomeRepresentation representation of the chromosome
+ * @throws InvalidRepresentationException iff the <code>representation</code> can not represent
+ * a valid chromosome
+ */
+ protected abstract void checkValidity(List<T> chromosomeRepresentation)
+ throws InvalidRepresentationException;
+
+ /**
+ * Returns the (immutable) inner representation of the chromosome.
+ *
+ * @return the representation of the chromosome
+ */
+ protected List<T> getRepresentation() {
+ return representation;
+ }
+
+ /**
+ * Returns the length of the chromosome.
+ *
+ * @return the length of the chromosome
+ */
+ public int getLength() {
+ return getRepresentation().size();
+ }
+
+ /**
+ * Creates a new instance of the same class as <code>this</code> is, with a given <code>
+ * arrayRepresentation</code>. This is needed in crossover and mutation operators, where we need
+ * a new instance of the same class, but with different array representation.
+ *
+ * <p>Usually, this method just calls a constructor of the class.
+ *
+ * @param chromosomeRepresentation the inner array representation of the new chromosome.
+ * @return new instance extended from FixedLengthChromosome with the given arrayRepresentation
+ */
+ public abstract AbstractListChromosome<T> newFixedLengthChromosome(
+ final List<T> chromosomeRepresentation);
+
+ /** {@inheritDoc} */
+ @Override
+ public String toString() {
+ return String.format("(f=%s %s)", getFitness(), getRepresentation());
+ }
+}
diff --git a/src/main/java/org/apache/commons/math3/genetics/BinaryChromosome.java b/src/main/java/org/apache/commons/math3/genetics/BinaryChromosome.java
new file mode 100644
index 0000000..6874ddc
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/genetics/BinaryChromosome.java
@@ -0,0 +1,100 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math3.genetics;
+
+import org.apache.commons.math3.exception.util.LocalizedFormats;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Chromosome represented by a vector of 0s and 1s.
+ *
+ * @since 2.0
+ */
+public abstract class BinaryChromosome extends AbstractListChromosome<Integer> {
+
+ /**
+ * Constructor.
+ *
+ * @param representation list of {0,1} values representing the chromosome
+ * @throws InvalidRepresentationException iff the <code>representation</code> can not represent
+ * a valid chromosome
+ */
+ public BinaryChromosome(List<Integer> representation) throws InvalidRepresentationException {
+ super(representation);
+ }
+
+ /**
+ * Constructor.
+ *
+ * @param representation array of {0,1} values representing the chromosome
+ * @throws InvalidRepresentationException iff the <code>representation</code> can not represent
+ * a valid chromosome
+ */
+ public BinaryChromosome(Integer[] representation) throws InvalidRepresentationException {
+ super(representation);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ protected void checkValidity(List<Integer> chromosomeRepresentation)
+ throws InvalidRepresentationException {
+ for (int i : chromosomeRepresentation) {
+ if (i < 0 || i > 1) {
+ throw new InvalidRepresentationException(LocalizedFormats.INVALID_BINARY_DIGIT, i);
+ }
+ }
+ }
+
+ /**
+ * Returns a representation of a random binary array of length <code>length</code>.
+ *
+ * @param length length of the array
+ * @return a random binary array of length <code>length</code>
+ */
+ public static List<Integer> randomBinaryRepresentation(int length) {
+ // random binary list
+ List<Integer> rList = new ArrayList<Integer>(length);
+ for (int j = 0; j < length; j++) {
+ rList.add(GeneticAlgorithm.getRandomGenerator().nextInt(2));
+ }
+ return rList;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ protected boolean isSame(Chromosome another) {
+ // type check
+ if (!(another instanceof BinaryChromosome)) {
+ return false;
+ }
+ BinaryChromosome anotherBc = (BinaryChromosome) another;
+ // size check
+ if (getLength() != anotherBc.getLength()) {
+ return false;
+ }
+
+ for (int i = 0; i < getRepresentation().size(); i++) {
+ if (!(getRepresentation().get(i).equals(anotherBc.getRepresentation().get(i)))) {
+ return false;
+ }
+ }
+ // all is ok
+ return true;
+ }
+}
diff --git a/src/main/java/org/apache/commons/math3/genetics/BinaryMutation.java b/src/main/java/org/apache/commons/math3/genetics/BinaryMutation.java
new file mode 100644
index 0000000..9372b13
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/genetics/BinaryMutation.java
@@ -0,0 +1,56 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math3.genetics;
+
+import org.apache.commons.math3.exception.MathIllegalArgumentException;
+import org.apache.commons.math3.exception.util.LocalizedFormats;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Mutation for {@link BinaryChromosome}s. Randomly changes one gene.
+ *
+ * @since 2.0
+ */
+public class BinaryMutation implements MutationPolicy {
+
+ /**
+ * Mutate the given chromosome. Randomly changes one gene.
+ *
+ * @param original the original chromosome.
+ * @return the mutated chromosome.
+ * @throws MathIllegalArgumentException if <code>original</code> is not an instance of {@link
+ * BinaryChromosome}.
+ */
+ public Chromosome mutate(Chromosome original) throws MathIllegalArgumentException {
+ if (!(original instanceof BinaryChromosome)) {
+ throw new MathIllegalArgumentException(LocalizedFormats.INVALID_BINARY_CHROMOSOME);
+ }
+
+ BinaryChromosome origChrom = (BinaryChromosome) original;
+ List<Integer> newRepr = new ArrayList<Integer>(origChrom.getRepresentation());
+
+ // randomly select a gene
+ int geneIndex = GeneticAlgorithm.getRandomGenerator().nextInt(origChrom.getLength());
+ // and change it
+ newRepr.set(geneIndex, origChrom.getRepresentation().get(geneIndex) == 0 ? 1 : 0);
+
+ Chromosome newChrom = origChrom.newFixedLengthChromosome(newRepr);
+ return newChrom;
+ }
+}
diff --git a/src/main/java/org/apache/commons/math3/genetics/Chromosome.java b/src/main/java/org/apache/commons/math3/genetics/Chromosome.java
new file mode 100644
index 0000000..532a726
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/genetics/Chromosome.java
@@ -0,0 +1,108 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math3.genetics;
+
+/**
+ * Individual in a population. Chromosomes are compared based on their fitness.
+ *
+ * <p>The chromosomes are IMMUTABLE, and so their fitness is also immutable and therefore it can be
+ * cached.
+ *
+ * @since 2.0
+ */
+public abstract class Chromosome implements Comparable<Chromosome>, Fitness {
+ /** Value assigned when no fitness has been computed yet. */
+ private static final double NO_FITNESS = Double.NEGATIVE_INFINITY;
+
+ /** Cached value of the fitness of this chromosome. */
+ private double fitness = NO_FITNESS;
+
+ /**
+ * Access the fitness of this chromosome. The bigger the fitness, the better the chromosome.
+ *
+ * <p>Computation of fitness is usually very time-consuming task, therefore the fitness is
+ * cached.
+ *
+ * @return the fitness
+ */
+ public double getFitness() {
+ if (this.fitness == NO_FITNESS) {
+ // no cache - compute the fitness
+ this.fitness = fitness();
+ }
+ return this.fitness;
+ }
+
+ /**
+ * Compares two chromosomes based on their fitness. The bigger the fitness, the better the
+ * chromosome.
+ *
+ * @param another another chromosome to compare
+ * @return
+ * <ul>
+ * <li>-1 if <code>another</code> is better than <code>this</code>
+ * <li>1 if <code>another</code> is worse than <code>this</code>
+ * <li>0 if the two chromosomes have the same fitness
+ * </ul>
+ */
+ public int compareTo(final Chromosome another) {
+ return Double.compare(getFitness(), another.getFitness());
+ }
+
+ /**
+ * Returns <code>true</code> iff <code>another</code> has the same representation and therefore
+ * the same fitness. By default, it returns false -- override it in your implementation if you
+ * need it.
+ *
+ * @param another chromosome to compare
+ * @return true if <code>another</code> is equivalent to this chromosome
+ */
+ protected boolean isSame(final Chromosome another) {
+ return false;
+ }
+
+ /**
+ * Searches the <code>population</code> for another chromosome with the same representation. If
+ * such chromosome is found, it is returned, if no such chromosome exists, returns <code>null
+ * </code>.
+ *
+ * @param population Population to search
+ * @return Chromosome with the same representation, or <code>null</code> if no such chromosome
+ * exists.
+ */
+ protected Chromosome findSameChromosome(final Population population) {
+ for (Chromosome anotherChr : population) {
+ if (this.isSame(anotherChr)) {
+ return anotherChr;
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Searches the population for a chromosome representing the same solution, and if it finds one,
+ * updates the fitness to its value.
+ *
+ * @param population Population to search
+ */
+ public void searchForFitnessUpdate(final Population population) {
+ Chromosome sameChromosome = findSameChromosome(population);
+ if (sameChromosome != null) {
+ fitness = sameChromosome.getFitness();
+ }
+ }
+}
diff --git a/src/main/java/org/apache/commons/math3/genetics/ChromosomePair.java b/src/main/java/org/apache/commons/math3/genetics/ChromosomePair.java
new file mode 100644
index 0000000..e47c1b7
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/genetics/ChromosomePair.java
@@ -0,0 +1,66 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math3.genetics;
+
+/**
+ * A pair of {@link Chromosome} objects.
+ *
+ * @since 2.0
+ */
+public class ChromosomePair {
+ /** the first chromosome in the pair. */
+ private final Chromosome first;
+
+ /** the second chromosome in the pair. */
+ private final Chromosome second;
+
+ /**
+ * Create a chromosome pair.
+ *
+ * @param c1 the first chromosome.
+ * @param c2 the second chromosome.
+ */
+ public ChromosomePair(final Chromosome c1, final Chromosome c2) {
+ super();
+ first = c1;
+ second = c2;
+ }
+
+ /**
+ * Access the first chromosome.
+ *
+ * @return the first chromosome.
+ */
+ public Chromosome getFirst() {
+ return first;
+ }
+
+ /**
+ * Access the second chromosome.
+ *
+ * @return the second chromosome.
+ */
+ public Chromosome getSecond() {
+ return second;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public String toString() {
+ return String.format("(%s,%s)", getFirst(), getSecond());
+ }
+}
diff --git a/src/main/java/org/apache/commons/math3/genetics/CrossoverPolicy.java b/src/main/java/org/apache/commons/math3/genetics/CrossoverPolicy.java
new file mode 100644
index 0000000..d959044
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/genetics/CrossoverPolicy.java
@@ -0,0 +1,40 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math3.genetics;
+
+import org.apache.commons.math3.exception.MathIllegalArgumentException;
+
+/**
+ * Policy used to create a pair of new chromosomes by performing a crossover operation on a source
+ * pair of chromosomes.
+ *
+ * @since 2.0
+ */
+public interface CrossoverPolicy {
+
+ /**
+ * Perform a crossover operation on the given chromosomes.
+ *
+ * @param first the first chromosome.
+ * @param second the second chromosome.
+ * @return the pair of new chromosomes that resulted from the crossover.
+ * @throws MathIllegalArgumentException if the given chromosomes are not compatible with this
+ * {@link CrossoverPolicy}
+ */
+ ChromosomePair crossover(Chromosome first, Chromosome second)
+ throws MathIllegalArgumentException;
+}
diff --git a/src/main/java/org/apache/commons/math3/genetics/CycleCrossover.java b/src/main/java/org/apache/commons/math3/genetics/CycleCrossover.java
new file mode 100644
index 0000000..e07e47c
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/genetics/CycleCrossover.java
@@ -0,0 +1,185 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math3.genetics;
+
+import org.apache.commons.math3.exception.DimensionMismatchException;
+import org.apache.commons.math3.exception.MathIllegalArgumentException;
+import org.apache.commons.math3.exception.util.LocalizedFormats;
+
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+/**
+ * Cycle Crossover [CX] builds offspring from <b>ordered</b> chromosomes by identifying cycles
+ * between two parent chromosomes. To form the children, the cycles are copied from the respective
+ * parents.
+ *
+ * <p>To form a cycle the following procedure is applied:
+ *
+ * <ol>
+ * <li>start with the first gene of parent 1
+ * <li>look at the gene at the same position of parent 2
+ * <li>go to the position with the same gene in parent 1
+ * <li>add this gene index to the cycle
+ * <li>repeat the steps 2-5 until we arrive at the starting gene of this cycle
+ * </ol>
+ *
+ * The indices that form a cycle are then used to form the children in alternating order, i.e. in
+ * cycle 1, the genes of parent 1 are copied to child 1, while in cycle 2 the genes of parent 1 are
+ * copied to child 2, and so forth ... Example (zero-start cycle):
+ *
+ * <pre>
+ * p1 = (8 4 7 3 6 2 5 1 9 0) X c1 = (8 1 2 3 4 5 6 7 9 0)
+ * p2 = (0 1 2 3 4 5 6 7 8 9) X c2 = (0 4 7 3 6 2 5 1 8 9)
+ *
+ * cycle 1: 8 0 9
+ * cycle 2: 4 1 7 2 5 6
+ * cycle 3: 3
+ * </pre>
+ *
+ * This policy works only on {@link AbstractListChromosome}, and therefore it is parameterized by T.
+ * Moreover, the chromosomes must have same lengths.
+ *
+ * @see <a
+ * href="http://www.rubicite.com/Tutorials/GeneticAlgorithms/CrossoverOperators/CycleCrossoverOperator.aspx">
+ * Cycle Crossover Operator</a>
+ * @param <T> generic type of the {@link AbstractListChromosome}s for crossover
+ * @since 3.1
+ */
+public class CycleCrossover<T> implements CrossoverPolicy {
+
+ /** If the start index shall be chosen randomly. */
+ private final boolean randomStart;
+
+ /** Creates a new {@link CycleCrossover} policy. */
+ public CycleCrossover() {
+ this(false);
+ }
+
+ /**
+ * Creates a new {@link CycleCrossover} policy using the given {@code randomStart} behavior.
+ *
+ * @param randomStart whether the start index shall be chosen randomly or be set to 0
+ */
+ public CycleCrossover(final boolean randomStart) {
+ this.randomStart = randomStart;
+ }
+
+ /**
+ * Returns whether the starting index is chosen randomly or set to zero.
+ *
+ * @return {@code true} if the starting index is chosen randomly, {@code false} otherwise
+ */
+ public boolean isRandomStart() {
+ return randomStart;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @throws MathIllegalArgumentException if the chromosomes are not an instance of {@link
+ * AbstractListChromosome}
+ * @throws DimensionMismatchException if the length of the two chromosomes is different
+ */
+ @SuppressWarnings("unchecked")
+ public ChromosomePair crossover(final Chromosome first, final Chromosome second)
+ throws DimensionMismatchException, MathIllegalArgumentException {
+
+ if (!(first instanceof AbstractListChromosome<?>
+ && second instanceof AbstractListChromosome<?>)) {
+ throw new MathIllegalArgumentException(
+ LocalizedFormats.INVALID_FIXED_LENGTH_CHROMOSOME);
+ }
+ return mate((AbstractListChromosome<T>) first, (AbstractListChromosome<T>) second);
+ }
+
+ /**
+ * Helper for {@link #crossover(Chromosome, Chromosome)}. Performs the actual crossover.
+ *
+ * @param first the first chromosome
+ * @param second the second chromosome
+ * @return the pair of new chromosomes that resulted from the crossover
+ * @throws DimensionMismatchException if the length of the two chromosomes is different
+ */
+ protected ChromosomePair mate(
+ final AbstractListChromosome<T> first, final AbstractListChromosome<T> second)
+ throws DimensionMismatchException {
+
+ final int length = first.getLength();
+ if (length != second.getLength()) {
+ throw new DimensionMismatchException(second.getLength(), length);
+ }
+
+ // array representations of the parents
+ final List<T> parent1Rep = first.getRepresentation();
+ final List<T> parent2Rep = second.getRepresentation();
+ // and of the children: do a crossover copy to simplify the later processing
+ final List<T> child1Rep = new ArrayList<T>(second.getRepresentation());
+ final List<T> child2Rep = new ArrayList<T>(first.getRepresentation());
+
+ // the set of all visited indices so far
+ final Set<Integer> visitedIndices = new HashSet<Integer>(length);
+ // the indices of the current cycle
+ final List<Integer> indices = new ArrayList<Integer>(length);
+
+ // determine the starting index
+ int idx = randomStart ? GeneticAlgorithm.getRandomGenerator().nextInt(length) : 0;
+ int cycle = 1;
+
+ while (visitedIndices.size() < length) {
+ indices.add(idx);
+
+ T item = parent2Rep.get(idx);
+ idx = parent1Rep.indexOf(item);
+
+ while (idx != indices.get(0)) {
+ // add that index to the cycle indices
+ indices.add(idx);
+ // get the item in the second parent at that index
+ item = parent2Rep.get(idx);
+ // get the index of that item in the first parent
+ idx = parent1Rep.indexOf(item);
+ }
+
+ // for even cycles: swap the child elements on the indices found in this cycle
+ if (cycle++ % 2 != 0) {
+ for (int i : indices) {
+ T tmp = child1Rep.get(i);
+ child1Rep.set(i, child2Rep.get(i));
+ child2Rep.set(i, tmp);
+ }
+ }
+
+ visitedIndices.addAll(indices);
+ // find next starting index: last one + 1 until we find an unvisited index
+ idx = (indices.get(0) + 1) % length;
+ while (visitedIndices.contains(idx) && visitedIndices.size() < length) {
+ idx++;
+ if (idx >= length) {
+ idx = 0;
+ }
+ }
+ indices.clear();
+ }
+
+ return new ChromosomePair(
+ first.newFixedLengthChromosome(child1Rep),
+ second.newFixedLengthChromosome(child2Rep));
+ }
+}
diff --git a/src/main/java/org/apache/commons/math3/genetics/ElitisticListPopulation.java b/src/main/java/org/apache/commons/math3/genetics/ElitisticListPopulation.java
new file mode 100644
index 0000000..0e7009d
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/genetics/ElitisticListPopulation.java
@@ -0,0 +1,125 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math3.genetics;
+
+import org.apache.commons.math3.exception.NotPositiveException;
+import org.apache.commons.math3.exception.NullArgumentException;
+import org.apache.commons.math3.exception.NumberIsTooLargeException;
+import org.apache.commons.math3.exception.OutOfRangeException;
+import org.apache.commons.math3.exception.util.LocalizedFormats;
+import org.apache.commons.math3.util.FastMath;
+
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * Population of chromosomes which uses elitism (certain percentage of the best chromosomes is
+ * directly copied to the next generation).
+ *
+ * @since 2.0
+ */
+public class ElitisticListPopulation extends ListPopulation {
+
+ /** percentage of chromosomes copied to the next generation */
+ private double elitismRate = 0.9;
+
+ /**
+ * Creates a new {@link ElitisticListPopulation} instance.
+ *
+ * @param chromosomes list of chromosomes in the population
+ * @param populationLimit maximal size of the population
+ * @param elitismRate how many best chromosomes will be directly transferred to the next
+ * generation [in %]
+ * @throws NullArgumentException if the list of chromosomes is {@code null}
+ * @throws NotPositiveException if the population limit is not a positive number (&lt; 1)
+ * @throws NumberIsTooLargeException if the list of chromosomes exceeds the population limit
+ * @throws OutOfRangeException if the elitism rate is outside the [0, 1] range
+ */
+ public ElitisticListPopulation(
+ final List<Chromosome> chromosomes, final int populationLimit, final double elitismRate)
+ throws NullArgumentException,
+ NotPositiveException,
+ NumberIsTooLargeException,
+ OutOfRangeException {
+
+ super(chromosomes, populationLimit);
+ setElitismRate(elitismRate);
+ }
+
+ /**
+ * Creates a new {@link ElitisticListPopulation} instance and initializes its inner chromosome
+ * list.
+ *
+ * @param populationLimit maximal size of the population
+ * @param elitismRate how many best chromosomes will be directly transferred to the next
+ * generation [in %]
+ * @throws NotPositiveException if the population limit is not a positive number (&lt; 1)
+ * @throws OutOfRangeException if the elitism rate is outside the [0, 1] range
+ */
+ public ElitisticListPopulation(final int populationLimit, final double elitismRate)
+ throws NotPositiveException, OutOfRangeException {
+
+ super(populationLimit);
+ setElitismRate(elitismRate);
+ }
+
+ /**
+ * Start the population for the next generation. The <code>{@link #elitismRate}</code> percents
+ * of the best chromosomes are directly copied to the next generation.
+ *
+ * @return the beginnings of the next generation.
+ */
+ public Population nextGeneration() {
+ // initialize a new generation with the same parameters
+ ElitisticListPopulation nextGeneration =
+ new ElitisticListPopulation(getPopulationLimit(), getElitismRate());
+
+ final List<Chromosome> oldChromosomes = getChromosomeList();
+ Collections.sort(oldChromosomes);
+
+ // index of the last "not good enough" chromosome
+ int boundIndex = (int) FastMath.ceil((1.0 - getElitismRate()) * oldChromosomes.size());
+ for (int i = boundIndex; i < oldChromosomes.size(); i++) {
+ nextGeneration.addChromosome(oldChromosomes.get(i));
+ }
+ return nextGeneration;
+ }
+
+ /**
+ * Sets the elitism rate, i.e. how many best chromosomes will be directly transferred to the
+ * next generation [in %].
+ *
+ * @param elitismRate how many best chromosomes will be directly transferred to the next
+ * generation [in %]
+ * @throws OutOfRangeException if the elitism rate is outside the [0, 1] range
+ */
+ public void setElitismRate(final double elitismRate) throws OutOfRangeException {
+ if (elitismRate < 0 || elitismRate > 1) {
+ throw new OutOfRangeException(LocalizedFormats.ELITISM_RATE, elitismRate, 0, 1);
+ }
+ this.elitismRate = elitismRate;
+ }
+
+ /**
+ * Access the elitism rate.
+ *
+ * @return the elitism rate
+ */
+ public double getElitismRate() {
+ return this.elitismRate;
+ }
+}
diff --git a/src/main/java/org/apache/commons/math3/genetics/Fitness.java b/src/main/java/org/apache/commons/math3/genetics/Fitness.java
new file mode 100644
index 0000000..66f8d56
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/genetics/Fitness.java
@@ -0,0 +1,32 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math3.genetics;
+
+/**
+ * Fitness of a chromosome.
+ *
+ * @since 2.0
+ */
+public interface Fitness {
+
+ /**
+ * Compute the fitness. This is usually very time-consuming, so the value should be cached.
+ *
+ * @return fitness
+ */
+ double fitness();
+}
diff --git a/src/main/java/org/apache/commons/math3/genetics/FixedElapsedTime.java b/src/main/java/org/apache/commons/math3/genetics/FixedElapsedTime.java
new file mode 100644
index 0000000..af63094
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/genetics/FixedElapsedTime.java
@@ -0,0 +1,78 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math3.genetics;
+
+import org.apache.commons.math3.exception.NumberIsTooSmallException;
+
+import java.util.concurrent.TimeUnit;
+
+/**
+ * Stops after a fixed amount of time has elapsed.
+ *
+ * <p>The first time {@link #isSatisfied(Population)} is invoked, the end time of the evolution is
+ * determined based on the provided <code>maxTime</code> value. Once the elapsed time reaches the
+ * configured <code>maxTime</code> value, {@link #isSatisfied(Population)} returns true.
+ *
+ * @since 3.1
+ */
+public class FixedElapsedTime implements StoppingCondition {
+ /** Maximum allowed time period (in nanoseconds). */
+ private final long maxTimePeriod;
+
+ /** The predetermined termination time (stopping condition). */
+ private long endTime = -1;
+
+ /**
+ * Create a new {@link FixedElapsedTime} instance.
+ *
+ * @param maxTime maximum number of seconds generations are allowed to evolve
+ * @throws NumberIsTooSmallException if the provided time is &lt; 0
+ */
+ public FixedElapsedTime(final long maxTime) throws NumberIsTooSmallException {
+ this(maxTime, TimeUnit.SECONDS);
+ }
+
+ /**
+ * Create a new {@link FixedElapsedTime} instance.
+ *
+ * @param maxTime maximum time generations are allowed to evolve
+ * @param unit {@link TimeUnit} of the maxTime argument
+ * @throws NumberIsTooSmallException if the provided time is &lt; 0
+ */
+ public FixedElapsedTime(final long maxTime, final TimeUnit unit)
+ throws NumberIsTooSmallException {
+ if (maxTime < 0) {
+ throw new NumberIsTooSmallException(maxTime, 0, true);
+ }
+ maxTimePeriod = unit.toNanos(maxTime);
+ }
+
+ /**
+ * Determine whether or not the maximum allowed time has passed. The termination time is
+ * determined after the first generation.
+ *
+ * @param population ignored (no impact on result)
+ * @return <code>true</code> IFF the maximum allowed time period has elapsed
+ */
+ public boolean isSatisfied(final Population population) {
+ if (endTime < 0) {
+ endTime = System.nanoTime() + maxTimePeriod;
+ }
+
+ return System.nanoTime() >= endTime;
+ }
+}
diff --git a/src/main/java/org/apache/commons/math3/genetics/FixedGenerationCount.java b/src/main/java/org/apache/commons/math3/genetics/FixedGenerationCount.java
new file mode 100644
index 0000000..d7d2088
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/genetics/FixedGenerationCount.java
@@ -0,0 +1,71 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math3.genetics;
+
+import org.apache.commons.math3.exception.NumberIsTooSmallException;
+
+/**
+ * Stops after a fixed number of generations. Each time {@link #isSatisfied(Population)} is invoked,
+ * a generation counter is incremented. Once the counter reaches the configured <code>maxGenerations
+ * </code> value, {@link #isSatisfied(Population)} returns true.
+ *
+ * @since 2.0
+ */
+public class FixedGenerationCount implements StoppingCondition {
+ /** Number of generations that have passed */
+ private int numGenerations = 0;
+
+ /** Maximum number of generations (stopping criteria) */
+ private final int maxGenerations;
+
+ /**
+ * Create a new FixedGenerationCount instance.
+ *
+ * @param maxGenerations number of generations to evolve
+ * @throws NumberIsTooSmallException if the number of generations is &lt; 1
+ */
+ public FixedGenerationCount(final int maxGenerations) throws NumberIsTooSmallException {
+ if (maxGenerations <= 0) {
+ throw new NumberIsTooSmallException(maxGenerations, 1, true);
+ }
+ this.maxGenerations = maxGenerations;
+ }
+
+ /**
+ * Determine whether or not the given number of generations have passed. Increments the number
+ * of generations counter if the maximum has not been reached.
+ *
+ * @param population ignored (no impact on result)
+ * @return <code>true</code> IFF the maximum number of generations has been exceeded
+ */
+ public boolean isSatisfied(final Population population) {
+ if (this.numGenerations < this.maxGenerations) {
+ numGenerations++;
+ return false;
+ }
+ return true;
+ }
+
+ /**
+ * Returns the number of generations that have already passed.
+ *
+ * @return the number of generations that have passed
+ */
+ public int getNumGenerations() {
+ return numGenerations;
+ }
+}
diff --git a/src/main/java/org/apache/commons/math3/genetics/GeneticAlgorithm.java b/src/main/java/org/apache/commons/math3/genetics/GeneticAlgorithm.java
new file mode 100644
index 0000000..e0f1127
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/genetics/GeneticAlgorithm.java
@@ -0,0 +1,240 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math3.genetics;
+
+import org.apache.commons.math3.exception.OutOfRangeException;
+import org.apache.commons.math3.exception.util.LocalizedFormats;
+import org.apache.commons.math3.random.JDKRandomGenerator;
+import org.apache.commons.math3.random.RandomGenerator;
+
+/**
+ * Implementation of a genetic algorithm. All factors that govern the operation of the algorithm can
+ * be configured for a specific problem.
+ *
+ * @since 2.0
+ */
+public class GeneticAlgorithm {
+
+ /**
+ * Static random number generator shared by GA implementation classes. Set the randomGenerator
+ * seed to get reproducible results. Use {@link #setRandomGenerator(RandomGenerator)} to supply
+ * an alternative to the default JDK-provided PRNG.
+ */
+ // @GuardedBy("this")
+ private static RandomGenerator randomGenerator = new JDKRandomGenerator();
+
+ /** the crossover policy used by the algorithm. */
+ private final CrossoverPolicy crossoverPolicy;
+
+ /** the rate of crossover for the algorithm. */
+ private final double crossoverRate;
+
+ /** the mutation policy used by the algorithm. */
+ private final MutationPolicy mutationPolicy;
+
+ /** the rate of mutation for the algorithm. */
+ private final double mutationRate;
+
+ /** the selection policy used by the algorithm. */
+ private final SelectionPolicy selectionPolicy;
+
+ /** the number of generations evolved to reach {@link StoppingCondition} in the last run. */
+ private int generationsEvolved = 0;
+
+ /**
+ * Create a new genetic algorithm.
+ *
+ * @param crossoverPolicy The {@link CrossoverPolicy}
+ * @param crossoverRate The crossover rate as a percentage (0-1 inclusive)
+ * @param mutationPolicy The {@link MutationPolicy}
+ * @param mutationRate The mutation rate as a percentage (0-1 inclusive)
+ * @param selectionPolicy The {@link SelectionPolicy}
+ * @throws OutOfRangeException if the crossover or mutation rate is outside the [0, 1] range
+ */
+ public GeneticAlgorithm(
+ final CrossoverPolicy crossoverPolicy,
+ final double crossoverRate,
+ final MutationPolicy mutationPolicy,
+ final double mutationRate,
+ final SelectionPolicy selectionPolicy)
+ throws OutOfRangeException {
+
+ if (crossoverRate < 0 || crossoverRate > 1) {
+ throw new OutOfRangeException(LocalizedFormats.CROSSOVER_RATE, crossoverRate, 0, 1);
+ }
+ if (mutationRate < 0 || mutationRate > 1) {
+ throw new OutOfRangeException(LocalizedFormats.MUTATION_RATE, mutationRate, 0, 1);
+ }
+ this.crossoverPolicy = crossoverPolicy;
+ this.crossoverRate = crossoverRate;
+ this.mutationPolicy = mutationPolicy;
+ this.mutationRate = mutationRate;
+ this.selectionPolicy = selectionPolicy;
+ }
+
+ /**
+ * Set the (static) random generator.
+ *
+ * @param random random generator
+ */
+ public static synchronized void setRandomGenerator(final RandomGenerator random) {
+ randomGenerator = random;
+ }
+
+ /**
+ * Returns the (static) random generator.
+ *
+ * @return the static random generator shared by GA implementation classes
+ */
+ public static synchronized RandomGenerator getRandomGenerator() {
+ return randomGenerator;
+ }
+
+ /**
+ * Evolve the given population. Evolution stops when the stopping condition is satisfied.
+ * Updates the {@link #getGenerationsEvolved() generationsEvolved} property with the number of
+ * generations evolved before the StoppingCondition is satisfied.
+ *
+ * @param initial the initial, seed population.
+ * @param condition the stopping condition used to stop evolution.
+ * @return the population that satisfies the stopping condition.
+ */
+ public Population evolve(final Population initial, final StoppingCondition condition) {
+ Population current = initial;
+ generationsEvolved = 0;
+ while (!condition.isSatisfied(current)) {
+ current = nextGeneration(current);
+ generationsEvolved++;
+ }
+ return current;
+ }
+
+ /**
+ * Evolve the given population into the next generation.
+ *
+ * <p>
+ *
+ * <ol>
+ * <li>Get nextGeneration population to fill from <code>current</code> generation, using its
+ * nextGeneration method
+ * <li>Loop until new generation is filled:
+ * <ul>
+ * <li>Apply configured SelectionPolicy to select a pair of parents from <code>current
+ * </code>
+ * <li>With probability = {@link #getCrossoverRate()}, apply configured {@link
+ * CrossoverPolicy} to parents
+ * <li>With probability = {@link #getMutationRate()}, apply configured {@link
+ * MutationPolicy} to each of the offspring
+ * <li>Add offspring individually to nextGeneration, space permitting
+ * </ul>
+ * <li>Return nextGeneration
+ * </ol>
+ *
+ * @param current the current population.
+ * @return the population for the next generation.
+ */
+ public Population nextGeneration(final Population current) {
+ Population nextGeneration = current.nextGeneration();
+
+ RandomGenerator randGen = getRandomGenerator();
+
+ while (nextGeneration.getPopulationSize() < nextGeneration.getPopulationLimit()) {
+ // select parent chromosomes
+ ChromosomePair pair = getSelectionPolicy().select(current);
+
+ // crossover?
+ if (randGen.nextDouble() < getCrossoverRate()) {
+ // apply crossover policy to create two offspring
+ pair = getCrossoverPolicy().crossover(pair.getFirst(), pair.getSecond());
+ }
+
+ // mutation?
+ if (randGen.nextDouble() < getMutationRate()) {
+ // apply mutation policy to the chromosomes
+ pair =
+ new ChromosomePair(
+ getMutationPolicy().mutate(pair.getFirst()),
+ getMutationPolicy().mutate(pair.getSecond()));
+ }
+
+ // add the first chromosome to the population
+ nextGeneration.addChromosome(pair.getFirst());
+ // is there still a place for the second chromosome?
+ if (nextGeneration.getPopulationSize() < nextGeneration.getPopulationLimit()) {
+ // add the second chromosome to the population
+ nextGeneration.addChromosome(pair.getSecond());
+ }
+ }
+
+ return nextGeneration;
+ }
+
+ /**
+ * Returns the crossover policy.
+ *
+ * @return crossover policy
+ */
+ public CrossoverPolicy getCrossoverPolicy() {
+ return crossoverPolicy;
+ }
+
+ /**
+ * Returns the crossover rate.
+ *
+ * @return crossover rate
+ */
+ public double getCrossoverRate() {
+ return crossoverRate;
+ }
+
+ /**
+ * Returns the mutation policy.
+ *
+ * @return mutation policy
+ */
+ public MutationPolicy getMutationPolicy() {
+ return mutationPolicy;
+ }
+
+ /**
+ * Returns the mutation rate.
+ *
+ * @return mutation rate
+ */
+ public double getMutationRate() {
+ return mutationRate;
+ }
+
+ /**
+ * Returns the selection policy.
+ *
+ * @return selection policy
+ */
+ public SelectionPolicy getSelectionPolicy() {
+ return selectionPolicy;
+ }
+
+ /**
+ * Returns the number of generations evolved to reach {@link StoppingCondition} in the last run.
+ *
+ * @return number of generations evolved
+ * @since 2.1
+ */
+ public int getGenerationsEvolved() {
+ return generationsEvolved;
+ }
+}
diff --git a/src/main/java/org/apache/commons/math3/genetics/InvalidRepresentationException.java b/src/main/java/org/apache/commons/math3/genetics/InvalidRepresentationException.java
new file mode 100644
index 0000000..33bc90c
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/genetics/InvalidRepresentationException.java
@@ -0,0 +1,41 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math3.genetics;
+
+import org.apache.commons.math3.exception.MathIllegalArgumentException;
+import org.apache.commons.math3.exception.util.Localizable;
+
+/**
+ * Exception indicating that the representation of a chromosome is not valid.
+ *
+ * @since 2.0
+ */
+public class InvalidRepresentationException extends MathIllegalArgumentException {
+
+ /** Serialization version id */
+ private static final long serialVersionUID = 1L;
+
+ /**
+ * Construct an InvalidRepresentationException with a specialized message.
+ *
+ * @param pattern Message pattern.
+ * @param args Arguments.
+ */
+ public InvalidRepresentationException(Localizable pattern, Object... args) {
+ super(pattern, args);
+ }
+}
diff --git a/src/main/java/org/apache/commons/math3/genetics/ListPopulation.java b/src/main/java/org/apache/commons/math3/genetics/ListPopulation.java
new file mode 100644
index 0000000..b2023d6
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/genetics/ListPopulation.java
@@ -0,0 +1,245 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math3.genetics;
+
+import org.apache.commons.math3.exception.NotPositiveException;
+import org.apache.commons.math3.exception.NullArgumentException;
+import org.apache.commons.math3.exception.NumberIsTooLargeException;
+import org.apache.commons.math3.exception.NumberIsTooSmallException;
+import org.apache.commons.math3.exception.util.LocalizedFormats;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.List;
+
+/**
+ * Population of chromosomes represented by a {@link List}.
+ *
+ * @since 2.0
+ */
+public abstract class ListPopulation implements Population {
+
+ /** List of chromosomes */
+ private List<Chromosome> chromosomes;
+
+ /** maximal size of the population */
+ private int populationLimit;
+
+ /**
+ * Creates a new ListPopulation instance and initializes its inner chromosome list.
+ *
+ * @param populationLimit maximal size of the population
+ * @throws NotPositiveException if the population limit is not a positive number (&lt; 1)
+ */
+ public ListPopulation(final int populationLimit) throws NotPositiveException {
+ this(Collections.<Chromosome>emptyList(), populationLimit);
+ }
+
+ /**
+ * Creates a new ListPopulation instance.
+ *
+ * <p>Note: the chromosomes of the specified list are added to the population.
+ *
+ * @param chromosomes list of chromosomes to be added to the population
+ * @param populationLimit maximal size of the population
+ * @throws NullArgumentException if the list of chromosomes is {@code null}
+ * @throws NotPositiveException if the population limit is not a positive number (&lt; 1)
+ * @throws NumberIsTooLargeException if the list of chromosomes exceeds the population limit
+ */
+ public ListPopulation(final List<Chromosome> chromosomes, final int populationLimit)
+ throws NullArgumentException, NotPositiveException, NumberIsTooLargeException {
+
+ if (chromosomes == null) {
+ throw new NullArgumentException();
+ }
+ if (populationLimit <= 0) {
+ throw new NotPositiveException(
+ LocalizedFormats.POPULATION_LIMIT_NOT_POSITIVE, populationLimit);
+ }
+ if (chromosomes.size() > populationLimit) {
+ throw new NumberIsTooLargeException(
+ LocalizedFormats.LIST_OF_CHROMOSOMES_BIGGER_THAN_POPULATION_SIZE,
+ chromosomes.size(),
+ populationLimit,
+ false);
+ }
+ this.populationLimit = populationLimit;
+ this.chromosomes = new ArrayList<Chromosome>(populationLimit);
+ this.chromosomes.addAll(chromosomes);
+ }
+
+ /**
+ * Sets the list of chromosomes.
+ *
+ * <p>Note: this method removes all existing chromosomes in the population and adds all
+ * chromosomes of the specified list to the population.
+ *
+ * @param chromosomes the list of chromosomes
+ * @throws NullArgumentException if the list of chromosomes is {@code null}
+ * @throws NumberIsTooLargeException if the list of chromosomes exceeds the population limit
+ * @deprecated use {@link #addChromosomes(Collection)} instead
+ */
+ @Deprecated
+ public void setChromosomes(final List<Chromosome> chromosomes)
+ throws NullArgumentException, NumberIsTooLargeException {
+
+ if (chromosomes == null) {
+ throw new NullArgumentException();
+ }
+ if (chromosomes.size() > populationLimit) {
+ throw new NumberIsTooLargeException(
+ LocalizedFormats.LIST_OF_CHROMOSOMES_BIGGER_THAN_POPULATION_SIZE,
+ chromosomes.size(),
+ populationLimit,
+ false);
+ }
+ this.chromosomes.clear();
+ this.chromosomes.addAll(chromosomes);
+ }
+
+ /**
+ * Add a {@link Collection} of chromosomes to this {@link Population}.
+ *
+ * @param chromosomeColl a {@link Collection} of chromosomes
+ * @throws NumberIsTooLargeException if the population would exceed the population limit when
+ * adding this chromosome
+ * @since 3.1
+ */
+ public void addChromosomes(final Collection<Chromosome> chromosomeColl)
+ throws NumberIsTooLargeException {
+ if (chromosomes.size() + chromosomeColl.size() > populationLimit) {
+ throw new NumberIsTooLargeException(
+ LocalizedFormats.LIST_OF_CHROMOSOMES_BIGGER_THAN_POPULATION_SIZE,
+ chromosomes.size(),
+ populationLimit,
+ false);
+ }
+ this.chromosomes.addAll(chromosomeColl);
+ }
+
+ /**
+ * Returns an unmodifiable list of the chromosomes in this population.
+ *
+ * @return the unmodifiable list of chromosomes
+ */
+ public List<Chromosome> getChromosomes() {
+ return Collections.unmodifiableList(chromosomes);
+ }
+
+ /**
+ * Access the list of chromosomes.
+ *
+ * @return the list of chromosomes
+ * @since 3.1
+ */
+ protected List<Chromosome> getChromosomeList() {
+ return chromosomes;
+ }
+
+ /**
+ * Add the given chromosome to the population.
+ *
+ * @param chromosome the chromosome to add.
+ * @throws NumberIsTooLargeException if the population would exceed the {@code populationLimit}
+ * after adding this chromosome
+ */
+ public void addChromosome(final Chromosome chromosome) throws NumberIsTooLargeException {
+ if (chromosomes.size() >= populationLimit) {
+ throw new NumberIsTooLargeException(
+ LocalizedFormats.LIST_OF_CHROMOSOMES_BIGGER_THAN_POPULATION_SIZE,
+ chromosomes.size(),
+ populationLimit,
+ false);
+ }
+ this.chromosomes.add(chromosome);
+ }
+
+ /**
+ * Access the fittest chromosome in this population.
+ *
+ * @return the fittest chromosome.
+ */
+ public Chromosome getFittestChromosome() {
+ // best so far
+ Chromosome bestChromosome = this.chromosomes.get(0);
+ for (Chromosome chromosome : this.chromosomes) {
+ if (chromosome.compareTo(bestChromosome) > 0) {
+ // better chromosome found
+ bestChromosome = chromosome;
+ }
+ }
+ return bestChromosome;
+ }
+
+ /**
+ * Access the maximum population size.
+ *
+ * @return the maximum population size.
+ */
+ public int getPopulationLimit() {
+ return this.populationLimit;
+ }
+
+ /**
+ * Sets the maximal population size.
+ *
+ * @param populationLimit maximal population size.
+ * @throws NotPositiveException if the population limit is not a positive number (&lt; 1)
+ * @throws NumberIsTooSmallException if the new population size is smaller than the current
+ * number of chromosomes in the population
+ */
+ public void setPopulationLimit(final int populationLimit)
+ throws NotPositiveException, NumberIsTooSmallException {
+ if (populationLimit <= 0) {
+ throw new NotPositiveException(
+ LocalizedFormats.POPULATION_LIMIT_NOT_POSITIVE, populationLimit);
+ }
+ if (populationLimit < chromosomes.size()) {
+ throw new NumberIsTooSmallException(populationLimit, chromosomes.size(), true);
+ }
+ this.populationLimit = populationLimit;
+ }
+
+ /**
+ * Access the current population size.
+ *
+ * @return the current population size.
+ */
+ public int getPopulationSize() {
+ return this.chromosomes.size();
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public String toString() {
+ return this.chromosomes.toString();
+ }
+
+ /**
+ * Returns an iterator over the unmodifiable list of chromosomes.
+ *
+ * <p>Any call to {@link Iterator#remove()} will result in a {@link
+ * UnsupportedOperationException}.
+ *
+ * @return chromosome iterator
+ */
+ public Iterator<Chromosome> iterator() {
+ return getChromosomes().iterator();
+ }
+}
diff --git a/src/main/java/org/apache/commons/math3/genetics/MutationPolicy.java b/src/main/java/org/apache/commons/math3/genetics/MutationPolicy.java
new file mode 100644
index 0000000..ded2ec6
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/genetics/MutationPolicy.java
@@ -0,0 +1,37 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math3.genetics;
+
+import org.apache.commons.math3.exception.MathIllegalArgumentException;
+
+/**
+ * Algorithm used to mutate a chromosome.
+ *
+ * @since 2.0
+ */
+public interface MutationPolicy {
+
+ /**
+ * Mutate the given chromosome.
+ *
+ * @param original the original chromosome.
+ * @return the mutated chromosome.
+ * @throws MathIllegalArgumentException if the given chromosome is not compatible with this
+ * {@link MutationPolicy}
+ */
+ Chromosome mutate(Chromosome original) throws MathIllegalArgumentException;
+}
diff --git a/src/main/java/org/apache/commons/math3/genetics/NPointCrossover.java b/src/main/java/org/apache/commons/math3/genetics/NPointCrossover.java
new file mode 100644
index 0000000..f78997e
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/genetics/NPointCrossover.java
@@ -0,0 +1,187 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math3.genetics;
+
+import org.apache.commons.math3.exception.DimensionMismatchException;
+import org.apache.commons.math3.exception.MathIllegalArgumentException;
+import org.apache.commons.math3.exception.NotStrictlyPositiveException;
+import org.apache.commons.math3.exception.NumberIsTooLargeException;
+import org.apache.commons.math3.exception.util.LocalizedFormats;
+import org.apache.commons.math3.random.RandomGenerator;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * N-point crossover policy. For each iteration a random crossover point is selected and the first
+ * part from each parent is copied to the corresponding child, and the second parts are copied
+ * crosswise.
+ *
+ * <p>Example (2-point crossover):
+ *
+ * <pre>
+ * -C- denotes a crossover point
+ * -C- -C- -C- -C-
+ * p1 = (1 0 | 1 0 0 1 | 0 1 1) X p2 = (0 1 | 1 0 1 0 | 1 1 1)
+ * \----/ \-------/ \-----/ \----/ \--------/ \-----/
+ * || (*) || || (**) ||
+ * VV (**) VV VV (*) VV
+ * /----\ /--------\ /-----\ /----\ /--------\ /-----\
+ * c1 = (1 0 | 1 0 1 0 | 0 1 1) X c2 = (0 1 | 1 0 0 1 | 0 1 1)
+ * </pre>
+ *
+ * This policy works only on {@link AbstractListChromosome}, and therefore it is parameterized by T.
+ * Moreover, the chromosomes must have same lengths.
+ *
+ * @param <T> generic type of the {@link AbstractListChromosome}s for crossover
+ * @since 3.1
+ */
+public class NPointCrossover<T> implements CrossoverPolicy {
+
+ /** The number of crossover points. */
+ private final int crossoverPoints;
+
+ /**
+ * Creates a new {@link NPointCrossover} policy using the given number of points.
+ *
+ * <p><b>Note</b>: the number of crossover points must be &lt; <code>chromosome length - 1
+ * </code>. This condition can only be checked at runtime, as the chromosome length is not known
+ * in advance.
+ *
+ * @param crossoverPoints the number of crossover points
+ * @throws NotStrictlyPositiveException if the number of {@code crossoverPoints} is not strictly
+ * positive
+ */
+ public NPointCrossover(final int crossoverPoints) throws NotStrictlyPositiveException {
+ if (crossoverPoints <= 0) {
+ throw new NotStrictlyPositiveException(crossoverPoints);
+ }
+ this.crossoverPoints = crossoverPoints;
+ }
+
+ /**
+ * Returns the number of crossover points used by this {@link CrossoverPolicy}.
+ *
+ * @return the number of crossover points
+ */
+ public int getCrossoverPoints() {
+ return crossoverPoints;
+ }
+
+ /**
+ * Performs a N-point crossover. N random crossover points are selected and are used to divide
+ * the parent chromosomes into segments. The segments are copied in alternate order from the two
+ * parents to the corresponding child chromosomes.
+ *
+ * <p>Example (2-point crossover):
+ *
+ * <pre>
+ * -C- denotes a crossover point
+ * -C- -C- -C- -C-
+ * p1 = (1 0 | 1 0 0 1 | 0 1 1) X p2 = (0 1 | 1 0 1 0 | 1 1 1)
+ * \----/ \-------/ \-----/ \----/ \--------/ \-----/
+ * || (*) || || (**) ||
+ * VV (**) VV VV (*) VV
+ * /----\ /--------\ /-----\ /----\ /--------\ /-----\
+ * c1 = (1 0 | 1 0 1 0 | 0 1 1) X c2 = (0 1 | 1 0 0 1 | 0 1 1)
+ * </pre>
+ *
+ * @param first first parent (p1)
+ * @param second second parent (p2)
+ * @return pair of two children (c1,c2)
+ * @throws MathIllegalArgumentException iff one of the chromosomes is not an instance of {@link
+ * AbstractListChromosome}
+ * @throws DimensionMismatchException if the length of the two chromosomes is different
+ */
+ @SuppressWarnings("unchecked") // OK because of instanceof checks
+ public ChromosomePair crossover(final Chromosome first, final Chromosome second)
+ throws DimensionMismatchException, MathIllegalArgumentException {
+
+ if (!(first instanceof AbstractListChromosome<?>
+ && second instanceof AbstractListChromosome<?>)) {
+ throw new MathIllegalArgumentException(
+ LocalizedFormats.INVALID_FIXED_LENGTH_CHROMOSOME);
+ }
+ return mate((AbstractListChromosome<T>) first, (AbstractListChromosome<T>) second);
+ }
+
+ /**
+ * Helper for {@link #crossover(Chromosome, Chromosome)}. Performs the actual crossover.
+ *
+ * @param first the first chromosome
+ * @param second the second chromosome
+ * @return the pair of new chromosomes that resulted from the crossover
+ * @throws DimensionMismatchException if the length of the two chromosomes is different
+ * @throws NumberIsTooLargeException if the number of crossoverPoints is too large for the
+ * actual chromosomes
+ */
+ private ChromosomePair mate(
+ final AbstractListChromosome<T> first, final AbstractListChromosome<T> second)
+ throws DimensionMismatchException, NumberIsTooLargeException {
+
+ final int length = first.getLength();
+ if (length != second.getLength()) {
+ throw new DimensionMismatchException(second.getLength(), length);
+ }
+ if (crossoverPoints >= length) {
+ throw new NumberIsTooLargeException(crossoverPoints, length, false);
+ }
+
+ // array representations of the parents
+ final List<T> parent1Rep = first.getRepresentation();
+ final List<T> parent2Rep = second.getRepresentation();
+ // and of the children
+ final List<T> child1Rep = new ArrayList<T>(length);
+ final List<T> child2Rep = new ArrayList<T>(length);
+
+ final RandomGenerator random = GeneticAlgorithm.getRandomGenerator();
+
+ List<T> c1 = child1Rep;
+ List<T> c2 = child2Rep;
+
+ int remainingPoints = crossoverPoints;
+ int lastIndex = 0;
+ for (int i = 0; i < crossoverPoints; i++, remainingPoints--) {
+ // select the next crossover point at random
+ final int crossoverIndex =
+ 1 + lastIndex + random.nextInt(length - lastIndex - remainingPoints);
+
+ // copy the current segment
+ for (int j = lastIndex; j < crossoverIndex; j++) {
+ c1.add(parent1Rep.get(j));
+ c2.add(parent2Rep.get(j));
+ }
+
+ // swap the children for the next segment
+ List<T> tmp = c1;
+ c1 = c2;
+ c2 = tmp;
+
+ lastIndex = crossoverIndex;
+ }
+
+ // copy the last segment
+ for (int j = lastIndex; j < length; j++) {
+ c1.add(parent1Rep.get(j));
+ c2.add(parent2Rep.get(j));
+ }
+
+ return new ChromosomePair(
+ first.newFixedLengthChromosome(child1Rep),
+ second.newFixedLengthChromosome(child2Rep));
+ }
+}
diff --git a/src/main/java/org/apache/commons/math3/genetics/OnePointCrossover.java b/src/main/java/org/apache/commons/math3/genetics/OnePointCrossover.java
new file mode 100644
index 0000000..9514771
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/genetics/OnePointCrossover.java
@@ -0,0 +1,128 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math3.genetics;
+
+import org.apache.commons.math3.exception.DimensionMismatchException;
+import org.apache.commons.math3.exception.MathIllegalArgumentException;
+import org.apache.commons.math3.exception.util.LocalizedFormats;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * One point crossover policy. A random crossover point is selected and the first part from each
+ * parent is copied to the corresponding child, and the second parts are copied crosswise.
+ *
+ * <p>Example:
+ *
+ * <pre>
+ * -C- denotes a crossover point
+ * -C- -C-
+ * p1 = (1 0 1 0 0 1 | 0 1 1) X p2 = (0 1 1 0 1 0 | 1 1 1)
+ * \------------/ \-----/ \------------/ \-----/
+ * || (*) || (**)
+ * VV (**) VV (*)
+ * /------------\ /-----\ /------------\ /-----\
+ * c1 = (1 0 1 0 0 1 | 1 1 1) X c2 = (0 1 1 0 1 0 | 0 1 1)
+ * </pre>
+ *
+ * This policy works only on {@link AbstractListChromosome}, and therefore it is parameterized by T.
+ * Moreover, the chromosomes must have same lengths.
+ *
+ * @param <T> generic type of the {@link AbstractListChromosome}s for crossover
+ * @since 2.0
+ */
+public class OnePointCrossover<T> implements CrossoverPolicy {
+
+ /**
+ * Performs one point crossover. A random crossover point is selected and the first part from
+ * each parent is copied to the corresponding child, and the second parts are copied crosswise.
+ *
+ * <p>Example:
+ *
+ * <pre>
+ * -C- denotes a crossover point
+ * -C- -C-
+ * p1 = (1 0 1 0 0 1 | 0 1 1) X p2 = (0 1 1 0 1 0 | 1 1 1)
+ * \------------/ \-----/ \------------/ \-----/
+ * || (*) || (**)
+ * VV (**) VV (*)
+ * /------------\ /-----\ /------------\ /-----\
+ * c1 = (1 0 1 0 0 1 | 1 1 1) X c2 = (0 1 1 0 1 0 | 0 1 1)
+ * </pre>
+ *
+ * @param first first parent (p1)
+ * @param second second parent (p2)
+ * @return pair of two children (c1,c2)
+ * @throws MathIllegalArgumentException iff one of the chromosomes is not an instance of {@link
+ * AbstractListChromosome}
+ * @throws DimensionMismatchException if the length of the two chromosomes is different
+ */
+ @SuppressWarnings("unchecked") // OK because of instanceof checks
+ public ChromosomePair crossover(final Chromosome first, final Chromosome second)
+ throws DimensionMismatchException, MathIllegalArgumentException {
+
+ if (!(first instanceof AbstractListChromosome<?>
+ && second instanceof AbstractListChromosome<?>)) {
+ throw new MathIllegalArgumentException(
+ LocalizedFormats.INVALID_FIXED_LENGTH_CHROMOSOME);
+ }
+ return crossover((AbstractListChromosome<T>) first, (AbstractListChromosome<T>) second);
+ }
+
+ /**
+ * Helper for {@link #crossover(Chromosome, Chromosome)}. Performs the actual crossover.
+ *
+ * @param first the first chromosome.
+ * @param second the second chromosome.
+ * @return the pair of new chromosomes that resulted from the crossover.
+ * @throws DimensionMismatchException if the length of the two chromosomes is different
+ */
+ private ChromosomePair crossover(
+ final AbstractListChromosome<T> first, final AbstractListChromosome<T> second)
+ throws DimensionMismatchException {
+ final int length = first.getLength();
+ if (length != second.getLength()) {
+ throw new DimensionMismatchException(second.getLength(), length);
+ }
+
+ // array representations of the parents
+ final List<T> parent1Rep = first.getRepresentation();
+ final List<T> parent2Rep = second.getRepresentation();
+ // and of the children
+ final List<T> child1Rep = new ArrayList<T>(length);
+ final List<T> child2Rep = new ArrayList<T>(length);
+
+ // select a crossover point at random (0 and length makes no sense)
+ final int crossoverIndex = 1 + (GeneticAlgorithm.getRandomGenerator().nextInt(length - 2));
+
+ // copy the first part
+ for (int i = 0; i < crossoverIndex; i++) {
+ child1Rep.add(parent1Rep.get(i));
+ child2Rep.add(parent2Rep.get(i));
+ }
+ // and switch the second part
+ for (int i = crossoverIndex; i < length; i++) {
+ child1Rep.add(parent2Rep.get(i));
+ child2Rep.add(parent1Rep.get(i));
+ }
+
+ return new ChromosomePair(
+ first.newFixedLengthChromosome(child1Rep),
+ second.newFixedLengthChromosome(child2Rep));
+ }
+}
diff --git a/src/main/java/org/apache/commons/math3/genetics/OrderedCrossover.java b/src/main/java/org/apache/commons/math3/genetics/OrderedCrossover.java
new file mode 100644
index 0000000..eeb62ed
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/genetics/OrderedCrossover.java
@@ -0,0 +1,154 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math3.genetics;
+
+import org.apache.commons.math3.exception.DimensionMismatchException;
+import org.apache.commons.math3.exception.MathIllegalArgumentException;
+import org.apache.commons.math3.exception.util.LocalizedFormats;
+import org.apache.commons.math3.random.RandomGenerator;
+import org.apache.commons.math3.util.FastMath;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+/**
+ * Order 1 Crossover [OX1] builds offspring from <b>ordered</b> chromosomes by copying a consecutive
+ * slice from one parent, and filling up the remaining genes from the other parent as they appear.
+ *
+ * <p>This policy works by applying the following rules:
+ *
+ * <ol>
+ * <li>select a random slice of consecutive genes from parent 1
+ * <li>copy the slice to child 1 and mark out the genes in parent 2
+ * <li>starting from the right side of the slice, copy genes from parent 2 as they appear to child
+ * 1 if they are not yet marked out.
+ * </ol>
+ *
+ * <p>Example (random sublist from index 3 to 7, underlined):
+ *
+ * <pre>
+ * p1 = (8 4 7 3 6 2 5 1 9 0) X c1 = (0 4 7 3 6 2 5 1 8 9)
+ * --------- ---------
+ * p2 = (0 1 2 3 4 5 6 7 8 9) X c2 = (8 1 2 3 4 5 6 7 9 0)
+ * </pre>
+ *
+ * <p>This policy works only on {@link AbstractListChromosome}, and therefore it is parameterized by
+ * T. Moreover, the chromosomes must have same lengths.
+ *
+ * @see <a
+ * href="http://www.rubicite.com/Tutorials/GeneticAlgorithms/CrossoverOperators/Order1CrossoverOperator.aspx">
+ * Order 1 Crossover Operator</a>
+ * @param <T> generic type of the {@link AbstractListChromosome}s for crossover
+ * @since 3.1
+ */
+public class OrderedCrossover<T> implements CrossoverPolicy {
+
+ /**
+ * {@inheritDoc}
+ *
+ * @throws MathIllegalArgumentException iff one of the chromosomes is not an instance of {@link
+ * AbstractListChromosome}
+ * @throws DimensionMismatchException if the length of the two chromosomes is different
+ */
+ @SuppressWarnings("unchecked")
+ public ChromosomePair crossover(final Chromosome first, final Chromosome second)
+ throws DimensionMismatchException, MathIllegalArgumentException {
+
+ if (!(first instanceof AbstractListChromosome<?>
+ && second instanceof AbstractListChromosome<?>)) {
+ throw new MathIllegalArgumentException(
+ LocalizedFormats.INVALID_FIXED_LENGTH_CHROMOSOME);
+ }
+ return mate((AbstractListChromosome<T>) first, (AbstractListChromosome<T>) second);
+ }
+
+ /**
+ * Helper for {@link #crossover(Chromosome, Chromosome)}. Performs the actual crossover.
+ *
+ * @param first the first chromosome
+ * @param second the second chromosome
+ * @return the pair of new chromosomes that resulted from the crossover
+ * @throws DimensionMismatchException if the length of the two chromosomes is different
+ */
+ protected ChromosomePair mate(
+ final AbstractListChromosome<T> first, final AbstractListChromosome<T> second)
+ throws DimensionMismatchException {
+
+ final int length = first.getLength();
+ if (length != second.getLength()) {
+ throw new DimensionMismatchException(second.getLength(), length);
+ }
+
+ // array representations of the parents
+ final List<T> parent1Rep = first.getRepresentation();
+ final List<T> parent2Rep = second.getRepresentation();
+ // and of the children
+ final List<T> child1 = new ArrayList<T>(length);
+ final List<T> child2 = new ArrayList<T>(length);
+ // sets of already inserted items for quick access
+ final Set<T> child1Set = new HashSet<T>(length);
+ final Set<T> child2Set = new HashSet<T>(length);
+
+ final RandomGenerator random = GeneticAlgorithm.getRandomGenerator();
+ // choose random points, making sure that lb < ub.
+ int a = random.nextInt(length);
+ int b;
+ do {
+ b = random.nextInt(length);
+ } while (a == b);
+ // determine the lower and upper bounds
+ final int lb = FastMath.min(a, b);
+ final int ub = FastMath.max(a, b);
+
+ // add the subLists that are between lb and ub
+ child1.addAll(parent1Rep.subList(lb, ub + 1));
+ child1Set.addAll(child1);
+ child2.addAll(parent2Rep.subList(lb, ub + 1));
+ child2Set.addAll(child2);
+
+ // iterate over every item in the parents
+ for (int i = 1; i <= length; i++) {
+ final int idx = (ub + i) % length;
+
+ // retrieve the current item in each parent
+ final T item1 = parent1Rep.get(idx);
+ final T item2 = parent2Rep.get(idx);
+
+ // if the first child already contains the item in the second parent add it
+ if (!child1Set.contains(item2)) {
+ child1.add(item2);
+ child1Set.add(item2);
+ }
+
+ // if the second child already contains the item in the first parent add it
+ if (!child2Set.contains(item1)) {
+ child2.add(item1);
+ child2Set.add(item1);
+ }
+ }
+
+ // rotate so that the original slice is in the same place as in the parents.
+ Collections.rotate(child1, lb);
+ Collections.rotate(child2, lb);
+
+ return new ChromosomePair(
+ first.newFixedLengthChromosome(child1), second.newFixedLengthChromosome(child2));
+ }
+}
diff --git a/src/main/java/org/apache/commons/math3/genetics/PermutationChromosome.java b/src/main/java/org/apache/commons/math3/genetics/PermutationChromosome.java
new file mode 100644
index 0000000..0857733
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/genetics/PermutationChromosome.java
@@ -0,0 +1,38 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math3.genetics;
+
+import java.util.List;
+
+/**
+ * Interface indicating that the chromosome represents a permutation of objects.
+ *
+ * @param <T> type of the permuted objects
+ * @since 2.0
+ */
+public interface PermutationChromosome<T> {
+
+ /**
+ * Permutes the <code>sequence</code> of objects of type T according to the permutation this
+ * chromosome represents. For example, if this chromosome represents a permutation (3,0,1,2),
+ * and the unpermuted sequence is (a,b,c,d), this yields (d,a,b,c).
+ *
+ * @param sequence the unpermuted (original) sequence of objects
+ * @return permutation of <code>sequence</code> represented by this permutation
+ */
+ List<T> decode(List<T> sequence);
+}
diff --git a/src/main/java/org/apache/commons/math3/genetics/Population.java b/src/main/java/org/apache/commons/math3/genetics/Population.java
new file mode 100644
index 0000000..25b6c2a
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/genetics/Population.java
@@ -0,0 +1,63 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math3.genetics;
+
+import org.apache.commons.math3.exception.NumberIsTooLargeException;
+
+/**
+ * A collection of chromosomes that facilitates generational evolution.
+ *
+ * @since 2.0
+ */
+public interface Population extends Iterable<Chromosome> {
+ /**
+ * Access the current population size.
+ *
+ * @return the current population size.
+ */
+ int getPopulationSize();
+
+ /**
+ * Access the maximum population size.
+ *
+ * @return the maximum population size.
+ */
+ int getPopulationLimit();
+
+ /**
+ * Start the population for the next generation.
+ *
+ * @return the beginnings of the next generation.
+ */
+ Population nextGeneration();
+
+ /**
+ * Add the given chromosome to the population.
+ *
+ * @param chromosome the chromosome to add.
+ * @throws NumberIsTooLargeException if the population would exceed the population limit when
+ * adding this chromosome
+ */
+ void addChromosome(Chromosome chromosome) throws NumberIsTooLargeException;
+
+ /**
+ * Access the fittest chromosome in this population.
+ *
+ * @return the fittest chromosome.
+ */
+ Chromosome getFittestChromosome();
+}
diff --git a/src/main/java/org/apache/commons/math3/genetics/RandomKey.java b/src/main/java/org/apache/commons/math3/genetics/RandomKey.java
new file mode 100644
index 0000000..b33eaec
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/genetics/RandomKey.java
@@ -0,0 +1,296 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math3.genetics;
+
+import org.apache.commons.math3.exception.DimensionMismatchException;
+import org.apache.commons.math3.exception.MathIllegalArgumentException;
+import org.apache.commons.math3.exception.util.LocalizedFormats;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.List;
+
+/**
+ * Random Key chromosome is used for permutation representation. It is a vector of a fixed length of
+ * real numbers in [0,1] interval. The index of the i-th smallest value in the vector represents an
+ * i-th member of the permutation.
+ *
+ * <p>For example, the random key [0.2, 0.3, 0.8, 0.1] corresponds to the permutation of indices
+ * (3,0,1,2). If the original (unpermuted) sequence would be (a,b,c,d), this would mean the sequence
+ * (d,a,b,c).
+ *
+ * <p>With this representation, common operators like n-point crossover can be used, because any
+ * such chromosome represents a valid permutation.
+ *
+ * <p>Since the chromosome (and thus its arrayRepresentation) is immutable, the array representation
+ * is sorted only once in the constructor.
+ *
+ * <p>For details, see:
+ *
+ * <ul>
+ * <li>Bean, J.C.: Genetic algorithms and random keys for sequencing and optimization. ORSA
+ * Journal on Computing 6 (1994) 154-160
+ * <li>Rothlauf, F.: Representations for Genetic and Evolutionary Algorithms. Volume 104 of
+ * Studies in Fuzziness and Soft Computing. Physica-Verlag, Heidelberg (2002)
+ * </ul>
+ *
+ * @param <T> type of the permuted objects
+ * @since 2.0
+ */
+public abstract class RandomKey<T> extends AbstractListChromosome<Double>
+ implements PermutationChromosome<T> {
+
+ /** Cache of sorted representation (unmodifiable). */
+ private final List<Double> sortedRepresentation;
+
+ /** Base sequence [0,1,...,n-1], permuted according to the representation (unmodifiable). */
+ private final List<Integer> baseSeqPermutation;
+
+ /**
+ * Constructor.
+ *
+ * @param representation list of [0,1] values representing the permutation
+ * @throws InvalidRepresentationException iff the <code>representation</code> can not represent
+ * a valid chromosome
+ */
+ public RandomKey(final List<Double> representation) throws InvalidRepresentationException {
+ super(representation);
+ // store the sorted representation
+ List<Double> sortedRepr = new ArrayList<Double>(getRepresentation());
+ Collections.sort(sortedRepr);
+ sortedRepresentation = Collections.unmodifiableList(sortedRepr);
+ // store the permutation of [0,1,...,n-1] list for toString() and isSame() methods
+ baseSeqPermutation =
+ Collections.unmodifiableList(
+ decodeGeneric(
+ baseSequence(getLength()),
+ getRepresentation(),
+ sortedRepresentation));
+ }
+
+ /**
+ * Constructor.
+ *
+ * @param representation array of [0,1] values representing the permutation
+ * @throws InvalidRepresentationException iff the <code>representation</code> can not represent
+ * a valid chromosome
+ */
+ public RandomKey(final Double[] representation) throws InvalidRepresentationException {
+ this(Arrays.asList(representation));
+ }
+
+ /** {@inheritDoc} */
+ public List<T> decode(final List<T> sequence) {
+ return decodeGeneric(sequence, getRepresentation(), sortedRepresentation);
+ }
+
+ /**
+ * Decodes a permutation represented by <code>representation</code> and returns a (generic) list
+ * with the permuted values.
+ *
+ * @param <S> generic type of the sequence values
+ * @param sequence the unpermuted sequence
+ * @param representation representation of the permutation ([0,1] vector)
+ * @param sortedRepr sorted <code>representation</code>
+ * @return list with the sequence values permuted according to the representation
+ * @throws DimensionMismatchException iff the length of the <code>sequence</code>, <code>
+ * representation</code> or <code>sortedRepr</code> lists are not equal
+ */
+ private static <S> List<S> decodeGeneric(
+ final List<S> sequence, List<Double> representation, final List<Double> sortedRepr)
+ throws DimensionMismatchException {
+
+ int l = sequence.size();
+
+ // the size of the three lists must be equal
+ if (representation.size() != l) {
+ throw new DimensionMismatchException(representation.size(), l);
+ }
+ if (sortedRepr.size() != l) {
+ throw new DimensionMismatchException(sortedRepr.size(), l);
+ }
+
+ // do not modify the original representation
+ List<Double> reprCopy = new ArrayList<Double>(representation);
+
+ // now find the indices in the original repr and use them for permuting
+ List<S> res = new ArrayList<S>(l);
+ for (int i = 0; i < l; i++) {
+ int index = reprCopy.indexOf(sortedRepr.get(i));
+ res.add(sequence.get(index));
+ reprCopy.set(index, null);
+ }
+ return res;
+ }
+
+ /**
+ * Returns <code>true</code> iff <code>another</code> is a RandomKey and encodes the same
+ * permutation.
+ *
+ * @param another chromosome to compare
+ * @return true iff chromosomes encode the same permutation
+ */
+ @Override
+ protected boolean isSame(final Chromosome another) {
+ // type check
+ if (!(another instanceof RandomKey<?>)) {
+ return false;
+ }
+ RandomKey<?> anotherRk = (RandomKey<?>) another;
+ // size check
+ if (getLength() != anotherRk.getLength()) {
+ return false;
+ }
+
+ // two different representations can still encode the same permutation
+ // the ordering is what counts
+ List<Integer> thisPerm = this.baseSeqPermutation;
+ List<Integer> anotherPerm = anotherRk.baseSeqPermutation;
+
+ for (int i = 0; i < getLength(); i++) {
+ if (thisPerm.get(i) != anotherPerm.get(i)) {
+ return false;
+ }
+ }
+ // the permutations are the same
+ return true;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ protected void checkValidity(final List<Double> chromosomeRepresentation)
+ throws InvalidRepresentationException {
+
+ for (double val : chromosomeRepresentation) {
+ if (val < 0 || val > 1) {
+ throw new InvalidRepresentationException(
+ LocalizedFormats.OUT_OF_RANGE_SIMPLE, val, 0, 1);
+ }
+ }
+ }
+
+ /**
+ * Generates a representation corresponding to a random permutation of length l which can be
+ * passed to the RandomKey constructor.
+ *
+ * @param l length of the permutation
+ * @return representation of a random permutation
+ */
+ public static final List<Double> randomPermutation(final int l) {
+ List<Double> repr = new ArrayList<Double>(l);
+ for (int i = 0; i < l; i++) {
+ repr.add(GeneticAlgorithm.getRandomGenerator().nextDouble());
+ }
+ return repr;
+ }
+
+ /**
+ * Generates a representation corresponding to an identity permutation of length l which can be
+ * passed to the RandomKey constructor.
+ *
+ * @param l length of the permutation
+ * @return representation of an identity permutation
+ */
+ public static final List<Double> identityPermutation(final int l) {
+ List<Double> repr = new ArrayList<Double>(l);
+ for (int i = 0; i < l; i++) {
+ repr.add((double) i / l);
+ }
+ return repr;
+ }
+
+ /**
+ * Generates a representation of a permutation corresponding to the <code>data</code> sorted by
+ * <code>comparator</code>. The <code>data</code> is not modified during the process.
+ *
+ * <p>This is useful if you want to inject some permutations to the initial population.
+ *
+ * @param <S> type of the data
+ * @param data list of data determining the order
+ * @param comparator how the data will be compared
+ * @return list representation of the permutation corresponding to the parameters
+ */
+ public static <S> List<Double> comparatorPermutation(
+ final List<S> data, final Comparator<S> comparator) {
+ List<S> sortedData = new ArrayList<S>(data);
+ Collections.sort(sortedData, comparator);
+
+ return inducedPermutation(data, sortedData);
+ }
+
+ /**
+ * Generates a representation of a permutation corresponding to a permutation which yields
+ * <code>permutedData</code> when applied to <code>originalData</code>.
+ *
+ * <p>This method can be viewed as an inverse to {@link #decode(List)}.
+ *
+ * @param <S> type of the data
+ * @param originalData the original, unpermuted data
+ * @param permutedData the data, somehow permuted
+ * @return representation of a permutation corresponding to the permutation <code>
+ * originalData -> permutedData</code>
+ * @throws DimensionMismatchException iff the length of <code>originalData</code> and <code>
+ * permutedData</code> lists are not equal
+ * @throws MathIllegalArgumentException iff the <code>permutedData</code> and <code>originalData
+ * </code> lists contain different data
+ */
+ public static <S> List<Double> inducedPermutation(
+ final List<S> originalData, final List<S> permutedData)
+ throws DimensionMismatchException, MathIllegalArgumentException {
+
+ if (originalData.size() != permutedData.size()) {
+ throw new DimensionMismatchException(permutedData.size(), originalData.size());
+ }
+ int l = originalData.size();
+
+ List<S> origDataCopy = new ArrayList<S>(originalData);
+
+ Double[] res = new Double[l];
+ for (int i = 0; i < l; i++) {
+ int index = origDataCopy.indexOf(permutedData.get(i));
+ if (index == -1) {
+ throw new MathIllegalArgumentException(
+ LocalizedFormats.DIFFERENT_ORIG_AND_PERMUTED_DATA);
+ }
+ res[index] = (double) i / l;
+ origDataCopy.set(index, null);
+ }
+ return Arrays.asList(res);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public String toString() {
+ return String.format("(f=%s pi=(%s))", getFitness(), baseSeqPermutation);
+ }
+
+ /**
+ * Helper for constructor. Generates a list of natural numbers (0,1,...,l-1).
+ *
+ * @param l length of list to generate
+ * @return list of integers from 0 to l-1
+ */
+ private static List<Integer> baseSequence(final int l) {
+ List<Integer> baseSequence = new ArrayList<Integer>(l);
+ for (int i = 0; i < l; i++) {
+ baseSequence.add(i);
+ }
+ return baseSequence;
+ }
+}
diff --git a/src/main/java/org/apache/commons/math3/genetics/RandomKeyMutation.java b/src/main/java/org/apache/commons/math3/genetics/RandomKeyMutation.java
new file mode 100644
index 0000000..6d1512a
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/genetics/RandomKeyMutation.java
@@ -0,0 +1,55 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math3.genetics;
+
+import org.apache.commons.math3.exception.MathIllegalArgumentException;
+import org.apache.commons.math3.exception.util.LocalizedFormats;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Mutation operator for {@link RandomKey}s. Changes a randomly chosen element of the array
+ * representation to a random value uniformly distributed in [0,1].
+ *
+ * @since 2.0
+ */
+public class RandomKeyMutation implements MutationPolicy {
+
+ /**
+ * {@inheritDoc}
+ *
+ * @throws MathIllegalArgumentException if <code>original</code> is not a {@link RandomKey}
+ * instance
+ */
+ public Chromosome mutate(final Chromosome original) throws MathIllegalArgumentException {
+ if (!(original instanceof RandomKey<?>)) {
+ throw new MathIllegalArgumentException(
+ LocalizedFormats.RANDOMKEY_MUTATION_WRONG_CLASS,
+ original.getClass().getSimpleName());
+ }
+
+ RandomKey<?> originalRk = (RandomKey<?>) original;
+ List<Double> repr = originalRk.getRepresentation();
+ int rInd = GeneticAlgorithm.getRandomGenerator().nextInt(repr.size());
+
+ List<Double> newRepr = new ArrayList<Double>(repr);
+ newRepr.set(rInd, GeneticAlgorithm.getRandomGenerator().nextDouble());
+
+ return originalRk.newFixedLengthChromosome(newRepr);
+ }
+}
diff --git a/src/main/java/org/apache/commons/math3/genetics/SelectionPolicy.java b/src/main/java/org/apache/commons/math3/genetics/SelectionPolicy.java
new file mode 100644
index 0000000..c19c922
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/genetics/SelectionPolicy.java
@@ -0,0 +1,36 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math3.genetics;
+
+import org.apache.commons.math3.exception.MathIllegalArgumentException;
+
+/**
+ * Algorithm used to select a chromosome pair from a population.
+ *
+ * @since 2.0
+ */
+public interface SelectionPolicy {
+ /**
+ * Select two chromosomes from the population.
+ *
+ * @param population the population from which the chromosomes are choosen.
+ * @return the selected chromosomes.
+ * @throws MathIllegalArgumentException if the population is not compatible with this {@link
+ * SelectionPolicy}
+ */
+ ChromosomePair select(Population population) throws MathIllegalArgumentException;
+}
diff --git a/src/main/java/org/apache/commons/math3/genetics/StoppingCondition.java b/src/main/java/org/apache/commons/math3/genetics/StoppingCondition.java
new file mode 100644
index 0000000..06eb2ad
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/genetics/StoppingCondition.java
@@ -0,0 +1,33 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math3.genetics;
+
+/**
+ * Algorithm used to determine when to stop evolution.
+ *
+ * @since 2.0
+ */
+public interface StoppingCondition {
+ /**
+ * Determine whether or not the given population satisfies the stopping condition.
+ *
+ * @param population the population to test.
+ * @return <code>true</code> if this stopping condition is met by the given population, <code>
+ * false</code> otherwise.
+ */
+ boolean isSatisfied(Population population);
+}
diff --git a/src/main/java/org/apache/commons/math3/genetics/TournamentSelection.java b/src/main/java/org/apache/commons/math3/genetics/TournamentSelection.java
new file mode 100644
index 0000000..2a9d9d7
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/genetics/TournamentSelection.java
@@ -0,0 +1,119 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math3.genetics;
+
+import org.apache.commons.math3.exception.MathIllegalArgumentException;
+import org.apache.commons.math3.exception.util.LocalizedFormats;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Tournament selection scheme. Each of the two selected chromosomes is selected based on n-ary
+ * tournament -- this is done by drawing {@link #arity} random chromosomes without replacement from
+ * the population, and then selecting the fittest chromosome among them.
+ *
+ * @since 2.0
+ */
+public class TournamentSelection implements SelectionPolicy {
+
+ /** number of chromosomes included in the tournament selections */
+ private int arity;
+
+ /**
+ * Creates a new TournamentSelection instance.
+ *
+ * @param arity how many chromosomes will be drawn to the tournament
+ */
+ public TournamentSelection(final int arity) {
+ this.arity = arity;
+ }
+
+ /**
+ * Select two chromosomes from the population. Each of the two selected chromosomes is selected
+ * based on n-ary tournament -- this is done by drawing {@link #arity} random chromosomes
+ * without replacement from the population, and then selecting the fittest chromosome among
+ * them.
+ *
+ * @param population the population from which the chromosomes are chosen.
+ * @return the selected chromosomes.
+ * @throws MathIllegalArgumentException if the tournament arity is bigger than the population
+ * size
+ */
+ public ChromosomePair select(final Population population) throws MathIllegalArgumentException {
+ return new ChromosomePair(
+ tournament((ListPopulation) population), tournament((ListPopulation) population));
+ }
+
+ /**
+ * Helper for {@link #select(Population)}. Draw {@link #arity} random chromosomes without
+ * replacement from the population, and then select the fittest chromosome among them.
+ *
+ * @param population the population from which the chromosomes are chosen.
+ * @return the selected chromosome.
+ * @throws MathIllegalArgumentException if the tournament arity is bigger than the population
+ * size
+ */
+ private Chromosome tournament(final ListPopulation population)
+ throws MathIllegalArgumentException {
+ if (population.getPopulationSize() < this.arity) {
+ throw new MathIllegalArgumentException(
+ LocalizedFormats.TOO_LARGE_TOURNAMENT_ARITY,
+ arity,
+ population.getPopulationSize());
+ }
+ // auxiliary population
+ ListPopulation tournamentPopulation =
+ new ListPopulation(this.arity) {
+ /** {@inheritDoc} */
+ public Population nextGeneration() {
+ // not useful here
+ return null;
+ }
+ };
+
+ // create a copy of the chromosome list
+ List<Chromosome> chromosomes = new ArrayList<Chromosome>(population.getChromosomes());
+ for (int i = 0; i < this.arity; i++) {
+ // select a random individual and add it to the tournament
+ int rind = GeneticAlgorithm.getRandomGenerator().nextInt(chromosomes.size());
+ tournamentPopulation.addChromosome(chromosomes.get(rind));
+ // do not select it again
+ chromosomes.remove(rind);
+ }
+ // the winner takes it all
+ return tournamentPopulation.getFittestChromosome();
+ }
+
+ /**
+ * Gets the arity (number of chromosomes drawn to the tournament).
+ *
+ * @return arity of the tournament
+ */
+ public int getArity() {
+ return arity;
+ }
+
+ /**
+ * Sets the arity (number of chromosomes drawn to the tournament).
+ *
+ * @param arity arity of the tournament
+ */
+ public void setArity(final int arity) {
+ this.arity = arity;
+ }
+}
diff --git a/src/main/java/org/apache/commons/math3/genetics/UniformCrossover.java b/src/main/java/org/apache/commons/math3/genetics/UniformCrossover.java
new file mode 100644
index 0000000..c11450a
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/genetics/UniformCrossover.java
@@ -0,0 +1,140 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math3.genetics;
+
+import org.apache.commons.math3.exception.DimensionMismatchException;
+import org.apache.commons.math3.exception.MathIllegalArgumentException;
+import org.apache.commons.math3.exception.OutOfRangeException;
+import org.apache.commons.math3.exception.util.LocalizedFormats;
+import org.apache.commons.math3.random.RandomGenerator;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Perform Uniform Crossover [UX] on the specified chromosomes. A fixed mixing ratio is used to
+ * combine genes from the first and second parents, e.g. using a ratio of 0.5 would result in
+ * approximately 50% of genes coming from each parent. This is typically a poor method of crossover,
+ * but empirical evidence suggests that it is more exploratory and results in a larger part of the
+ * problem space being searched.
+ *
+ * <p>This crossover policy evaluates each gene of the parent chromosomes by chosing a uniform
+ * random number {@code p} in the range [0, 1]. If {@code p} &lt; {@code ratio}, the parent genes
+ * are swapped. This means with a ratio of 0.7, 30% of the genes from the first parent and 70% from
+ * the second parent will be selected for the first offspring (and vice versa for the second
+ * offspring).
+ *
+ * <p>This policy works only on {@link AbstractListChromosome}, and therefore it is parameterized by
+ * T. Moreover, the chromosomes must have same lengths.
+ *
+ * @see <a href="http://en.wikipedia.org/wiki/Crossover_%28genetic_algorithm%29">Crossover
+ * techniques (Wikipedia)</a>
+ * @see <a
+ * href="http://www.obitko.com/tutorials/genetic-algorithms/crossover-mutation.php">Crossover
+ * (Obitko.com)</a>
+ * @see <a href="http://www.tomaszgwiazda.com/uniformX.htm">Uniform crossover</a>
+ * @param <T> generic type of the {@link AbstractListChromosome}s for crossover
+ * @since 3.1
+ */
+public class UniformCrossover<T> implements CrossoverPolicy {
+
+ /** The mixing ratio. */
+ private final double ratio;
+
+ /**
+ * Creates a new {@link UniformCrossover} policy using the given mixing ratio.
+ *
+ * @param ratio the mixing ratio
+ * @throws OutOfRangeException if the mixing ratio is outside the [0, 1] range
+ */
+ public UniformCrossover(final double ratio) throws OutOfRangeException {
+ if (ratio < 0.0d || ratio > 1.0d) {
+ throw new OutOfRangeException(LocalizedFormats.CROSSOVER_RATE, ratio, 0.0d, 1.0d);
+ }
+ this.ratio = ratio;
+ }
+
+ /**
+ * Returns the mixing ratio used by this {@link CrossoverPolicy}.
+ *
+ * @return the mixing ratio
+ */
+ public double getRatio() {
+ return ratio;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @throws MathIllegalArgumentException iff one of the chromosomes is not an instance of {@link
+ * AbstractListChromosome}
+ * @throws DimensionMismatchException if the length of the two chromosomes is different
+ */
+ @SuppressWarnings("unchecked")
+ public ChromosomePair crossover(final Chromosome first, final Chromosome second)
+ throws DimensionMismatchException, MathIllegalArgumentException {
+
+ if (!(first instanceof AbstractListChromosome<?>
+ && second instanceof AbstractListChromosome<?>)) {
+ throw new MathIllegalArgumentException(
+ LocalizedFormats.INVALID_FIXED_LENGTH_CHROMOSOME);
+ }
+ return mate((AbstractListChromosome<T>) first, (AbstractListChromosome<T>) second);
+ }
+
+ /**
+ * Helper for {@link #crossover(Chromosome, Chromosome)}. Performs the actual crossover.
+ *
+ * @param first the first chromosome
+ * @param second the second chromosome
+ * @return the pair of new chromosomes that resulted from the crossover
+ * @throws DimensionMismatchException if the length of the two chromosomes is different
+ */
+ private ChromosomePair mate(
+ final AbstractListChromosome<T> first, final AbstractListChromosome<T> second)
+ throws DimensionMismatchException {
+ final int length = first.getLength();
+ if (length != second.getLength()) {
+ throw new DimensionMismatchException(second.getLength(), length);
+ }
+
+ // array representations of the parents
+ final List<T> parent1Rep = first.getRepresentation();
+ final List<T> parent2Rep = second.getRepresentation();
+ // and of the children
+ final List<T> child1Rep = new ArrayList<T>(length);
+ final List<T> child2Rep = new ArrayList<T>(length);
+
+ final RandomGenerator random = GeneticAlgorithm.getRandomGenerator();
+
+ for (int index = 0; index < length; index++) {
+
+ if (random.nextDouble() < ratio) {
+ // swap the bits -> take other parent
+ child1Rep.add(parent2Rep.get(index));
+ child2Rep.add(parent1Rep.get(index));
+ } else {
+ child1Rep.add(parent1Rep.get(index));
+ child2Rep.add(parent2Rep.get(index));
+ }
+ }
+
+ return new ChromosomePair(
+ first.newFixedLengthChromosome(child1Rep),
+ second.newFixedLengthChromosome(child2Rep));
+ }
+}
diff --git a/src/main/java/org/apache/commons/math3/genetics/package-info.java b/src/main/java/org/apache/commons/math3/genetics/package-info.java
new file mode 100644
index 0000000..32109e8
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/genetics/package-info.java
@@ -0,0 +1,18 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/** This package provides Genetic Algorithms components and implementations. */
+package org.apache.commons.math3.genetics;
diff --git a/src/main/java/org/apache/commons/math3/geometry/Point.java b/src/main/java/org/apache/commons/math3/geometry/Point.java
new file mode 100644
index 0000000..49f290a
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/geometry/Point.java
@@ -0,0 +1,52 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math3.geometry;
+
+import java.io.Serializable;
+
+/**
+ * This interface represents a generic geometrical point.
+ *
+ * @param <S> Type of the space.
+ * @see Space
+ * @see Vector
+ * @since 3.3
+ */
+public interface Point<S extends Space> extends Serializable {
+
+ /**
+ * Get the space to which the point belongs.
+ *
+ * @return containing space
+ */
+ Space getSpace();
+
+ /**
+ * Returns true if any coordinate of this point is NaN; false otherwise
+ *
+ * @return true if any coordinate of this point is NaN; false otherwise
+ */
+ boolean isNaN();
+
+ /**
+ * Compute the distance between the instance and another point.
+ *
+ * @param p second point
+ * @return the distance between the instance and p
+ */
+ double distance(Point<S> p);
+}
diff --git a/src/main/java/org/apache/commons/math3/geometry/Space.java b/src/main/java/org/apache/commons/math3/geometry/Space.java
new file mode 100644
index 0000000..7b563c6
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/geometry/Space.java
@@ -0,0 +1,47 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math3.geometry;
+
+import org.apache.commons.math3.exception.MathUnsupportedOperationException;
+
+import java.io.Serializable;
+
+/**
+ * This interface represents a generic space, with affine and vectorial counterparts.
+ *
+ * @see Vector
+ * @since 3.0
+ */
+public interface Space extends Serializable {
+
+ /**
+ * Get the dimension of the space.
+ *
+ * @return dimension of the space
+ */
+ int getDimension();
+
+ /**
+ * Get the n-1 dimension subspace of this space.
+ *
+ * @return n-1 dimension sub-space of this space
+ * @see #getDimension()
+ * @exception MathUnsupportedOperationException for dimension-1 spaces which do not have
+ * sub-spaces
+ */
+ Space getSubSpace() throws MathUnsupportedOperationException;
+}
diff --git a/src/main/java/org/apache/commons/math3/geometry/Vector.java b/src/main/java/org/apache/commons/math3/geometry/Vector.java
new file mode 100644
index 0000000..92ad04a
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/geometry/Vector.java
@@ -0,0 +1,194 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math3.geometry;
+
+import org.apache.commons.math3.exception.MathArithmeticException;
+
+import java.text.NumberFormat;
+
+/**
+ * This interface represents a generic vector in a vectorial space or a point in an affine space.
+ *
+ * @param <S> Type of the space.
+ * @see Space
+ * @see Point
+ * @since 3.0
+ */
+public interface Vector<S extends Space> extends Point<S> {
+
+ /**
+ * Get the null vector of the vectorial space or origin point of the affine space.
+ *
+ * @return null vector of the vectorial space or origin point of the affine space
+ */
+ Vector<S> getZero();
+
+ /**
+ * Get the L<sub>1</sub> norm for the vector.
+ *
+ * @return L<sub>1</sub> norm for the vector
+ */
+ double getNorm1();
+
+ /**
+ * Get the L<sub>2</sub> norm for the vector.
+ *
+ * @return Euclidean norm for the vector
+ */
+ double getNorm();
+
+ /**
+ * Get the square of the norm for the vector.
+ *
+ * @return square of the Euclidean norm for the vector
+ */
+ double getNormSq();
+
+ /**
+ * Get the L<sub>&infin;</sub> norm for the vector.
+ *
+ * @return L<sub>&infin;</sub> norm for the vector
+ */
+ double getNormInf();
+
+ /**
+ * Add a vector to the instance.
+ *
+ * @param v vector to add
+ * @return a new vector
+ */
+ Vector<S> add(Vector<S> v);
+
+ /**
+ * Add a scaled vector to the instance.
+ *
+ * @param factor scale factor to apply to v before adding it
+ * @param v vector to add
+ * @return a new vector
+ */
+ Vector<S> add(double factor, Vector<S> v);
+
+ /**
+ * Subtract a vector from the instance.
+ *
+ * @param v vector to subtract
+ * @return a new vector
+ */
+ Vector<S> subtract(Vector<S> v);
+
+ /**
+ * Subtract a scaled vector from the instance.
+ *
+ * @param factor scale factor to apply to v before subtracting it
+ * @param v vector to subtract
+ * @return a new vector
+ */
+ Vector<S> subtract(double factor, Vector<S> v);
+
+ /**
+ * Get the opposite of the instance.
+ *
+ * @return a new vector which is opposite to the instance
+ */
+ Vector<S> negate();
+
+ /**
+ * Get a normalized vector aligned with the instance.
+ *
+ * @return a new normalized vector
+ * @exception MathArithmeticException if the norm is zero
+ */
+ Vector<S> normalize() throws MathArithmeticException;
+
+ /**
+ * Multiply the instance by a scalar.
+ *
+ * @param a scalar
+ * @return a new vector
+ */
+ Vector<S> scalarMultiply(double a);
+
+ /**
+ * Returns true if any coordinate of this vector is infinite and none are NaN; false otherwise
+ *
+ * @return true if any coordinate of this vector is infinite and none are NaN; false otherwise
+ */
+ boolean isInfinite();
+
+ /**
+ * Compute the distance between the instance and another vector according to the L<sub>1</sub>
+ * norm.
+ *
+ * <p>Calling this method is equivalent to calling: <code>q.subtract(p).getNorm1()</code> except
+ * that no intermediate vector is built
+ *
+ * @param v second vector
+ * @return the distance between the instance and p according to the L<sub>1</sub> norm
+ */
+ double distance1(Vector<S> v);
+
+ /**
+ * Compute the distance between the instance and another vector according to the L<sub>2</sub>
+ * norm.
+ *
+ * <p>Calling this method is equivalent to calling: <code>q.subtract(p).getNorm()</code> except
+ * that no intermediate vector is built
+ *
+ * @param v second vector
+ * @return the distance between the instance and p according to the L<sub>2</sub> norm
+ */
+ double distance(Vector<S> v);
+
+ /**
+ * Compute the distance between the instance and another vector according to the
+ * L<sub>&infin;</sub> norm.
+ *
+ * <p>Calling this method is equivalent to calling: <code>q.subtract(p).getNormInf()</code>
+ * except that no intermediate vector is built
+ *
+ * @param v second vector
+ * @return the distance between the instance and p according to the L<sub>&infin;</sub> norm
+ */
+ double distanceInf(Vector<S> v);
+
+ /**
+ * Compute the square of the distance between the instance and another vector.
+ *
+ * <p>Calling this method is equivalent to calling: <code>q.subtract(p).getNormSq()</code>
+ * except that no intermediate vector is built
+ *
+ * @param v second vector
+ * @return the square of the distance between the instance and p
+ */
+ double distanceSq(Vector<S> v);
+
+ /**
+ * Compute the dot-product of the instance and another vector.
+ *
+ * @param v second vector
+ * @return the dot product this.v
+ */
+ double dotProduct(Vector<S> v);
+
+ /**
+ * Get a string representation of this vector.
+ *
+ * @param format the custom format for components
+ * @return a string representation of this vector
+ */
+ String toString(final NumberFormat format);
+}
diff --git a/src/main/java/org/apache/commons/math3/geometry/VectorFormat.java b/src/main/java/org/apache/commons/math3/geometry/VectorFormat.java
new file mode 100644
index 0000000..7c6d0c5
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/geometry/VectorFormat.java
@@ -0,0 +1,307 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math3.geometry;
+
+import org.apache.commons.math3.exception.MathParseException;
+import org.apache.commons.math3.util.CompositeFormat;
+
+import java.text.FieldPosition;
+import java.text.NumberFormat;
+import java.text.ParsePosition;
+import java.util.Locale;
+
+/**
+ * Formats a vector in components list format "{x; y; ...}".
+ *
+ * <p>The prefix and suffix "{" and "}" and the separator "; " can be replaced by any user-defined
+ * strings. The number format for components can be configured.
+ *
+ * <p>White space is ignored at parse time, even if it is in the prefix, suffix or separator
+ * specifications. So even if the default separator does include a space character that is used at
+ * format time, both input string "{1;1;1}" and " { 1 ; 1 ; 1 } " will be parsed without error and
+ * the same vector will be returned. In the second case, however, the parse position after parsing
+ * will be just after the closing curly brace, i.e. just before the trailing space.
+ *
+ * <p><b>Note:</b> using "," as a separator may interfere with the grouping separator of the default
+ * {@link NumberFormat} for the current locale. Thus it is advised to use a {@link NumberFormat}
+ * instance with disabled grouping in such a case.
+ *
+ * @param <S> Type of the space.
+ * @since 3.0
+ */
+public abstract class VectorFormat<S extends Space> {
+
+ /** The default prefix: "{". */
+ public static final String DEFAULT_PREFIX = "{";
+
+ /** The default suffix: "}". */
+ public static final String DEFAULT_SUFFIX = "}";
+
+ /** The default separator: ", ". */
+ public static final String DEFAULT_SEPARATOR = "; ";
+
+ /** Prefix. */
+ private final String prefix;
+
+ /** Suffix. */
+ private final String suffix;
+
+ /** Separator. */
+ private final String separator;
+
+ /** Trimmed prefix. */
+ private final String trimmedPrefix;
+
+ /** Trimmed suffix. */
+ private final String trimmedSuffix;
+
+ /** Trimmed separator. */
+ private final String trimmedSeparator;
+
+ /** The format used for components. */
+ private final NumberFormat format;
+
+ /**
+ * Create an instance with default settings.
+ *
+ * <p>The instance uses the default prefix, suffix and separator: "{", "}", and "; " and the
+ * default number format for components.
+ */
+ protected VectorFormat() {
+ this(
+ DEFAULT_PREFIX,
+ DEFAULT_SUFFIX,
+ DEFAULT_SEPARATOR,
+ CompositeFormat.getDefaultNumberFormat());
+ }
+
+ /**
+ * Create an instance with a custom number format for components.
+ *
+ * @param format the custom format for components.
+ */
+ protected VectorFormat(final NumberFormat format) {
+ this(DEFAULT_PREFIX, DEFAULT_SUFFIX, DEFAULT_SEPARATOR, format);
+ }
+
+ /**
+ * Create an instance with custom prefix, suffix and separator.
+ *
+ * @param prefix prefix to use instead of the default "{"
+ * @param suffix suffix to use instead of the default "}"
+ * @param separator separator to use instead of the default "; "
+ */
+ protected VectorFormat(final String prefix, final String suffix, final String separator) {
+ this(prefix, suffix, separator, CompositeFormat.getDefaultNumberFormat());
+ }
+
+ /**
+ * Create an instance with custom prefix, suffix, separator and format for components.
+ *
+ * @param prefix prefix to use instead of the default "{"
+ * @param suffix suffix to use instead of the default "}"
+ * @param separator separator to use instead of the default "; "
+ * @param format the custom format for components.
+ */
+ protected VectorFormat(
+ final String prefix,
+ final String suffix,
+ final String separator,
+ final NumberFormat format) {
+ this.prefix = prefix;
+ this.suffix = suffix;
+ this.separator = separator;
+ trimmedPrefix = prefix.trim();
+ trimmedSuffix = suffix.trim();
+ trimmedSeparator = separator.trim();
+ this.format = format;
+ }
+
+ /**
+ * Get the set of locales for which point/vector formats are available.
+ *
+ * <p>This is the same set as the {@link NumberFormat} set.
+ *
+ * @return available point/vector format locales.
+ */
+ public static Locale[] getAvailableLocales() {
+ return NumberFormat.getAvailableLocales();
+ }
+
+ /**
+ * Get the format prefix.
+ *
+ * @return format prefix.
+ */
+ public String getPrefix() {
+ return prefix;
+ }
+
+ /**
+ * Get the format suffix.
+ *
+ * @return format suffix.
+ */
+ public String getSuffix() {
+ return suffix;
+ }
+
+ /**
+ * Get the format separator between components.
+ *
+ * @return format separator.
+ */
+ public String getSeparator() {
+ return separator;
+ }
+
+ /**
+ * Get the components format.
+ *
+ * @return components format.
+ */
+ public NumberFormat getFormat() {
+ return format;
+ }
+
+ /**
+ * Formats a {@link Vector} object to produce a string.
+ *
+ * @param vector the object to format.
+ * @return a formatted string.
+ */
+ public String format(Vector<S> vector) {
+ return format(vector, new StringBuffer(), new FieldPosition(0)).toString();
+ }
+
+ /**
+ * Formats a {@link Vector} object to produce a string.
+ *
+ * @param vector the object to format.
+ * @param toAppendTo where the text is to be appended
+ * @param pos On input: an alignment field, if desired. On output: the offsets of the alignment
+ * field
+ * @return the value passed in as toAppendTo.
+ */
+ public abstract StringBuffer format(
+ Vector<S> vector, StringBuffer toAppendTo, FieldPosition pos);
+
+ /**
+ * Formats the coordinates of a {@link Vector} to produce a string.
+ *
+ * @param toAppendTo where the text is to be appended
+ * @param pos On input: an alignment field, if desired. On output: the offsets of the alignment
+ * field
+ * @param coordinates coordinates of the object to format.
+ * @return the value passed in as toAppendTo.
+ */
+ protected StringBuffer format(
+ StringBuffer toAppendTo, FieldPosition pos, double... coordinates) {
+
+ pos.setBeginIndex(0);
+ pos.setEndIndex(0);
+
+ // format prefix
+ toAppendTo.append(prefix);
+
+ // format components
+ for (int i = 0; i < coordinates.length; ++i) {
+ if (i > 0) {
+ toAppendTo.append(separator);
+ }
+ CompositeFormat.formatDouble(coordinates[i], format, toAppendTo, pos);
+ }
+
+ // format suffix
+ toAppendTo.append(suffix);
+
+ return toAppendTo;
+ }
+
+ /**
+ * Parses a string to produce a {@link Vector} object.
+ *
+ * @param source the string to parse
+ * @return the parsed {@link Vector} object.
+ * @throws MathParseException if the beginning of the specified string cannot be parsed.
+ */
+ public abstract Vector<S> parse(String source) throws MathParseException;
+
+ /**
+ * Parses a string to produce a {@link Vector} object.
+ *
+ * @param source the string to parse
+ * @param pos input/output parsing parameter.
+ * @return the parsed {@link Vector} object.
+ */
+ public abstract Vector<S> parse(String source, ParsePosition pos);
+
+ /**
+ * Parses a string to produce an array of coordinates.
+ *
+ * @param dimension dimension of the space
+ * @param source the string to parse
+ * @param pos input/output parsing parameter.
+ * @return coordinates array.
+ */
+ protected double[] parseCoordinates(int dimension, String source, ParsePosition pos) {
+
+ int initialIndex = pos.getIndex();
+ double[] coordinates = new double[dimension];
+
+ // parse prefix
+ CompositeFormat.parseAndIgnoreWhitespace(source, pos);
+ if (!CompositeFormat.parseFixedstring(source, trimmedPrefix, pos)) {
+ return null;
+ }
+
+ for (int i = 0; i < dimension; ++i) {
+
+ // skip whitespace
+ CompositeFormat.parseAndIgnoreWhitespace(source, pos);
+
+ // parse separator
+ if (i > 0 && !CompositeFormat.parseFixedstring(source, trimmedSeparator, pos)) {
+ return null;
+ }
+
+ // skip whitespace
+ CompositeFormat.parseAndIgnoreWhitespace(source, pos);
+
+ // parse coordinate
+ Number c = CompositeFormat.parseNumber(source, format, pos);
+ if (c == null) {
+ // invalid coordinate
+ // set index back to initial, error index should already be set
+ pos.setIndex(initialIndex);
+ return null;
+ }
+
+ // store coordinate
+ coordinates[i] = c.doubleValue();
+ }
+
+ // parse suffix
+ CompositeFormat.parseAndIgnoreWhitespace(source, pos);
+ if (!CompositeFormat.parseFixedstring(source, trimmedSuffix, pos)) {
+ return null;
+ }
+
+ return coordinates;
+ }
+}
diff --git a/src/main/java/org/apache/commons/math3/geometry/enclosing/Encloser.java b/src/main/java/org/apache/commons/math3/geometry/enclosing/Encloser.java
new file mode 100644
index 0000000..9b2588a
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/geometry/enclosing/Encloser.java
@@ -0,0 +1,36 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math3.geometry.enclosing;
+
+import org.apache.commons.math3.geometry.Point;
+import org.apache.commons.math3.geometry.Space;
+
+/** Interface for algorithms computing enclosing balls.
+ * @param <S> Space type.
+ * @param <P> Point type.
+ * @see EnclosingBall
+ * @since 3.3
+ */
+public interface Encloser<S extends Space, P extends Point<S>> {
+
+ /** Find a ball enclosing a list of points.
+ * @param points points to enclose
+ * @return enclosing ball
+ */
+ EnclosingBall<S, P> enclose(Iterable<P> points);
+
+}
diff --git a/src/main/java/org/apache/commons/math3/geometry/enclosing/EnclosingBall.java b/src/main/java/org/apache/commons/math3/geometry/enclosing/EnclosingBall.java
new file mode 100644
index 0000000..eedbd46
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/geometry/enclosing/EnclosingBall.java
@@ -0,0 +1,103 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math3.geometry.enclosing;
+
+import java.io.Serializable;
+
+import org.apache.commons.math3.geometry.Point;
+import org.apache.commons.math3.geometry.Space;
+
+/** This class represents a ball enclosing some points.
+ * @param <S> Space type.
+ * @param <P> Point type.
+ * @see Space
+ * @see Point
+ * @see Encloser
+ * @since 3.3
+ */
+public class EnclosingBall<S extends Space, P extends Point<S>> implements Serializable {
+
+ /** Serializable UID. */
+ private static final long serialVersionUID = 20140126L;
+
+ /** Center of the ball. */
+ private final P center;
+
+ /** Radius of the ball. */
+ private final double radius;
+
+ /** Support points used to define the ball. */
+ private final P[] support;
+
+ /** Simple constructor.
+ * @param center center of the ball
+ * @param radius radius of the ball
+ * @param support support points used to define the ball
+ */
+ public EnclosingBall(final P center, final double radius, final P ... support) {
+ this.center = center;
+ this.radius = radius;
+ this.support = support.clone();
+ }
+
+ /** Get the center of the ball.
+ * @return center of the ball
+ */
+ public P getCenter() {
+ return center;
+ }
+
+ /** Get the radius of the ball.
+ * @return radius of the ball (can be negative if the ball is empty)
+ */
+ public double getRadius() {
+ return radius;
+ }
+
+ /** Get the support points used to define the ball.
+ * @return support points used to define the ball
+ */
+ public P[] getSupport() {
+ return support.clone();
+ }
+
+ /** Get the number of support points used to define the ball.
+ * @return number of support points used to define the ball
+ */
+ public int getSupportSize() {
+ return support.length;
+ }
+
+ /** Check if a point is within the ball or at boundary.
+ * @param point point to test
+ * @return true if the point is within the ball or at boundary
+ */
+ public boolean contains(final P point) {
+ return point.distance(center) <= radius;
+ }
+
+ /** Check if a point is within an enlarged ball or at boundary.
+ * @param point point to test
+ * @param margin margin to consider
+ * @return true if the point is within the ball enlarged
+ * by the margin or at boundary
+ */
+ public boolean contains(final P point, final double margin) {
+ return point.distance(center) <= radius + margin;
+ }
+
+}
diff --git a/src/main/java/org/apache/commons/math3/geometry/enclosing/SupportBallGenerator.java b/src/main/java/org/apache/commons/math3/geometry/enclosing/SupportBallGenerator.java
new file mode 100644
index 0000000..3a0f875
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/geometry/enclosing/SupportBallGenerator.java
@@ -0,0 +1,42 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math3.geometry.enclosing;
+
+import java.util.List;
+
+import org.apache.commons.math3.geometry.Point;
+import org.apache.commons.math3.geometry.Space;
+
+/** Interface for generating balls based on support points.
+ * <p>
+ * This generator is used in the {@link WelzlEncloser Emo Welzl} algorithm
+ * and its derivatives.
+ * </p>
+ * @param <S> Space type.
+ * @param <P> Point type.
+ * @see EnclosingBall
+ * @since 3.3
+ */
+public interface SupportBallGenerator<S extends Space, P extends Point<S>> {
+
+ /** Create a ball whose boundary lies on prescribed support points.
+ * @param support support points (may be empty)
+ * @return ball whose boundary lies on the prescribed support points
+ */
+ EnclosingBall<S, P> ballOnSupport(List<P> support);
+
+}
diff --git a/src/main/java/org/apache/commons/math3/geometry/enclosing/WelzlEncloser.java b/src/main/java/org/apache/commons/math3/geometry/enclosing/WelzlEncloser.java
new file mode 100644
index 0000000..987e7d9
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/geometry/enclosing/WelzlEncloser.java
@@ -0,0 +1,181 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math3.geometry.enclosing;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.apache.commons.math3.exception.MathInternalError;
+import org.apache.commons.math3.geometry.Point;
+import org.apache.commons.math3.geometry.Space;
+
+/** Class implementing Emo Welzl algorithm to find the smallest enclosing ball in linear time.
+ * <p>
+ * The class implements the algorithm described in paper <a
+ * href="http://www.inf.ethz.ch/personal/emo/PublFiles/SmallEnclDisk_LNCS555_91.pdf">Smallest
+ * Enclosing Disks (Balls and Ellipsoids)</a> by Emo Welzl, Lecture Notes in Computer Science
+ * 555 (1991) 359-370. The pivoting improvement published in the paper <a
+ * href="http://www.inf.ethz.ch/personal/gaertner/texts/own_work/esa99_final.pdf">Fast and
+ * Robust Smallest Enclosing Balls</a>, by Bernd Gärtner and further modified in
+ * paper <a
+ * href=http://www.idt.mdh.se/kurser/ct3340/ht12/MINICONFERENCE/FinalPapers/ircse12_submission_30.pdf">
+ * Efficient Computation of Smallest Enclosing Balls in Three Dimensions</a> by Linus Källberg
+ * to avoid performing local copies of data have been included.
+ * </p>
+ * @param <S> Space type.
+ * @param <P> Point type.
+ * @since 3.3
+ */
+public class WelzlEncloser<S extends Space, P extends Point<S>> implements Encloser<S, P> {
+
+ /** Tolerance below which points are consider to be identical. */
+ private final double tolerance;
+
+ /** Generator for balls on support. */
+ private final SupportBallGenerator<S, P> generator;
+
+ /** Simple constructor.
+ * @param tolerance below which points are consider to be identical
+ * @param generator generator for balls on support
+ */
+ public WelzlEncloser(final double tolerance, final SupportBallGenerator<S, P> generator) {
+ this.tolerance = tolerance;
+ this.generator = generator;
+ }
+
+ /** {@inheritDoc} */
+ public EnclosingBall<S, P> enclose(final Iterable<P> points) {
+
+ if (points == null || !points.iterator().hasNext()) {
+ // return an empty ball
+ return generator.ballOnSupport(new ArrayList<P>());
+ }
+
+ // Emo Welzl algorithm with Bernd Gärtner and Linus Källberg improvements
+ return pivotingBall(points);
+
+ }
+
+ /** Compute enclosing ball using Gärtner's pivoting heuristic.
+ * @param points points to be enclosed
+ * @return enclosing ball
+ */
+ private EnclosingBall<S, P> pivotingBall(final Iterable<P> points) {
+
+ final P first = points.iterator().next();
+ final List<P> extreme = new ArrayList<P>(first.getSpace().getDimension() + 1);
+ final List<P> support = new ArrayList<P>(first.getSpace().getDimension() + 1);
+
+ // start with only first point selected as a candidate support
+ extreme.add(first);
+ EnclosingBall<S, P> ball = moveToFrontBall(extreme, extreme.size(), support);
+
+ while (true) {
+
+ // select the point farthest to current ball
+ final P farthest = selectFarthest(points, ball);
+
+ if (ball.contains(farthest, tolerance)) {
+ // we have found a ball containing all points
+ return ball;
+ }
+
+ // recurse search, restricted to the small subset containing support and farthest point
+ support.clear();
+ support.add(farthest);
+ EnclosingBall<S, P> savedBall = ball;
+ ball = moveToFrontBall(extreme, extreme.size(), support);
+ if (ball.getRadius() < savedBall.getRadius()) {
+ // this should never happen
+ throw new MathInternalError();
+ }
+
+ // it was an interesting point, move it to the front
+ // according to Gärtner's heuristic
+ extreme.add(0, farthest);
+
+ // prune the least interesting points
+ extreme.subList(ball.getSupportSize(), extreme.size()).clear();
+
+
+ }
+ }
+
+ /** Compute enclosing ball using Welzl's move to front heuristic.
+ * @param extreme subset of extreme points
+ * @param nbExtreme number of extreme points to consider
+ * @param support points that must belong to the ball support
+ * @return enclosing ball, for the extreme subset only
+ */
+ private EnclosingBall<S, P> moveToFrontBall(final List<P> extreme, final int nbExtreme,
+ final List<P> support) {
+
+ // create a new ball on the prescribed support
+ EnclosingBall<S, P> ball = generator.ballOnSupport(support);
+
+ if (ball.getSupportSize() <= ball.getCenter().getSpace().getDimension()) {
+
+ for (int i = 0; i < nbExtreme; ++i) {
+ final P pi = extreme.get(i);
+ if (!ball.contains(pi, tolerance)) {
+
+ // we have found an outside point,
+ // enlarge the ball by adding it to the support
+ support.add(pi);
+ ball = moveToFrontBall(extreme, i, support);
+ support.remove(support.size() - 1);
+
+ // it was an interesting point, move it to the front
+ // according to Welzl's heuristic
+ for (int j = i; j > 0; --j) {
+ extreme.set(j, extreme.get(j - 1));
+ }
+ extreme.set(0, pi);
+
+ }
+ }
+
+ }
+
+ return ball;
+
+ }
+
+ /** Select the point farthest to the current ball.
+ * @param points points to be enclosed
+ * @param ball current ball
+ * @return farthest point
+ */
+ public P selectFarthest(final Iterable<P> points, final EnclosingBall<S, P> ball) {
+
+ final P center = ball.getCenter();
+ P farthest = null;
+ double dMax = -1.0;
+
+ for (final P point : points) {
+ final double d = point.distance(center);
+ if (d > dMax) {
+ farthest = point;
+ dMax = d;
+ }
+ }
+
+ return farthest;
+
+ }
+
+}
diff --git a/src/main/java/org/apache/commons/math3/geometry/enclosing/package-info.java b/src/main/java/org/apache/commons/math3/geometry/enclosing/package-info.java
new file mode 100644
index 0000000..20462a1
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/geometry/enclosing/package-info.java
@@ -0,0 +1,24 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/**
+ *
+ * <p>
+ * This package provides interfaces and classes related to the smallest enclosing ball problem.
+ * </p>
+ *
+ */
+package org.apache.commons.math3.geometry.enclosing;
diff --git a/src/main/java/org/apache/commons/math3/geometry/euclidean/oned/Euclidean1D.java b/src/main/java/org/apache/commons/math3/geometry/euclidean/oned/Euclidean1D.java
new file mode 100644
index 0000000..14d130d
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/geometry/euclidean/oned/Euclidean1D.java
@@ -0,0 +1,100 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math3.geometry.euclidean.oned;
+
+import java.io.Serializable;
+
+import org.apache.commons.math3.exception.MathUnsupportedOperationException;
+import org.apache.commons.math3.exception.util.LocalizedFormats;
+import org.apache.commons.math3.geometry.Space;
+
+/**
+ * This class implements a one-dimensional space.
+ * @since 3.0
+ */
+public class Euclidean1D implements Serializable, Space {
+
+ /** Serializable version identifier. */
+ private static final long serialVersionUID = -1178039568877797126L;
+
+ /** Private constructor for the singleton.
+ */
+ private Euclidean1D() {
+ }
+
+ /** Get the unique instance.
+ * @return the unique instance
+ */
+ public static Euclidean1D getInstance() {
+ return LazyHolder.INSTANCE;
+ }
+
+ /** {@inheritDoc} */
+ public int getDimension() {
+ return 1;
+ }
+
+ /** {@inheritDoc}
+ * <p>
+ * As the 1-dimension Euclidean space does not have proper sub-spaces,
+ * this method always throws a {@link NoSubSpaceException}
+ * </p>
+ * @return nothing
+ * @throws NoSubSpaceException in all cases
+ */
+ public Space getSubSpace() throws NoSubSpaceException {
+ throw new NoSubSpaceException();
+ }
+
+ // CHECKSTYLE: stop HideUtilityClassConstructor
+ /** Holder for the instance.
+ * <p>We use here the Initialization On Demand Holder Idiom.</p>
+ */
+ private static class LazyHolder {
+ /** Cached field instance. */
+ private static final Euclidean1D INSTANCE = new Euclidean1D();
+ }
+ // CHECKSTYLE: resume HideUtilityClassConstructor
+
+ /** Handle deserialization of the singleton.
+ * @return the singleton instance
+ */
+ private Object readResolve() {
+ // return the singleton instance
+ return LazyHolder.INSTANCE;
+ }
+
+ /** Specialized exception for inexistent sub-space.
+ * <p>
+ * This exception is thrown when attempting to get the sub-space of a one-dimensional space
+ * </p>
+ */
+ public static class NoSubSpaceException extends MathUnsupportedOperationException {
+
+ /** Serializable UID. */
+ private static final long serialVersionUID = 20140225L;
+
+ /** Simple constructor.
+ */
+ public NoSubSpaceException() {
+ super(LocalizedFormats.NOT_SUPPORTED_IN_DIMENSION_N, 1);
+ }
+
+ }
+
+}
diff --git a/src/main/java/org/apache/commons/math3/geometry/euclidean/oned/Interval.java b/src/main/java/org/apache/commons/math3/geometry/euclidean/oned/Interval.java
new file mode 100644
index 0000000..ca15231
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/geometry/euclidean/oned/Interval.java
@@ -0,0 +1,135 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math3.geometry.euclidean.oned;
+
+import org.apache.commons.math3.geometry.partitioning.Region.Location;
+import org.apache.commons.math3.exception.NumberIsTooSmallException;
+import org.apache.commons.math3.exception.util.LocalizedFormats;
+
+
+/** This class represents a 1D interval.
+ * @see IntervalsSet
+ * @since 3.0
+ */
+public class Interval {
+
+ /** The lower bound of the interval. */
+ private final double lower;
+
+ /** The upper bound of the interval. */
+ private final double upper;
+
+ /** Simple constructor.
+ * @param lower lower bound of the interval
+ * @param upper upper bound of the interval
+ */
+ public Interval(final double lower, final double upper) {
+ if (upper < lower) {
+ throw new NumberIsTooSmallException(LocalizedFormats.ENDPOINTS_NOT_AN_INTERVAL,
+ upper, lower, true);
+ }
+ this.lower = lower;
+ this.upper = upper;
+ }
+
+ /** Get the lower bound of the interval.
+ * @return lower bound of the interval
+ * @since 3.1
+ */
+ public double getInf() {
+ return lower;
+ }
+
+ /** Get the lower bound of the interval.
+ * @return lower bound of the interval
+ * @deprecated as of 3.1, replaced by {@link #getInf()}
+ */
+ @Deprecated
+ public double getLower() {
+ return getInf();
+ }
+
+ /** Get the upper bound of the interval.
+ * @return upper bound of the interval
+ * @since 3.1
+ */
+ public double getSup() {
+ return upper;
+ }
+
+ /** Get the upper bound of the interval.
+ * @return upper bound of the interval
+ * @deprecated as of 3.1, replaced by {@link #getSup()}
+ */
+ @Deprecated
+ public double getUpper() {
+ return getSup();
+ }
+
+ /** Get the size of the interval.
+ * @return size of the interval
+ * @since 3.1
+ */
+ public double getSize() {
+ return upper - lower;
+ }
+
+ /** Get the length of the interval.
+ * @return length of the interval
+ * @deprecated as of 3.1, replaced by {@link #getSize()}
+ */
+ @Deprecated
+ public double getLength() {
+ return getSize();
+ }
+
+ /** Get the barycenter of the interval.
+ * @return barycenter of the interval
+ * @since 3.1
+ */
+ public double getBarycenter() {
+ return 0.5 * (lower + upper);
+ }
+
+ /** Get the midpoint of the interval.
+ * @return midpoint of the interval
+ * @deprecated as of 3.1, replaced by {@link #getBarycenter()}
+ */
+ @Deprecated
+ public double getMidPoint() {
+ return getBarycenter();
+ }
+
+ /** Check a point with respect to the interval.
+ * @param point point to check
+ * @param tolerance tolerance below which points are considered to
+ * belong to the boundary
+ * @return a code representing the point status: either {@link
+ * Location#INSIDE}, {@link Location#OUTSIDE} or {@link Location#BOUNDARY}
+ * @since 3.1
+ */
+ public Location checkPoint(final double point, final double tolerance) {
+ if (point < lower - tolerance || point > upper + tolerance) {
+ return Location.OUTSIDE;
+ } else if (point > lower + tolerance && point < upper - tolerance) {
+ return Location.INSIDE;
+ } else {
+ return Location.BOUNDARY;
+ }
+ }
+
+}
diff --git a/src/main/java/org/apache/commons/math3/geometry/euclidean/oned/IntervalsSet.java b/src/main/java/org/apache/commons/math3/geometry/euclidean/oned/IntervalsSet.java
new file mode 100644
index 0000000..5ce7edb
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/geometry/euclidean/oned/IntervalsSet.java
@@ -0,0 +1,686 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math3.geometry.euclidean.oned;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.List;
+import java.util.NoSuchElementException;
+
+import org.apache.commons.math3.geometry.Point;
+import org.apache.commons.math3.geometry.partitioning.AbstractRegion;
+import org.apache.commons.math3.geometry.partitioning.BSPTree;
+import org.apache.commons.math3.geometry.partitioning.BoundaryProjection;
+import org.apache.commons.math3.geometry.partitioning.SubHyperplane;
+import org.apache.commons.math3.util.Precision;
+
+/** This class represents a 1D region: a set of intervals.
+ * @since 3.0
+ */
+public class IntervalsSet extends AbstractRegion<Euclidean1D, Euclidean1D> implements Iterable<double[]> {
+
+ /** Default value for tolerance. */
+ private static final double DEFAULT_TOLERANCE = 1.0e-10;
+
+ /** Build an intervals set representing the whole real line.
+ * @param tolerance tolerance below which points are considered identical.
+ * @since 3.3
+ */
+ public IntervalsSet(final double tolerance) {
+ super(tolerance);
+ }
+
+ /** Build an intervals set corresponding to a single interval.
+ * @param lower lower bound of the interval, must be lesser or equal
+ * to {@code upper} (may be {@code Double.NEGATIVE_INFINITY})
+ * @param upper upper bound of the interval, must be greater or equal
+ * to {@code lower} (may be {@code Double.POSITIVE_INFINITY})
+ * @param tolerance tolerance below which points are considered identical.
+ * @since 3.3
+ */
+ public IntervalsSet(final double lower, final double upper, final double tolerance) {
+ super(buildTree(lower, upper, tolerance), tolerance);
+ }
+
+ /** Build an intervals set from an inside/outside BSP tree.
+ * <p>The leaf nodes of the BSP tree <em>must</em> have a
+ * {@code Boolean} attribute representing the inside status of
+ * the corresponding cell (true for inside cells, false for outside
+ * cells). In order to avoid building too many small objects, it is
+ * recommended to use the predefined constants
+ * {@code Boolean.TRUE} and {@code Boolean.FALSE}</p>
+ * @param tree inside/outside BSP tree representing the intervals set
+ * @param tolerance tolerance below which points are considered identical.
+ * @since 3.3
+ */
+ public IntervalsSet(final BSPTree<Euclidean1D> tree, final double tolerance) {
+ super(tree, tolerance);
+ }
+
+ /** Build an intervals set from a Boundary REPresentation (B-rep).
+ * <p>The boundary is provided as a collection of {@link
+ * SubHyperplane sub-hyperplanes}. Each sub-hyperplane has the
+ * interior part of the region on its minus side and the exterior on
+ * its plus side.</p>
+ * <p>The boundary elements can be in any order, and can form
+ * several non-connected sets (like for example polygons with holes
+ * or a set of disjoints polyhedrons considered as a whole). In
+ * fact, the elements do not even need to be connected together
+ * (their topological connections are not used here). However, if the
+ * boundary does not really separate an inside open from an outside
+ * open (open having here its topological meaning), then subsequent
+ * calls to the {@link
+ * org.apache.commons.math3.geometry.partitioning.Region#checkPoint(org.apache.commons.math3.geometry.Point)
+ * checkPoint} method will not be meaningful anymore.</p>
+ * <p>If the boundary is empty, the region will represent the whole
+ * space.</p>
+ * @param boundary collection of boundary elements
+ * @param tolerance tolerance below which points are considered identical.
+ * @since 3.3
+ */
+ public IntervalsSet(final Collection<SubHyperplane<Euclidean1D>> boundary,
+ final double tolerance) {
+ super(boundary, tolerance);
+ }
+
+ /** Build an intervals set representing the whole real line.
+ * @deprecated as of 3.1 replaced with {@link #IntervalsSet(double)}
+ */
+ @Deprecated
+ public IntervalsSet() {
+ this(DEFAULT_TOLERANCE);
+ }
+
+ /** Build an intervals set corresponding to a single interval.
+ * @param lower lower bound of the interval, must be lesser or equal
+ * to {@code upper} (may be {@code Double.NEGATIVE_INFINITY})
+ * @param upper upper bound of the interval, must be greater or equal
+ * to {@code lower} (may be {@code Double.POSITIVE_INFINITY})
+ * @deprecated as of 3.3 replaced with {@link #IntervalsSet(double, double, double)}
+ */
+ @Deprecated
+ public IntervalsSet(final double lower, final double upper) {
+ this(lower, upper, DEFAULT_TOLERANCE);
+ }
+
+ /** Build an intervals set from an inside/outside BSP tree.
+ * <p>The leaf nodes of the BSP tree <em>must</em> have a
+ * {@code Boolean} attribute representing the inside status of
+ * the corresponding cell (true for inside cells, false for outside
+ * cells). In order to avoid building too many small objects, it is
+ * recommended to use the predefined constants
+ * {@code Boolean.TRUE} and {@code Boolean.FALSE}</p>
+ * @param tree inside/outside BSP tree representing the intervals set
+ * @deprecated as of 3.3, replaced with {@link #IntervalsSet(BSPTree, double)}
+ */
+ @Deprecated
+ public IntervalsSet(final BSPTree<Euclidean1D> tree) {
+ this(tree, DEFAULT_TOLERANCE);
+ }
+
+ /** Build an intervals set from a Boundary REPresentation (B-rep).
+ * <p>The boundary is provided as a collection of {@link
+ * SubHyperplane sub-hyperplanes}. Each sub-hyperplane has the
+ * interior part of the region on its minus side and the exterior on
+ * its plus side.</p>
+ * <p>The boundary elements can be in any order, and can form
+ * several non-connected sets (like for example polygons with holes
+ * or a set of disjoints polyhedrons considered as a whole). In
+ * fact, the elements do not even need to be connected together
+ * (their topological connections are not used here). However, if the
+ * boundary does not really separate an inside open from an outside
+ * open (open having here its topological meaning), then subsequent
+ * calls to the {@link
+ * org.apache.commons.math3.geometry.partitioning.Region#checkPoint(org.apache.commons.math3.geometry.Point)
+ * checkPoint} method will not be meaningful anymore.</p>
+ * <p>If the boundary is empty, the region will represent the whole
+ * space.</p>
+ * @param boundary collection of boundary elements
+ * @deprecated as of 3.3, replaced with {@link #IntervalsSet(Collection, double)}
+ */
+ @Deprecated
+ public IntervalsSet(final Collection<SubHyperplane<Euclidean1D>> boundary) {
+ this(boundary, DEFAULT_TOLERANCE);
+ }
+
+ /** Build an inside/outside tree representing a single interval.
+ * @param lower lower bound of the interval, must be lesser or equal
+ * to {@code upper} (may be {@code Double.NEGATIVE_INFINITY})
+ * @param upper upper bound of the interval, must be greater or equal
+ * to {@code lower} (may be {@code Double.POSITIVE_INFINITY})
+ * @param tolerance tolerance below which points are considered identical.
+ * @return the built tree
+ */
+ private static BSPTree<Euclidean1D> buildTree(final double lower, final double upper,
+ final double tolerance) {
+ if (Double.isInfinite(lower) && (lower < 0)) {
+ if (Double.isInfinite(upper) && (upper > 0)) {
+ // the tree must cover the whole real line
+ return new BSPTree<Euclidean1D>(Boolean.TRUE);
+ }
+ // the tree must be open on the negative infinity side
+ final SubHyperplane<Euclidean1D> upperCut =
+ new OrientedPoint(new Vector1D(upper), true, tolerance).wholeHyperplane();
+ return new BSPTree<Euclidean1D>(upperCut,
+ new BSPTree<Euclidean1D>(Boolean.FALSE),
+ new BSPTree<Euclidean1D>(Boolean.TRUE),
+ null);
+ }
+ final SubHyperplane<Euclidean1D> lowerCut =
+ new OrientedPoint(new Vector1D(lower), false, tolerance).wholeHyperplane();
+ if (Double.isInfinite(upper) && (upper > 0)) {
+ // the tree must be open on the positive infinity side
+ return new BSPTree<Euclidean1D>(lowerCut,
+ new BSPTree<Euclidean1D>(Boolean.FALSE),
+ new BSPTree<Euclidean1D>(Boolean.TRUE),
+ null);
+ }
+
+ // the tree must be bounded on the two sides
+ final SubHyperplane<Euclidean1D> upperCut =
+ new OrientedPoint(new Vector1D(upper), true, tolerance).wholeHyperplane();
+ return new BSPTree<Euclidean1D>(lowerCut,
+ new BSPTree<Euclidean1D>(Boolean.FALSE),
+ new BSPTree<Euclidean1D>(upperCut,
+ new BSPTree<Euclidean1D>(Boolean.FALSE),
+ new BSPTree<Euclidean1D>(Boolean.TRUE),
+ null),
+ null);
+
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public IntervalsSet buildNew(final BSPTree<Euclidean1D> tree) {
+ return new IntervalsSet(tree, getTolerance());
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ protected void computeGeometricalProperties() {
+ if (getTree(false).getCut() == null) {
+ setBarycenter((Point<Euclidean1D>) Vector1D.NaN);
+ setSize(((Boolean) getTree(false).getAttribute()) ? Double.POSITIVE_INFINITY : 0);
+ } else {
+ double size = 0.0;
+ double sum = 0.0;
+ for (final Interval interval : asList()) {
+ size += interval.getSize();
+ sum += interval.getSize() * interval.getBarycenter();
+ }
+ setSize(size);
+ if (Double.isInfinite(size)) {
+ setBarycenter((Point<Euclidean1D>) Vector1D.NaN);
+ } else if (size >= Precision.SAFE_MIN) {
+ setBarycenter((Point<Euclidean1D>) new Vector1D(sum / size));
+ } else {
+ setBarycenter((Point<Euclidean1D>) ((OrientedPoint) getTree(false).getCut().getHyperplane()).getLocation());
+ }
+ }
+ }
+
+ /** Get the lowest value belonging to the instance.
+ * @return lowest value belonging to the instance
+ * ({@code Double.NEGATIVE_INFINITY} if the instance doesn't
+ * have any low bound, {@code Double.POSITIVE_INFINITY} if the
+ * instance is empty)
+ */
+ public double getInf() {
+ BSPTree<Euclidean1D> node = getTree(false);
+ double inf = Double.POSITIVE_INFINITY;
+ while (node.getCut() != null) {
+ final OrientedPoint op = (OrientedPoint) node.getCut().getHyperplane();
+ inf = op.getLocation().getX();
+ node = op.isDirect() ? node.getMinus() : node.getPlus();
+ }
+ return ((Boolean) node.getAttribute()) ? Double.NEGATIVE_INFINITY : inf;
+ }
+
+ /** Get the highest value belonging to the instance.
+ * @return highest value belonging to the instance
+ * ({@code Double.POSITIVE_INFINITY} if the instance doesn't
+ * have any high bound, {@code Double.NEGATIVE_INFINITY} if the
+ * instance is empty)
+ */
+ public double getSup() {
+ BSPTree<Euclidean1D> node = getTree(false);
+ double sup = Double.NEGATIVE_INFINITY;
+ while (node.getCut() != null) {
+ final OrientedPoint op = (OrientedPoint) node.getCut().getHyperplane();
+ sup = op.getLocation().getX();
+ node = op.isDirect() ? node.getPlus() : node.getMinus();
+ }
+ return ((Boolean) node.getAttribute()) ? Double.POSITIVE_INFINITY : sup;
+ }
+
+ /** {@inheritDoc}
+ * @since 3.3
+ */
+ @Override
+ public BoundaryProjection<Euclidean1D> projectToBoundary(final Point<Euclidean1D> point) {
+
+ // get position of test point
+ final double x = ((Vector1D) point).getX();
+
+ double previous = Double.NEGATIVE_INFINITY;
+ for (final double[] a : this) {
+ if (x < a[0]) {
+ // the test point lies between the previous and the current intervals
+ // offset will be positive
+ final double previousOffset = x - previous;
+ final double currentOffset = a[0] - x;
+ if (previousOffset < currentOffset) {
+ return new BoundaryProjection<Euclidean1D>(point, finiteOrNullPoint(previous), previousOffset);
+ } else {
+ return new BoundaryProjection<Euclidean1D>(point, finiteOrNullPoint(a[0]), currentOffset);
+ }
+ } else if (x <= a[1]) {
+ // the test point lies within the current interval
+ // offset will be negative
+ final double offset0 = a[0] - x;
+ final double offset1 = x - a[1];
+ if (offset0 < offset1) {
+ return new BoundaryProjection<Euclidean1D>(point, finiteOrNullPoint(a[1]), offset1);
+ } else {
+ return new BoundaryProjection<Euclidean1D>(point, finiteOrNullPoint(a[0]), offset0);
+ }
+ }
+ previous = a[1];
+ }
+
+ // the test point if past the last sub-interval
+ return new BoundaryProjection<Euclidean1D>(point, finiteOrNullPoint(previous), x - previous);
+
+ }
+
+ /** Build a finite point.
+ * @param x abscissa of the point
+ * @return a new point for finite abscissa, null otherwise
+ */
+ private Vector1D finiteOrNullPoint(final double x) {
+ return Double.isInfinite(x) ? null : new Vector1D(x);
+ }
+
+ /** Build an ordered list of intervals representing the instance.
+ * <p>This method builds this intervals set as an ordered list of
+ * {@link Interval Interval} elements. If the intervals set has no
+ * lower limit, the first interval will have its low bound equal to
+ * {@code Double.NEGATIVE_INFINITY}. If the intervals set has
+ * no upper limit, the last interval will have its upper bound equal
+ * to {@code Double.POSITIVE_INFINITY}. An empty tree will
+ * build an empty list while a tree representing the whole real line
+ * will build a one element list with both bounds being
+ * infinite.</p>
+ * @return a new ordered list containing {@link Interval Interval}
+ * elements
+ */
+ public List<Interval> asList() {
+ final List<Interval> list = new ArrayList<Interval>();
+ for (final double[] a : this) {
+ list.add(new Interval(a[0], a[1]));
+ }
+ return list;
+ }
+
+ /** Get the first leaf node of a tree.
+ * @param root tree root
+ * @return first leaf node
+ */
+ private BSPTree<Euclidean1D> getFirstLeaf(final BSPTree<Euclidean1D> root) {
+
+ if (root.getCut() == null) {
+ return root;
+ }
+
+ // find the smallest internal node
+ BSPTree<Euclidean1D> smallest = null;
+ for (BSPTree<Euclidean1D> n = root; n != null; n = previousInternalNode(n)) {
+ smallest = n;
+ }
+
+ return leafBefore(smallest);
+
+ }
+
+ /** Get the node corresponding to the first interval boundary.
+ * @return smallest internal node,
+ * or null if there are no internal nodes (i.e. the set is either empty or covers the real line)
+ */
+ private BSPTree<Euclidean1D> getFirstIntervalBoundary() {
+
+ // start search at the tree root
+ BSPTree<Euclidean1D> node = getTree(false);
+ if (node.getCut() == null) {
+ return null;
+ }
+
+ // walk tree until we find the smallest internal node
+ node = getFirstLeaf(node).getParent();
+
+ // walk tree until we find an interval boundary
+ while (node != null && !(isIntervalStart(node) || isIntervalEnd(node))) {
+ node = nextInternalNode(node);
+ }
+
+ return node;
+
+ }
+
+ /** Check if an internal node corresponds to the start abscissa of an interval.
+ * @param node internal node to check
+ * @return true if the node corresponds to the start abscissa of an interval
+ */
+ private boolean isIntervalStart(final BSPTree<Euclidean1D> node) {
+
+ if ((Boolean) leafBefore(node).getAttribute()) {
+ // it has an inside cell before it, it may end an interval but not start it
+ return false;
+ }
+
+ if (!(Boolean) leafAfter(node).getAttribute()) {
+ // it has an outside cell after it, it is a dummy cut away from real intervals
+ return false;
+ }
+
+ // the cell has an outside before and an inside after it
+ // it is the start of an interval
+ return true;
+
+ }
+
+ /** Check if an internal node corresponds to the end abscissa of an interval.
+ * @param node internal node to check
+ * @return true if the node corresponds to the end abscissa of an interval
+ */
+ private boolean isIntervalEnd(final BSPTree<Euclidean1D> node) {
+
+ if (!(Boolean) leafBefore(node).getAttribute()) {
+ // it has an outside cell before it, it may start an interval but not end it
+ return false;
+ }
+
+ if ((Boolean) leafAfter(node).getAttribute()) {
+ // it has an inside cell after it, it is a dummy cut in the middle of an interval
+ return false;
+ }
+
+ // the cell has an inside before and an outside after it
+ // it is the end of an interval
+ return true;
+
+ }
+
+ /** Get the next internal node.
+ * @param node current internal node
+ * @return next internal node in ascending order, or null
+ * if this is the last internal node
+ */
+ private BSPTree<Euclidean1D> nextInternalNode(BSPTree<Euclidean1D> node) {
+
+ if (childAfter(node).getCut() != null) {
+ // the next node is in the sub-tree
+ return leafAfter(node).getParent();
+ }
+
+ // there is nothing left deeper in the tree, we backtrack
+ while (isAfterParent(node)) {
+ node = node.getParent();
+ }
+ return node.getParent();
+
+ }
+
+ /** Get the previous internal node.
+ * @param node current internal node
+ * @return previous internal node in ascending order, or null
+ * if this is the first internal node
+ */
+ private BSPTree<Euclidean1D> previousInternalNode(BSPTree<Euclidean1D> node) {
+
+ if (childBefore(node).getCut() != null) {
+ // the next node is in the sub-tree
+ return leafBefore(node).getParent();
+ }
+
+ // there is nothing left deeper in the tree, we backtrack
+ while (isBeforeParent(node)) {
+ node = node.getParent();
+ }
+ return node.getParent();
+
+ }
+
+ /** Find the leaf node just before an internal node.
+ * @param node internal node at which the sub-tree starts
+ * @return leaf node just before the internal node
+ */
+ private BSPTree<Euclidean1D> leafBefore(BSPTree<Euclidean1D> node) {
+
+ node = childBefore(node);
+ while (node.getCut() != null) {
+ node = childAfter(node);
+ }
+
+ return node;
+
+ }
+
+ /** Find the leaf node just after an internal node.
+ * @param node internal node at which the sub-tree starts
+ * @return leaf node just after the internal node
+ */
+ private BSPTree<Euclidean1D> leafAfter(BSPTree<Euclidean1D> node) {
+
+ node = childAfter(node);
+ while (node.getCut() != null) {
+ node = childBefore(node);
+ }
+
+ return node;
+
+ }
+
+ /** Check if a node is the child before its parent in ascending order.
+ * @param node child node considered
+ * @return true is the node has a parent end is before it in ascending order
+ */
+ private boolean isBeforeParent(final BSPTree<Euclidean1D> node) {
+ final BSPTree<Euclidean1D> parent = node.getParent();
+ if (parent == null) {
+ return false;
+ } else {
+ return node == childBefore(parent);
+ }
+ }
+
+ /** Check if a node is the child after its parent in ascending order.
+ * @param node child node considered
+ * @return true is the node has a parent end is after it in ascending order
+ */
+ private boolean isAfterParent(final BSPTree<Euclidean1D> node) {
+ final BSPTree<Euclidean1D> parent = node.getParent();
+ if (parent == null) {
+ return false;
+ } else {
+ return node == childAfter(parent);
+ }
+ }
+
+ /** Find the child node just before an internal node.
+ * @param node internal node at which the sub-tree starts
+ * @return child node just before the internal node
+ */
+ private BSPTree<Euclidean1D> childBefore(BSPTree<Euclidean1D> node) {
+ if (isDirect(node)) {
+ // smaller abscissas are on minus side, larger abscissas are on plus side
+ return node.getMinus();
+ } else {
+ // smaller abscissas are on plus side, larger abscissas are on minus side
+ return node.getPlus();
+ }
+ }
+
+ /** Find the child node just after an internal node.
+ * @param node internal node at which the sub-tree starts
+ * @return child node just after the internal node
+ */
+ private BSPTree<Euclidean1D> childAfter(BSPTree<Euclidean1D> node) {
+ if (isDirect(node)) {
+ // smaller abscissas are on minus side, larger abscissas are on plus side
+ return node.getPlus();
+ } else {
+ // smaller abscissas are on plus side, larger abscissas are on minus side
+ return node.getMinus();
+ }
+ }
+
+ /** Check if an internal node has a direct oriented point.
+ * @param node internal node to check
+ * @return true if the oriented point is direct
+ */
+ private boolean isDirect(final BSPTree<Euclidean1D> node) {
+ return ((OrientedPoint) node.getCut().getHyperplane()).isDirect();
+ }
+
+ /** Get the abscissa of an internal node.
+ * @param node internal node to check
+ * @return abscissa
+ */
+ private double getAngle(final BSPTree<Euclidean1D> node) {
+ return ((OrientedPoint) node.getCut().getHyperplane()).getLocation().getX();
+ }
+
+ /** {@inheritDoc}
+ * <p>
+ * The iterator returns the limit values of sub-intervals in ascending order.
+ * </p>
+ * <p>
+ * The iterator does <em>not</em> support the optional {@code remove} operation.
+ * </p>
+ * @since 3.3
+ */
+ public Iterator<double[]> iterator() {
+ return new SubIntervalsIterator();
+ }
+
+ /** Local iterator for sub-intervals. */
+ private class SubIntervalsIterator implements Iterator<double[]> {
+
+ /** Current node. */
+ private BSPTree<Euclidean1D> current;
+
+ /** Sub-interval no yet returned. */
+ private double[] pending;
+
+ /** Simple constructor.
+ */
+ SubIntervalsIterator() {
+
+ current = getFirstIntervalBoundary();
+
+ if (current == null) {
+ // all the leaf tree nodes share the same inside/outside status
+ if ((Boolean) getFirstLeaf(getTree(false)).getAttribute()) {
+ // it is an inside node, it represents the full real line
+ pending = new double[] {
+ Double.NEGATIVE_INFINITY, Double.POSITIVE_INFINITY
+ };
+ } else {
+ pending = null;
+ }
+ } else if (isIntervalEnd(current)) {
+ // the first boundary is an interval end,
+ // so the first interval starts at infinity
+ pending = new double[] {
+ Double.NEGATIVE_INFINITY, getAngle(current)
+ };
+ } else {
+ selectPending();
+ }
+ }
+
+ /** Walk the tree to select the pending sub-interval.
+ */
+ private void selectPending() {
+
+ // look for the start of the interval
+ BSPTree<Euclidean1D> start = current;
+ while (start != null && !isIntervalStart(start)) {
+ start = nextInternalNode(start);
+ }
+
+ if (start == null) {
+ // we have exhausted the iterator
+ current = null;
+ pending = null;
+ return;
+ }
+
+ // look for the end of the interval
+ BSPTree<Euclidean1D> end = start;
+ while (end != null && !isIntervalEnd(end)) {
+ end = nextInternalNode(end);
+ }
+
+ if (end != null) {
+
+ // we have identified the interval
+ pending = new double[] {
+ getAngle(start), getAngle(end)
+ };
+
+ // prepare search for next interval
+ current = end;
+
+ } else {
+
+ // the final interval is open toward infinity
+ pending = new double[] {
+ getAngle(start), Double.POSITIVE_INFINITY
+ };
+
+ // there won't be any other intervals
+ current = null;
+
+ }
+
+ }
+
+ /** {@inheritDoc} */
+ public boolean hasNext() {
+ return pending != null;
+ }
+
+ /** {@inheritDoc} */
+ public double[] next() {
+ if (pending == null) {
+ throw new NoSuchElementException();
+ }
+ final double[] next = pending;
+ selectPending();
+ return next;
+ }
+
+ /** {@inheritDoc} */
+ public void remove() {
+ throw new UnsupportedOperationException();
+ }
+
+ }
+
+}
diff --git a/src/main/java/org/apache/commons/math3/geometry/euclidean/oned/OrientedPoint.java b/src/main/java/org/apache/commons/math3/geometry/euclidean/oned/OrientedPoint.java
new file mode 100644
index 0000000..512bf5d
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/geometry/euclidean/oned/OrientedPoint.java
@@ -0,0 +1,153 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math3.geometry.euclidean.oned;
+
+import org.apache.commons.math3.geometry.Point;
+import org.apache.commons.math3.geometry.Vector;
+import org.apache.commons.math3.geometry.partitioning.Hyperplane;
+
+/** This class represents a 1D oriented hyperplane.
+ * <p>An hyperplane in 1D is a simple point, its orientation being a
+ * boolean.</p>
+ * <p>Instances of this class are guaranteed to be immutable.</p>
+ * @since 3.0
+ */
+public class OrientedPoint implements Hyperplane<Euclidean1D> {
+
+ /** Default value for tolerance. */
+ private static final double DEFAULT_TOLERANCE = 1.0e-10;
+
+ /** Vector location. */
+ private Vector1D location;
+
+ /** Orientation. */
+ private boolean direct;
+
+ /** Tolerance below which points are considered to belong to the hyperplane. */
+ private final double tolerance;
+
+ /** Simple constructor.
+ * @param location location of the hyperplane
+ * @param direct if true, the plus side of the hyperplane is towards
+ * abscissas greater than {@code location}
+ * @param tolerance tolerance below which points are considered to belong to the hyperplane
+ * @since 3.3
+ */
+ public OrientedPoint(final Vector1D location, final boolean direct, final double tolerance) {
+ this.location = location;
+ this.direct = direct;
+ this.tolerance = tolerance;
+ }
+
+ /** Simple constructor.
+ * @param location location of the hyperplane
+ * @param direct if true, the plus side of the hyperplane is towards
+ * abscissas greater than {@code location}
+ * @deprecated as of 3.3, replaced with {@link #OrientedPoint(Vector1D, boolean, double)}
+ */
+ @Deprecated
+ public OrientedPoint(final Vector1D location, final boolean direct) {
+ this(location, direct, DEFAULT_TOLERANCE);
+ }
+
+ /** Copy the instance.
+ * <p>Since instances are immutable, this method directly returns
+ * the instance.</p>
+ * @return the instance itself
+ */
+ public OrientedPoint copySelf() {
+ return this;
+ }
+
+ /** Get the offset (oriented distance) of a vector.
+ * @param vector vector to check
+ * @return offset of the vector
+ */
+ public double getOffset(Vector<Euclidean1D> vector) {
+ return getOffset((Point<Euclidean1D>) vector);
+ }
+
+ /** {@inheritDoc} */
+ public double getOffset(final Point<Euclidean1D> point) {
+ final double delta = ((Vector1D) point).getX() - location.getX();
+ return direct ? delta : -delta;
+ }
+
+ /** Build a region covering the whole hyperplane.
+ * <p>Since this class represent zero dimension spaces which does
+ * not have lower dimension sub-spaces, this method returns a dummy
+ * implementation of a {@link
+ * org.apache.commons.math3.geometry.partitioning.SubHyperplane SubHyperplane}.
+ * This implementation is only used to allow the {@link
+ * org.apache.commons.math3.geometry.partitioning.SubHyperplane
+ * SubHyperplane} class implementation to work properly, it should
+ * <em>not</em> be used otherwise.</p>
+ * @return a dummy sub hyperplane
+ */
+ public SubOrientedPoint wholeHyperplane() {
+ return new SubOrientedPoint(this, null);
+ }
+
+ /** Build a region covering the whole space.
+ * @return a region containing the instance (really an {@link
+ * IntervalsSet IntervalsSet} instance)
+ */
+ public IntervalsSet wholeSpace() {
+ return new IntervalsSet(tolerance);
+ }
+
+ /** {@inheritDoc} */
+ public boolean sameOrientationAs(final Hyperplane<Euclidean1D> other) {
+ return !(direct ^ ((OrientedPoint) other).direct);
+ }
+
+ /** {@inheritDoc}
+ * @since 3.3
+ */
+ public Point<Euclidean1D> project(Point<Euclidean1D> point) {
+ return location;
+ }
+
+ /** {@inheritDoc}
+ * @since 3.3
+ */
+ public double getTolerance() {
+ return tolerance;
+ }
+
+ /** Get the hyperplane location on the real line.
+ * @return the hyperplane location
+ */
+ public Vector1D getLocation() {
+ return location;
+ }
+
+ /** Check if the hyperplane orientation is direct.
+ * @return true if the plus side of the hyperplane is towards
+ * abscissae greater than hyperplane location
+ */
+ public boolean isDirect() {
+ return direct;
+ }
+
+ /** Revert the instance.
+ */
+ public void revertSelf() {
+ direct = !direct;
+ }
+
+}
diff --git a/src/main/java/org/apache/commons/math3/geometry/euclidean/oned/SubOrientedPoint.java b/src/main/java/org/apache/commons/math3/geometry/euclidean/oned/SubOrientedPoint.java
new file mode 100644
index 0000000..a0288bb
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/geometry/euclidean/oned/SubOrientedPoint.java
@@ -0,0 +1,72 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math3.geometry.euclidean.oned;
+
+import org.apache.commons.math3.geometry.partitioning.AbstractSubHyperplane;
+import org.apache.commons.math3.geometry.partitioning.Hyperplane;
+import org.apache.commons.math3.geometry.partitioning.Region;
+
+/** This class represents sub-hyperplane for {@link OrientedPoint}.
+ * <p>An hyperplane in 1D is a simple point, its orientation being a
+ * boolean.</p>
+ * <p>Instances of this class are guaranteed to be immutable.</p>
+ * @since 3.0
+ */
+public class SubOrientedPoint extends AbstractSubHyperplane<Euclidean1D, Euclidean1D> {
+
+ /** Simple constructor.
+ * @param hyperplane underlying hyperplane
+ * @param remainingRegion remaining region of the hyperplane
+ */
+ public SubOrientedPoint(final Hyperplane<Euclidean1D> hyperplane,
+ final Region<Euclidean1D> remainingRegion) {
+ super(hyperplane, remainingRegion);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public double getSize() {
+ return 0;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public boolean isEmpty() {
+ return false;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ protected AbstractSubHyperplane<Euclidean1D, Euclidean1D> buildNew(final Hyperplane<Euclidean1D> hyperplane,
+ final Region<Euclidean1D> remainingRegion) {
+ return new SubOrientedPoint(hyperplane, remainingRegion);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public SplitSubHyperplane<Euclidean1D> split(final Hyperplane<Euclidean1D> hyperplane) {
+ final double global = hyperplane.getOffset(((OrientedPoint) getHyperplane()).getLocation());
+ if (global < -1.0e-10) {
+ return new SplitSubHyperplane<Euclidean1D>(null, this);
+ } else if (global > 1.0e-10) {
+ return new SplitSubHyperplane<Euclidean1D>(this, null);
+ } else {
+ return new SplitSubHyperplane<Euclidean1D>(null, null);
+ }
+ }
+
+}
diff --git a/src/main/java/org/apache/commons/math3/geometry/euclidean/oned/Vector1D.java b/src/main/java/org/apache/commons/math3/geometry/euclidean/oned/Vector1D.java
new file mode 100644
index 0000000..1ec7a4e
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/geometry/euclidean/oned/Vector1D.java
@@ -0,0 +1,356 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math3.geometry.euclidean.oned;
+
+import java.text.NumberFormat;
+
+import org.apache.commons.math3.exception.MathArithmeticException;
+import org.apache.commons.math3.exception.util.LocalizedFormats;
+import org.apache.commons.math3.geometry.Point;
+import org.apache.commons.math3.geometry.Space;
+import org.apache.commons.math3.geometry.Vector;
+import org.apache.commons.math3.util.FastMath;
+import org.apache.commons.math3.util.MathUtils;
+
+/** This class represents a 1D vector.
+ * <p>Instances of this class are guaranteed to be immutable.</p>
+ * @since 3.0
+ */
+public class Vector1D implements Vector<Euclidean1D> {
+
+ /** Origin (coordinates: 0). */
+ public static final Vector1D ZERO = new Vector1D(0.0);
+
+ /** Unit (coordinates: 1). */
+ public static final Vector1D ONE = new Vector1D(1.0);
+
+ // CHECKSTYLE: stop ConstantName
+ /** A vector with all coordinates set to NaN. */
+ public static final Vector1D NaN = new Vector1D(Double.NaN);
+ // CHECKSTYLE: resume ConstantName
+
+ /** A vector with all coordinates set to positive infinity. */
+ public static final Vector1D POSITIVE_INFINITY =
+ new Vector1D(Double.POSITIVE_INFINITY);
+
+ /** A vector with all coordinates set to negative infinity. */
+ public static final Vector1D NEGATIVE_INFINITY =
+ new Vector1D(Double.NEGATIVE_INFINITY);
+
+ /** Serializable UID. */
+ private static final long serialVersionUID = 7556674948671647925L;
+
+ /** Abscissa. */
+ private final double x;
+
+ /** Simple constructor.
+ * Build a vector from its coordinates
+ * @param x abscissa
+ * @see #getX()
+ */
+ public Vector1D(double x) {
+ this.x = x;
+ }
+
+ /** Multiplicative constructor
+ * Build a vector from another one and a scale factor.
+ * The vector built will be a * u
+ * @param a scale factor
+ * @param u base (unscaled) vector
+ */
+ public Vector1D(double a, Vector1D u) {
+ this.x = a * u.x;
+ }
+
+ /** Linear constructor
+ * Build a vector from two other ones and corresponding scale factors.
+ * The vector built will be a1 * u1 + a2 * u2
+ * @param a1 first scale factor
+ * @param u1 first base (unscaled) vector
+ * @param a2 second scale factor
+ * @param u2 second base (unscaled) vector
+ */
+ public Vector1D(double a1, Vector1D u1, double a2, Vector1D u2) {
+ this.x = a1 * u1.x + a2 * u2.x;
+ }
+
+ /** Linear constructor
+ * Build a vector from three other ones and corresponding scale factors.
+ * The vector built will be a1 * u1 + a2 * u2 + a3 * u3
+ * @param a1 first scale factor
+ * @param u1 first base (unscaled) vector
+ * @param a2 second scale factor
+ * @param u2 second base (unscaled) vector
+ * @param a3 third scale factor
+ * @param u3 third base (unscaled) vector
+ */
+ public Vector1D(double a1, Vector1D u1, double a2, Vector1D u2,
+ double a3, Vector1D u3) {
+ this.x = a1 * u1.x + a2 * u2.x + a3 * u3.x;
+ }
+
+ /** Linear constructor
+ * Build a vector from four other ones and corresponding scale factors.
+ * The vector built will be a1 * u1 + a2 * u2 + a3 * u3 + a4 * u4
+ * @param a1 first scale factor
+ * @param u1 first base (unscaled) vector
+ * @param a2 second scale factor
+ * @param u2 second base (unscaled) vector
+ * @param a3 third scale factor
+ * @param u3 third base (unscaled) vector
+ * @param a4 fourth scale factor
+ * @param u4 fourth base (unscaled) vector
+ */
+ public Vector1D(double a1, Vector1D u1, double a2, Vector1D u2,
+ double a3, Vector1D u3, double a4, Vector1D u4) {
+ this.x = a1 * u1.x + a2 * u2.x + a3 * u3.x + a4 * u4.x;
+ }
+
+ /** Get the abscissa of the vector.
+ * @return abscissa of the vector
+ * @see #Vector1D(double)
+ */
+ public double getX() {
+ return x;
+ }
+
+ /** {@inheritDoc} */
+ public Space getSpace() {
+ return Euclidean1D.getInstance();
+ }
+
+ /** {@inheritDoc} */
+ public Vector1D getZero() {
+ return ZERO;
+ }
+
+ /** {@inheritDoc} */
+ public double getNorm1() {
+ return FastMath.abs(x);
+ }
+
+ /** {@inheritDoc} */
+ public double getNorm() {
+ return FastMath.abs(x);
+ }
+
+ /** {@inheritDoc} */
+ public double getNormSq() {
+ return x * x;
+ }
+
+ /** {@inheritDoc} */
+ public double getNormInf() {
+ return FastMath.abs(x);
+ }
+
+ /** {@inheritDoc} */
+ public Vector1D add(Vector<Euclidean1D> v) {
+ Vector1D v1 = (Vector1D) v;
+ return new Vector1D(x + v1.getX());
+ }
+
+ /** {@inheritDoc} */
+ public Vector1D add(double factor, Vector<Euclidean1D> v) {
+ Vector1D v1 = (Vector1D) v;
+ return new Vector1D(x + factor * v1.getX());
+ }
+
+ /** {@inheritDoc} */
+ public Vector1D subtract(Vector<Euclidean1D> p) {
+ Vector1D p3 = (Vector1D) p;
+ return new Vector1D(x - p3.x);
+ }
+
+ /** {@inheritDoc} */
+ public Vector1D subtract(double factor, Vector<Euclidean1D> v) {
+ Vector1D v1 = (Vector1D) v;
+ return new Vector1D(x - factor * v1.getX());
+ }
+
+ /** {@inheritDoc} */
+ public Vector1D normalize() throws MathArithmeticException {
+ double s = getNorm();
+ if (s == 0) {
+ throw new MathArithmeticException(LocalizedFormats.CANNOT_NORMALIZE_A_ZERO_NORM_VECTOR);
+ }
+ return scalarMultiply(1 / s);
+ }
+ /** {@inheritDoc} */
+ public Vector1D negate() {
+ return new Vector1D(-x);
+ }
+
+ /** {@inheritDoc} */
+ public Vector1D scalarMultiply(double a) {
+ return new Vector1D(a * x);
+ }
+
+ /** {@inheritDoc} */
+ public boolean isNaN() {
+ return Double.isNaN(x);
+ }
+
+ /** {@inheritDoc} */
+ public boolean isInfinite() {
+ return !isNaN() && Double.isInfinite(x);
+ }
+
+ /** {@inheritDoc} */
+ public double distance1(Vector<Euclidean1D> p) {
+ Vector1D p3 = (Vector1D) p;
+ final double dx = FastMath.abs(p3.x - x);
+ return dx;
+ }
+
+ /** {@inheritDoc}
+ * @deprecated as of 3.3, replaced with {@link #distance(Point)}
+ */
+ @Deprecated
+ public double distance(Vector<Euclidean1D> p) {
+ return distance((Point<Euclidean1D>) p);
+ }
+
+ /** {@inheritDoc} */
+ public double distance(Point<Euclidean1D> p) {
+ Vector1D p3 = (Vector1D) p;
+ final double dx = p3.x - x;
+ return FastMath.abs(dx);
+ }
+
+ /** {@inheritDoc} */
+ public double distanceInf(Vector<Euclidean1D> p) {
+ Vector1D p3 = (Vector1D) p;
+ final double dx = FastMath.abs(p3.x - x);
+ return dx;
+ }
+
+ /** {@inheritDoc} */
+ public double distanceSq(Vector<Euclidean1D> p) {
+ Vector1D p3 = (Vector1D) p;
+ final double dx = p3.x - x;
+ return dx * dx;
+ }
+
+ /** {@inheritDoc} */
+ public double dotProduct(final Vector<Euclidean1D> v) {
+ final Vector1D v1 = (Vector1D) v;
+ return x * v1.x;
+ }
+
+ /** Compute the distance between two vectors according to the L<sub>2</sub> norm.
+ * <p>Calling this method is equivalent to calling:
+ * <code>p1.subtract(p2).getNorm()</code> except that no intermediate
+ * vector is built</p>
+ * @param p1 first vector
+ * @param p2 second vector
+ * @return the distance between p1 and p2 according to the L<sub>2</sub> norm
+ */
+ public static double distance(Vector1D p1, Vector1D p2) {
+ return p1.distance(p2);
+ }
+
+ /** Compute the distance between two vectors according to the L<sub>&infin;</sub> norm.
+ * <p>Calling this method is equivalent to calling:
+ * <code>p1.subtract(p2).getNormInf()</code> except that no intermediate
+ * vector is built</p>
+ * @param p1 first vector
+ * @param p2 second vector
+ * @return the distance between p1 and p2 according to the L<sub>&infin;</sub> norm
+ */
+ public static double distanceInf(Vector1D p1, Vector1D p2) {
+ return p1.distanceInf(p2);
+ }
+
+ /** Compute the square of the distance between two vectors.
+ * <p>Calling this method is equivalent to calling:
+ * <code>p1.subtract(p2).getNormSq()</code> except that no intermediate
+ * vector is built</p>
+ * @param p1 first vector
+ * @param p2 second vector
+ * @return the square of the distance between p1 and p2
+ */
+ public static double distanceSq(Vector1D p1, Vector1D p2) {
+ return p1.distanceSq(p2);
+ }
+
+ /**
+ * Test for the equality of two 1D vectors.
+ * <p>
+ * If all coordinates of two 1D vectors are exactly the same, and none are
+ * <code>Double.NaN</code>, the two 1D vectors are considered to be equal.
+ * </p>
+ * <p>
+ * <code>NaN</code> coordinates are considered to affect globally the vector
+ * and be equals to each other - i.e, if either (or all) coordinates of the
+ * 1D vector are equal to <code>Double.NaN</code>, the 1D vector is equal to
+ * {@link #NaN}.
+ * </p>
+ *
+ * @param other Object to test for equality to this
+ * @return true if two 1D vector objects are equal, false if
+ * object is null, not an instance of Vector1D, or
+ * not equal to this Vector1D instance
+ *
+ */
+ @Override
+ public boolean equals(Object other) {
+
+ if (this == other) {
+ return true;
+ }
+
+ if (other instanceof Vector1D) {
+ final Vector1D rhs = (Vector1D)other;
+ if (rhs.isNaN()) {
+ return this.isNaN();
+ }
+
+ return x == rhs.x;
+ }
+ return false;
+ }
+
+ /**
+ * Get a hashCode for the 1D vector.
+ * <p>
+ * All NaN values have the same hash code.</p>
+ *
+ * @return a hash code value for this object
+ */
+ @Override
+ public int hashCode() {
+ if (isNaN()) {
+ return 7785;
+ }
+ return 997 * MathUtils.hash(x);
+ }
+
+ /** Get a string representation of this vector.
+ * @return a string representation of this vector
+ */
+ @Override
+ public String toString() {
+ return Vector1DFormat.getInstance().format(this);
+ }
+
+ /** {@inheritDoc} */
+ public String toString(final NumberFormat format) {
+ return new Vector1DFormat(format).format(this);
+ }
+
+}
diff --git a/src/main/java/org/apache/commons/math3/geometry/euclidean/oned/Vector1DFormat.java b/src/main/java/org/apache/commons/math3/geometry/euclidean/oned/Vector1DFormat.java
new file mode 100644
index 0000000..27f1905
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/geometry/euclidean/oned/Vector1DFormat.java
@@ -0,0 +1,135 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math3.geometry.euclidean.oned;
+
+import java.text.FieldPosition;
+import java.text.NumberFormat;
+import java.text.ParsePosition;
+import java.util.Locale;
+
+import org.apache.commons.math3.exception.MathParseException;
+import org.apache.commons.math3.geometry.Vector;
+import org.apache.commons.math3.geometry.VectorFormat;
+import org.apache.commons.math3.util.CompositeFormat;
+
+/**
+ * Formats a 1D vector in components list format "{x}".
+ * <p>The prefix and suffix "{" and "}" can be replaced by
+ * any user-defined strings. The number format for components can be configured.</p>
+ * <p>White space is ignored at parse time, even if it is in the prefix, suffix
+ * or separator specifications. So even if the default separator does include a space
+ * character that is used at format time, both input string "{1}" and
+ * " { 1 } " will be parsed without error and the same vector will be
+ * returned. In the second case, however, the parse position after parsing will be
+ * just after the closing curly brace, i.e. just before the trailing space.</p>
+ * <p><b>Note:</b> using "," as a separator may interfere with the grouping separator
+ * of the default {@link NumberFormat} for the current locale. Thus it is advised
+ * to use a {@link NumberFormat} instance with disabled grouping in such a case.</p>
+ *
+ * @since 3.0
+ */
+public class Vector1DFormat extends VectorFormat<Euclidean1D> {
+
+ /**
+ * Create an instance with default settings.
+ * <p>The instance uses the default prefix, suffix and separator:
+ * "{", "}", and "; " and the default number format for components.</p>
+ */
+ public Vector1DFormat() {
+ super(DEFAULT_PREFIX, DEFAULT_SUFFIX, DEFAULT_SEPARATOR,
+ CompositeFormat.getDefaultNumberFormat());
+ }
+
+ /**
+ * Create an instance with a custom number format for components.
+ * @param format the custom format for components.
+ */
+ public Vector1DFormat(final NumberFormat format) {
+ super(DEFAULT_PREFIX, DEFAULT_SUFFIX, DEFAULT_SEPARATOR, format);
+ }
+
+ /**
+ * Create an instance with custom prefix, suffix and separator.
+ * @param prefix prefix to use instead of the default "{"
+ * @param suffix suffix to use instead of the default "}"
+ */
+ public Vector1DFormat(final String prefix, final String suffix) {
+ super(prefix, suffix, DEFAULT_SEPARATOR, CompositeFormat.getDefaultNumberFormat());
+ }
+
+ /**
+ * Create an instance with custom prefix, suffix, separator and format
+ * for components.
+ * @param prefix prefix to use instead of the default "{"
+ * @param suffix suffix to use instead of the default "}"
+ * @param format the custom format for components.
+ */
+ public Vector1DFormat(final String prefix, final String suffix,
+ final NumberFormat format) {
+ super(prefix, suffix, DEFAULT_SEPARATOR, format);
+ }
+
+ /**
+ * Returns the default 1D vector format for the current locale.
+ * @return the default 1D vector format.
+ */
+ public static Vector1DFormat getInstance() {
+ return getInstance(Locale.getDefault());
+ }
+
+ /**
+ * Returns the default 1D vector format for the given locale.
+ * @param locale the specific locale used by the format.
+ * @return the 1D vector format specific to the given locale.
+ */
+ public static Vector1DFormat getInstance(final Locale locale) {
+ return new Vector1DFormat(CompositeFormat.getDefaultNumberFormat(locale));
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public StringBuffer format(final Vector<Euclidean1D> vector, final StringBuffer toAppendTo,
+ final FieldPosition pos) {
+ final Vector1D p1 = (Vector1D) vector;
+ return format(toAppendTo, pos, p1.getX());
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public Vector1D parse(final String source) throws MathParseException {
+ ParsePosition parsePosition = new ParsePosition(0);
+ Vector1D result = parse(source, parsePosition);
+ if (parsePosition.getIndex() == 0) {
+ throw new MathParseException(source,
+ parsePosition.getErrorIndex(),
+ Vector1D.class);
+ }
+ return result;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public Vector1D parse(final String source, final ParsePosition pos) {
+ final double[] coordinates = parseCoordinates(1, source, pos);
+ if (coordinates == null) {
+ return null;
+ }
+ return new Vector1D(coordinates[0]);
+ }
+
+}
diff --git a/src/main/java/org/apache/commons/math3/geometry/euclidean/oned/package-info.java b/src/main/java/org/apache/commons/math3/geometry/euclidean/oned/package-info.java
new file mode 100644
index 0000000..0fa3788
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/geometry/euclidean/oned/package-info.java
@@ -0,0 +1,24 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/**
+ *
+ * <p>
+ * This package provides basic 1D geometry components.
+ * </p>
+ *
+ */
+package org.apache.commons.math3.geometry.euclidean.oned;
diff --git a/src/main/java/org/apache/commons/math3/geometry/euclidean/threed/CardanEulerSingularityException.java b/src/main/java/org/apache/commons/math3/geometry/euclidean/threed/CardanEulerSingularityException.java
new file mode 100644
index 0000000..728074d
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/geometry/euclidean/threed/CardanEulerSingularityException.java
@@ -0,0 +1,44 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math3.geometry.euclidean.threed;
+
+import org.apache.commons.math3.exception.MathIllegalStateException;
+import org.apache.commons.math3.exception.util.LocalizedFormats;
+
+/** This class represents exceptions thrown while extractiong Cardan
+ * or Euler angles from a rotation.
+
+ * @since 1.2
+ */
+public class CardanEulerSingularityException
+ extends MathIllegalStateException {
+
+ /** Serializable version identifier */
+ private static final long serialVersionUID = -1360952845582206770L;
+
+ /**
+ * Simple constructor.
+ * build an exception with a default message.
+ * @param isCardan if true, the rotation is related to Cardan angles,
+ * if false it is related to EulerAngles
+ */
+ public CardanEulerSingularityException(boolean isCardan) {
+ super(isCardan ? LocalizedFormats.CARDAN_ANGLES_SINGULARITY : LocalizedFormats.EULER_ANGLES_SINGULARITY);
+ }
+
+}
diff --git a/src/main/java/org/apache/commons/math3/geometry/euclidean/threed/Euclidean3D.java b/src/main/java/org/apache/commons/math3/geometry/euclidean/threed/Euclidean3D.java
new file mode 100644
index 0000000..dc06936
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/geometry/euclidean/threed/Euclidean3D.java
@@ -0,0 +1,74 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math3.geometry.euclidean.threed;
+
+import java.io.Serializable;
+
+import org.apache.commons.math3.geometry.Space;
+import org.apache.commons.math3.geometry.euclidean.twod.Euclidean2D;
+
+/**
+ * This class implements a three-dimensional space.
+ * @since 3.0
+ */
+public class Euclidean3D implements Serializable, Space {
+
+ /** Serializable version identifier. */
+ private static final long serialVersionUID = 6249091865814886817L;
+
+ /** Private constructor for the singleton.
+ */
+ private Euclidean3D() {
+ }
+
+ /** Get the unique instance.
+ * @return the unique instance
+ */
+ public static Euclidean3D getInstance() {
+ return LazyHolder.INSTANCE;
+ }
+
+ /** {@inheritDoc} */
+ public int getDimension() {
+ return 3;
+ }
+
+ /** {@inheritDoc} */
+ public Euclidean2D getSubSpace() {
+ return Euclidean2D.getInstance();
+ }
+
+ // CHECKSTYLE: stop HideUtilityClassConstructor
+ /** Holder for the instance.
+ * <p>We use here the Initialization On Demand Holder Idiom.</p>
+ */
+ private static class LazyHolder {
+ /** Cached field instance. */
+ private static final Euclidean3D INSTANCE = new Euclidean3D();
+ }
+ // CHECKSTYLE: resume HideUtilityClassConstructor
+
+ /** Handle deserialization of the singleton.
+ * @return the singleton instance
+ */
+ private Object readResolve() {
+ // return the singleton instance
+ return LazyHolder.INSTANCE;
+ }
+
+}
diff --git a/src/main/java/org/apache/commons/math3/geometry/euclidean/threed/FieldRotation.java b/src/main/java/org/apache/commons/math3/geometry/euclidean/threed/FieldRotation.java
new file mode 100644
index 0000000..4e2278b
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/geometry/euclidean/threed/FieldRotation.java
@@ -0,0 +1,1663 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math3.geometry.euclidean.threed;
+
+import java.io.Serializable;
+
+import org.apache.commons.math3.RealFieldElement;
+import org.apache.commons.math3.Field;
+import org.apache.commons.math3.exception.MathArithmeticException;
+import org.apache.commons.math3.exception.MathIllegalArgumentException;
+import org.apache.commons.math3.exception.util.LocalizedFormats;
+import org.apache.commons.math3.util.FastMath;
+import org.apache.commons.math3.util.MathArrays;
+
+/**
+ * This class is a re-implementation of {@link Rotation} using {@link RealFieldElement}.
+ * <p>Instance of this class are guaranteed to be immutable.</p>
+ *
+ * @param <T> the type of the field elements
+ * @see FieldVector3D
+ * @see RotationOrder
+ * @since 3.2
+ */
+
+public class FieldRotation<T extends RealFieldElement<T>> implements Serializable {
+
+ /** Serializable version identifier */
+ private static final long serialVersionUID = 20130224l;
+
+ /** Scalar coordinate of the quaternion. */
+ private final T q0;
+
+ /** First coordinate of the vectorial part of the quaternion. */
+ private final T q1;
+
+ /** Second coordinate of the vectorial part of the quaternion. */
+ private final T q2;
+
+ /** Third coordinate of the vectorial part of the quaternion. */
+ private final T q3;
+
+ /** Build a rotation from the quaternion coordinates.
+ * <p>A rotation can be built from a <em>normalized</em> quaternion,
+ * i.e. a quaternion for which q<sub>0</sub><sup>2</sup> +
+ * q<sub>1</sub><sup>2</sup> + q<sub>2</sub><sup>2</sup> +
+ * q<sub>3</sub><sup>2</sup> = 1. If the quaternion is not normalized,
+ * the constructor can normalize it in a preprocessing step.</p>
+ * <p>Note that some conventions put the scalar part of the quaternion
+ * as the 4<sup>th</sup> component and the vector part as the first three
+ * components. This is <em>not</em> our convention. We put the scalar part
+ * as the first component.</p>
+ * @param q0 scalar part of the quaternion
+ * @param q1 first coordinate of the vectorial part of the quaternion
+ * @param q2 second coordinate of the vectorial part of the quaternion
+ * @param q3 third coordinate of the vectorial part of the quaternion
+ * @param needsNormalization if true, the coordinates are considered
+ * not to be normalized, a normalization preprocessing step is performed
+ * before using them
+ */
+ public FieldRotation(final T q0, final T q1, final T q2, final T q3, final boolean needsNormalization) {
+
+ if (needsNormalization) {
+ // normalization preprocessing
+ final T inv =
+ q0.multiply(q0).add(q1.multiply(q1)).add(q2.multiply(q2)).add(q3.multiply(q3)).sqrt().reciprocal();
+ this.q0 = inv.multiply(q0);
+ this.q1 = inv.multiply(q1);
+ this.q2 = inv.multiply(q2);
+ this.q3 = inv.multiply(q3);
+ } else {
+ this.q0 = q0;
+ this.q1 = q1;
+ this.q2 = q2;
+ this.q3 = q3;
+ }
+
+ }
+
+ /** Build a rotation from an axis and an angle.
+ * <p>We use the convention that angles are oriented according to
+ * the effect of the rotation on vectors around the axis. That means
+ * that if (i, j, k) is a direct frame and if we first provide +k as
+ * the axis and &pi;/2 as the angle to this constructor, and then
+ * {@link #applyTo(FieldVector3D) apply} the instance to +i, we will get
+ * +j.</p>
+ * <p>Another way to represent our convention is to say that a rotation
+ * of angle &theta; about the unit vector (x, y, z) is the same as the
+ * rotation build from quaternion components { cos(-&theta;/2),
+ * x * sin(-&theta;/2), y * sin(-&theta;/2), z * sin(-&theta;/2) }.
+ * Note the minus sign on the angle!</p>
+ * <p>On the one hand this convention is consistent with a vectorial
+ * perspective (moving vectors in fixed frames), on the other hand it
+ * is different from conventions with a frame perspective (fixed vectors
+ * viewed from different frames) like the ones used for example in spacecraft
+ * attitude community or in the graphics community.</p>
+ * @param axis axis around which to rotate
+ * @param angle rotation angle.
+ * @exception MathIllegalArgumentException if the axis norm is zero
+ * @deprecated as of 3.6, replaced with {@link
+ * #FieldRotation(FieldVector3D, RealFieldElement, RotationConvention)}
+ */
+ @Deprecated
+ public FieldRotation(final FieldVector3D<T> axis, final T angle)
+ throws MathIllegalArgumentException {
+ this(axis, angle, RotationConvention.VECTOR_OPERATOR);
+ }
+
+ /** Build a rotation from an axis and an angle.
+ * <p>We use the convention that angles are oriented according to
+ * the effect of the rotation on vectors around the axis. That means
+ * that if (i, j, k) is a direct frame and if we first provide +k as
+ * the axis and &pi;/2 as the angle to this constructor, and then
+ * {@link #applyTo(FieldVector3D) apply} the instance to +i, we will get
+ * +j.</p>
+ * <p>Another way to represent our convention is to say that a rotation
+ * of angle &theta; about the unit vector (x, y, z) is the same as the
+ * rotation build from quaternion components { cos(-&theta;/2),
+ * x * sin(-&theta;/2), y * sin(-&theta;/2), z * sin(-&theta;/2) }.
+ * Note the minus sign on the angle!</p>
+ * <p>On the one hand this convention is consistent with a vectorial
+ * perspective (moving vectors in fixed frames), on the other hand it
+ * is different from conventions with a frame perspective (fixed vectors
+ * viewed from different frames) like the ones used for example in spacecraft
+ * attitude community or in the graphics community.</p>
+ * @param axis axis around which to rotate
+ * @param angle rotation angle.
+ * @param convention convention to use for the semantics of the angle
+ * @exception MathIllegalArgumentException if the axis norm is zero
+ * @since 3.6
+ */
+ public FieldRotation(final FieldVector3D<T> axis, final T angle, final RotationConvention convention)
+ throws MathIllegalArgumentException {
+
+ final T norm = axis.getNorm();
+ if (norm.getReal() == 0) {
+ throw new MathIllegalArgumentException(LocalizedFormats.ZERO_NORM_FOR_ROTATION_AXIS);
+ }
+
+ final T halfAngle = angle.multiply(convention == RotationConvention.VECTOR_OPERATOR ? -0.5 : 0.5);
+ final T coeff = halfAngle.sin().divide(norm);
+
+ q0 = halfAngle.cos();
+ q1 = coeff.multiply(axis.getX());
+ q2 = coeff.multiply(axis.getY());
+ q3 = coeff.multiply(axis.getZ());
+
+ }
+
+ /** Build a rotation from a 3X3 matrix.
+
+ * <p>Rotation matrices are orthogonal matrices, i.e. unit matrices
+ * (which are matrices for which m.m<sup>T</sup> = I) with real
+ * coefficients. The module of the determinant of unit matrices is
+ * 1, among the orthogonal 3X3 matrices, only the ones having a
+ * positive determinant (+1) are rotation matrices.</p>
+
+ * <p>When a rotation is defined by a matrix with truncated values
+ * (typically when it is extracted from a technical sheet where only
+ * four to five significant digits are available), the matrix is not
+ * orthogonal anymore. This constructor handles this case
+ * transparently by using a copy of the given matrix and applying a
+ * correction to the copy in order to perfect its orthogonality. If
+ * the Frobenius norm of the correction needed is above the given
+ * threshold, then the matrix is considered to be too far from a
+ * true rotation matrix and an exception is thrown.<p>
+
+ * @param m rotation matrix
+ * @param threshold convergence threshold for the iterative
+ * orthogonality correction (convergence is reached when the
+ * difference between two steps of the Frobenius norm of the
+ * correction is below this threshold)
+
+ * @exception NotARotationMatrixException if the matrix is not a 3X3
+ * matrix, or if it cannot be transformed into an orthogonal matrix
+ * with the given threshold, or if the determinant of the resulting
+ * orthogonal matrix is negative
+
+ */
+ public FieldRotation(final T[][] m, final double threshold)
+ throws NotARotationMatrixException {
+
+ // dimension check
+ if ((m.length != 3) || (m[0].length != 3) ||
+ (m[1].length != 3) || (m[2].length != 3)) {
+ throw new NotARotationMatrixException(
+ LocalizedFormats.ROTATION_MATRIX_DIMENSIONS,
+ m.length, m[0].length);
+ }
+
+ // compute a "close" orthogonal matrix
+ final T[][] ort = orthogonalizeMatrix(m, threshold);
+
+ // check the sign of the determinant
+ final T d0 = ort[1][1].multiply(ort[2][2]).subtract(ort[2][1].multiply(ort[1][2]));
+ final T d1 = ort[0][1].multiply(ort[2][2]).subtract(ort[2][1].multiply(ort[0][2]));
+ final T d2 = ort[0][1].multiply(ort[1][2]).subtract(ort[1][1].multiply(ort[0][2]));
+ final T det =
+ ort[0][0].multiply(d0).subtract(ort[1][0].multiply(d1)).add(ort[2][0].multiply(d2));
+ if (det.getReal() < 0.0) {
+ throw new NotARotationMatrixException(
+ LocalizedFormats.CLOSEST_ORTHOGONAL_MATRIX_HAS_NEGATIVE_DETERMINANT,
+ det);
+ }
+
+ final T[] quat = mat2quat(ort);
+ q0 = quat[0];
+ q1 = quat[1];
+ q2 = quat[2];
+ q3 = quat[3];
+
+ }
+
+ /** Build the rotation that transforms a pair of vectors into another pair.
+
+ * <p>Except for possible scale factors, if the instance were applied to
+ * the pair (u<sub>1</sub>, u<sub>2</sub>) it will produce the pair
+ * (v<sub>1</sub>, v<sub>2</sub>).</p>
+
+ * <p>If the angular separation between u<sub>1</sub> and u<sub>2</sub> is
+ * not the same as the angular separation between v<sub>1</sub> and
+ * v<sub>2</sub>, then a corrected v'<sub>2</sub> will be used rather than
+ * v<sub>2</sub>, the corrected vector will be in the (&pm;v<sub>1</sub>,
+ * +v<sub>2</sub>) half-plane.</p>
+
+ * @param u1 first vector of the origin pair
+ * @param u2 second vector of the origin pair
+ * @param v1 desired image of u1 by the rotation
+ * @param v2 desired image of u2 by the rotation
+ * @exception MathArithmeticException if the norm of one of the vectors is zero,
+ * or if one of the pair is degenerated (i.e. the vectors of the pair are collinear)
+ */
+ public FieldRotation(FieldVector3D<T> u1, FieldVector3D<T> u2, FieldVector3D<T> v1, FieldVector3D<T> v2)
+ throws MathArithmeticException {
+
+ // build orthonormalized base from u1, u2
+ // this fails when vectors are null or collinear, which is forbidden to define a rotation
+ final FieldVector3D<T> u3 = FieldVector3D.crossProduct(u1, u2).normalize();
+ u2 = FieldVector3D.crossProduct(u3, u1).normalize();
+ u1 = u1.normalize();
+
+ // build an orthonormalized base from v1, v2
+ // this fails when vectors are null or collinear, which is forbidden to define a rotation
+ final FieldVector3D<T> v3 = FieldVector3D.crossProduct(v1, v2).normalize();
+ v2 = FieldVector3D.crossProduct(v3, v1).normalize();
+ v1 = v1.normalize();
+
+ // buid a matrix transforming the first base into the second one
+ final T[][] array = MathArrays.buildArray(u1.getX().getField(), 3, 3);
+ array[0][0] = u1.getX().multiply(v1.getX()).add(u2.getX().multiply(v2.getX())).add(u3.getX().multiply(v3.getX()));
+ array[0][1] = u1.getY().multiply(v1.getX()).add(u2.getY().multiply(v2.getX())).add(u3.getY().multiply(v3.getX()));
+ array[0][2] = u1.getZ().multiply(v1.getX()).add(u2.getZ().multiply(v2.getX())).add(u3.getZ().multiply(v3.getX()));
+ array[1][0] = u1.getX().multiply(v1.getY()).add(u2.getX().multiply(v2.getY())).add(u3.getX().multiply(v3.getY()));
+ array[1][1] = u1.getY().multiply(v1.getY()).add(u2.getY().multiply(v2.getY())).add(u3.getY().multiply(v3.getY()));
+ array[1][2] = u1.getZ().multiply(v1.getY()).add(u2.getZ().multiply(v2.getY())).add(u3.getZ().multiply(v3.getY()));
+ array[2][0] = u1.getX().multiply(v1.getZ()).add(u2.getX().multiply(v2.getZ())).add(u3.getX().multiply(v3.getZ()));
+ array[2][1] = u1.getY().multiply(v1.getZ()).add(u2.getY().multiply(v2.getZ())).add(u3.getY().multiply(v3.getZ()));
+ array[2][2] = u1.getZ().multiply(v1.getZ()).add(u2.getZ().multiply(v2.getZ())).add(u3.getZ().multiply(v3.getZ()));
+
+ T[] quat = mat2quat(array);
+ q0 = quat[0];
+ q1 = quat[1];
+ q2 = quat[2];
+ q3 = quat[3];
+
+ }
+
+ /** Build one of the rotations that transform one vector into another one.
+
+ * <p>Except for a possible scale factor, if the instance were
+ * applied to the vector u it will produce the vector v. There is an
+ * infinite number of such rotations, this constructor choose the
+ * one with the smallest associated angle (i.e. the one whose axis
+ * is orthogonal to the (u, v) plane). If u and v are collinear, an
+ * arbitrary rotation axis is chosen.</p>
+
+ * @param u origin vector
+ * @param v desired image of u by the rotation
+ * @exception MathArithmeticException if the norm of one of the vectors is zero
+ */
+ public FieldRotation(final FieldVector3D<T> u, final FieldVector3D<T> v) throws MathArithmeticException {
+
+ final T normProduct = u.getNorm().multiply(v.getNorm());
+ if (normProduct.getReal() == 0) {
+ throw new MathArithmeticException(LocalizedFormats.ZERO_NORM_FOR_ROTATION_DEFINING_VECTOR);
+ }
+
+ final T dot = FieldVector3D.dotProduct(u, v);
+
+ if (dot.getReal() < ((2.0e-15 - 1.0) * normProduct.getReal())) {
+ // special case u = -v: we select a PI angle rotation around
+ // an arbitrary vector orthogonal to u
+ final FieldVector3D<T> w = u.orthogonal();
+ q0 = normProduct.getField().getZero();
+ q1 = w.getX().negate();
+ q2 = w.getY().negate();
+ q3 = w.getZ().negate();
+ } else {
+ // general case: (u, v) defines a plane, we select
+ // the shortest possible rotation: axis orthogonal to this plane
+ q0 = dot.divide(normProduct).add(1.0).multiply(0.5).sqrt();
+ final T coeff = q0.multiply(normProduct).multiply(2.0).reciprocal();
+ final FieldVector3D<T> q = FieldVector3D.crossProduct(v, u);
+ q1 = coeff.multiply(q.getX());
+ q2 = coeff.multiply(q.getY());
+ q3 = coeff.multiply(q.getZ());
+ }
+
+ }
+
+ /** Build a rotation from three Cardan or Euler elementary rotations.
+
+ * <p>Cardan rotations are three successive rotations around the
+ * canonical axes X, Y and Z, each axis being used once. There are
+ * 6 such sets of rotations (XYZ, XZY, YXZ, YZX, ZXY and ZYX). Euler
+ * rotations are three successive rotations around the canonical
+ * axes X, Y and Z, the first and last rotations being around the
+ * same axis. There are 6 such sets of rotations (XYX, XZX, YXY,
+ * YZY, ZXZ and ZYZ), the most popular one being ZXZ.</p>
+ * <p>Beware that many people routinely use the term Euler angles even
+ * for what really are Cardan angles (this confusion is especially
+ * widespread in the aerospace business where Roll, Pitch and Yaw angles
+ * are often wrongly tagged as Euler angles).</p>
+
+ * @param order order of rotations to use
+ * @param alpha1 angle of the first elementary rotation
+ * @param alpha2 angle of the second elementary rotation
+ * @param alpha3 angle of the third elementary rotation
+ * @deprecated as of 3.6, replaced with {@link
+ * #FieldRotation(RotationOrder, RotationConvention,
+ * RealFieldElement, RealFieldElement, RealFieldElement)}
+ */
+ @Deprecated
+ public FieldRotation(final RotationOrder order, final T alpha1, final T alpha2, final T alpha3) {
+ this(order, RotationConvention.VECTOR_OPERATOR, alpha1, alpha2, alpha3);
+ }
+
+ /** Build a rotation from three Cardan or Euler elementary rotations.
+
+ * <p>Cardan rotations are three successive rotations around the
+ * canonical axes X, Y and Z, each axis being used once. There are
+ * 6 such sets of rotations (XYZ, XZY, YXZ, YZX, ZXY and ZYX). Euler
+ * rotations are three successive rotations around the canonical
+ * axes X, Y and Z, the first and last rotations being around the
+ * same axis. There are 6 such sets of rotations (XYX, XZX, YXY,
+ * YZY, ZXZ and ZYZ), the most popular one being ZXZ.</p>
+ * <p>Beware that many people routinely use the term Euler angles even
+ * for what really are Cardan angles (this confusion is especially
+ * widespread in the aerospace business where Roll, Pitch and Yaw angles
+ * are often wrongly tagged as Euler angles).</p>
+
+ * @param order order of rotations to compose, from left to right
+ * (i.e. we will use {@code r1.compose(r2.compose(r3, convention), convention)})
+ * @param convention convention to use for the semantics of the angle
+ * @param alpha1 angle of the first elementary rotation
+ * @param alpha2 angle of the second elementary rotation
+ * @param alpha3 angle of the third elementary rotation
+ * @since 3.6
+ */
+ public FieldRotation(final RotationOrder order, final RotationConvention convention,
+ final T alpha1, final T alpha2, final T alpha3) {
+ final T one = alpha1.getField().getOne();
+ final FieldRotation<T> r1 = new FieldRotation<T>(new FieldVector3D<T>(one, order.getA1()), alpha1, convention);
+ final FieldRotation<T> r2 = new FieldRotation<T>(new FieldVector3D<T>(one, order.getA2()), alpha2, convention);
+ final FieldRotation<T> r3 = new FieldRotation<T>(new FieldVector3D<T>(one, order.getA3()), alpha3, convention);
+ final FieldRotation<T> composed = r1.compose(r2.compose(r3, convention), convention);
+ q0 = composed.q0;
+ q1 = composed.q1;
+ q2 = composed.q2;
+ q3 = composed.q3;
+ }
+
+ /** Convert an orthogonal rotation matrix to a quaternion.
+ * @param ort orthogonal rotation matrix
+ * @return quaternion corresponding to the matrix
+ */
+ private T[] mat2quat(final T[][] ort) {
+
+ final T[] quat = MathArrays.buildArray(ort[0][0].getField(), 4);
+
+ // There are different ways to compute the quaternions elements
+ // from the matrix. They all involve computing one element from
+ // the diagonal of the matrix, and computing the three other ones
+ // using a formula involving a division by the first element,
+ // which unfortunately can be zero. Since the norm of the
+ // quaternion is 1, we know at least one element has an absolute
+ // value greater or equal to 0.5, so it is always possible to
+ // select the right formula and avoid division by zero and even
+ // numerical inaccuracy. Checking the elements in turn and using
+ // the first one greater than 0.45 is safe (this leads to a simple
+ // test since qi = 0.45 implies 4 qi^2 - 1 = -0.19)
+ T s = ort[0][0].add(ort[1][1]).add(ort[2][2]);
+ if (s.getReal() > -0.19) {
+ // compute q0 and deduce q1, q2 and q3
+ quat[0] = s.add(1.0).sqrt().multiply(0.5);
+ T inv = quat[0].reciprocal().multiply(0.25);
+ quat[1] = inv.multiply(ort[1][2].subtract(ort[2][1]));
+ quat[2] = inv.multiply(ort[2][0].subtract(ort[0][2]));
+ quat[3] = inv.multiply(ort[0][1].subtract(ort[1][0]));
+ } else {
+ s = ort[0][0].subtract(ort[1][1]).subtract(ort[2][2]);
+ if (s.getReal() > -0.19) {
+ // compute q1 and deduce q0, q2 and q3
+ quat[1] = s.add(1.0).sqrt().multiply(0.5);
+ T inv = quat[1].reciprocal().multiply(0.25);
+ quat[0] = inv.multiply(ort[1][2].subtract(ort[2][1]));
+ quat[2] = inv.multiply(ort[0][1].add(ort[1][0]));
+ quat[3] = inv.multiply(ort[0][2].add(ort[2][0]));
+ } else {
+ s = ort[1][1].subtract(ort[0][0]).subtract(ort[2][2]);
+ if (s.getReal() > -0.19) {
+ // compute q2 and deduce q0, q1 and q3
+ quat[2] = s.add(1.0).sqrt().multiply(0.5);
+ T inv = quat[2].reciprocal().multiply(0.25);
+ quat[0] = inv.multiply(ort[2][0].subtract(ort[0][2]));
+ quat[1] = inv.multiply(ort[0][1].add(ort[1][0]));
+ quat[3] = inv.multiply(ort[2][1].add(ort[1][2]));
+ } else {
+ // compute q3 and deduce q0, q1 and q2
+ s = ort[2][2].subtract(ort[0][0]).subtract(ort[1][1]);
+ quat[3] = s.add(1.0).sqrt().multiply(0.5);
+ T inv = quat[3].reciprocal().multiply(0.25);
+ quat[0] = inv.multiply(ort[0][1].subtract(ort[1][0]));
+ quat[1] = inv.multiply(ort[0][2].add(ort[2][0]));
+ quat[2] = inv.multiply(ort[2][1].add(ort[1][2]));
+ }
+ }
+ }
+
+ return quat;
+
+ }
+
+ /** Revert a rotation.
+ * Build a rotation which reverse the effect of another
+ * rotation. This means that if r(u) = v, then r.revert(v) = u. The
+ * instance is not changed.
+ * @return a new rotation whose effect is the reverse of the effect
+ * of the instance
+ */
+ public FieldRotation<T> revert() {
+ return new FieldRotation<T>(q0.negate(), q1, q2, q3, false);
+ }
+
+ /** Get the scalar coordinate of the quaternion.
+ * @return scalar coordinate of the quaternion
+ */
+ public T getQ0() {
+ return q0;
+ }
+
+ /** Get the first coordinate of the vectorial part of the quaternion.
+ * @return first coordinate of the vectorial part of the quaternion
+ */
+ public T getQ1() {
+ return q1;
+ }
+
+ /** Get the second coordinate of the vectorial part of the quaternion.
+ * @return second coordinate of the vectorial part of the quaternion
+ */
+ public T getQ2() {
+ return q2;
+ }
+
+ /** Get the third coordinate of the vectorial part of the quaternion.
+ * @return third coordinate of the vectorial part of the quaternion
+ */
+ public T getQ3() {
+ return q3;
+ }
+
+ /** Get the normalized axis of the rotation.
+ * @return normalized axis of the rotation
+ * @see #FieldRotation(FieldVector3D, RealFieldElement)
+ * @deprecated as of 3.6, replaced with {@link #getAxis(RotationConvention)}
+ */
+ @Deprecated
+ public FieldVector3D<T> getAxis() {
+ return getAxis(RotationConvention.VECTOR_OPERATOR);
+ }
+
+ /** Get the normalized axis of the rotation.
+ * <p>
+ * Note that as {@link #getAngle()} always returns an angle
+ * between 0 and &pi;, changing the convention changes the
+ * direction of the axis, not the sign of the angle.
+ * </p>
+ * @param convention convention to use for the semantics of the angle
+ * @return normalized axis of the rotation
+ * @see #FieldRotation(FieldVector3D, RealFieldElement)
+ * @since 3.6
+ */
+ public FieldVector3D<T> getAxis(final RotationConvention convention) {
+ final T squaredSine = q1.multiply(q1).add(q2.multiply(q2)).add(q3.multiply(q3));
+ if (squaredSine.getReal() == 0) {
+ final Field<T> field = squaredSine.getField();
+ return new FieldVector3D<T>(convention == RotationConvention.VECTOR_OPERATOR ? field.getOne(): field.getOne().negate(),
+ field.getZero(),
+ field.getZero());
+ } else {
+ final double sgn = convention == RotationConvention.VECTOR_OPERATOR ? +1 : -1;
+ if (q0.getReal() < 0) {
+ T inverse = squaredSine.sqrt().reciprocal().multiply(sgn);
+ return new FieldVector3D<T>(q1.multiply(inverse), q2.multiply(inverse), q3.multiply(inverse));
+ }
+ final T inverse = squaredSine.sqrt().reciprocal().negate().multiply(sgn);
+ return new FieldVector3D<T>(q1.multiply(inverse), q2.multiply(inverse), q3.multiply(inverse));
+ }
+ }
+
+ /** Get the angle of the rotation.
+ * @return angle of the rotation (between 0 and &pi;)
+ * @see #FieldRotation(FieldVector3D, RealFieldElement)
+ */
+ public T getAngle() {
+ if ((q0.getReal() < -0.1) || (q0.getReal() > 0.1)) {
+ return q1.multiply(q1).add(q2.multiply(q2)).add(q3.multiply(q3)).sqrt().asin().multiply(2);
+ } else if (q0.getReal() < 0) {
+ return q0.negate().acos().multiply(2);
+ }
+ return q0.acos().multiply(2);
+ }
+
+ /** Get the Cardan or Euler angles corresponding to the instance.
+
+ * <p>The equations show that each rotation can be defined by two
+ * different values of the Cardan or Euler angles set. For example
+ * if Cardan angles are used, the rotation defined by the angles
+ * a<sub>1</sub>, a<sub>2</sub> and a<sub>3</sub> is the same as
+ * the rotation defined by the angles &pi; + a<sub>1</sub>, &pi;
+ * - a<sub>2</sub> and &pi; + a<sub>3</sub>. This method implements
+ * the following arbitrary choices:</p>
+ * <ul>
+ * <li>for Cardan angles, the chosen set is the one for which the
+ * second angle is between -&pi;/2 and &pi;/2 (i.e its cosine is
+ * positive),</li>
+ * <li>for Euler angles, the chosen set is the one for which the
+ * second angle is between 0 and &pi; (i.e its sine is positive).</li>
+ * </ul>
+
+ * <p>Cardan and Euler angle have a very disappointing drawback: all
+ * of them have singularities. This means that if the instance is
+ * too close to the singularities corresponding to the given
+ * rotation order, it will be impossible to retrieve the angles. For
+ * Cardan angles, this is often called gimbal lock. There is
+ * <em>nothing</em> to do to prevent this, it is an intrinsic problem
+ * with Cardan and Euler representation (but not a problem with the
+ * rotation itself, which is perfectly well defined). For Cardan
+ * angles, singularities occur when the second angle is close to
+ * -&pi;/2 or +&pi;/2, for Euler angle singularities occur when the
+ * second angle is close to 0 or &pi;, this implies that the identity
+ * rotation is always singular for Euler angles!</p>
+
+ * @param order rotation order to use
+ * @return an array of three angles, in the order specified by the set
+ * @exception CardanEulerSingularityException if the rotation is
+ * singular with respect to the angles set specified
+ * @deprecated as of 3.6, replaced with {@link #getAngles(RotationOrder, RotationConvention)}
+ */
+ @Deprecated
+ public T[] getAngles(final RotationOrder order)
+ throws CardanEulerSingularityException {
+ return getAngles(order, RotationConvention.VECTOR_OPERATOR);
+ }
+
+ /** Get the Cardan or Euler angles corresponding to the instance.
+
+ * <p>The equations show that each rotation can be defined by two
+ * different values of the Cardan or Euler angles set. For example
+ * if Cardan angles are used, the rotation defined by the angles
+ * a<sub>1</sub>, a<sub>2</sub> and a<sub>3</sub> is the same as
+ * the rotation defined by the angles &pi; + a<sub>1</sub>, &pi;
+ * - a<sub>2</sub> and &pi; + a<sub>3</sub>. This method implements
+ * the following arbitrary choices:</p>
+ * <ul>
+ * <li>for Cardan angles, the chosen set is the one for which the
+ * second angle is between -&pi;/2 and &pi;/2 (i.e its cosine is
+ * positive),</li>
+ * <li>for Euler angles, the chosen set is the one for which the
+ * second angle is between 0 and &pi; (i.e its sine is positive).</li>
+ * </ul>
+
+ * <p>Cardan and Euler angle have a very disappointing drawback: all
+ * of them have singularities. This means that if the instance is
+ * too close to the singularities corresponding to the given
+ * rotation order, it will be impossible to retrieve the angles. For
+ * Cardan angles, this is often called gimbal lock. There is
+ * <em>nothing</em> to do to prevent this, it is an intrinsic problem
+ * with Cardan and Euler representation (but not a problem with the
+ * rotation itself, which is perfectly well defined). For Cardan
+ * angles, singularities occur when the second angle is close to
+ * -&pi;/2 or +&pi;/2, for Euler angle singularities occur when the
+ * second angle is close to 0 or &pi;, this implies that the identity
+ * rotation is always singular for Euler angles!</p>
+
+ * @param order rotation order to use
+ * @param convention convention to use for the semantics of the angle
+ * @return an array of three angles, in the order specified by the set
+ * @exception CardanEulerSingularityException if the rotation is
+ * singular with respect to the angles set specified
+ * @since 3.6
+ */
+ public T[] getAngles(final RotationOrder order, RotationConvention convention)
+ throws CardanEulerSingularityException {
+
+ if (convention == RotationConvention.VECTOR_OPERATOR) {
+ if (order == RotationOrder.XYZ) {
+
+ // r (+K) coordinates are :
+ // sin (theta), -cos (theta) sin (phi), cos (theta) cos (phi)
+ // (-r) (+I) coordinates are :
+ // cos (psi) cos (theta), -sin (psi) cos (theta), sin (theta)
+ final // and we can choose to have theta in the interval [-PI/2 ; +PI/2]
+ FieldVector3D<T> v1 = applyTo(vector(0, 0, 1));
+ final FieldVector3D<T> v2 = applyInverseTo(vector(1, 0, 0));
+ if ((v2.getZ().getReal() < -0.9999999999) || (v2.getZ().getReal() > 0.9999999999)) {
+ throw new CardanEulerSingularityException(true);
+ }
+ return buildArray(v1.getY().negate().atan2(v1.getZ()),
+ v2.getZ().asin(),
+ v2.getY().negate().atan2(v2.getX()));
+
+ } else if (order == RotationOrder.XZY) {
+
+ // r (+J) coordinates are :
+ // -sin (psi), cos (psi) cos (phi), cos (psi) sin (phi)
+ // (-r) (+I) coordinates are :
+ // cos (theta) cos (psi), -sin (psi), sin (theta) cos (psi)
+ // and we can choose to have psi in the interval [-PI/2 ; +PI/2]
+ final FieldVector3D<T> v1 = applyTo(vector(0, 1, 0));
+ final FieldVector3D<T> v2 = applyInverseTo(vector(1, 0, 0));
+ if ((v2.getY().getReal() < -0.9999999999) || (v2.getY().getReal() > 0.9999999999)) {
+ throw new CardanEulerSingularityException(true);
+ }
+ return buildArray(v1.getZ().atan2(v1.getY()),
+ v2.getY().asin().negate(),
+ v2.getZ().atan2(v2.getX()));
+
+ } else if (order == RotationOrder.YXZ) {
+
+ // r (+K) coordinates are :
+ // cos (phi) sin (theta), -sin (phi), cos (phi) cos (theta)
+ // (-r) (+J) coordinates are :
+ // sin (psi) cos (phi), cos (psi) cos (phi), -sin (phi)
+ // and we can choose to have phi in the interval [-PI/2 ; +PI/2]
+ final FieldVector3D<T> v1 = applyTo(vector(0, 0, 1));
+ final FieldVector3D<T> v2 = applyInverseTo(vector(0, 1, 0));
+ if ((v2.getZ().getReal() < -0.9999999999) || (v2.getZ().getReal() > 0.9999999999)) {
+ throw new CardanEulerSingularityException(true);
+ }
+ return buildArray(v1.getX().atan2(v1.getZ()),
+ v2.getZ().asin().negate(),
+ v2.getX().atan2(v2.getY()));
+
+ } else if (order == RotationOrder.YZX) {
+
+ // r (+I) coordinates are :
+ // cos (psi) cos (theta), sin (psi), -cos (psi) sin (theta)
+ // (-r) (+J) coordinates are :
+ // sin (psi), cos (phi) cos (psi), -sin (phi) cos (psi)
+ // and we can choose to have psi in the interval [-PI/2 ; +PI/2]
+ final FieldVector3D<T> v1 = applyTo(vector(1, 0, 0));
+ final FieldVector3D<T> v2 = applyInverseTo(vector(0, 1, 0));
+ if ((v2.getX().getReal() < -0.9999999999) || (v2.getX().getReal() > 0.9999999999)) {
+ throw new CardanEulerSingularityException(true);
+ }
+ return buildArray(v1.getZ().negate().atan2(v1.getX()),
+ v2.getX().asin(),
+ v2.getZ().negate().atan2(v2.getY()));
+
+ } else if (order == RotationOrder.ZXY) {
+
+ // r (+J) coordinates are :
+ // -cos (phi) sin (psi), cos (phi) cos (psi), sin (phi)
+ // (-r) (+K) coordinates are :
+ // -sin (theta) cos (phi), sin (phi), cos (theta) cos (phi)
+ // and we can choose to have phi in the interval [-PI/2 ; +PI/2]
+ final FieldVector3D<T> v1 = applyTo(vector(0, 1, 0));
+ final FieldVector3D<T> v2 = applyInverseTo(vector(0, 0, 1));
+ if ((v2.getY().getReal() < -0.9999999999) || (v2.getY().getReal() > 0.9999999999)) {
+ throw new CardanEulerSingularityException(true);
+ }
+ return buildArray(v1.getX().negate().atan2(v1.getY()),
+ v2.getY().asin(),
+ v2.getX().negate().atan2(v2.getZ()));
+
+ } else if (order == RotationOrder.ZYX) {
+
+ // r (+I) coordinates are :
+ // cos (theta) cos (psi), cos (theta) sin (psi), -sin (theta)
+ // (-r) (+K) coordinates are :
+ // -sin (theta), sin (phi) cos (theta), cos (phi) cos (theta)
+ // and we can choose to have theta in the interval [-PI/2 ; +PI/2]
+ final FieldVector3D<T> v1 = applyTo(vector(1, 0, 0));
+ final FieldVector3D<T> v2 = applyInverseTo(vector(0, 0, 1));
+ if ((v2.getX().getReal() < -0.9999999999) || (v2.getX().getReal() > 0.9999999999)) {
+ throw new CardanEulerSingularityException(true);
+ }
+ return buildArray(v1.getY().atan2(v1.getX()),
+ v2.getX().asin().negate(),
+ v2.getY().atan2(v2.getZ()));
+
+ } else if (order == RotationOrder.XYX) {
+
+ // r (+I) coordinates are :
+ // cos (theta), sin (phi1) sin (theta), -cos (phi1) sin (theta)
+ // (-r) (+I) coordinates are :
+ // cos (theta), sin (theta) sin (phi2), sin (theta) cos (phi2)
+ // and we can choose to have theta in the interval [0 ; PI]
+ final FieldVector3D<T> v1 = applyTo(vector(1, 0, 0));
+ final FieldVector3D<T> v2 = applyInverseTo(vector(1, 0, 0));
+ if ((v2.getX().getReal() < -0.9999999999) || (v2.getX().getReal() > 0.9999999999)) {
+ throw new CardanEulerSingularityException(false);
+ }
+ return buildArray(v1.getY().atan2(v1.getZ().negate()),
+ v2.getX().acos(),
+ v2.getY().atan2(v2.getZ()));
+
+ } else if (order == RotationOrder.XZX) {
+
+ // r (+I) coordinates are :
+ // cos (psi), cos (phi1) sin (psi), sin (phi1) sin (psi)
+ // (-r) (+I) coordinates are :
+ // cos (psi), -sin (psi) cos (phi2), sin (psi) sin (phi2)
+ // and we can choose to have psi in the interval [0 ; PI]
+ final FieldVector3D<T> v1 = applyTo(vector(1, 0, 0));
+ final FieldVector3D<T> v2 = applyInverseTo(vector(1, 0, 0));
+ if ((v2.getX().getReal() < -0.9999999999) || (v2.getX().getReal() > 0.9999999999)) {
+ throw new CardanEulerSingularityException(false);
+ }
+ return buildArray(v1.getZ().atan2(v1.getY()),
+ v2.getX().acos(),
+ v2.getZ().atan2(v2.getY().negate()));
+
+ } else if (order == RotationOrder.YXY) {
+
+ // r (+J) coordinates are :
+ // sin (theta1) sin (phi), cos (phi), cos (theta1) sin (phi)
+ // (-r) (+J) coordinates are :
+ // sin (phi) sin (theta2), cos (phi), -sin (phi) cos (theta2)
+ // and we can choose to have phi in the interval [0 ; PI]
+ final FieldVector3D<T> v1 = applyTo(vector(0, 1, 0));
+ final FieldVector3D<T> v2 = applyInverseTo(vector(0, 1, 0));
+ if ((v2.getY().getReal() < -0.9999999999) || (v2.getY().getReal() > 0.9999999999)) {
+ throw new CardanEulerSingularityException(false);
+ }
+ return buildArray(v1.getX().atan2(v1.getZ()),
+ v2.getY().acos(),
+ v2.getX().atan2(v2.getZ().negate()));
+
+ } else if (order == RotationOrder.YZY) {
+
+ // r (+J) coordinates are :
+ // -cos (theta1) sin (psi), cos (psi), sin (theta1) sin (psi)
+ // (-r) (+J) coordinates are :
+ // sin (psi) cos (theta2), cos (psi), sin (psi) sin (theta2)
+ // and we can choose to have psi in the interval [0 ; PI]
+ final FieldVector3D<T> v1 = applyTo(vector(0, 1, 0));
+ final FieldVector3D<T> v2 = applyInverseTo(vector(0, 1, 0));
+ if ((v2.getY().getReal() < -0.9999999999) || (v2.getY().getReal() > 0.9999999999)) {
+ throw new CardanEulerSingularityException(false);
+ }
+ return buildArray(v1.getZ().atan2(v1.getX().negate()),
+ v2.getY().acos(),
+ v2.getZ().atan2(v2.getX()));
+
+ } else if (order == RotationOrder.ZXZ) {
+
+ // r (+K) coordinates are :
+ // sin (psi1) sin (phi), -cos (psi1) sin (phi), cos (phi)
+ // (-r) (+K) coordinates are :
+ // sin (phi) sin (psi2), sin (phi) cos (psi2), cos (phi)
+ // and we can choose to have phi in the interval [0 ; PI]
+ final FieldVector3D<T> v1 = applyTo(vector(0, 0, 1));
+ final FieldVector3D<T> v2 = applyInverseTo(vector(0, 0, 1));
+ if ((v2.getZ().getReal() < -0.9999999999) || (v2.getZ().getReal() > 0.9999999999)) {
+ throw new CardanEulerSingularityException(false);
+ }
+ return buildArray(v1.getX().atan2(v1.getY().negate()),
+ v2.getZ().acos(),
+ v2.getX().atan2(v2.getY()));
+
+ } else { // last possibility is ZYZ
+
+ // r (+K) coordinates are :
+ // cos (psi1) sin (theta), sin (psi1) sin (theta), cos (theta)
+ // (-r) (+K) coordinates are :
+ // -sin (theta) cos (psi2), sin (theta) sin (psi2), cos (theta)
+ // and we can choose to have theta in the interval [0 ; PI]
+ final FieldVector3D<T> v1 = applyTo(vector(0, 0, 1));
+ final FieldVector3D<T> v2 = applyInverseTo(vector(0, 0, 1));
+ if ((v2.getZ().getReal() < -0.9999999999) || (v2.getZ().getReal() > 0.9999999999)) {
+ throw new CardanEulerSingularityException(false);
+ }
+ return buildArray(v1.getY().atan2(v1.getX()),
+ v2.getZ().acos(),
+ v2.getY().atan2(v2.getX().negate()));
+
+ }
+ } else {
+ if (order == RotationOrder.XYZ) {
+
+ // r (Vector3D.plusI) coordinates are :
+ // cos (theta) cos (psi), -cos (theta) sin (psi), sin (theta)
+ // (-r) (Vector3D.plusK) coordinates are :
+ // sin (theta), -sin (phi) cos (theta), cos (phi) cos (theta)
+ // and we can choose to have theta in the interval [-PI/2 ; +PI/2]
+ FieldVector3D<T> v1 = applyTo(Vector3D.PLUS_I);
+ FieldVector3D<T> v2 = applyInverseTo(Vector3D.PLUS_K);
+ if ((v2.getX().getReal() < -0.9999999999) || (v2.getX().getReal() > 0.9999999999)) {
+ throw new CardanEulerSingularityException(true);
+ }
+ return buildArray(v2.getY().negate().atan2(v2.getZ()),
+ v2.getX().asin(),
+ v1.getY().negate().atan2(v1.getX()));
+
+ } else if (order == RotationOrder.XZY) {
+
+ // r (Vector3D.plusI) coordinates are :
+ // cos (psi) cos (theta), -sin (psi), cos (psi) sin (theta)
+ // (-r) (Vector3D.plusJ) coordinates are :
+ // -sin (psi), cos (phi) cos (psi), sin (phi) cos (psi)
+ // and we can choose to have psi in the interval [-PI/2 ; +PI/2]
+ FieldVector3D<T> v1 = applyTo(Vector3D.PLUS_I);
+ FieldVector3D<T> v2 = applyInverseTo(Vector3D.PLUS_J);
+ if ((v2.getX().getReal() < -0.9999999999) || (v2.getX().getReal() > 0.9999999999)) {
+ throw new CardanEulerSingularityException(true);
+ }
+ return buildArray(v2.getZ().atan2(v2.getY()),
+ v2.getX().asin().negate(),
+ v1.getZ().atan2(v1.getX()));
+
+ } else if (order == RotationOrder.YXZ) {
+
+ // r (Vector3D.plusJ) coordinates are :
+ // cos (phi) sin (psi), cos (phi) cos (psi), -sin (phi)
+ // (-r) (Vector3D.plusK) coordinates are :
+ // sin (theta) cos (phi), -sin (phi), cos (theta) cos (phi)
+ // and we can choose to have phi in the interval [-PI/2 ; +PI/2]
+ FieldVector3D<T> v1 = applyTo(Vector3D.PLUS_J);
+ FieldVector3D<T> v2 = applyInverseTo(Vector3D.PLUS_K);
+ if ((v2.getY().getReal() < -0.9999999999) || (v2.getY().getReal() > 0.9999999999)) {
+ throw new CardanEulerSingularityException(true);
+ }
+ return buildArray(v2.getX().atan2(v2.getZ()),
+ v2.getY().asin().negate(),
+ v1.getX().atan2(v1.getY()));
+
+ } else if (order == RotationOrder.YZX) {
+
+ // r (Vector3D.plusJ) coordinates are :
+ // sin (psi), cos (psi) cos (phi), -cos (psi) sin (phi)
+ // (-r) (Vector3D.plusI) coordinates are :
+ // cos (theta) cos (psi), sin (psi), -sin (theta) cos (psi)
+ // and we can choose to have psi in the interval [-PI/2 ; +PI/2]
+ FieldVector3D<T> v1 = applyTo(Vector3D.PLUS_J);
+ FieldVector3D<T> v2 = applyInverseTo(Vector3D.PLUS_I);
+ if ((v2.getY().getReal() < -0.9999999999) || (v2.getY().getReal() > 0.9999999999)) {
+ throw new CardanEulerSingularityException(true);
+ }
+ return buildArray(v2.getZ().negate().atan2(v2.getX()),
+ v2.getY().asin(),
+ v1.getZ().negate().atan2(v1.getY()));
+
+ } else if (order == RotationOrder.ZXY) {
+
+ // r (Vector3D.plusK) coordinates are :
+ // -cos (phi) sin (theta), sin (phi), cos (phi) cos (theta)
+ // (-r) (Vector3D.plusJ) coordinates are :
+ // -sin (psi) cos (phi), cos (psi) cos (phi), sin (phi)
+ // and we can choose to have phi in the interval [-PI/2 ; +PI/2]
+ FieldVector3D<T> v1 = applyTo(Vector3D.PLUS_K);
+ FieldVector3D<T> v2 = applyInverseTo(Vector3D.PLUS_J);
+ if ((v2.getZ().getReal() < -0.9999999999) || (v2.getZ().getReal() > 0.9999999999)) {
+ throw new CardanEulerSingularityException(true);
+ }
+ return buildArray(v2.getX().negate().atan2(v2.getY()),
+ v2.getZ().asin(),
+ v1.getX().negate().atan2(v1.getZ()));
+
+ } else if (order == RotationOrder.ZYX) {
+
+ // r (Vector3D.plusK) coordinates are :
+ // -sin (theta), cos (theta) sin (phi), cos (theta) cos (phi)
+ // (-r) (Vector3D.plusI) coordinates are :
+ // cos (psi) cos (theta), sin (psi) cos (theta), -sin (theta)
+ // and we can choose to have theta in the interval [-PI/2 ; +PI/2]
+ FieldVector3D<T> v1 = applyTo(Vector3D.PLUS_K);
+ FieldVector3D<T> v2 = applyInverseTo(Vector3D.PLUS_I);
+ if ((v2.getZ().getReal() < -0.9999999999) || (v2.getZ().getReal() > 0.9999999999)) {
+ throw new CardanEulerSingularityException(true);
+ }
+ return buildArray(v2.getY().atan2(v2.getX()),
+ v2.getZ().asin().negate(),
+ v1.getY().atan2(v1.getZ()));
+
+ } else if (order == RotationOrder.XYX) {
+
+ // r (Vector3D.plusI) coordinates are :
+ // cos (theta), sin (phi2) sin (theta), cos (phi2) sin (theta)
+ // (-r) (Vector3D.plusI) coordinates are :
+ // cos (theta), sin (theta) sin (phi1), -sin (theta) cos (phi1)
+ // and we can choose to have theta in the interval [0 ; PI]
+ FieldVector3D<T> v1 = applyTo(Vector3D.PLUS_I);
+ FieldVector3D<T> v2 = applyInverseTo(Vector3D.PLUS_I);
+ if ((v2.getX().getReal() < -0.9999999999) || (v2.getX().getReal() > 0.9999999999)) {
+ throw new CardanEulerSingularityException(false);
+ }
+ return buildArray(v2.getY().atan2(v2.getZ().negate()),
+ v2.getX().acos(),
+ v1.getY().atan2(v1.getZ()));
+
+ } else if (order == RotationOrder.XZX) {
+
+ // r (Vector3D.plusI) coordinates are :
+ // cos (psi), -cos (phi2) sin (psi), sin (phi2) sin (psi)
+ // (-r) (Vector3D.plusI) coordinates are :
+ // cos (psi), sin (psi) cos (phi1), sin (psi) sin (phi1)
+ // and we can choose to have psi in the interval [0 ; PI]
+ FieldVector3D<T> v1 = applyTo(Vector3D.PLUS_I);
+ FieldVector3D<T> v2 = applyInverseTo(Vector3D.PLUS_I);
+ if ((v2.getX().getReal() < -0.9999999999) || (v2.getX().getReal() > 0.9999999999)) {
+ throw new CardanEulerSingularityException(false);
+ }
+ return buildArray(v2.getZ().atan2(v2.getY()),
+ v2.getX().acos(),
+ v1.getZ().atan2(v1.getY().negate()));
+
+ } else if (order == RotationOrder.YXY) {
+
+ // r (Vector3D.plusJ) coordinates are :
+ // sin (phi) sin (theta2), cos (phi), -sin (phi) cos (theta2)
+ // (-r) (Vector3D.plusJ) coordinates are :
+ // sin (theta1) sin (phi), cos (phi), cos (theta1) sin (phi)
+ // and we can choose to have phi in the interval [0 ; PI]
+ FieldVector3D<T> v1 = applyTo(Vector3D.PLUS_J);
+ FieldVector3D<T> v2 = applyInverseTo(Vector3D.PLUS_J);
+ if ((v2.getY().getReal() < -0.9999999999) || (v2.getY().getReal() > 0.9999999999)) {
+ throw new CardanEulerSingularityException(false);
+ }
+ return buildArray(v2.getX().atan2(v2.getZ()),
+ v2.getY().acos(),
+ v1.getX().atan2(v1.getZ().negate()));
+
+ } else if (order == RotationOrder.YZY) {
+
+ // r (Vector3D.plusJ) coordinates are :
+ // sin (psi) cos (theta2), cos (psi), sin (psi) sin (theta2)
+ // (-r) (Vector3D.plusJ) coordinates are :
+ // -cos (theta1) sin (psi), cos (psi), sin (theta1) sin (psi)
+ // and we can choose to have psi in the interval [0 ; PI]
+ FieldVector3D<T> v1 = applyTo(Vector3D.PLUS_J);
+ FieldVector3D<T> v2 = applyInverseTo(Vector3D.PLUS_J);
+ if ((v2.getY().getReal() < -0.9999999999) || (v2.getY().getReal() > 0.9999999999)) {
+ throw new CardanEulerSingularityException(false);
+ }
+ return buildArray(v2.getZ().atan2(v2.getX().negate()),
+ v2.getY().acos(),
+ v1.getZ().atan2(v1.getX()));
+
+ } else if (order == RotationOrder.ZXZ) {
+
+ // r (Vector3D.plusK) coordinates are :
+ // sin (phi) sin (psi2), sin (phi) cos (psi2), cos (phi)
+ // (-r) (Vector3D.plusK) coordinates are :
+ // sin (psi1) sin (phi), -cos (psi1) sin (phi), cos (phi)
+ // and we can choose to have phi in the interval [0 ; PI]
+ FieldVector3D<T> v1 = applyTo(Vector3D.PLUS_K);
+ FieldVector3D<T> v2 = applyInverseTo(Vector3D.PLUS_K);
+ if ((v2.getZ().getReal() < -0.9999999999) || (v2.getZ().getReal() > 0.9999999999)) {
+ throw new CardanEulerSingularityException(false);
+ }
+ return buildArray(v2.getX().atan2(v2.getY().negate()),
+ v2.getZ().acos(),
+ v1.getX().atan2(v1.getY()));
+
+ } else { // last possibility is ZYZ
+
+ // r (Vector3D.plusK) coordinates are :
+ // -sin (theta) cos (psi2), sin (theta) sin (psi2), cos (theta)
+ // (-r) (Vector3D.plusK) coordinates are :
+ // cos (psi1) sin (theta), sin (psi1) sin (theta), cos (theta)
+ // and we can choose to have theta in the interval [0 ; PI]
+ FieldVector3D<T> v1 = applyTo(Vector3D.PLUS_K);
+ FieldVector3D<T> v2 = applyInverseTo(Vector3D.PLUS_K);
+ if ((v2.getZ().getReal() < -0.9999999999) || (v2.getZ().getReal() > 0.9999999999)) {
+ throw new CardanEulerSingularityException(false);
+ }
+ return buildArray(v2.getY().atan2(v2.getX()),
+ v2.getZ().acos(),
+ v1.getY().atan2(v1.getX().negate()));
+
+ }
+ }
+
+ }
+
+ /** Create a dimension 3 array.
+ * @param a0 first array element
+ * @param a1 second array element
+ * @param a2 third array element
+ * @return new array
+ */
+ private T[] buildArray(final T a0, final T a1, final T a2) {
+ final T[] array = MathArrays.buildArray(a0.getField(), 3);
+ array[0] = a0;
+ array[1] = a1;
+ array[2] = a2;
+ return array;
+ }
+
+ /** Create a constant vector.
+ * @param x abscissa
+ * @param y ordinate
+ * @param z height
+ * @return a constant vector
+ */
+ private FieldVector3D<T> vector(final double x, final double y, final double z) {
+ final T zero = q0.getField().getZero();
+ return new FieldVector3D<T>(zero.add(x), zero.add(y), zero.add(z));
+ }
+
+ /** Get the 3X3 matrix corresponding to the instance
+ * @return the matrix corresponding to the instance
+ */
+ public T[][] getMatrix() {
+
+ // products
+ final T q0q0 = q0.multiply(q0);
+ final T q0q1 = q0.multiply(q1);
+ final T q0q2 = q0.multiply(q2);
+ final T q0q3 = q0.multiply(q3);
+ final T q1q1 = q1.multiply(q1);
+ final T q1q2 = q1.multiply(q2);
+ final T q1q3 = q1.multiply(q3);
+ final T q2q2 = q2.multiply(q2);
+ final T q2q3 = q2.multiply(q3);
+ final T q3q3 = q3.multiply(q3);
+
+ // create the matrix
+ final T[][] m = MathArrays.buildArray(q0.getField(), 3, 3);
+
+ m [0][0] = q0q0.add(q1q1).multiply(2).subtract(1);
+ m [1][0] = q1q2.subtract(q0q3).multiply(2);
+ m [2][0] = q1q3.add(q0q2).multiply(2);
+
+ m [0][1] = q1q2.add(q0q3).multiply(2);
+ m [1][1] = q0q0.add(q2q2).multiply(2).subtract(1);
+ m [2][1] = q2q3.subtract(q0q1).multiply(2);
+
+ m [0][2] = q1q3.subtract(q0q2).multiply(2);
+ m [1][2] = q2q3.add(q0q1).multiply(2);
+ m [2][2] = q0q0.add(q3q3).multiply(2).subtract(1);
+
+ return m;
+
+ }
+
+ /** Convert to a constant vector without derivatives.
+ * @return a constant vector
+ */
+ public Rotation toRotation() {
+ return new Rotation(q0.getReal(), q1.getReal(), q2.getReal(), q3.getReal(), false);
+ }
+
+ /** Apply the rotation to a vector.
+ * @param u vector to apply the rotation to
+ * @return a new vector which is the image of u by the rotation
+ */
+ public FieldVector3D<T> applyTo(final FieldVector3D<T> u) {
+
+ final T x = u.getX();
+ final T y = u.getY();
+ final T z = u.getZ();
+
+ final T s = q1.multiply(x).add(q2.multiply(y)).add(q3.multiply(z));
+
+ return new FieldVector3D<T>(q0.multiply(x.multiply(q0).subtract(q2.multiply(z).subtract(q3.multiply(y)))).add(s.multiply(q1)).multiply(2).subtract(x),
+ q0.multiply(y.multiply(q0).subtract(q3.multiply(x).subtract(q1.multiply(z)))).add(s.multiply(q2)).multiply(2).subtract(y),
+ q0.multiply(z.multiply(q0).subtract(q1.multiply(y).subtract(q2.multiply(x)))).add(s.multiply(q3)).multiply(2).subtract(z));
+
+ }
+
+ /** Apply the rotation to a vector.
+ * @param u vector to apply the rotation to
+ * @return a new vector which is the image of u by the rotation
+ */
+ public FieldVector3D<T> applyTo(final Vector3D u) {
+
+ final double x = u.getX();
+ final double y = u.getY();
+ final double z = u.getZ();
+
+ final T s = q1.multiply(x).add(q2.multiply(y)).add(q3.multiply(z));
+
+ return new FieldVector3D<T>(q0.multiply(q0.multiply(x).subtract(q2.multiply(z).subtract(q3.multiply(y)))).add(s.multiply(q1)).multiply(2).subtract(x),
+ q0.multiply(q0.multiply(y).subtract(q3.multiply(x).subtract(q1.multiply(z)))).add(s.multiply(q2)).multiply(2).subtract(y),
+ q0.multiply(q0.multiply(z).subtract(q1.multiply(y).subtract(q2.multiply(x)))).add(s.multiply(q3)).multiply(2).subtract(z));
+
+ }
+
+ /** Apply the rotation to a vector stored in an array.
+ * @param in an array with three items which stores vector to rotate
+ * @param out an array with three items to put result to (it can be the same
+ * array as in)
+ */
+ public void applyTo(final T[] in, final T[] out) {
+
+ final T x = in[0];
+ final T y = in[1];
+ final T z = in[2];
+
+ final T s = q1.multiply(x).add(q2.multiply(y)).add(q3.multiply(z));
+
+ out[0] = q0.multiply(x.multiply(q0).subtract(q2.multiply(z).subtract(q3.multiply(y)))).add(s.multiply(q1)).multiply(2).subtract(x);
+ out[1] = q0.multiply(y.multiply(q0).subtract(q3.multiply(x).subtract(q1.multiply(z)))).add(s.multiply(q2)).multiply(2).subtract(y);
+ out[2] = q0.multiply(z.multiply(q0).subtract(q1.multiply(y).subtract(q2.multiply(x)))).add(s.multiply(q3)).multiply(2).subtract(z);
+
+ }
+
+ /** Apply the rotation to a vector stored in an array.
+ * @param in an array with three items which stores vector to rotate
+ * @param out an array with three items to put result to
+ */
+ public void applyTo(final double[] in, final T[] out) {
+
+ final double x = in[0];
+ final double y = in[1];
+ final double z = in[2];
+
+ final T s = q1.multiply(x).add(q2.multiply(y)).add(q3.multiply(z));
+
+ out[0] = q0.multiply(q0.multiply(x).subtract(q2.multiply(z).subtract(q3.multiply(y)))).add(s.multiply(q1)).multiply(2).subtract(x);
+ out[1] = q0.multiply(q0.multiply(y).subtract(q3.multiply(x).subtract(q1.multiply(z)))).add(s.multiply(q2)).multiply(2).subtract(y);
+ out[2] = q0.multiply(q0.multiply(z).subtract(q1.multiply(y).subtract(q2.multiply(x)))).add(s.multiply(q3)).multiply(2).subtract(z);
+
+ }
+
+ /** Apply a rotation to a vector.
+ * @param r rotation to apply
+ * @param u vector to apply the rotation to
+ * @param <T> the type of the field elements
+ * @return a new vector which is the image of u by the rotation
+ */
+ public static <T extends RealFieldElement<T>> FieldVector3D<T> applyTo(final Rotation r, final FieldVector3D<T> u) {
+
+ final T x = u.getX();
+ final T y = u.getY();
+ final T z = u.getZ();
+
+ final T s = x.multiply(r.getQ1()).add(y.multiply(r.getQ2())).add(z.multiply(r.getQ3()));
+
+ return new FieldVector3D<T>(x.multiply(r.getQ0()).subtract(z.multiply(r.getQ2()).subtract(y.multiply(r.getQ3()))).multiply(r.getQ0()).add(s.multiply(r.getQ1())).multiply(2).subtract(x),
+ y.multiply(r.getQ0()).subtract(x.multiply(r.getQ3()).subtract(z.multiply(r.getQ1()))).multiply(r.getQ0()).add(s.multiply(r.getQ2())).multiply(2).subtract(y),
+ z.multiply(r.getQ0()).subtract(y.multiply(r.getQ1()).subtract(x.multiply(r.getQ2()))).multiply(r.getQ0()).add(s.multiply(r.getQ3())).multiply(2).subtract(z));
+
+ }
+
+ /** Apply the inverse of the rotation to a vector.
+ * @param u vector to apply the inverse of the rotation to
+ * @return a new vector which such that u is its image by the rotation
+ */
+ public FieldVector3D<T> applyInverseTo(final FieldVector3D<T> u) {
+
+ final T x = u.getX();
+ final T y = u.getY();
+ final T z = u.getZ();
+
+ final T s = q1.multiply(x).add(q2.multiply(y)).add(q3.multiply(z));
+ final T m0 = q0.negate();
+
+ return new FieldVector3D<T>(m0.multiply(x.multiply(m0).subtract(q2.multiply(z).subtract(q3.multiply(y)))).add(s.multiply(q1)).multiply(2).subtract(x),
+ m0.multiply(y.multiply(m0).subtract(q3.multiply(x).subtract(q1.multiply(z)))).add(s.multiply(q2)).multiply(2).subtract(y),
+ m0.multiply(z.multiply(m0).subtract(q1.multiply(y).subtract(q2.multiply(x)))).add(s.multiply(q3)).multiply(2).subtract(z));
+
+ }
+
+ /** Apply the inverse of the rotation to a vector.
+ * @param u vector to apply the inverse of the rotation to
+ * @return a new vector which such that u is its image by the rotation
+ */
+ public FieldVector3D<T> applyInverseTo(final Vector3D u) {
+
+ final double x = u.getX();
+ final double y = u.getY();
+ final double z = u.getZ();
+
+ final T s = q1.multiply(x).add(q2.multiply(y)).add(q3.multiply(z));
+ final T m0 = q0.negate();
+
+ return new FieldVector3D<T>(m0.multiply(m0.multiply(x).subtract(q2.multiply(z).subtract(q3.multiply(y)))).add(s.multiply(q1)).multiply(2).subtract(x),
+ m0.multiply(m0.multiply(y).subtract(q3.multiply(x).subtract(q1.multiply(z)))).add(s.multiply(q2)).multiply(2).subtract(y),
+ m0.multiply(m0.multiply(z).subtract(q1.multiply(y).subtract(q2.multiply(x)))).add(s.multiply(q3)).multiply(2).subtract(z));
+
+ }
+
+ /** Apply the inverse of the rotation to a vector stored in an array.
+ * @param in an array with three items which stores vector to rotate
+ * @param out an array with three items to put result to (it can be the same
+ * array as in)
+ */
+ public void applyInverseTo(final T[] in, final T[] out) {
+
+ final T x = in[0];
+ final T y = in[1];
+ final T z = in[2];
+
+ final T s = q1.multiply(x).add(q2.multiply(y)).add(q3.multiply(z));
+ final T m0 = q0.negate();
+
+ out[0] = m0.multiply(x.multiply(m0).subtract(q2.multiply(z).subtract(q3.multiply(y)))).add(s.multiply(q1)).multiply(2).subtract(x);
+ out[1] = m0.multiply(y.multiply(m0).subtract(q3.multiply(x).subtract(q1.multiply(z)))).add(s.multiply(q2)).multiply(2).subtract(y);
+ out[2] = m0.multiply(z.multiply(m0).subtract(q1.multiply(y).subtract(q2.multiply(x)))).add(s.multiply(q3)).multiply(2).subtract(z);
+
+ }
+
+ /** Apply the inverse of the rotation to a vector stored in an array.
+ * @param in an array with three items which stores vector to rotate
+ * @param out an array with three items to put result to
+ */
+ public void applyInverseTo(final double[] in, final T[] out) {
+
+ final double x = in[0];
+ final double y = in[1];
+ final double z = in[2];
+
+ final T s = q1.multiply(x).add(q2.multiply(y)).add(q3.multiply(z));
+ final T m0 = q0.negate();
+
+ out[0] = m0.multiply(m0.multiply(x).subtract(q2.multiply(z).subtract(q3.multiply(y)))).add(s.multiply(q1)).multiply(2).subtract(x);
+ out[1] = m0.multiply(m0.multiply(y).subtract(q3.multiply(x).subtract(q1.multiply(z)))).add(s.multiply(q2)).multiply(2).subtract(y);
+ out[2] = m0.multiply(m0.multiply(z).subtract(q1.multiply(y).subtract(q2.multiply(x)))).add(s.multiply(q3)).multiply(2).subtract(z);
+
+ }
+
+ /** Apply the inverse of a rotation to a vector.
+ * @param r rotation to apply
+ * @param u vector to apply the inverse of the rotation to
+ * @param <T> the type of the field elements
+ * @return a new vector which such that u is its image by the rotation
+ */
+ public static <T extends RealFieldElement<T>> FieldVector3D<T> applyInverseTo(final Rotation r, final FieldVector3D<T> u) {
+
+ final T x = u.getX();
+ final T y = u.getY();
+ final T z = u.getZ();
+
+ final T s = x.multiply(r.getQ1()).add(y.multiply(r.getQ2())).add(z.multiply(r.getQ3()));
+ final double m0 = -r.getQ0();
+
+ return new FieldVector3D<T>(x.multiply(m0).subtract(z.multiply(r.getQ2()).subtract(y.multiply(r.getQ3()))).multiply(m0).add(s.multiply(r.getQ1())).multiply(2).subtract(x),
+ y.multiply(m0).subtract(x.multiply(r.getQ3()).subtract(z.multiply(r.getQ1()))).multiply(m0).add(s.multiply(r.getQ2())).multiply(2).subtract(y),
+ z.multiply(m0).subtract(y.multiply(r.getQ1()).subtract(x.multiply(r.getQ2()))).multiply(m0).add(s.multiply(r.getQ3())).multiply(2).subtract(z));
+
+ }
+
+ /** Apply the instance to another rotation.
+ * <p>
+ * Calling this method is equivalent to call
+ * {@link #compose(FieldRotation, RotationConvention)
+ * compose(r, RotationConvention.VECTOR_OPERATOR)}.
+ * </p>
+ * @param r rotation to apply the rotation to
+ * @return a new rotation which is the composition of r by the instance
+ */
+ public FieldRotation<T> applyTo(final FieldRotation<T> r) {
+ return compose(r, RotationConvention.VECTOR_OPERATOR);
+ }
+
+ /** Compose the instance with another rotation.
+ * <p>
+ * If the semantics of the rotations composition corresponds to a
+ * {@link RotationConvention#VECTOR_OPERATOR vector operator} convention,
+ * applying the instance to a rotation is computing the composition
+ * in an order compliant with the following rule : let {@code u} be any
+ * vector and {@code v} its image by {@code r1} (i.e.
+ * {@code r1.applyTo(u) = v}). Let {@code w} be the image of {@code v} by
+ * rotation {@code r2} (i.e. {@code r2.applyTo(v) = w}). Then
+ * {@code w = comp.applyTo(u)}, where
+ * {@code comp = r2.compose(r1, RotationConvention.VECTOR_OPERATOR)}.
+ * </p>
+ * <p>
+ * If the semantics of the rotations composition corresponds to a
+ * {@link RotationConvention#FRAME_TRANSFORM frame transform} convention,
+ * the application order will be reversed. So keeping the exact same
+ * meaning of all {@code r1}, {@code r2}, {@code u}, {@code v}, {@code w}
+ * and {@code comp} as above, {@code comp} could also be computed as
+ * {@code comp = r1.compose(r2, RotationConvention.FRAME_TRANSFORM)}.
+ * </p>
+ * @param r rotation to apply the rotation to
+ * @param convention convention to use for the semantics of the angle
+ * @return a new rotation which is the composition of r by the instance
+ */
+ public FieldRotation<T> compose(final FieldRotation<T> r, final RotationConvention convention) {
+ return convention == RotationConvention.VECTOR_OPERATOR ?
+ composeInternal(r) : r.composeInternal(this);
+ }
+
+ /** Compose the instance with another rotation using vector operator convention.
+ * @param r rotation to apply the rotation to
+ * @return a new rotation which is the composition of r by the instance
+ * using vector operator convention
+ */
+ private FieldRotation<T> composeInternal(final FieldRotation<T> r) {
+ return new FieldRotation<T>(r.q0.multiply(q0).subtract(r.q1.multiply(q1).add(r.q2.multiply(q2)).add(r.q3.multiply(q3))),
+ r.q1.multiply(q0).add(r.q0.multiply(q1)).add(r.q2.multiply(q3).subtract(r.q3.multiply(q2))),
+ r.q2.multiply(q0).add(r.q0.multiply(q2)).add(r.q3.multiply(q1).subtract(r.q1.multiply(q3))),
+ r.q3.multiply(q0).add(r.q0.multiply(q3)).add(r.q1.multiply(q2).subtract(r.q2.multiply(q1))),
+ false);
+ }
+
+ /** Apply the instance to another rotation.
+ * <p>
+ * Calling this method is equivalent to call
+ * {@link #compose(Rotation, RotationConvention)
+ * compose(r, RotationConvention.VECTOR_OPERATOR)}.
+ * </p>
+ * @param r rotation to apply the rotation to
+ * @return a new rotation which is the composition of r by the instance
+ */
+ public FieldRotation<T> applyTo(final Rotation r) {
+ return compose(r, RotationConvention.VECTOR_OPERATOR);
+ }
+
+ /** Compose the instance with another rotation.
+ * <p>
+ * If the semantics of the rotations composition corresponds to a
+ * {@link RotationConvention#VECTOR_OPERATOR vector operator} convention,
+ * applying the instance to a rotation is computing the composition
+ * in an order compliant with the following rule : let {@code u} be any
+ * vector and {@code v} its image by {@code r1} (i.e.
+ * {@code r1.applyTo(u) = v}). Let {@code w} be the image of {@code v} by
+ * rotation {@code r2} (i.e. {@code r2.applyTo(v) = w}). Then
+ * {@code w = comp.applyTo(u)}, where
+ * {@code comp = r2.compose(r1, RotationConvention.VECTOR_OPERATOR)}.
+ * </p>
+ * <p>
+ * If the semantics of the rotations composition corresponds to a
+ * {@link RotationConvention#FRAME_TRANSFORM frame transform} convention,
+ * the application order will be reversed. So keeping the exact same
+ * meaning of all {@code r1}, {@code r2}, {@code u}, {@code v}, {@code w}
+ * and {@code comp} as above, {@code comp} could also be computed as
+ * {@code comp = r1.compose(r2, RotationConvention.FRAME_TRANSFORM)}.
+ * </p>
+ * @param r rotation to apply the rotation to
+ * @param convention convention to use for the semantics of the angle
+ * @return a new rotation which is the composition of r by the instance
+ */
+ public FieldRotation<T> compose(final Rotation r, final RotationConvention convention) {
+ return convention == RotationConvention.VECTOR_OPERATOR ?
+ composeInternal(r) : applyTo(r, this);
+ }
+
+ /** Compose the instance with another rotation using vector operator convention.
+ * @param r rotation to apply the rotation to
+ * @return a new rotation which is the composition of r by the instance
+ * using vector operator convention
+ */
+ private FieldRotation<T> composeInternal(final Rotation r) {
+ return new FieldRotation<T>(q0.multiply(r.getQ0()).subtract(q1.multiply(r.getQ1()).add(q2.multiply(r.getQ2())).add(q3.multiply(r.getQ3()))),
+ q0.multiply(r.getQ1()).add(q1.multiply(r.getQ0())).add(q3.multiply(r.getQ2()).subtract(q2.multiply(r.getQ3()))),
+ q0.multiply(r.getQ2()).add(q2.multiply(r.getQ0())).add(q1.multiply(r.getQ3()).subtract(q3.multiply(r.getQ1()))),
+ q0.multiply(r.getQ3()).add(q3.multiply(r.getQ0())).add(q2.multiply(r.getQ1()).subtract(q1.multiply(r.getQ2()))),
+ false);
+ }
+
+ /** Apply a rotation to another rotation.
+ * Applying a rotation to another rotation is computing the composition
+ * in an order compliant with the following rule : let u be any
+ * vector and v its image by rInner (i.e. rInner.applyTo(u) = v), let w be the image
+ * of v by rOuter (i.e. rOuter.applyTo(v) = w), then w = comp.applyTo(u),
+ * where comp = applyTo(rOuter, rInner).
+ * @param r1 rotation to apply
+ * @param rInner rotation to apply the rotation to
+ * @param <T> the type of the field elements
+ * @return a new rotation which is the composition of r by the instance
+ */
+ public static <T extends RealFieldElement<T>> FieldRotation<T> applyTo(final Rotation r1, final FieldRotation<T> rInner) {
+ return new FieldRotation<T>(rInner.q0.multiply(r1.getQ0()).subtract(rInner.q1.multiply(r1.getQ1()).add(rInner.q2.multiply(r1.getQ2())).add(rInner.q3.multiply(r1.getQ3()))),
+ rInner.q1.multiply(r1.getQ0()).add(rInner.q0.multiply(r1.getQ1())).add(rInner.q2.multiply(r1.getQ3()).subtract(rInner.q3.multiply(r1.getQ2()))),
+ rInner.q2.multiply(r1.getQ0()).add(rInner.q0.multiply(r1.getQ2())).add(rInner.q3.multiply(r1.getQ1()).subtract(rInner.q1.multiply(r1.getQ3()))),
+ rInner.q3.multiply(r1.getQ0()).add(rInner.q0.multiply(r1.getQ3())).add(rInner.q1.multiply(r1.getQ2()).subtract(rInner.q2.multiply(r1.getQ1()))),
+ false);
+ }
+
+ /** Apply the inverse of the instance to another rotation.
+ * <p>
+ * Calling this method is equivalent to call
+ * {@link #composeInverse(FieldRotation, RotationConvention)
+ * composeInverse(r, RotationConvention.VECTOR_OPERATOR)}.
+ * </p>
+ * @param r rotation to apply the rotation to
+ * @return a new rotation which is the composition of r by the inverse
+ * of the instance
+ */
+ public FieldRotation<T> applyInverseTo(final FieldRotation<T> r) {
+ return composeInverse(r, RotationConvention.VECTOR_OPERATOR);
+ }
+
+ /** Compose the inverse of the instance with another rotation.
+ * <p>
+ * If the semantics of the rotations composition corresponds to a
+ * {@link RotationConvention#VECTOR_OPERATOR vector operator} convention,
+ * applying the inverse of the instance to a rotation is computing
+ * the composition in an order compliant with the following rule :
+ * let {@code u} be any vector and {@code v} its image by {@code r1}
+ * (i.e. {@code r1.applyTo(u) = v}). Let {@code w} be the inverse image
+ * of {@code v} by {@code r2} (i.e. {@code r2.applyInverseTo(v) = w}).
+ * Then {@code w = comp.applyTo(u)}, where
+ * {@code comp = r2.composeInverse(r1)}.
+ * </p>
+ * <p>
+ * If the semantics of the rotations composition corresponds to a
+ * {@link RotationConvention#FRAME_TRANSFORM frame transform} convention,
+ * the application order will be reversed, which means it is the
+ * <em>innermost</em> rotation that will be reversed. So keeping the exact same
+ * meaning of all {@code r1}, {@code r2}, {@code u}, {@code v}, {@code w}
+ * and {@code comp} as above, {@code comp} could also be computed as
+ * {@code comp = r1.revert().composeInverse(r2.revert(), RotationConvention.FRAME_TRANSFORM)}.
+ * </p>
+ * @param r rotation to apply the rotation to
+ * @param convention convention to use for the semantics of the angle
+ * @return a new rotation which is the composition of r by the inverse
+ * of the instance
+ */
+ public FieldRotation<T> composeInverse(final FieldRotation<T> r, final RotationConvention convention) {
+ return convention == RotationConvention.VECTOR_OPERATOR ?
+ composeInverseInternal(r) : r.composeInternal(revert());
+ }
+
+ /** Compose the inverse of the instance with another rotation
+ * using vector operator convention.
+ * @param r rotation to apply the rotation to
+ * @return a new rotation which is the composition of r by the inverse
+ * of the instance using vector operator convention
+ */
+ private FieldRotation<T> composeInverseInternal(FieldRotation<T> r) {
+ return new FieldRotation<T>(r.q0.multiply(q0).add(r.q1.multiply(q1).add(r.q2.multiply(q2)).add(r.q3.multiply(q3))).negate(),
+ r.q0.multiply(q1).add(r.q2.multiply(q3).subtract(r.q3.multiply(q2))).subtract(r.q1.multiply(q0)),
+ r.q0.multiply(q2).add(r.q3.multiply(q1).subtract(r.q1.multiply(q3))).subtract(r.q2.multiply(q0)),
+ r.q0.multiply(q3).add(r.q1.multiply(q2).subtract(r.q2.multiply(q1))).subtract(r.q3.multiply(q0)),
+ false);
+ }
+
+ /** Apply the inverse of the instance to another rotation.
+ * <p>
+ * Calling this method is equivalent to call
+ * {@link #composeInverse(Rotation, RotationConvention)
+ * composeInverse(r, RotationConvention.VECTOR_OPERATOR)}.
+ * </p>
+ * @param r rotation to apply the rotation to
+ * @return a new rotation which is the composition of r by the inverse
+ * of the instance
+ */
+ public FieldRotation<T> applyInverseTo(final Rotation r) {
+ return composeInverse(r, RotationConvention.VECTOR_OPERATOR);
+ }
+
+ /** Compose the inverse of the instance with another rotation.
+ * <p>
+ * If the semantics of the rotations composition corresponds to a
+ * {@link RotationConvention#VECTOR_OPERATOR vector operator} convention,
+ * applying the inverse of the instance to a rotation is computing
+ * the composition in an order compliant with the following rule :
+ * let {@code u} be any vector and {@code v} its image by {@code r1}
+ * (i.e. {@code r1.applyTo(u) = v}). Let {@code w} be the inverse image
+ * of {@code v} by {@code r2} (i.e. {@code r2.applyInverseTo(v) = w}).
+ * Then {@code w = comp.applyTo(u)}, where
+ * {@code comp = r2.composeInverse(r1)}.
+ * </p>
+ * <p>
+ * If the semantics of the rotations composition corresponds to a
+ * {@link RotationConvention#FRAME_TRANSFORM frame transform} convention,
+ * the application order will be reversed, which means it is the
+ * <em>innermost</em> rotation that will be reversed. So keeping the exact same
+ * meaning of all {@code r1}, {@code r2}, {@code u}, {@code v}, {@code w}
+ * and {@code comp} as above, {@code comp} could also be computed as
+ * {@code comp = r1.revert().composeInverse(r2.revert(), RotationConvention.FRAME_TRANSFORM)}.
+ * </p>
+ * @param r rotation to apply the rotation to
+ * @param convention convention to use for the semantics of the angle
+ * @return a new rotation which is the composition of r by the inverse
+ * of the instance
+ */
+ public FieldRotation<T> composeInverse(final Rotation r, final RotationConvention convention) {
+ return convention == RotationConvention.VECTOR_OPERATOR ?
+ composeInverseInternal(r) : applyTo(r, revert());
+ }
+
+ /** Compose the inverse of the instance with another rotation
+ * using vector operator convention.
+ * @param r rotation to apply the rotation to
+ * @return a new rotation which is the composition of r by the inverse
+ * of the instance using vector operator convention
+ */
+ private FieldRotation<T> composeInverseInternal(Rotation r) {
+ return new FieldRotation<T>(q0.multiply(r.getQ0()).add(q1.multiply(r.getQ1()).add(q2.multiply(r.getQ2())).add(q3.multiply(r.getQ3()))).negate(),
+ q1.multiply(r.getQ0()).add(q3.multiply(r.getQ2()).subtract(q2.multiply(r.getQ3()))).subtract(q0.multiply(r.getQ1())),
+ q2.multiply(r.getQ0()).add(q1.multiply(r.getQ3()).subtract(q3.multiply(r.getQ1()))).subtract(q0.multiply(r.getQ2())),
+ q3.multiply(r.getQ0()).add(q2.multiply(r.getQ1()).subtract(q1.multiply(r.getQ2()))).subtract(q0.multiply(r.getQ3())),
+ false);
+ }
+
+ /** Apply the inverse of a rotation to another rotation.
+ * Applying the inverse of a rotation to another rotation is computing
+ * the composition in an order compliant with the following rule :
+ * let u be any vector and v its image by rInner (i.e. rInner.applyTo(u) = v),
+ * let w be the inverse image of v by rOuter
+ * (i.e. rOuter.applyInverseTo(v) = w), then w = comp.applyTo(u), where
+ * comp = applyInverseTo(rOuter, rInner).
+ * @param rOuter rotation to apply the rotation to
+ * @param rInner rotation to apply the rotation to
+ * @param <T> the type of the field elements
+ * @return a new rotation which is the composition of r by the inverse
+ * of the instance
+ */
+ public static <T extends RealFieldElement<T>> FieldRotation<T> applyInverseTo(final Rotation rOuter, final FieldRotation<T> rInner) {
+ return new FieldRotation<T>(rInner.q0.multiply(rOuter.getQ0()).add(rInner.q1.multiply(rOuter.getQ1()).add(rInner.q2.multiply(rOuter.getQ2())).add(rInner.q3.multiply(rOuter.getQ3()))).negate(),
+ rInner.q0.multiply(rOuter.getQ1()).add(rInner.q2.multiply(rOuter.getQ3()).subtract(rInner.q3.multiply(rOuter.getQ2()))).subtract(rInner.q1.multiply(rOuter.getQ0())),
+ rInner.q0.multiply(rOuter.getQ2()).add(rInner.q3.multiply(rOuter.getQ1()).subtract(rInner.q1.multiply(rOuter.getQ3()))).subtract(rInner.q2.multiply(rOuter.getQ0())),
+ rInner.q0.multiply(rOuter.getQ3()).add(rInner.q1.multiply(rOuter.getQ2()).subtract(rInner.q2.multiply(rOuter.getQ1()))).subtract(rInner.q3.multiply(rOuter.getQ0())),
+ false);
+ }
+
+ /** Perfect orthogonality on a 3X3 matrix.
+ * @param m initial matrix (not exactly orthogonal)
+ * @param threshold convergence threshold for the iterative
+ * orthogonality correction (convergence is reached when the
+ * difference between two steps of the Frobenius norm of the
+ * correction is below this threshold)
+ * @return an orthogonal matrix close to m
+ * @exception NotARotationMatrixException if the matrix cannot be
+ * orthogonalized with the given threshold after 10 iterations
+ */
+ private T[][] orthogonalizeMatrix(final T[][] m, final double threshold)
+ throws NotARotationMatrixException {
+
+ T x00 = m[0][0];
+ T x01 = m[0][1];
+ T x02 = m[0][2];
+ T x10 = m[1][0];
+ T x11 = m[1][1];
+ T x12 = m[1][2];
+ T x20 = m[2][0];
+ T x21 = m[2][1];
+ T x22 = m[2][2];
+ double fn = 0;
+ double fn1;
+
+ final T[][] o = MathArrays.buildArray(m[0][0].getField(), 3, 3);
+
+ // iterative correction: Xn+1 = Xn - 0.5 * (Xn.Mt.Xn - M)
+ int i = 0;
+ while (++i < 11) {
+
+ // Mt.Xn
+ final T mx00 = m[0][0].multiply(x00).add(m[1][0].multiply(x10)).add(m[2][0].multiply(x20));
+ final T mx10 = m[0][1].multiply(x00).add(m[1][1].multiply(x10)).add(m[2][1].multiply(x20));
+ final T mx20 = m[0][2].multiply(x00).add(m[1][2].multiply(x10)).add(m[2][2].multiply(x20));
+ final T mx01 = m[0][0].multiply(x01).add(m[1][0].multiply(x11)).add(m[2][0].multiply(x21));
+ final T mx11 = m[0][1].multiply(x01).add(m[1][1].multiply(x11)).add(m[2][1].multiply(x21));
+ final T mx21 = m[0][2].multiply(x01).add(m[1][2].multiply(x11)).add(m[2][2].multiply(x21));
+ final T mx02 = m[0][0].multiply(x02).add(m[1][0].multiply(x12)).add(m[2][0].multiply(x22));
+ final T mx12 = m[0][1].multiply(x02).add(m[1][1].multiply(x12)).add(m[2][1].multiply(x22));
+ final T mx22 = m[0][2].multiply(x02).add(m[1][2].multiply(x12)).add(m[2][2].multiply(x22));
+
+ // Xn+1
+ o[0][0] = x00.subtract(x00.multiply(mx00).add(x01.multiply(mx10)).add(x02.multiply(mx20)).subtract(m[0][0]).multiply(0.5));
+ o[0][1] = x01.subtract(x00.multiply(mx01).add(x01.multiply(mx11)).add(x02.multiply(mx21)).subtract(m[0][1]).multiply(0.5));
+ o[0][2] = x02.subtract(x00.multiply(mx02).add(x01.multiply(mx12)).add(x02.multiply(mx22)).subtract(m[0][2]).multiply(0.5));
+ o[1][0] = x10.subtract(x10.multiply(mx00).add(x11.multiply(mx10)).add(x12.multiply(mx20)).subtract(m[1][0]).multiply(0.5));
+ o[1][1] = x11.subtract(x10.multiply(mx01).add(x11.multiply(mx11)).add(x12.multiply(mx21)).subtract(m[1][1]).multiply(0.5));
+ o[1][2] = x12.subtract(x10.multiply(mx02).add(x11.multiply(mx12)).add(x12.multiply(mx22)).subtract(m[1][2]).multiply(0.5));
+ o[2][0] = x20.subtract(x20.multiply(mx00).add(x21.multiply(mx10)).add(x22.multiply(mx20)).subtract(m[2][0]).multiply(0.5));
+ o[2][1] = x21.subtract(x20.multiply(mx01).add(x21.multiply(mx11)).add(x22.multiply(mx21)).subtract(m[2][1]).multiply(0.5));
+ o[2][2] = x22.subtract(x20.multiply(mx02).add(x21.multiply(mx12)).add(x22.multiply(mx22)).subtract(m[2][2]).multiply(0.5));
+
+ // correction on each elements
+ final double corr00 = o[0][0].getReal() - m[0][0].getReal();
+ final double corr01 = o[0][1].getReal() - m[0][1].getReal();
+ final double corr02 = o[0][2].getReal() - m[0][2].getReal();
+ final double corr10 = o[1][0].getReal() - m[1][0].getReal();
+ final double corr11 = o[1][1].getReal() - m[1][1].getReal();
+ final double corr12 = o[1][2].getReal() - m[1][2].getReal();
+ final double corr20 = o[2][0].getReal() - m[2][0].getReal();
+ final double corr21 = o[2][1].getReal() - m[2][1].getReal();
+ final double corr22 = o[2][2].getReal() - m[2][2].getReal();
+
+ // Frobenius norm of the correction
+ fn1 = corr00 * corr00 + corr01 * corr01 + corr02 * corr02 +
+ corr10 * corr10 + corr11 * corr11 + corr12 * corr12 +
+ corr20 * corr20 + corr21 * corr21 + corr22 * corr22;
+
+ // convergence test
+ if (FastMath.abs(fn1 - fn) <= threshold) {
+ return o;
+ }
+
+ // prepare next iteration
+ x00 = o[0][0];
+ x01 = o[0][1];
+ x02 = o[0][2];
+ x10 = o[1][0];
+ x11 = o[1][1];
+ x12 = o[1][2];
+ x20 = o[2][0];
+ x21 = o[2][1];
+ x22 = o[2][2];
+ fn = fn1;
+
+ }
+
+ // the algorithm did not converge after 10 iterations
+ throw new NotARotationMatrixException(LocalizedFormats.UNABLE_TO_ORTHOGONOLIZE_MATRIX,
+ i - 1);
+
+ }
+
+ /** Compute the <i>distance</i> between two rotations.
+ * <p>The <i>distance</i> is intended here as a way to check if two
+ * rotations are almost similar (i.e. they transform vectors the same way)
+ * or very different. It is mathematically defined as the angle of
+ * the rotation r that prepended to one of the rotations gives the other
+ * one:</p>
+ * <pre>
+ * r<sub>1</sub>(r) = r<sub>2</sub>
+ * </pre>
+ * <p>This distance is an angle between 0 and &pi;. Its value is the smallest
+ * possible upper bound of the angle in radians between r<sub>1</sub>(v)
+ * and r<sub>2</sub>(v) for all possible vectors v. This upper bound is
+ * reached for some v. The distance is equal to 0 if and only if the two
+ * rotations are identical.</p>
+ * <p>Comparing two rotations should always be done using this value rather
+ * than for example comparing the components of the quaternions. It is much
+ * more stable, and has a geometric meaning. Also comparing quaternions
+ * components is error prone since for example quaternions (0.36, 0.48, -0.48, -0.64)
+ * and (-0.36, -0.48, 0.48, 0.64) represent exactly the same rotation despite
+ * their components are different (they are exact opposites).</p>
+ * @param r1 first rotation
+ * @param r2 second rotation
+ * @param <T> the type of the field elements
+ * @return <i>distance</i> between r1 and r2
+ */
+ public static <T extends RealFieldElement<T>> T distance(final FieldRotation<T> r1, final FieldRotation<T> r2) {
+ return r1.composeInverseInternal(r2).getAngle();
+ }
+
+}
diff --git a/src/main/java/org/apache/commons/math3/geometry/euclidean/threed/FieldVector3D.java b/src/main/java/org/apache/commons/math3/geometry/euclidean/threed/FieldVector3D.java
new file mode 100644
index 0000000..0bd04e5
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/geometry/euclidean/threed/FieldVector3D.java
@@ -0,0 +1,1185 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math3.geometry.euclidean.threed;
+
+import java.io.Serializable;
+import java.text.NumberFormat;
+
+import org.apache.commons.math3.RealFieldElement;
+import org.apache.commons.math3.exception.DimensionMismatchException;
+import org.apache.commons.math3.exception.MathArithmeticException;
+import org.apache.commons.math3.exception.util.LocalizedFormats;
+import org.apache.commons.math3.util.FastMath;
+import org.apache.commons.math3.util.MathArrays;
+
+/**
+ * This class is a re-implementation of {@link Vector3D} using {@link RealFieldElement}.
+ * <p>Instance of this class are guaranteed to be immutable.</p>
+ * @param <T> the type of the field elements
+ * @since 3.2
+ */
+public class FieldVector3D<T extends RealFieldElement<T>> implements Serializable {
+
+ /** Serializable version identifier. */
+ private static final long serialVersionUID = 20130224L;
+
+ /** Abscissa. */
+ private final T x;
+
+ /** Ordinate. */
+ private final T y;
+
+ /** Height. */
+ private final T z;
+
+ /** Simple constructor.
+ * Build a vector from its coordinates
+ * @param x abscissa
+ * @param y ordinate
+ * @param z height
+ * @see #getX()
+ * @see #getY()
+ * @see #getZ()
+ */
+ public FieldVector3D(final T x, final T y, final T z) {
+ this.x = x;
+ this.y = y;
+ this.z = z;
+ }
+
+ /** Simple constructor.
+ * Build a vector from its coordinates
+ * @param v coordinates array
+ * @exception DimensionMismatchException if array does not have 3 elements
+ * @see #toArray()
+ */
+ public FieldVector3D(final T[] v) throws DimensionMismatchException {
+ if (v.length != 3) {
+ throw new DimensionMismatchException(v.length, 3);
+ }
+ this.x = v[0];
+ this.y = v[1];
+ this.z = v[2];
+ }
+
+ /** Simple constructor.
+ * Build a vector from its azimuthal coordinates
+ * @param alpha azimuth (&alpha;) around Z
+ * (0 is +X, &pi;/2 is +Y, &pi; is -X and 3&pi;/2 is -Y)
+ * @param delta elevation (&delta;) above (XY) plane, from -&pi;/2 to +&pi;/2
+ * @see #getAlpha()
+ * @see #getDelta()
+ */
+ public FieldVector3D(final T alpha, final T delta) {
+ T cosDelta = delta.cos();
+ this.x = alpha.cos().multiply(cosDelta);
+ this.y = alpha.sin().multiply(cosDelta);
+ this.z = delta.sin();
+ }
+
+ /** Multiplicative constructor
+ * Build a vector from another one and a scale factor.
+ * The vector built will be a * u
+ * @param a scale factor
+ * @param u base (unscaled) vector
+ */
+ public FieldVector3D(final T a, final FieldVector3D<T>u) {
+ this.x = a.multiply(u.x);
+ this.y = a.multiply(u.y);
+ this.z = a.multiply(u.z);
+ }
+
+ /** Multiplicative constructor
+ * Build a vector from another one and a scale factor.
+ * The vector built will be a * u
+ * @param a scale factor
+ * @param u base (unscaled) vector
+ */
+ public FieldVector3D(final T a, final Vector3D u) {
+ this.x = a.multiply(u.getX());
+ this.y = a.multiply(u.getY());
+ this.z = a.multiply(u.getZ());
+ }
+
+ /** Multiplicative constructor
+ * Build a vector from another one and a scale factor.
+ * The vector built will be a * u
+ * @param a scale factor
+ * @param u base (unscaled) vector
+ */
+ public FieldVector3D(final double a, final FieldVector3D<T> u) {
+ this.x = u.x.multiply(a);
+ this.y = u.y.multiply(a);
+ this.z = u.z.multiply(a);
+ }
+
+ /** Linear constructor
+ * Build a vector from two other ones and corresponding scale factors.
+ * The vector built will be a1 * u1 + a2 * u2
+ * @param a1 first scale factor
+ * @param u1 first base (unscaled) vector
+ * @param a2 second scale factor
+ * @param u2 second base (unscaled) vector
+ */
+ public FieldVector3D(final T a1, final FieldVector3D<T> u1,
+ final T a2, final FieldVector3D<T> u2) {
+ final T prototype = a1;
+ this.x = prototype.linearCombination(a1, u1.getX(), a2, u2.getX());
+ this.y = prototype.linearCombination(a1, u1.getY(), a2, u2.getY());
+ this.z = prototype.linearCombination(a1, u1.getZ(), a2, u2.getZ());
+ }
+
+ /** Linear constructor
+ * Build a vector from two other ones and corresponding scale factors.
+ * The vector built will be a1 * u1 + a2 * u2
+ * @param a1 first scale factor
+ * @param u1 first base (unscaled) vector
+ * @param a2 second scale factor
+ * @param u2 second base (unscaled) vector
+ */
+ public FieldVector3D(final T a1, final Vector3D u1,
+ final T a2, final Vector3D u2) {
+ final T prototype = a1;
+ this.x = prototype.linearCombination(u1.getX(), a1, u2.getX(), a2);
+ this.y = prototype.linearCombination(u1.getY(), a1, u2.getY(), a2);
+ this.z = prototype.linearCombination(u1.getZ(), a1, u2.getZ(), a2);
+ }
+
+ /** Linear constructor
+ * Build a vector from two other ones and corresponding scale factors.
+ * The vector built will be a1 * u1 + a2 * u2
+ * @param a1 first scale factor
+ * @param u1 first base (unscaled) vector
+ * @param a2 second scale factor
+ * @param u2 second base (unscaled) vector
+ */
+ public FieldVector3D(final double a1, final FieldVector3D<T> u1,
+ final double a2, final FieldVector3D<T> u2) {
+ final T prototype = u1.getX();
+ this.x = prototype.linearCombination(a1, u1.getX(), a2, u2.getX());
+ this.y = prototype.linearCombination(a1, u1.getY(), a2, u2.getY());
+ this.z = prototype.linearCombination(a1, u1.getZ(), a2, u2.getZ());
+ }
+
+ /** Linear constructor
+ * Build a vector from three other ones and corresponding scale factors.
+ * The vector built will be a1 * u1 + a2 * u2 + a3 * u3
+ * @param a1 first scale factor
+ * @param u1 first base (unscaled) vector
+ * @param a2 second scale factor
+ * @param u2 second base (unscaled) vector
+ * @param a3 third scale factor
+ * @param u3 third base (unscaled) vector
+ */
+ public FieldVector3D(final T a1, final FieldVector3D<T> u1,
+ final T a2, final FieldVector3D<T> u2,
+ final T a3, final FieldVector3D<T> u3) {
+ final T prototype = a1;
+ this.x = prototype.linearCombination(a1, u1.getX(), a2, u2.getX(), a3, u3.getX());
+ this.y = prototype.linearCombination(a1, u1.getY(), a2, u2.getY(), a3, u3.getY());
+ this.z = prototype.linearCombination(a1, u1.getZ(), a2, u2.getZ(), a3, u3.getZ());
+ }
+
+ /** Linear constructor
+ * Build a vector from three other ones and corresponding scale factors.
+ * The vector built will be a1 * u1 + a2 * u2 + a3 * u3
+ * @param a1 first scale factor
+ * @param u1 first base (unscaled) vector
+ * @param a2 second scale factor
+ * @param u2 second base (unscaled) vector
+ * @param a3 third scale factor
+ * @param u3 third base (unscaled) vector
+ */
+ public FieldVector3D(final T a1, final Vector3D u1,
+ final T a2, final Vector3D u2,
+ final T a3, final Vector3D u3) {
+ final T prototype = a1;
+ this.x = prototype.linearCombination(u1.getX(), a1, u2.getX(), a2, u3.getX(), a3);
+ this.y = prototype.linearCombination(u1.getY(), a1, u2.getY(), a2, u3.getY(), a3);
+ this.z = prototype.linearCombination(u1.getZ(), a1, u2.getZ(), a2, u3.getZ(), a3);
+ }
+
+ /** Linear constructor
+ * Build a vector from three other ones and corresponding scale factors.
+ * The vector built will be a1 * u1 + a2 * u2 + a3 * u3
+ * @param a1 first scale factor
+ * @param u1 first base (unscaled) vector
+ * @param a2 second scale factor
+ * @param u2 second base (unscaled) vector
+ * @param a3 third scale factor
+ * @param u3 third base (unscaled) vector
+ */
+ public FieldVector3D(final double a1, final FieldVector3D<T> u1,
+ final double a2, final FieldVector3D<T> u2,
+ final double a3, final FieldVector3D<T> u3) {
+ final T prototype = u1.getX();
+ this.x = prototype.linearCombination(a1, u1.getX(), a2, u2.getX(), a3, u3.getX());
+ this.y = prototype.linearCombination(a1, u1.getY(), a2, u2.getY(), a3, u3.getY());
+ this.z = prototype.linearCombination(a1, u1.getZ(), a2, u2.getZ(), a3, u3.getZ());
+ }
+
+ /** Linear constructor
+ * Build a vector from four other ones and corresponding scale factors.
+ * The vector built will be a1 * u1 + a2 * u2 + a3 * u3 + a4 * u4
+ * @param a1 first scale factor
+ * @param u1 first base (unscaled) vector
+ * @param a2 second scale factor
+ * @param u2 second base (unscaled) vector
+ * @param a3 third scale factor
+ * @param u3 third base (unscaled) vector
+ * @param a4 fourth scale factor
+ * @param u4 fourth base (unscaled) vector
+ */
+ public FieldVector3D(final T a1, final FieldVector3D<T> u1,
+ final T a2, final FieldVector3D<T> u2,
+ final T a3, final FieldVector3D<T> u3,
+ final T a4, final FieldVector3D<T> u4) {
+ final T prototype = a1;
+ this.x = prototype.linearCombination(a1, u1.getX(), a2, u2.getX(), a3, u3.getX(), a4, u4.getX());
+ this.y = prototype.linearCombination(a1, u1.getY(), a2, u2.getY(), a3, u3.getY(), a4, u4.getY());
+ this.z = prototype.linearCombination(a1, u1.getZ(), a2, u2.getZ(), a3, u3.getZ(), a4, u4.getZ());
+ }
+
+ /** Linear constructor
+ * Build a vector from four other ones and corresponding scale factors.
+ * The vector built will be a1 * u1 + a2 * u2 + a3 * u3 + a4 * u4
+ * @param a1 first scale factor
+ * @param u1 first base (unscaled) vector
+ * @param a2 second scale factor
+ * @param u2 second base (unscaled) vector
+ * @param a3 third scale factor
+ * @param u3 third base (unscaled) vector
+ * @param a4 fourth scale factor
+ * @param u4 fourth base (unscaled) vector
+ */
+ public FieldVector3D(final T a1, final Vector3D u1,
+ final T a2, final Vector3D u2,
+ final T a3, final Vector3D u3,
+ final T a4, final Vector3D u4) {
+ final T prototype = a1;
+ this.x = prototype.linearCombination(u1.getX(), a1, u2.getX(), a2, u3.getX(), a3, u4.getX(), a4);
+ this.y = prototype.linearCombination(u1.getY(), a1, u2.getY(), a2, u3.getY(), a3, u4.getY(), a4);
+ this.z = prototype.linearCombination(u1.getZ(), a1, u2.getZ(), a2, u3.getZ(), a3, u4.getZ(), a4);
+ }
+
+ /** Linear constructor
+ * Build a vector from four other ones and corresponding scale factors.
+ * The vector built will be a1 * u1 + a2 * u2 + a3 * u3 + a4 * u4
+ * @param a1 first scale factor
+ * @param u1 first base (unscaled) vector
+ * @param a2 second scale factor
+ * @param u2 second base (unscaled) vector
+ * @param a3 third scale factor
+ * @param u3 third base (unscaled) vector
+ * @param a4 fourth scale factor
+ * @param u4 fourth base (unscaled) vector
+ */
+ public FieldVector3D(final double a1, final FieldVector3D<T> u1,
+ final double a2, final FieldVector3D<T> u2,
+ final double a3, final FieldVector3D<T> u3,
+ final double a4, final FieldVector3D<T> u4) {
+ final T prototype = u1.getX();
+ this.x = prototype.linearCombination(a1, u1.getX(), a2, u2.getX(), a3, u3.getX(), a4, u4.getX());
+ this.y = prototype.linearCombination(a1, u1.getY(), a2, u2.getY(), a3, u3.getY(), a4, u4.getY());
+ this.z = prototype.linearCombination(a1, u1.getZ(), a2, u2.getZ(), a3, u3.getZ(), a4, u4.getZ());
+ }
+
+ /** Get the abscissa of the vector.
+ * @return abscissa of the vector
+ * @see #FieldVector3D(RealFieldElement, RealFieldElement, RealFieldElement)
+ */
+ public T getX() {
+ return x;
+ }
+
+ /** Get the ordinate of the vector.
+ * @return ordinate of the vector
+ * @see #FieldVector3D(RealFieldElement, RealFieldElement, RealFieldElement)
+ */
+ public T getY() {
+ return y;
+ }
+
+ /** Get the height of the vector.
+ * @return height of the vector
+ * @see #FieldVector3D(RealFieldElement, RealFieldElement, RealFieldElement)
+ */
+ public T getZ() {
+ return z;
+ }
+
+ /** Get the vector coordinates as a dimension 3 array.
+ * @return vector coordinates
+ * @see #FieldVector3D(RealFieldElement[])
+ */
+ public T[] toArray() {
+ final T[] array = MathArrays.buildArray(x.getField(), 3);
+ array[0] = x;
+ array[1] = y;
+ array[2] = z;
+ return array;
+ }
+
+ /** Convert to a constant vector without derivatives.
+ * @return a constant vector
+ */
+ public Vector3D toVector3D() {
+ return new Vector3D(x.getReal(), y.getReal(), z.getReal());
+ }
+
+ /** Get the L<sub>1</sub> norm for the vector.
+ * @return L<sub>1</sub> norm for the vector
+ */
+ public T getNorm1() {
+ return x.abs().add(y.abs()).add(z.abs());
+ }
+
+ /** Get the L<sub>2</sub> norm for the vector.
+ * @return Euclidean norm for the vector
+ */
+ public T getNorm() {
+ // there are no cancellation problems here, so we use the straightforward formula
+ return x.multiply(x).add(y.multiply(y)).add(z.multiply(z)).sqrt();
+ }
+
+ /** Get the square of the norm for the vector.
+ * @return square of the Euclidean norm for the vector
+ */
+ public T getNormSq() {
+ // there are no cancellation problems here, so we use the straightforward formula
+ return x.multiply(x).add(y.multiply(y)).add(z.multiply(z));
+ }
+
+ /** Get the L<sub>&infin;</sub> norm for the vector.
+ * @return L<sub>&infin;</sub> norm for the vector
+ */
+ public T getNormInf() {
+ final T xAbs = x.abs();
+ final T yAbs = y.abs();
+ final T zAbs = z.abs();
+ if (xAbs.getReal() <= yAbs.getReal()) {
+ if (yAbs.getReal() <= zAbs.getReal()) {
+ return zAbs;
+ } else {
+ return yAbs;
+ }
+ } else {
+ if (xAbs.getReal() <= zAbs.getReal()) {
+ return zAbs;
+ } else {
+ return xAbs;
+ }
+ }
+ }
+
+ /** Get the azimuth of the vector.
+ * @return azimuth (&alpha;) of the vector, between -&pi; and +&pi;
+ * @see #FieldVector3D(RealFieldElement, RealFieldElement)
+ */
+ public T getAlpha() {
+ return y.atan2(x);
+ }
+
+ /** Get the elevation of the vector.
+ * @return elevation (&delta;) of the vector, between -&pi;/2 and +&pi;/2
+ * @see #FieldVector3D(RealFieldElement, RealFieldElement)
+ */
+ public T getDelta() {
+ return z.divide(getNorm()).asin();
+ }
+
+ /** Add a vector to the instance.
+ * @param v vector to add
+ * @return a new vector
+ */
+ public FieldVector3D<T> add(final FieldVector3D<T> v) {
+ return new FieldVector3D<T>(x.add(v.x), y.add(v.y), z.add(v.z));
+ }
+
+ /** Add a vector to the instance.
+ * @param v vector to add
+ * @return a new vector
+ */
+ public FieldVector3D<T> add(final Vector3D v) {
+ return new FieldVector3D<T>(x.add(v.getX()), y.add(v.getY()), z.add(v.getZ()));
+ }
+
+ /** Add a scaled vector to the instance.
+ * @param factor scale factor to apply to v before adding it
+ * @param v vector to add
+ * @return a new vector
+ */
+ public FieldVector3D<T> add(final T factor, final FieldVector3D<T> v) {
+ return new FieldVector3D<T>(x.getField().getOne(), this, factor, v);
+ }
+
+ /** Add a scaled vector to the instance.
+ * @param factor scale factor to apply to v before adding it
+ * @param v vector to add
+ * @return a new vector
+ */
+ public FieldVector3D<T> add(final T factor, final Vector3D v) {
+ return new FieldVector3D<T>(x.add(factor.multiply(v.getX())),
+ y.add(factor.multiply(v.getY())),
+ z.add(factor.multiply(v.getZ())));
+ }
+
+ /** Add a scaled vector to the instance.
+ * @param factor scale factor to apply to v before adding it
+ * @param v vector to add
+ * @return a new vector
+ */
+ public FieldVector3D<T> add(final double factor, final FieldVector3D<T> v) {
+ return new FieldVector3D<T>(1.0, this, factor, v);
+ }
+
+ /** Add a scaled vector to the instance.
+ * @param factor scale factor to apply to v before adding it
+ * @param v vector to add
+ * @return a new vector
+ */
+ public FieldVector3D<T> add(final double factor, final Vector3D v) {
+ return new FieldVector3D<T>(x.add(factor * v.getX()),
+ y.add(factor * v.getY()),
+ z.add(factor * v.getZ()));
+ }
+
+ /** Subtract a vector from the instance.
+ * @param v vector to subtract
+ * @return a new vector
+ */
+ public FieldVector3D<T> subtract(final FieldVector3D<T> v) {
+ return new FieldVector3D<T>(x.subtract(v.x), y.subtract(v.y), z.subtract(v.z));
+ }
+
+ /** Subtract a vector from the instance.
+ * @param v vector to subtract
+ * @return a new vector
+ */
+ public FieldVector3D<T> subtract(final Vector3D v) {
+ return new FieldVector3D<T>(x.subtract(v.getX()), y.subtract(v.getY()), z.subtract(v.getZ()));
+ }
+
+ /** Subtract a scaled vector from the instance.
+ * @param factor scale factor to apply to v before subtracting it
+ * @param v vector to subtract
+ * @return a new vector
+ */
+ public FieldVector3D<T> subtract(final T factor, final FieldVector3D<T> v) {
+ return new FieldVector3D<T>(x.getField().getOne(), this, factor.negate(), v);
+ }
+
+ /** Subtract a scaled vector from the instance.
+ * @param factor scale factor to apply to v before subtracting it
+ * @param v vector to subtract
+ * @return a new vector
+ */
+ public FieldVector3D<T> subtract(final T factor, final Vector3D v) {
+ return new FieldVector3D<T>(x.subtract(factor.multiply(v.getX())),
+ y.subtract(factor.multiply(v.getY())),
+ z.subtract(factor.multiply(v.getZ())));
+ }
+
+ /** Subtract a scaled vector from the instance.
+ * @param factor scale factor to apply to v before subtracting it
+ * @param v vector to subtract
+ * @return a new vector
+ */
+ public FieldVector3D<T> subtract(final double factor, final FieldVector3D<T> v) {
+ return new FieldVector3D<T>(1.0, this, -factor, v);
+ }
+
+ /** Subtract a scaled vector from the instance.
+ * @param factor scale factor to apply to v before subtracting it
+ * @param v vector to subtract
+ * @return a new vector
+ */
+ public FieldVector3D<T> subtract(final double factor, final Vector3D v) {
+ return new FieldVector3D<T>(x.subtract(factor * v.getX()),
+ y.subtract(factor * v.getY()),
+ z.subtract(factor * v.getZ()));
+ }
+
+ /** Get a normalized vector aligned with the instance.
+ * @return a new normalized vector
+ * @exception MathArithmeticException if the norm is zero
+ */
+ public FieldVector3D<T> normalize() throws MathArithmeticException {
+ final T s = getNorm();
+ if (s.getReal() == 0) {
+ throw new MathArithmeticException(LocalizedFormats.CANNOT_NORMALIZE_A_ZERO_NORM_VECTOR);
+ }
+ return scalarMultiply(s.reciprocal());
+ }
+
+ /** Get a vector orthogonal to the instance.
+ * <p>There are an infinite number of normalized vectors orthogonal
+ * to the instance. This method picks up one of them almost
+ * arbitrarily. It is useful when one needs to compute a reference
+ * frame with one of the axes in a predefined direction. The
+ * following example shows how to build a frame having the k axis
+ * aligned with the known vector u :
+ * <pre><code>
+ * Vector3D k = u.normalize();
+ * Vector3D i = k.orthogonal();
+ * Vector3D j = Vector3D.crossProduct(k, i);
+ * </code></pre></p>
+ * @return a new normalized vector orthogonal to the instance
+ * @exception MathArithmeticException if the norm of the instance is null
+ */
+ public FieldVector3D<T> orthogonal() throws MathArithmeticException {
+
+ final double threshold = 0.6 * getNorm().getReal();
+ if (threshold == 0) {
+ throw new MathArithmeticException(LocalizedFormats.ZERO_NORM);
+ }
+
+ if (FastMath.abs(x.getReal()) <= threshold) {
+ final T inverse = y.multiply(y).add(z.multiply(z)).sqrt().reciprocal();
+ return new FieldVector3D<T>(inverse.getField().getZero(), inverse.multiply(z), inverse.multiply(y).negate());
+ } else if (FastMath.abs(y.getReal()) <= threshold) {
+ final T inverse = x.multiply(x).add(z.multiply(z)).sqrt().reciprocal();
+ return new FieldVector3D<T>(inverse.multiply(z).negate(), inverse.getField().getZero(), inverse.multiply(x));
+ } else {
+ final T inverse = x.multiply(x).add(y.multiply(y)).sqrt().reciprocal();
+ return new FieldVector3D<T>(inverse.multiply(y), inverse.multiply(x).negate(), inverse.getField().getZero());
+ }
+
+ }
+
+ /** Compute the angular separation between two vectors.
+ * <p>This method computes the angular separation between two
+ * vectors using the dot product for well separated vectors and the
+ * cross product for almost aligned vectors. This allows to have a
+ * good accuracy in all cases, even for vectors very close to each
+ * other.</p>
+ * @param v1 first vector
+ * @param v2 second vector
+ * @param <T> the type of the field elements
+ * @return angular separation between v1 and v2
+ * @exception MathArithmeticException if either vector has a null norm
+ */
+ public static <T extends RealFieldElement<T>> T angle(final FieldVector3D<T> v1, final FieldVector3D<T> v2)
+ throws MathArithmeticException {
+
+ final T normProduct = v1.getNorm().multiply(v2.getNorm());
+ if (normProduct.getReal() == 0) {
+ throw new MathArithmeticException(LocalizedFormats.ZERO_NORM);
+ }
+
+ final T dot = dotProduct(v1, v2);
+ final double threshold = normProduct.getReal() * 0.9999;
+ if ((dot.getReal() < -threshold) || (dot.getReal() > threshold)) {
+ // the vectors are almost aligned, compute using the sine
+ FieldVector3D<T> v3 = crossProduct(v1, v2);
+ if (dot.getReal() >= 0) {
+ return v3.getNorm().divide(normProduct).asin();
+ }
+ return v3.getNorm().divide(normProduct).asin().subtract(FastMath.PI).negate();
+ }
+
+ // the vectors are sufficiently separated to use the cosine
+ return dot.divide(normProduct).acos();
+
+ }
+
+ /** Compute the angular separation between two vectors.
+ * <p>This method computes the angular separation between two
+ * vectors using the dot product for well separated vectors and the
+ * cross product for almost aligned vectors. This allows to have a
+ * good accuracy in all cases, even for vectors very close to each
+ * other.</p>
+ * @param v1 first vector
+ * @param v2 second vector
+ * @param <T> the type of the field elements
+ * @return angular separation between v1 and v2
+ * @exception MathArithmeticException if either vector has a null norm
+ */
+ public static <T extends RealFieldElement<T>> T angle(final FieldVector3D<T> v1, final Vector3D v2)
+ throws MathArithmeticException {
+
+ final T normProduct = v1.getNorm().multiply(v2.getNorm());
+ if (normProduct.getReal() == 0) {
+ throw new MathArithmeticException(LocalizedFormats.ZERO_NORM);
+ }
+
+ final T dot = dotProduct(v1, v2);
+ final double threshold = normProduct.getReal() * 0.9999;
+ if ((dot.getReal() < -threshold) || (dot.getReal() > threshold)) {
+ // the vectors are almost aligned, compute using the sine
+ FieldVector3D<T> v3 = crossProduct(v1, v2);
+ if (dot.getReal() >= 0) {
+ return v3.getNorm().divide(normProduct).asin();
+ }
+ return v3.getNorm().divide(normProduct).asin().subtract(FastMath.PI).negate();
+ }
+
+ // the vectors are sufficiently separated to use the cosine
+ return dot.divide(normProduct).acos();
+
+ }
+
+ /** Compute the angular separation between two vectors.
+ * <p>This method computes the angular separation between two
+ * vectors using the dot product for well separated vectors and the
+ * cross product for almost aligned vectors. This allows to have a
+ * good accuracy in all cases, even for vectors very close to each
+ * other.</p>
+ * @param v1 first vector
+ * @param v2 second vector
+ * @param <T> the type of the field elements
+ * @return angular separation between v1 and v2
+ * @exception MathArithmeticException if either vector has a null norm
+ */
+ public static <T extends RealFieldElement<T>> T angle(final Vector3D v1, final FieldVector3D<T> v2)
+ throws MathArithmeticException {
+ return angle(v2, v1);
+ }
+
+ /** Get the opposite of the instance.
+ * @return a new vector which is opposite to the instance
+ */
+ public FieldVector3D<T> negate() {
+ return new FieldVector3D<T>(x.negate(), y.negate(), z.negate());
+ }
+
+ /** Multiply the instance by a scalar.
+ * @param a scalar
+ * @return a new vector
+ */
+ public FieldVector3D<T> scalarMultiply(final T a) {
+ return new FieldVector3D<T>(x.multiply(a), y.multiply(a), z.multiply(a));
+ }
+
+ /** Multiply the instance by a scalar.
+ * @param a scalar
+ * @return a new vector
+ */
+ public FieldVector3D<T> scalarMultiply(final double a) {
+ return new FieldVector3D<T>(x.multiply(a), y.multiply(a), z.multiply(a));
+ }
+
+ /**
+ * Returns true if any coordinate of this vector is NaN; false otherwise
+ * @return true if any coordinate of this vector is NaN; false otherwise
+ */
+ public boolean isNaN() {
+ return Double.isNaN(x.getReal()) || Double.isNaN(y.getReal()) || Double.isNaN(z.getReal());
+ }
+
+ /**
+ * Returns true if any coordinate of this vector is infinite and none are NaN;
+ * false otherwise
+ * @return true if any coordinate of this vector is infinite and none are NaN;
+ * false otherwise
+ */
+ public boolean isInfinite() {
+ return !isNaN() && (Double.isInfinite(x.getReal()) || Double.isInfinite(y.getReal()) || Double.isInfinite(z.getReal()));
+ }
+
+ /**
+ * Test for the equality of two 3D vectors.
+ * <p>
+ * If all coordinates of two 3D vectors are exactly the same, and none of their
+ * {@link RealFieldElement#getReal() real part} are <code>NaN</code>, the
+ * two 3D vectors are considered to be equal.
+ * </p>
+ * <p>
+ * <code>NaN</code> coordinates are considered to affect globally the vector
+ * and be equals to each other - i.e, if either (or all) real part of the
+ * coordinates of the 3D vector are <code>NaN</code>, the 3D vector is <code>NaN</code>.
+ * </p>
+ *
+ * @param other Object to test for equality to this
+ * @return true if two 3D vector objects are equal, false if
+ * object is null, not an instance of Vector3D, or
+ * not equal to this Vector3D instance
+ *
+ */
+ @Override
+ public boolean equals(Object other) {
+
+ if (this == other) {
+ return true;
+ }
+
+ if (other instanceof FieldVector3D) {
+ @SuppressWarnings("unchecked")
+ final FieldVector3D<T> rhs = (FieldVector3D<T>) other;
+ if (rhs.isNaN()) {
+ return this.isNaN();
+ }
+
+ return x.equals(rhs.x) && y.equals(rhs.y) && z.equals(rhs.z);
+
+ }
+ return false;
+ }
+
+ /**
+ * Get a hashCode for the 3D vector.
+ * <p>
+ * All NaN values have the same hash code.</p>
+ *
+ * @return a hash code value for this object
+ */
+ @Override
+ public int hashCode() {
+ if (isNaN()) {
+ return 409;
+ }
+ return 311 * (107 * x.hashCode() + 83 * y.hashCode() + z.hashCode());
+ }
+
+ /** Compute the dot-product of the instance and another vector.
+ * <p>
+ * The implementation uses specific multiplication and addition
+ * algorithms to preserve accuracy and reduce cancellation effects.
+ * It should be very accurate even for nearly orthogonal vectors.
+ * </p>
+ * @see MathArrays#linearCombination(double, double, double, double, double, double)
+ * @param v second vector
+ * @return the dot product this.v
+ */
+ public T dotProduct(final FieldVector3D<T> v) {
+ return x.linearCombination(x, v.x, y, v.y, z, v.z);
+ }
+
+ /** Compute the dot-product of the instance and another vector.
+ * <p>
+ * The implementation uses specific multiplication and addition
+ * algorithms to preserve accuracy and reduce cancellation effects.
+ * It should be very accurate even for nearly orthogonal vectors.
+ * </p>
+ * @see MathArrays#linearCombination(double, double, double, double, double, double)
+ * @param v second vector
+ * @return the dot product this.v
+ */
+ public T dotProduct(final Vector3D v) {
+ return x.linearCombination(v.getX(), x, v.getY(), y, v.getZ(), z);
+ }
+
+ /** Compute the cross-product of the instance with another vector.
+ * @param v other vector
+ * @return the cross product this ^ v as a new Vector3D
+ */
+ public FieldVector3D<T> crossProduct(final FieldVector3D<T> v) {
+ return new FieldVector3D<T>(x.linearCombination(y, v.z, z.negate(), v.y),
+ y.linearCombination(z, v.x, x.negate(), v.z),
+ z.linearCombination(x, v.y, y.negate(), v.x));
+ }
+
+ /** Compute the cross-product of the instance with another vector.
+ * @param v other vector
+ * @return the cross product this ^ v as a new Vector3D
+ */
+ public FieldVector3D<T> crossProduct(final Vector3D v) {
+ return new FieldVector3D<T>(x.linearCombination(v.getZ(), y, -v.getY(), z),
+ y.linearCombination(v.getX(), z, -v.getZ(), x),
+ z.linearCombination(v.getY(), x, -v.getX(), y));
+ }
+
+ /** Compute the distance between the instance and another vector according to the L<sub>1</sub> norm.
+ * <p>Calling this method is equivalent to calling:
+ * <code>q.subtract(p).getNorm1()</code> except that no intermediate
+ * vector is built</p>
+ * @param v second vector
+ * @return the distance between the instance and p according to the L<sub>1</sub> norm
+ */
+ public T distance1(final FieldVector3D<T> v) {
+ final T dx = v.x.subtract(x).abs();
+ final T dy = v.y.subtract(y).abs();
+ final T dz = v.z.subtract(z).abs();
+ return dx.add(dy).add(dz);
+ }
+
+ /** Compute the distance between the instance and another vector according to the L<sub>1</sub> norm.
+ * <p>Calling this method is equivalent to calling:
+ * <code>q.subtract(p).getNorm1()</code> except that no intermediate
+ * vector is built</p>
+ * @param v second vector
+ * @return the distance between the instance and p according to the L<sub>1</sub> norm
+ */
+ public T distance1(final Vector3D v) {
+ final T dx = x.subtract(v.getX()).abs();
+ final T dy = y.subtract(v.getY()).abs();
+ final T dz = z.subtract(v.getZ()).abs();
+ return dx.add(dy).add(dz);
+ }
+
+ /** Compute the distance between the instance and another vector according to the L<sub>2</sub> norm.
+ * <p>Calling this method is equivalent to calling:
+ * <code>q.subtract(p).getNorm()</code> except that no intermediate
+ * vector is built</p>
+ * @param v second vector
+ * @return the distance between the instance and p according to the L<sub>2</sub> norm
+ */
+ public T distance(final FieldVector3D<T> v) {
+ final T dx = v.x.subtract(x);
+ final T dy = v.y.subtract(y);
+ final T dz = v.z.subtract(z);
+ return dx.multiply(dx).add(dy.multiply(dy)).add(dz.multiply(dz)).sqrt();
+ }
+
+ /** Compute the distance between the instance and another vector according to the L<sub>2</sub> norm.
+ * <p>Calling this method is equivalent to calling:
+ * <code>q.subtract(p).getNorm()</code> except that no intermediate
+ * vector is built</p>
+ * @param v second vector
+ * @return the distance between the instance and p according to the L<sub>2</sub> norm
+ */
+ public T distance(final Vector3D v) {
+ final T dx = x.subtract(v.getX());
+ final T dy = y.subtract(v.getY());
+ final T dz = z.subtract(v.getZ());
+ return dx.multiply(dx).add(dy.multiply(dy)).add(dz.multiply(dz)).sqrt();
+ }
+
+ /** Compute the distance between the instance and another vector according to the L<sub>&infin;</sub> norm.
+ * <p>Calling this method is equivalent to calling:
+ * <code>q.subtract(p).getNormInf()</code> except that no intermediate
+ * vector is built</p>
+ * @param v second vector
+ * @return the distance between the instance and p according to the L<sub>&infin;</sub> norm
+ */
+ public T distanceInf(final FieldVector3D<T> v) {
+ final T dx = v.x.subtract(x).abs();
+ final T dy = v.y.subtract(y).abs();
+ final T dz = v.z.subtract(z).abs();
+ if (dx.getReal() <= dy.getReal()) {
+ if (dy.getReal() <= dz.getReal()) {
+ return dz;
+ } else {
+ return dy;
+ }
+ } else {
+ if (dx.getReal() <= dz.getReal()) {
+ return dz;
+ } else {
+ return dx;
+ }
+ }
+ }
+
+ /** Compute the distance between the instance and another vector according to the L<sub>&infin;</sub> norm.
+ * <p>Calling this method is equivalent to calling:
+ * <code>q.subtract(p).getNormInf()</code> except that no intermediate
+ * vector is built</p>
+ * @param v second vector
+ * @return the distance between the instance and p according to the L<sub>&infin;</sub> norm
+ */
+ public T distanceInf(final Vector3D v) {
+ final T dx = x.subtract(v.getX()).abs();
+ final T dy = y.subtract(v.getY()).abs();
+ final T dz = z.subtract(v.getZ()).abs();
+ if (dx.getReal() <= dy.getReal()) {
+ if (dy.getReal() <= dz.getReal()) {
+ return dz;
+ } else {
+ return dy;
+ }
+ } else {
+ if (dx.getReal() <= dz.getReal()) {
+ return dz;
+ } else {
+ return dx;
+ }
+ }
+ }
+
+ /** Compute the square of the distance between the instance and another vector.
+ * <p>Calling this method is equivalent to calling:
+ * <code>q.subtract(p).getNormSq()</code> except that no intermediate
+ * vector is built</p>
+ * @param v second vector
+ * @return the square of the distance between the instance and p
+ */
+ public T distanceSq(final FieldVector3D<T> v) {
+ final T dx = v.x.subtract(x);
+ final T dy = v.y.subtract(y);
+ final T dz = v.z.subtract(z);
+ return dx.multiply(dx).add(dy.multiply(dy)).add(dz.multiply(dz));
+ }
+
+ /** Compute the square of the distance between the instance and another vector.
+ * <p>Calling this method is equivalent to calling:
+ * <code>q.subtract(p).getNormSq()</code> except that no intermediate
+ * vector is built</p>
+ * @param v second vector
+ * @return the square of the distance between the instance and p
+ */
+ public T distanceSq(final Vector3D v) {
+ final T dx = x.subtract(v.getX());
+ final T dy = y.subtract(v.getY());
+ final T dz = z.subtract(v.getZ());
+ return dx.multiply(dx).add(dy.multiply(dy)).add(dz.multiply(dz));
+ }
+
+ /** Compute the dot-product of two vectors.
+ * @param v1 first vector
+ * @param v2 second vector
+ * @param <T> the type of the field elements
+ * @return the dot product v1.v2
+ */
+ public static <T extends RealFieldElement<T>> T dotProduct(final FieldVector3D<T> v1,
+ final FieldVector3D<T> v2) {
+ return v1.dotProduct(v2);
+ }
+
+ /** Compute the dot-product of two vectors.
+ * @param v1 first vector
+ * @param v2 second vector
+ * @param <T> the type of the field elements
+ * @return the dot product v1.v2
+ */
+ public static <T extends RealFieldElement<T>> T dotProduct(final FieldVector3D<T> v1,
+ final Vector3D v2) {
+ return v1.dotProduct(v2);
+ }
+
+ /** Compute the dot-product of two vectors.
+ * @param v1 first vector
+ * @param v2 second vector
+ * @param <T> the type of the field elements
+ * @return the dot product v1.v2
+ */
+ public static <T extends RealFieldElement<T>> T dotProduct(final Vector3D v1,
+ final FieldVector3D<T> v2) {
+ return v2.dotProduct(v1);
+ }
+
+ /** Compute the cross-product of two vectors.
+ * @param v1 first vector
+ * @param v2 second vector
+ * @param <T> the type of the field elements
+ * @return the cross product v1 ^ v2 as a new Vector
+ */
+ public static <T extends RealFieldElement<T>> FieldVector3D<T> crossProduct(final FieldVector3D<T> v1,
+ final FieldVector3D<T> v2) {
+ return v1.crossProduct(v2);
+ }
+
+ /** Compute the cross-product of two vectors.
+ * @param v1 first vector
+ * @param v2 second vector
+ * @param <T> the type of the field elements
+ * @return the cross product v1 ^ v2 as a new Vector
+ */
+ public static <T extends RealFieldElement<T>> FieldVector3D<T> crossProduct(final FieldVector3D<T> v1,
+ final Vector3D v2) {
+ return v1.crossProduct(v2);
+ }
+
+ /** Compute the cross-product of two vectors.
+ * @param v1 first vector
+ * @param v2 second vector
+ * @param <T> the type of the field elements
+ * @return the cross product v1 ^ v2 as a new Vector
+ */
+ public static <T extends RealFieldElement<T>> FieldVector3D<T> crossProduct(final Vector3D v1,
+ final FieldVector3D<T> v2) {
+ return new FieldVector3D<T>(v2.x.linearCombination(v1.getY(), v2.z, -v1.getZ(), v2.y),
+ v2.y.linearCombination(v1.getZ(), v2.x, -v1.getX(), v2.z),
+ v2.z.linearCombination(v1.getX(), v2.y, -v1.getY(), v2.x));
+ }
+
+ /** Compute the distance between two vectors according to the L<sub>1</sub> norm.
+ * <p>Calling this method is equivalent to calling:
+ * <code>v1.subtract(v2).getNorm1()</code> except that no intermediate
+ * vector is built</p>
+ * @param v1 first vector
+ * @param v2 second vector
+ * @param <T> the type of the field elements
+ * @return the distance between v1 and v2 according to the L<sub>1</sub> norm
+ */
+ public static <T extends RealFieldElement<T>> T distance1(final FieldVector3D<T> v1,
+ final FieldVector3D<T> v2) {
+ return v1.distance1(v2);
+ }
+
+ /** Compute the distance between two vectors according to the L<sub>1</sub> norm.
+ * <p>Calling this method is equivalent to calling:
+ * <code>v1.subtract(v2).getNorm1()</code> except that no intermediate
+ * vector is built</p>
+ * @param v1 first vector
+ * @param v2 second vector
+ * @param <T> the type of the field elements
+ * @return the distance between v1 and v2 according to the L<sub>1</sub> norm
+ */
+ public static <T extends RealFieldElement<T>> T distance1(final FieldVector3D<T> v1,
+ final Vector3D v2) {
+ return v1.distance1(v2);
+ }
+
+ /** Compute the distance between two vectors according to the L<sub>1</sub> norm.
+ * <p>Calling this method is equivalent to calling:
+ * <code>v1.subtract(v2).getNorm1()</code> except that no intermediate
+ * vector is built</p>
+ * @param v1 first vector
+ * @param v2 second vector
+ * @param <T> the type of the field elements
+ * @return the distance between v1 and v2 according to the L<sub>1</sub> norm
+ */
+ public static <T extends RealFieldElement<T>> T distance1(final Vector3D v1,
+ final FieldVector3D<T> v2) {
+ return v2.distance1(v1);
+ }
+
+ /** Compute the distance between two vectors according to the L<sub>2</sub> norm.
+ * <p>Calling this method is equivalent to calling:
+ * <code>v1.subtract(v2).getNorm()</code> except that no intermediate
+ * vector is built</p>
+ * @param v1 first vector
+ * @param v2 second vector
+ * @param <T> the type of the field elements
+ * @return the distance between v1 and v2 according to the L<sub>2</sub> norm
+ */
+ public static <T extends RealFieldElement<T>> T distance(final FieldVector3D<T> v1,
+ final FieldVector3D<T> v2) {
+ return v1.distance(v2);
+ }
+
+ /** Compute the distance between two vectors according to the L<sub>2</sub> norm.
+ * <p>Calling this method is equivalent to calling:
+ * <code>v1.subtract(v2).getNorm()</code> except that no intermediate
+ * vector is built</p>
+ * @param v1 first vector
+ * @param v2 second vector
+ * @param <T> the type of the field elements
+ * @return the distance between v1 and v2 according to the L<sub>2</sub> norm
+ */
+ public static <T extends RealFieldElement<T>> T distance(final FieldVector3D<T> v1,
+ final Vector3D v2) {
+ return v1.distance(v2);
+ }
+
+ /** Compute the distance between two vectors according to the L<sub>2</sub> norm.
+ * <p>Calling this method is equivalent to calling:
+ * <code>v1.subtract(v2).getNorm()</code> except that no intermediate
+ * vector is built</p>
+ * @param v1 first vector
+ * @param v2 second vector
+ * @param <T> the type of the field elements
+ * @return the distance between v1 and v2 according to the L<sub>2</sub> norm
+ */
+ public static <T extends RealFieldElement<T>> T distance(final Vector3D v1,
+ final FieldVector3D<T> v2) {
+ return v2.distance(v1);
+ }
+
+ /** Compute the distance between two vectors according to the L<sub>&infin;</sub> norm.
+ * <p>Calling this method is equivalent to calling:
+ * <code>v1.subtract(v2).getNormInf()</code> except that no intermediate
+ * vector is built</p>
+ * @param v1 first vector
+ * @param v2 second vector
+ * @param <T> the type of the field elements
+ * @return the distance between v1 and v2 according to the L<sub>&infin;</sub> norm
+ */
+ public static <T extends RealFieldElement<T>> T distanceInf(final FieldVector3D<T> v1,
+ final FieldVector3D<T> v2) {
+ return v1.distanceInf(v2);
+ }
+
+ /** Compute the distance between two vectors according to the L<sub>&infin;</sub> norm.
+ * <p>Calling this method is equivalent to calling:
+ * <code>v1.subtract(v2).getNormInf()</code> except that no intermediate
+ * vector is built</p>
+ * @param v1 first vector
+ * @param v2 second vector
+ * @param <T> the type of the field elements
+ * @return the distance between v1 and v2 according to the L<sub>&infin;</sub> norm
+ */
+ public static <T extends RealFieldElement<T>> T distanceInf(final FieldVector3D<T> v1,
+ final Vector3D v2) {
+ return v1.distanceInf(v2);
+ }
+
+ /** Compute the distance between two vectors according to the L<sub>&infin;</sub> norm.
+ * <p>Calling this method is equivalent to calling:
+ * <code>v1.subtract(v2).getNormInf()</code> except that no intermediate
+ * vector is built</p>
+ * @param v1 first vector
+ * @param v2 second vector
+ * @param <T> the type of the field elements
+ * @return the distance between v1 and v2 according to the L<sub>&infin;</sub> norm
+ */
+ public static <T extends RealFieldElement<T>> T distanceInf(final Vector3D v1,
+ final FieldVector3D<T> v2) {
+ return v2.distanceInf(v1);
+ }
+
+ /** Compute the square of the distance between two vectors.
+ * <p>Calling this method is equivalent to calling:
+ * <code>v1.subtract(v2).getNormSq()</code> except that no intermediate
+ * vector is built</p>
+ * @param v1 first vector
+ * @param v2 second vector
+ * @param <T> the type of the field elements
+ * @return the square of the distance between v1 and v2
+ */
+ public static <T extends RealFieldElement<T>> T distanceSq(final FieldVector3D<T> v1,
+ final FieldVector3D<T> v2) {
+ return v1.distanceSq(v2);
+ }
+
+ /** Compute the square of the distance between two vectors.
+ * <p>Calling this method is equivalent to calling:
+ * <code>v1.subtract(v2).getNormSq()</code> except that no intermediate
+ * vector is built</p>
+ * @param v1 first vector
+ * @param v2 second vector
+ * @param <T> the type of the field elements
+ * @return the square of the distance between v1 and v2
+ */
+ public static <T extends RealFieldElement<T>> T distanceSq(final FieldVector3D<T> v1,
+ final Vector3D v2) {
+ return v1.distanceSq(v2);
+ }
+
+ /** Compute the square of the distance between two vectors.
+ * <p>Calling this method is equivalent to calling:
+ * <code>v1.subtract(v2).getNormSq()</code> except that no intermediate
+ * vector is built</p>
+ * @param v1 first vector
+ * @param v2 second vector
+ * @param <T> the type of the field elements
+ * @return the square of the distance between v1 and v2
+ */
+ public static <T extends RealFieldElement<T>> T distanceSq(final Vector3D v1,
+ final FieldVector3D<T> v2) {
+ return v2.distanceSq(v1);
+ }
+
+ /** Get a string representation of this vector.
+ * @return a string representation of this vector
+ */
+ @Override
+ public String toString() {
+ return Vector3DFormat.getInstance().format(toVector3D());
+ }
+
+ /** Get a string representation of this vector.
+ * @param format the custom format for components
+ * @return a string representation of this vector
+ */
+ public String toString(final NumberFormat format) {
+ return new Vector3DFormat(format).format(toVector3D());
+ }
+
+}
diff --git a/src/main/java/org/apache/commons/math3/geometry/euclidean/threed/Line.java b/src/main/java/org/apache/commons/math3/geometry/euclidean/threed/Line.java
new file mode 100644
index 0000000..e234495
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/geometry/euclidean/threed/Line.java
@@ -0,0 +1,275 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math3.geometry.euclidean.threed;
+
+import org.apache.commons.math3.exception.MathIllegalArgumentException;
+import org.apache.commons.math3.exception.util.LocalizedFormats;
+import org.apache.commons.math3.geometry.Point;
+import org.apache.commons.math3.geometry.Vector;
+import org.apache.commons.math3.geometry.euclidean.oned.Euclidean1D;
+import org.apache.commons.math3.geometry.euclidean.oned.IntervalsSet;
+import org.apache.commons.math3.geometry.euclidean.oned.Vector1D;
+import org.apache.commons.math3.geometry.partitioning.Embedding;
+import org.apache.commons.math3.util.FastMath;
+import org.apache.commons.math3.util.Precision;
+
+/** The class represent lines in a three dimensional space.
+
+ * <p>Each oriented line is intrinsically associated with an abscissa
+ * which is a coordinate on the line. The point at abscissa 0 is the
+ * orthogonal projection of the origin on the line, another equivalent
+ * way to express this is to say that it is the point of the line
+ * which is closest to the origin. Abscissa increases in the line
+ * direction.</p>
+
+ * @since 3.0
+ */
+public class Line implements Embedding<Euclidean3D, Euclidean1D> {
+
+ /** Default value for tolerance. */
+ private static final double DEFAULT_TOLERANCE = 1.0e-10;
+
+ /** Line direction. */
+ private Vector3D direction;
+
+ /** Line point closest to the origin. */
+ private Vector3D zero;
+
+ /** Tolerance below which points are considered identical. */
+ private final double tolerance;
+
+ /** Build a line from two points.
+ * @param p1 first point belonging to the line (this can be any point)
+ * @param p2 second point belonging to the line (this can be any point, different from p1)
+ * @param tolerance tolerance below which points are considered identical
+ * @exception MathIllegalArgumentException if the points are equal
+ * @since 3.3
+ */
+ public Line(final Vector3D p1, final Vector3D p2, final double tolerance)
+ throws MathIllegalArgumentException {
+ reset(p1, p2);
+ this.tolerance = tolerance;
+ }
+
+ /** Copy constructor.
+ * <p>The created instance is completely independent from the
+ * original instance, it is a deep copy.</p>
+ * @param line line to copy
+ */
+ public Line(final Line line) {
+ this.direction = line.direction;
+ this.zero = line.zero;
+ this.tolerance = line.tolerance;
+ }
+
+ /** Build a line from two points.
+ * @param p1 first point belonging to the line (this can be any point)
+ * @param p2 second point belonging to the line (this can be any point, different from p1)
+ * @exception MathIllegalArgumentException if the points are equal
+ * @deprecated as of 3.3, replaced with {@link #Line(Vector3D, Vector3D, double)}
+ */
+ @Deprecated
+ public Line(final Vector3D p1, final Vector3D p2) throws MathIllegalArgumentException {
+ this(p1, p2, DEFAULT_TOLERANCE);
+ }
+
+ /** Reset the instance as if built from two points.
+ * @param p1 first point belonging to the line (this can be any point)
+ * @param p2 second point belonging to the line (this can be any point, different from p1)
+ * @exception MathIllegalArgumentException if the points are equal
+ */
+ public void reset(final Vector3D p1, final Vector3D p2) throws MathIllegalArgumentException {
+ final Vector3D delta = p2.subtract(p1);
+ final double norm2 = delta.getNormSq();
+ if (norm2 == 0.0) {
+ throw new MathIllegalArgumentException(LocalizedFormats.ZERO_NORM);
+ }
+ this.direction = new Vector3D(1.0 / FastMath.sqrt(norm2), delta);
+ zero = new Vector3D(1.0, p1, -p1.dotProduct(delta) / norm2, delta);
+ }
+
+ /** Get the tolerance below which points are considered identical.
+ * @return tolerance below which points are considered identical
+ * @since 3.3
+ */
+ public double getTolerance() {
+ return tolerance;
+ }
+
+ /** Get a line with reversed direction.
+ * @return a new instance, with reversed direction
+ */
+ public Line revert() {
+ final Line reverted = new Line(this);
+ reverted.direction = reverted.direction.negate();
+ return reverted;
+ }
+
+ /** Get the normalized direction vector.
+ * @return normalized direction vector
+ */
+ public Vector3D getDirection() {
+ return direction;
+ }
+
+ /** Get the line point closest to the origin.
+ * @return line point closest to the origin
+ */
+ public Vector3D getOrigin() {
+ return zero;
+ }
+
+ /** Get the abscissa of a point with respect to the line.
+ * <p>The abscissa is 0 if the projection of the point and the
+ * projection of the frame origin on the line are the same
+ * point.</p>
+ * @param point point to check
+ * @return abscissa of the point
+ */
+ public double getAbscissa(final Vector3D point) {
+ return point.subtract(zero).dotProduct(direction);
+ }
+
+ /** Get one point from the line.
+ * @param abscissa desired abscissa for the point
+ * @return one point belonging to the line, at specified abscissa
+ */
+ public Vector3D pointAt(final double abscissa) {
+ return new Vector3D(1.0, zero, abscissa, direction);
+ }
+
+ /** Transform a space point into a sub-space point.
+ * @param vector n-dimension point of the space
+ * @return (n-1)-dimension point of the sub-space corresponding to
+ * the specified space point
+ */
+ public Vector1D toSubSpace(Vector<Euclidean3D> vector) {
+ return toSubSpace((Point<Euclidean3D>) vector);
+ }
+
+ /** Transform a sub-space point into a space point.
+ * @param vector (n-1)-dimension point of the sub-space
+ * @return n-dimension point of the space corresponding to the
+ * specified sub-space point
+ */
+ public Vector3D toSpace(Vector<Euclidean1D> vector) {
+ return toSpace((Point<Euclidean1D>) vector);
+ }
+
+ /** {@inheritDoc}
+ * @see #getAbscissa(Vector3D)
+ */
+ public Vector1D toSubSpace(final Point<Euclidean3D> point) {
+ return new Vector1D(getAbscissa((Vector3D) point));
+ }
+
+ /** {@inheritDoc}
+ * @see #pointAt(double)
+ */
+ public Vector3D toSpace(final Point<Euclidean1D> point) {
+ return pointAt(((Vector1D) point).getX());
+ }
+
+ /** Check if the instance is similar to another line.
+ * <p>Lines are considered similar if they contain the same
+ * points. This does not mean they are equal since they can have
+ * opposite directions.</p>
+ * @param line line to which instance should be compared
+ * @return true if the lines are similar
+ */
+ public boolean isSimilarTo(final Line line) {
+ final double angle = Vector3D.angle(direction, line.direction);
+ return ((angle < tolerance) || (angle > (FastMath.PI - tolerance))) && contains(line.zero);
+ }
+
+ /** Check if the instance contains a point.
+ * @param p point to check
+ * @return true if p belongs to the line
+ */
+ public boolean contains(final Vector3D p) {
+ return distance(p) < tolerance;
+ }
+
+ /** Compute the distance between the instance and a point.
+ * @param p to check
+ * @return distance between the instance and the point
+ */
+ public double distance(final Vector3D p) {
+ final Vector3D d = p.subtract(zero);
+ final Vector3D n = new Vector3D(1.0, d, -d.dotProduct(direction), direction);
+ return n.getNorm();
+ }
+
+ /** Compute the shortest distance between the instance and another line.
+ * @param line line to check against the instance
+ * @return shortest distance between the instance and the line
+ */
+ public double distance(final Line line) {
+
+ final Vector3D normal = Vector3D.crossProduct(direction, line.direction);
+ final double n = normal.getNorm();
+ if (n < Precision.SAFE_MIN) {
+ // lines are parallel
+ return distance(line.zero);
+ }
+
+ // signed separation of the two parallel planes that contains the lines
+ final double offset = line.zero.subtract(zero).dotProduct(normal) / n;
+
+ return FastMath.abs(offset);
+
+ }
+
+ /** Compute the point of the instance closest to another line.
+ * @param line line to check against the instance
+ * @return point of the instance closest to another line
+ */
+ public Vector3D closestPoint(final Line line) {
+
+ final double cos = direction.dotProduct(line.direction);
+ final double n = 1 - cos * cos;
+ if (n < Precision.EPSILON) {
+ // the lines are parallel
+ return zero;
+ }
+
+ final Vector3D delta0 = line.zero.subtract(zero);
+ final double a = delta0.dotProduct(direction);
+ final double b = delta0.dotProduct(line.direction);
+
+ return new Vector3D(1, zero, (a - b * cos) / n, direction);
+
+ }
+
+ /** Get the intersection point of the instance and another line.
+ * @param line other line
+ * @return intersection point of the instance and the other line
+ * or null if there are no intersection points
+ */
+ public Vector3D intersection(final Line line) {
+ final Vector3D closest = closestPoint(line);
+ return line.contains(closest) ? closest : null;
+ }
+
+ /** Build a sub-line covering the whole line.
+ * @return a sub-line covering the whole line
+ */
+ public SubLine wholeLine() {
+ return new SubLine(this, new IntervalsSet(tolerance));
+ }
+
+}
diff --git a/src/main/java/org/apache/commons/math3/geometry/euclidean/threed/NotARotationMatrixException.java b/src/main/java/org/apache/commons/math3/geometry/euclidean/threed/NotARotationMatrixException.java
new file mode 100644
index 0000000..3f1f3d3
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/geometry/euclidean/threed/NotARotationMatrixException.java
@@ -0,0 +1,47 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math3.geometry.euclidean.threed;
+
+import org.apache.commons.math3.exception.MathIllegalArgumentException;
+import org.apache.commons.math3.exception.util.Localizable;
+
+/**
+ * This class represents exceptions thrown while building rotations
+ * from matrices.
+ *
+ * @since 1.2
+ */
+
+public class NotARotationMatrixException
+ extends MathIllegalArgumentException {
+
+ /** Serializable version identifier */
+ private static final long serialVersionUID = 5647178478658937642L;
+
+ /**
+ * Simple constructor.
+ * Build an exception by translating and formating a message
+ * @param specifier format specifier (to be translated)
+ * @param parts to insert in the format (no translation)
+ * @since 2.2
+ */
+ public NotARotationMatrixException(Localizable specifier, Object ... parts) {
+ super(specifier, parts);
+ }
+
+}
diff --git a/src/main/java/org/apache/commons/math3/geometry/euclidean/threed/OutlineExtractor.java b/src/main/java/org/apache/commons/math3/geometry/euclidean/threed/OutlineExtractor.java
new file mode 100644
index 0000000..0f8af88
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/geometry/euclidean/threed/OutlineExtractor.java
@@ -0,0 +1,263 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math3.geometry.euclidean.threed;
+
+import java.util.ArrayList;
+
+import org.apache.commons.math3.geometry.Point;
+import org.apache.commons.math3.geometry.euclidean.twod.Euclidean2D;
+import org.apache.commons.math3.geometry.euclidean.twod.PolygonsSet;
+import org.apache.commons.math3.geometry.euclidean.twod.Vector2D;
+import org.apache.commons.math3.geometry.partitioning.AbstractSubHyperplane;
+import org.apache.commons.math3.geometry.partitioning.BSPTree;
+import org.apache.commons.math3.geometry.partitioning.BSPTreeVisitor;
+import org.apache.commons.math3.geometry.partitioning.BoundaryAttribute;
+import org.apache.commons.math3.geometry.partitioning.RegionFactory;
+import org.apache.commons.math3.geometry.partitioning.SubHyperplane;
+import org.apache.commons.math3.util.FastMath;
+
+/** Extractor for {@link PolygonsSet polyhedrons sets} outlines.
+ * <p>This class extracts the 2D outlines from {{@link PolygonsSet
+ * polyhedrons sets} in a specified projection plane.</p>
+ * @since 3.0
+ */
+public class OutlineExtractor {
+
+ /** Abscissa axis of the projection plane. */
+ private Vector3D u;
+
+ /** Ordinate axis of the projection plane. */
+ private Vector3D v;
+
+ /** Normal of the projection plane (viewing direction). */
+ private Vector3D w;
+
+ /** Build an extractor for a specific projection plane.
+ * @param u abscissa axis of the projection point
+ * @param v ordinate axis of the projection point
+ */
+ public OutlineExtractor(final Vector3D u, final Vector3D v) {
+ this.u = u;
+ this.v = v;
+ w = Vector3D.crossProduct(u, v);
+ }
+
+ /** Extract the outline of a polyhedrons set.
+ * @param polyhedronsSet polyhedrons set whose outline must be extracted
+ * @return an outline, as an array of loops.
+ */
+ public Vector2D[][] getOutline(final PolyhedronsSet polyhedronsSet) {
+
+ // project all boundary facets into one polygons set
+ final BoundaryProjector projector = new BoundaryProjector(polyhedronsSet.getTolerance());
+ polyhedronsSet.getTree(true).visit(projector);
+ final PolygonsSet projected = projector.getProjected();
+
+ // Remove the spurious intermediate vertices from the outline
+ final Vector2D[][] outline = projected.getVertices();
+ for (int i = 0; i < outline.length; ++i) {
+ final Vector2D[] rawLoop = outline[i];
+ int end = rawLoop.length;
+ int j = 0;
+ while (j < end) {
+ if (pointIsBetween(rawLoop, end, j)) {
+ // the point should be removed
+ for (int k = j; k < (end - 1); ++k) {
+ rawLoop[k] = rawLoop[k + 1];
+ }
+ --end;
+ } else {
+ // the point remains in the loop
+ ++j;
+ }
+ }
+ if (end != rawLoop.length) {
+ // resize the array
+ outline[i] = new Vector2D[end];
+ System.arraycopy(rawLoop, 0, outline[i], 0, end);
+ }
+ }
+
+ return outline;
+
+ }
+
+ /** Check if a point is geometrically between its neighbor in an array.
+ * <p>The neighbors are computed considering the array is a loop
+ * (i.e. point at index (n-1) is before point at index 0)</p>
+ * @param loop points array
+ * @param n number of points to consider in the array
+ * @param i index of the point to check (must be between 0 and n-1)
+ * @return true if the point is exactly between its neighbors
+ */
+ private boolean pointIsBetween(final Vector2D[] loop, final int n, final int i) {
+ final Vector2D previous = loop[(i + n - 1) % n];
+ final Vector2D current = loop[i];
+ final Vector2D next = loop[(i + 1) % n];
+ final double dx1 = current.getX() - previous.getX();
+ final double dy1 = current.getY() - previous.getY();
+ final double dx2 = next.getX() - current.getX();
+ final double dy2 = next.getY() - current.getY();
+ final double cross = dx1 * dy2 - dx2 * dy1;
+ final double dot = dx1 * dx2 + dy1 * dy2;
+ final double d1d2 = FastMath.sqrt((dx1 * dx1 + dy1 * dy1) * (dx2 * dx2 + dy2 * dy2));
+ return (FastMath.abs(cross) <= (1.0e-6 * d1d2)) && (dot >= 0.0);
+ }
+
+ /** Visitor projecting the boundary facets on a plane. */
+ private class BoundaryProjector implements BSPTreeVisitor<Euclidean3D> {
+
+ /** Projection of the polyhedrons set on the plane. */
+ private PolygonsSet projected;
+
+ /** Tolerance below which points are considered identical. */
+ private final double tolerance;
+
+ /** Simple constructor.
+ * @param tolerance tolerance below which points are considered identical
+ */
+ BoundaryProjector(final double tolerance) {
+ this.projected = new PolygonsSet(new BSPTree<Euclidean2D>(Boolean.FALSE), tolerance);
+ this.tolerance = tolerance;
+ }
+
+ /** {@inheritDoc} */
+ public Order visitOrder(final BSPTree<Euclidean3D> node) {
+ return Order.MINUS_SUB_PLUS;
+ }
+
+ /** {@inheritDoc} */
+ public void visitInternalNode(final BSPTree<Euclidean3D> node) {
+ @SuppressWarnings("unchecked")
+ final BoundaryAttribute<Euclidean3D> attribute =
+ (BoundaryAttribute<Euclidean3D>) node.getAttribute();
+ if (attribute.getPlusOutside() != null) {
+ addContribution(attribute.getPlusOutside(), false);
+ }
+ if (attribute.getPlusInside() != null) {
+ addContribution(attribute.getPlusInside(), true);
+ }
+ }
+
+ /** {@inheritDoc} */
+ public void visitLeafNode(final BSPTree<Euclidean3D> node) {
+ }
+
+ /** Add he contribution of a boundary facet.
+ * @param facet boundary facet
+ * @param reversed if true, the facet has the inside on its plus side
+ */
+ private void addContribution(final SubHyperplane<Euclidean3D> facet, final boolean reversed) {
+
+ // extract the vertices of the facet
+ @SuppressWarnings("unchecked")
+ final AbstractSubHyperplane<Euclidean3D, Euclidean2D> absFacet =
+ (AbstractSubHyperplane<Euclidean3D, Euclidean2D>) facet;
+ final Plane plane = (Plane) facet.getHyperplane();
+
+ final double scal = plane.getNormal().dotProduct(w);
+ if (FastMath.abs(scal) > 1.0e-3) {
+ Vector2D[][] vertices =
+ ((PolygonsSet) absFacet.getRemainingRegion()).getVertices();
+
+ if ((scal < 0) ^ reversed) {
+ // the facet is seen from the inside,
+ // we need to invert its boundary orientation
+ final Vector2D[][] newVertices = new Vector2D[vertices.length][];
+ for (int i = 0; i < vertices.length; ++i) {
+ final Vector2D[] loop = vertices[i];
+ final Vector2D[] newLoop = new Vector2D[loop.length];
+ if (loop[0] == null) {
+ newLoop[0] = null;
+ for (int j = 1; j < loop.length; ++j) {
+ newLoop[j] = loop[loop.length - j];
+ }
+ } else {
+ for (int j = 0; j < loop.length; ++j) {
+ newLoop[j] = loop[loop.length - (j + 1)];
+ }
+ }
+ newVertices[i] = newLoop;
+ }
+
+ // use the reverted vertices
+ vertices = newVertices;
+
+ }
+
+ // compute the projection of the facet in the outline plane
+ final ArrayList<SubHyperplane<Euclidean2D>> edges = new ArrayList<SubHyperplane<Euclidean2D>>();
+ for (Vector2D[] loop : vertices) {
+ final boolean closed = loop[0] != null;
+ int previous = closed ? (loop.length - 1) : 1;
+ Vector3D previous3D = plane.toSpace((Point<Euclidean2D>) loop[previous]);
+ int current = (previous + 1) % loop.length;
+ Vector2D pPoint = new Vector2D(previous3D.dotProduct(u),
+ previous3D.dotProduct(v));
+ while (current < loop.length) {
+
+ final Vector3D current3D = plane.toSpace((Point<Euclidean2D>) loop[current]);
+ final Vector2D cPoint = new Vector2D(current3D.dotProduct(u),
+ current3D.dotProduct(v));
+ final org.apache.commons.math3.geometry.euclidean.twod.Line line =
+ new org.apache.commons.math3.geometry.euclidean.twod.Line(pPoint, cPoint, tolerance);
+ SubHyperplane<Euclidean2D> edge = line.wholeHyperplane();
+
+ if (closed || (previous != 1)) {
+ // the previous point is a real vertex
+ // it defines one bounding point of the edge
+ final double angle = line.getAngle() + 0.5 * FastMath.PI;
+ final org.apache.commons.math3.geometry.euclidean.twod.Line l =
+ new org.apache.commons.math3.geometry.euclidean.twod.Line(pPoint, angle, tolerance);
+ edge = edge.split(l).getPlus();
+ }
+
+ if (closed || (current != (loop.length - 1))) {
+ // the current point is a real vertex
+ // it defines one bounding point of the edge
+ final double angle = line.getAngle() + 0.5 * FastMath.PI;
+ final org.apache.commons.math3.geometry.euclidean.twod.Line l =
+ new org.apache.commons.math3.geometry.euclidean.twod.Line(cPoint, angle, tolerance);
+ edge = edge.split(l).getMinus();
+ }
+
+ edges.add(edge);
+
+ previous = current++;
+ previous3D = current3D;
+ pPoint = cPoint;
+
+ }
+ }
+ final PolygonsSet projectedFacet = new PolygonsSet(edges, tolerance);
+
+ // add the contribution of the facet to the global outline
+ projected = (PolygonsSet) new RegionFactory<Euclidean2D>().union(projected, projectedFacet);
+
+ }
+ }
+
+ /** Get the projection of the polyhedrons set on the plane.
+ * @return projection of the polyhedrons set on the plane
+ */
+ public PolygonsSet getProjected() {
+ return projected;
+ }
+
+ }
+
+}
diff --git a/src/main/java/org/apache/commons/math3/geometry/euclidean/threed/Plane.java b/src/main/java/org/apache/commons/math3/geometry/euclidean/threed/Plane.java
new file mode 100644
index 0000000..158818d
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/geometry/euclidean/threed/Plane.java
@@ -0,0 +1,527 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math3.geometry.euclidean.threed;
+
+import org.apache.commons.math3.exception.MathArithmeticException;
+import org.apache.commons.math3.exception.util.LocalizedFormats;
+import org.apache.commons.math3.geometry.Point;
+import org.apache.commons.math3.geometry.Vector;
+import org.apache.commons.math3.geometry.euclidean.oned.Euclidean1D;
+import org.apache.commons.math3.geometry.euclidean.oned.Vector1D;
+import org.apache.commons.math3.geometry.euclidean.twod.Euclidean2D;
+import org.apache.commons.math3.geometry.euclidean.twod.PolygonsSet;
+import org.apache.commons.math3.geometry.euclidean.twod.Vector2D;
+import org.apache.commons.math3.geometry.partitioning.Embedding;
+import org.apache.commons.math3.geometry.partitioning.Hyperplane;
+import org.apache.commons.math3.util.FastMath;
+
+/** The class represent planes in a three dimensional space.
+ * @since 3.0
+ */
+public class Plane implements Hyperplane<Euclidean3D>, Embedding<Euclidean3D, Euclidean2D> {
+
+ /** Default value for tolerance. */
+ private static final double DEFAULT_TOLERANCE = 1.0e-10;
+
+ /** Offset of the origin with respect to the plane. */
+ private double originOffset;
+
+ /** Origin of the plane frame. */
+ private Vector3D origin;
+
+ /** First vector of the plane frame (in plane). */
+ private Vector3D u;
+
+ /** Second vector of the plane frame (in plane). */
+ private Vector3D v;
+
+ /** Third vector of the plane frame (plane normal). */
+ private Vector3D w;
+
+ /** Tolerance below which points are considered identical. */
+ private final double tolerance;
+
+ /** Build a plane normal to a given direction and containing the origin.
+ * @param normal normal direction to the plane
+ * @param tolerance tolerance below which points are considered identical
+ * @exception MathArithmeticException if the normal norm is too small
+ * @since 3.3
+ */
+ public Plane(final Vector3D normal, final double tolerance)
+ throws MathArithmeticException {
+ setNormal(normal);
+ this.tolerance = tolerance;
+ originOffset = 0;
+ setFrame();
+ }
+
+ /** Build a plane from a point and a normal.
+ * @param p point belonging to the plane
+ * @param normal normal direction to the plane
+ * @param tolerance tolerance below which points are considered identical
+ * @exception MathArithmeticException if the normal norm is too small
+ * @since 3.3
+ */
+ public Plane(final Vector3D p, final Vector3D normal, final double tolerance)
+ throws MathArithmeticException {
+ setNormal(normal);
+ this.tolerance = tolerance;
+ originOffset = -p.dotProduct(w);
+ setFrame();
+ }
+
+ /** Build a plane from three points.
+ * <p>The plane is oriented in the direction of
+ * {@code (p2-p1) ^ (p3-p1)}</p>
+ * @param p1 first point belonging to the plane
+ * @param p2 second point belonging to the plane
+ * @param p3 third point belonging to the plane
+ * @param tolerance tolerance below which points are considered identical
+ * @exception MathArithmeticException if the points do not constitute a plane
+ * @since 3.3
+ */
+ public Plane(final Vector3D p1, final Vector3D p2, final Vector3D p3, final double tolerance)
+ throws MathArithmeticException {
+ this(p1, p2.subtract(p1).crossProduct(p3.subtract(p1)), tolerance);
+ }
+
+ /** Build a plane normal to a given direction and containing the origin.
+ * @param normal normal direction to the plane
+ * @exception MathArithmeticException if the normal norm is too small
+ * @deprecated as of 3.3, replaced with {@link #Plane(Vector3D, double)}
+ */
+ @Deprecated
+ public Plane(final Vector3D normal) throws MathArithmeticException {
+ this(normal, DEFAULT_TOLERANCE);
+ }
+
+ /** Build a plane from a point and a normal.
+ * @param p point belonging to the plane
+ * @param normal normal direction to the plane
+ * @exception MathArithmeticException if the normal norm is too small
+ * @deprecated as of 3.3, replaced with {@link #Plane(Vector3D, Vector3D, double)}
+ */
+ @Deprecated
+ public Plane(final Vector3D p, final Vector3D normal) throws MathArithmeticException {
+ this(p, normal, DEFAULT_TOLERANCE);
+ }
+
+ /** Build a plane from three points.
+ * <p>The plane is oriented in the direction of
+ * {@code (p2-p1) ^ (p3-p1)}</p>
+ * @param p1 first point belonging to the plane
+ * @param p2 second point belonging to the plane
+ * @param p3 third point belonging to the plane
+ * @exception MathArithmeticException if the points do not constitute a plane
+ * @deprecated as of 3.3, replaced with {@link #Plane(Vector3D, Vector3D, Vector3D, double)}
+ */
+ @Deprecated
+ public Plane(final Vector3D p1, final Vector3D p2, final Vector3D p3)
+ throws MathArithmeticException {
+ this(p1, p2, p3, DEFAULT_TOLERANCE);
+ }
+
+ /** Copy constructor.
+ * <p>The instance created is completely independant of the original
+ * one. A deep copy is used, none of the underlying object are
+ * shared.</p>
+ * @param plane plane to copy
+ */
+ public Plane(final Plane plane) {
+ originOffset = plane.originOffset;
+ origin = plane.origin;
+ u = plane.u;
+ v = plane.v;
+ w = plane.w;
+ tolerance = plane.tolerance;
+ }
+
+ /** Copy the instance.
+ * <p>The instance created is completely independant of the original
+ * one. A deep copy is used, none of the underlying objects are
+ * shared (except for immutable objects).</p>
+ * @return a new hyperplane, copy of the instance
+ */
+ public Plane copySelf() {
+ return new Plane(this);
+ }
+
+ /** Reset the instance as if built from a point and a normal.
+ * @param p point belonging to the plane
+ * @param normal normal direction to the plane
+ * @exception MathArithmeticException if the normal norm is too small
+ */
+ public void reset(final Vector3D p, final Vector3D normal) throws MathArithmeticException {
+ setNormal(normal);
+ originOffset = -p.dotProduct(w);
+ setFrame();
+ }
+
+ /** Reset the instance from another one.
+ * <p>The updated instance is completely independant of the original
+ * one. A deep reset is used none of the underlying object is
+ * shared.</p>
+ * @param original plane to reset from
+ */
+ public void reset(final Plane original) {
+ originOffset = original.originOffset;
+ origin = original.origin;
+ u = original.u;
+ v = original.v;
+ w = original.w;
+ }
+
+ /** Set the normal vactor.
+ * @param normal normal direction to the plane (will be copied)
+ * @exception MathArithmeticException if the normal norm is too small
+ */
+ private void setNormal(final Vector3D normal) throws MathArithmeticException {
+ final double norm = normal.getNorm();
+ if (norm < 1.0e-10) {
+ throw new MathArithmeticException(LocalizedFormats.ZERO_NORM);
+ }
+ w = new Vector3D(1.0 / norm, normal);
+ }
+
+ /** Reset the plane frame.
+ */
+ private void setFrame() {
+ origin = new Vector3D(-originOffset, w);
+ u = w.orthogonal();
+ v = Vector3D.crossProduct(w, u);
+ }
+
+ /** Get the origin point of the plane frame.
+ * <p>The point returned is the orthogonal projection of the
+ * 3D-space origin in the plane.</p>
+ * @return the origin point of the plane frame (point closest to the
+ * 3D-space origin)
+ */
+ public Vector3D getOrigin() {
+ return origin;
+ }
+
+ /** Get the normalized normal vector.
+ * <p>The frame defined by ({@link #getU getU}, {@link #getV getV},
+ * {@link #getNormal getNormal}) is a rigth-handed orthonormalized
+ * frame).</p>
+ * @return normalized normal vector
+ * @see #getU
+ * @see #getV
+ */
+ public Vector3D getNormal() {
+ return w;
+ }
+
+ /** Get the plane first canonical vector.
+ * <p>The frame defined by ({@link #getU getU}, {@link #getV getV},
+ * {@link #getNormal getNormal}) is a rigth-handed orthonormalized
+ * frame).</p>
+ * @return normalized first canonical vector
+ * @see #getV
+ * @see #getNormal
+ */
+ public Vector3D getU() {
+ return u;
+ }
+
+ /** Get the plane second canonical vector.
+ * <p>The frame defined by ({@link #getU getU}, {@link #getV getV},
+ * {@link #getNormal getNormal}) is a rigth-handed orthonormalized
+ * frame).</p>
+ * @return normalized second canonical vector
+ * @see #getU
+ * @see #getNormal
+ */
+ public Vector3D getV() {
+ return v;
+ }
+
+ /** {@inheritDoc}
+ * @since 3.3
+ */
+ public Point<Euclidean3D> project(Point<Euclidean3D> point) {
+ return toSpace(toSubSpace(point));
+ }
+
+ /** {@inheritDoc}
+ * @since 3.3
+ */
+ public double getTolerance() {
+ return tolerance;
+ }
+
+ /** Revert the plane.
+ * <p>Replace the instance by a similar plane with opposite orientation.</p>
+ * <p>The new plane frame is chosen in such a way that a 3D point that had
+ * {@code (x, y)} in-plane coordinates and {@code z} offset with
+ * respect to the plane and is unaffected by the change will have
+ * {@code (y, x)} in-plane coordinates and {@code -z} offset with
+ * respect to the new plane. This means that the {@code u} and {@code v}
+ * vectors returned by the {@link #getU} and {@link #getV} methods are exchanged,
+ * and the {@code w} vector returned by the {@link #getNormal} method is
+ * reversed.</p>
+ */
+ public void revertSelf() {
+ final Vector3D tmp = u;
+ u = v;
+ v = tmp;
+ w = w.negate();
+ originOffset = -originOffset;
+ }
+
+ /** Transform a space point into a sub-space point.
+ * @param vector n-dimension point of the space
+ * @return (n-1)-dimension point of the sub-space corresponding to
+ * the specified space point
+ */
+ public Vector2D toSubSpace(Vector<Euclidean3D> vector) {
+ return toSubSpace((Point<Euclidean3D>) vector);
+ }
+
+ /** Transform a sub-space point into a space point.
+ * @param vector (n-1)-dimension point of the sub-space
+ * @return n-dimension point of the space corresponding to the
+ * specified sub-space point
+ */
+ public Vector3D toSpace(Vector<Euclidean2D> vector) {
+ return toSpace((Point<Euclidean2D>) vector);
+ }
+
+ /** Transform a 3D space point into an in-plane point.
+ * @param point point of the space (must be a {@link Vector3D
+ * Vector3D} instance)
+ * @return in-plane point (really a {@link
+ * org.apache.commons.math3.geometry.euclidean.twod.Vector2D Vector2D} instance)
+ * @see #toSpace
+ */
+ public Vector2D toSubSpace(final Point<Euclidean3D> point) {
+ final Vector3D p3D = (Vector3D) point;
+ return new Vector2D(p3D.dotProduct(u), p3D.dotProduct(v));
+ }
+
+ /** Transform an in-plane point into a 3D space point.
+ * @param point in-plane point (must be a {@link
+ * org.apache.commons.math3.geometry.euclidean.twod.Vector2D Vector2D} instance)
+ * @return 3D space point (really a {@link Vector3D Vector3D} instance)
+ * @see #toSubSpace
+ */
+ public Vector3D toSpace(final Point<Euclidean2D> point) {
+ final Vector2D p2D = (Vector2D) point;
+ return new Vector3D(p2D.getX(), u, p2D.getY(), v, -originOffset, w);
+ }
+
+ /** Get one point from the 3D-space.
+ * @param inPlane desired in-plane coordinates for the point in the
+ * plane
+ * @param offset desired offset for the point
+ * @return one point in the 3D-space, with given coordinates and offset
+ * relative to the plane
+ */
+ public Vector3D getPointAt(final Vector2D inPlane, final double offset) {
+ return new Vector3D(inPlane.getX(), u, inPlane.getY(), v, offset - originOffset, w);
+ }
+
+ /** Check if the instance is similar to another plane.
+ * <p>Planes are considered similar if they contain the same
+ * points. This does not mean they are equal since they can have
+ * opposite normals.</p>
+ * @param plane plane to which the instance is compared
+ * @return true if the planes are similar
+ */
+ public boolean isSimilarTo(final Plane plane) {
+ final double angle = Vector3D.angle(w, plane.w);
+ return ((angle < 1.0e-10) && (FastMath.abs(originOffset - plane.originOffset) < tolerance)) ||
+ ((angle > (FastMath.PI - 1.0e-10)) && (FastMath.abs(originOffset + plane.originOffset) < tolerance));
+ }
+
+ /** Rotate the plane around the specified point.
+ * <p>The instance is not modified, a new instance is created.</p>
+ * @param center rotation center
+ * @param rotation vectorial rotation operator
+ * @return a new plane
+ */
+ public Plane rotate(final Vector3D center, final Rotation rotation) {
+
+ final Vector3D delta = origin.subtract(center);
+ final Plane plane = new Plane(center.add(rotation.applyTo(delta)),
+ rotation.applyTo(w), tolerance);
+
+ // make sure the frame is transformed as desired
+ plane.u = rotation.applyTo(u);
+ plane.v = rotation.applyTo(v);
+
+ return plane;
+
+ }
+
+ /** Translate the plane by the specified amount.
+ * <p>The instance is not modified, a new instance is created.</p>
+ * @param translation translation to apply
+ * @return a new plane
+ */
+ public Plane translate(final Vector3D translation) {
+
+ final Plane plane = new Plane(origin.add(translation), w, tolerance);
+
+ // make sure the frame is transformed as desired
+ plane.u = u;
+ plane.v = v;
+
+ return plane;
+
+ }
+
+ /** Get the intersection of a line with the instance.
+ * @param line line intersecting the instance
+ * @return intersection point between between the line and the
+ * instance (null if the line is parallel to the instance)
+ */
+ public Vector3D intersection(final Line line) {
+ final Vector3D direction = line.getDirection();
+ final double dot = w.dotProduct(direction);
+ if (FastMath.abs(dot) < 1.0e-10) {
+ return null;
+ }
+ final Vector3D point = line.toSpace((Point<Euclidean1D>) Vector1D.ZERO);
+ final double k = -(originOffset + w.dotProduct(point)) / dot;
+ return new Vector3D(1.0, point, k, direction);
+ }
+
+ /** Build the line shared by the instance and another plane.
+ * @param other other plane
+ * @return line at the intersection of the instance and the
+ * other plane (really a {@link Line Line} instance)
+ */
+ public Line intersection(final Plane other) {
+ final Vector3D direction = Vector3D.crossProduct(w, other.w);
+ if (direction.getNorm() < tolerance) {
+ return null;
+ }
+ final Vector3D point = intersection(this, other, new Plane(direction, tolerance));
+ return new Line(point, point.add(direction), tolerance);
+ }
+
+ /** Get the intersection point of three planes.
+ * @param plane1 first plane1
+ * @param plane2 second plane2
+ * @param plane3 third plane2
+ * @return intersection point of three planes, null if some planes are parallel
+ */
+ public static Vector3D intersection(final Plane plane1, final Plane plane2, final Plane plane3) {
+
+ // coefficients of the three planes linear equations
+ final double a1 = plane1.w.getX();
+ final double b1 = plane1.w.getY();
+ final double c1 = plane1.w.getZ();
+ final double d1 = plane1.originOffset;
+
+ final double a2 = plane2.w.getX();
+ final double b2 = plane2.w.getY();
+ final double c2 = plane2.w.getZ();
+ final double d2 = plane2.originOffset;
+
+ final double a3 = plane3.w.getX();
+ final double b3 = plane3.w.getY();
+ final double c3 = plane3.w.getZ();
+ final double d3 = plane3.originOffset;
+
+ // direct Cramer resolution of the linear system
+ // (this is still feasible for a 3x3 system)
+ final double a23 = b2 * c3 - b3 * c2;
+ final double b23 = c2 * a3 - c3 * a2;
+ final double c23 = a2 * b3 - a3 * b2;
+ final double determinant = a1 * a23 + b1 * b23 + c1 * c23;
+ if (FastMath.abs(determinant) < 1.0e-10) {
+ return null;
+ }
+
+ final double r = 1.0 / determinant;
+ return new Vector3D(
+ (-a23 * d1 - (c1 * b3 - c3 * b1) * d2 - (c2 * b1 - c1 * b2) * d3) * r,
+ (-b23 * d1 - (c3 * a1 - c1 * a3) * d2 - (c1 * a2 - c2 * a1) * d3) * r,
+ (-c23 * d1 - (b1 * a3 - b3 * a1) * d2 - (b2 * a1 - b1 * a2) * d3) * r);
+
+ }
+
+ /** Build a region covering the whole hyperplane.
+ * @return a region covering the whole hyperplane
+ */
+ public SubPlane wholeHyperplane() {
+ return new SubPlane(this, new PolygonsSet(tolerance));
+ }
+
+ /** Build a region covering the whole space.
+ * @return a region containing the instance (really a {@link
+ * PolyhedronsSet PolyhedronsSet} instance)
+ */
+ public PolyhedronsSet wholeSpace() {
+ return new PolyhedronsSet(tolerance);
+ }
+
+ /** Check if the instance contains a point.
+ * @param p point to check
+ * @return true if p belongs to the plane
+ */
+ public boolean contains(final Vector3D p) {
+ return FastMath.abs(getOffset(p)) < tolerance;
+ }
+
+ /** Get the offset (oriented distance) of a parallel plane.
+ * <p>This method should be called only for parallel planes otherwise
+ * the result is not meaningful.</p>
+ * <p>The offset is 0 if both planes are the same, it is
+ * positive if the plane is on the plus side of the instance and
+ * negative if it is on the minus side, according to its natural
+ * orientation.</p>
+ * @param plane plane to check
+ * @return offset of the plane
+ */
+ public double getOffset(final Plane plane) {
+ return originOffset + (sameOrientationAs(plane) ? -plane.originOffset : plane.originOffset);
+ }
+
+ /** Get the offset (oriented distance) of a vector.
+ * @param vector vector to check
+ * @return offset of the vector
+ */
+ public double getOffset(Vector<Euclidean3D> vector) {
+ return getOffset((Point<Euclidean3D>) vector);
+ }
+
+ /** Get the offset (oriented distance) of a point.
+ * <p>The offset is 0 if the point is on the underlying hyperplane,
+ * it is positive if the point is on one particular side of the
+ * hyperplane, and it is negative if the point is on the other side,
+ * according to the hyperplane natural orientation.</p>
+ * @param point point to check
+ * @return offset of the point
+ */
+ public double getOffset(final Point<Euclidean3D> point) {
+ return ((Vector3D) point).dotProduct(w) + originOffset;
+ }
+
+ /** Check if the instance has the same orientation as another hyperplane.
+ * @param other other hyperplane to check against the instance
+ * @return true if the instance and the other hyperplane have
+ * the same orientation
+ */
+ public boolean sameOrientationAs(final Hyperplane<Euclidean3D> other) {
+ return (((Plane) other).w).dotProduct(w) > 0.0;
+ }
+
+}
diff --git a/src/main/java/org/apache/commons/math3/geometry/euclidean/threed/PolyhedronsSet.java b/src/main/java/org/apache/commons/math3/geometry/euclidean/threed/PolyhedronsSet.java
new file mode 100644
index 0000000..f190e22
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/geometry/euclidean/threed/PolyhedronsSet.java
@@ -0,0 +1,739 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math3.geometry.euclidean.threed;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.List;
+
+import org.apache.commons.math3.exception.MathIllegalArgumentException;
+import org.apache.commons.math3.exception.NumberIsTooSmallException;
+import org.apache.commons.math3.exception.util.LocalizedFormats;
+import org.apache.commons.math3.geometry.Point;
+import org.apache.commons.math3.geometry.euclidean.oned.Euclidean1D;
+import org.apache.commons.math3.geometry.euclidean.twod.Euclidean2D;
+import org.apache.commons.math3.geometry.euclidean.twod.PolygonsSet;
+import org.apache.commons.math3.geometry.euclidean.twod.SubLine;
+import org.apache.commons.math3.geometry.euclidean.twod.Vector2D;
+import org.apache.commons.math3.geometry.partitioning.AbstractRegion;
+import org.apache.commons.math3.geometry.partitioning.BSPTree;
+import org.apache.commons.math3.geometry.partitioning.BSPTreeVisitor;
+import org.apache.commons.math3.geometry.partitioning.BoundaryAttribute;
+import org.apache.commons.math3.geometry.partitioning.Hyperplane;
+import org.apache.commons.math3.geometry.partitioning.Region;
+import org.apache.commons.math3.geometry.partitioning.RegionFactory;
+import org.apache.commons.math3.geometry.partitioning.SubHyperplane;
+import org.apache.commons.math3.geometry.partitioning.Transform;
+import org.apache.commons.math3.util.FastMath;
+
+/** This class represents a 3D region: a set of polyhedrons.
+ * @since 3.0
+ */
+public class PolyhedronsSet extends AbstractRegion<Euclidean3D, Euclidean2D> {
+
+ /** Default value for tolerance. */
+ private static final double DEFAULT_TOLERANCE = 1.0e-10;
+
+ /** Build a polyhedrons set representing the whole real line.
+ * @param tolerance tolerance below which points are considered identical
+ * @since 3.3
+ */
+ public PolyhedronsSet(final double tolerance) {
+ super(tolerance);
+ }
+
+ /** Build a polyhedrons set from a BSP tree.
+ * <p>The leaf nodes of the BSP tree <em>must</em> have a
+ * {@code Boolean} attribute representing the inside status of
+ * the corresponding cell (true for inside cells, false for outside
+ * cells). In order to avoid building too many small objects, it is
+ * recommended to use the predefined constants
+ * {@code Boolean.TRUE} and {@code Boolean.FALSE}</p>
+ * <p>
+ * This constructor is aimed at expert use, as building the tree may
+ * be a difficult task. It is not intended for general use and for
+ * performances reasons does not check thoroughly its input, as this would
+ * require walking the full tree each time. Failing to provide a tree with
+ * the proper attributes, <em>will</em> therefore generate problems like
+ * {@link NullPointerException} or {@link ClassCastException} only later on.
+ * This limitation is known and explains why this constructor is for expert
+ * use only. The caller does have the responsibility to provided correct arguments.
+ * </p>
+ * @param tree inside/outside BSP tree representing the region
+ * @param tolerance tolerance below which points are considered identical
+ * @since 3.3
+ */
+ public PolyhedronsSet(final BSPTree<Euclidean3D> tree, final double tolerance) {
+ super(tree, tolerance);
+ }
+
+ /** Build a polyhedrons set from a Boundary REPresentation (B-rep) specified by sub-hyperplanes.
+ * <p>The boundary is provided as a collection of {@link
+ * SubHyperplane sub-hyperplanes}. Each sub-hyperplane has the
+ * interior part of the region on its minus side and the exterior on
+ * its plus side.</p>
+ * <p>The boundary elements can be in any order, and can form
+ * several non-connected sets (like for example polyhedrons with holes
+ * or a set of disjoint polyhedrons considered as a whole). In
+ * fact, the elements do not even need to be connected together
+ * (their topological connections are not used here). However, if the
+ * boundary does not really separate an inside open from an outside
+ * open (open having here its topological meaning), then subsequent
+ * calls to the {@link Region#checkPoint(Point) checkPoint} method will
+ * not be meaningful anymore.</p>
+ * <p>If the boundary is empty, the region will represent the whole
+ * space.</p>
+ * @param boundary collection of boundary elements, as a
+ * collection of {@link SubHyperplane SubHyperplane} objects
+ * @param tolerance tolerance below which points are considered identical
+ * @since 3.3
+ */
+ public PolyhedronsSet(final Collection<SubHyperplane<Euclidean3D>> boundary,
+ final double tolerance) {
+ super(boundary, tolerance);
+ }
+
+ /** Build a polyhedrons set from a Boundary REPresentation (B-rep) specified by connected vertices.
+ * <p>
+ * The boundary is provided as a list of vertices and a list of facets.
+ * Each facet is specified as an integer array containing the arrays vertices
+ * indices in the vertices list. Each facet normal is oriented by right hand
+ * rule to the facet vertices list.
+ * </p>
+ * <p>
+ * Some basic sanity checks are performed but not everything is thoroughly
+ * assessed, so it remains under caller responsibility to ensure the vertices
+ * and facets are consistent and properly define a polyhedrons set.
+ * </p>
+ * @param vertices list of polyhedrons set vertices
+ * @param facets list of facets, as vertices indices in the vertices list
+ * @param tolerance tolerance below which points are considered identical
+ * @exception MathIllegalArgumentException if some basic sanity checks fail
+ * @since 3.5
+ */
+ public PolyhedronsSet(final List<Vector3D> vertices, final List<int[]> facets,
+ final double tolerance) {
+ super(buildBoundary(vertices, facets, tolerance), tolerance);
+ }
+
+ /** Build a parallellepipedic box.
+ * @param xMin low bound along the x direction
+ * @param xMax high bound along the x direction
+ * @param yMin low bound along the y direction
+ * @param yMax high bound along the y direction
+ * @param zMin low bound along the z direction
+ * @param zMax high bound along the z direction
+ * @param tolerance tolerance below which points are considered identical
+ * @since 3.3
+ */
+ public PolyhedronsSet(final double xMin, final double xMax,
+ final double yMin, final double yMax,
+ final double zMin, final double zMax,
+ final double tolerance) {
+ super(buildBoundary(xMin, xMax, yMin, yMax, zMin, zMax, tolerance), tolerance);
+ }
+
+ /** Build a polyhedrons set representing the whole real line.
+ * @deprecated as of 3.3, replaced with {@link #PolyhedronsSet(double)}
+ */
+ @Deprecated
+ public PolyhedronsSet() {
+ this(DEFAULT_TOLERANCE);
+ }
+
+ /** Build a polyhedrons set from a BSP tree.
+ * <p>The leaf nodes of the BSP tree <em>must</em> have a
+ * {@code Boolean} attribute representing the inside status of
+ * the corresponding cell (true for inside cells, false for outside
+ * cells). In order to avoid building too many small objects, it is
+ * recommended to use the predefined constants
+ * {@code Boolean.TRUE} and {@code Boolean.FALSE}</p>
+ * @param tree inside/outside BSP tree representing the region
+ * @deprecated as of 3.3, replaced with {@link #PolyhedronsSet(BSPTree, double)}
+ */
+ @Deprecated
+ public PolyhedronsSet(final BSPTree<Euclidean3D> tree) {
+ this(tree, DEFAULT_TOLERANCE);
+ }
+
+ /** Build a polyhedrons set from a Boundary REPresentation (B-rep).
+ * <p>The boundary is provided as a collection of {@link
+ * SubHyperplane sub-hyperplanes}. Each sub-hyperplane has the
+ * interior part of the region on its minus side and the exterior on
+ * its plus side.</p>
+ * <p>The boundary elements can be in any order, and can form
+ * several non-connected sets (like for example polyhedrons with holes
+ * or a set of disjoint polyhedrons considered as a whole). In
+ * fact, the elements do not even need to be connected together
+ * (their topological connections are not used here). However, if the
+ * boundary does not really separate an inside open from an outside
+ * open (open having here its topological meaning), then subsequent
+ * calls to the {@link Region#checkPoint(Point) checkPoint} method will
+ * not be meaningful anymore.</p>
+ * <p>If the boundary is empty, the region will represent the whole
+ * space.</p>
+ * @param boundary collection of boundary elements, as a
+ * collection of {@link SubHyperplane SubHyperplane} objects
+ * @deprecated as of 3.3, replaced with {@link #PolyhedronsSet(Collection, double)}
+ */
+ @Deprecated
+ public PolyhedronsSet(final Collection<SubHyperplane<Euclidean3D>> boundary) {
+ this(boundary, DEFAULT_TOLERANCE);
+ }
+
+ /** Build a parallellepipedic box.
+ * @param xMin low bound along the x direction
+ * @param xMax high bound along the x direction
+ * @param yMin low bound along the y direction
+ * @param yMax high bound along the y direction
+ * @param zMin low bound along the z direction
+ * @param zMax high bound along the z direction
+ * @deprecated as of 3.3, replaced with {@link #PolyhedronsSet(double, double,
+ * double, double, double, double, double)}
+ */
+ @Deprecated
+ public PolyhedronsSet(final double xMin, final double xMax,
+ final double yMin, final double yMax,
+ final double zMin, final double zMax) {
+ this(xMin, xMax, yMin, yMax, zMin, zMax, DEFAULT_TOLERANCE);
+ }
+
+ /** Build a parallellepipedic box boundary.
+ * @param xMin low bound along the x direction
+ * @param xMax high bound along the x direction
+ * @param yMin low bound along the y direction
+ * @param yMax high bound along the y direction
+ * @param zMin low bound along the z direction
+ * @param zMax high bound along the z direction
+ * @param tolerance tolerance below which points are considered identical
+ * @return boundary tree
+ * @since 3.3
+ */
+ private static BSPTree<Euclidean3D> buildBoundary(final double xMin, final double xMax,
+ final double yMin, final double yMax,
+ final double zMin, final double zMax,
+ final double tolerance) {
+ if ((xMin >= xMax - tolerance) || (yMin >= yMax - tolerance) || (zMin >= zMax - tolerance)) {
+ // too thin box, build an empty polygons set
+ return new BSPTree<Euclidean3D>(Boolean.FALSE);
+ }
+ final Plane pxMin = new Plane(new Vector3D(xMin, 0, 0), Vector3D.MINUS_I, tolerance);
+ final Plane pxMax = new Plane(new Vector3D(xMax, 0, 0), Vector3D.PLUS_I, tolerance);
+ final Plane pyMin = new Plane(new Vector3D(0, yMin, 0), Vector3D.MINUS_J, tolerance);
+ final Plane pyMax = new Plane(new Vector3D(0, yMax, 0), Vector3D.PLUS_J, tolerance);
+ final Plane pzMin = new Plane(new Vector3D(0, 0, zMin), Vector3D.MINUS_K, tolerance);
+ final Plane pzMax = new Plane(new Vector3D(0, 0, zMax), Vector3D.PLUS_K, tolerance);
+ @SuppressWarnings("unchecked")
+ final Region<Euclidean3D> boundary =
+ new RegionFactory<Euclidean3D>().buildConvex(pxMin, pxMax, pyMin, pyMax, pzMin, pzMax);
+ return boundary.getTree(false);
+ }
+
+ /** Build boundary from vertices and facets.
+ * @param vertices list of polyhedrons set vertices
+ * @param facets list of facets, as vertices indices in the vertices list
+ * @param tolerance tolerance below which points are considered identical
+ * @return boundary as a list of sub-hyperplanes
+ * @exception MathIllegalArgumentException if some basic sanity checks fail
+ * @since 3.5
+ */
+ private static List<SubHyperplane<Euclidean3D>> buildBoundary(final List<Vector3D> vertices,
+ final List<int[]> facets,
+ final double tolerance) {
+
+ // check vertices distances
+ for (int i = 0; i < vertices.size() - 1; ++i) {
+ final Vector3D vi = vertices.get(i);
+ for (int j = i + 1; j < vertices.size(); ++j) {
+ if (Vector3D.distance(vi, vertices.get(j)) <= tolerance) {
+ throw new MathIllegalArgumentException(LocalizedFormats.CLOSE_VERTICES,
+ vi.getX(), vi.getY(), vi.getZ());
+ }
+ }
+ }
+
+ // find how vertices are referenced by facets
+ final int[][] references = findReferences(vertices, facets);
+
+ // find how vertices are linked together by edges along the facets they belong to
+ final int[][] successors = successors(vertices, facets, references);
+
+ // check edges orientations
+ for (int vA = 0; vA < vertices.size(); ++vA) {
+ for (final int vB : successors[vA]) {
+
+ if (vB >= 0) {
+ // when facets are properly oriented, if vB is the successor of vA on facet f1,
+ // then there must be an adjacent facet f2 where vA is the successor of vB
+ boolean found = false;
+ for (final int v : successors[vB]) {
+ found = found || (v == vA);
+ }
+ if (!found) {
+ final Vector3D start = vertices.get(vA);
+ final Vector3D end = vertices.get(vB);
+ throw new MathIllegalArgumentException(LocalizedFormats.EDGE_CONNECTED_TO_ONE_FACET,
+ start.getX(), start.getY(), start.getZ(),
+ end.getX(), end.getY(), end.getZ());
+ }
+ }
+ }
+ }
+
+ final List<SubHyperplane<Euclidean3D>> boundary = new ArrayList<SubHyperplane<Euclidean3D>>();
+
+ for (final int[] facet : facets) {
+
+ // define facet plane from the first 3 points
+ Plane plane = new Plane(vertices.get(facet[0]), vertices.get(facet[1]), vertices.get(facet[2]),
+ tolerance);
+
+ // check all points are in the plane
+ final Vector2D[] two2Points = new Vector2D[facet.length];
+ for (int i = 0 ; i < facet.length; ++i) {
+ final Vector3D v = vertices.get(facet[i]);
+ if (!plane.contains(v)) {
+ throw new MathIllegalArgumentException(LocalizedFormats.OUT_OF_PLANE,
+ v.getX(), v.getY(), v.getZ());
+ }
+ two2Points[i] = plane.toSubSpace(v);
+ }
+
+ // create the polygonal facet
+ boundary.add(new SubPlane(plane, new PolygonsSet(tolerance, two2Points)));
+
+ }
+
+ return boundary;
+
+ }
+
+ /** Find the facets that reference each edges.
+ * @param vertices list of polyhedrons set vertices
+ * @param facets list of facets, as vertices indices in the vertices list
+ * @return references array such that r[v][k] = f for some k if facet f contains vertex v
+ * @exception MathIllegalArgumentException if some facets have fewer than 3 vertices
+ * @since 3.5
+ */
+ private static int[][] findReferences(final List<Vector3D> vertices, final List<int[]> facets) {
+
+ // find the maximum number of facets a vertex belongs to
+ final int[] nbFacets = new int[vertices.size()];
+ int maxFacets = 0;
+ for (final int[] facet : facets) {
+ if (facet.length < 3) {
+ throw new NumberIsTooSmallException(LocalizedFormats.WRONG_NUMBER_OF_POINTS,
+ 3, facet.length, true);
+ }
+ for (final int index : facet) {
+ maxFacets = FastMath.max(maxFacets, ++nbFacets[index]);
+ }
+ }
+
+ // set up the references array
+ final int[][] references = new int[vertices.size()][maxFacets];
+ for (int[] r : references) {
+ Arrays.fill(r, -1);
+ }
+ for (int f = 0; f < facets.size(); ++f) {
+ for (final int v : facets.get(f)) {
+ // vertex v is referenced by facet f
+ int k = 0;
+ while (k < maxFacets && references[v][k] >= 0) {
+ ++k;
+ }
+ references[v][k] = f;
+ }
+ }
+
+ return references;
+
+ }
+
+ /** Find the successors of all vertices among all facets they belong to.
+ * @param vertices list of polyhedrons set vertices
+ * @param facets list of facets, as vertices indices in the vertices list
+ * @param references facets references array
+ * @return indices of vertices that follow vertex v in some facet (the array
+ * may contain extra entries at the end, set to negative indices)
+ * @exception MathIllegalArgumentException if the same vertex appears more than
+ * once in the successors list (which means one facet orientation is wrong)
+ * @since 3.5
+ */
+ private static int[][] successors(final List<Vector3D> vertices, final List<int[]> facets,
+ final int[][] references) {
+
+ // create an array large enough
+ final int[][] successors = new int[vertices.size()][references[0].length];
+ for (final int[] s : successors) {
+ Arrays.fill(s, -1);
+ }
+
+ for (int v = 0; v < vertices.size(); ++v) {
+ for (int k = 0; k < successors[v].length && references[v][k] >= 0; ++k) {
+
+ // look for vertex v
+ final int[] facet = facets.get(references[v][k]);
+ int i = 0;
+ while (i < facet.length && facet[i] != v) {
+ ++i;
+ }
+
+ // we have found vertex v, we deduce its successor on current facet
+ successors[v][k] = facet[(i + 1) % facet.length];
+ for (int l = 0; l < k; ++l) {
+ if (successors[v][l] == successors[v][k]) {
+ final Vector3D start = vertices.get(v);
+ final Vector3D end = vertices.get(successors[v][k]);
+ throw new MathIllegalArgumentException(LocalizedFormats.FACET_ORIENTATION_MISMATCH,
+ start.getX(), start.getY(), start.getZ(),
+ end.getX(), end.getY(), end.getZ());
+ }
+ }
+
+ }
+ }
+
+ return successors;
+
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public PolyhedronsSet buildNew(final BSPTree<Euclidean3D> tree) {
+ return new PolyhedronsSet(tree, getTolerance());
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ protected void computeGeometricalProperties() {
+
+ // compute the contribution of all boundary facets
+ getTree(true).visit(new FacetsContributionVisitor());
+
+ if (getSize() < 0) {
+ // the polyhedrons set as a finite outside
+ // surrounded by an infinite inside
+ setSize(Double.POSITIVE_INFINITY);
+ setBarycenter((Point<Euclidean3D>) Vector3D.NaN);
+ } else {
+ // the polyhedrons set is finite, apply the remaining scaling factors
+ setSize(getSize() / 3.0);
+ setBarycenter((Point<Euclidean3D>) new Vector3D(1.0 / (4 * getSize()), (Vector3D) getBarycenter()));
+ }
+
+ }
+
+ /** Visitor computing geometrical properties. */
+ private class FacetsContributionVisitor implements BSPTreeVisitor<Euclidean3D> {
+
+ /** Simple constructor. */
+ FacetsContributionVisitor() {
+ setSize(0);
+ setBarycenter((Point<Euclidean3D>) new Vector3D(0, 0, 0));
+ }
+
+ /** {@inheritDoc} */
+ public Order visitOrder(final BSPTree<Euclidean3D> node) {
+ return Order.MINUS_SUB_PLUS;
+ }
+
+ /** {@inheritDoc} */
+ public void visitInternalNode(final BSPTree<Euclidean3D> node) {
+ @SuppressWarnings("unchecked")
+ final BoundaryAttribute<Euclidean3D> attribute =
+ (BoundaryAttribute<Euclidean3D>) node.getAttribute();
+ if (attribute.getPlusOutside() != null) {
+ addContribution(attribute.getPlusOutside(), false);
+ }
+ if (attribute.getPlusInside() != null) {
+ addContribution(attribute.getPlusInside(), true);
+ }
+ }
+
+ /** {@inheritDoc} */
+ public void visitLeafNode(final BSPTree<Euclidean3D> node) {
+ }
+
+ /** Add he contribution of a boundary facet.
+ * @param facet boundary facet
+ * @param reversed if true, the facet has the inside on its plus side
+ */
+ private void addContribution(final SubHyperplane<Euclidean3D> facet, final boolean reversed) {
+
+ final Region<Euclidean2D> polygon = ((SubPlane) facet).getRemainingRegion();
+ final double area = polygon.getSize();
+
+ if (Double.isInfinite(area)) {
+ setSize(Double.POSITIVE_INFINITY);
+ setBarycenter((Point<Euclidean3D>) Vector3D.NaN);
+ } else {
+
+ final Plane plane = (Plane) facet.getHyperplane();
+ final Vector3D facetB = plane.toSpace(polygon.getBarycenter());
+ double scaled = area * facetB.dotProduct(plane.getNormal());
+ if (reversed) {
+ scaled = -scaled;
+ }
+
+ setSize(getSize() + scaled);
+ setBarycenter((Point<Euclidean3D>) new Vector3D(1.0, (Vector3D) getBarycenter(), scaled, facetB));
+
+ }
+
+ }
+
+ }
+
+ /** Get the first sub-hyperplane crossed by a semi-infinite line.
+ * @param point start point of the part of the line considered
+ * @param line line to consider (contains point)
+ * @return the first sub-hyperplane crossed by the line after the
+ * given point, or null if the line does not intersect any
+ * sub-hyperplane
+ */
+ public SubHyperplane<Euclidean3D> firstIntersection(final Vector3D point, final Line line) {
+ return recurseFirstIntersection(getTree(true), point, line);
+ }
+
+ /** Get the first sub-hyperplane crossed by a semi-infinite line.
+ * @param node current node
+ * @param point start point of the part of the line considered
+ * @param line line to consider (contains point)
+ * @return the first sub-hyperplane crossed by the line after the
+ * given point, or null if the line does not intersect any
+ * sub-hyperplane
+ */
+ private SubHyperplane<Euclidean3D> recurseFirstIntersection(final BSPTree<Euclidean3D> node,
+ final Vector3D point,
+ final Line line) {
+
+ final SubHyperplane<Euclidean3D> cut = node.getCut();
+ if (cut == null) {
+ return null;
+ }
+ final BSPTree<Euclidean3D> minus = node.getMinus();
+ final BSPTree<Euclidean3D> plus = node.getPlus();
+ final Plane plane = (Plane) cut.getHyperplane();
+
+ // establish search order
+ final double offset = plane.getOffset((Point<Euclidean3D>) point);
+ final boolean in = FastMath.abs(offset) < getTolerance();
+ final BSPTree<Euclidean3D> near;
+ final BSPTree<Euclidean3D> far;
+ if (offset < 0) {
+ near = minus;
+ far = plus;
+ } else {
+ near = plus;
+ far = minus;
+ }
+
+ if (in) {
+ // search in the cut hyperplane
+ final SubHyperplane<Euclidean3D> facet = boundaryFacet(point, node);
+ if (facet != null) {
+ return facet;
+ }
+ }
+
+ // search in the near branch
+ final SubHyperplane<Euclidean3D> crossed = recurseFirstIntersection(near, point, line);
+ if (crossed != null) {
+ return crossed;
+ }
+
+ if (!in) {
+ // search in the cut hyperplane
+ final Vector3D hit3D = plane.intersection(line);
+ if (hit3D != null && line.getAbscissa(hit3D) > line.getAbscissa(point)) {
+ final SubHyperplane<Euclidean3D> facet = boundaryFacet(hit3D, node);
+ if (facet != null) {
+ return facet;
+ }
+ }
+ }
+
+ // search in the far branch
+ return recurseFirstIntersection(far, point, line);
+
+ }
+
+ /** Check if a point belongs to the boundary part of a node.
+ * @param point point to check
+ * @param node node containing the boundary facet to check
+ * @return the boundary facet this points belongs to (or null if it
+ * does not belong to any boundary facet)
+ */
+ private SubHyperplane<Euclidean3D> boundaryFacet(final Vector3D point,
+ final BSPTree<Euclidean3D> node) {
+ final Vector2D point2D = ((Plane) node.getCut().getHyperplane()).toSubSpace((Point<Euclidean3D>) point);
+ @SuppressWarnings("unchecked")
+ final BoundaryAttribute<Euclidean3D> attribute =
+ (BoundaryAttribute<Euclidean3D>) node.getAttribute();
+ if ((attribute.getPlusOutside() != null) &&
+ (((SubPlane) attribute.getPlusOutside()).getRemainingRegion().checkPoint(point2D) == Location.INSIDE)) {
+ return attribute.getPlusOutside();
+ }
+ if ((attribute.getPlusInside() != null) &&
+ (((SubPlane) attribute.getPlusInside()).getRemainingRegion().checkPoint(point2D) == Location.INSIDE)) {
+ return attribute.getPlusInside();
+ }
+ return null;
+ }
+
+ /** Rotate the region around the specified point.
+ * <p>The instance is not modified, a new instance is created.</p>
+ * @param center rotation center
+ * @param rotation vectorial rotation operator
+ * @return a new instance representing the rotated region
+ */
+ public PolyhedronsSet rotate(final Vector3D center, final Rotation rotation) {
+ return (PolyhedronsSet) applyTransform(new RotationTransform(center, rotation));
+ }
+
+ /** 3D rotation as a Transform. */
+ private static class RotationTransform implements Transform<Euclidean3D, Euclidean2D> {
+
+ /** Center point of the rotation. */
+ private Vector3D center;
+
+ /** Vectorial rotation. */
+ private Rotation rotation;
+
+ /** Cached original hyperplane. */
+ private Plane cachedOriginal;
+
+ /** Cached 2D transform valid inside the cached original hyperplane. */
+ private Transform<Euclidean2D, Euclidean1D> cachedTransform;
+
+ /** Build a rotation transform.
+ * @param center center point of the rotation
+ * @param rotation vectorial rotation
+ */
+ RotationTransform(final Vector3D center, final Rotation rotation) {
+ this.center = center;
+ this.rotation = rotation;
+ }
+
+ /** {@inheritDoc} */
+ public Vector3D apply(final Point<Euclidean3D> point) {
+ final Vector3D delta = ((Vector3D) point).subtract(center);
+ return new Vector3D(1.0, center, 1.0, rotation.applyTo(delta));
+ }
+
+ /** {@inheritDoc} */
+ public Plane apply(final Hyperplane<Euclidean3D> hyperplane) {
+ return ((Plane) hyperplane).rotate(center, rotation);
+ }
+
+ /** {@inheritDoc} */
+ public SubHyperplane<Euclidean2D> apply(final SubHyperplane<Euclidean2D> sub,
+ final Hyperplane<Euclidean3D> original,
+ final Hyperplane<Euclidean3D> transformed) {
+ if (original != cachedOriginal) {
+ // we have changed hyperplane, reset the in-hyperplane transform
+
+ final Plane oPlane = (Plane) original;
+ final Plane tPlane = (Plane) transformed;
+ final Vector3D p00 = oPlane.getOrigin();
+ final Vector3D p10 = oPlane.toSpace((Point<Euclidean2D>) new Vector2D(1.0, 0.0));
+ final Vector3D p01 = oPlane.toSpace((Point<Euclidean2D>) new Vector2D(0.0, 1.0));
+ final Vector2D tP00 = tPlane.toSubSpace((Point<Euclidean3D>) apply(p00));
+ final Vector2D tP10 = tPlane.toSubSpace((Point<Euclidean3D>) apply(p10));
+ final Vector2D tP01 = tPlane.toSubSpace((Point<Euclidean3D>) apply(p01));
+
+ cachedOriginal = (Plane) original;
+ cachedTransform =
+ org.apache.commons.math3.geometry.euclidean.twod.Line.getTransform(tP10.getX() - tP00.getX(),
+ tP10.getY() - tP00.getY(),
+ tP01.getX() - tP00.getX(),
+ tP01.getY() - tP00.getY(),
+ tP00.getX(),
+ tP00.getY());
+
+ }
+ return ((SubLine) sub).applyTransform(cachedTransform);
+ }
+
+ }
+
+ /** Translate the region by the specified amount.
+ * <p>The instance is not modified, a new instance is created.</p>
+ * @param translation translation to apply
+ * @return a new instance representing the translated region
+ */
+ public PolyhedronsSet translate(final Vector3D translation) {
+ return (PolyhedronsSet) applyTransform(new TranslationTransform(translation));
+ }
+
+ /** 3D translation as a transform. */
+ private static class TranslationTransform implements Transform<Euclidean3D, Euclidean2D> {
+
+ /** Translation vector. */
+ private Vector3D translation;
+
+ /** Cached original hyperplane. */
+ private Plane cachedOriginal;
+
+ /** Cached 2D transform valid inside the cached original hyperplane. */
+ private Transform<Euclidean2D, Euclidean1D> cachedTransform;
+
+ /** Build a translation transform.
+ * @param translation translation vector
+ */
+ TranslationTransform(final Vector3D translation) {
+ this.translation = translation;
+ }
+
+ /** {@inheritDoc} */
+ public Vector3D apply(final Point<Euclidean3D> point) {
+ return new Vector3D(1.0, (Vector3D) point, 1.0, translation);
+ }
+
+ /** {@inheritDoc} */
+ public Plane apply(final Hyperplane<Euclidean3D> hyperplane) {
+ return ((Plane) hyperplane).translate(translation);
+ }
+
+ /** {@inheritDoc} */
+ public SubHyperplane<Euclidean2D> apply(final SubHyperplane<Euclidean2D> sub,
+ final Hyperplane<Euclidean3D> original,
+ final Hyperplane<Euclidean3D> transformed) {
+ if (original != cachedOriginal) {
+ // we have changed hyperplane, reset the in-hyperplane transform
+
+ final Plane oPlane = (Plane) original;
+ final Plane tPlane = (Plane) transformed;
+ final Vector2D shift = tPlane.toSubSpace((Point<Euclidean3D>) apply(oPlane.getOrigin()));
+
+ cachedOriginal = (Plane) original;
+ cachedTransform =
+ org.apache.commons.math3.geometry.euclidean.twod.Line.getTransform(1, 0, 0, 1,
+ shift.getX(),
+ shift.getY());
+
+ }
+
+ return ((SubLine) sub).applyTransform(cachedTransform);
+
+ }
+
+ }
+
+}
diff --git a/src/main/java/org/apache/commons/math3/geometry/euclidean/threed/Rotation.java b/src/main/java/org/apache/commons/math3/geometry/euclidean/threed/Rotation.java
new file mode 100644
index 0000000..f4df3b5
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/geometry/euclidean/threed/Rotation.java
@@ -0,0 +1,1424 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math3.geometry.euclidean.threed;
+
+import java.io.Serializable;
+
+import org.apache.commons.math3.exception.MathArithmeticException;
+import org.apache.commons.math3.exception.MathIllegalArgumentException;
+import org.apache.commons.math3.exception.util.LocalizedFormats;
+import org.apache.commons.math3.util.FastMath;
+import org.apache.commons.math3.util.MathArrays;
+
+/**
+ * This class implements rotations in a three-dimensional space.
+ *
+ * <p>Rotations can be represented by several different mathematical
+ * entities (matrices, axe and angle, Cardan or Euler angles,
+ * quaternions). This class presents an higher level abstraction, more
+ * user-oriented and hiding this implementation details. Well, for the
+ * curious, we use quaternions for the internal representation. The
+ * user can build a rotation from any of these representations, and
+ * any of these representations can be retrieved from a
+ * <code>Rotation</code> instance (see the various constructors and
+ * getters). In addition, a rotation can also be built implicitly
+ * from a set of vectors and their image.</p>
+ * <p>This implies that this class can be used to convert from one
+ * representation to another one. For example, converting a rotation
+ * matrix into a set of Cardan angles from can be done using the
+ * following single line of code:</p>
+ * <pre>
+ * double[] angles = new Rotation(matrix, 1.0e-10).getAngles(RotationOrder.XYZ);
+ * </pre>
+ * <p>Focus is oriented on what a rotation <em>do</em> rather than on its
+ * underlying representation. Once it has been built, and regardless of its
+ * internal representation, a rotation is an <em>operator</em> which basically
+ * transforms three dimensional {@link Vector3D vectors} into other three
+ * dimensional {@link Vector3D vectors}. Depending on the application, the
+ * meaning of these vectors may vary and the semantics of the rotation also.</p>
+ * <p>For example in an spacecraft attitude simulation tool, users will often
+ * consider the vectors are fixed (say the Earth direction for example) and the
+ * frames change. The rotation transforms the coordinates of the vector in inertial
+ * frame into the coordinates of the same vector in satellite frame. In this
+ * case, the rotation implicitly defines the relation between the two frames.</p>
+ * <p>Another example could be a telescope control application, where the rotation
+ * would transform the sighting direction at rest into the desired observing
+ * direction when the telescope is pointed towards an object of interest. In this
+ * case the rotation transforms the direction at rest in a topocentric frame
+ * into the sighting direction in the same topocentric frame. This implies in this
+ * case the frame is fixed and the vector moves.</p>
+ * <p>In many case, both approaches will be combined. In our telescope example,
+ * we will probably also need to transform the observing direction in the topocentric
+ * frame into the observing direction in inertial frame taking into account the observatory
+ * location and the Earth rotation, which would essentially be an application of the
+ * first approach.</p>
+ *
+ * <p>These examples show that a rotation is what the user wants it to be. This
+ * class does not push the user towards one specific definition and hence does not
+ * provide methods like <code>projectVectorIntoDestinationFrame</code> or
+ * <code>computeTransformedDirection</code>. It provides simpler and more generic
+ * methods: {@link #applyTo(Vector3D) applyTo(Vector3D)} and {@link
+ * #applyInverseTo(Vector3D) applyInverseTo(Vector3D)}.</p>
+ *
+ * <p>Since a rotation is basically a vectorial operator, several rotations can be
+ * composed together and the composite operation <code>r = r<sub>1</sub> o
+ * r<sub>2</sub></code> (which means that for each vector <code>u</code>,
+ * <code>r(u) = r<sub>1</sub>(r<sub>2</sub>(u))</code>) is also a rotation. Hence
+ * we can consider that in addition to vectors, a rotation can be applied to other
+ * rotations as well (or to itself). With our previous notations, we would say we
+ * can apply <code>r<sub>1</sub></code> to <code>r<sub>2</sub></code> and the result
+ * we get is <code>r = r<sub>1</sub> o r<sub>2</sub></code>. For this purpose, the
+ * class provides the methods: {@link #applyTo(Rotation) applyTo(Rotation)} and
+ * {@link #applyInverseTo(Rotation) applyInverseTo(Rotation)}.</p>
+ *
+ * <p>Rotations are guaranteed to be immutable objects.</p>
+ *
+ * @see Vector3D
+ * @see RotationOrder
+ * @since 1.2
+ */
+
+public class Rotation implements Serializable {
+
+ /** Identity rotation. */
+ public static final Rotation IDENTITY = new Rotation(1.0, 0.0, 0.0, 0.0, false);
+
+ /** Serializable version identifier */
+ private static final long serialVersionUID = -2153622329907944313L;
+
+ /** Scalar coordinate of the quaternion. */
+ private final double q0;
+
+ /** First coordinate of the vectorial part of the quaternion. */
+ private final double q1;
+
+ /** Second coordinate of the vectorial part of the quaternion. */
+ private final double q2;
+
+ /** Third coordinate of the vectorial part of the quaternion. */
+ private final double q3;
+
+ /** Build a rotation from the quaternion coordinates.
+ * <p>A rotation can be built from a <em>normalized</em> quaternion,
+ * i.e. a quaternion for which q<sub>0</sub><sup>2</sup> +
+ * q<sub>1</sub><sup>2</sup> + q<sub>2</sub><sup>2</sup> +
+ * q<sub>3</sub><sup>2</sup> = 1. If the quaternion is not normalized,
+ * the constructor can normalize it in a preprocessing step.</p>
+ * <p>Note that some conventions put the scalar part of the quaternion
+ * as the 4<sup>th</sup> component and the vector part as the first three
+ * components. This is <em>not</em> our convention. We put the scalar part
+ * as the first component.</p>
+ * @param q0 scalar part of the quaternion
+ * @param q1 first coordinate of the vectorial part of the quaternion
+ * @param q2 second coordinate of the vectorial part of the quaternion
+ * @param q3 third coordinate of the vectorial part of the quaternion
+ * @param needsNormalization if true, the coordinates are considered
+ * not to be normalized, a normalization preprocessing step is performed
+ * before using them
+ */
+ public Rotation(double q0, double q1, double q2, double q3,
+ boolean needsNormalization) {
+
+ if (needsNormalization) {
+ // normalization preprocessing
+ double inv = 1.0 / FastMath.sqrt(q0 * q0 + q1 * q1 + q2 * q2 + q3 * q3);
+ q0 *= inv;
+ q1 *= inv;
+ q2 *= inv;
+ q3 *= inv;
+ }
+
+ this.q0 = q0;
+ this.q1 = q1;
+ this.q2 = q2;
+ this.q3 = q3;
+
+ }
+
+ /** Build a rotation from an axis and an angle.
+ * <p>
+ * Calling this constructor is equivalent to call
+ * {@link #Rotation(Vector3D, double, RotationConvention)
+ * new Rotation(axis, angle, RotationConvention.VECTOR_OPERATOR)}
+ * </p>
+ * @param axis axis around which to rotate
+ * @param angle rotation angle.
+ * @exception MathIllegalArgumentException if the axis norm is zero
+ * @deprecated as of 3.6, replaced with {@link #Rotation(Vector3D, double, RotationConvention)}
+ */
+ @Deprecated
+ public Rotation(Vector3D axis, double angle) throws MathIllegalArgumentException {
+ this(axis, angle, RotationConvention.VECTOR_OPERATOR);
+ }
+
+ /** Build a rotation from an axis and an angle.
+ * @param axis axis around which to rotate
+ * @param angle rotation angle
+ * @param convention convention to use for the semantics of the angle
+ * @exception MathIllegalArgumentException if the axis norm is zero
+ * @since 3.6
+ */
+ public Rotation(final Vector3D axis, final double angle, final RotationConvention convention)
+ throws MathIllegalArgumentException {
+
+ double norm = axis.getNorm();
+ if (norm == 0) {
+ throw new MathIllegalArgumentException(LocalizedFormats.ZERO_NORM_FOR_ROTATION_AXIS);
+ }
+
+ double halfAngle = convention == RotationConvention.VECTOR_OPERATOR ? -0.5 * angle : +0.5 * angle;
+ double coeff = FastMath.sin(halfAngle) / norm;
+
+ q0 = FastMath.cos (halfAngle);
+ q1 = coeff * axis.getX();
+ q2 = coeff * axis.getY();
+ q3 = coeff * axis.getZ();
+
+ }
+
+ /** Build a rotation from a 3X3 matrix.
+
+ * <p>Rotation matrices are orthogonal matrices, i.e. unit matrices
+ * (which are matrices for which m.m<sup>T</sup> = I) with real
+ * coefficients. The module of the determinant of unit matrices is
+ * 1, among the orthogonal 3X3 matrices, only the ones having a
+ * positive determinant (+1) are rotation matrices.</p>
+
+ * <p>When a rotation is defined by a matrix with truncated values
+ * (typically when it is extracted from a technical sheet where only
+ * four to five significant digits are available), the matrix is not
+ * orthogonal anymore. This constructor handles this case
+ * transparently by using a copy of the given matrix and applying a
+ * correction to the copy in order to perfect its orthogonality. If
+ * the Frobenius norm of the correction needed is above the given
+ * threshold, then the matrix is considered to be too far from a
+ * true rotation matrix and an exception is thrown.<p>
+
+ * @param m rotation matrix
+ * @param threshold convergence threshold for the iterative
+ * orthogonality correction (convergence is reached when the
+ * difference between two steps of the Frobenius norm of the
+ * correction is below this threshold)
+
+ * @exception NotARotationMatrixException if the matrix is not a 3X3
+ * matrix, or if it cannot be transformed into an orthogonal matrix
+ * with the given threshold, or if the determinant of the resulting
+ * orthogonal matrix is negative
+
+ */
+ public Rotation(double[][] m, double threshold)
+ throws NotARotationMatrixException {
+
+ // dimension check
+ if ((m.length != 3) || (m[0].length != 3) ||
+ (m[1].length != 3) || (m[2].length != 3)) {
+ throw new NotARotationMatrixException(
+ LocalizedFormats.ROTATION_MATRIX_DIMENSIONS,
+ m.length, m[0].length);
+ }
+
+ // compute a "close" orthogonal matrix
+ double[][] ort = orthogonalizeMatrix(m, threshold);
+
+ // check the sign of the determinant
+ double det = ort[0][0] * (ort[1][1] * ort[2][2] - ort[2][1] * ort[1][2]) -
+ ort[1][0] * (ort[0][1] * ort[2][2] - ort[2][1] * ort[0][2]) +
+ ort[2][0] * (ort[0][1] * ort[1][2] - ort[1][1] * ort[0][2]);
+ if (det < 0.0) {
+ throw new NotARotationMatrixException(
+ LocalizedFormats.CLOSEST_ORTHOGONAL_MATRIX_HAS_NEGATIVE_DETERMINANT,
+ det);
+ }
+
+ double[] quat = mat2quat(ort);
+ q0 = quat[0];
+ q1 = quat[1];
+ q2 = quat[2];
+ q3 = quat[3];
+
+ }
+
+ /** Build the rotation that transforms a pair of vectors into another pair.
+
+ * <p>Except for possible scale factors, if the instance were applied to
+ * the pair (u<sub>1</sub>, u<sub>2</sub>) it will produce the pair
+ * (v<sub>1</sub>, v<sub>2</sub>).</p>
+
+ * <p>If the angular separation between u<sub>1</sub> and u<sub>2</sub> is
+ * not the same as the angular separation between v<sub>1</sub> and
+ * v<sub>2</sub>, then a corrected v'<sub>2</sub> will be used rather than
+ * v<sub>2</sub>, the corrected vector will be in the (&pm;v<sub>1</sub>,
+ * +v<sub>2</sub>) half-plane.</p>
+
+ * @param u1 first vector of the origin pair
+ * @param u2 second vector of the origin pair
+ * @param v1 desired image of u1 by the rotation
+ * @param v2 desired image of u2 by the rotation
+ * @exception MathArithmeticException if the norm of one of the vectors is zero,
+ * or if one of the pair is degenerated (i.e. the vectors of the pair are collinear)
+ */
+ public Rotation(Vector3D u1, Vector3D u2, Vector3D v1, Vector3D v2)
+ throws MathArithmeticException {
+
+ // build orthonormalized base from u1, u2
+ // this fails when vectors are null or collinear, which is forbidden to define a rotation
+ final Vector3D u3 = u1.crossProduct(u2).normalize();
+ u2 = u3.crossProduct(u1).normalize();
+ u1 = u1.normalize();
+
+ // build an orthonormalized base from v1, v2
+ // this fails when vectors are null or collinear, which is forbidden to define a rotation
+ final Vector3D v3 = v1.crossProduct(v2).normalize();
+ v2 = v3.crossProduct(v1).normalize();
+ v1 = v1.normalize();
+
+ // buid a matrix transforming the first base into the second one
+ final double[][] m = new double[][] {
+ {
+ MathArrays.linearCombination(u1.getX(), v1.getX(), u2.getX(), v2.getX(), u3.getX(), v3.getX()),
+ MathArrays.linearCombination(u1.getY(), v1.getX(), u2.getY(), v2.getX(), u3.getY(), v3.getX()),
+ MathArrays.linearCombination(u1.getZ(), v1.getX(), u2.getZ(), v2.getX(), u3.getZ(), v3.getX())
+ },
+ {
+ MathArrays.linearCombination(u1.getX(), v1.getY(), u2.getX(), v2.getY(), u3.getX(), v3.getY()),
+ MathArrays.linearCombination(u1.getY(), v1.getY(), u2.getY(), v2.getY(), u3.getY(), v3.getY()),
+ MathArrays.linearCombination(u1.getZ(), v1.getY(), u2.getZ(), v2.getY(), u3.getZ(), v3.getY())
+ },
+ {
+ MathArrays.linearCombination(u1.getX(), v1.getZ(), u2.getX(), v2.getZ(), u3.getX(), v3.getZ()),
+ MathArrays.linearCombination(u1.getY(), v1.getZ(), u2.getY(), v2.getZ(), u3.getY(), v3.getZ()),
+ MathArrays.linearCombination(u1.getZ(), v1.getZ(), u2.getZ(), v2.getZ(), u3.getZ(), v3.getZ())
+ }
+ };
+
+ double[] quat = mat2quat(m);
+ q0 = quat[0];
+ q1 = quat[1];
+ q2 = quat[2];
+ q3 = quat[3];
+
+ }
+
+ /** Build one of the rotations that transform one vector into another one.
+
+ * <p>Except for a possible scale factor, if the instance were
+ * applied to the vector u it will produce the vector v. There is an
+ * infinite number of such rotations, this constructor choose the
+ * one with the smallest associated angle (i.e. the one whose axis
+ * is orthogonal to the (u, v) plane). If u and v are collinear, an
+ * arbitrary rotation axis is chosen.</p>
+
+ * @param u origin vector
+ * @param v desired image of u by the rotation
+ * @exception MathArithmeticException if the norm of one of the vectors is zero
+ */
+ public Rotation(Vector3D u, Vector3D v) throws MathArithmeticException {
+
+ double normProduct = u.getNorm() * v.getNorm();
+ if (normProduct == 0) {
+ throw new MathArithmeticException(LocalizedFormats.ZERO_NORM_FOR_ROTATION_DEFINING_VECTOR);
+ }
+
+ double dot = u.dotProduct(v);
+
+ if (dot < ((2.0e-15 - 1.0) * normProduct)) {
+ // special case u = -v: we select a PI angle rotation around
+ // an arbitrary vector orthogonal to u
+ Vector3D w = u.orthogonal();
+ q0 = 0.0;
+ q1 = -w.getX();
+ q2 = -w.getY();
+ q3 = -w.getZ();
+ } else {
+ // general case: (u, v) defines a plane, we select
+ // the shortest possible rotation: axis orthogonal to this plane
+ q0 = FastMath.sqrt(0.5 * (1.0 + dot / normProduct));
+ double coeff = 1.0 / (2.0 * q0 * normProduct);
+ Vector3D q = v.crossProduct(u);
+ q1 = coeff * q.getX();
+ q2 = coeff * q.getY();
+ q3 = coeff * q.getZ();
+ }
+
+ }
+
+ /** Build a rotation from three Cardan or Euler elementary rotations.
+
+ * <p>
+ * Calling this constructor is equivalent to call
+ * {@link #Rotation(RotationOrder, RotationConvention, double, double, double)
+ * new Rotation(order, RotationConvention.VECTOR_OPERATOR, alpha1, alpha2, alpha3)}
+ * </p>
+
+ * @param order order of rotations to use
+ * @param alpha1 angle of the first elementary rotation
+ * @param alpha2 angle of the second elementary rotation
+ * @param alpha3 angle of the third elementary rotation
+ * @deprecated as of 3.6, replaced with {@link
+ * #Rotation(RotationOrder, RotationConvention, double, double, double)}
+ */
+ @Deprecated
+ public Rotation(RotationOrder order,
+ double alpha1, double alpha2, double alpha3) {
+ this(order, RotationConvention.VECTOR_OPERATOR, alpha1, alpha2, alpha3);
+ }
+
+ /** Build a rotation from three Cardan or Euler elementary rotations.
+
+ * <p>Cardan rotations are three successive rotations around the
+ * canonical axes X, Y and Z, each axis being used once. There are
+ * 6 such sets of rotations (XYZ, XZY, YXZ, YZX, ZXY and ZYX). Euler
+ * rotations are three successive rotations around the canonical
+ * axes X, Y and Z, the first and last rotations being around the
+ * same axis. There are 6 such sets of rotations (XYX, XZX, YXY,
+ * YZY, ZXZ and ZYZ), the most popular one being ZXZ.</p>
+ * <p>Beware that many people routinely use the term Euler angles even
+ * for what really are Cardan angles (this confusion is especially
+ * widespread in the aerospace business where Roll, Pitch and Yaw angles
+ * are often wrongly tagged as Euler angles).</p>
+
+ * @param order order of rotations to compose, from left to right
+ * (i.e. we will use {@code r1.compose(r2.compose(r3, convention), convention)})
+ * @param convention convention to use for the semantics of the angle
+ * @param alpha1 angle of the first elementary rotation
+ * @param alpha2 angle of the second elementary rotation
+ * @param alpha3 angle of the third elementary rotation
+ * @since 3.6
+ */
+ public Rotation(RotationOrder order, RotationConvention convention,
+ double alpha1, double alpha2, double alpha3) {
+ Rotation r1 = new Rotation(order.getA1(), alpha1, convention);
+ Rotation r2 = new Rotation(order.getA2(), alpha2, convention);
+ Rotation r3 = new Rotation(order.getA3(), alpha3, convention);
+ Rotation composed = r1.compose(r2.compose(r3, convention), convention);
+ q0 = composed.q0;
+ q1 = composed.q1;
+ q2 = composed.q2;
+ q3 = composed.q3;
+ }
+
+ /** Convert an orthogonal rotation matrix to a quaternion.
+ * @param ort orthogonal rotation matrix
+ * @return quaternion corresponding to the matrix
+ */
+ private static double[] mat2quat(final double[][] ort) {
+
+ final double[] quat = new double[4];
+
+ // There are different ways to compute the quaternions elements
+ // from the matrix. They all involve computing one element from
+ // the diagonal of the matrix, and computing the three other ones
+ // using a formula involving a division by the first element,
+ // which unfortunately can be zero. Since the norm of the
+ // quaternion is 1, we know at least one element has an absolute
+ // value greater or equal to 0.5, so it is always possible to
+ // select the right formula and avoid division by zero and even
+ // numerical inaccuracy. Checking the elements in turn and using
+ // the first one greater than 0.45 is safe (this leads to a simple
+ // test since qi = 0.45 implies 4 qi^2 - 1 = -0.19)
+ double s = ort[0][0] + ort[1][1] + ort[2][2];
+ if (s > -0.19) {
+ // compute q0 and deduce q1, q2 and q3
+ quat[0] = 0.5 * FastMath.sqrt(s + 1.0);
+ double inv = 0.25 / quat[0];
+ quat[1] = inv * (ort[1][2] - ort[2][1]);
+ quat[2] = inv * (ort[2][0] - ort[0][2]);
+ quat[3] = inv * (ort[0][1] - ort[1][0]);
+ } else {
+ s = ort[0][0] - ort[1][1] - ort[2][2];
+ if (s > -0.19) {
+ // compute q1 and deduce q0, q2 and q3
+ quat[1] = 0.5 * FastMath.sqrt(s + 1.0);
+ double inv = 0.25 / quat[1];
+ quat[0] = inv * (ort[1][2] - ort[2][1]);
+ quat[2] = inv * (ort[0][1] + ort[1][0]);
+ quat[3] = inv * (ort[0][2] + ort[2][0]);
+ } else {
+ s = ort[1][1] - ort[0][0] - ort[2][2];
+ if (s > -0.19) {
+ // compute q2 and deduce q0, q1 and q3
+ quat[2] = 0.5 * FastMath.sqrt(s + 1.0);
+ double inv = 0.25 / quat[2];
+ quat[0] = inv * (ort[2][0] - ort[0][2]);
+ quat[1] = inv * (ort[0][1] + ort[1][0]);
+ quat[3] = inv * (ort[2][1] + ort[1][2]);
+ } else {
+ // compute q3 and deduce q0, q1 and q2
+ s = ort[2][2] - ort[0][0] - ort[1][1];
+ quat[3] = 0.5 * FastMath.sqrt(s + 1.0);
+ double inv = 0.25 / quat[3];
+ quat[0] = inv * (ort[0][1] - ort[1][0]);
+ quat[1] = inv * (ort[0][2] + ort[2][0]);
+ quat[2] = inv * (ort[2][1] + ort[1][2]);
+ }
+ }
+ }
+
+ return quat;
+
+ }
+
+ /** Revert a rotation.
+ * Build a rotation which reverse the effect of another
+ * rotation. This means that if r(u) = v, then r.revert(v) = u. The
+ * instance is not changed.
+ * @return a new rotation whose effect is the reverse of the effect
+ * of the instance
+ */
+ public Rotation revert() {
+ return new Rotation(-q0, q1, q2, q3, false);
+ }
+
+ /** Get the scalar coordinate of the quaternion.
+ * @return scalar coordinate of the quaternion
+ */
+ public double getQ0() {
+ return q0;
+ }
+
+ /** Get the first coordinate of the vectorial part of the quaternion.
+ * @return first coordinate of the vectorial part of the quaternion
+ */
+ public double getQ1() {
+ return q1;
+ }
+
+ /** Get the second coordinate of the vectorial part of the quaternion.
+ * @return second coordinate of the vectorial part of the quaternion
+ */
+ public double getQ2() {
+ return q2;
+ }
+
+ /** Get the third coordinate of the vectorial part of the quaternion.
+ * @return third coordinate of the vectorial part of the quaternion
+ */
+ public double getQ3() {
+ return q3;
+ }
+
+ /** Get the normalized axis of the rotation.
+ * <p>
+ * Calling this method is equivalent to call
+ * {@link #getAxis(RotationConvention) getAxis(RotationConvention.VECTOR_OPERATOR)}
+ * </p>
+ * @return normalized axis of the rotation
+ * @see #Rotation(Vector3D, double, RotationConvention)
+ * @deprecated as of 3.6, replaced with {@link #getAxis(RotationConvention)}
+ */
+ @Deprecated
+ public Vector3D getAxis() {
+ return getAxis(RotationConvention.VECTOR_OPERATOR);
+ }
+
+ /** Get the normalized axis of the rotation.
+ * <p>
+ * Note that as {@link #getAngle()} always returns an angle
+ * between 0 and &pi;, changing the convention changes the
+ * direction of the axis, not the sign of the angle.
+ * </p>
+ * @param convention convention to use for the semantics of the angle
+ * @return normalized axis of the rotation
+ * @see #Rotation(Vector3D, double, RotationConvention)
+ * @since 3.6
+ */
+ public Vector3D getAxis(final RotationConvention convention) {
+ final double squaredSine = q1 * q1 + q2 * q2 + q3 * q3;
+ if (squaredSine == 0) {
+ return convention == RotationConvention.VECTOR_OPERATOR ? Vector3D.PLUS_I : Vector3D.MINUS_I;
+ } else {
+ final double sgn = convention == RotationConvention.VECTOR_OPERATOR ? +1 : -1;
+ if (q0 < 0) {
+ final double inverse = sgn / FastMath.sqrt(squaredSine);
+ return new Vector3D(q1 * inverse, q2 * inverse, q3 * inverse);
+ }
+ final double inverse = -sgn / FastMath.sqrt(squaredSine);
+ return new Vector3D(q1 * inverse, q2 * inverse, q3 * inverse);
+ }
+ }
+
+ /** Get the angle of the rotation.
+ * @return angle of the rotation (between 0 and &pi;)
+ * @see #Rotation(Vector3D, double)
+ */
+ public double getAngle() {
+ if ((q0 < -0.1) || (q0 > 0.1)) {
+ return 2 * FastMath.asin(FastMath.sqrt(q1 * q1 + q2 * q2 + q3 * q3));
+ } else if (q0 < 0) {
+ return 2 * FastMath.acos(-q0);
+ }
+ return 2 * FastMath.acos(q0);
+ }
+
+ /** Get the Cardan or Euler angles corresponding to the instance.
+
+ * <p>
+ * Calling this method is equivalent to call
+ * {@link #getAngles(RotationOrder, RotationConvention)
+ * getAngles(order, RotationConvention.VECTOR_OPERATOR)}
+ * </p>
+
+ * @param order rotation order to use
+ * @return an array of three angles, in the order specified by the set
+ * @exception CardanEulerSingularityException if the rotation is
+ * singular with respect to the angles set specified
+ * @deprecated as of 3.6, replaced with {@link #getAngles(RotationOrder, RotationConvention)}
+ */
+ @Deprecated
+ public double[] getAngles(RotationOrder order)
+ throws CardanEulerSingularityException {
+ return getAngles(order, RotationConvention.VECTOR_OPERATOR);
+ }
+
+ /** Get the Cardan or Euler angles corresponding to the instance.
+
+ * <p>The equations show that each rotation can be defined by two
+ * different values of the Cardan or Euler angles set. For example
+ * if Cardan angles are used, the rotation defined by the angles
+ * a<sub>1</sub>, a<sub>2</sub> and a<sub>3</sub> is the same as
+ * the rotation defined by the angles &pi; + a<sub>1</sub>, &pi;
+ * - a<sub>2</sub> and &pi; + a<sub>3</sub>. This method implements
+ * the following arbitrary choices:</p>
+ * <ul>
+ * <li>for Cardan angles, the chosen set is the one for which the
+ * second angle is between -&pi;/2 and &pi;/2 (i.e its cosine is
+ * positive),</li>
+ * <li>for Euler angles, the chosen set is the one for which the
+ * second angle is between 0 and &pi; (i.e its sine is positive).</li>
+ * </ul>
+
+ * <p>Cardan and Euler angle have a very disappointing drawback: all
+ * of them have singularities. This means that if the instance is
+ * too close to the singularities corresponding to the given
+ * rotation order, it will be impossible to retrieve the angles. For
+ * Cardan angles, this is often called gimbal lock. There is
+ * <em>nothing</em> to do to prevent this, it is an intrinsic problem
+ * with Cardan and Euler representation (but not a problem with the
+ * rotation itself, which is perfectly well defined). For Cardan
+ * angles, singularities occur when the second angle is close to
+ * -&pi;/2 or +&pi;/2, for Euler angle singularities occur when the
+ * second angle is close to 0 or &pi;, this implies that the identity
+ * rotation is always singular for Euler angles!</p>
+
+ * @param order rotation order to use
+ * @param convention convention to use for the semantics of the angle
+ * @return an array of three angles, in the order specified by the set
+ * @exception CardanEulerSingularityException if the rotation is
+ * singular with respect to the angles set specified
+ * @since 3.6
+ */
+ public double[] getAngles(RotationOrder order, RotationConvention convention)
+ throws CardanEulerSingularityException {
+
+ if (convention == RotationConvention.VECTOR_OPERATOR) {
+ if (order == RotationOrder.XYZ) {
+
+ // r (Vector3D.plusK) coordinates are :
+ // sin (theta), -cos (theta) sin (phi), cos (theta) cos (phi)
+ // (-r) (Vector3D.plusI) coordinates are :
+ // cos (psi) cos (theta), -sin (psi) cos (theta), sin (theta)
+ // and we can choose to have theta in the interval [-PI/2 ; +PI/2]
+ Vector3D v1 = applyTo(Vector3D.PLUS_K);
+ Vector3D v2 = applyInverseTo(Vector3D.PLUS_I);
+ if ((v2.getZ() < -0.9999999999) || (v2.getZ() > 0.9999999999)) {
+ throw new CardanEulerSingularityException(true);
+ }
+ return new double[] {
+ FastMath.atan2(-(v1.getY()), v1.getZ()),
+ FastMath.asin(v2.getZ()),
+ FastMath.atan2(-(v2.getY()), v2.getX())
+ };
+
+ } else if (order == RotationOrder.XZY) {
+
+ // r (Vector3D.plusJ) coordinates are :
+ // -sin (psi), cos (psi) cos (phi), cos (psi) sin (phi)
+ // (-r) (Vector3D.plusI) coordinates are :
+ // cos (theta) cos (psi), -sin (psi), sin (theta) cos (psi)
+ // and we can choose to have psi in the interval [-PI/2 ; +PI/2]
+ Vector3D v1 = applyTo(Vector3D.PLUS_J);
+ Vector3D v2 = applyInverseTo(Vector3D.PLUS_I);
+ if ((v2.getY() < -0.9999999999) || (v2.getY() > 0.9999999999)) {
+ throw new CardanEulerSingularityException(true);
+ }
+ return new double[] {
+ FastMath.atan2(v1.getZ(), v1.getY()),
+ -FastMath.asin(v2.getY()),
+ FastMath.atan2(v2.getZ(), v2.getX())
+ };
+
+ } else if (order == RotationOrder.YXZ) {
+
+ // r (Vector3D.plusK) coordinates are :
+ // cos (phi) sin (theta), -sin (phi), cos (phi) cos (theta)
+ // (-r) (Vector3D.plusJ) coordinates are :
+ // sin (psi) cos (phi), cos (psi) cos (phi), -sin (phi)
+ // and we can choose to have phi in the interval [-PI/2 ; +PI/2]
+ Vector3D v1 = applyTo(Vector3D.PLUS_K);
+ Vector3D v2 = applyInverseTo(Vector3D.PLUS_J);
+ if ((v2.getZ() < -0.9999999999) || (v2.getZ() > 0.9999999999)) {
+ throw new CardanEulerSingularityException(true);
+ }
+ return new double[] {
+ FastMath.atan2(v1.getX(), v1.getZ()),
+ -FastMath.asin(v2.getZ()),
+ FastMath.atan2(v2.getX(), v2.getY())
+ };
+
+ } else if (order == RotationOrder.YZX) {
+
+ // r (Vector3D.plusI) coordinates are :
+ // cos (psi) cos (theta), sin (psi), -cos (psi) sin (theta)
+ // (-r) (Vector3D.plusJ) coordinates are :
+ // sin (psi), cos (phi) cos (psi), -sin (phi) cos (psi)
+ // and we can choose to have psi in the interval [-PI/2 ; +PI/2]
+ Vector3D v1 = applyTo(Vector3D.PLUS_I);
+ Vector3D v2 = applyInverseTo(Vector3D.PLUS_J);
+ if ((v2.getX() < -0.9999999999) || (v2.getX() > 0.9999999999)) {
+ throw new CardanEulerSingularityException(true);
+ }
+ return new double[] {
+ FastMath.atan2(-(v1.getZ()), v1.getX()),
+ FastMath.asin(v2.getX()),
+ FastMath.atan2(-(v2.getZ()), v2.getY())
+ };
+
+ } else if (order == RotationOrder.ZXY) {
+
+ // r (Vector3D.plusJ) coordinates are :
+ // -cos (phi) sin (psi), cos (phi) cos (psi), sin (phi)
+ // (-r) (Vector3D.plusK) coordinates are :
+ // -sin (theta) cos (phi), sin (phi), cos (theta) cos (phi)
+ // and we can choose to have phi in the interval [-PI/2 ; +PI/2]
+ Vector3D v1 = applyTo(Vector3D.PLUS_J);
+ Vector3D v2 = applyInverseTo(Vector3D.PLUS_K);
+ if ((v2.getY() < -0.9999999999) || (v2.getY() > 0.9999999999)) {
+ throw new CardanEulerSingularityException(true);
+ }
+ return new double[] {
+ FastMath.atan2(-(v1.getX()), v1.getY()),
+ FastMath.asin(v2.getY()),
+ FastMath.atan2(-(v2.getX()), v2.getZ())
+ };
+
+ } else if (order == RotationOrder.ZYX) {
+
+ // r (Vector3D.plusI) coordinates are :
+ // cos (theta) cos (psi), cos (theta) sin (psi), -sin (theta)
+ // (-r) (Vector3D.plusK) coordinates are :
+ // -sin (theta), sin (phi) cos (theta), cos (phi) cos (theta)
+ // and we can choose to have theta in the interval [-PI/2 ; +PI/2]
+ Vector3D v1 = applyTo(Vector3D.PLUS_I);
+ Vector3D v2 = applyInverseTo(Vector3D.PLUS_K);
+ if ((v2.getX() < -0.9999999999) || (v2.getX() > 0.9999999999)) {
+ throw new CardanEulerSingularityException(true);
+ }
+ return new double[] {
+ FastMath.atan2(v1.getY(), v1.getX()),
+ -FastMath.asin(v2.getX()),
+ FastMath.atan2(v2.getY(), v2.getZ())
+ };
+
+ } else if (order == RotationOrder.XYX) {
+
+ // r (Vector3D.plusI) coordinates are :
+ // cos (theta), sin (phi1) sin (theta), -cos (phi1) sin (theta)
+ // (-r) (Vector3D.plusI) coordinates are :
+ // cos (theta), sin (theta) sin (phi2), sin (theta) cos (phi2)
+ // and we can choose to have theta in the interval [0 ; PI]
+ Vector3D v1 = applyTo(Vector3D.PLUS_I);
+ Vector3D v2 = applyInverseTo(Vector3D.PLUS_I);
+ if ((v2.getX() < -0.9999999999) || (v2.getX() > 0.9999999999)) {
+ throw new CardanEulerSingularityException(false);
+ }
+ return new double[] {
+ FastMath.atan2(v1.getY(), -v1.getZ()),
+ FastMath.acos(v2.getX()),
+ FastMath.atan2(v2.getY(), v2.getZ())
+ };
+
+ } else if (order == RotationOrder.XZX) {
+
+ // r (Vector3D.plusI) coordinates are :
+ // cos (psi), cos (phi1) sin (psi), sin (phi1) sin (psi)
+ // (-r) (Vector3D.plusI) coordinates are :
+ // cos (psi), -sin (psi) cos (phi2), sin (psi) sin (phi2)
+ // and we can choose to have psi in the interval [0 ; PI]
+ Vector3D v1 = applyTo(Vector3D.PLUS_I);
+ Vector3D v2 = applyInverseTo(Vector3D.PLUS_I);
+ if ((v2.getX() < -0.9999999999) || (v2.getX() > 0.9999999999)) {
+ throw new CardanEulerSingularityException(false);
+ }
+ return new double[] {
+ FastMath.atan2(v1.getZ(), v1.getY()),
+ FastMath.acos(v2.getX()),
+ FastMath.atan2(v2.getZ(), -v2.getY())
+ };
+
+ } else if (order == RotationOrder.YXY) {
+
+ // r (Vector3D.plusJ) coordinates are :
+ // sin (theta1) sin (phi), cos (phi), cos (theta1) sin (phi)
+ // (-r) (Vector3D.plusJ) coordinates are :
+ // sin (phi) sin (theta2), cos (phi), -sin (phi) cos (theta2)
+ // and we can choose to have phi in the interval [0 ; PI]
+ Vector3D v1 = applyTo(Vector3D.PLUS_J);
+ Vector3D v2 = applyInverseTo(Vector3D.PLUS_J);
+ if ((v2.getY() < -0.9999999999) || (v2.getY() > 0.9999999999)) {
+ throw new CardanEulerSingularityException(false);
+ }
+ return new double[] {
+ FastMath.atan2(v1.getX(), v1.getZ()),
+ FastMath.acos(v2.getY()),
+ FastMath.atan2(v2.getX(), -v2.getZ())
+ };
+
+ } else if (order == RotationOrder.YZY) {
+
+ // r (Vector3D.plusJ) coordinates are :
+ // -cos (theta1) sin (psi), cos (psi), sin (theta1) sin (psi)
+ // (-r) (Vector3D.plusJ) coordinates are :
+ // sin (psi) cos (theta2), cos (psi), sin (psi) sin (theta2)
+ // and we can choose to have psi in the interval [0 ; PI]
+ Vector3D v1 = applyTo(Vector3D.PLUS_J);
+ Vector3D v2 = applyInverseTo(Vector3D.PLUS_J);
+ if ((v2.getY() < -0.9999999999) || (v2.getY() > 0.9999999999)) {
+ throw new CardanEulerSingularityException(false);
+ }
+ return new double[] {
+ FastMath.atan2(v1.getZ(), -v1.getX()),
+ FastMath.acos(v2.getY()),
+ FastMath.atan2(v2.getZ(), v2.getX())
+ };
+
+ } else if (order == RotationOrder.ZXZ) {
+
+ // r (Vector3D.plusK) coordinates are :
+ // sin (psi1) sin (phi), -cos (psi1) sin (phi), cos (phi)
+ // (-r) (Vector3D.plusK) coordinates are :
+ // sin (phi) sin (psi2), sin (phi) cos (psi2), cos (phi)
+ // and we can choose to have phi in the interval [0 ; PI]
+ Vector3D v1 = applyTo(Vector3D.PLUS_K);
+ Vector3D v2 = applyInverseTo(Vector3D.PLUS_K);
+ if ((v2.getZ() < -0.9999999999) || (v2.getZ() > 0.9999999999)) {
+ throw new CardanEulerSingularityException(false);
+ }
+ return new double[] {
+ FastMath.atan2(v1.getX(), -v1.getY()),
+ FastMath.acos(v2.getZ()),
+ FastMath.atan2(v2.getX(), v2.getY())
+ };
+
+ } else { // last possibility is ZYZ
+
+ // r (Vector3D.plusK) coordinates are :
+ // cos (psi1) sin (theta), sin (psi1) sin (theta), cos (theta)
+ // (-r) (Vector3D.plusK) coordinates are :
+ // -sin (theta) cos (psi2), sin (theta) sin (psi2), cos (theta)
+ // and we can choose to have theta in the interval [0 ; PI]
+ Vector3D v1 = applyTo(Vector3D.PLUS_K);
+ Vector3D v2 = applyInverseTo(Vector3D.PLUS_K);
+ if ((v2.getZ() < -0.9999999999) || (v2.getZ() > 0.9999999999)) {
+ throw new CardanEulerSingularityException(false);
+ }
+ return new double[] {
+ FastMath.atan2(v1.getY(), v1.getX()),
+ FastMath.acos(v2.getZ()),
+ FastMath.atan2(v2.getY(), -v2.getX())
+ };
+
+ }
+ } else {
+ if (order == RotationOrder.XYZ) {
+
+ // r (Vector3D.plusI) coordinates are :
+ // cos (theta) cos (psi), -cos (theta) sin (psi), sin (theta)
+ // (-r) (Vector3D.plusK) coordinates are :
+ // sin (theta), -sin (phi) cos (theta), cos (phi) cos (theta)
+ // and we can choose to have theta in the interval [-PI/2 ; +PI/2]
+ Vector3D v1 = applyTo(Vector3D.PLUS_I);
+ Vector3D v2 = applyInverseTo(Vector3D.PLUS_K);
+ if ((v2.getX() < -0.9999999999) || (v2.getX() > 0.9999999999)) {
+ throw new CardanEulerSingularityException(true);
+ }
+ return new double[] {
+ FastMath.atan2(-v2.getY(), v2.getZ()),
+ FastMath.asin(v2.getX()),
+ FastMath.atan2(-v1.getY(), v1.getX())
+ };
+
+ } else if (order == RotationOrder.XZY) {
+
+ // r (Vector3D.plusI) coordinates are :
+ // cos (psi) cos (theta), -sin (psi), cos (psi) sin (theta)
+ // (-r) (Vector3D.plusJ) coordinates are :
+ // -sin (psi), cos (phi) cos (psi), sin (phi) cos (psi)
+ // and we can choose to have psi in the interval [-PI/2 ; +PI/2]
+ Vector3D v1 = applyTo(Vector3D.PLUS_I);
+ Vector3D v2 = applyInverseTo(Vector3D.PLUS_J);
+ if ((v2.getX() < -0.9999999999) || (v2.getX() > 0.9999999999)) {
+ throw new CardanEulerSingularityException(true);
+ }
+ return new double[] {
+ FastMath.atan2(v2.getZ(), v2.getY()),
+ -FastMath.asin(v2.getX()),
+ FastMath.atan2(v1.getZ(), v1.getX())
+ };
+
+ } else if (order == RotationOrder.YXZ) {
+
+ // r (Vector3D.plusJ) coordinates are :
+ // cos (phi) sin (psi), cos (phi) cos (psi), -sin (phi)
+ // (-r) (Vector3D.plusK) coordinates are :
+ // sin (theta) cos (phi), -sin (phi), cos (theta) cos (phi)
+ // and we can choose to have phi in the interval [-PI/2 ; +PI/2]
+ Vector3D v1 = applyTo(Vector3D.PLUS_J);
+ Vector3D v2 = applyInverseTo(Vector3D.PLUS_K);
+ if ((v2.getY() < -0.9999999999) || (v2.getY() > 0.9999999999)) {
+ throw new CardanEulerSingularityException(true);
+ }
+ return new double[] {
+ FastMath.atan2(v2.getX(), v2.getZ()),
+ -FastMath.asin(v2.getY()),
+ FastMath.atan2(v1.getX(), v1.getY())
+ };
+
+ } else if (order == RotationOrder.YZX) {
+
+ // r (Vector3D.plusJ) coordinates are :
+ // sin (psi), cos (psi) cos (phi), -cos (psi) sin (phi)
+ // (-r) (Vector3D.plusI) coordinates are :
+ // cos (theta) cos (psi), sin (psi), -sin (theta) cos (psi)
+ // and we can choose to have psi in the interval [-PI/2 ; +PI/2]
+ Vector3D v1 = applyTo(Vector3D.PLUS_J);
+ Vector3D v2 = applyInverseTo(Vector3D.PLUS_I);
+ if ((v2.getY() < -0.9999999999) || (v2.getY() > 0.9999999999)) {
+ throw new CardanEulerSingularityException(true);
+ }
+ return new double[] {
+ FastMath.atan2(-v2.getZ(), v2.getX()),
+ FastMath.asin(v2.getY()),
+ FastMath.atan2(-v1.getZ(), v1.getY())
+ };
+
+ } else if (order == RotationOrder.ZXY) {
+
+ // r (Vector3D.plusK) coordinates are :
+ // -cos (phi) sin (theta), sin (phi), cos (phi) cos (theta)
+ // (-r) (Vector3D.plusJ) coordinates are :
+ // -sin (psi) cos (phi), cos (psi) cos (phi), sin (phi)
+ // and we can choose to have phi in the interval [-PI/2 ; +PI/2]
+ Vector3D v1 = applyTo(Vector3D.PLUS_K);
+ Vector3D v2 = applyInverseTo(Vector3D.PLUS_J);
+ if ((v2.getZ() < -0.9999999999) || (v2.getZ() > 0.9999999999)) {
+ throw new CardanEulerSingularityException(true);
+ }
+ return new double[] {
+ FastMath.atan2(-v2.getX(), v2.getY()),
+ FastMath.asin(v2.getZ()),
+ FastMath.atan2(-v1.getX(), v1.getZ())
+ };
+
+ } else if (order == RotationOrder.ZYX) {
+
+ // r (Vector3D.plusK) coordinates are :
+ // -sin (theta), cos (theta) sin (phi), cos (theta) cos (phi)
+ // (-r) (Vector3D.plusI) coordinates are :
+ // cos (psi) cos (theta), sin (psi) cos (theta), -sin (theta)
+ // and we can choose to have theta in the interval [-PI/2 ; +PI/2]
+ Vector3D v1 = applyTo(Vector3D.PLUS_K);
+ Vector3D v2 = applyInverseTo(Vector3D.PLUS_I);
+ if ((v2.getZ() < -0.9999999999) || (v2.getZ() > 0.9999999999)) {
+ throw new CardanEulerSingularityException(true);
+ }
+ return new double[] {
+ FastMath.atan2(v2.getY(), v2.getX()),
+ -FastMath.asin(v2.getZ()),
+ FastMath.atan2(v1.getY(), v1.getZ())
+ };
+
+ } else if (order == RotationOrder.XYX) {
+
+ // r (Vector3D.plusI) coordinates are :
+ // cos (theta), sin (phi2) sin (theta), cos (phi2) sin (theta)
+ // (-r) (Vector3D.plusI) coordinates are :
+ // cos (theta), sin (theta) sin (phi1), -sin (theta) cos (phi1)
+ // and we can choose to have theta in the interval [0 ; PI]
+ Vector3D v1 = applyTo(Vector3D.PLUS_I);
+ Vector3D v2 = applyInverseTo(Vector3D.PLUS_I);
+ if ((v2.getX() < -0.9999999999) || (v2.getX() > 0.9999999999)) {
+ throw new CardanEulerSingularityException(false);
+ }
+ return new double[] {
+ FastMath.atan2(v2.getY(), -v2.getZ()),
+ FastMath.acos(v2.getX()),
+ FastMath.atan2(v1.getY(), v1.getZ())
+ };
+
+ } else if (order == RotationOrder.XZX) {
+
+ // r (Vector3D.plusI) coordinates are :
+ // cos (psi), -cos (phi2) sin (psi), sin (phi2) sin (psi)
+ // (-r) (Vector3D.plusI) coordinates are :
+ // cos (psi), sin (psi) cos (phi1), sin (psi) sin (phi1)
+ // and we can choose to have psi in the interval [0 ; PI]
+ Vector3D v1 = applyTo(Vector3D.PLUS_I);
+ Vector3D v2 = applyInverseTo(Vector3D.PLUS_I);
+ if ((v2.getX() < -0.9999999999) || (v2.getX() > 0.9999999999)) {
+ throw new CardanEulerSingularityException(false);
+ }
+ return new double[] {
+ FastMath.atan2(v2.getZ(), v2.getY()),
+ FastMath.acos(v2.getX()),
+ FastMath.atan2(v1.getZ(), -v1.getY())
+ };
+
+ } else if (order == RotationOrder.YXY) {
+
+ // r (Vector3D.plusJ) coordinates are :
+ // sin (phi) sin (theta2), cos (phi), -sin (phi) cos (theta2)
+ // (-r) (Vector3D.plusJ) coordinates are :
+ // sin (theta1) sin (phi), cos (phi), cos (theta1) sin (phi)
+ // and we can choose to have phi in the interval [0 ; PI]
+ Vector3D v1 = applyTo(Vector3D.PLUS_J);
+ Vector3D v2 = applyInverseTo(Vector3D.PLUS_J);
+ if ((v2.getY() < -0.9999999999) || (v2.getY() > 0.9999999999)) {
+ throw new CardanEulerSingularityException(false);
+ }
+ return new double[] {
+ FastMath.atan2(v2.getX(), v2.getZ()),
+ FastMath.acos(v2.getY()),
+ FastMath.atan2(v1.getX(), -v1.getZ())
+ };
+
+ } else if (order == RotationOrder.YZY) {
+
+ // r (Vector3D.plusJ) coordinates are :
+ // sin (psi) cos (theta2), cos (psi), sin (psi) sin (theta2)
+ // (-r) (Vector3D.plusJ) coordinates are :
+ // -cos (theta1) sin (psi), cos (psi), sin (theta1) sin (psi)
+ // and we can choose to have psi in the interval [0 ; PI]
+ Vector3D v1 = applyTo(Vector3D.PLUS_J);
+ Vector3D v2 = applyInverseTo(Vector3D.PLUS_J);
+ if ((v2.getY() < -0.9999999999) || (v2.getY() > 0.9999999999)) {
+ throw new CardanEulerSingularityException(false);
+ }
+ return new double[] {
+ FastMath.atan2(v2.getZ(), -v2.getX()),
+ FastMath.acos(v2.getY()),
+ FastMath.atan2(v1.getZ(), v1.getX())
+ };
+
+ } else if (order == RotationOrder.ZXZ) {
+
+ // r (Vector3D.plusK) coordinates are :
+ // sin (phi) sin (psi2), sin (phi) cos (psi2), cos (phi)
+ // (-r) (Vector3D.plusK) coordinates are :
+ // sin (psi1) sin (phi), -cos (psi1) sin (phi), cos (phi)
+ // and we can choose to have phi in the interval [0 ; PI]
+ Vector3D v1 = applyTo(Vector3D.PLUS_K);
+ Vector3D v2 = applyInverseTo(Vector3D.PLUS_K);
+ if ((v2.getZ() < -0.9999999999) || (v2.getZ() > 0.9999999999)) {
+ throw new CardanEulerSingularityException(false);
+ }
+ return new double[] {
+ FastMath.atan2(v2.getX(), -v2.getY()),
+ FastMath.acos(v2.getZ()),
+ FastMath.atan2(v1.getX(), v1.getY())
+ };
+
+ } else { // last possibility is ZYZ
+
+ // r (Vector3D.plusK) coordinates are :
+ // -sin (theta) cos (psi2), sin (theta) sin (psi2), cos (theta)
+ // (-r) (Vector3D.plusK) coordinates are :
+ // cos (psi1) sin (theta), sin (psi1) sin (theta), cos (theta)
+ // and we can choose to have theta in the interval [0 ; PI]
+ Vector3D v1 = applyTo(Vector3D.PLUS_K);
+ Vector3D v2 = applyInverseTo(Vector3D.PLUS_K);
+ if ((v2.getZ() < -0.9999999999) || (v2.getZ() > 0.9999999999)) {
+ throw new CardanEulerSingularityException(false);
+ }
+ return new double[] {
+ FastMath.atan2(v2.getY(), v2.getX()),
+ FastMath.acos(v2.getZ()),
+ FastMath.atan2(v1.getY(), -v1.getX())
+ };
+
+ }
+ }
+
+ }
+
+ /** Get the 3X3 matrix corresponding to the instance
+ * @return the matrix corresponding to the instance
+ */
+ public double[][] getMatrix() {
+
+ // products
+ double q0q0 = q0 * q0;
+ double q0q1 = q0 * q1;
+ double q0q2 = q0 * q2;
+ double q0q3 = q0 * q3;
+ double q1q1 = q1 * q1;
+ double q1q2 = q1 * q2;
+ double q1q3 = q1 * q3;
+ double q2q2 = q2 * q2;
+ double q2q3 = q2 * q3;
+ double q3q3 = q3 * q3;
+
+ // create the matrix
+ double[][] m = new double[3][];
+ m[0] = new double[3];
+ m[1] = new double[3];
+ m[2] = new double[3];
+
+ m [0][0] = 2.0 * (q0q0 + q1q1) - 1.0;
+ m [1][0] = 2.0 * (q1q2 - q0q3);
+ m [2][0] = 2.0 * (q1q3 + q0q2);
+
+ m [0][1] = 2.0 * (q1q2 + q0q3);
+ m [1][1] = 2.0 * (q0q0 + q2q2) - 1.0;
+ m [2][1] = 2.0 * (q2q3 - q0q1);
+
+ m [0][2] = 2.0 * (q1q3 - q0q2);
+ m [1][2] = 2.0 * (q2q3 + q0q1);
+ m [2][2] = 2.0 * (q0q0 + q3q3) - 1.0;
+
+ return m;
+
+ }
+
+ /** Apply the rotation to a vector.
+ * @param u vector to apply the rotation to
+ * @return a new vector which is the image of u by the rotation
+ */
+ public Vector3D applyTo(Vector3D u) {
+
+ double x = u.getX();
+ double y = u.getY();
+ double z = u.getZ();
+
+ double s = q1 * x + q2 * y + q3 * z;
+
+ return new Vector3D(2 * (q0 * (x * q0 - (q2 * z - q3 * y)) + s * q1) - x,
+ 2 * (q0 * (y * q0 - (q3 * x - q1 * z)) + s * q2) - y,
+ 2 * (q0 * (z * q0 - (q1 * y - q2 * x)) + s * q3) - z);
+
+ }
+
+ /** Apply the rotation to a vector stored in an array.
+ * @param in an array with three items which stores vector to rotate
+ * @param out an array with three items to put result to (it can be the same
+ * array as in)
+ */
+ public void applyTo(final double[] in, final double[] out) {
+
+ final double x = in[0];
+ final double y = in[1];
+ final double z = in[2];
+
+ final double s = q1 * x + q2 * y + q3 * z;
+
+ out[0] = 2 * (q0 * (x * q0 - (q2 * z - q3 * y)) + s * q1) - x;
+ out[1] = 2 * (q0 * (y * q0 - (q3 * x - q1 * z)) + s * q2) - y;
+ out[2] = 2 * (q0 * (z * q0 - (q1 * y - q2 * x)) + s * q3) - z;
+
+ }
+
+ /** Apply the inverse of the rotation to a vector.
+ * @param u vector to apply the inverse of the rotation to
+ * @return a new vector which such that u is its image by the rotation
+ */
+ public Vector3D applyInverseTo(Vector3D u) {
+
+ double x = u.getX();
+ double y = u.getY();
+ double z = u.getZ();
+
+ double s = q1 * x + q2 * y + q3 * z;
+ double m0 = -q0;
+
+ return new Vector3D(2 * (m0 * (x * m0 - (q2 * z - q3 * y)) + s * q1) - x,
+ 2 * (m0 * (y * m0 - (q3 * x - q1 * z)) + s * q2) - y,
+ 2 * (m0 * (z * m0 - (q1 * y - q2 * x)) + s * q3) - z);
+
+ }
+
+ /** Apply the inverse of the rotation to a vector stored in an array.
+ * @param in an array with three items which stores vector to rotate
+ * @param out an array with three items to put result to (it can be the same
+ * array as in)
+ */
+ public void applyInverseTo(final double[] in, final double[] out) {
+
+ final double x = in[0];
+ final double y = in[1];
+ final double z = in[2];
+
+ final double s = q1 * x + q2 * y + q3 * z;
+ final double m0 = -q0;
+
+ out[0] = 2 * (m0 * (x * m0 - (q2 * z - q3 * y)) + s * q1) - x;
+ out[1] = 2 * (m0 * (y * m0 - (q3 * x - q1 * z)) + s * q2) - y;
+ out[2] = 2 * (m0 * (z * m0 - (q1 * y - q2 * x)) + s * q3) - z;
+
+ }
+
+ /** Apply the instance to another rotation.
+ * <p>
+ * Calling this method is equivalent to call
+ * {@link #compose(Rotation, RotationConvention)
+ * compose(r, RotationConvention.VECTOR_OPERATOR)}.
+ * </p>
+ * @param r rotation to apply the rotation to
+ * @return a new rotation which is the composition of r by the instance
+ */
+ public Rotation applyTo(Rotation r) {
+ return compose(r, RotationConvention.VECTOR_OPERATOR);
+ }
+
+ /** Compose the instance with another rotation.
+ * <p>
+ * If the semantics of the rotations composition corresponds to a
+ * {@link RotationConvention#VECTOR_OPERATOR vector operator} convention,
+ * applying the instance to a rotation is computing the composition
+ * in an order compliant with the following rule : let {@code u} be any
+ * vector and {@code v} its image by {@code r1} (i.e.
+ * {@code r1.applyTo(u) = v}). Let {@code w} be the image of {@code v} by
+ * rotation {@code r2} (i.e. {@code r2.applyTo(v) = w}). Then
+ * {@code w = comp.applyTo(u)}, where
+ * {@code comp = r2.compose(r1, RotationConvention.VECTOR_OPERATOR)}.
+ * </p>
+ * <p>
+ * If the semantics of the rotations composition corresponds to a
+ * {@link RotationConvention#FRAME_TRANSFORM frame transform} convention,
+ * the application order will be reversed. So keeping the exact same
+ * meaning of all {@code r1}, {@code r2}, {@code u}, {@code v}, {@code w}
+ * and {@code comp} as above, {@code comp} could also be computed as
+ * {@code comp = r1.compose(r2, RotationConvention.FRAME_TRANSFORM)}.
+ * </p>
+ * @param r rotation to apply the rotation to
+ * @param convention convention to use for the semantics of the angle
+ * @return a new rotation which is the composition of r by the instance
+ */
+ public Rotation compose(final Rotation r, final RotationConvention convention) {
+ return convention == RotationConvention.VECTOR_OPERATOR ?
+ composeInternal(r) : r.composeInternal(this);
+ }
+
+ /** Compose the instance with another rotation using vector operator convention.
+ * @param r rotation to apply the rotation to
+ * @return a new rotation which is the composition of r by the instance
+ * using vector operator convention
+ */
+ private Rotation composeInternal(final Rotation r) {
+ return new Rotation(r.q0 * q0 - (r.q1 * q1 + r.q2 * q2 + r.q3 * q3),
+ r.q1 * q0 + r.q0 * q1 + (r.q2 * q3 - r.q3 * q2),
+ r.q2 * q0 + r.q0 * q2 + (r.q3 * q1 - r.q1 * q3),
+ r.q3 * q0 + r.q0 * q3 + (r.q1 * q2 - r.q2 * q1),
+ false);
+ }
+
+ /** Apply the inverse of the instance to another rotation.
+ * <p>
+ * Calling this method is equivalent to call
+ * {@link #composeInverse(Rotation, RotationConvention)
+ * composeInverse(r, RotationConvention.VECTOR_OPERATOR)}.
+ * </p>
+ * @param r rotation to apply the rotation to
+ * @return a new rotation which is the composition of r by the inverse
+ * of the instance
+ */
+ public Rotation applyInverseTo(Rotation r) {
+ return composeInverse(r, RotationConvention.VECTOR_OPERATOR);
+ }
+
+ /** Compose the inverse of the instance with another rotation.
+ * <p>
+ * If the semantics of the rotations composition corresponds to a
+ * {@link RotationConvention#VECTOR_OPERATOR vector operator} convention,
+ * applying the inverse of the instance to a rotation is computing
+ * the composition in an order compliant with the following rule :
+ * let {@code u} be any vector and {@code v} its image by {@code r1}
+ * (i.e. {@code r1.applyTo(u) = v}). Let {@code w} be the inverse image
+ * of {@code v} by {@code r2} (i.e. {@code r2.applyInverseTo(v) = w}).
+ * Then {@code w = comp.applyTo(u)}, where
+ * {@code comp = r2.composeInverse(r1)}.
+ * </p>
+ * <p>
+ * If the semantics of the rotations composition corresponds to a
+ * {@link RotationConvention#FRAME_TRANSFORM frame transform} convention,
+ * the application order will be reversed, which means it is the
+ * <em>innermost</em> rotation that will be reversed. So keeping the exact same
+ * meaning of all {@code r1}, {@code r2}, {@code u}, {@code v}, {@code w}
+ * and {@code comp} as above, {@code comp} could also be computed as
+ * {@code comp = r1.revert().composeInverse(r2.revert(), RotationConvention.FRAME_TRANSFORM)}.
+ * </p>
+ * @param r rotation to apply the rotation to
+ * @param convention convention to use for the semantics of the angle
+ * @return a new rotation which is the composition of r by the inverse
+ * of the instance
+ */
+ public Rotation composeInverse(final Rotation r, final RotationConvention convention) {
+ return convention == RotationConvention.VECTOR_OPERATOR ?
+ composeInverseInternal(r) : r.composeInternal(revert());
+ }
+
+ /** Compose the inverse of the instance with another rotation
+ * using vector operator convention.
+ * @param r rotation to apply the rotation to
+ * @return a new rotation which is the composition of r by the inverse
+ * of the instance using vector operator convention
+ */
+ private Rotation composeInverseInternal(Rotation r) {
+ return new Rotation(-r.q0 * q0 - (r.q1 * q1 + r.q2 * q2 + r.q3 * q3),
+ -r.q1 * q0 + r.q0 * q1 + (r.q2 * q3 - r.q3 * q2),
+ -r.q2 * q0 + r.q0 * q2 + (r.q3 * q1 - r.q1 * q3),
+ -r.q3 * q0 + r.q0 * q3 + (r.q1 * q2 - r.q2 * q1),
+ false);
+ }
+
+ /** Perfect orthogonality on a 3X3 matrix.
+ * @param m initial matrix (not exactly orthogonal)
+ * @param threshold convergence threshold for the iterative
+ * orthogonality correction (convergence is reached when the
+ * difference between two steps of the Frobenius norm of the
+ * correction is below this threshold)
+ * @return an orthogonal matrix close to m
+ * @exception NotARotationMatrixException if the matrix cannot be
+ * orthogonalized with the given threshold after 10 iterations
+ */
+ private double[][] orthogonalizeMatrix(double[][] m, double threshold)
+ throws NotARotationMatrixException {
+ double[] m0 = m[0];
+ double[] m1 = m[1];
+ double[] m2 = m[2];
+ double x00 = m0[0];
+ double x01 = m0[1];
+ double x02 = m0[2];
+ double x10 = m1[0];
+ double x11 = m1[1];
+ double x12 = m1[2];
+ double x20 = m2[0];
+ double x21 = m2[1];
+ double x22 = m2[2];
+ double fn = 0;
+ double fn1;
+
+ double[][] o = new double[3][3];
+ double[] o0 = o[0];
+ double[] o1 = o[1];
+ double[] o2 = o[2];
+
+ // iterative correction: Xn+1 = Xn - 0.5 * (Xn.Mt.Xn - M)
+ int i = 0;
+ while (++i < 11) {
+
+ // Mt.Xn
+ double mx00 = m0[0] * x00 + m1[0] * x10 + m2[0] * x20;
+ double mx10 = m0[1] * x00 + m1[1] * x10 + m2[1] * x20;
+ double mx20 = m0[2] * x00 + m1[2] * x10 + m2[2] * x20;
+ double mx01 = m0[0] * x01 + m1[0] * x11 + m2[0] * x21;
+ double mx11 = m0[1] * x01 + m1[1] * x11 + m2[1] * x21;
+ double mx21 = m0[2] * x01 + m1[2] * x11 + m2[2] * x21;
+ double mx02 = m0[0] * x02 + m1[0] * x12 + m2[0] * x22;
+ double mx12 = m0[1] * x02 + m1[1] * x12 + m2[1] * x22;
+ double mx22 = m0[2] * x02 + m1[2] * x12 + m2[2] * x22;
+
+ // Xn+1
+ o0[0] = x00 - 0.5 * (x00 * mx00 + x01 * mx10 + x02 * mx20 - m0[0]);
+ o0[1] = x01 - 0.5 * (x00 * mx01 + x01 * mx11 + x02 * mx21 - m0[1]);
+ o0[2] = x02 - 0.5 * (x00 * mx02 + x01 * mx12 + x02 * mx22 - m0[2]);
+ o1[0] = x10 - 0.5 * (x10 * mx00 + x11 * mx10 + x12 * mx20 - m1[0]);
+ o1[1] = x11 - 0.5 * (x10 * mx01 + x11 * mx11 + x12 * mx21 - m1[1]);
+ o1[2] = x12 - 0.5 * (x10 * mx02 + x11 * mx12 + x12 * mx22 - m1[2]);
+ o2[0] = x20 - 0.5 * (x20 * mx00 + x21 * mx10 + x22 * mx20 - m2[0]);
+ o2[1] = x21 - 0.5 * (x20 * mx01 + x21 * mx11 + x22 * mx21 - m2[1]);
+ o2[2] = x22 - 0.5 * (x20 * mx02 + x21 * mx12 + x22 * mx22 - m2[2]);
+
+ // correction on each elements
+ double corr00 = o0[0] - m0[0];
+ double corr01 = o0[1] - m0[1];
+ double corr02 = o0[2] - m0[2];
+ double corr10 = o1[0] - m1[0];
+ double corr11 = o1[1] - m1[1];
+ double corr12 = o1[2] - m1[2];
+ double corr20 = o2[0] - m2[0];
+ double corr21 = o2[1] - m2[1];
+ double corr22 = o2[2] - m2[2];
+
+ // Frobenius norm of the correction
+ fn1 = corr00 * corr00 + corr01 * corr01 + corr02 * corr02 +
+ corr10 * corr10 + corr11 * corr11 + corr12 * corr12 +
+ corr20 * corr20 + corr21 * corr21 + corr22 * corr22;
+
+ // convergence test
+ if (FastMath.abs(fn1 - fn) <= threshold) {
+ return o;
+ }
+
+ // prepare next iteration
+ x00 = o0[0];
+ x01 = o0[1];
+ x02 = o0[2];
+ x10 = o1[0];
+ x11 = o1[1];
+ x12 = o1[2];
+ x20 = o2[0];
+ x21 = o2[1];
+ x22 = o2[2];
+ fn = fn1;
+
+ }
+
+ // the algorithm did not converge after 10 iterations
+ throw new NotARotationMatrixException(
+ LocalizedFormats.UNABLE_TO_ORTHOGONOLIZE_MATRIX,
+ i - 1);
+ }
+
+ /** Compute the <i>distance</i> between two rotations.
+ * <p>The <i>distance</i> is intended here as a way to check if two
+ * rotations are almost similar (i.e. they transform vectors the same way)
+ * or very different. It is mathematically defined as the angle of
+ * the rotation r that prepended to one of the rotations gives the other
+ * one:</p>
+ * <pre>
+ * r<sub>1</sub>(r) = r<sub>2</sub>
+ * </pre>
+ * <p>This distance is an angle between 0 and &pi;. Its value is the smallest
+ * possible upper bound of the angle in radians between r<sub>1</sub>(v)
+ * and r<sub>2</sub>(v) for all possible vectors v. This upper bound is
+ * reached for some v. The distance is equal to 0 if and only if the two
+ * rotations are identical.</p>
+ * <p>Comparing two rotations should always be done using this value rather
+ * than for example comparing the components of the quaternions. It is much
+ * more stable, and has a geometric meaning. Also comparing quaternions
+ * components is error prone since for example quaternions (0.36, 0.48, -0.48, -0.64)
+ * and (-0.36, -0.48, 0.48, 0.64) represent exactly the same rotation despite
+ * their components are different (they are exact opposites).</p>
+ * @param r1 first rotation
+ * @param r2 second rotation
+ * @return <i>distance</i> between r1 and r2
+ */
+ public static double distance(Rotation r1, Rotation r2) {
+ return r1.composeInverseInternal(r2).getAngle();
+ }
+
+}
diff --git a/src/main/java/org/apache/commons/math3/geometry/euclidean/threed/RotationConvention.java b/src/main/java/org/apache/commons/math3/geometry/euclidean/threed/RotationConvention.java
new file mode 100644
index 0000000..6111ac3
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/geometry/euclidean/threed/RotationConvention.java
@@ -0,0 +1,79 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math3.geometry.euclidean.threed;
+
+/**
+ * This enumerates is used to differentiate the semantics of a rotation.
+ * @see Rotation
+ * @since 3.6
+ */
+public enum RotationConvention {
+
+ /** Constant for rotation that have the semantics of a vector operator.
+ * <p>
+ * According to this convention, the rotation moves vectors with respect
+ * to a fixed reference frame.
+ * </p>
+ * <p>
+ * This means that if we define rotation r is a 90 degrees rotation around
+ * the Z axis, the image of vector {@link Vector3D#PLUS_I} would be
+ * {@link Vector3D#PLUS_J}, the image of vector {@link Vector3D#PLUS_J}
+ * would be {@link Vector3D#MINUS_I}, the image of vector {@link Vector3D#PLUS_K}
+ * would be {@link Vector3D#PLUS_K}, and the image of vector with coordinates (1, 2, 3)
+ * would be vector (-2, 1, 3). This means that the vector rotates counterclockwise.
+ * </p>
+ * <p>
+ * This convention was the only one supported by Apache Commons Math up to version 3.5.
+ * </p>
+ * <p>
+ * The difference with {@link #FRAME_TRANSFORM} is only the semantics of the sign
+ * of the angle. It is always possible to create or use a rotation using either
+ * convention to really represent a rotation that would have been best created or
+ * used with the other convention, by changing accordingly the sign of the
+ * rotation angle. This is how things were done up to version 3.5.
+ * </p>
+ */
+ VECTOR_OPERATOR,
+
+ /** Constant for rotation that have the semantics of a frame conversion.
+ * <p>
+ * According to this convention, the rotation considered vectors to be fixed,
+ * but their coordinates change as they are converted from an initial frame to
+ * a destination frame rotated with respect to the initial frame.
+ * </p>
+ * <p>
+ * This means that if we define rotation r is a 90 degrees rotation around
+ * the Z axis, the image of vector {@link Vector3D#PLUS_I} would be
+ * {@link Vector3D#MINUS_J}, the image of vector {@link Vector3D#PLUS_J}
+ * would be {@link Vector3D#PLUS_I}, the image of vector {@link Vector3D#PLUS_K}
+ * would be {@link Vector3D#PLUS_K}, and the image of vector with coordinates (1, 2, 3)
+ * would be vector (2, -1, 3). This means that the coordinates of the vector rotates
+ * clockwise, because they are expressed with respect to a destination frame that is rotated
+ * counterclockwise.
+ * </p>
+ * <p>
+ * The difference with {@link #VECTOR_OPERATOR} is only the semantics of the sign
+ * of the angle. It is always possible to create or use a rotation using either
+ * convention to really represent a rotation that would have been best created or
+ * used with the other convention, by changing accordingly the sign of the
+ * rotation angle. This is how things were done up to version 3.5.
+ * </p>
+ */
+ FRAME_TRANSFORM;
+
+}
diff --git a/src/main/java/org/apache/commons/math3/geometry/euclidean/threed/RotationOrder.java b/src/main/java/org/apache/commons/math3/geometry/euclidean/threed/RotationOrder.java
new file mode 100644
index 0000000..03bc1c2
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/geometry/euclidean/threed/RotationOrder.java
@@ -0,0 +1,174 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math3.geometry.euclidean.threed;
+
+/**
+ * This class is a utility representing a rotation order specification
+ * for Cardan or Euler angles specification.
+ *
+ * This class cannot be instanciated by the user. He can only use one
+ * of the twelve predefined supported orders as an argument to either
+ * the {@link Rotation#Rotation(RotationOrder,double,double,double)}
+ * constructor or the {@link Rotation#getAngles} method.
+ *
+ * @since 1.2
+ */
+public final class RotationOrder {
+
+ /** Set of Cardan angles.
+ * this ordered set of rotations is around X, then around Y, then
+ * around Z
+ */
+ public static final RotationOrder XYZ =
+ new RotationOrder("XYZ", Vector3D.PLUS_I, Vector3D.PLUS_J, Vector3D.PLUS_K);
+
+ /** Set of Cardan angles.
+ * this ordered set of rotations is around X, then around Z, then
+ * around Y
+ */
+ public static final RotationOrder XZY =
+ new RotationOrder("XZY", Vector3D.PLUS_I, Vector3D.PLUS_K, Vector3D.PLUS_J);
+
+ /** Set of Cardan angles.
+ * this ordered set of rotations is around Y, then around X, then
+ * around Z
+ */
+ public static final RotationOrder YXZ =
+ new RotationOrder("YXZ", Vector3D.PLUS_J, Vector3D.PLUS_I, Vector3D.PLUS_K);
+
+ /** Set of Cardan angles.
+ * this ordered set of rotations is around Y, then around Z, then
+ * around X
+ */
+ public static final RotationOrder YZX =
+ new RotationOrder("YZX", Vector3D.PLUS_J, Vector3D.PLUS_K, Vector3D.PLUS_I);
+
+ /** Set of Cardan angles.
+ * this ordered set of rotations is around Z, then around X, then
+ * around Y
+ */
+ public static final RotationOrder ZXY =
+ new RotationOrder("ZXY", Vector3D.PLUS_K, Vector3D.PLUS_I, Vector3D.PLUS_J);
+
+ /** Set of Cardan angles.
+ * this ordered set of rotations is around Z, then around Y, then
+ * around X
+ */
+ public static final RotationOrder ZYX =
+ new RotationOrder("ZYX", Vector3D.PLUS_K, Vector3D.PLUS_J, Vector3D.PLUS_I);
+
+ /** Set of Euler angles.
+ * this ordered set of rotations is around X, then around Y, then
+ * around X
+ */
+ public static final RotationOrder XYX =
+ new RotationOrder("XYX", Vector3D.PLUS_I, Vector3D.PLUS_J, Vector3D.PLUS_I);
+
+ /** Set of Euler angles.
+ * this ordered set of rotations is around X, then around Z, then
+ * around X
+ */
+ public static final RotationOrder XZX =
+ new RotationOrder("XZX", Vector3D.PLUS_I, Vector3D.PLUS_K, Vector3D.PLUS_I);
+
+ /** Set of Euler angles.
+ * this ordered set of rotations is around Y, then around X, then
+ * around Y
+ */
+ public static final RotationOrder YXY =
+ new RotationOrder("YXY", Vector3D.PLUS_J, Vector3D.PLUS_I, Vector3D.PLUS_J);
+
+ /** Set of Euler angles.
+ * this ordered set of rotations is around Y, then around Z, then
+ * around Y
+ */
+ public static final RotationOrder YZY =
+ new RotationOrder("YZY", Vector3D.PLUS_J, Vector3D.PLUS_K, Vector3D.PLUS_J);
+
+ /** Set of Euler angles.
+ * this ordered set of rotations is around Z, then around X, then
+ * around Z
+ */
+ public static final RotationOrder ZXZ =
+ new RotationOrder("ZXZ", Vector3D.PLUS_K, Vector3D.PLUS_I, Vector3D.PLUS_K);
+
+ /** Set of Euler angles.
+ * this ordered set of rotations is around Z, then around Y, then
+ * around Z
+ */
+ public static final RotationOrder ZYZ =
+ new RotationOrder("ZYZ", Vector3D.PLUS_K, Vector3D.PLUS_J, Vector3D.PLUS_K);
+
+ /** Name of the rotations order. */
+ private final String name;
+
+ /** Axis of the first rotation. */
+ private final Vector3D a1;
+
+ /** Axis of the second rotation. */
+ private final Vector3D a2;
+
+ /** Axis of the third rotation. */
+ private final Vector3D a3;
+
+ /** Private constructor.
+ * This is a utility class that cannot be instantiated by the user,
+ * so its only constructor is private.
+ * @param name name of the rotation order
+ * @param a1 axis of the first rotation
+ * @param a2 axis of the second rotation
+ * @param a3 axis of the third rotation
+ */
+ private RotationOrder(final String name,
+ final Vector3D a1, final Vector3D a2, final Vector3D a3) {
+ this.name = name;
+ this.a1 = a1;
+ this.a2 = a2;
+ this.a3 = a3;
+ }
+
+ /** Get a string representation of the instance.
+ * @return a string representation of the instance (in fact, its name)
+ */
+ @Override
+ public String toString() {
+ return name;
+ }
+
+ /** Get the axis of the first rotation.
+ * @return axis of the first rotation
+ */
+ public Vector3D getA1() {
+ return a1;
+ }
+
+ /** Get the axis of the second rotation.
+ * @return axis of the second rotation
+ */
+ public Vector3D getA2() {
+ return a2;
+ }
+
+ /** Get the axis of the second rotation.
+ * @return axis of the second rotation
+ */
+ public Vector3D getA3() {
+ return a3;
+ }
+
+}
diff --git a/src/main/java/org/apache/commons/math3/geometry/euclidean/threed/Segment.java b/src/main/java/org/apache/commons/math3/geometry/euclidean/threed/Segment.java
new file mode 100644
index 0000000..200b462
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/geometry/euclidean/threed/Segment.java
@@ -0,0 +1,66 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math3.geometry.euclidean.threed;
+
+
+/** Simple container for a two-points segment.
+ * @since 3.0
+ */
+public class Segment {
+
+ /** Start point of the segment. */
+ private final Vector3D start;
+
+ /** End point of the segments. */
+ private final Vector3D end;
+
+ /** Line containing the segment. */
+ private final Line line;
+
+ /** Build a segment.
+ * @param start start point of the segment
+ * @param end end point of the segment
+ * @param line line containing the segment
+ */
+ public Segment(final Vector3D start, final Vector3D end, final Line line) {
+ this.start = start;
+ this.end = end;
+ this.line = line;
+ }
+
+ /** Get the start point of the segment.
+ * @return start point of the segment
+ */
+ public Vector3D getStart() {
+ return start;
+ }
+
+ /** Get the end point of the segment.
+ * @return end point of the segment
+ */
+ public Vector3D getEnd() {
+ return end;
+ }
+
+ /** Get the line containing the segment.
+ * @return line containing the segment
+ */
+ public Line getLine() {
+ return line;
+ }
+
+}
diff --git a/src/main/java/org/apache/commons/math3/geometry/euclidean/threed/SphereGenerator.java b/src/main/java/org/apache/commons/math3/geometry/euclidean/threed/SphereGenerator.java
new file mode 100644
index 0000000..b553510
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/geometry/euclidean/threed/SphereGenerator.java
@@ -0,0 +1,152 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math3.geometry.euclidean.threed;
+
+import java.util.Arrays;
+import java.util.List;
+
+import org.apache.commons.math3.fraction.BigFraction;
+import org.apache.commons.math3.geometry.enclosing.EnclosingBall;
+import org.apache.commons.math3.geometry.enclosing.SupportBallGenerator;
+import org.apache.commons.math3.geometry.euclidean.twod.DiskGenerator;
+import org.apache.commons.math3.geometry.euclidean.twod.Euclidean2D;
+import org.apache.commons.math3.geometry.euclidean.twod.Vector2D;
+import org.apache.commons.math3.util.FastMath;
+
+/** Class generating an enclosing ball from its support points.
+ * @since 3.3
+ */
+public class SphereGenerator implements SupportBallGenerator<Euclidean3D, Vector3D> {
+
+ /** {@inheritDoc} */
+ public EnclosingBall<Euclidean3D, Vector3D> ballOnSupport(final List<Vector3D> support) {
+
+ if (support.size() < 1) {
+ return new EnclosingBall<Euclidean3D, Vector3D>(Vector3D.ZERO, Double.NEGATIVE_INFINITY);
+ } else {
+ final Vector3D vA = support.get(0);
+ if (support.size() < 2) {
+ return new EnclosingBall<Euclidean3D, Vector3D>(vA, 0, vA);
+ } else {
+ final Vector3D vB = support.get(1);
+ if (support.size() < 3) {
+ return new EnclosingBall<Euclidean3D, Vector3D>(new Vector3D(0.5, vA, 0.5, vB),
+ 0.5 * vA.distance(vB),
+ vA, vB);
+ } else {
+ final Vector3D vC = support.get(2);
+ if (support.size() < 4) {
+
+ // delegate to 2D disk generator
+ final Plane p = new Plane(vA, vB, vC,
+ 1.0e-10 * (vA.getNorm1() + vB.getNorm1() + vC.getNorm1()));
+ final EnclosingBall<Euclidean2D, Vector2D> disk =
+ new DiskGenerator().ballOnSupport(Arrays.asList(p.toSubSpace(vA),
+ p.toSubSpace(vB),
+ p.toSubSpace(vC)));
+
+ // convert back to 3D
+ return new EnclosingBall<Euclidean3D, Vector3D>(p.toSpace(disk.getCenter()),
+ disk.getRadius(), vA, vB, vC);
+
+ } else {
+ final Vector3D vD = support.get(3);
+ // a sphere is 3D can be defined as:
+ // (1) (x - x_0)^2 + (y - y_0)^2 + (z - z_0)^2 = r^2
+ // which can be written:
+ // (2) (x^2 + y^2 + z^2) - 2 x_0 x - 2 y_0 y - 2 z_0 z + (x_0^2 + y_0^2 + z_0^2 - r^2) = 0
+ // or simply:
+ // (3) (x^2 + y^2 + z^2) + a x + b y + c z + d = 0
+ // with sphere center coordinates -a/2, -b/2, -c/2
+ // If the sphere exists, a b, c and d are a non zero solution to
+ // [ (x^2 + y^2 + z^2) x y z 1 ] [ 1 ] [ 0 ]
+ // [ (xA^2 + yA^2 + zA^2) xA yA zA 1 ] [ a ] [ 0 ]
+ // [ (xB^2 + yB^2 + zB^2) xB yB zB 1 ] * [ b ] = [ 0 ]
+ // [ (xC^2 + yC^2 + zC^2) xC yC zC 1 ] [ c ] [ 0 ]
+ // [ (xD^2 + yD^2 + zD^2) xD yD zD 1 ] [ d ] [ 0 ]
+ // So the determinant of the matrix is zero. Computing this determinant
+ // by expanding it using the minors m_ij of first row leads to
+ // (4) m_11 (x^2 + y^2 + z^2) - m_12 x + m_13 y - m_14 z + m_15 = 0
+ // So by identifying equations (2) and (4) we get the coordinates
+ // of center as:
+ // x_0 = +m_12 / (2 m_11)
+ // y_0 = -m_13 / (2 m_11)
+ // z_0 = +m_14 / (2 m_11)
+ // Note that the minors m_11, m_12, m_13 and m_14 all have the last column
+ // filled with 1.0, hence simplifying the computation
+ final BigFraction[] c2 = new BigFraction[] {
+ new BigFraction(vA.getX()), new BigFraction(vB.getX()),
+ new BigFraction(vC.getX()), new BigFraction(vD.getX())
+ };
+ final BigFraction[] c3 = new BigFraction[] {
+ new BigFraction(vA.getY()), new BigFraction(vB.getY()),
+ new BigFraction(vC.getY()), new BigFraction(vD.getY())
+ };
+ final BigFraction[] c4 = new BigFraction[] {
+ new BigFraction(vA.getZ()), new BigFraction(vB.getZ()),
+ new BigFraction(vC.getZ()), new BigFraction(vD.getZ())
+ };
+ final BigFraction[] c1 = new BigFraction[] {
+ c2[0].multiply(c2[0]).add(c3[0].multiply(c3[0])).add(c4[0].multiply(c4[0])),
+ c2[1].multiply(c2[1]).add(c3[1].multiply(c3[1])).add(c4[1].multiply(c4[1])),
+ c2[2].multiply(c2[2]).add(c3[2].multiply(c3[2])).add(c4[2].multiply(c4[2])),
+ c2[3].multiply(c2[3]).add(c3[3].multiply(c3[3])).add(c4[3].multiply(c4[3]))
+ };
+ final BigFraction twoM11 = minor(c2, c3, c4).multiply(2);
+ final BigFraction m12 = minor(c1, c3, c4);
+ final BigFraction m13 = minor(c1, c2, c4);
+ final BigFraction m14 = minor(c1, c2, c3);
+ final BigFraction centerX = m12.divide(twoM11);
+ final BigFraction centerY = m13.divide(twoM11).negate();
+ final BigFraction centerZ = m14.divide(twoM11);
+ final BigFraction dx = c2[0].subtract(centerX);
+ final BigFraction dy = c3[0].subtract(centerY);
+ final BigFraction dz = c4[0].subtract(centerZ);
+ final BigFraction r2 = dx.multiply(dx).add(dy.multiply(dy)).add(dz.multiply(dz));
+ return new EnclosingBall<Euclidean3D, Vector3D>(new Vector3D(centerX.doubleValue(),
+ centerY.doubleValue(),
+ centerZ.doubleValue()),
+ FastMath.sqrt(r2.doubleValue()),
+ vA, vB, vC, vD);
+ }
+ }
+ }
+ }
+ }
+
+ /** Compute a dimension 4 minor, when 4<sup>th</sup> column is known to be filled with 1.0.
+ * @param c1 first column
+ * @param c2 second column
+ * @param c3 third column
+ * @return value of the minor computed has an exact fraction
+ */
+ private BigFraction minor(final BigFraction[] c1, final BigFraction[] c2, final BigFraction[] c3) {
+ return c2[0].multiply(c3[1]).multiply(c1[2].subtract(c1[3])).
+ add(c2[0].multiply(c3[2]).multiply(c1[3].subtract(c1[1]))).
+ add(c2[0].multiply(c3[3]).multiply(c1[1].subtract(c1[2]))).
+ add(c2[1].multiply(c3[0]).multiply(c1[3].subtract(c1[2]))).
+ add(c2[1].multiply(c3[2]).multiply(c1[0].subtract(c1[3]))).
+ add(c2[1].multiply(c3[3]).multiply(c1[2].subtract(c1[0]))).
+ add(c2[2].multiply(c3[0]).multiply(c1[1].subtract(c1[3]))).
+ add(c2[2].multiply(c3[1]).multiply(c1[3].subtract(c1[0]))).
+ add(c2[2].multiply(c3[3]).multiply(c1[0].subtract(c1[1]))).
+ add(c2[3].multiply(c3[0]).multiply(c1[2].subtract(c1[1]))).
+ add(c2[3].multiply(c3[1]).multiply(c1[0].subtract(c1[2]))).
+ add(c2[3].multiply(c3[2]).multiply(c1[1].subtract(c1[0])));
+ }
+
+}
diff --git a/src/main/java/org/apache/commons/math3/geometry/euclidean/threed/SphericalCoordinates.java b/src/main/java/org/apache/commons/math3/geometry/euclidean/threed/SphericalCoordinates.java
new file mode 100644
index 0000000..016e0a0
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/geometry/euclidean/threed/SphericalCoordinates.java
@@ -0,0 +1,395 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math3.geometry.euclidean.threed;
+
+
+import java.io.Serializable;
+
+import org.apache.commons.math3.util.FastMath;
+
+/** This class provides conversions related to <a
+ * href="http://mathworld.wolfram.com/SphericalCoordinates.html">spherical coordinates</a>.
+ * <p>
+ * The conventions used here are the mathematical ones, i.e. spherical coordinates are
+ * related to Cartesian coordinates as follows:
+ * </p>
+ * <ul>
+ * <li>x = r cos(&theta;) sin(&Phi;)</li>
+ * <li>y = r sin(&theta;) sin(&Phi;)</li>
+ * <li>z = r cos(&Phi;)</li>
+ * </ul>
+ * <ul>
+ * <li>r = &radic;(x<sup>2</sup>+y<sup>2</sup>+z<sup>2</sup>)</li>
+ * <li>&theta; = atan2(y, x)</li>
+ * <li>&Phi; = acos(z/r)</li>
+ * </ul>
+ * <p>
+ * r is the radius, &theta; is the azimuthal angle in the x-y plane and &Phi; is the polar
+ * (co-latitude) angle. These conventions are <em>different</em> from the conventions used
+ * in physics (and in particular in spherical harmonics) where the meanings of &theta; and
+ * &Phi; are reversed.
+ * </p>
+ * <p>
+ * This class provides conversion of coordinates and also of gradient and Hessian
+ * between spherical and Cartesian coordinates.
+ * </p>
+ * @since 3.2
+ */
+public class SphericalCoordinates implements Serializable {
+
+ /** Serializable UID. */
+ private static final long serialVersionUID = 20130206L;
+
+ /** Cartesian coordinates. */
+ private final Vector3D v;
+
+ /** Radius. */
+ private final double r;
+
+ /** Azimuthal angle in the x-y plane &theta;. */
+ private final double theta;
+
+ /** Polar angle (co-latitude) &Phi;. */
+ private final double phi;
+
+ /** Jacobian of (r, &theta; &Phi). */
+ private double[][] jacobian;
+
+ /** Hessian of radius. */
+ private double[][] rHessian;
+
+ /** Hessian of azimuthal angle in the x-y plane &theta;. */
+ private double[][] thetaHessian;
+
+ /** Hessian of polar (co-latitude) angle &Phi;. */
+ private double[][] phiHessian;
+
+ /** Build a spherical coordinates transformer from Cartesian coordinates.
+ * @param v Cartesian coordinates
+ */
+ public SphericalCoordinates(final Vector3D v) {
+
+ // Cartesian coordinates
+ this.v = v;
+
+ // remaining spherical coordinates
+ this.r = v.getNorm();
+ this.theta = v.getAlpha();
+ this.phi = FastMath.acos(v.getZ() / r);
+
+ }
+
+ /** Build a spherical coordinates transformer from spherical coordinates.
+ * @param r radius
+ * @param theta azimuthal angle in x-y plane
+ * @param phi polar (co-latitude) angle
+ */
+ public SphericalCoordinates(final double r, final double theta, final double phi) {
+
+ final double cosTheta = FastMath.cos(theta);
+ final double sinTheta = FastMath.sin(theta);
+ final double cosPhi = FastMath.cos(phi);
+ final double sinPhi = FastMath.sin(phi);
+
+ // spherical coordinates
+ this.r = r;
+ this.theta = theta;
+ this.phi = phi;
+
+ // Cartesian coordinates
+ this.v = new Vector3D(r * cosTheta * sinPhi,
+ r * sinTheta * sinPhi,
+ r * cosPhi);
+
+ }
+
+ /** Get the Cartesian coordinates.
+ * @return Cartesian coordinates
+ */
+ public Vector3D getCartesian() {
+ return v;
+ }
+
+ /** Get the radius.
+ * @return radius r
+ * @see #getTheta()
+ * @see #getPhi()
+ */
+ public double getR() {
+ return r;
+ }
+
+ /** Get the azimuthal angle in x-y plane.
+ * @return azimuthal angle in x-y plane &theta;
+ * @see #getR()
+ * @see #getPhi()
+ */
+ public double getTheta() {
+ return theta;
+ }
+
+ /** Get the polar (co-latitude) angle.
+ * @return polar (co-latitude) angle &Phi;
+ * @see #getR()
+ * @see #getTheta()
+ */
+ public double getPhi() {
+ return phi;
+ }
+
+ /** Convert a gradient with respect to spherical coordinates into a gradient
+ * with respect to Cartesian coordinates.
+ * @param sGradient gradient with respect to spherical coordinates
+ * {df/dr, df/d&theta;, df/d&Phi;}
+ * @return gradient with respect to Cartesian coordinates
+ * {df/dx, df/dy, df/dz}
+ */
+ public double[] toCartesianGradient(final double[] sGradient) {
+
+ // lazy evaluation of Jacobian
+ computeJacobian();
+
+ // compose derivatives as gradient^T . J
+ // the expressions have been simplified since we know jacobian[1][2] = dTheta/dZ = 0
+ return new double[] {
+ sGradient[0] * jacobian[0][0] + sGradient[1] * jacobian[1][0] + sGradient[2] * jacobian[2][0],
+ sGradient[0] * jacobian[0][1] + sGradient[1] * jacobian[1][1] + sGradient[2] * jacobian[2][1],
+ sGradient[0] * jacobian[0][2] + sGradient[2] * jacobian[2][2]
+ };
+
+ }
+
+ /** Convert a Hessian with respect to spherical coordinates into a Hessian
+ * with respect to Cartesian coordinates.
+ * <p>
+ * As Hessian are always symmetric, we use only the lower left part of the provided
+ * spherical Hessian, so the upper part may not be initialized. However, we still
+ * do fill up the complete array we create, with guaranteed symmetry.
+ * </p>
+ * @param sHessian Hessian with respect to spherical coordinates
+ * {{d<sup>2</sup>f/dr<sup>2</sup>, d<sup>2</sup>f/drd&theta;, d<sup>2</sup>f/drd&Phi;},
+ * {d<sup>2</sup>f/drd&theta;, d<sup>2</sup>f/d&theta;<sup>2</sup>, d<sup>2</sup>f/d&theta;d&Phi;},
+ * {d<sup>2</sup>f/drd&Phi;, d<sup>2</sup>f/d&theta;d&Phi;, d<sup>2</sup>f/d&Phi;<sup>2</sup>}
+ * @param sGradient gradient with respect to spherical coordinates
+ * {df/dr, df/d&theta;, df/d&Phi;}
+ * @return Hessian with respect to Cartesian coordinates
+ * {{d<sup>2</sup>f/dx<sup>2</sup>, d<sup>2</sup>f/dxdy, d<sup>2</sup>f/dxdz},
+ * {d<sup>2</sup>f/dxdy, d<sup>2</sup>f/dy<sup>2</sup>, d<sup>2</sup>f/dydz},
+ * {d<sup>2</sup>f/dxdz, d<sup>2</sup>f/dydz, d<sup>2</sup>f/dz<sup>2</sup>}}
+ */
+ public double[][] toCartesianHessian(final double[][] sHessian, final double[] sGradient) {
+
+ computeJacobian();
+ computeHessians();
+
+ // compose derivative as J^T . H_f . J + df/dr H_r + df/dtheta H_theta + df/dphi H_phi
+ // the expressions have been simplified since we know jacobian[1][2] = dTheta/dZ = 0
+ // and H_theta is only a 2x2 matrix as it does not depend on z
+ final double[][] hj = new double[3][3];
+ final double[][] cHessian = new double[3][3];
+
+ // compute H_f . J
+ // beware we use ONLY the lower-left part of sHessian
+ hj[0][0] = sHessian[0][0] * jacobian[0][0] + sHessian[1][0] * jacobian[1][0] + sHessian[2][0] * jacobian[2][0];
+ hj[0][1] = sHessian[0][0] * jacobian[0][1] + sHessian[1][0] * jacobian[1][1] + sHessian[2][0] * jacobian[2][1];
+ hj[0][2] = sHessian[0][0] * jacobian[0][2] + sHessian[2][0] * jacobian[2][2];
+ hj[1][0] = sHessian[1][0] * jacobian[0][0] + sHessian[1][1] * jacobian[1][0] + sHessian[2][1] * jacobian[2][0];
+ hj[1][1] = sHessian[1][0] * jacobian[0][1] + sHessian[1][1] * jacobian[1][1] + sHessian[2][1] * jacobian[2][1];
+ // don't compute hj[1][2] as it is not used below
+ hj[2][0] = sHessian[2][0] * jacobian[0][0] + sHessian[2][1] * jacobian[1][0] + sHessian[2][2] * jacobian[2][0];
+ hj[2][1] = sHessian[2][0] * jacobian[0][1] + sHessian[2][1] * jacobian[1][1] + sHessian[2][2] * jacobian[2][1];
+ hj[2][2] = sHessian[2][0] * jacobian[0][2] + sHessian[2][2] * jacobian[2][2];
+
+ // compute lower-left part of J^T . H_f . J
+ cHessian[0][0] = jacobian[0][0] * hj[0][0] + jacobian[1][0] * hj[1][0] + jacobian[2][0] * hj[2][0];
+ cHessian[1][0] = jacobian[0][1] * hj[0][0] + jacobian[1][1] * hj[1][0] + jacobian[2][1] * hj[2][0];
+ cHessian[2][0] = jacobian[0][2] * hj[0][0] + jacobian[2][2] * hj[2][0];
+ cHessian[1][1] = jacobian[0][1] * hj[0][1] + jacobian[1][1] * hj[1][1] + jacobian[2][1] * hj[2][1];
+ cHessian[2][1] = jacobian[0][2] * hj[0][1] + jacobian[2][2] * hj[2][1];
+ cHessian[2][2] = jacobian[0][2] * hj[0][2] + jacobian[2][2] * hj[2][2];
+
+ // add gradient contribution
+ cHessian[0][0] += sGradient[0] * rHessian[0][0] + sGradient[1] * thetaHessian[0][0] + sGradient[2] * phiHessian[0][0];
+ cHessian[1][0] += sGradient[0] * rHessian[1][0] + sGradient[1] * thetaHessian[1][0] + sGradient[2] * phiHessian[1][0];
+ cHessian[2][0] += sGradient[0] * rHessian[2][0] + sGradient[2] * phiHessian[2][0];
+ cHessian[1][1] += sGradient[0] * rHessian[1][1] + sGradient[1] * thetaHessian[1][1] + sGradient[2] * phiHessian[1][1];
+ cHessian[2][1] += sGradient[0] * rHessian[2][1] + sGradient[2] * phiHessian[2][1];
+ cHessian[2][2] += sGradient[0] * rHessian[2][2] + sGradient[2] * phiHessian[2][2];
+
+ // ensure symmetry
+ cHessian[0][1] = cHessian[1][0];
+ cHessian[0][2] = cHessian[2][0];
+ cHessian[1][2] = cHessian[2][1];
+
+ return cHessian;
+
+ }
+
+ /** Lazy evaluation of (r, &theta;, &phi;) Jacobian.
+ */
+ private void computeJacobian() {
+ if (jacobian == null) {
+
+ // intermediate variables
+ final double x = v.getX();
+ final double y = v.getY();
+ final double z = v.getZ();
+ final double rho2 = x * x + y * y;
+ final double rho = FastMath.sqrt(rho2);
+ final double r2 = rho2 + z * z;
+
+ jacobian = new double[3][3];
+
+ // row representing the gradient of r
+ jacobian[0][0] = x / r;
+ jacobian[0][1] = y / r;
+ jacobian[0][2] = z / r;
+
+ // row representing the gradient of theta
+ jacobian[1][0] = -y / rho2;
+ jacobian[1][1] = x / rho2;
+ // jacobian[1][2] is already set to 0 at allocation time
+
+ // row representing the gradient of phi
+ jacobian[2][0] = x * z / (rho * r2);
+ jacobian[2][1] = y * z / (rho * r2);
+ jacobian[2][2] = -rho / r2;
+
+ }
+ }
+
+ /** Lazy evaluation of Hessians.
+ */
+ private void computeHessians() {
+
+ if (rHessian == null) {
+
+ // intermediate variables
+ final double x = v.getX();
+ final double y = v.getY();
+ final double z = v.getZ();
+ final double x2 = x * x;
+ final double y2 = y * y;
+ final double z2 = z * z;
+ final double rho2 = x2 + y2;
+ final double rho = FastMath.sqrt(rho2);
+ final double r2 = rho2 + z2;
+ final double xOr = x / r;
+ final double yOr = y / r;
+ final double zOr = z / r;
+ final double xOrho2 = x / rho2;
+ final double yOrho2 = y / rho2;
+ final double xOr3 = xOr / r2;
+ final double yOr3 = yOr / r2;
+ final double zOr3 = zOr / r2;
+
+ // lower-left part of Hessian of r
+ rHessian = new double[3][3];
+ rHessian[0][0] = y * yOr3 + z * zOr3;
+ rHessian[1][0] = -x * yOr3;
+ rHessian[2][0] = -z * xOr3;
+ rHessian[1][1] = x * xOr3 + z * zOr3;
+ rHessian[2][1] = -y * zOr3;
+ rHessian[2][2] = x * xOr3 + y * yOr3;
+
+ // upper-right part is symmetric
+ rHessian[0][1] = rHessian[1][0];
+ rHessian[0][2] = rHessian[2][0];
+ rHessian[1][2] = rHessian[2][1];
+
+ // lower-left part of Hessian of azimuthal angle theta
+ thetaHessian = new double[2][2];
+ thetaHessian[0][0] = 2 * xOrho2 * yOrho2;
+ thetaHessian[1][0] = yOrho2 * yOrho2 - xOrho2 * xOrho2;
+ thetaHessian[1][1] = -2 * xOrho2 * yOrho2;
+
+ // upper-right part is symmetric
+ thetaHessian[0][1] = thetaHessian[1][0];
+
+ // lower-left part of Hessian of polar (co-latitude) angle phi
+ final double rhor2 = rho * r2;
+ final double rho2r2 = rho * rhor2;
+ final double rhor4 = rhor2 * r2;
+ final double rho3r4 = rhor4 * rho2;
+ final double r2P2rho2 = 3 * rho2 + z2;
+ phiHessian = new double[3][3];
+ phiHessian[0][0] = z * (rho2r2 - x2 * r2P2rho2) / rho3r4;
+ phiHessian[1][0] = -x * y * z * r2P2rho2 / rho3r4;
+ phiHessian[2][0] = x * (rho2 - z2) / rhor4;
+ phiHessian[1][1] = z * (rho2r2 - y2 * r2P2rho2) / rho3r4;
+ phiHessian[2][1] = y * (rho2 - z2) / rhor4;
+ phiHessian[2][2] = 2 * rho * zOr3 / r;
+
+ // upper-right part is symmetric
+ phiHessian[0][1] = phiHessian[1][0];
+ phiHessian[0][2] = phiHessian[2][0];
+ phiHessian[1][2] = phiHessian[2][1];
+
+ }
+
+ }
+
+ /**
+ * Replace the instance with a data transfer object for serialization.
+ * @return data transfer object that will be serialized
+ */
+ private Object writeReplace() {
+ return new DataTransferObject(v.getX(), v.getY(), v.getZ());
+ }
+
+ /** Internal class used only for serialization. */
+ private static class DataTransferObject implements Serializable {
+
+ /** Serializable UID. */
+ private static final long serialVersionUID = 20130206L;
+
+ /** Abscissa.
+ * @serial
+ */
+ private final double x;
+
+ /** Ordinate.
+ * @serial
+ */
+ private final double y;
+
+ /** Height.
+ * @serial
+ */
+ private final double z;
+
+ /** Simple constructor.
+ * @param x abscissa
+ * @param y ordinate
+ * @param z height
+ */
+ DataTransferObject(final double x, final double y, final double z) {
+ this.x = x;
+ this.y = y;
+ this.z = z;
+ }
+
+ /** Replace the deserialized data transfer object with a {@link SphericalCoordinates}.
+ * @return replacement {@link SphericalCoordinates}
+ */
+ private Object readResolve() {
+ return new SphericalCoordinates(new Vector3D(x, y, z));
+ }
+
+ }
+
+}
diff --git a/src/main/java/org/apache/commons/math3/geometry/euclidean/threed/SubLine.java b/src/main/java/org/apache/commons/math3/geometry/euclidean/threed/SubLine.java
new file mode 100644
index 0000000..2ac917f
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/geometry/euclidean/threed/SubLine.java
@@ -0,0 +1,165 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math3.geometry.euclidean.threed;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.apache.commons.math3.exception.MathIllegalArgumentException;
+import org.apache.commons.math3.geometry.Point;
+import org.apache.commons.math3.geometry.euclidean.oned.Euclidean1D;
+import org.apache.commons.math3.geometry.euclidean.oned.Interval;
+import org.apache.commons.math3.geometry.euclidean.oned.IntervalsSet;
+import org.apache.commons.math3.geometry.euclidean.oned.Vector1D;
+import org.apache.commons.math3.geometry.partitioning.Region.Location;
+
+/** This class represents a subset of a {@link Line}.
+ * @since 3.0
+ */
+public class SubLine {
+
+ /** Default value for tolerance. */
+ private static final double DEFAULT_TOLERANCE = 1.0e-10;
+
+ /** Underlying line. */
+ private final Line line;
+
+ /** Remaining region of the hyperplane. */
+ private final IntervalsSet remainingRegion;
+
+ /** Simple constructor.
+ * @param line underlying line
+ * @param remainingRegion remaining region of the line
+ */
+ public SubLine(final Line line, final IntervalsSet remainingRegion) {
+ this.line = line;
+ this.remainingRegion = remainingRegion;
+ }
+
+ /** Create a sub-line from two endpoints.
+ * @param start start point
+ * @param end end point
+ * @param tolerance tolerance below which points are considered identical
+ * @exception MathIllegalArgumentException if the points are equal
+ * @since 3.3
+ */
+ public SubLine(final Vector3D start, final Vector3D end, final double tolerance)
+ throws MathIllegalArgumentException {
+ this(new Line(start, end, tolerance), buildIntervalSet(start, end, tolerance));
+ }
+
+ /** Create a sub-line from two endpoints.
+ * @param start start point
+ * @param end end point
+ * @exception MathIllegalArgumentException if the points are equal
+ * @deprecated as of 3.3, replaced with {@link #SubLine(Vector3D, Vector3D, double)}
+ */
+ public SubLine(final Vector3D start, final Vector3D end)
+ throws MathIllegalArgumentException {
+ this(start, end, DEFAULT_TOLERANCE);
+ }
+
+ /** Create a sub-line from a segment.
+ * @param segment single segment forming the sub-line
+ * @exception MathIllegalArgumentException if the segment endpoints are equal
+ */
+ public SubLine(final Segment segment) throws MathIllegalArgumentException {
+ this(segment.getLine(),
+ buildIntervalSet(segment.getStart(), segment.getEnd(), segment.getLine().getTolerance()));
+ }
+
+ /** Get the endpoints of the sub-line.
+ * <p>
+ * A subline may be any arbitrary number of disjoints segments, so the endpoints
+ * are provided as a list of endpoint pairs. Each element of the list represents
+ * one segment, and each segment contains a start point at index 0 and an end point
+ * at index 1. If the sub-line is unbounded in the negative infinity direction,
+ * the start point of the first segment will have infinite coordinates. If the
+ * sub-line is unbounded in the positive infinity direction, the end point of the
+ * last segment will have infinite coordinates. So a sub-line covering the whole
+ * line will contain just one row and both elements of this row will have infinite
+ * coordinates. If the sub-line is empty, the returned list will contain 0 segments.
+ * </p>
+ * @return list of segments endpoints
+ */
+ public List<Segment> getSegments() {
+
+ final List<Interval> list = remainingRegion.asList();
+ final List<Segment> segments = new ArrayList<Segment>(list.size());
+
+ for (final Interval interval : list) {
+ final Vector3D start = line.toSpace((Point<Euclidean1D>) new Vector1D(interval.getInf()));
+ final Vector3D end = line.toSpace((Point<Euclidean1D>) new Vector1D(interval.getSup()));
+ segments.add(new Segment(start, end, line));
+ }
+
+ return segments;
+
+ }
+
+ /** Get the intersection of the instance and another sub-line.
+ * <p>
+ * This method is related to the {@link Line#intersection(Line)
+ * intersection} method in the {@link Line Line} class, but in addition
+ * to compute the point along infinite lines, it also checks the point
+ * lies on both sub-line ranges.
+ * </p>
+ * @param subLine other sub-line which may intersect instance
+ * @param includeEndPoints if true, endpoints are considered to belong to
+ * instance (i.e. they are closed sets) and may be returned, otherwise endpoints
+ * are considered to not belong to instance (i.e. they are open sets) and intersection
+ * occurring on endpoints lead to null being returned
+ * @return the intersection point if there is one, null if the sub-lines don't intersect
+ */
+ public Vector3D intersection(final SubLine subLine, final boolean includeEndPoints) {
+
+ // compute the intersection on infinite line
+ Vector3D v1D = line.intersection(subLine.line);
+ if (v1D == null) {
+ return null;
+ }
+
+ // check location of point with respect to first sub-line
+ Location loc1 = remainingRegion.checkPoint((Point<Euclidean1D>) line.toSubSpace((Point<Euclidean3D>) v1D));
+
+ // check location of point with respect to second sub-line
+ Location loc2 = subLine.remainingRegion.checkPoint((Point<Euclidean1D>) subLine.line.toSubSpace((Point<Euclidean3D>) v1D));
+
+ if (includeEndPoints) {
+ return ((loc1 != Location.OUTSIDE) && (loc2 != Location.OUTSIDE)) ? v1D : null;
+ } else {
+ return ((loc1 == Location.INSIDE) && (loc2 == Location.INSIDE)) ? v1D : null;
+ }
+
+ }
+
+ /** Build an interval set from two points.
+ * @param start start point
+ * @param end end point
+ * @return an interval set
+ * @param tolerance tolerance below which points are considered identical
+ * @exception MathIllegalArgumentException if the points are equal
+ */
+ private static IntervalsSet buildIntervalSet(final Vector3D start, final Vector3D end, final double tolerance)
+ throws MathIllegalArgumentException {
+ final Line line = new Line(start, end, tolerance);
+ return new IntervalsSet(line.toSubSpace((Point<Euclidean3D>) start).getX(),
+ line.toSubSpace((Point<Euclidean3D>) end).getX(),
+ tolerance);
+ }
+
+}
diff --git a/src/main/java/org/apache/commons/math3/geometry/euclidean/threed/SubPlane.java b/src/main/java/org/apache/commons/math3/geometry/euclidean/threed/SubPlane.java
new file mode 100644
index 0000000..ce02a38
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/geometry/euclidean/threed/SubPlane.java
@@ -0,0 +1,108 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math3.geometry.euclidean.threed;
+
+import org.apache.commons.math3.geometry.Point;
+import org.apache.commons.math3.geometry.euclidean.oned.Euclidean1D;
+import org.apache.commons.math3.geometry.euclidean.oned.Vector1D;
+import org.apache.commons.math3.geometry.euclidean.twod.Euclidean2D;
+import org.apache.commons.math3.geometry.euclidean.twod.PolygonsSet;
+import org.apache.commons.math3.geometry.euclidean.twod.Vector2D;
+import org.apache.commons.math3.geometry.partitioning.AbstractSubHyperplane;
+import org.apache.commons.math3.geometry.partitioning.BSPTree;
+import org.apache.commons.math3.geometry.partitioning.Hyperplane;
+import org.apache.commons.math3.geometry.partitioning.Region;
+import org.apache.commons.math3.geometry.partitioning.SubHyperplane;
+
+/** This class represents a sub-hyperplane for {@link Plane}.
+ * @since 3.0
+ */
+public class SubPlane extends AbstractSubHyperplane<Euclidean3D, Euclidean2D> {
+
+ /** Simple constructor.
+ * @param hyperplane underlying hyperplane
+ * @param remainingRegion remaining region of the hyperplane
+ */
+ public SubPlane(final Hyperplane<Euclidean3D> hyperplane,
+ final Region<Euclidean2D> remainingRegion) {
+ super(hyperplane, remainingRegion);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ protected AbstractSubHyperplane<Euclidean3D, Euclidean2D> buildNew(final Hyperplane<Euclidean3D> hyperplane,
+ final Region<Euclidean2D> remainingRegion) {
+ return new SubPlane(hyperplane, remainingRegion);
+ }
+
+ /** Split the instance in two parts by an hyperplane.
+ * @param hyperplane splitting hyperplane
+ * @return an object containing both the part of the instance
+ * on the plus side of the instance and the part of the
+ * instance on the minus side of the instance
+ */
+ @Override
+ public SplitSubHyperplane<Euclidean3D> split(Hyperplane<Euclidean3D> hyperplane) {
+
+ final Plane otherPlane = (Plane) hyperplane;
+ final Plane thisPlane = (Plane) getHyperplane();
+ final Line inter = otherPlane.intersection(thisPlane);
+ final double tolerance = thisPlane.getTolerance();
+
+ if (inter == null) {
+ // the hyperplanes are parallel
+ final double global = otherPlane.getOffset(thisPlane);
+ if (global < -tolerance) {
+ return new SplitSubHyperplane<Euclidean3D>(null, this);
+ } else if (global > tolerance) {
+ return new SplitSubHyperplane<Euclidean3D>(this, null);
+ } else {
+ return new SplitSubHyperplane<Euclidean3D>(null, null);
+ }
+ }
+
+ // the hyperplanes do intersect
+ Vector2D p = thisPlane.toSubSpace((Point<Euclidean3D>) inter.toSpace((Point<Euclidean1D>) Vector1D.ZERO));
+ Vector2D q = thisPlane.toSubSpace((Point<Euclidean3D>) inter.toSpace((Point<Euclidean1D>) Vector1D.ONE));
+ Vector3D crossP = Vector3D.crossProduct(inter.getDirection(), thisPlane.getNormal());
+ if (crossP.dotProduct(otherPlane.getNormal()) < 0) {
+ final Vector2D tmp = p;
+ p = q;
+ q = tmp;
+ }
+ final SubHyperplane<Euclidean2D> l2DMinus =
+ new org.apache.commons.math3.geometry.euclidean.twod.Line(p, q, tolerance).wholeHyperplane();
+ final SubHyperplane<Euclidean2D> l2DPlus =
+ new org.apache.commons.math3.geometry.euclidean.twod.Line(q, p, tolerance).wholeHyperplane();
+
+ final BSPTree<Euclidean2D> splitTree = getRemainingRegion().getTree(false).split(l2DMinus);
+ final BSPTree<Euclidean2D> plusTree = getRemainingRegion().isEmpty(splitTree.getPlus()) ?
+ new BSPTree<Euclidean2D>(Boolean.FALSE) :
+ new BSPTree<Euclidean2D>(l2DPlus, new BSPTree<Euclidean2D>(Boolean.FALSE),
+ splitTree.getPlus(), null);
+
+ final BSPTree<Euclidean2D> minusTree = getRemainingRegion().isEmpty(splitTree.getMinus()) ?
+ new BSPTree<Euclidean2D>(Boolean.FALSE) :
+ new BSPTree<Euclidean2D>(l2DMinus, new BSPTree<Euclidean2D>(Boolean.FALSE),
+ splitTree.getMinus(), null);
+
+ return new SplitSubHyperplane<Euclidean3D>(new SubPlane(thisPlane.copySelf(), new PolygonsSet(plusTree, tolerance)),
+ new SubPlane(thisPlane.copySelf(), new PolygonsSet(minusTree, tolerance)));
+
+ }
+
+}
diff --git a/src/main/java/org/apache/commons/math3/geometry/euclidean/threed/Vector3D.java b/src/main/java/org/apache/commons/math3/geometry/euclidean/threed/Vector3D.java
new file mode 100644
index 0000000..3eaea3a
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/geometry/euclidean/threed/Vector3D.java
@@ -0,0 +1,588 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math3.geometry.euclidean.threed;
+
+import java.io.Serializable;
+import java.text.NumberFormat;
+
+import org.apache.commons.math3.exception.DimensionMismatchException;
+import org.apache.commons.math3.exception.MathArithmeticException;
+import org.apache.commons.math3.exception.util.LocalizedFormats;
+import org.apache.commons.math3.geometry.Point;
+import org.apache.commons.math3.geometry.Space;
+import org.apache.commons.math3.geometry.Vector;
+import org.apache.commons.math3.util.FastMath;
+import org.apache.commons.math3.util.MathArrays;
+import org.apache.commons.math3.util.MathUtils;
+
+/**
+ * This class implements vectors in a three-dimensional space.
+ * <p>Instance of this class are guaranteed to be immutable.</p>
+ * @since 1.2
+ */
+public class Vector3D implements Serializable, Vector<Euclidean3D> {
+
+ /** Null vector (coordinates: 0, 0, 0). */
+ public static final Vector3D ZERO = new Vector3D(0, 0, 0);
+
+ /** First canonical vector (coordinates: 1, 0, 0). */
+ public static final Vector3D PLUS_I = new Vector3D(1, 0, 0);
+
+ /** Opposite of the first canonical vector (coordinates: -1, 0, 0). */
+ public static final Vector3D MINUS_I = new Vector3D(-1, 0, 0);
+
+ /** Second canonical vector (coordinates: 0, 1, 0). */
+ public static final Vector3D PLUS_J = new Vector3D(0, 1, 0);
+
+ /** Opposite of the second canonical vector (coordinates: 0, -1, 0). */
+ public static final Vector3D MINUS_J = new Vector3D(0, -1, 0);
+
+ /** Third canonical vector (coordinates: 0, 0, 1). */
+ public static final Vector3D PLUS_K = new Vector3D(0, 0, 1);
+
+ /** Opposite of the third canonical vector (coordinates: 0, 0, -1). */
+ public static final Vector3D MINUS_K = new Vector3D(0, 0, -1);
+
+ // CHECKSTYLE: stop ConstantName
+ /** A vector with all coordinates set to NaN. */
+ public static final Vector3D NaN = new Vector3D(Double.NaN, Double.NaN, Double.NaN);
+ // CHECKSTYLE: resume ConstantName
+
+ /** A vector with all coordinates set to positive infinity. */
+ public static final Vector3D POSITIVE_INFINITY =
+ new Vector3D(Double.POSITIVE_INFINITY, Double.POSITIVE_INFINITY, Double.POSITIVE_INFINITY);
+
+ /** A vector with all coordinates set to negative infinity. */
+ public static final Vector3D NEGATIVE_INFINITY =
+ new Vector3D(Double.NEGATIVE_INFINITY, Double.NEGATIVE_INFINITY, Double.NEGATIVE_INFINITY);
+
+ /** Serializable version identifier. */
+ private static final long serialVersionUID = 1313493323784566947L;
+
+ /** Abscissa. */
+ private final double x;
+
+ /** Ordinate. */
+ private final double y;
+
+ /** Height. */
+ private final double z;
+
+ /** Simple constructor.
+ * Build a vector from its coordinates
+ * @param x abscissa
+ * @param y ordinate
+ * @param z height
+ * @see #getX()
+ * @see #getY()
+ * @see #getZ()
+ */
+ public Vector3D(double x, double y, double z) {
+ this.x = x;
+ this.y = y;
+ this.z = z;
+ }
+
+ /** Simple constructor.
+ * Build a vector from its coordinates
+ * @param v coordinates array
+ * @exception DimensionMismatchException if array does not have 3 elements
+ * @see #toArray()
+ */
+ public Vector3D(double[] v) throws DimensionMismatchException {
+ if (v.length != 3) {
+ throw new DimensionMismatchException(v.length, 3);
+ }
+ this.x = v[0];
+ this.y = v[1];
+ this.z = v[2];
+ }
+
+ /** Simple constructor.
+ * Build a vector from its azimuthal coordinates
+ * @param alpha azimuth (&alpha;) around Z
+ * (0 is +X, &pi;/2 is +Y, &pi; is -X and 3&pi;/2 is -Y)
+ * @param delta elevation (&delta;) above (XY) plane, from -&pi;/2 to +&pi;/2
+ * @see #getAlpha()
+ * @see #getDelta()
+ */
+ public Vector3D(double alpha, double delta) {
+ double cosDelta = FastMath.cos(delta);
+ this.x = FastMath.cos(alpha) * cosDelta;
+ this.y = FastMath.sin(alpha) * cosDelta;
+ this.z = FastMath.sin(delta);
+ }
+
+ /** Multiplicative constructor
+ * Build a vector from another one and a scale factor.
+ * The vector built will be a * u
+ * @param a scale factor
+ * @param u base (unscaled) vector
+ */
+ public Vector3D(double a, Vector3D u) {
+ this.x = a * u.x;
+ this.y = a * u.y;
+ this.z = a * u.z;
+ }
+
+ /** Linear constructor
+ * Build a vector from two other ones and corresponding scale factors.
+ * The vector built will be a1 * u1 + a2 * u2
+ * @param a1 first scale factor
+ * @param u1 first base (unscaled) vector
+ * @param a2 second scale factor
+ * @param u2 second base (unscaled) vector
+ */
+ public Vector3D(double a1, Vector3D u1, double a2, Vector3D u2) {
+ this.x = MathArrays.linearCombination(a1, u1.x, a2, u2.x);
+ this.y = MathArrays.linearCombination(a1, u1.y, a2, u2.y);
+ this.z = MathArrays.linearCombination(a1, u1.z, a2, u2.z);
+ }
+
+ /** Linear constructor
+ * Build a vector from three other ones and corresponding scale factors.
+ * The vector built will be a1 * u1 + a2 * u2 + a3 * u3
+ * @param a1 first scale factor
+ * @param u1 first base (unscaled) vector
+ * @param a2 second scale factor
+ * @param u2 second base (unscaled) vector
+ * @param a3 third scale factor
+ * @param u3 third base (unscaled) vector
+ */
+ public Vector3D(double a1, Vector3D u1, double a2, Vector3D u2,
+ double a3, Vector3D u3) {
+ this.x = MathArrays.linearCombination(a1, u1.x, a2, u2.x, a3, u3.x);
+ this.y = MathArrays.linearCombination(a1, u1.y, a2, u2.y, a3, u3.y);
+ this.z = MathArrays.linearCombination(a1, u1.z, a2, u2.z, a3, u3.z);
+ }
+
+ /** Linear constructor
+ * Build a vector from four other ones and corresponding scale factors.
+ * The vector built will be a1 * u1 + a2 * u2 + a3 * u3 + a4 * u4
+ * @param a1 first scale factor
+ * @param u1 first base (unscaled) vector
+ * @param a2 second scale factor
+ * @param u2 second base (unscaled) vector
+ * @param a3 third scale factor
+ * @param u3 third base (unscaled) vector
+ * @param a4 fourth scale factor
+ * @param u4 fourth base (unscaled) vector
+ */
+ public Vector3D(double a1, Vector3D u1, double a2, Vector3D u2,
+ double a3, Vector3D u3, double a4, Vector3D u4) {
+ this.x = MathArrays.linearCombination(a1, u1.x, a2, u2.x, a3, u3.x, a4, u4.x);
+ this.y = MathArrays.linearCombination(a1, u1.y, a2, u2.y, a3, u3.y, a4, u4.y);
+ this.z = MathArrays.linearCombination(a1, u1.z, a2, u2.z, a3, u3.z, a4, u4.z);
+ }
+
+ /** Get the abscissa of the vector.
+ * @return abscissa of the vector
+ * @see #Vector3D(double, double, double)
+ */
+ public double getX() {
+ return x;
+ }
+
+ /** Get the ordinate of the vector.
+ * @return ordinate of the vector
+ * @see #Vector3D(double, double, double)
+ */
+ public double getY() {
+ return y;
+ }
+
+ /** Get the height of the vector.
+ * @return height of the vector
+ * @see #Vector3D(double, double, double)
+ */
+ public double getZ() {
+ return z;
+ }
+
+ /** Get the vector coordinates as a dimension 3 array.
+ * @return vector coordinates
+ * @see #Vector3D(double[])
+ */
+ public double[] toArray() {
+ return new double[] { x, y, z };
+ }
+
+ /** {@inheritDoc} */
+ public Space getSpace() {
+ return Euclidean3D.getInstance();
+ }
+
+ /** {@inheritDoc} */
+ public Vector3D getZero() {
+ return ZERO;
+ }
+
+ /** {@inheritDoc} */
+ public double getNorm1() {
+ return FastMath.abs(x) + FastMath.abs(y) + FastMath.abs(z);
+ }
+
+ /** {@inheritDoc} */
+ public double getNorm() {
+ // there are no cancellation problems here, so we use the straightforward formula
+ return FastMath.sqrt (x * x + y * y + z * z);
+ }
+
+ /** {@inheritDoc} */
+ public double getNormSq() {
+ // there are no cancellation problems here, so we use the straightforward formula
+ return x * x + y * y + z * z;
+ }
+
+ /** {@inheritDoc} */
+ public double getNormInf() {
+ return FastMath.max(FastMath.max(FastMath.abs(x), FastMath.abs(y)), FastMath.abs(z));
+ }
+
+ /** Get the azimuth of the vector.
+ * @return azimuth (&alpha;) of the vector, between -&pi; and +&pi;
+ * @see #Vector3D(double, double)
+ */
+ public double getAlpha() {
+ return FastMath.atan2(y, x);
+ }
+
+ /** Get the elevation of the vector.
+ * @return elevation (&delta;) of the vector, between -&pi;/2 and +&pi;/2
+ * @see #Vector3D(double, double)
+ */
+ public double getDelta() {
+ return FastMath.asin(z / getNorm());
+ }
+
+ /** {@inheritDoc} */
+ public Vector3D add(final Vector<Euclidean3D> v) {
+ final Vector3D v3 = (Vector3D) v;
+ return new Vector3D(x + v3.x, y + v3.y, z + v3.z);
+ }
+
+ /** {@inheritDoc} */
+ public Vector3D add(double factor, final Vector<Euclidean3D> v) {
+ return new Vector3D(1, this, factor, (Vector3D) v);
+ }
+
+ /** {@inheritDoc} */
+ public Vector3D subtract(final Vector<Euclidean3D> v) {
+ final Vector3D v3 = (Vector3D) v;
+ return new Vector3D(x - v3.x, y - v3.y, z - v3.z);
+ }
+
+ /** {@inheritDoc} */
+ public Vector3D subtract(final double factor, final Vector<Euclidean3D> v) {
+ return new Vector3D(1, this, -factor, (Vector3D) v);
+ }
+
+ /** {@inheritDoc} */
+ public Vector3D normalize() throws MathArithmeticException {
+ double s = getNorm();
+ if (s == 0) {
+ throw new MathArithmeticException(LocalizedFormats.CANNOT_NORMALIZE_A_ZERO_NORM_VECTOR);
+ }
+ return scalarMultiply(1 / s);
+ }
+
+ /** Get a vector orthogonal to the instance.
+ * <p>There are an infinite number of normalized vectors orthogonal
+ * to the instance. This method picks up one of them almost
+ * arbitrarily. It is useful when one needs to compute a reference
+ * frame with one of the axes in a predefined direction. The
+ * following example shows how to build a frame having the k axis
+ * aligned with the known vector u :
+ * <pre><code>
+ * Vector3D k = u.normalize();
+ * Vector3D i = k.orthogonal();
+ * Vector3D j = Vector3D.crossProduct(k, i);
+ * </code></pre></p>
+ * @return a new normalized vector orthogonal to the instance
+ * @exception MathArithmeticException if the norm of the instance is null
+ */
+ public Vector3D orthogonal() throws MathArithmeticException {
+
+ double threshold = 0.6 * getNorm();
+ if (threshold == 0) {
+ throw new MathArithmeticException(LocalizedFormats.ZERO_NORM);
+ }
+
+ if (FastMath.abs(x) <= threshold) {
+ double inverse = 1 / FastMath.sqrt(y * y + z * z);
+ return new Vector3D(0, inverse * z, -inverse * y);
+ } else if (FastMath.abs(y) <= threshold) {
+ double inverse = 1 / FastMath.sqrt(x * x + z * z);
+ return new Vector3D(-inverse * z, 0, inverse * x);
+ }
+ double inverse = 1 / FastMath.sqrt(x * x + y * y);
+ return new Vector3D(inverse * y, -inverse * x, 0);
+
+ }
+
+ /** Compute the angular separation between two vectors.
+ * <p>This method computes the angular separation between two
+ * vectors using the dot product for well separated vectors and the
+ * cross product for almost aligned vectors. This allows to have a
+ * good accuracy in all cases, even for vectors very close to each
+ * other.</p>
+ * @param v1 first vector
+ * @param v2 second vector
+ * @return angular separation between v1 and v2
+ * @exception MathArithmeticException if either vector has a null norm
+ */
+ public static double angle(Vector3D v1, Vector3D v2) throws MathArithmeticException {
+
+ double normProduct = v1.getNorm() * v2.getNorm();
+ if (normProduct == 0) {
+ throw new MathArithmeticException(LocalizedFormats.ZERO_NORM);
+ }
+
+ double dot = v1.dotProduct(v2);
+ double threshold = normProduct * 0.9999;
+ if ((dot < -threshold) || (dot > threshold)) {
+ // the vectors are almost aligned, compute using the sine
+ Vector3D v3 = crossProduct(v1, v2);
+ if (dot >= 0) {
+ return FastMath.asin(v3.getNorm() / normProduct);
+ }
+ return FastMath.PI - FastMath.asin(v3.getNorm() / normProduct);
+ }
+
+ // the vectors are sufficiently separated to use the cosine
+ return FastMath.acos(dot / normProduct);
+
+ }
+
+ /** {@inheritDoc} */
+ public Vector3D negate() {
+ return new Vector3D(-x, -y, -z);
+ }
+
+ /** {@inheritDoc} */
+ public Vector3D scalarMultiply(double a) {
+ return new Vector3D(a * x, a * y, a * z);
+ }
+
+ /** {@inheritDoc} */
+ public boolean isNaN() {
+ return Double.isNaN(x) || Double.isNaN(y) || Double.isNaN(z);
+ }
+
+ /** {@inheritDoc} */
+ public boolean isInfinite() {
+ return !isNaN() && (Double.isInfinite(x) || Double.isInfinite(y) || Double.isInfinite(z));
+ }
+
+ /**
+ * Test for the equality of two 3D vectors.
+ * <p>
+ * If all coordinates of two 3D vectors are exactly the same, and none are
+ * <code>Double.NaN</code>, the two 3D vectors are considered to be equal.
+ * </p>
+ * <p>
+ * <code>NaN</code> coordinates are considered to affect globally the vector
+ * and be equals to each other - i.e, if either (or all) coordinates of the
+ * 3D vector are equal to <code>Double.NaN</code>, the 3D vector is equal to
+ * {@link #NaN}.
+ * </p>
+ *
+ * @param other Object to test for equality to this
+ * @return true if two 3D vector objects are equal, false if
+ * object is null, not an instance of Vector3D, or
+ * not equal to this Vector3D instance
+ *
+ */
+ @Override
+ public boolean equals(Object other) {
+
+ if (this == other) {
+ return true;
+ }
+
+ if (other instanceof Vector3D) {
+ final Vector3D rhs = (Vector3D)other;
+ if (rhs.isNaN()) {
+ return this.isNaN();
+ }
+
+ return (x == rhs.x) && (y == rhs.y) && (z == rhs.z);
+ }
+ return false;
+ }
+
+ /**
+ * Get a hashCode for the 3D vector.
+ * <p>
+ * All NaN values have the same hash code.</p>
+ *
+ * @return a hash code value for this object
+ */
+ @Override
+ public int hashCode() {
+ if (isNaN()) {
+ return 642;
+ }
+ return 643 * (164 * MathUtils.hash(x) + 3 * MathUtils.hash(y) + MathUtils.hash(z));
+ }
+
+ /** {@inheritDoc}
+ * <p>
+ * The implementation uses specific multiplication and addition
+ * algorithms to preserve accuracy and reduce cancellation effects.
+ * It should be very accurate even for nearly orthogonal vectors.
+ * </p>
+ * @see MathArrays#linearCombination(double, double, double, double, double, double)
+ */
+ public double dotProduct(final Vector<Euclidean3D> v) {
+ final Vector3D v3 = (Vector3D) v;
+ return MathArrays.linearCombination(x, v3.x, y, v3.y, z, v3.z);
+ }
+
+ /** Compute the cross-product of the instance with another vector.
+ * @param v other vector
+ * @return the cross product this ^ v as a new Vector3D
+ */
+ public Vector3D crossProduct(final Vector<Euclidean3D> v) {
+ final Vector3D v3 = (Vector3D) v;
+ return new Vector3D(MathArrays.linearCombination(y, v3.z, -z, v3.y),
+ MathArrays.linearCombination(z, v3.x, -x, v3.z),
+ MathArrays.linearCombination(x, v3.y, -y, v3.x));
+ }
+
+ /** {@inheritDoc} */
+ public double distance1(Vector<Euclidean3D> v) {
+ final Vector3D v3 = (Vector3D) v;
+ final double dx = FastMath.abs(v3.x - x);
+ final double dy = FastMath.abs(v3.y - y);
+ final double dz = FastMath.abs(v3.z - z);
+ return dx + dy + dz;
+ }
+
+ /** {@inheritDoc} */
+ public double distance(Vector<Euclidean3D> v) {
+ return distance((Point<Euclidean3D>) v);
+ }
+
+ /** {@inheritDoc} */
+ public double distance(Point<Euclidean3D> v) {
+ final Vector3D v3 = (Vector3D) v;
+ final double dx = v3.x - x;
+ final double dy = v3.y - y;
+ final double dz = v3.z - z;
+ return FastMath.sqrt(dx * dx + dy * dy + dz * dz);
+ }
+
+ /** {@inheritDoc} */
+ public double distanceInf(Vector<Euclidean3D> v) {
+ final Vector3D v3 = (Vector3D) v;
+ final double dx = FastMath.abs(v3.x - x);
+ final double dy = FastMath.abs(v3.y - y);
+ final double dz = FastMath.abs(v3.z - z);
+ return FastMath.max(FastMath.max(dx, dy), dz);
+ }
+
+ /** {@inheritDoc} */
+ public double distanceSq(Vector<Euclidean3D> v) {
+ final Vector3D v3 = (Vector3D) v;
+ final double dx = v3.x - x;
+ final double dy = v3.y - y;
+ final double dz = v3.z - z;
+ return dx * dx + dy * dy + dz * dz;
+ }
+
+ /** Compute the dot-product of two vectors.
+ * @param v1 first vector
+ * @param v2 second vector
+ * @return the dot product v1.v2
+ */
+ public static double dotProduct(Vector3D v1, Vector3D v2) {
+ return v1.dotProduct(v2);
+ }
+
+ /** Compute the cross-product of two vectors.
+ * @param v1 first vector
+ * @param v2 second vector
+ * @return the cross product v1 ^ v2 as a new Vector
+ */
+ public static Vector3D crossProduct(final Vector3D v1, final Vector3D v2) {
+ return v1.crossProduct(v2);
+ }
+
+ /** Compute the distance between two vectors according to the L<sub>1</sub> norm.
+ * <p>Calling this method is equivalent to calling:
+ * <code>v1.subtract(v2).getNorm1()</code> except that no intermediate
+ * vector is built</p>
+ * @param v1 first vector
+ * @param v2 second vector
+ * @return the distance between v1 and v2 according to the L<sub>1</sub> norm
+ */
+ public static double distance1(Vector3D v1, Vector3D v2) {
+ return v1.distance1(v2);
+ }
+
+ /** Compute the distance between two vectors according to the L<sub>2</sub> norm.
+ * <p>Calling this method is equivalent to calling:
+ * <code>v1.subtract(v2).getNorm()</code> except that no intermediate
+ * vector is built</p>
+ * @param v1 first vector
+ * @param v2 second vector
+ * @return the distance between v1 and v2 according to the L<sub>2</sub> norm
+ */
+ public static double distance(Vector3D v1, Vector3D v2) {
+ return v1.distance(v2);
+ }
+
+ /** Compute the distance between two vectors according to the L<sub>&infin;</sub> norm.
+ * <p>Calling this method is equivalent to calling:
+ * <code>v1.subtract(v2).getNormInf()</code> except that no intermediate
+ * vector is built</p>
+ * @param v1 first vector
+ * @param v2 second vector
+ * @return the distance between v1 and v2 according to the L<sub>&infin;</sub> norm
+ */
+ public static double distanceInf(Vector3D v1, Vector3D v2) {
+ return v1.distanceInf(v2);
+ }
+
+ /** Compute the square of the distance between two vectors.
+ * <p>Calling this method is equivalent to calling:
+ * <code>v1.subtract(v2).getNormSq()</code> except that no intermediate
+ * vector is built</p>
+ * @param v1 first vector
+ * @param v2 second vector
+ * @return the square of the distance between v1 and v2
+ */
+ public static double distanceSq(Vector3D v1, Vector3D v2) {
+ return v1.distanceSq(v2);
+ }
+
+ /** Get a string representation of this vector.
+ * @return a string representation of this vector
+ */
+ @Override
+ public String toString() {
+ return Vector3DFormat.getInstance().format(this);
+ }
+
+ /** {@inheritDoc} */
+ public String toString(final NumberFormat format) {
+ return new Vector3DFormat(format).format(this);
+ }
+
+}
diff --git a/src/main/java/org/apache/commons/math3/geometry/euclidean/threed/Vector3DFormat.java b/src/main/java/org/apache/commons/math3/geometry/euclidean/threed/Vector3DFormat.java
new file mode 100644
index 0000000..da3f71e
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/geometry/euclidean/threed/Vector3DFormat.java
@@ -0,0 +1,155 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math3.geometry.euclidean.threed;
+
+import java.text.FieldPosition;
+import java.text.NumberFormat;
+import java.text.ParsePosition;
+import java.util.Locale;
+
+import org.apache.commons.math3.exception.MathParseException;
+import org.apache.commons.math3.geometry.Vector;
+import org.apache.commons.math3.geometry.VectorFormat;
+import org.apache.commons.math3.util.CompositeFormat;
+
+/**
+ * Formats a 3D vector in components list format "{x; y; z}".
+ * <p>The prefix and suffix "{" and "}" and the separator "; " can be replaced by
+ * any user-defined strings. The number format for components can be configured.</p>
+ * <p>White space is ignored at parse time, even if it is in the prefix, suffix
+ * or separator specifications. So even if the default separator does include a space
+ * character that is used at format time, both input string "{1;1;1}" and
+ * " { 1 ; 1 ; 1 } " will be parsed without error and the same vector will be
+ * returned. In the second case, however, the parse position after parsing will be
+ * just after the closing curly brace, i.e. just before the trailing space.</p>
+ * <p><b>Note:</b> using "," as a separator may interfere with the grouping separator
+ * of the default {@link NumberFormat} for the current locale. Thus it is advised
+ * to use a {@link NumberFormat} instance with disabled grouping in such a case.</p>
+ *
+ */
+public class Vector3DFormat extends VectorFormat<Euclidean3D> {
+
+ /**
+ * Create an instance with default settings.
+ * <p>The instance uses the default prefix, suffix and separator:
+ * "{", "}", and "; " and the default number format for components.</p>
+ */
+ public Vector3DFormat() {
+ super(DEFAULT_PREFIX, DEFAULT_SUFFIX, DEFAULT_SEPARATOR,
+ CompositeFormat.getDefaultNumberFormat());
+ }
+
+ /**
+ * Create an instance with a custom number format for components.
+ * @param format the custom format for components.
+ */
+ public Vector3DFormat(final NumberFormat format) {
+ super(DEFAULT_PREFIX, DEFAULT_SUFFIX, DEFAULT_SEPARATOR, format);
+ }
+
+ /**
+ * Create an instance with custom prefix, suffix and separator.
+ * @param prefix prefix to use instead of the default "{"
+ * @param suffix suffix to use instead of the default "}"
+ * @param separator separator to use instead of the default "; "
+ */
+ public Vector3DFormat(final String prefix, final String suffix,
+ final String separator) {
+ super(prefix, suffix, separator, CompositeFormat.getDefaultNumberFormat());
+ }
+
+ /**
+ * Create an instance with custom prefix, suffix, separator and format
+ * for components.
+ * @param prefix prefix to use instead of the default "{"
+ * @param suffix suffix to use instead of the default "}"
+ * @param separator separator to use instead of the default "; "
+ * @param format the custom format for components.
+ */
+ public Vector3DFormat(final String prefix, final String suffix,
+ final String separator, final NumberFormat format) {
+ super(prefix, suffix, separator, format);
+ }
+
+ /**
+ * Returns the default 3D vector format for the current locale.
+ * @return the default 3D vector format.
+ */
+ public static Vector3DFormat getInstance() {
+ return getInstance(Locale.getDefault());
+ }
+
+ /**
+ * Returns the default 3D vector format for the given locale.
+ * @param locale the specific locale used by the format.
+ * @return the 3D vector format specific to the given locale.
+ */
+ public static Vector3DFormat getInstance(final Locale locale) {
+ return new Vector3DFormat(CompositeFormat.getDefaultNumberFormat(locale));
+ }
+
+ /**
+ * Formats a {@link Vector3D} object to produce a string.
+ * @param vector the object to format.
+ * @param toAppendTo where the text is to be appended
+ * @param pos On input: an alignment field, if desired. On output: the
+ * offsets of the alignment field
+ * @return the value passed in as toAppendTo.
+ */
+ @Override
+ public StringBuffer format(final Vector<Euclidean3D> vector, final StringBuffer toAppendTo,
+ final FieldPosition pos) {
+ final Vector3D v3 = (Vector3D) vector;
+ return format(toAppendTo, pos, v3.getX(), v3.getY(), v3.getZ());
+ }
+
+ /**
+ * Parses a string to produce a {@link Vector3D} object.
+ * @param source the string to parse
+ * @return the parsed {@link Vector3D} object.
+ * @throws MathParseException if the beginning of the specified string
+ * cannot be parsed.
+ */
+ @Override
+ public Vector3D parse(final String source) throws MathParseException {
+ ParsePosition parsePosition = new ParsePosition(0);
+ Vector3D result = parse(source, parsePosition);
+ if (parsePosition.getIndex() == 0) {
+ throw new MathParseException(source,
+ parsePosition.getErrorIndex(),
+ Vector3D.class);
+ }
+ return result;
+ }
+
+ /**
+ * Parses a string to produce a {@link Vector3D} object.
+ * @param source the string to parse
+ * @param pos input/ouput parsing parameter.
+ * @return the parsed {@link Vector3D} object.
+ */
+ @Override
+ public Vector3D parse(final String source, final ParsePosition pos) {
+ final double[] coordinates = parseCoordinates(3, source, pos);
+ if (coordinates == null) {
+ return null;
+ }
+ return new Vector3D(coordinates[0], coordinates[1], coordinates[2]);
+ }
+
+}
diff --git a/src/main/java/org/apache/commons/math3/geometry/euclidean/threed/package-info.java b/src/main/java/org/apache/commons/math3/geometry/euclidean/threed/package-info.java
new file mode 100644
index 0000000..eaa3c6a
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/geometry/euclidean/threed/package-info.java
@@ -0,0 +1,24 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/**
+ *
+ * <p>
+ * This package provides basic 3D geometry components.
+ * </p>
+ *
+ */
+package org.apache.commons.math3.geometry.euclidean.threed;
diff --git a/src/main/java/org/apache/commons/math3/geometry/euclidean/twod/DiskGenerator.java b/src/main/java/org/apache/commons/math3/geometry/euclidean/twod/DiskGenerator.java
new file mode 100644
index 0000000..332b1b7
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/geometry/euclidean/twod/DiskGenerator.java
@@ -0,0 +1,108 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math3.geometry.euclidean.twod;
+
+import java.util.List;
+
+import org.apache.commons.math3.fraction.BigFraction;
+import org.apache.commons.math3.geometry.enclosing.EnclosingBall;
+import org.apache.commons.math3.geometry.enclosing.SupportBallGenerator;
+import org.apache.commons.math3.util.FastMath;
+
+/** Class generating an enclosing ball from its support points.
+ * @since 3.3
+ */
+public class DiskGenerator implements SupportBallGenerator<Euclidean2D, Vector2D> {
+
+ /** {@inheritDoc} */
+ public EnclosingBall<Euclidean2D, Vector2D> ballOnSupport(final List<Vector2D> support) {
+
+ if (support.size() < 1) {
+ return new EnclosingBall<Euclidean2D, Vector2D>(Vector2D.ZERO, Double.NEGATIVE_INFINITY);
+ } else {
+ final Vector2D vA = support.get(0);
+ if (support.size() < 2) {
+ return new EnclosingBall<Euclidean2D, Vector2D>(vA, 0, vA);
+ } else {
+ final Vector2D vB = support.get(1);
+ if (support.size() < 3) {
+ return new EnclosingBall<Euclidean2D, Vector2D>(new Vector2D(0.5, vA, 0.5, vB),
+ 0.5 * vA.distance(vB),
+ vA, vB);
+ } else {
+ final Vector2D vC = support.get(2);
+ // a disk is 2D can be defined as:
+ // (1) (x - x_0)^2 + (y - y_0)^2 = r^2
+ // which can be written:
+ // (2) (x^2 + y^2) - 2 x_0 x - 2 y_0 y + (x_0^2 + y_0^2 - r^2) = 0
+ // or simply:
+ // (3) (x^2 + y^2) + a x + b y + c = 0
+ // with disk center coordinates -a/2, -b/2
+ // If the disk exists, a, b and c are a non-zero solution to
+ // [ (x^2 + y^2 ) x y 1 ] [ 1 ] [ 0 ]
+ // [ (xA^2 + yA^2) xA yA 1 ] [ a ] [ 0 ]
+ // [ (xB^2 + yB^2) xB yB 1 ] * [ b ] = [ 0 ]
+ // [ (xC^2 + yC^2) xC yC 1 ] [ c ] [ 0 ]
+ // So the determinant of the matrix is zero. Computing this determinant
+ // by expanding it using the minors m_ij of first row leads to
+ // (4) m_11 (x^2 + y^2) - m_12 x + m_13 y - m_14 = 0
+ // So by identifying equations (2) and (4) we get the coordinates
+ // of center as:
+ // x_0 = +m_12 / (2 m_11)
+ // y_0 = -m_13 / (2 m_11)
+ // Note that the minors m_11, m_12 and m_13 all have the last column
+ // filled with 1.0, hence simplifying the computation
+ final BigFraction[] c2 = new BigFraction[] {
+ new BigFraction(vA.getX()), new BigFraction(vB.getX()), new BigFraction(vC.getX())
+ };
+ final BigFraction[] c3 = new BigFraction[] {
+ new BigFraction(vA.getY()), new BigFraction(vB.getY()), new BigFraction(vC.getY())
+ };
+ final BigFraction[] c1 = new BigFraction[] {
+ c2[0].multiply(c2[0]).add(c3[0].multiply(c3[0])),
+ c2[1].multiply(c2[1]).add(c3[1].multiply(c3[1])),
+ c2[2].multiply(c2[2]).add(c3[2].multiply(c3[2]))
+ };
+ final BigFraction twoM11 = minor(c2, c3).multiply(2);
+ final BigFraction m12 = minor(c1, c3);
+ final BigFraction m13 = minor(c1, c2);
+ final BigFraction centerX = m12.divide(twoM11);
+ final BigFraction centerY = m13.divide(twoM11).negate();
+ final BigFraction dx = c2[0].subtract(centerX);
+ final BigFraction dy = c3[0].subtract(centerY);
+ final BigFraction r2 = dx.multiply(dx).add(dy.multiply(dy));
+ return new EnclosingBall<Euclidean2D, Vector2D>(new Vector2D(centerX.doubleValue(),
+ centerY.doubleValue()),
+ FastMath.sqrt(r2.doubleValue()),
+ vA, vB, vC);
+ }
+ }
+ }
+ }
+
+ /** Compute a dimension 3 minor, when 3<sup>d</sup> column is known to be filled with 1.0.
+ * @param c1 first column
+ * @param c2 second column
+ * @return value of the minor computed has an exact fraction
+ */
+ private BigFraction minor(final BigFraction[] c1, final BigFraction[] c2) {
+ return c2[0].multiply(c1[2].subtract(c1[1])).
+ add(c2[1].multiply(c1[0].subtract(c1[2]))).
+ add(c2[2].multiply(c1[1].subtract(c1[0])));
+ }
+
+}
diff --git a/src/main/java/org/apache/commons/math3/geometry/euclidean/twod/Euclidean2D.java b/src/main/java/org/apache/commons/math3/geometry/euclidean/twod/Euclidean2D.java
new file mode 100644
index 0000000..af7630d
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/geometry/euclidean/twod/Euclidean2D.java
@@ -0,0 +1,74 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math3.geometry.euclidean.twod;
+
+import java.io.Serializable;
+
+import org.apache.commons.math3.geometry.Space;
+import org.apache.commons.math3.geometry.euclidean.oned.Euclidean1D;
+
+/**
+ * This class implements a two-dimensional space.
+ * @since 3.0
+ */
+public class Euclidean2D implements Serializable, Space {
+
+ /** Serializable version identifier. */
+ private static final long serialVersionUID = 4793432849757649566L;
+
+ /** Private constructor for the singleton.
+ */
+ private Euclidean2D() {
+ }
+
+ /** Get the unique instance.
+ * @return the unique instance
+ */
+ public static Euclidean2D getInstance() {
+ return LazyHolder.INSTANCE;
+ }
+
+ /** {@inheritDoc} */
+ public int getDimension() {
+ return 2;
+ }
+
+ /** {@inheritDoc} */
+ public Euclidean1D getSubSpace() {
+ return Euclidean1D.getInstance();
+ }
+
+ // CHECKSTYLE: stop HideUtilityClassConstructor
+ /** Holder for the instance.
+ * <p>We use here the Initialization On Demand Holder Idiom.</p>
+ */
+ private static class LazyHolder {
+ /** Cached field instance. */
+ private static final Euclidean2D INSTANCE = new Euclidean2D();
+ }
+ // CHECKSTYLE: resume HideUtilityClassConstructor
+
+ /** Handle deserialization of the singleton.
+ * @return the singleton instance
+ */
+ private Object readResolve() {
+ // return the singleton instance
+ return LazyHolder.INSTANCE;
+ }
+
+}
diff --git a/src/main/java/org/apache/commons/math3/geometry/euclidean/twod/Line.java b/src/main/java/org/apache/commons/math3/geometry/euclidean/twod/Line.java
new file mode 100644
index 0000000..c300fa1
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/geometry/euclidean/twod/Line.java
@@ -0,0 +1,587 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math3.geometry.euclidean.twod;
+
+import java.awt.geom.AffineTransform;
+
+import org.apache.commons.math3.exception.MathIllegalArgumentException;
+import org.apache.commons.math3.exception.util.LocalizedFormats;
+import org.apache.commons.math3.geometry.Point;
+import org.apache.commons.math3.geometry.Vector;
+import org.apache.commons.math3.geometry.euclidean.oned.Euclidean1D;
+import org.apache.commons.math3.geometry.euclidean.oned.IntervalsSet;
+import org.apache.commons.math3.geometry.euclidean.oned.OrientedPoint;
+import org.apache.commons.math3.geometry.euclidean.oned.Vector1D;
+import org.apache.commons.math3.geometry.partitioning.Embedding;
+import org.apache.commons.math3.geometry.partitioning.Hyperplane;
+import org.apache.commons.math3.geometry.partitioning.SubHyperplane;
+import org.apache.commons.math3.geometry.partitioning.Transform;
+import org.apache.commons.math3.util.FastMath;
+import org.apache.commons.math3.util.MathArrays;
+import org.apache.commons.math3.util.MathUtils;
+
+/** This class represents an oriented line in the 2D plane.
+
+ * <p>An oriented line can be defined either by prolongating a line
+ * segment between two points past these points, or by one point and
+ * an angular direction (in trigonometric orientation).</p>
+
+ * <p>Since it is oriented the two half planes at its two sides are
+ * unambiguously identified as a left half plane and a right half
+ * plane. This can be used to identify the interior and the exterior
+ * in a simple way by local properties only when part of a line is
+ * used to define part of a polygon boundary.</p>
+
+ * <p>A line can also be used to completely define a reference frame
+ * in the plane. It is sufficient to select one specific point in the
+ * line (the orthogonal projection of the original reference frame on
+ * the line) and to use the unit vector in the line direction and the
+ * orthogonal vector oriented from left half plane to right half
+ * plane. We define two coordinates by the process, the
+ * <em>abscissa</em> along the line, and the <em>offset</em> across
+ * the line. All points of the plane are uniquely identified by these
+ * two coordinates. The line is the set of points at zero offset, the
+ * left half plane is the set of points with negative offsets and the
+ * right half plane is the set of points with positive offsets.</p>
+
+ * @since 3.0
+ */
+public class Line implements Hyperplane<Euclidean2D>, Embedding<Euclidean2D, Euclidean1D> {
+
+ /** Default value for tolerance. */
+ private static final double DEFAULT_TOLERANCE = 1.0e-10;
+
+ /** Angle with respect to the abscissa axis. */
+ private double angle;
+
+ /** Cosine of the line angle. */
+ private double cos;
+
+ /** Sine of the line angle. */
+ private double sin;
+
+ /** Offset of the frame origin. */
+ private double originOffset;
+
+ /** Tolerance below which points are considered identical. */
+ private final double tolerance;
+
+ /** Reverse line. */
+ private Line reverse;
+
+ /** Build a line from two points.
+ * <p>The line is oriented from p1 to p2</p>
+ * @param p1 first point
+ * @param p2 second point
+ * @param tolerance tolerance below which points are considered identical
+ * @since 3.3
+ */
+ public Line(final Vector2D p1, final Vector2D p2, final double tolerance) {
+ reset(p1, p2);
+ this.tolerance = tolerance;
+ }
+
+ /** Build a line from a point and an angle.
+ * @param p point belonging to the line
+ * @param angle angle of the line with respect to abscissa axis
+ * @param tolerance tolerance below which points are considered identical
+ * @since 3.3
+ */
+ public Line(final Vector2D p, final double angle, final double tolerance) {
+ reset(p, angle);
+ this.tolerance = tolerance;
+ }
+
+ /** Build a line from its internal characteristics.
+ * @param angle angle of the line with respect to abscissa axis
+ * @param cos cosine of the angle
+ * @param sin sine of the angle
+ * @param originOffset offset of the origin
+ * @param tolerance tolerance below which points are considered identical
+ * @since 3.3
+ */
+ private Line(final double angle, final double cos, final double sin,
+ final double originOffset, final double tolerance) {
+ this.angle = angle;
+ this.cos = cos;
+ this.sin = sin;
+ this.originOffset = originOffset;
+ this.tolerance = tolerance;
+ this.reverse = null;
+ }
+
+ /** Build a line from two points.
+ * <p>The line is oriented from p1 to p2</p>
+ * @param p1 first point
+ * @param p2 second point
+ * @deprecated as of 3.3, replaced with {@link #Line(Vector2D, Vector2D, double)}
+ */
+ @Deprecated
+ public Line(final Vector2D p1, final Vector2D p2) {
+ this(p1, p2, DEFAULT_TOLERANCE);
+ }
+
+ /** Build a line from a point and an angle.
+ * @param p point belonging to the line
+ * @param angle angle of the line with respect to abscissa axis
+ * @deprecated as of 3.3, replaced with {@link #Line(Vector2D, double, double)}
+ */
+ @Deprecated
+ public Line(final Vector2D p, final double angle) {
+ this(p, angle, DEFAULT_TOLERANCE);
+ }
+
+ /** Copy constructor.
+ * <p>The created instance is completely independent from the
+ * original instance, it is a deep copy.</p>
+ * @param line line to copy
+ */
+ public Line(final Line line) {
+ angle = MathUtils.normalizeAngle(line.angle, FastMath.PI);
+ cos = line.cos;
+ sin = line.sin;
+ originOffset = line.originOffset;
+ tolerance = line.tolerance;
+ reverse = null;
+ }
+
+ /** {@inheritDoc} */
+ public Line copySelf() {
+ return new Line(this);
+ }
+
+ /** Reset the instance as if built from two points.
+ * <p>The line is oriented from p1 to p2</p>
+ * @param p1 first point
+ * @param p2 second point
+ */
+ public void reset(final Vector2D p1, final Vector2D p2) {
+ unlinkReverse();
+ final double dx = p2.getX() - p1.getX();
+ final double dy = p2.getY() - p1.getY();
+ final double d = FastMath.hypot(dx, dy);
+ if (d == 0.0) {
+ angle = 0.0;
+ cos = 1.0;
+ sin = 0.0;
+ originOffset = p1.getY();
+ } else {
+ angle = FastMath.PI + FastMath.atan2(-dy, -dx);
+ cos = dx / d;
+ sin = dy / d;
+ originOffset = MathArrays.linearCombination(p2.getX(), p1.getY(), -p1.getX(), p2.getY()) / d;
+ }
+ }
+
+ /** Reset the instance as if built from a line and an angle.
+ * @param p point belonging to the line
+ * @param alpha angle of the line with respect to abscissa axis
+ */
+ public void reset(final Vector2D p, final double alpha) {
+ unlinkReverse();
+ this.angle = MathUtils.normalizeAngle(alpha, FastMath.PI);
+ cos = FastMath.cos(this.angle);
+ sin = FastMath.sin(this.angle);
+ originOffset = MathArrays.linearCombination(cos, p.getY(), -sin, p.getX());
+ }
+
+ /** Revert the instance.
+ */
+ public void revertSelf() {
+ unlinkReverse();
+ if (angle < FastMath.PI) {
+ angle += FastMath.PI;
+ } else {
+ angle -= FastMath.PI;
+ }
+ cos = -cos;
+ sin = -sin;
+ originOffset = -originOffset;
+ }
+
+ /** Unset the link between an instance and its reverse.
+ */
+ private void unlinkReverse() {
+ if (reverse != null) {
+ reverse.reverse = null;
+ }
+ reverse = null;
+ }
+
+ /** Get the reverse of the instance.
+ * <p>Get a line with reversed orientation with respect to the
+ * instance.</p>
+ * <p>
+ * As long as neither the instance nor its reverse are modified
+ * (i.e. as long as none of the {@link #reset(Vector2D, Vector2D)},
+ * {@link #reset(Vector2D, double)}, {@link #revertSelf()},
+ * {@link #setAngle(double)} or {@link #setOriginOffset(double)}
+ * methods are called), then the line and its reverse remain linked
+ * together so that {@code line.getReverse().getReverse() == line}.
+ * When one of the line is modified, the link is deleted as both
+ * instance becomes independent.
+ * </p>
+ * @return a new line, with orientation opposite to the instance orientation
+ */
+ public Line getReverse() {
+ if (reverse == null) {
+ reverse = new Line((angle < FastMath.PI) ? (angle + FastMath.PI) : (angle - FastMath.PI),
+ -cos, -sin, -originOffset, tolerance);
+ reverse.reverse = this;
+ }
+ return reverse;
+ }
+
+ /** Transform a space point into a sub-space point.
+ * @param vector n-dimension point of the space
+ * @return (n-1)-dimension point of the sub-space corresponding to
+ * the specified space point
+ */
+ public Vector1D toSubSpace(Vector<Euclidean2D> vector) {
+ return toSubSpace((Point<Euclidean2D>) vector);
+ }
+
+ /** Transform a sub-space point into a space point.
+ * @param vector (n-1)-dimension point of the sub-space
+ * @return n-dimension point of the space corresponding to the
+ * specified sub-space point
+ */
+ public Vector2D toSpace(Vector<Euclidean1D> vector) {
+ return toSpace((Point<Euclidean1D>) vector);
+ }
+
+ /** {@inheritDoc} */
+ public Vector1D toSubSpace(final Point<Euclidean2D> point) {
+ Vector2D p2 = (Vector2D) point;
+ return new Vector1D(MathArrays.linearCombination(cos, p2.getX(), sin, p2.getY()));
+ }
+
+ /** {@inheritDoc} */
+ public Vector2D toSpace(final Point<Euclidean1D> point) {
+ final double abscissa = ((Vector1D) point).getX();
+ return new Vector2D(MathArrays.linearCombination(abscissa, cos, -originOffset, sin),
+ MathArrays.linearCombination(abscissa, sin, originOffset, cos));
+ }
+
+ /** Get the intersection point of the instance and another line.
+ * @param other other line
+ * @return intersection point of the instance and the other line
+ * or null if there are no intersection points
+ */
+ public Vector2D intersection(final Line other) {
+ final double d = MathArrays.linearCombination(sin, other.cos, -other.sin, cos);
+ if (FastMath.abs(d) < tolerance) {
+ return null;
+ }
+ return new Vector2D(MathArrays.linearCombination(cos, other.originOffset, -other.cos, originOffset) / d,
+ MathArrays.linearCombination(sin, other.originOffset, -other.sin, originOffset) / d);
+ }
+
+ /** {@inheritDoc}
+ * @since 3.3
+ */
+ public Point<Euclidean2D> project(Point<Euclidean2D> point) {
+ return toSpace(toSubSpace(point));
+ }
+
+ /** {@inheritDoc}
+ * @since 3.3
+ */
+ public double getTolerance() {
+ return tolerance;
+ }
+
+ /** {@inheritDoc} */
+ public SubLine wholeHyperplane() {
+ return new SubLine(this, new IntervalsSet(tolerance));
+ }
+
+ /** Build a region covering the whole space.
+ * @return a region containing the instance (really a {@link
+ * PolygonsSet PolygonsSet} instance)
+ */
+ public PolygonsSet wholeSpace() {
+ return new PolygonsSet(tolerance);
+ }
+
+ /** Get the offset (oriented distance) of a parallel line.
+ * <p>This method should be called only for parallel lines otherwise
+ * the result is not meaningful.</p>
+ * <p>The offset is 0 if both lines are the same, it is
+ * positive if the line is on the right side of the instance and
+ * negative if it is on the left side, according to its natural
+ * orientation.</p>
+ * @param line line to check
+ * @return offset of the line
+ */
+ public double getOffset(final Line line) {
+ return originOffset +
+ (MathArrays.linearCombination(cos, line.cos, sin, line.sin) > 0 ? -line.originOffset : line.originOffset);
+ }
+
+ /** Get the offset (oriented distance) of a vector.
+ * @param vector vector to check
+ * @return offset of the vector
+ */
+ public double getOffset(Vector<Euclidean2D> vector) {
+ return getOffset((Point<Euclidean2D>) vector);
+ }
+
+ /** {@inheritDoc} */
+ public double getOffset(final Point<Euclidean2D> point) {
+ Vector2D p2 = (Vector2D) point;
+ return MathArrays.linearCombination(sin, p2.getX(), -cos, p2.getY(), 1.0, originOffset);
+ }
+
+ /** {@inheritDoc} */
+ public boolean sameOrientationAs(final Hyperplane<Euclidean2D> other) {
+ final Line otherL = (Line) other;
+ return MathArrays.linearCombination(sin, otherL.sin, cos, otherL.cos) >= 0.0;
+ }
+
+ /** Get one point from the plane.
+ * @param abscissa desired abscissa for the point
+ * @param offset desired offset for the point
+ * @return one point in the plane, with given abscissa and offset
+ * relative to the line
+ */
+ public Vector2D getPointAt(final Vector1D abscissa, final double offset) {
+ final double x = abscissa.getX();
+ final double dOffset = offset - originOffset;
+ return new Vector2D(MathArrays.linearCombination(x, cos, dOffset, sin),
+ MathArrays.linearCombination(x, sin, -dOffset, cos));
+ }
+
+ /** Check if the line contains a point.
+ * @param p point to check
+ * @return true if p belongs to the line
+ */
+ public boolean contains(final Vector2D p) {
+ return FastMath.abs(getOffset(p)) < tolerance;
+ }
+
+ /** Compute the distance between the instance and a point.
+ * <p>This is a shortcut for invoking FastMath.abs(getOffset(p)),
+ * and provides consistency with what is in the
+ * org.apache.commons.math3.geometry.euclidean.threed.Line class.</p>
+ *
+ * @param p to check
+ * @return distance between the instance and the point
+ * @since 3.1
+ */
+ public double distance(final Vector2D p) {
+ return FastMath.abs(getOffset(p));
+ }
+
+ /** Check the instance is parallel to another line.
+ * @param line other line to check
+ * @return true if the instance is parallel to the other line
+ * (they can have either the same or opposite orientations)
+ */
+ public boolean isParallelTo(final Line line) {
+ return FastMath.abs(MathArrays.linearCombination(sin, line.cos, -cos, line.sin)) < tolerance;
+ }
+
+ /** Translate the line to force it passing by a point.
+ * @param p point by which the line should pass
+ */
+ public void translateToPoint(final Vector2D p) {
+ originOffset = MathArrays.linearCombination(cos, p.getY(), -sin, p.getX());
+ }
+
+ /** Get the angle of the line.
+ * @return the angle of the line with respect to the abscissa axis
+ */
+ public double getAngle() {
+ return MathUtils.normalizeAngle(angle, FastMath.PI);
+ }
+
+ /** Set the angle of the line.
+ * @param angle new angle of the line with respect to the abscissa axis
+ */
+ public void setAngle(final double angle) {
+ unlinkReverse();
+ this.angle = MathUtils.normalizeAngle(angle, FastMath.PI);
+ cos = FastMath.cos(this.angle);
+ sin = FastMath.sin(this.angle);
+ }
+
+ /** Get the offset of the origin.
+ * @return the offset of the origin
+ */
+ public double getOriginOffset() {
+ return originOffset;
+ }
+
+ /** Set the offset of the origin.
+ * @param offset offset of the origin
+ */
+ public void setOriginOffset(final double offset) {
+ unlinkReverse();
+ originOffset = offset;
+ }
+
+ /** Get a {@link org.apache.commons.math3.geometry.partitioning.Transform
+ * Transform} embedding an affine transform.
+ * @param transform affine transform to embed (must be inversible
+ * otherwise the {@link
+ * org.apache.commons.math3.geometry.partitioning.Transform#apply(Hyperplane)
+ * apply(Hyperplane)} method would work only for some lines, and
+ * fail for other ones)
+ * @return a new transform that can be applied to either {@link
+ * Vector2D Vector2D}, {@link Line Line} or {@link
+ * org.apache.commons.math3.geometry.partitioning.SubHyperplane
+ * SubHyperplane} instances
+ * @exception MathIllegalArgumentException if the transform is non invertible
+ * @deprecated as of 3.6, replaced with {@link #getTransform(double, double, double, double, double, double)}
+ */
+ @Deprecated
+ public static Transform<Euclidean2D, Euclidean1D> getTransform(final AffineTransform transform)
+ throws MathIllegalArgumentException {
+ final double[] m = new double[6];
+ transform.getMatrix(m);
+ return new LineTransform(m[0], m[1], m[2], m[3], m[4], m[5]);
+ }
+
+ /** Get a {@link org.apache.commons.math3.geometry.partitioning.Transform
+ * Transform} embedding an affine transform.
+ * @param cXX transform factor between input abscissa and output abscissa
+ * @param cYX transform factor between input abscissa and output ordinate
+ * @param cXY transform factor between input ordinate and output abscissa
+ * @param cYY transform factor between input ordinate and output ordinate
+ * @param cX1 transform addendum for output abscissa
+ * @param cY1 transform addendum for output ordinate
+ * @return a new transform that can be applied to either {@link
+ * Vector2D Vector2D}, {@link Line Line} or {@link
+ * org.apache.commons.math3.geometry.partitioning.SubHyperplane
+ * SubHyperplane} instances
+ * @exception MathIllegalArgumentException if the transform is non invertible
+ * @since 3.6
+ */
+ public static Transform<Euclidean2D, Euclidean1D> getTransform(final double cXX,
+ final double cYX,
+ final double cXY,
+ final double cYY,
+ final double cX1,
+ final double cY1)
+ throws MathIllegalArgumentException {
+ return new LineTransform(cXX, cYX, cXY, cYY, cX1, cY1);
+ }
+
+ /** Class embedding an affine transform.
+ * <p>This class is used in order to apply an affine transform to a
+ * line. Using a specific object allow to perform some computations
+ * on the transform only once even if the same transform is to be
+ * applied to a large number of lines (for example to a large
+ * polygon)./<p>
+ */
+ private static class LineTransform implements Transform<Euclidean2D, Euclidean1D> {
+
+ /** Transform factor between input abscissa and output abscissa. */
+ private double cXX;
+
+ /** Transform factor between input abscissa and output ordinate. */
+ private double cYX;
+
+ /** Transform factor between input ordinate and output abscissa. */
+ private double cXY;
+
+ /** Transform factor between input ordinate and output ordinate. */
+ private double cYY;
+
+ /** Transform addendum for output abscissa. */
+ private double cX1;
+
+ /** Transform addendum for output ordinate. */
+ private double cY1;
+
+ /** cXY * cY1 - cYY * cX1. */
+ private double c1Y;
+
+ /** cXX * cY1 - cYX * cX1. */
+ private double c1X;
+
+ /** cXX * cYY - cYX * cXY. */
+ private double c11;
+
+ /** Build an affine line transform from a n {@code AffineTransform}.
+ * @param cXX transform factor between input abscissa and output abscissa
+ * @param cYX transform factor between input abscissa and output ordinate
+ * @param cXY transform factor between input ordinate and output abscissa
+ * @param cYY transform factor between input ordinate and output ordinate
+ * @param cX1 transform addendum for output abscissa
+ * @param cY1 transform addendum for output ordinate
+ * @exception MathIllegalArgumentException if the transform is non invertible
+ * @since 3.6
+ */
+ LineTransform(final double cXX, final double cYX, final double cXY,
+ final double cYY, final double cX1, final double cY1)
+ throws MathIllegalArgumentException {
+
+ this.cXX = cXX;
+ this.cYX = cYX;
+ this.cXY = cXY;
+ this.cYY = cYY;
+ this.cX1 = cX1;
+ this.cY1 = cY1;
+
+ c1Y = MathArrays.linearCombination(cXY, cY1, -cYY, cX1);
+ c1X = MathArrays.linearCombination(cXX, cY1, -cYX, cX1);
+ c11 = MathArrays.linearCombination(cXX, cYY, -cYX, cXY);
+
+ if (FastMath.abs(c11) < 1.0e-20) {
+ throw new MathIllegalArgumentException(LocalizedFormats.NON_INVERTIBLE_TRANSFORM);
+ }
+
+ }
+
+ /** {@inheritDoc} */
+ public Vector2D apply(final Point<Euclidean2D> point) {
+ final Vector2D p2D = (Vector2D) point;
+ final double x = p2D.getX();
+ final double y = p2D.getY();
+ return new Vector2D(MathArrays.linearCombination(cXX, x, cXY, y, cX1, 1),
+ MathArrays.linearCombination(cYX, x, cYY, y, cY1, 1));
+ }
+
+ /** {@inheritDoc} */
+ public Line apply(final Hyperplane<Euclidean2D> hyperplane) {
+ final Line line = (Line) hyperplane;
+ final double rOffset = MathArrays.linearCombination(c1X, line.cos, c1Y, line.sin, c11, line.originOffset);
+ final double rCos = MathArrays.linearCombination(cXX, line.cos, cXY, line.sin);
+ final double rSin = MathArrays.linearCombination(cYX, line.cos, cYY, line.sin);
+ final double inv = 1.0 / FastMath.sqrt(rSin * rSin + rCos * rCos);
+ return new Line(FastMath.PI + FastMath.atan2(-rSin, -rCos),
+ inv * rCos, inv * rSin,
+ inv * rOffset, line.tolerance);
+ }
+
+ /** {@inheritDoc} */
+ public SubHyperplane<Euclidean1D> apply(final SubHyperplane<Euclidean1D> sub,
+ final Hyperplane<Euclidean2D> original,
+ final Hyperplane<Euclidean2D> transformed) {
+ final OrientedPoint op = (OrientedPoint) sub.getHyperplane();
+ final Line originalLine = (Line) original;
+ final Line transformedLine = (Line) transformed;
+ final Vector1D newLoc =
+ transformedLine.toSubSpace(apply(originalLine.toSpace(op.getLocation())));
+ return new OrientedPoint(newLoc, op.isDirect(), originalLine.tolerance).wholeHyperplane();
+ }
+
+ }
+
+}
diff --git a/src/main/java/org/apache/commons/math3/geometry/euclidean/twod/NestedLoops.java b/src/main/java/org/apache/commons/math3/geometry/euclidean/twod/NestedLoops.java
new file mode 100644
index 0000000..83928fa
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/geometry/euclidean/twod/NestedLoops.java
@@ -0,0 +1,201 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math3.geometry.euclidean.twod;
+
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+
+import org.apache.commons.math3.exception.MathIllegalArgumentException;
+import org.apache.commons.math3.exception.util.LocalizedFormats;
+import org.apache.commons.math3.geometry.Point;
+import org.apache.commons.math3.geometry.euclidean.oned.IntervalsSet;
+import org.apache.commons.math3.geometry.partitioning.Region;
+import org.apache.commons.math3.geometry.partitioning.RegionFactory;
+import org.apache.commons.math3.geometry.partitioning.SubHyperplane;
+
+/** This class represent a tree of nested 2D boundary loops.
+
+ * <p>This class is used for piecewise polygons construction.
+ * Polygons are built using the outline edges as
+ * representative of boundaries, the orientation of these lines are
+ * meaningful. However, we want to allow the user to specify its
+ * outline loops without having to take care of this orientation. This
+ * class is devoted to correct mis-oriented loops.<p>
+
+ * <p>Orientation is computed assuming the piecewise polygon is finite,
+ * i.e. the outermost loops have their exterior side facing points at
+ * infinity, and hence are oriented counter-clockwise. The orientation of
+ * internal loops is computed as the reverse of the orientation of
+ * their immediate surrounding loop.</p>
+
+ * @since 3.0
+ */
+class NestedLoops {
+
+ /** Boundary loop. */
+ private Vector2D[] loop;
+
+ /** Surrounded loops. */
+ private List<NestedLoops> surrounded;
+
+ /** Polygon enclosing a finite region. */
+ private Region<Euclidean2D> polygon;
+
+ /** Indicator for original loop orientation. */
+ private boolean originalIsClockwise;
+
+ /** Tolerance below which points are considered identical. */
+ private final double tolerance;
+
+ /** Simple Constructor.
+ * <p>Build an empty tree of nested loops. This instance will become
+ * the root node of a complete tree, it is not associated with any
+ * loop by itself, the outermost loops are in the root tree child
+ * nodes.</p>
+ * @param tolerance tolerance below which points are considered identical
+ * @since 3.3
+ */
+ NestedLoops(final double tolerance) {
+ this.surrounded = new ArrayList<NestedLoops>();
+ this.tolerance = tolerance;
+ }
+
+ /** Constructor.
+ * <p>Build a tree node with neither parent nor children</p>
+ * @param loop boundary loop (will be reversed in place if needed)
+ * @param tolerance tolerance below which points are considered identical
+ * @exception MathIllegalArgumentException if an outline has an open boundary loop
+ * @since 3.3
+ */
+ private NestedLoops(final Vector2D[] loop, final double tolerance)
+ throws MathIllegalArgumentException {
+
+ if (loop[0] == null) {
+ throw new MathIllegalArgumentException(LocalizedFormats.OUTLINE_BOUNDARY_LOOP_OPEN);
+ }
+
+ this.loop = loop;
+ this.surrounded = new ArrayList<NestedLoops>();
+ this.tolerance = tolerance;
+
+ // build the polygon defined by the loop
+ final ArrayList<SubHyperplane<Euclidean2D>> edges = new ArrayList<SubHyperplane<Euclidean2D>>();
+ Vector2D current = loop[loop.length - 1];
+ for (int i = 0; i < loop.length; ++i) {
+ final Vector2D previous = current;
+ current = loop[i];
+ final Line line = new Line(previous, current, tolerance);
+ final IntervalsSet region =
+ new IntervalsSet(line.toSubSpace((Point<Euclidean2D>) previous).getX(),
+ line.toSubSpace((Point<Euclidean2D>) current).getX(),
+ tolerance);
+ edges.add(new SubLine(line, region));
+ }
+ polygon = new PolygonsSet(edges, tolerance);
+
+ // ensure the polygon encloses a finite region of the plane
+ if (Double.isInfinite(polygon.getSize())) {
+ polygon = new RegionFactory<Euclidean2D>().getComplement(polygon);
+ originalIsClockwise = false;
+ } else {
+ originalIsClockwise = true;
+ }
+
+ }
+
+ /** Add a loop in a tree.
+ * @param bLoop boundary loop (will be reversed in place if needed)
+ * @exception MathIllegalArgumentException if an outline has crossing
+ * boundary loops or open boundary loops
+ */
+ public void add(final Vector2D[] bLoop) throws MathIllegalArgumentException {
+ add(new NestedLoops(bLoop, tolerance));
+ }
+
+ /** Add a loop in a tree.
+ * @param node boundary loop (will be reversed in place if needed)
+ * @exception MathIllegalArgumentException if an outline has boundary
+ * loops that cross each other
+ */
+ private void add(final NestedLoops node) throws MathIllegalArgumentException {
+
+ // check if we can go deeper in the tree
+ for (final NestedLoops child : surrounded) {
+ if (child.polygon.contains(node.polygon)) {
+ child.add(node);
+ return;
+ }
+ }
+
+ // check if we can absorb some of the instance children
+ for (final Iterator<NestedLoops> iterator = surrounded.iterator(); iterator.hasNext();) {
+ final NestedLoops child = iterator.next();
+ if (node.polygon.contains(child.polygon)) {
+ node.surrounded.add(child);
+ iterator.remove();
+ }
+ }
+
+ // we should be separate from the remaining children
+ RegionFactory<Euclidean2D> factory = new RegionFactory<Euclidean2D>();
+ for (final NestedLoops child : surrounded) {
+ if (!factory.intersection(node.polygon, child.polygon).isEmpty()) {
+ throw new MathIllegalArgumentException(LocalizedFormats.CROSSING_BOUNDARY_LOOPS);
+ }
+ }
+
+ surrounded.add(node);
+
+ }
+
+ /** Correct the orientation of the loops contained in the tree.
+ * <p>This is this method that really inverts the loops that where
+ * provided through the {@link #add(Vector2D[]) add} method if
+ * they are mis-oriented</p>
+ */
+ public void correctOrientation() {
+ for (NestedLoops child : surrounded) {
+ child.setClockWise(true);
+ }
+ }
+
+ /** Set the loop orientation.
+ * @param clockwise if true, the loop should be set to clockwise
+ * orientation
+ */
+ private void setClockWise(final boolean clockwise) {
+
+ if (originalIsClockwise ^ clockwise) {
+ // we need to inverse the original loop
+ int min = -1;
+ int max = loop.length;
+ while (++min < --max) {
+ final Vector2D tmp = loop[min];
+ loop[min] = loop[max];
+ loop[max] = tmp;
+ }
+ }
+
+ // go deeper in the tree
+ for (final NestedLoops child : surrounded) {
+ child.setClockWise(!clockwise);
+ }
+
+ }
+
+}
diff --git a/src/main/java/org/apache/commons/math3/geometry/euclidean/twod/PolygonsSet.java b/src/main/java/org/apache/commons/math3/geometry/euclidean/twod/PolygonsSet.java
new file mode 100644
index 0000000..61fae9f
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/geometry/euclidean/twod/PolygonsSet.java
@@ -0,0 +1,1160 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math3.geometry.euclidean.twod;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+
+import org.apache.commons.math3.geometry.Point;
+import org.apache.commons.math3.geometry.euclidean.oned.Euclidean1D;
+import org.apache.commons.math3.geometry.euclidean.oned.Interval;
+import org.apache.commons.math3.geometry.euclidean.oned.IntervalsSet;
+import org.apache.commons.math3.geometry.euclidean.oned.Vector1D;
+import org.apache.commons.math3.geometry.partitioning.AbstractRegion;
+import org.apache.commons.math3.geometry.partitioning.AbstractSubHyperplane;
+import org.apache.commons.math3.geometry.partitioning.BSPTree;
+import org.apache.commons.math3.geometry.partitioning.BSPTreeVisitor;
+import org.apache.commons.math3.geometry.partitioning.BoundaryAttribute;
+import org.apache.commons.math3.geometry.partitioning.Hyperplane;
+import org.apache.commons.math3.geometry.partitioning.Side;
+import org.apache.commons.math3.geometry.partitioning.SubHyperplane;
+import org.apache.commons.math3.util.FastMath;
+import org.apache.commons.math3.util.Precision;
+
+/** This class represents a 2D region: a set of polygons.
+ * @since 3.0
+ */
+public class PolygonsSet extends AbstractRegion<Euclidean2D, Euclidean1D> {
+
+ /** Default value for tolerance. */
+ private static final double DEFAULT_TOLERANCE = 1.0e-10;
+
+ /** Vertices organized as boundary loops. */
+ private Vector2D[][] vertices;
+
+ /** Build a polygons set representing the whole plane.
+ * @param tolerance tolerance below which points are considered identical
+ * @since 3.3
+ */
+ public PolygonsSet(final double tolerance) {
+ super(tolerance);
+ }
+
+ /** Build a polygons set from a BSP tree.
+ * <p>The leaf nodes of the BSP tree <em>must</em> have a
+ * {@code Boolean} attribute representing the inside status of
+ * the corresponding cell (true for inside cells, false for outside
+ * cells). In order to avoid building too many small objects, it is
+ * recommended to use the predefined constants
+ * {@code Boolean.TRUE} and {@code Boolean.FALSE}</p>
+ * <p>
+ * This constructor is aimed at expert use, as building the tree may
+ * be a difficult task. It is not intended for general use and for
+ * performances reasons does not check thoroughly its input, as this would
+ * require walking the full tree each time. Failing to provide a tree with
+ * the proper attributes, <em>will</em> therefore generate problems like
+ * {@link NullPointerException} or {@link ClassCastException} only later on.
+ * This limitation is known and explains why this constructor is for expert
+ * use only. The caller does have the responsibility to provided correct arguments.
+ * </p>
+ * @param tree inside/outside BSP tree representing the region
+ * @param tolerance tolerance below which points are considered identical
+ * @since 3.3
+ */
+ public PolygonsSet(final BSPTree<Euclidean2D> tree, final double tolerance) {
+ super(tree, tolerance);
+ }
+
+ /** Build a polygons set from a Boundary REPresentation (B-rep).
+ * <p>The boundary is provided as a collection of {@link
+ * SubHyperplane sub-hyperplanes}. Each sub-hyperplane has the
+ * interior part of the region on its minus side and the exterior on
+ * its plus side.</p>
+ * <p>The boundary elements can be in any order, and can form
+ * several non-connected sets (like for example polygons with holes
+ * or a set of disjoint polygons considered as a whole). In
+ * fact, the elements do not even need to be connected together
+ * (their topological connections are not used here). However, if the
+ * boundary does not really separate an inside open from an outside
+ * open (open having here its topological meaning), then subsequent
+ * calls to the {@link
+ * org.apache.commons.math3.geometry.partitioning.Region#checkPoint(org.apache.commons.math3.geometry.Point)
+ * checkPoint} method will not be meaningful anymore.</p>
+ * <p>If the boundary is empty, the region will represent the whole
+ * space.</p>
+ * @param boundary collection of boundary elements, as a
+ * collection of {@link SubHyperplane SubHyperplane} objects
+ * @param tolerance tolerance below which points are considered identical
+ * @since 3.3
+ */
+ public PolygonsSet(final Collection<SubHyperplane<Euclidean2D>> boundary, final double tolerance) {
+ super(boundary, tolerance);
+ }
+
+ /** Build a parallellepipedic box.
+ * @param xMin low bound along the x direction
+ * @param xMax high bound along the x direction
+ * @param yMin low bound along the y direction
+ * @param yMax high bound along the y direction
+ * @param tolerance tolerance below which points are considered identical
+ * @since 3.3
+ */
+ public PolygonsSet(final double xMin, final double xMax,
+ final double yMin, final double yMax,
+ final double tolerance) {
+ super(boxBoundary(xMin, xMax, yMin, yMax, tolerance), tolerance);
+ }
+
+ /** Build a polygon from a simple list of vertices.
+ * <p>The boundary is provided as a list of points considering to
+ * represent the vertices of a simple loop. The interior part of the
+ * region is on the left side of this path and the exterior is on its
+ * right side.</p>
+ * <p>This constructor does not handle polygons with a boundary
+ * forming several disconnected paths (such as polygons with holes).</p>
+ * <p>For cases where this simple constructor applies, it is expected to
+ * be numerically more robust than the {@link #PolygonsSet(Collection) general
+ * constructor} using {@link SubHyperplane subhyperplanes}.</p>
+ * <p>If the list is empty, the region will represent the whole
+ * space.</p>
+ * <p>
+ * Polygons with thin pikes or dents are inherently difficult to handle because
+ * they involve lines with almost opposite directions at some vertices. Polygons
+ * whose vertices come from some physical measurement with noise are also
+ * difficult because an edge that should be straight may be broken in lots of
+ * different pieces with almost equal directions. In both cases, computing the
+ * lines intersections is not numerically robust due to the almost 0 or almost
+ * &pi; angle. Such cases need to carefully adjust the {@code hyperplaneThickness}
+ * parameter. A too small value would often lead to completely wrong polygons
+ * with large area wrongly identified as inside or outside. Large values are
+ * often much safer. As a rule of thumb, a value slightly below the size of the
+ * most accurate detail needed is a good value for the {@code hyperplaneThickness}
+ * parameter.
+ * </p>
+ * @param hyperplaneThickness tolerance below which points are considered to
+ * belong to the hyperplane (which is therefore more a slab)
+ * @param vertices vertices of the simple loop boundary
+ */
+ public PolygonsSet(final double hyperplaneThickness, final Vector2D ... vertices) {
+ super(verticesToTree(hyperplaneThickness, vertices), hyperplaneThickness);
+ }
+
+ /** Build a polygons set representing the whole real line.
+ * @deprecated as of 3.3, replaced with {@link #PolygonsSet(double)}
+ */
+ @Deprecated
+ public PolygonsSet() {
+ this(DEFAULT_TOLERANCE);
+ }
+
+ /** Build a polygons set from a BSP tree.
+ * <p>The leaf nodes of the BSP tree <em>must</em> have a
+ * {@code Boolean} attribute representing the inside status of
+ * the corresponding cell (true for inside cells, false for outside
+ * cells). In order to avoid building too many small objects, it is
+ * recommended to use the predefined constants
+ * {@code Boolean.TRUE} and {@code Boolean.FALSE}</p>
+ * @param tree inside/outside BSP tree representing the region
+ * @deprecated as of 3.3, replaced with {@link #PolygonsSet(BSPTree, double)}
+ */
+ @Deprecated
+ public PolygonsSet(final BSPTree<Euclidean2D> tree) {
+ this(tree, DEFAULT_TOLERANCE);
+ }
+
+ /** Build a polygons set from a Boundary REPresentation (B-rep).
+ * <p>The boundary is provided as a collection of {@link
+ * SubHyperplane sub-hyperplanes}. Each sub-hyperplane has the
+ * interior part of the region on its minus side and the exterior on
+ * its plus side.</p>
+ * <p>The boundary elements can be in any order, and can form
+ * several non-connected sets (like for example polygons with holes
+ * or a set of disjoint polygons considered as a whole). In
+ * fact, the elements do not even need to be connected together
+ * (their topological connections are not used here). However, if the
+ * boundary does not really separate an inside open from an outside
+ * open (open having here its topological meaning), then subsequent
+ * calls to the {@link
+ * org.apache.commons.math3.geometry.partitioning.Region#checkPoint(org.apache.commons.math3.geometry.Point)
+ * checkPoint} method will not be meaningful anymore.</p>
+ * <p>If the boundary is empty, the region will represent the whole
+ * space.</p>
+ * @param boundary collection of boundary elements, as a
+ * collection of {@link SubHyperplane SubHyperplane} objects
+ * @deprecated as of 3.3, replaced with {@link #PolygonsSet(Collection, double)}
+ */
+ @Deprecated
+ public PolygonsSet(final Collection<SubHyperplane<Euclidean2D>> boundary) {
+ this(boundary, DEFAULT_TOLERANCE);
+ }
+
+ /** Build a parallellepipedic box.
+ * @param xMin low bound along the x direction
+ * @param xMax high bound along the x direction
+ * @param yMin low bound along the y direction
+ * @param yMax high bound along the y direction
+ * @deprecated as of 3.3, replaced with {@link #PolygonsSet(double, double, double, double, double)}
+ */
+ @Deprecated
+ public PolygonsSet(final double xMin, final double xMax,
+ final double yMin, final double yMax) {
+ this(xMin, xMax, yMin, yMax, DEFAULT_TOLERANCE);
+ }
+
+ /** Create a list of hyperplanes representing the boundary of a box.
+ * @param xMin low bound along the x direction
+ * @param xMax high bound along the x direction
+ * @param yMin low bound along the y direction
+ * @param yMax high bound along the y direction
+ * @param tolerance tolerance below which points are considered identical
+ * @return boundary of the box
+ */
+ private static Line[] boxBoundary(final double xMin, final double xMax,
+ final double yMin, final double yMax,
+ final double tolerance) {
+ if ((xMin >= xMax - tolerance) || (yMin >= yMax - tolerance)) {
+ // too thin box, build an empty polygons set
+ return null;
+ }
+ final Vector2D minMin = new Vector2D(xMin, yMin);
+ final Vector2D minMax = new Vector2D(xMin, yMax);
+ final Vector2D maxMin = new Vector2D(xMax, yMin);
+ final Vector2D maxMax = new Vector2D(xMax, yMax);
+ return new Line[] {
+ new Line(minMin, maxMin, tolerance),
+ new Line(maxMin, maxMax, tolerance),
+ new Line(maxMax, minMax, tolerance),
+ new Line(minMax, minMin, tolerance)
+ };
+ }
+
+ /** Build the BSP tree of a polygons set from a simple list of vertices.
+ * <p>The boundary is provided as a list of points considering to
+ * represent the vertices of a simple loop. The interior part of the
+ * region is on the left side of this path and the exterior is on its
+ * right side.</p>
+ * <p>This constructor does not handle polygons with a boundary
+ * forming several disconnected paths (such as polygons with holes).</p>
+ * <p>For cases where this simple constructor applies, it is expected to
+ * be numerically more robust than the {@link #PolygonsSet(Collection) general
+ * constructor} using {@link SubHyperplane subhyperplanes}.</p>
+ * @param hyperplaneThickness tolerance below which points are consider to
+ * belong to the hyperplane (which is therefore more a slab)
+ * @param vertices vertices of the simple loop boundary
+ * @return the BSP tree of the input vertices
+ */
+ private static BSPTree<Euclidean2D> verticesToTree(final double hyperplaneThickness,
+ final Vector2D ... vertices) {
+
+ final int n = vertices.length;
+ if (n == 0) {
+ // the tree represents the whole space
+ return new BSPTree<Euclidean2D>(Boolean.TRUE);
+ }
+
+ // build the vertices
+ final Vertex[] vArray = new Vertex[n];
+ for (int i = 0; i < n; ++i) {
+ vArray[i] = new Vertex(vertices[i]);
+ }
+
+ // build the edges
+ List<Edge> edges = new ArrayList<Edge>(n);
+ for (int i = 0; i < n; ++i) {
+
+ // get the endpoints of the edge
+ final Vertex start = vArray[i];
+ final Vertex end = vArray[(i + 1) % n];
+
+ // get the line supporting the edge, taking care not to recreate it
+ // if it was already created earlier due to another edge being aligned
+ // with the current one
+ Line line = start.sharedLineWith(end);
+ if (line == null) {
+ line = new Line(start.getLocation(), end.getLocation(), hyperplaneThickness);
+ }
+
+ // create the edge and store it
+ edges.add(new Edge(start, end, line));
+
+ // check if another vertex also happens to be on this line
+ for (final Vertex vertex : vArray) {
+ if (vertex != start && vertex != end &&
+ FastMath.abs(line.getOffset((Point<Euclidean2D>) vertex.getLocation())) <= hyperplaneThickness) {
+ vertex.bindWith(line);
+ }
+ }
+
+ }
+
+ // build the tree top-down
+ final BSPTree<Euclidean2D> tree = new BSPTree<Euclidean2D>();
+ insertEdges(hyperplaneThickness, tree, edges);
+
+ return tree;
+
+ }
+
+ /** Recursively build a tree by inserting cut sub-hyperplanes.
+ * @param hyperplaneThickness tolerance below which points are consider to
+ * belong to the hyperplane (which is therefore more a slab)
+ * @param node current tree node (it is a leaf node at the beginning
+ * of the call)
+ * @param edges list of edges to insert in the cell defined by this node
+ * (excluding edges not belonging to the cell defined by this node)
+ */
+ private static void insertEdges(final double hyperplaneThickness,
+ final BSPTree<Euclidean2D> node,
+ final List<Edge> edges) {
+
+ // find an edge with an hyperplane that can be inserted in the node
+ int index = 0;
+ Edge inserted =null;
+ while (inserted == null && index < edges.size()) {
+ inserted = edges.get(index++);
+ if (inserted.getNode() == null) {
+ if (node.insertCut(inserted.getLine())) {
+ inserted.setNode(node);
+ } else {
+ inserted = null;
+ }
+ } else {
+ inserted = null;
+ }
+ }
+
+ if (inserted == null) {
+ // no suitable edge was found, the node remains a leaf node
+ // we need to set its inside/outside boolean indicator
+ final BSPTree<Euclidean2D> parent = node.getParent();
+ if (parent == null || node == parent.getMinus()) {
+ node.setAttribute(Boolean.TRUE);
+ } else {
+ node.setAttribute(Boolean.FALSE);
+ }
+ return;
+ }
+
+ // we have split the node by inserting an edge as a cut sub-hyperplane
+ // distribute the remaining edges in the two sub-trees
+ final List<Edge> plusList = new ArrayList<Edge>();
+ final List<Edge> minusList = new ArrayList<Edge>();
+ for (final Edge edge : edges) {
+ if (edge != inserted) {
+ final double startOffset = inserted.getLine().getOffset((Point<Euclidean2D>) edge.getStart().getLocation());
+ final double endOffset = inserted.getLine().getOffset((Point<Euclidean2D>) edge.getEnd().getLocation());
+ Side startSide = (FastMath.abs(startOffset) <= hyperplaneThickness) ?
+ Side.HYPER : ((startOffset < 0) ? Side.MINUS : Side.PLUS);
+ Side endSide = (FastMath.abs(endOffset) <= hyperplaneThickness) ?
+ Side.HYPER : ((endOffset < 0) ? Side.MINUS : Side.PLUS);
+ switch (startSide) {
+ case PLUS:
+ if (endSide == Side.MINUS) {
+ // we need to insert a split point on the hyperplane
+ final Vertex splitPoint = edge.split(inserted.getLine());
+ minusList.add(splitPoint.getOutgoing());
+ plusList.add(splitPoint.getIncoming());
+ } else {
+ plusList.add(edge);
+ }
+ break;
+ case MINUS:
+ if (endSide == Side.PLUS) {
+ // we need to insert a split point on the hyperplane
+ final Vertex splitPoint = edge.split(inserted.getLine());
+ minusList.add(splitPoint.getIncoming());
+ plusList.add(splitPoint.getOutgoing());
+ } else {
+ minusList.add(edge);
+ }
+ break;
+ default:
+ if (endSide == Side.PLUS) {
+ plusList.add(edge);
+ } else if (endSide == Side.MINUS) {
+ minusList.add(edge);
+ }
+ break;
+ }
+ }
+ }
+
+ // recurse through lower levels
+ if (!plusList.isEmpty()) {
+ insertEdges(hyperplaneThickness, node.getPlus(), plusList);
+ } else {
+ node.getPlus().setAttribute(Boolean.FALSE);
+ }
+ if (!minusList.isEmpty()) {
+ insertEdges(hyperplaneThickness, node.getMinus(), minusList);
+ } else {
+ node.getMinus().setAttribute(Boolean.TRUE);
+ }
+
+ }
+
+ /** Internal class for holding vertices while they are processed to build a BSP tree. */
+ private static class Vertex {
+
+ /** Vertex location. */
+ private final Vector2D location;
+
+ /** Incoming edge. */
+ private Edge incoming;
+
+ /** Outgoing edge. */
+ private Edge outgoing;
+
+ /** Lines bound with this vertex. */
+ private final List<Line> lines;
+
+ /** Build a non-processed vertex not owned by any node yet.
+ * @param location vertex location
+ */
+ Vertex(final Vector2D location) {
+ this.location = location;
+ this.incoming = null;
+ this.outgoing = null;
+ this.lines = new ArrayList<Line>();
+ }
+
+ /** Get Vertex location.
+ * @return vertex location
+ */
+ public Vector2D getLocation() {
+ return location;
+ }
+
+ /** Bind a line considered to contain this vertex.
+ * @param line line to bind with this vertex
+ */
+ public void bindWith(final Line line) {
+ lines.add(line);
+ }
+
+ /** Get the common line bound with both the instance and another vertex, if any.
+ * <p>
+ * When two vertices are both bound to the same line, this means they are
+ * already handled by node associated with this line, so there is no need
+ * to create a cut hyperplane for them.
+ * </p>
+ * @param vertex other vertex to check instance against
+ * @return line bound with both the instance and another vertex, or null if the
+ * two vertices do not share a line yet
+ */
+ public Line sharedLineWith(final Vertex vertex) {
+ for (final Line line1 : lines) {
+ for (final Line line2 : vertex.lines) {
+ if (line1 == line2) {
+ return line1;
+ }
+ }
+ }
+ return null;
+ }
+
+ /** Set incoming edge.
+ * <p>
+ * The line supporting the incoming edge is automatically bound
+ * with the instance.
+ * </p>
+ * @param incoming incoming edge
+ */
+ public void setIncoming(final Edge incoming) {
+ this.incoming = incoming;
+ bindWith(incoming.getLine());
+ }
+
+ /** Get incoming edge.
+ * @return incoming edge
+ */
+ public Edge getIncoming() {
+ return incoming;
+ }
+
+ /** Set outgoing edge.
+ * <p>
+ * The line supporting the outgoing edge is automatically bound
+ * with the instance.
+ * </p>
+ * @param outgoing outgoing edge
+ */
+ public void setOutgoing(final Edge outgoing) {
+ this.outgoing = outgoing;
+ bindWith(outgoing.getLine());
+ }
+
+ /** Get outgoing edge.
+ * @return outgoing edge
+ */
+ public Edge getOutgoing() {
+ return outgoing;
+ }
+
+ }
+
+ /** Internal class for holding edges while they are processed to build a BSP tree. */
+ private static class Edge {
+
+ /** Start vertex. */
+ private final Vertex start;
+
+ /** End vertex. */
+ private final Vertex end;
+
+ /** Line supporting the edge. */
+ private final Line line;
+
+ /** Node whose cut hyperplane contains this edge. */
+ private BSPTree<Euclidean2D> node;
+
+ /** Build an edge not contained in any node yet.
+ * @param start start vertex
+ * @param end end vertex
+ * @param line line supporting the edge
+ */
+ Edge(final Vertex start, final Vertex end, final Line line) {
+
+ this.start = start;
+ this.end = end;
+ this.line = line;
+ this.node = null;
+
+ // connect the vertices back to the edge
+ start.setOutgoing(this);
+ end.setIncoming(this);
+
+ }
+
+ /** Get start vertex.
+ * @return start vertex
+ */
+ public Vertex getStart() {
+ return start;
+ }
+
+ /** Get end vertex.
+ * @return end vertex
+ */
+ public Vertex getEnd() {
+ return end;
+ }
+
+ /** Get the line supporting this edge.
+ * @return line supporting this edge
+ */
+ public Line getLine() {
+ return line;
+ }
+
+ /** Set the node whose cut hyperplane contains this edge.
+ * @param node node whose cut hyperplane contains this edge
+ */
+ public void setNode(final BSPTree<Euclidean2D> node) {
+ this.node = node;
+ }
+
+ /** Get the node whose cut hyperplane contains this edge.
+ * @return node whose cut hyperplane contains this edge
+ * (null if edge has not yet been inserted into the BSP tree)
+ */
+ public BSPTree<Euclidean2D> getNode() {
+ return node;
+ }
+
+ /** Split the edge.
+ * <p>
+ * Once split, this edge is not referenced anymore by the vertices,
+ * it is replaced by the two half-edges and an intermediate splitting
+ * vertex is introduced to connect these two halves.
+ * </p>
+ * @param splitLine line splitting the edge in two halves
+ * @return split vertex (its incoming and outgoing edges are the two halves)
+ */
+ public Vertex split(final Line splitLine) {
+ final Vertex splitVertex = new Vertex(line.intersection(splitLine));
+ splitVertex.bindWith(splitLine);
+ final Edge startHalf = new Edge(start, splitVertex, line);
+ final Edge endHalf = new Edge(splitVertex, end, line);
+ startHalf.node = node;
+ endHalf.node = node;
+ return splitVertex;
+ }
+
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public PolygonsSet buildNew(final BSPTree<Euclidean2D> tree) {
+ return new PolygonsSet(tree, getTolerance());
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ protected void computeGeometricalProperties() {
+
+ final Vector2D[][] v = getVertices();
+
+ if (v.length == 0) {
+ final BSPTree<Euclidean2D> tree = getTree(false);
+ if (tree.getCut() == null && (Boolean) tree.getAttribute()) {
+ // the instance covers the whole space
+ setSize(Double.POSITIVE_INFINITY);
+ setBarycenter((Point<Euclidean2D>) Vector2D.NaN);
+ } else {
+ setSize(0);
+ setBarycenter((Point<Euclidean2D>) new Vector2D(0, 0));
+ }
+ } else if (v[0][0] == null) {
+ // there is at least one open-loop: the polygon is infinite
+ setSize(Double.POSITIVE_INFINITY);
+ setBarycenter((Point<Euclidean2D>) Vector2D.NaN);
+ } else {
+ // all loops are closed, we compute some integrals around the shape
+
+ double sum = 0;
+ double sumX = 0;
+ double sumY = 0;
+
+ for (Vector2D[] loop : v) {
+ double x1 = loop[loop.length - 1].getX();
+ double y1 = loop[loop.length - 1].getY();
+ for (final Vector2D point : loop) {
+ final double x0 = x1;
+ final double y0 = y1;
+ x1 = point.getX();
+ y1 = point.getY();
+ final double factor = x0 * y1 - y0 * x1;
+ sum += factor;
+ sumX += factor * (x0 + x1);
+ sumY += factor * (y0 + y1);
+ }
+ }
+
+ if (sum < 0) {
+ // the polygon as a finite outside surrounded by an infinite inside
+ setSize(Double.POSITIVE_INFINITY);
+ setBarycenter((Point<Euclidean2D>) Vector2D.NaN);
+ } else {
+ setSize(sum / 2);
+ setBarycenter((Point<Euclidean2D>) new Vector2D(sumX / (3 * sum), sumY / (3 * sum)));
+ }
+
+ }
+
+ }
+
+ /** Get the vertices of the polygon.
+ * <p>The polygon boundary can be represented as an array of loops,
+ * each loop being itself an array of vertices.</p>
+ * <p>In order to identify open loops which start and end by
+ * infinite edges, the open loops arrays start with a null point. In
+ * this case, the first non null point and the last point of the
+ * array do not represent real vertices, they are dummy points
+ * intended only to get the direction of the first and last edge. An
+ * open loop consisting of a single infinite line will therefore be
+ * represented by a three elements array with one null point
+ * followed by two dummy points. The open loops are always the first
+ * ones in the loops array.</p>
+ * <p>If the polygon has no boundary at all, a zero length loop
+ * array will be returned.</p>
+ * <p>All line segments in the various loops have the inside of the
+ * region on their left side and the outside on their right side
+ * when moving in the underlying line direction. This means that
+ * closed loops surrounding finite areas obey the direct
+ * trigonometric orientation.</p>
+ * @return vertices of the polygon, organized as oriented boundary
+ * loops with the open loops first (the returned value is guaranteed
+ * to be non-null)
+ */
+ public Vector2D[][] getVertices() {
+ if (vertices == null) {
+ if (getTree(false).getCut() == null) {
+ vertices = new Vector2D[0][];
+ } else {
+
+ // build the unconnected segments
+ final SegmentsBuilder visitor = new SegmentsBuilder(getTolerance());
+ getTree(true).visit(visitor);
+ final List<ConnectableSegment> segments = visitor.getSegments();
+
+ // connect all segments, using topological criteria first
+ // and using Euclidean distance only as a last resort
+ int pending = segments.size();
+ pending -= naturalFollowerConnections(segments);
+ if (pending > 0) {
+ pending -= splitEdgeConnections(segments);
+ }
+ if (pending > 0) {
+ pending -= closeVerticesConnections(segments);
+ }
+
+ // create the segment loops
+ final ArrayList<List<Segment>> loops = new ArrayList<List<Segment>>();
+ for (ConnectableSegment s = getUnprocessed(segments); s != null; s = getUnprocessed(segments)) {
+ final List<Segment> loop = followLoop(s);
+ if (loop != null) {
+ if (loop.get(0).getStart() == null) {
+ // this is an open loop, we put it on the front
+ loops.add(0, loop);
+ } else {
+ // this is a closed loop, we put it on the back
+ loops.add(loop);
+ }
+ }
+ }
+
+ // transform the loops in an array of arrays of points
+ vertices = new Vector2D[loops.size()][];
+ int i = 0;
+
+ for (final List<Segment> loop : loops) {
+ if (loop.size() < 2 ||
+ (loop.size() == 2 && loop.get(0).getStart() == null && loop.get(1).getEnd() == null)) {
+ // single infinite line
+ final Line line = loop.get(0).getLine();
+ vertices[i++] = new Vector2D[] {
+ null,
+ line.toSpace((Point<Euclidean1D>) new Vector1D(-Float.MAX_VALUE)),
+ line.toSpace((Point<Euclidean1D>) new Vector1D(+Float.MAX_VALUE))
+ };
+ } else if (loop.get(0).getStart() == null) {
+ // open loop with at least one real point
+ final Vector2D[] array = new Vector2D[loop.size() + 2];
+ int j = 0;
+ for (Segment segment : loop) {
+
+ if (j == 0) {
+ // null point and first dummy point
+ double x = segment.getLine().toSubSpace((Point<Euclidean2D>) segment.getEnd()).getX();
+ x -= FastMath.max(1.0, FastMath.abs(x / 2));
+ array[j++] = null;
+ array[j++] = segment.getLine().toSpace((Point<Euclidean1D>) new Vector1D(x));
+ }
+
+ if (j < (array.length - 1)) {
+ // current point
+ array[j++] = segment.getEnd();
+ }
+
+ if (j == (array.length - 1)) {
+ // last dummy point
+ double x = segment.getLine().toSubSpace((Point<Euclidean2D>) segment.getStart()).getX();
+ x += FastMath.max(1.0, FastMath.abs(x / 2));
+ array[j++] = segment.getLine().toSpace((Point<Euclidean1D>) new Vector1D(x));
+ }
+
+ }
+ vertices[i++] = array;
+ } else {
+ final Vector2D[] array = new Vector2D[loop.size()];
+ int j = 0;
+ for (Segment segment : loop) {
+ array[j++] = segment.getStart();
+ }
+ vertices[i++] = array;
+ }
+ }
+
+ }
+ }
+
+ return vertices.clone();
+
+ }
+
+ /** Connect the segments using only natural follower information.
+ * @param segments segments complete segments list
+ * @return number of connections performed
+ */
+ private int naturalFollowerConnections(final List<ConnectableSegment> segments) {
+ int connected = 0;
+ for (final ConnectableSegment segment : segments) {
+ if (segment.getNext() == null) {
+ final BSPTree<Euclidean2D> node = segment.getNode();
+ final BSPTree<Euclidean2D> end = segment.getEndNode();
+ for (final ConnectableSegment candidateNext : segments) {
+ if (candidateNext.getPrevious() == null &&
+ candidateNext.getNode() == end &&
+ candidateNext.getStartNode() == node) {
+ // connect the two segments
+ segment.setNext(candidateNext);
+ candidateNext.setPrevious(segment);
+ ++connected;
+ break;
+ }
+ }
+ }
+ }
+ return connected;
+ }
+
+ /** Connect the segments resulting from a line splitting a straight edge.
+ * @param segments segments complete segments list
+ * @return number of connections performed
+ */
+ private int splitEdgeConnections(final List<ConnectableSegment> segments) {
+ int connected = 0;
+ for (final ConnectableSegment segment : segments) {
+ if (segment.getNext() == null) {
+ final Hyperplane<Euclidean2D> hyperplane = segment.getNode().getCut().getHyperplane();
+ final BSPTree<Euclidean2D> end = segment.getEndNode();
+ for (final ConnectableSegment candidateNext : segments) {
+ if (candidateNext.getPrevious() == null &&
+ candidateNext.getNode().getCut().getHyperplane() == hyperplane &&
+ candidateNext.getStartNode() == end) {
+ // connect the two segments
+ segment.setNext(candidateNext);
+ candidateNext.setPrevious(segment);
+ ++connected;
+ break;
+ }
+ }
+ }
+ }
+ return connected;
+ }
+
+ /** Connect the segments using Euclidean distance.
+ * <p>
+ * This connection heuristic should be used last, as it relies
+ * only on a fuzzy distance criterion.
+ * </p>
+ * @param segments segments complete segments list
+ * @return number of connections performed
+ */
+ private int closeVerticesConnections(final List<ConnectableSegment> segments) {
+ int connected = 0;
+ for (final ConnectableSegment segment : segments) {
+ if (segment.getNext() == null && segment.getEnd() != null) {
+ final Vector2D end = segment.getEnd();
+ ConnectableSegment selectedNext = null;
+ double min = Double.POSITIVE_INFINITY;
+ for (final ConnectableSegment candidateNext : segments) {
+ if (candidateNext.getPrevious() == null && candidateNext.getStart() != null) {
+ final double distance = Vector2D.distance(end, candidateNext.getStart());
+ if (distance < min) {
+ selectedNext = candidateNext;
+ min = distance;
+ }
+ }
+ }
+ if (min <= getTolerance()) {
+ // connect the two segments
+ segment.setNext(selectedNext);
+ selectedNext.setPrevious(segment);
+ ++connected;
+ }
+ }
+ }
+ return connected;
+ }
+
+ /** Get first unprocessed segment from a list.
+ * @param segments segments list
+ * @return first segment that has not been processed yet
+ * or null if all segments have been processed
+ */
+ private ConnectableSegment getUnprocessed(final List<ConnectableSegment> segments) {
+ for (final ConnectableSegment segment : segments) {
+ if (!segment.isProcessed()) {
+ return segment;
+ }
+ }
+ return null;
+ }
+
+ /** Build the loop containing a segment.
+ * <p>
+ * The segment put in the loop will be marked as processed.
+ * </p>
+ * @param defining segment used to define the loop
+ * @return loop containing the segment (may be null if the loop is a
+ * degenerated infinitely thin 2 points loop
+ */
+ private List<Segment> followLoop(final ConnectableSegment defining) {
+
+ final List<Segment> loop = new ArrayList<Segment>();
+ loop.add(defining);
+ defining.setProcessed(true);
+
+ // add segments in connection order
+ ConnectableSegment next = defining.getNext();
+ while (next != defining && next != null) {
+ loop.add(next);
+ next.setProcessed(true);
+ next = next.getNext();
+ }
+
+ if (next == null) {
+ // the loop is open and we have found its end,
+ // we need to find its start too
+ ConnectableSegment previous = defining.getPrevious();
+ while (previous != null) {
+ loop.add(0, previous);
+ previous.setProcessed(true);
+ previous = previous.getPrevious();
+ }
+ }
+
+ // filter out spurious vertices
+ filterSpuriousVertices(loop);
+
+ if (loop.size() == 2 && loop.get(0).getStart() != null) {
+ // this is a degenerated infinitely thin closed loop, we simply ignore it
+ return null;
+ } else {
+ return loop;
+ }
+
+ }
+
+ /** Filter out spurious vertices on straight lines (at machine precision).
+ * @param loop segments loop to filter (will be modified in-place)
+ */
+ private void filterSpuriousVertices(final List<Segment> loop) {
+ for (int i = 0; i < loop.size(); ++i) {
+ final Segment previous = loop.get(i);
+ int j = (i + 1) % loop.size();
+ final Segment next = loop.get(j);
+ if (next != null &&
+ Precision.equals(previous.getLine().getAngle(), next.getLine().getAngle(), Precision.EPSILON)) {
+ // the vertex between the two edges is a spurious one
+ // replace the two segments by a single one
+ loop.set(j, new Segment(previous.getStart(), next.getEnd(), previous.getLine()));
+ loop.remove(i--);
+ }
+ }
+ }
+
+ /** Private extension of Segment allowing connection. */
+ private static class ConnectableSegment extends Segment {
+
+ /** Node containing segment. */
+ private final BSPTree<Euclidean2D> node;
+
+ /** Node whose intersection with current node defines start point. */
+ private final BSPTree<Euclidean2D> startNode;
+
+ /** Node whose intersection with current node defines end point. */
+ private final BSPTree<Euclidean2D> endNode;
+
+ /** Previous segment. */
+ private ConnectableSegment previous;
+
+ /** Next segment. */
+ private ConnectableSegment next;
+
+ /** Indicator for completely processed segments. */
+ private boolean processed;
+
+ /** Build a segment.
+ * @param start start point of the segment
+ * @param end end point of the segment
+ * @param line line containing the segment
+ * @param node node containing the segment
+ * @param startNode node whose intersection with current node defines start point
+ * @param endNode node whose intersection with current node defines end point
+ */
+ ConnectableSegment(final Vector2D start, final Vector2D end, final Line line,
+ final BSPTree<Euclidean2D> node,
+ final BSPTree<Euclidean2D> startNode,
+ final BSPTree<Euclidean2D> endNode) {
+ super(start, end, line);
+ this.node = node;
+ this.startNode = startNode;
+ this.endNode = endNode;
+ this.previous = null;
+ this.next = null;
+ this.processed = false;
+ }
+
+ /** Get the node containing segment.
+ * @return node containing segment
+ */
+ public BSPTree<Euclidean2D> getNode() {
+ return node;
+ }
+
+ /** Get the node whose intersection with current node defines start point.
+ * @return node whose intersection with current node defines start point
+ */
+ public BSPTree<Euclidean2D> getStartNode() {
+ return startNode;
+ }
+
+ /** Get the node whose intersection with current node defines end point.
+ * @return node whose intersection with current node defines end point
+ */
+ public BSPTree<Euclidean2D> getEndNode() {
+ return endNode;
+ }
+
+ /** Get the previous segment.
+ * @return previous segment
+ */
+ public ConnectableSegment getPrevious() {
+ return previous;
+ }
+
+ /** Set the previous segment.
+ * @param previous previous segment
+ */
+ public void setPrevious(final ConnectableSegment previous) {
+ this.previous = previous;
+ }
+
+ /** Get the next segment.
+ * @return next segment
+ */
+ public ConnectableSegment getNext() {
+ return next;
+ }
+
+ /** Set the next segment.
+ * @param next previous segment
+ */
+ public void setNext(final ConnectableSegment next) {
+ this.next = next;
+ }
+
+ /** Set the processed flag.
+ * @param processed processed flag to set
+ */
+ public void setProcessed(final boolean processed) {
+ this.processed = processed;
+ }
+
+ /** Check if the segment has been processed.
+ * @return true if the segment has been processed
+ */
+ public boolean isProcessed() {
+ return processed;
+ }
+
+ }
+
+ /** Visitor building segments. */
+ private static class SegmentsBuilder implements BSPTreeVisitor<Euclidean2D> {
+
+ /** Tolerance for close nodes connection. */
+ private final double tolerance;
+
+ /** Built segments. */
+ private final List<ConnectableSegment> segments;
+
+ /** Simple constructor.
+ * @param tolerance tolerance for close nodes connection
+ */
+ SegmentsBuilder(final double tolerance) {
+ this.tolerance = tolerance;
+ this.segments = new ArrayList<ConnectableSegment>();
+ }
+
+ /** {@inheritDoc} */
+ public Order visitOrder(final BSPTree<Euclidean2D> node) {
+ return Order.MINUS_SUB_PLUS;
+ }
+
+ /** {@inheritDoc} */
+ public void visitInternalNode(final BSPTree<Euclidean2D> node) {
+ @SuppressWarnings("unchecked")
+ final BoundaryAttribute<Euclidean2D> attribute = (BoundaryAttribute<Euclidean2D>) node.getAttribute();
+ final Iterable<BSPTree<Euclidean2D>> splitters = attribute.getSplitters();
+ if (attribute.getPlusOutside() != null) {
+ addContribution(attribute.getPlusOutside(), node, splitters, false);
+ }
+ if (attribute.getPlusInside() != null) {
+ addContribution(attribute.getPlusInside(), node, splitters, true);
+ }
+ }
+
+ /** {@inheritDoc} */
+ public void visitLeafNode(final BSPTree<Euclidean2D> node) {
+ }
+
+ /** Add the contribution of a boundary facet.
+ * @param sub boundary facet
+ * @param node node containing segment
+ * @param splitters splitters for the boundary facet
+ * @param reversed if true, the facet has the inside on its plus side
+ */
+ private void addContribution(final SubHyperplane<Euclidean2D> sub,
+ final BSPTree<Euclidean2D> node,
+ final Iterable<BSPTree<Euclidean2D>> splitters,
+ final boolean reversed) {
+ @SuppressWarnings("unchecked")
+ final AbstractSubHyperplane<Euclidean2D, Euclidean1D> absSub =
+ (AbstractSubHyperplane<Euclidean2D, Euclidean1D>) sub;
+ final Line line = (Line) sub.getHyperplane();
+ final List<Interval> intervals = ((IntervalsSet) absSub.getRemainingRegion()).asList();
+ for (final Interval i : intervals) {
+
+ // find the 2D points
+ final Vector2D startV = Double.isInfinite(i.getInf()) ?
+ null : (Vector2D) line.toSpace((Point<Euclidean1D>) new Vector1D(i.getInf()));
+ final Vector2D endV = Double.isInfinite(i.getSup()) ?
+ null : (Vector2D) line.toSpace((Point<Euclidean1D>) new Vector1D(i.getSup()));
+
+ // recover the connectivity information
+ final BSPTree<Euclidean2D> startN = selectClosest(startV, splitters);
+ final BSPTree<Euclidean2D> endN = selectClosest(endV, splitters);
+
+ if (reversed) {
+ segments.add(new ConnectableSegment(endV, startV, line.getReverse(),
+ node, endN, startN));
+ } else {
+ segments.add(new ConnectableSegment(startV, endV, line,
+ node, startN, endN));
+ }
+
+ }
+ }
+
+ /** Select the node whose cut sub-hyperplane is closest to specified point.
+ * @param point reference point
+ * @param candidates candidate nodes
+ * @return node closest to point, or null if no node is closer than tolerance
+ */
+ private BSPTree<Euclidean2D> selectClosest(final Vector2D point, final Iterable<BSPTree<Euclidean2D>> candidates) {
+
+ BSPTree<Euclidean2D> selected = null;
+ double min = Double.POSITIVE_INFINITY;
+
+ for (final BSPTree<Euclidean2D> node : candidates) {
+ final double distance = FastMath.abs(node.getCut().getHyperplane().getOffset(point));
+ if (distance < min) {
+ selected = node;
+ min = distance;
+ }
+ }
+
+ return min <= tolerance ? selected : null;
+
+ }
+
+ /** Get the segments.
+ * @return built segments
+ */
+ public List<ConnectableSegment> getSegments() {
+ return segments;
+ }
+
+ }
+
+}
diff --git a/src/main/java/org/apache/commons/math3/geometry/euclidean/twod/Segment.java b/src/main/java/org/apache/commons/math3/geometry/euclidean/twod/Segment.java
new file mode 100644
index 0000000..2ef7f4e
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/geometry/euclidean/twod/Segment.java
@@ -0,0 +1,112 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math3.geometry.euclidean.twod;
+
+import org.apache.commons.math3.geometry.Point;
+import org.apache.commons.math3.util.FastMath;
+
+/** Simple container for a two-points segment.
+ * @since 3.0
+ */
+public class Segment {
+
+ /** Start point of the segment. */
+ private final Vector2D start;
+
+ /** End point of the segment. */
+ private final Vector2D end;
+
+ /** Line containing the segment. */
+ private final Line line;
+
+ /** Build a segment.
+ * @param start start point of the segment
+ * @param end end point of the segment
+ * @param line line containing the segment
+ */
+ public Segment(final Vector2D start, final Vector2D end, final Line line) {
+ this.start = start;
+ this.end = end;
+ this.line = line;
+ }
+
+ /** Get the start point of the segment.
+ * @return start point of the segment
+ */
+ public Vector2D getStart() {
+ return start;
+ }
+
+ /** Get the end point of the segment.
+ * @return end point of the segment
+ */
+ public Vector2D getEnd() {
+ return end;
+ }
+
+ /** Get the line containing the segment.
+ * @return line containing the segment
+ */
+ public Line getLine() {
+ return line;
+ }
+
+ /** Calculates the shortest distance from a point to this line segment.
+ * <p>
+ * If the perpendicular extension from the point to the line does not
+ * cross in the bounds of the line segment, the shortest distance to
+ * the two end points will be returned.
+ * </p>
+ *
+ * Algorithm adapted from:
+ * <a href="http://www.codeguru.com/forum/printthread.php?s=cc8cf0596231f9a7dba4da6e77c29db3&t=194400&pp=15&page=1">
+ * Thread @ Codeguru</a>
+ *
+ * @param p to check
+ * @return distance between the instance and the point
+ * @since 3.1
+ */
+ public double distance(final Vector2D p) {
+ final double deltaX = end.getX() - start.getX();
+ final double deltaY = end.getY() - start.getY();
+
+ final double r = ((p.getX() - start.getX()) * deltaX + (p.getY() - start.getY()) * deltaY) /
+ (deltaX * deltaX + deltaY * deltaY);
+
+ // r == 0 => P = startPt
+ // r == 1 => P = endPt
+ // r < 0 => P is on the backward extension of the segment
+ // r > 1 => P is on the forward extension of the segment
+ // 0 < r < 1 => P is on the segment
+
+ // if point isn't on the line segment, just return the shortest distance to the end points
+ if (r < 0 || r > 1) {
+ final double dist1 = getStart().distance((Point<Euclidean2D>) p);
+ final double dist2 = getEnd().distance((Point<Euclidean2D>) p);
+
+ return FastMath.min(dist1, dist2);
+ }
+ else {
+ // find point on line and see if it is in the line segment
+ final double px = start.getX() + r * deltaX;
+ final double py = start.getY() + r * deltaY;
+
+ final Vector2D interPt = new Vector2D(px, py);
+ return interPt.distance((Point<Euclidean2D>) p);
+ }
+ }
+}
diff --git a/src/main/java/org/apache/commons/math3/geometry/euclidean/twod/SubLine.java b/src/main/java/org/apache/commons/math3/geometry/euclidean/twod/SubLine.java
new file mode 100644
index 0000000..d930b76
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/geometry/euclidean/twod/SubLine.java
@@ -0,0 +1,214 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math3.geometry.euclidean.twod;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.apache.commons.math3.geometry.Point;
+import org.apache.commons.math3.geometry.euclidean.oned.Euclidean1D;
+import org.apache.commons.math3.geometry.euclidean.oned.Interval;
+import org.apache.commons.math3.geometry.euclidean.oned.IntervalsSet;
+import org.apache.commons.math3.geometry.euclidean.oned.OrientedPoint;
+import org.apache.commons.math3.geometry.euclidean.oned.Vector1D;
+import org.apache.commons.math3.geometry.partitioning.AbstractSubHyperplane;
+import org.apache.commons.math3.geometry.partitioning.BSPTree;
+import org.apache.commons.math3.geometry.partitioning.Hyperplane;
+import org.apache.commons.math3.geometry.partitioning.Region;
+import org.apache.commons.math3.geometry.partitioning.Region.Location;
+import org.apache.commons.math3.geometry.partitioning.SubHyperplane;
+import org.apache.commons.math3.util.FastMath;
+
+/** This class represents a sub-hyperplane for {@link Line}.
+ * @since 3.0
+ */
+public class SubLine extends AbstractSubHyperplane<Euclidean2D, Euclidean1D> {
+
+ /** Default value for tolerance. */
+ private static final double DEFAULT_TOLERANCE = 1.0e-10;
+
+ /** Simple constructor.
+ * @param hyperplane underlying hyperplane
+ * @param remainingRegion remaining region of the hyperplane
+ */
+ public SubLine(final Hyperplane<Euclidean2D> hyperplane,
+ final Region<Euclidean1D> remainingRegion) {
+ super(hyperplane, remainingRegion);
+ }
+
+ /** Create a sub-line from two endpoints.
+ * @param start start point
+ * @param end end point
+ * @param tolerance tolerance below which points are considered identical
+ * @since 3.3
+ */
+ public SubLine(final Vector2D start, final Vector2D end, final double tolerance) {
+ super(new Line(start, end, tolerance), buildIntervalSet(start, end, tolerance));
+ }
+
+ /** Create a sub-line from two endpoints.
+ * @param start start point
+ * @param end end point
+ * @deprecated as of 3.3, replaced with {@link #SubLine(Vector2D, Vector2D, double)}
+ */
+ @Deprecated
+ public SubLine(final Vector2D start, final Vector2D end) {
+ this(start, end, DEFAULT_TOLERANCE);
+ }
+
+ /** Create a sub-line from a segment.
+ * @param segment single segment forming the sub-line
+ */
+ public SubLine(final Segment segment) {
+ super(segment.getLine(),
+ buildIntervalSet(segment.getStart(), segment.getEnd(), segment.getLine().getTolerance()));
+ }
+
+ /** Get the endpoints of the sub-line.
+ * <p>
+ * A subline may be any arbitrary number of disjoints segments, so the endpoints
+ * are provided as a list of endpoint pairs. Each element of the list represents
+ * one segment, and each segment contains a start point at index 0 and an end point
+ * at index 1. If the sub-line is unbounded in the negative infinity direction,
+ * the start point of the first segment will have infinite coordinates. If the
+ * sub-line is unbounded in the positive infinity direction, the end point of the
+ * last segment will have infinite coordinates. So a sub-line covering the whole
+ * line will contain just one row and both elements of this row will have infinite
+ * coordinates. If the sub-line is empty, the returned list will contain 0 segments.
+ * </p>
+ * @return list of segments endpoints
+ */
+ public List<Segment> getSegments() {
+
+ final Line line = (Line) getHyperplane();
+ final List<Interval> list = ((IntervalsSet) getRemainingRegion()).asList();
+ final List<Segment> segments = new ArrayList<Segment>(list.size());
+
+ for (final Interval interval : list) {
+ final Vector2D start = line.toSpace((Point<Euclidean1D>) new Vector1D(interval.getInf()));
+ final Vector2D end = line.toSpace((Point<Euclidean1D>) new Vector1D(interval.getSup()));
+ segments.add(new Segment(start, end, line));
+ }
+
+ return segments;
+
+ }
+
+ /** Get the intersection of the instance and another sub-line.
+ * <p>
+ * This method is related to the {@link Line#intersection(Line)
+ * intersection} method in the {@link Line Line} class, but in addition
+ * to compute the point along infinite lines, it also checks the point
+ * lies on both sub-line ranges.
+ * </p>
+ * @param subLine other sub-line which may intersect instance
+ * @param includeEndPoints if true, endpoints are considered to belong to
+ * instance (i.e. they are closed sets) and may be returned, otherwise endpoints
+ * are considered to not belong to instance (i.e. they are open sets) and intersection
+ * occurring on endpoints lead to null being returned
+ * @return the intersection point if there is one, null if the sub-lines don't intersect
+ */
+ public Vector2D intersection(final SubLine subLine, final boolean includeEndPoints) {
+
+ // retrieve the underlying lines
+ Line line1 = (Line) getHyperplane();
+ Line line2 = (Line) subLine.getHyperplane();
+
+ // compute the intersection on infinite line
+ Vector2D v2D = line1.intersection(line2);
+ if (v2D == null) {
+ return null;
+ }
+
+ // check location of point with respect to first sub-line
+ Location loc1 = getRemainingRegion().checkPoint(line1.toSubSpace((Point<Euclidean2D>) v2D));
+
+ // check location of point with respect to second sub-line
+ Location loc2 = subLine.getRemainingRegion().checkPoint(line2.toSubSpace((Point<Euclidean2D>) v2D));
+
+ if (includeEndPoints) {
+ return ((loc1 != Location.OUTSIDE) && (loc2 != Location.OUTSIDE)) ? v2D : null;
+ } else {
+ return ((loc1 == Location.INSIDE) && (loc2 == Location.INSIDE)) ? v2D : null;
+ }
+
+ }
+
+ /** Build an interval set from two points.
+ * @param start start point
+ * @param end end point
+ * @param tolerance tolerance below which points are considered identical
+ * @return an interval set
+ */
+ private static IntervalsSet buildIntervalSet(final Vector2D start, final Vector2D end, final double tolerance) {
+ final Line line = new Line(start, end, tolerance);
+ return new IntervalsSet(line.toSubSpace((Point<Euclidean2D>) start).getX(),
+ line.toSubSpace((Point<Euclidean2D>) end).getX(),
+ tolerance);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ protected AbstractSubHyperplane<Euclidean2D, Euclidean1D> buildNew(final Hyperplane<Euclidean2D> hyperplane,
+ final Region<Euclidean1D> remainingRegion) {
+ return new SubLine(hyperplane, remainingRegion);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public SplitSubHyperplane<Euclidean2D> split(final Hyperplane<Euclidean2D> hyperplane) {
+
+ final Line thisLine = (Line) getHyperplane();
+ final Line otherLine = (Line) hyperplane;
+ final Vector2D crossing = thisLine.intersection(otherLine);
+ final double tolerance = thisLine.getTolerance();
+
+ if (crossing == null) {
+ // the lines are parallel
+ final double global = otherLine.getOffset(thisLine);
+ if (global < -tolerance) {
+ return new SplitSubHyperplane<Euclidean2D>(null, this);
+ } else if (global > tolerance) {
+ return new SplitSubHyperplane<Euclidean2D>(this, null);
+ } else {
+ return new SplitSubHyperplane<Euclidean2D>(null, null);
+ }
+ }
+
+ // the lines do intersect
+ final boolean direct = FastMath.sin(thisLine.getAngle() - otherLine.getAngle()) < 0;
+ final Vector1D x = thisLine.toSubSpace((Point<Euclidean2D>) crossing);
+ final SubHyperplane<Euclidean1D> subPlus =
+ new OrientedPoint(x, !direct, tolerance).wholeHyperplane();
+ final SubHyperplane<Euclidean1D> subMinus =
+ new OrientedPoint(x, direct, tolerance).wholeHyperplane();
+
+ final BSPTree<Euclidean1D> splitTree = getRemainingRegion().getTree(false).split(subMinus);
+ final BSPTree<Euclidean1D> plusTree = getRemainingRegion().isEmpty(splitTree.getPlus()) ?
+ new BSPTree<Euclidean1D>(Boolean.FALSE) :
+ new BSPTree<Euclidean1D>(subPlus, new BSPTree<Euclidean1D>(Boolean.FALSE),
+ splitTree.getPlus(), null);
+ final BSPTree<Euclidean1D> minusTree = getRemainingRegion().isEmpty(splitTree.getMinus()) ?
+ new BSPTree<Euclidean1D>(Boolean.FALSE) :
+ new BSPTree<Euclidean1D>(subMinus, new BSPTree<Euclidean1D>(Boolean.FALSE),
+ splitTree.getMinus(), null);
+ return new SplitSubHyperplane<Euclidean2D>(new SubLine(thisLine.copySelf(), new IntervalsSet(plusTree, tolerance)),
+ new SubLine(thisLine.copySelf(), new IntervalsSet(minusTree, tolerance)));
+
+ }
+
+}
diff --git a/src/main/java/org/apache/commons/math3/geometry/euclidean/twod/Vector2D.java b/src/main/java/org/apache/commons/math3/geometry/euclidean/twod/Vector2D.java
new file mode 100644
index 0000000..191d225
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/geometry/euclidean/twod/Vector2D.java
@@ -0,0 +1,460 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math3.geometry.euclidean.twod;
+
+import java.text.NumberFormat;
+
+import org.apache.commons.math3.exception.DimensionMismatchException;
+import org.apache.commons.math3.exception.MathArithmeticException;
+import org.apache.commons.math3.exception.util.LocalizedFormats;
+import org.apache.commons.math3.geometry.Point;
+import org.apache.commons.math3.geometry.Space;
+import org.apache.commons.math3.geometry.Vector;
+import org.apache.commons.math3.util.FastMath;
+import org.apache.commons.math3.util.MathArrays;
+import org.apache.commons.math3.util.MathUtils;
+
+/** This class represents a 2D vector.
+ * <p>Instances of this class are guaranteed to be immutable.</p>
+ * @since 3.0
+ */
+public class Vector2D implements Vector<Euclidean2D> {
+
+ /** Origin (coordinates: 0, 0). */
+ public static final Vector2D ZERO = new Vector2D(0, 0);
+
+ // CHECKSTYLE: stop ConstantName
+ /** A vector with all coordinates set to NaN. */
+ public static final Vector2D NaN = new Vector2D(Double.NaN, Double.NaN);
+ // CHECKSTYLE: resume ConstantName
+
+ /** A vector with all coordinates set to positive infinity. */
+ public static final Vector2D POSITIVE_INFINITY =
+ new Vector2D(Double.POSITIVE_INFINITY, Double.POSITIVE_INFINITY);
+
+ /** A vector with all coordinates set to negative infinity. */
+ public static final Vector2D NEGATIVE_INFINITY =
+ new Vector2D(Double.NEGATIVE_INFINITY, Double.NEGATIVE_INFINITY);
+
+ /** Serializable UID. */
+ private static final long serialVersionUID = 266938651998679754L;
+
+ /** Abscissa. */
+ private final double x;
+
+ /** Ordinate. */
+ private final double y;
+
+ /** Simple constructor.
+ * Build a vector from its coordinates
+ * @param x abscissa
+ * @param y ordinate
+ * @see #getX()
+ * @see #getY()
+ */
+ public Vector2D(double x, double y) {
+ this.x = x;
+ this.y = y;
+ }
+
+ /** Simple constructor.
+ * Build a vector from its coordinates
+ * @param v coordinates array
+ * @exception DimensionMismatchException if array does not have 2 elements
+ * @see #toArray()
+ */
+ public Vector2D(double[] v) throws DimensionMismatchException {
+ if (v.length != 2) {
+ throw new DimensionMismatchException(v.length, 2);
+ }
+ this.x = v[0];
+ this.y = v[1];
+ }
+
+ /** Multiplicative constructor
+ * Build a vector from another one and a scale factor.
+ * The vector built will be a * u
+ * @param a scale factor
+ * @param u base (unscaled) vector
+ */
+ public Vector2D(double a, Vector2D u) {
+ this.x = a * u.x;
+ this.y = a * u.y;
+ }
+
+ /** Linear constructor
+ * Build a vector from two other ones and corresponding scale factors.
+ * The vector built will be a1 * u1 + a2 * u2
+ * @param a1 first scale factor
+ * @param u1 first base (unscaled) vector
+ * @param a2 second scale factor
+ * @param u2 second base (unscaled) vector
+ */
+ public Vector2D(double a1, Vector2D u1, double a2, Vector2D u2) {
+ this.x = a1 * u1.x + a2 * u2.x;
+ this.y = a1 * u1.y + a2 * u2.y;
+ }
+
+ /** Linear constructor
+ * Build a vector from three other ones and corresponding scale factors.
+ * The vector built will be a1 * u1 + a2 * u2 + a3 * u3
+ * @param a1 first scale factor
+ * @param u1 first base (unscaled) vector
+ * @param a2 second scale factor
+ * @param u2 second base (unscaled) vector
+ * @param a3 third scale factor
+ * @param u3 third base (unscaled) vector
+ */
+ public Vector2D(double a1, Vector2D u1, double a2, Vector2D u2,
+ double a3, Vector2D u3) {
+ this.x = a1 * u1.x + a2 * u2.x + a3 * u3.x;
+ this.y = a1 * u1.y + a2 * u2.y + a3 * u3.y;
+ }
+
+ /** Linear constructor
+ * Build a vector from four other ones and corresponding scale factors.
+ * The vector built will be a1 * u1 + a2 * u2 + a3 * u3 + a4 * u4
+ * @param a1 first scale factor
+ * @param u1 first base (unscaled) vector
+ * @param a2 second scale factor
+ * @param u2 second base (unscaled) vector
+ * @param a3 third scale factor
+ * @param u3 third base (unscaled) vector
+ * @param a4 fourth scale factor
+ * @param u4 fourth base (unscaled) vector
+ */
+ public Vector2D(double a1, Vector2D u1, double a2, Vector2D u2,
+ double a3, Vector2D u3, double a4, Vector2D u4) {
+ this.x = a1 * u1.x + a2 * u2.x + a3 * u3.x + a4 * u4.x;
+ this.y = a1 * u1.y + a2 * u2.y + a3 * u3.y + a4 * u4.y;
+ }
+
+ /** Get the abscissa of the vector.
+ * @return abscissa of the vector
+ * @see #Vector2D(double, double)
+ */
+ public double getX() {
+ return x;
+ }
+
+ /** Get the ordinate of the vector.
+ * @return ordinate of the vector
+ * @see #Vector2D(double, double)
+ */
+ public double getY() {
+ return y;
+ }
+
+ /** Get the vector coordinates as a dimension 2 array.
+ * @return vector coordinates
+ * @see #Vector2D(double[])
+ */
+ public double[] toArray() {
+ return new double[] { x, y };
+ }
+
+ /** {@inheritDoc} */
+ public Space getSpace() {
+ return Euclidean2D.getInstance();
+ }
+
+ /** {@inheritDoc} */
+ public Vector2D getZero() {
+ return ZERO;
+ }
+
+ /** {@inheritDoc} */
+ public double getNorm1() {
+ return FastMath.abs(x) + FastMath.abs(y);
+ }
+
+ /** {@inheritDoc} */
+ public double getNorm() {
+ return FastMath.sqrt (x * x + y * y);
+ }
+
+ /** {@inheritDoc} */
+ public double getNormSq() {
+ return x * x + y * y;
+ }
+
+ /** {@inheritDoc} */
+ public double getNormInf() {
+ return FastMath.max(FastMath.abs(x), FastMath.abs(y));
+ }
+
+ /** {@inheritDoc} */
+ public Vector2D add(Vector<Euclidean2D> v) {
+ Vector2D v2 = (Vector2D) v;
+ return new Vector2D(x + v2.getX(), y + v2.getY());
+ }
+
+ /** {@inheritDoc} */
+ public Vector2D add(double factor, Vector<Euclidean2D> v) {
+ Vector2D v2 = (Vector2D) v;
+ return new Vector2D(x + factor * v2.getX(), y + factor * v2.getY());
+ }
+
+ /** {@inheritDoc} */
+ public Vector2D subtract(Vector<Euclidean2D> p) {
+ Vector2D p3 = (Vector2D) p;
+ return new Vector2D(x - p3.x, y - p3.y);
+ }
+
+ /** {@inheritDoc} */
+ public Vector2D subtract(double factor, Vector<Euclidean2D> v) {
+ Vector2D v2 = (Vector2D) v;
+ return new Vector2D(x - factor * v2.getX(), y - factor * v2.getY());
+ }
+
+ /** {@inheritDoc} */
+ public Vector2D normalize() throws MathArithmeticException {
+ double s = getNorm();
+ if (s == 0) {
+ throw new MathArithmeticException(LocalizedFormats.CANNOT_NORMALIZE_A_ZERO_NORM_VECTOR);
+ }
+ return scalarMultiply(1 / s);
+ }
+
+ /** Compute the angular separation between two vectors.
+ * <p>This method computes the angular separation between two
+ * vectors using the dot product for well separated vectors and the
+ * cross product for almost aligned vectors. This allows to have a
+ * good accuracy in all cases, even for vectors very close to each
+ * other.</p>
+ * @param v1 first vector
+ * @param v2 second vector
+ * @return angular separation between v1 and v2
+ * @exception MathArithmeticException if either vector has a null norm
+ */
+ public static double angle(Vector2D v1, Vector2D v2) throws MathArithmeticException {
+
+ double normProduct = v1.getNorm() * v2.getNorm();
+ if (normProduct == 0) {
+ throw new MathArithmeticException(LocalizedFormats.ZERO_NORM);
+ }
+
+ double dot = v1.dotProduct(v2);
+ double threshold = normProduct * 0.9999;
+ if ((dot < -threshold) || (dot > threshold)) {
+ // the vectors are almost aligned, compute using the sine
+ final double n = FastMath.abs(MathArrays.linearCombination(v1.x, v2.y, -v1.y, v2.x));
+ if (dot >= 0) {
+ return FastMath.asin(n / normProduct);
+ }
+ return FastMath.PI - FastMath.asin(n / normProduct);
+ }
+
+ // the vectors are sufficiently separated to use the cosine
+ return FastMath.acos(dot / normProduct);
+
+ }
+
+ /** {@inheritDoc} */
+ public Vector2D negate() {
+ return new Vector2D(-x, -y);
+ }
+
+ /** {@inheritDoc} */
+ public Vector2D scalarMultiply(double a) {
+ return new Vector2D(a * x, a * y);
+ }
+
+ /** {@inheritDoc} */
+ public boolean isNaN() {
+ return Double.isNaN(x) || Double.isNaN(y);
+ }
+
+ /** {@inheritDoc} */
+ public boolean isInfinite() {
+ return !isNaN() && (Double.isInfinite(x) || Double.isInfinite(y));
+ }
+
+ /** {@inheritDoc} */
+ public double distance1(Vector<Euclidean2D> p) {
+ Vector2D p3 = (Vector2D) p;
+ final double dx = FastMath.abs(p3.x - x);
+ final double dy = FastMath.abs(p3.y - y);
+ return dx + dy;
+ }
+
+ /** {@inheritDoc}
+ */
+ public double distance(Vector<Euclidean2D> p) {
+ return distance((Point<Euclidean2D>) p);
+ }
+
+ /** {@inheritDoc} */
+ public double distance(Point<Euclidean2D> p) {
+ Vector2D p3 = (Vector2D) p;
+ final double dx = p3.x - x;
+ final double dy = p3.y - y;
+ return FastMath.sqrt(dx * dx + dy * dy);
+ }
+
+ /** {@inheritDoc} */
+ public double distanceInf(Vector<Euclidean2D> p) {
+ Vector2D p3 = (Vector2D) p;
+ final double dx = FastMath.abs(p3.x - x);
+ final double dy = FastMath.abs(p3.y - y);
+ return FastMath.max(dx, dy);
+ }
+
+ /** {@inheritDoc} */
+ public double distanceSq(Vector<Euclidean2D> p) {
+ Vector2D p3 = (Vector2D) p;
+ final double dx = p3.x - x;
+ final double dy = p3.y - y;
+ return dx * dx + dy * dy;
+ }
+
+ /** {@inheritDoc} */
+ public double dotProduct(final Vector<Euclidean2D> v) {
+ final Vector2D v2 = (Vector2D) v;
+ return MathArrays.linearCombination(x, v2.x, y, v2.y);
+ }
+
+ /**
+ * Compute the cross-product of the instance and the given points.
+ * <p>
+ * The cross product can be used to determine the location of a point
+ * with regard to the line formed by (p1, p2) and is calculated as:
+ * \[
+ * P = (x_2 - x_1)(y_3 - y_1) - (y_2 - y_1)(x_3 - x_1)
+ * \]
+ * with \(p3 = (x_3, y_3)\) being this instance.
+ * <p>
+ * If the result is 0, the points are collinear, i.e. lie on a single straight line L;
+ * if it is positive, this point lies to the left, otherwise to the right of the line
+ * formed by (p1, p2).
+ *
+ * @param p1 first point of the line
+ * @param p2 second point of the line
+ * @return the cross-product
+ *
+ * @see <a href="http://en.wikipedia.org/wiki/Cross_product">Cross product (Wikipedia)</a>
+ */
+ public double crossProduct(final Vector2D p1, final Vector2D p2) {
+ final double x1 = p2.getX() - p1.getX();
+ final double y1 = getY() - p1.getY();
+ final double x2 = getX() - p1.getX();
+ final double y2 = p2.getY() - p1.getY();
+ return MathArrays.linearCombination(x1, y1, -x2, y2);
+ }
+
+ /** Compute the distance between two vectors according to the L<sub>2</sub> norm.
+ * <p>Calling this method is equivalent to calling:
+ * <code>p1.subtract(p2).getNorm()</code> except that no intermediate
+ * vector is built</p>
+ * @param p1 first vector
+ * @param p2 second vector
+ * @return the distance between p1 and p2 according to the L<sub>2</sub> norm
+ */
+ public static double distance(Vector2D p1, Vector2D p2) {
+ return p1.distance(p2);
+ }
+
+ /** Compute the distance between two vectors according to the L<sub>&infin;</sub> norm.
+ * <p>Calling this method is equivalent to calling:
+ * <code>p1.subtract(p2).getNormInf()</code> except that no intermediate
+ * vector is built</p>
+ * @param p1 first vector
+ * @param p2 second vector
+ * @return the distance between p1 and p2 according to the L<sub>&infin;</sub> norm
+ */
+ public static double distanceInf(Vector2D p1, Vector2D p2) {
+ return p1.distanceInf(p2);
+ }
+
+ /** Compute the square of the distance between two vectors.
+ * <p>Calling this method is equivalent to calling:
+ * <code>p1.subtract(p2).getNormSq()</code> except that no intermediate
+ * vector is built</p>
+ * @param p1 first vector
+ * @param p2 second vector
+ * @return the square of the distance between p1 and p2
+ */
+ public static double distanceSq(Vector2D p1, Vector2D p2) {
+ return p1.distanceSq(p2);
+ }
+
+ /**
+ * Test for the equality of two 2D vectors.
+ * <p>
+ * If all coordinates of two 2D vectors are exactly the same, and none are
+ * <code>Double.NaN</code>, the two 2D vectors are considered to be equal.
+ * </p>
+ * <p>
+ * <code>NaN</code> coordinates are considered to affect globally the vector
+ * and be equals to each other - i.e, if either (or all) coordinates of the
+ * 2D vector are equal to <code>Double.NaN</code>, the 2D vector is equal to
+ * {@link #NaN}.
+ * </p>
+ *
+ * @param other Object to test for equality to this
+ * @return true if two 2D vector objects are equal, false if
+ * object is null, not an instance of Vector2D, or
+ * not equal to this Vector2D instance
+ *
+ */
+ @Override
+ public boolean equals(Object other) {
+
+ if (this == other) {
+ return true;
+ }
+
+ if (other instanceof Vector2D) {
+ final Vector2D rhs = (Vector2D)other;
+ if (rhs.isNaN()) {
+ return this.isNaN();
+ }
+
+ return (x == rhs.x) && (y == rhs.y);
+ }
+ return false;
+ }
+
+ /**
+ * Get a hashCode for the 2D vector.
+ * <p>
+ * All NaN values have the same hash code.</p>
+ *
+ * @return a hash code value for this object
+ */
+ @Override
+ public int hashCode() {
+ if (isNaN()) {
+ return 542;
+ }
+ return 122 * (76 * MathUtils.hash(x) + MathUtils.hash(y));
+ }
+
+ /** Get a string representation of this vector.
+ * @return a string representation of this vector
+ */
+ @Override
+ public String toString() {
+ return Vector2DFormat.getInstance().format(this);
+ }
+
+ /** {@inheritDoc} */
+ public String toString(final NumberFormat format) {
+ return new Vector2DFormat(format).format(this);
+ }
+
+}
diff --git a/src/main/java/org/apache/commons/math3/geometry/euclidean/twod/Vector2DFormat.java b/src/main/java/org/apache/commons/math3/geometry/euclidean/twod/Vector2DFormat.java
new file mode 100644
index 0000000..21261c5
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/geometry/euclidean/twod/Vector2DFormat.java
@@ -0,0 +1,138 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math3.geometry.euclidean.twod;
+
+import java.text.FieldPosition;
+import java.text.NumberFormat;
+import java.text.ParsePosition;
+import java.util.Locale;
+
+import org.apache.commons.math3.exception.MathParseException;
+import org.apache.commons.math3.geometry.Vector;
+import org.apache.commons.math3.geometry.VectorFormat;
+import org.apache.commons.math3.util.CompositeFormat;
+
+/**
+ * Formats a 2D vector in components list format "{x; y}".
+ * <p>The prefix and suffix "{" and "}" and the separator "; " can be replaced by
+ * any user-defined strings. The number format for components can be configured.</p>
+ * <p>White space is ignored at parse time, even if it is in the prefix, suffix
+ * or separator specifications. So even if the default separator does include a space
+ * character that is used at format time, both input string "{1;1}" and
+ * " { 1 ; 1 } " will be parsed without error and the same vector will be
+ * returned. In the second case, however, the parse position after parsing will be
+ * just after the closing curly brace, i.e. just before the trailing space.</p>
+ * <p><b>Note:</b> using "," as a separator may interfere with the grouping separator
+ * of the default {@link NumberFormat} for the current locale. Thus it is advised
+ * to use a {@link NumberFormat} instance with disabled grouping in such a case.</p>
+ *
+ * @since 3.0
+ */
+public class Vector2DFormat extends VectorFormat<Euclidean2D> {
+
+ /**
+ * Create an instance with default settings.
+ * <p>The instance uses the default prefix, suffix and separator:
+ * "{", "}", and "; " and the default number format for components.</p>
+ */
+ public Vector2DFormat() {
+ super(DEFAULT_PREFIX, DEFAULT_SUFFIX, DEFAULT_SEPARATOR,
+ CompositeFormat.getDefaultNumberFormat());
+ }
+
+ /**
+ * Create an instance with a custom number format for components.
+ * @param format the custom format for components.
+ */
+ public Vector2DFormat(final NumberFormat format) {
+ super(DEFAULT_PREFIX, DEFAULT_SUFFIX, DEFAULT_SEPARATOR, format);
+ }
+
+ /**
+ * Create an instance with custom prefix, suffix and separator.
+ * @param prefix prefix to use instead of the default "{"
+ * @param suffix suffix to use instead of the default "}"
+ * @param separator separator to use instead of the default "; "
+ */
+ public Vector2DFormat(final String prefix, final String suffix,
+ final String separator) {
+ super(prefix, suffix, separator, CompositeFormat.getDefaultNumberFormat());
+ }
+
+ /**
+ * Create an instance with custom prefix, suffix, separator and format
+ * for components.
+ * @param prefix prefix to use instead of the default "{"
+ * @param suffix suffix to use instead of the default "}"
+ * @param separator separator to use instead of the default "; "
+ * @param format the custom format for components.
+ */
+ public Vector2DFormat(final String prefix, final String suffix,
+ final String separator, final NumberFormat format) {
+ super(prefix, suffix, separator, format);
+ }
+
+ /**
+ * Returns the default 2D vector format for the current locale.
+ * @return the default 2D vector format.
+ */
+ public static Vector2DFormat getInstance() {
+ return getInstance(Locale.getDefault());
+ }
+
+ /**
+ * Returns the default 2D vector format for the given locale.
+ * @param locale the specific locale used by the format.
+ * @return the 2D vector format specific to the given locale.
+ */
+ public static Vector2DFormat getInstance(final Locale locale) {
+ return new Vector2DFormat(CompositeFormat.getDefaultNumberFormat(locale));
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public StringBuffer format(final Vector<Euclidean2D> vector, final StringBuffer toAppendTo,
+ final FieldPosition pos) {
+ final Vector2D p2 = (Vector2D) vector;
+ return format(toAppendTo, pos, p2.getX(), p2.getY());
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public Vector2D parse(final String source) throws MathParseException {
+ ParsePosition parsePosition = new ParsePosition(0);
+ Vector2D result = parse(source, parsePosition);
+ if (parsePosition.getIndex() == 0) {
+ throw new MathParseException(source,
+ parsePosition.getErrorIndex(),
+ Vector2D.class);
+ }
+ return result;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public Vector2D parse(final String source, final ParsePosition pos) {
+ final double[] coordinates = parseCoordinates(2, source, pos);
+ if (coordinates == null) {
+ return null;
+ }
+ return new Vector2D(coordinates[0], coordinates[1]);
+ }
+
+}
diff --git a/src/main/java/org/apache/commons/math3/geometry/euclidean/twod/hull/AbstractConvexHullGenerator2D.java b/src/main/java/org/apache/commons/math3/geometry/euclidean/twod/hull/AbstractConvexHullGenerator2D.java
new file mode 100644
index 0000000..b234ad5
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/geometry/euclidean/twod/hull/AbstractConvexHullGenerator2D.java
@@ -0,0 +1,116 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math3.geometry.euclidean.twod.hull;
+
+import java.util.Collection;
+
+import org.apache.commons.math3.exception.ConvergenceException;
+import org.apache.commons.math3.exception.MathIllegalArgumentException;
+import org.apache.commons.math3.exception.NullArgumentException;
+import org.apache.commons.math3.geometry.euclidean.twod.Vector2D;
+import org.apache.commons.math3.util.MathUtils;
+
+/**
+ * Abstract base class for convex hull generators in the two-dimensional euclidean space.
+ *
+ * @since 3.3
+ */
+abstract class AbstractConvexHullGenerator2D implements ConvexHullGenerator2D {
+
+ /** Default value for tolerance. */
+ private static final double DEFAULT_TOLERANCE = 1e-10;
+
+ /** Tolerance below which points are considered identical. */
+ private final double tolerance;
+
+ /**
+ * Indicates if collinear points on the hull shall be present in the output.
+ * If {@code false}, only the extreme points are added to the hull.
+ */
+ private final boolean includeCollinearPoints;
+
+ /**
+ * Simple constructor.
+ * <p>
+ * The default tolerance (1e-10) will be used to determine identical points.
+ *
+ * @param includeCollinearPoints indicates if collinear points on the hull shall be
+ * added as hull vertices
+ */
+ protected AbstractConvexHullGenerator2D(final boolean includeCollinearPoints) {
+ this(includeCollinearPoints, DEFAULT_TOLERANCE);
+ }
+
+ /**
+ * Simple constructor.
+ *
+ * @param includeCollinearPoints indicates if collinear points on the hull shall be
+ * added as hull vertices
+ * @param tolerance tolerance below which points are considered identical
+ */
+ protected AbstractConvexHullGenerator2D(final boolean includeCollinearPoints, final double tolerance) {
+ this.includeCollinearPoints = includeCollinearPoints;
+ this.tolerance = tolerance;
+ }
+
+ /**
+ * Get the tolerance below which points are considered identical.
+ * @return the tolerance below which points are considered identical
+ */
+ public double getTolerance() {
+ return tolerance;
+ }
+
+ /**
+ * Returns if collinear points on the hull will be added as hull vertices.
+ * @return {@code true} if collinear points are added as hull vertices, or {@code false}
+ * if only extreme points are present.
+ */
+ public boolean isIncludeCollinearPoints() {
+ return includeCollinearPoints;
+ }
+
+ /** {@inheritDoc} */
+ public ConvexHull2D generate(final Collection<Vector2D> points)
+ throws NullArgumentException, ConvergenceException {
+ // check for null points
+ MathUtils.checkNotNull(points);
+
+ Collection<Vector2D> hullVertices = null;
+ if (points.size() < 2) {
+ hullVertices = points;
+ } else {
+ hullVertices = findHullVertices(points);
+ }
+
+ try {
+ return new ConvexHull2D(hullVertices.toArray(new Vector2D[hullVertices.size()]),
+ tolerance);
+ } catch (MathIllegalArgumentException e) {
+ // the hull vertices may not form a convex hull if the tolerance value is to large
+ throw new ConvergenceException();
+ }
+ }
+
+ /**
+ * Find the convex hull vertices from the set of input points.
+ * @param points the set of input points
+ * @return the convex hull vertices in CCW winding
+ */
+ protected abstract Collection<Vector2D> findHullVertices(Collection<Vector2D> points);
+
+}
diff --git a/src/main/java/org/apache/commons/math3/geometry/euclidean/twod/hull/AklToussaintHeuristic.java b/src/main/java/org/apache/commons/math3/geometry/euclidean/twod/hull/AklToussaintHeuristic.java
new file mode 100644
index 0000000..f5d1b84
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/geometry/euclidean/twod/hull/AklToussaintHeuristic.java
@@ -0,0 +1,153 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math3.geometry.euclidean.twod.hull;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+
+import org.apache.commons.math3.geometry.euclidean.twod.Vector2D;
+
+/**
+ * A simple heuristic to improve the performance of convex hull algorithms.
+ * <p>
+ * The heuristic is based on the idea of a convex quadrilateral, which is formed by
+ * four points with the lowest and highest x / y coordinates. Any point that lies inside
+ * this quadrilateral can not be part of the convex hull and can thus be safely discarded
+ * before generating the convex hull itself.
+ * <p>
+ * The complexity of the operation is O(n), and may greatly improve the time it takes to
+ * construct the convex hull afterwards, depending on the point distribution.
+ *
+ * @see <a href="http://en.wikipedia.org/wiki/Convex_hull_algorithms#Akl-Toussaint_heuristic">
+ * Akl-Toussaint heuristic (Wikipedia)</a>
+ * @since 3.3
+ */
+public final class AklToussaintHeuristic {
+
+ /** Hide utility constructor. */
+ private AklToussaintHeuristic() {
+ }
+
+ /**
+ * Returns a point set that is reduced by all points for which it is safe to assume
+ * that they are not part of the convex hull.
+ *
+ * @param points the original point set
+ * @return a reduced point set, useful as input for convex hull algorithms
+ */
+ public static Collection<Vector2D> reducePoints(final Collection<Vector2D> points) {
+
+ // find the leftmost point
+ int size = 0;
+ Vector2D minX = null;
+ Vector2D maxX = null;
+ Vector2D minY = null;
+ Vector2D maxY = null;
+ for (Vector2D p : points) {
+ if (minX == null || p.getX() < minX.getX()) {
+ minX = p;
+ }
+ if (maxX == null || p.getX() > maxX.getX()) {
+ maxX = p;
+ }
+ if (minY == null || p.getY() < minY.getY()) {
+ minY = p;
+ }
+ if (maxY == null || p.getY() > maxY.getY()) {
+ maxY = p;
+ }
+ size++;
+ }
+
+ if (size < 4) {
+ return points;
+ }
+
+ final List<Vector2D> quadrilateral = buildQuadrilateral(minY, maxX, maxY, minX);
+ // if the quadrilateral is not well formed, e.g. only 2 points, do not attempt to reduce
+ if (quadrilateral.size() < 3) {
+ return points;
+ }
+
+ final List<Vector2D> reducedPoints = new ArrayList<Vector2D>(quadrilateral);
+ for (final Vector2D p : points) {
+ // check all points if they are within the quadrilateral
+ // in which case they can not be part of the convex hull
+ if (!insideQuadrilateral(p, quadrilateral)) {
+ reducedPoints.add(p);
+ }
+ }
+
+ return reducedPoints;
+ }
+
+ /**
+ * Build the convex quadrilateral with the found corner points (with min/max x/y coordinates).
+ *
+ * @param points the respective points with min/max x/y coordinate
+ * @return the quadrilateral
+ */
+ private static List<Vector2D> buildQuadrilateral(final Vector2D... points) {
+ List<Vector2D> quadrilateral = new ArrayList<Vector2D>();
+ for (Vector2D p : points) {
+ if (!quadrilateral.contains(p)) {
+ quadrilateral.add(p);
+ }
+ }
+ return quadrilateral;
+ }
+
+ /**
+ * Checks if the given point is located within the convex quadrilateral.
+ * @param point the point to check
+ * @param quadrilateralPoints the convex quadrilateral, represented by 4 points
+ * @return {@code true} if the point is inside the quadrilateral, {@code false} otherwise
+ */
+ private static boolean insideQuadrilateral(final Vector2D point,
+ final List<Vector2D> quadrilateralPoints) {
+
+ Vector2D p1 = quadrilateralPoints.get(0);
+ Vector2D p2 = quadrilateralPoints.get(1);
+
+ if (point.equals(p1) || point.equals(p2)) {
+ return true;
+ }
+
+ // get the location of the point relative to the first two vertices
+ final double last = point.crossProduct(p1, p2);
+ final int size = quadrilateralPoints.size();
+ // loop through the rest of the vertices
+ for (int i = 1; i < size; i++) {
+ p1 = p2;
+ p2 = quadrilateralPoints.get((i + 1) == size ? 0 : i + 1);
+
+ if (point.equals(p1) || point.equals(p2)) {
+ return true;
+ }
+
+ // do side of line test: multiply the last location with this location
+ // if they are the same sign then the operation will yield a positive result
+ // -x * -y = +xy, x * y = +xy, -x * y = -xy, x * -y = -xy
+ if (last * point.crossProduct(p1, p2) < 0) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+}
diff --git a/src/main/java/org/apache/commons/math3/geometry/euclidean/twod/hull/ConvexHull2D.java b/src/main/java/org/apache/commons/math3/geometry/euclidean/twod/hull/ConvexHull2D.java
new file mode 100644
index 0000000..5d9734b
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/geometry/euclidean/twod/hull/ConvexHull2D.java
@@ -0,0 +1,172 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math3.geometry.euclidean.twod.hull;
+
+import java.io.Serializable;
+
+import org.apache.commons.math3.exception.InsufficientDataException;
+import org.apache.commons.math3.exception.MathIllegalArgumentException;
+import org.apache.commons.math3.exception.util.LocalizedFormats;
+import org.apache.commons.math3.geometry.euclidean.twod.Euclidean2D;
+import org.apache.commons.math3.geometry.euclidean.twod.Line;
+import org.apache.commons.math3.geometry.euclidean.twod.Segment;
+import org.apache.commons.math3.geometry.euclidean.twod.Vector2D;
+import org.apache.commons.math3.geometry.hull.ConvexHull;
+import org.apache.commons.math3.geometry.partitioning.Region;
+import org.apache.commons.math3.geometry.partitioning.RegionFactory;
+import org.apache.commons.math3.util.MathArrays;
+import org.apache.commons.math3.util.Precision;
+
+/**
+ * This class represents a convex hull in an two-dimensional euclidean space.
+ *
+ * @since 3.3
+ */
+public class ConvexHull2D implements ConvexHull<Euclidean2D, Vector2D>, Serializable {
+
+ /** Serializable UID. */
+ private static final long serialVersionUID = 20140129L;
+
+ /** Vertices of the hull. */
+ private final Vector2D[] vertices;
+
+ /** Tolerance threshold used during creation of the hull vertices. */
+ private final double tolerance;
+
+ /**
+ * Line segments of the hull.
+ * The array is not serialized and will be created from the vertices on first access.
+ */
+ private transient Segment[] lineSegments;
+
+ /**
+ * Simple constructor.
+ * @param vertices the vertices of the convex hull, must be ordered
+ * @param tolerance tolerance below which points are considered identical
+ * @throws MathIllegalArgumentException if the vertices do not form a convex hull
+ */
+ public ConvexHull2D(final Vector2D[] vertices, final double tolerance)
+ throws MathIllegalArgumentException {
+
+ // assign tolerance as it will be used by the isConvex method
+ this.tolerance = tolerance;
+
+ if (!isConvex(vertices)) {
+ throw new MathIllegalArgumentException(LocalizedFormats.NOT_CONVEX);
+ }
+
+ this.vertices = vertices.clone();
+ }
+
+ /**
+ * Checks whether the given hull vertices form a convex hull.
+ * @param hullVertices the hull vertices
+ * @return {@code true} if the vertices form a convex hull, {@code false} otherwise
+ */
+ private boolean isConvex(final Vector2D[] hullVertices) {
+ if (hullVertices.length < 3) {
+ return true;
+ }
+
+ int sign = 0;
+ for (int i = 0; i < hullVertices.length; i++) {
+ final Vector2D p1 = hullVertices[i == 0 ? hullVertices.length - 1 : i - 1];
+ final Vector2D p2 = hullVertices[i];
+ final Vector2D p3 = hullVertices[i == hullVertices.length - 1 ? 0 : i + 1];
+
+ final Vector2D d1 = p2.subtract(p1);
+ final Vector2D d2 = p3.subtract(p2);
+
+ final double crossProduct = MathArrays.linearCombination(d1.getX(), d2.getY(), -d1.getY(), d2.getX());
+ final int cmp = Precision.compareTo(crossProduct, 0.0, tolerance);
+ // in case of collinear points the cross product will be zero
+ if (cmp != 0.0) {
+ if (sign != 0.0 && cmp != sign) {
+ return false;
+ }
+ sign = cmp;
+ }
+ }
+
+ return true;
+ }
+
+ /** {@inheritDoc} */
+ public Vector2D[] getVertices() {
+ return vertices.clone();
+ }
+
+ /**
+ * Get the line segments of the convex hull, ordered.
+ * @return the line segments of the convex hull
+ */
+ public Segment[] getLineSegments() {
+ return retrieveLineSegments().clone();
+ }
+
+ /**
+ * Retrieve the line segments from the cached array or create them if needed.
+ *
+ * @return the array of line segments
+ */
+ private Segment[] retrieveLineSegments() {
+ if (lineSegments == null) {
+ // construct the line segments - handle special cases of 1 or 2 points
+ final int size = vertices.length;
+ if (size <= 1) {
+ this.lineSegments = new Segment[0];
+ } else if (size == 2) {
+ this.lineSegments = new Segment[1];
+ final Vector2D p1 = vertices[0];
+ final Vector2D p2 = vertices[1];
+ this.lineSegments[0] = new Segment(p1, p2, new Line(p1, p2, tolerance));
+ } else {
+ this.lineSegments = new Segment[size];
+ Vector2D firstPoint = null;
+ Vector2D lastPoint = null;
+ int index = 0;
+ for (Vector2D point : vertices) {
+ if (lastPoint == null) {
+ firstPoint = point;
+ lastPoint = point;
+ } else {
+ this.lineSegments[index++] =
+ new Segment(lastPoint, point, new Line(lastPoint, point, tolerance));
+ lastPoint = point;
+ }
+ }
+ this.lineSegments[index] =
+ new Segment(lastPoint, firstPoint, new Line(lastPoint, firstPoint, tolerance));
+ }
+ }
+ return lineSegments;
+ }
+
+ /** {@inheritDoc} */
+ public Region<Euclidean2D> createRegion() throws InsufficientDataException {
+ if (vertices.length < 3) {
+ throw new InsufficientDataException();
+ }
+ final RegionFactory<Euclidean2D> factory = new RegionFactory<Euclidean2D>();
+ final Segment[] segments = retrieveLineSegments();
+ final Line[] lineArray = new Line[segments.length];
+ for (int i = 0; i < segments.length; i++) {
+ lineArray[i] = segments[i].getLine();
+ }
+ return factory.buildConvex(lineArray);
+ }
+}
diff --git a/src/main/java/org/apache/commons/math3/geometry/euclidean/twod/hull/ConvexHullGenerator2D.java b/src/main/java/org/apache/commons/math3/geometry/euclidean/twod/hull/ConvexHullGenerator2D.java
new file mode 100644
index 0000000..3e13e1a
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/geometry/euclidean/twod/hull/ConvexHullGenerator2D.java
@@ -0,0 +1,37 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math3.geometry.euclidean.twod.hull;
+
+import java.util.Collection;
+
+import org.apache.commons.math3.exception.ConvergenceException;
+import org.apache.commons.math3.exception.NullArgumentException;
+import org.apache.commons.math3.geometry.euclidean.twod.Euclidean2D;
+import org.apache.commons.math3.geometry.euclidean.twod.Vector2D;
+import org.apache.commons.math3.geometry.hull.ConvexHullGenerator;
+
+/**
+ * Interface for convex hull generators in the two-dimensional euclidean space.
+ *
+ * @since 3.3
+ */
+public interface ConvexHullGenerator2D extends ConvexHullGenerator<Euclidean2D, Vector2D> {
+
+ /** {@inheritDoc} */
+ ConvexHull2D generate(Collection<Vector2D> points) throws NullArgumentException, ConvergenceException;
+
+}
diff --git a/src/main/java/org/apache/commons/math3/geometry/euclidean/twod/hull/MonotoneChain.java b/src/main/java/org/apache/commons/math3/geometry/euclidean/twod/hull/MonotoneChain.java
new file mode 100644
index 0000000..4421344
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/geometry/euclidean/twod/hull/MonotoneChain.java
@@ -0,0 +1,181 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math3.geometry.euclidean.twod.hull;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.List;
+
+import org.apache.commons.math3.geometry.euclidean.twod.Line;
+import org.apache.commons.math3.geometry.euclidean.twod.Vector2D;
+import org.apache.commons.math3.util.FastMath;
+import org.apache.commons.math3.util.Precision;
+
+/**
+ * Implements Andrew's monotone chain method to generate the convex hull of a finite set of
+ * points in the two-dimensional euclidean space.
+ * <p>
+ * The runtime complexity is O(n log n), with n being the number of input points. If the
+ * point set is already sorted (by x-coordinate), the runtime complexity is O(n).
+ * <p>
+ * The implementation is not sensitive to collinear points on the hull. The parameter
+ * {@code includeCollinearPoints} allows to control the behavior with regard to collinear points.
+ * If {@code true}, all points on the boundary of the hull will be added to the hull vertices,
+ * otherwise only the extreme points will be present. By default, collinear points are not added
+ * as hull vertices.
+ * <p>
+ * The {@code tolerance} parameter (default: 1e-10) is used as epsilon criteria to determine
+ * identical and collinear points.
+ *
+ * @see <a href="http://en.wikibooks.org/wiki/Algorithm_Implementation/Geometry/Convex_hull/Monotone_chain">
+ * Andrew's monotone chain algorithm (Wikibooks)</a>
+ * @since 3.3
+ */
+public class MonotoneChain extends AbstractConvexHullGenerator2D {
+
+ /**
+ * Create a new MonotoneChain instance.
+ */
+ public MonotoneChain() {
+ this(false);
+ }
+
+ /**
+ * Create a new MonotoneChain instance.
+ * @param includeCollinearPoints whether collinear points shall be added as hull vertices
+ */
+ public MonotoneChain(final boolean includeCollinearPoints) {
+ super(includeCollinearPoints);
+ }
+
+ /**
+ * Create a new MonotoneChain instance.
+ * @param includeCollinearPoints whether collinear points shall be added as hull vertices
+ * @param tolerance tolerance below which points are considered identical
+ */
+ public MonotoneChain(final boolean includeCollinearPoints, final double tolerance) {
+ super(includeCollinearPoints, tolerance);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public Collection<Vector2D> findHullVertices(final Collection<Vector2D> points) {
+
+ final List<Vector2D> pointsSortedByXAxis = new ArrayList<Vector2D>(points);
+
+ // sort the points in increasing order on the x-axis
+ Collections.sort(pointsSortedByXAxis, new Comparator<Vector2D>() {
+ /** {@inheritDoc} */
+ public int compare(final Vector2D o1, final Vector2D o2) {
+ final double tolerance = getTolerance();
+ // need to take the tolerance value into account, otherwise collinear points
+ // will not be handled correctly when building the upper/lower hull
+ final int diff = Precision.compareTo(o1.getX(), o2.getX(), tolerance);
+ if (diff == 0) {
+ return Precision.compareTo(o1.getY(), o2.getY(), tolerance);
+ } else {
+ return diff;
+ }
+ }
+ });
+
+ // build lower hull
+ final List<Vector2D> lowerHull = new ArrayList<Vector2D>();
+ for (Vector2D p : pointsSortedByXAxis) {
+ updateHull(p, lowerHull);
+ }
+
+ // build upper hull
+ final List<Vector2D> upperHull = new ArrayList<Vector2D>();
+ for (int idx = pointsSortedByXAxis.size() - 1; idx >= 0; idx--) {
+ final Vector2D p = pointsSortedByXAxis.get(idx);
+ updateHull(p, upperHull);
+ }
+
+ // concatenate the lower and upper hulls
+ // the last point of each list is omitted as it is repeated at the beginning of the other list
+ final List<Vector2D> hullVertices = new ArrayList<Vector2D>(lowerHull.size() + upperHull.size() - 2);
+ for (int idx = 0; idx < lowerHull.size() - 1; idx++) {
+ hullVertices.add(lowerHull.get(idx));
+ }
+ for (int idx = 0; idx < upperHull.size() - 1; idx++) {
+ hullVertices.add(upperHull.get(idx));
+ }
+
+ // special case: if the lower and upper hull may contain only 1 point if all are identical
+ if (hullVertices.isEmpty() && ! lowerHull.isEmpty()) {
+ hullVertices.add(lowerHull.get(0));
+ }
+
+ return hullVertices;
+ }
+
+ /**
+ * Update the partial hull with the current point.
+ *
+ * @param point the current point
+ * @param hull the partial hull
+ */
+ private void updateHull(final Vector2D point, final List<Vector2D> hull) {
+ final double tolerance = getTolerance();
+
+ if (hull.size() == 1) {
+ // ensure that we do not add an identical point
+ final Vector2D p1 = hull.get(0);
+ if (p1.distance(point) < tolerance) {
+ return;
+ }
+ }
+
+ while (hull.size() >= 2) {
+ final int size = hull.size();
+ final Vector2D p1 = hull.get(size - 2);
+ final Vector2D p2 = hull.get(size - 1);
+
+ final double offset = new Line(p1, p2, tolerance).getOffset(point);
+ if (FastMath.abs(offset) < tolerance) {
+ // the point is collinear to the line (p1, p2)
+
+ final double distanceToCurrent = p1.distance(point);
+ if (distanceToCurrent < tolerance || p2.distance(point) < tolerance) {
+ // the point is assumed to be identical to either p1 or p2
+ return;
+ }
+
+ final double distanceToLast = p1.distance(p2);
+ if (isIncludeCollinearPoints()) {
+ final int index = distanceToCurrent < distanceToLast ? size - 1 : size;
+ hull.add(index, point);
+ } else {
+ if (distanceToCurrent > distanceToLast) {
+ hull.remove(size - 1);
+ hull.add(point);
+ }
+ }
+ return;
+ } else if (offset > 0) {
+ hull.remove(size - 1);
+ } else {
+ break;
+ }
+ }
+ hull.add(point);
+ }
+
+}
diff --git a/src/main/java/org/apache/commons/math3/geometry/euclidean/twod/hull/package-info.java b/src/main/java/org/apache/commons/math3/geometry/euclidean/twod/hull/package-info.java
new file mode 100644
index 0000000..d0469a4
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/geometry/euclidean/twod/hull/package-info.java
@@ -0,0 +1,25 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/**
+ *
+ * <p>
+ * This package provides algorithms to generate the convex hull
+ * for a set of points in an two-dimensional euclidean space.
+ * </p>
+ *
+ */
+package org.apache.commons.math3.geometry.euclidean.twod.hull;
diff --git a/src/main/java/org/apache/commons/math3/geometry/euclidean/twod/package-info.java b/src/main/java/org/apache/commons/math3/geometry/euclidean/twod/package-info.java
new file mode 100644
index 0000000..feb43b1
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/geometry/euclidean/twod/package-info.java
@@ -0,0 +1,24 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/**
+ *
+ * <p>
+ * This package provides basic 2D geometry components.
+ * </p>
+ *
+ */
+package org.apache.commons.math3.geometry.euclidean.twod;
diff --git a/src/main/java/org/apache/commons/math3/geometry/hull/ConvexHull.java b/src/main/java/org/apache/commons/math3/geometry/hull/ConvexHull.java
new file mode 100644
index 0000000..8dfa3f3
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/geometry/hull/ConvexHull.java
@@ -0,0 +1,48 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math3.geometry.hull;
+
+import java.io.Serializable;
+
+import org.apache.commons.math3.exception.InsufficientDataException;
+import org.apache.commons.math3.geometry.Point;
+import org.apache.commons.math3.geometry.Space;
+import org.apache.commons.math3.geometry.partitioning.Region;
+
+/**
+ * This class represents a convex hull.
+ *
+ * @param <S> Space type.
+ * @param <P> Point type.
+ * @since 3.3
+ */
+public interface ConvexHull<S extends Space, P extends Point<S>> extends Serializable {
+
+ /**
+ * Get the vertices of the convex hull.
+ * @return vertices of the convex hull
+ */
+ P[] getVertices();
+
+ /**
+ * Returns a new region that is enclosed by the convex hull.
+ * @return the region enclosed by the convex hull
+ * @throws InsufficientDataException if the number of vertices is not enough to
+ * build a region in the respective space
+ */
+ Region<S> createRegion() throws InsufficientDataException;
+}
diff --git a/src/main/java/org/apache/commons/math3/geometry/hull/ConvexHullGenerator.java b/src/main/java/org/apache/commons/math3/geometry/hull/ConvexHullGenerator.java
new file mode 100644
index 0000000..8f601d2
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/geometry/hull/ConvexHullGenerator.java
@@ -0,0 +1,49 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math3.geometry.hull;
+
+import java.util.Collection;
+
+import org.apache.commons.math3.exception.ConvergenceException;
+import org.apache.commons.math3.exception.NullArgumentException;
+import org.apache.commons.math3.geometry.Point;
+import org.apache.commons.math3.geometry.Space;
+
+/**
+ * Interface for convex hull generators.
+ *
+ * @param <S> Type of the {@link Space}
+ * @param <P> Type of the {@link Point}
+ *
+ * @see <a href="http://en.wikipedia.org/wiki/Convex_hull">Convex Hull (Wikipedia)</a>
+ * @see <a href="http://mathworld.wolfram.com/ConvexHull.html">Convex Hull (MathWorld)</a>
+ *
+ * @since 3.3
+ */
+public interface ConvexHullGenerator<S extends Space, P extends Point<S>> {
+
+ /**
+ * Builds the convex hull from the set of input points.
+ *
+ * @param points the set of input points
+ * @return the convex hull
+ * @throws NullArgumentException if the input collection is {@code null}
+ * @throws ConvergenceException if generator fails to generate a convex hull for
+ * the given set of input points
+ */
+ ConvexHull<S, P> generate(Collection<P> points) throws NullArgumentException, ConvergenceException;
+}
diff --git a/src/main/java/org/apache/commons/math3/geometry/hull/package-info.java b/src/main/java/org/apache/commons/math3/geometry/hull/package-info.java
new file mode 100644
index 0000000..2246682
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/geometry/hull/package-info.java
@@ -0,0 +1,24 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/**
+ *
+ * <p>
+ * This package provides interfaces and classes related to the convex hull problem.
+ * </p>
+ *
+ */
+package org.apache.commons.math3.geometry.hull;
diff --git a/src/main/java/org/apache/commons/math3/geometry/package-info.java b/src/main/java/org/apache/commons/math3/geometry/package-info.java
new file mode 100644
index 0000000..6c39cff
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/geometry/package-info.java
@@ -0,0 +1,21 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/**
+ * This package is the top level package for geometry. It provides only a few interfaces related to
+ * vectorial/affine spaces that are implemented in sub-packages.
+ */
+package org.apache.commons.math3.geometry;
diff --git a/src/main/java/org/apache/commons/math3/geometry/partitioning/AbstractRegion.java b/src/main/java/org/apache/commons/math3/geometry/partitioning/AbstractRegion.java
new file mode 100644
index 0000000..d901ab4
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/geometry/partitioning/AbstractRegion.java
@@ -0,0 +1,540 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math3.geometry.partitioning;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Comparator;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.TreeSet;
+
+import org.apache.commons.math3.geometry.Point;
+import org.apache.commons.math3.geometry.Space;
+import org.apache.commons.math3.geometry.Vector;
+
+/** Abstract class for all regions, independently of geometry type or dimension.
+
+ * @param <S> Type of the space.
+ * @param <T> Type of the sub-space.
+
+ * @since 3.0
+ */
+public abstract class AbstractRegion<S extends Space, T extends Space> implements Region<S> {
+
+ /** Inside/Outside BSP tree. */
+ private BSPTree<S> tree;
+
+ /** Tolerance below which points are considered to belong to hyperplanes. */
+ private final double tolerance;
+
+ /** Size of the instance. */
+ private double size;
+
+ /** Barycenter. */
+ private Point<S> barycenter;
+
+ /** Build a region representing the whole space.
+ * @param tolerance tolerance below which points are considered identical.
+ */
+ protected AbstractRegion(final double tolerance) {
+ this.tree = new BSPTree<S>(Boolean.TRUE);
+ this.tolerance = tolerance;
+ }
+
+ /** Build a region from an inside/outside BSP tree.
+ * <p>The leaf nodes of the BSP tree <em>must</em> have a
+ * {@code Boolean} attribute representing the inside status of
+ * the corresponding cell (true for inside cells, false for outside
+ * cells). In order to avoid building too many small objects, it is
+ * recommended to use the predefined constants
+ * {@code Boolean.TRUE} and {@code Boolean.FALSE}. The
+ * tree also <em>must</em> have either null internal nodes or
+ * internal nodes representing the boundary as specified in the
+ * {@link #getTree getTree} method).</p>
+ * @param tree inside/outside BSP tree representing the region
+ * @param tolerance tolerance below which points are considered identical.
+ */
+ protected AbstractRegion(final BSPTree<S> tree, final double tolerance) {
+ this.tree = tree;
+ this.tolerance = tolerance;
+ }
+
+ /** Build a Region from a Boundary REPresentation (B-rep).
+ * <p>The boundary is provided as a collection of {@link
+ * SubHyperplane sub-hyperplanes}. Each sub-hyperplane has the
+ * interior part of the region on its minus side and the exterior on
+ * its plus side.</p>
+ * <p>The boundary elements can be in any order, and can form
+ * several non-connected sets (like for example polygons with holes
+ * or a set of disjoints polyhedrons considered as a whole). In
+ * fact, the elements do not even need to be connected together
+ * (their topological connections are not used here). However, if the
+ * boundary does not really separate an inside open from an outside
+ * open (open having here its topological meaning), then subsequent
+ * calls to the {@link #checkPoint(Point) checkPoint} method will not be
+ * meaningful anymore.</p>
+ * <p>If the boundary is empty, the region will represent the whole
+ * space.</p>
+ * @param boundary collection of boundary elements, as a
+ * collection of {@link SubHyperplane SubHyperplane} objects
+ * @param tolerance tolerance below which points are considered identical.
+ */
+ protected AbstractRegion(final Collection<SubHyperplane<S>> boundary, final double tolerance) {
+
+ this.tolerance = tolerance;
+
+ if (boundary.size() == 0) {
+
+ // the tree represents the whole space
+ tree = new BSPTree<S>(Boolean.TRUE);
+
+ } else {
+
+ // sort the boundary elements in decreasing size order
+ // (we don't want equal size elements to be removed, so
+ // we use a trick to fool the TreeSet)
+ final TreeSet<SubHyperplane<S>> ordered = new TreeSet<SubHyperplane<S>>(new Comparator<SubHyperplane<S>>() {
+ /** {@inheritDoc} */
+ public int compare(final SubHyperplane<S> o1, final SubHyperplane<S> o2) {
+ final double size1 = o1.getSize();
+ final double size2 = o2.getSize();
+ return (size2 < size1) ? -1 : ((o1 == o2) ? 0 : +1);
+ }
+ });
+ ordered.addAll(boundary);
+
+ // build the tree top-down
+ tree = new BSPTree<S>();
+ insertCuts(tree, ordered);
+
+ // set up the inside/outside flags
+ tree.visit(new BSPTreeVisitor<S>() {
+
+ /** {@inheritDoc} */
+ public Order visitOrder(final BSPTree<S> node) {
+ return Order.PLUS_SUB_MINUS;
+ }
+
+ /** {@inheritDoc} */
+ public void visitInternalNode(final BSPTree<S> node) {
+ }
+
+ /** {@inheritDoc} */
+ public void visitLeafNode(final BSPTree<S> node) {
+ if (node.getParent() == null || node == node.getParent().getMinus()) {
+ node.setAttribute(Boolean.TRUE);
+ } else {
+ node.setAttribute(Boolean.FALSE);
+ }
+ }
+ });
+
+ }
+
+ }
+
+ /** Build a convex region from an array of bounding hyperplanes.
+ * @param hyperplanes array of bounding hyperplanes (if null, an
+ * empty region will be built)
+ * @param tolerance tolerance below which points are considered identical.
+ */
+ public AbstractRegion(final Hyperplane<S>[] hyperplanes, final double tolerance) {
+ this.tolerance = tolerance;
+ if ((hyperplanes == null) || (hyperplanes.length == 0)) {
+ tree = new BSPTree<S>(Boolean.FALSE);
+ } else {
+
+ // use the first hyperplane to build the right class
+ tree = hyperplanes[0].wholeSpace().getTree(false);
+
+ // chop off parts of the space
+ BSPTree<S> node = tree;
+ node.setAttribute(Boolean.TRUE);
+ for (final Hyperplane<S> hyperplane : hyperplanes) {
+ if (node.insertCut(hyperplane)) {
+ node.setAttribute(null);
+ node.getPlus().setAttribute(Boolean.FALSE);
+ node = node.getMinus();
+ node.setAttribute(Boolean.TRUE);
+ }
+ }
+
+ }
+
+ }
+
+ /** {@inheritDoc} */
+ public abstract AbstractRegion<S, T> buildNew(BSPTree<S> newTree);
+
+ /** Get the tolerance below which points are considered to belong to hyperplanes.
+ * @return tolerance below which points are considered to belong to hyperplanes
+ */
+ public double getTolerance() {
+ return tolerance;
+ }
+
+ /** Recursively build a tree by inserting cut sub-hyperplanes.
+ * @param node current tree node (it is a leaf node at the beginning
+ * of the call)
+ * @param boundary collection of edges belonging to the cell defined
+ * by the node
+ */
+ private void insertCuts(final BSPTree<S> node, final Collection<SubHyperplane<S>> boundary) {
+
+ final Iterator<SubHyperplane<S>> iterator = boundary.iterator();
+
+ // build the current level
+ Hyperplane<S> inserted = null;
+ while ((inserted == null) && iterator.hasNext()) {
+ inserted = iterator.next().getHyperplane();
+ if (!node.insertCut(inserted.copySelf())) {
+ inserted = null;
+ }
+ }
+
+ if (!iterator.hasNext()) {
+ return;
+ }
+
+ // distribute the remaining edges in the two sub-trees
+ final ArrayList<SubHyperplane<S>> plusList = new ArrayList<SubHyperplane<S>>();
+ final ArrayList<SubHyperplane<S>> minusList = new ArrayList<SubHyperplane<S>>();
+ while (iterator.hasNext()) {
+ final SubHyperplane<S> other = iterator.next();
+ final SubHyperplane.SplitSubHyperplane<S> split = other.split(inserted);
+ switch (split.getSide()) {
+ case PLUS:
+ plusList.add(other);
+ break;
+ case MINUS:
+ minusList.add(other);
+ break;
+ case BOTH:
+ plusList.add(split.getPlus());
+ minusList.add(split.getMinus());
+ break;
+ default:
+ // ignore the sub-hyperplanes belonging to the cut hyperplane
+ }
+ }
+
+ // recurse through lower levels
+ insertCuts(node.getPlus(), plusList);
+ insertCuts(node.getMinus(), minusList);
+
+ }
+
+ /** {@inheritDoc} */
+ public AbstractRegion<S, T> copySelf() {
+ return buildNew(tree.copySelf());
+ }
+
+ /** {@inheritDoc} */
+ public boolean isEmpty() {
+ return isEmpty(tree);
+ }
+
+ /** {@inheritDoc} */
+ public boolean isEmpty(final BSPTree<S> node) {
+
+ // we use a recursive function rather than the BSPTreeVisitor
+ // interface because we can stop visiting the tree as soon as we
+ // have found an inside cell
+
+ if (node.getCut() == null) {
+ // if we find an inside node, the region is not empty
+ return !((Boolean) node.getAttribute());
+ }
+
+ // check both sides of the sub-tree
+ return isEmpty(node.getMinus()) && isEmpty(node.getPlus());
+
+ }
+
+ /** {@inheritDoc} */
+ public boolean isFull() {
+ return isFull(tree);
+ }
+
+ /** {@inheritDoc} */
+ public boolean isFull(final BSPTree<S> node) {
+
+ // we use a recursive function rather than the BSPTreeVisitor
+ // interface because we can stop visiting the tree as soon as we
+ // have found an outside cell
+
+ if (node.getCut() == null) {
+ // if we find an outside node, the region does not cover full space
+ return (Boolean) node.getAttribute();
+ }
+
+ // check both sides of the sub-tree
+ return isFull(node.getMinus()) && isFull(node.getPlus());
+
+ }
+
+ /** {@inheritDoc} */
+ public boolean contains(final Region<S> region) {
+ return new RegionFactory<S>().difference(region, this).isEmpty();
+ }
+
+ /** {@inheritDoc}
+ * @since 3.3
+ */
+ public BoundaryProjection<S> projectToBoundary(final Point<S> point) {
+ final BoundaryProjector<S, T> projector = new BoundaryProjector<S, T>(point);
+ getTree(true).visit(projector);
+ return projector.getProjection();
+ }
+
+ /** Check a point with respect to the region.
+ * @param point point to check
+ * @return a code representing the point status: either {@link
+ * Region.Location#INSIDE}, {@link Region.Location#OUTSIDE} or
+ * {@link Region.Location#BOUNDARY}
+ */
+ public Location checkPoint(final Vector<S> point) {
+ return checkPoint((Point<S>) point);
+ }
+
+ /** {@inheritDoc} */
+ public Location checkPoint(final Point<S> point) {
+ return checkPoint(tree, point);
+ }
+
+ /** Check a point with respect to the region starting at a given node.
+ * @param node root node of the region
+ * @param point point to check
+ * @return a code representing the point status: either {@link
+ * Region.Location#INSIDE INSIDE}, {@link Region.Location#OUTSIDE
+ * OUTSIDE} or {@link Region.Location#BOUNDARY BOUNDARY}
+ */
+ protected Location checkPoint(final BSPTree<S> node, final Vector<S> point) {
+ return checkPoint(node, (Point<S>) point);
+ }
+
+ /** Check a point with respect to the region starting at a given node.
+ * @param node root node of the region
+ * @param point point to check
+ * @return a code representing the point status: either {@link
+ * Region.Location#INSIDE INSIDE}, {@link Region.Location#OUTSIDE
+ * OUTSIDE} or {@link Region.Location#BOUNDARY BOUNDARY}
+ */
+ protected Location checkPoint(final BSPTree<S> node, final Point<S> point) {
+ final BSPTree<S> cell = node.getCell(point, tolerance);
+ if (cell.getCut() == null) {
+ // the point is in the interior of a cell, just check the attribute
+ return ((Boolean) cell.getAttribute()) ? Location.INSIDE : Location.OUTSIDE;
+ }
+
+ // the point is on a cut-sub-hyperplane, is it on a boundary ?
+ final Location minusCode = checkPoint(cell.getMinus(), point);
+ final Location plusCode = checkPoint(cell.getPlus(), point);
+ return (minusCode == plusCode) ? minusCode : Location.BOUNDARY;
+
+ }
+
+ /** {@inheritDoc} */
+ public BSPTree<S> getTree(final boolean includeBoundaryAttributes) {
+ if (includeBoundaryAttributes && (tree.getCut() != null) && (tree.getAttribute() == null)) {
+ // compute the boundary attributes
+ tree.visit(new BoundaryBuilder<S>());
+ }
+ return tree;
+ }
+
+ /** {@inheritDoc} */
+ public double getBoundarySize() {
+ final BoundarySizeVisitor<S> visitor = new BoundarySizeVisitor<S>();
+ getTree(true).visit(visitor);
+ return visitor.getSize();
+ }
+
+ /** {@inheritDoc} */
+ public double getSize() {
+ if (barycenter == null) {
+ computeGeometricalProperties();
+ }
+ return size;
+ }
+
+ /** Set the size of the instance.
+ * @param size size of the instance
+ */
+ protected void setSize(final double size) {
+ this.size = size;
+ }
+
+ /** {@inheritDoc} */
+ public Point<S> getBarycenter() {
+ if (barycenter == null) {
+ computeGeometricalProperties();
+ }
+ return barycenter;
+ }
+
+ /** Set the barycenter of the instance.
+ * @param barycenter barycenter of the instance
+ */
+ protected void setBarycenter(final Vector<S> barycenter) {
+ setBarycenter((Point<S>) barycenter);
+ }
+
+ /** Set the barycenter of the instance.
+ * @param barycenter barycenter of the instance
+ */
+ protected void setBarycenter(final Point<S> barycenter) {
+ this.barycenter = barycenter;
+ }
+
+ /** Compute some geometrical properties.
+ * <p>The properties to compute are the barycenter and the size.</p>
+ */
+ protected abstract void computeGeometricalProperties();
+
+ /** {@inheritDoc} */
+ @Deprecated
+ public Side side(final Hyperplane<S> hyperplane) {
+ final InsideFinder<S> finder = new InsideFinder<S>(this);
+ finder.recurseSides(tree, hyperplane.wholeHyperplane());
+ return finder.plusFound() ?
+ (finder.minusFound() ? Side.BOTH : Side.PLUS) :
+ (finder.minusFound() ? Side.MINUS : Side.HYPER);
+ }
+
+ /** {@inheritDoc} */
+ public SubHyperplane<S> intersection(final SubHyperplane<S> sub) {
+ return recurseIntersection(tree, sub);
+ }
+
+ /** Recursively compute the parts of a sub-hyperplane that are
+ * contained in the region.
+ * @param node current BSP tree node
+ * @param sub sub-hyperplane traversing the region
+ * @return filtered sub-hyperplane
+ */
+ private SubHyperplane<S> recurseIntersection(final BSPTree<S> node, final SubHyperplane<S> sub) {
+
+ if (node.getCut() == null) {
+ return (Boolean) node.getAttribute() ? sub.copySelf() : null;
+ }
+
+ final Hyperplane<S> hyperplane = node.getCut().getHyperplane();
+ final SubHyperplane.SplitSubHyperplane<S> split = sub.split(hyperplane);
+ if (split.getPlus() != null) {
+ if (split.getMinus() != null) {
+ // both sides
+ final SubHyperplane<S> plus = recurseIntersection(node.getPlus(), split.getPlus());
+ final SubHyperplane<S> minus = recurseIntersection(node.getMinus(), split.getMinus());
+ if (plus == null) {
+ return minus;
+ } else if (minus == null) {
+ return plus;
+ } else {
+ return plus.reunite(minus);
+ }
+ } else {
+ // only on plus side
+ return recurseIntersection(node.getPlus(), sub);
+ }
+ } else if (split.getMinus() != null) {
+ // only on minus side
+ return recurseIntersection(node.getMinus(), sub);
+ } else {
+ // on hyperplane
+ return recurseIntersection(node.getPlus(),
+ recurseIntersection(node.getMinus(), sub));
+ }
+
+ }
+
+ /** Transform a region.
+ * <p>Applying a transform to a region consist in applying the
+ * transform to all the hyperplanes of the underlying BSP tree and
+ * of the boundary (and also to the sub-hyperplanes embedded in
+ * these hyperplanes) and to the barycenter. The instance is not
+ * modified, a new instance is built.</p>
+ * @param transform transform to apply
+ * @return a new region, resulting from the application of the
+ * transform to the instance
+ */
+ public AbstractRegion<S, T> applyTransform(final Transform<S, T> transform) {
+
+ // transform the tree, except for boundary attribute splitters
+ final Map<BSPTree<S>, BSPTree<S>> map = new HashMap<BSPTree<S>, BSPTree<S>>();
+ final BSPTree<S> transformedTree = recurseTransform(getTree(false), transform, map);
+
+ // set up the boundary attributes splitters
+ for (final Map.Entry<BSPTree<S>, BSPTree<S>> entry : map.entrySet()) {
+ if (entry.getKey().getCut() != null) {
+ @SuppressWarnings("unchecked")
+ BoundaryAttribute<S> original = (BoundaryAttribute<S>) entry.getKey().getAttribute();
+ if (original != null) {
+ @SuppressWarnings("unchecked")
+ BoundaryAttribute<S> transformed = (BoundaryAttribute<S>) entry.getValue().getAttribute();
+ for (final BSPTree<S> splitter : original.getSplitters()) {
+ transformed.getSplitters().add(map.get(splitter));
+ }
+ }
+ }
+ }
+
+ return buildNew(transformedTree);
+
+ }
+
+ /** Recursively transform an inside/outside BSP-tree.
+ * @param node current BSP tree node
+ * @param transform transform to apply
+ * @param map transformed nodes map
+ * @return a new tree
+ */
+ @SuppressWarnings("unchecked")
+ private BSPTree<S> recurseTransform(final BSPTree<S> node, final Transform<S, T> transform,
+ final Map<BSPTree<S>, BSPTree<S>> map) {
+
+ final BSPTree<S> transformedNode;
+ if (node.getCut() == null) {
+ transformedNode = new BSPTree<S>(node.getAttribute());
+ } else {
+
+ final SubHyperplane<S> sub = node.getCut();
+ final SubHyperplane<S> tSub = ((AbstractSubHyperplane<S, T>) sub).applyTransform(transform);
+ BoundaryAttribute<S> attribute = (BoundaryAttribute<S>) node.getAttribute();
+ if (attribute != null) {
+ final SubHyperplane<S> tPO = (attribute.getPlusOutside() == null) ?
+ null : ((AbstractSubHyperplane<S, T>) attribute.getPlusOutside()).applyTransform(transform);
+ final SubHyperplane<S> tPI = (attribute.getPlusInside() == null) ?
+ null : ((AbstractSubHyperplane<S, T>) attribute.getPlusInside()).applyTransform(transform);
+ // we start with an empty list of splitters, it will be filled in out of recursion
+ attribute = new BoundaryAttribute<S>(tPO, tPI, new NodesSet<S>());
+ }
+
+ transformedNode = new BSPTree<S>(tSub,
+ recurseTransform(node.getPlus(), transform, map),
+ recurseTransform(node.getMinus(), transform, map),
+ attribute);
+ }
+
+ map.put(node, transformedNode);
+ return transformedNode;
+
+ }
+
+}
diff --git a/src/main/java/org/apache/commons/math3/geometry/partitioning/AbstractSubHyperplane.java b/src/main/java/org/apache/commons/math3/geometry/partitioning/AbstractSubHyperplane.java
new file mode 100644
index 0000000..396b795
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/geometry/partitioning/AbstractSubHyperplane.java
@@ -0,0 +1,191 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math3.geometry.partitioning;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import org.apache.commons.math3.geometry.Space;
+
+/** This class implements the dimension-independent parts of {@link SubHyperplane}.
+
+ * <p>sub-hyperplanes are obtained when parts of an {@link
+ * Hyperplane hyperplane} are chopped off by other hyperplanes that
+ * intersect it. The remaining part is a convex region. Such objects
+ * appear in {@link BSPTree BSP trees} as the intersection of a cut
+ * hyperplane with the convex region which it splits, the chopping
+ * hyperplanes are the cut hyperplanes closer to the tree root.</p>
+
+ * @param <S> Type of the embedding space.
+ * @param <T> Type of the embedded sub-space.
+
+ * @since 3.0
+ */
+public abstract class AbstractSubHyperplane<S extends Space, T extends Space>
+ implements SubHyperplane<S> {
+
+ /** Underlying hyperplane. */
+ private final Hyperplane<S> hyperplane;
+
+ /** Remaining region of the hyperplane. */
+ private final Region<T> remainingRegion;
+
+ /** Build a sub-hyperplane from an hyperplane and a region.
+ * @param hyperplane underlying hyperplane
+ * @param remainingRegion remaining region of the hyperplane
+ */
+ protected AbstractSubHyperplane(final Hyperplane<S> hyperplane,
+ final Region<T> remainingRegion) {
+ this.hyperplane = hyperplane;
+ this.remainingRegion = remainingRegion;
+ }
+
+ /** Build a sub-hyperplane from an hyperplane and a region.
+ * @param hyper underlying hyperplane
+ * @param remaining remaining region of the hyperplane
+ * @return a new sub-hyperplane
+ */
+ protected abstract AbstractSubHyperplane<S, T> buildNew(final Hyperplane<S> hyper,
+ final Region<T> remaining);
+
+ /** {@inheritDoc} */
+ public AbstractSubHyperplane<S, T> copySelf() {
+ return buildNew(hyperplane.copySelf(), remainingRegion);
+ }
+
+ /** Get the underlying hyperplane.
+ * @return underlying hyperplane
+ */
+ public Hyperplane<S> getHyperplane() {
+ return hyperplane;
+ }
+
+ /** Get the remaining region of the hyperplane.
+ * <p>The returned region is expressed in the canonical hyperplane
+ * frame and has the hyperplane dimension. For example a chopped
+ * hyperplane in the 3D euclidean is a 2D plane and the
+ * corresponding region is a convex 2D polygon.</p>
+ * @return remaining region of the hyperplane
+ */
+ public Region<T> getRemainingRegion() {
+ return remainingRegion;
+ }
+
+ /** {@inheritDoc} */
+ public double getSize() {
+ return remainingRegion.getSize();
+ }
+
+ /** {@inheritDoc} */
+ public AbstractSubHyperplane<S, T> reunite(final SubHyperplane<S> other) {
+ @SuppressWarnings("unchecked")
+ AbstractSubHyperplane<S, T> o = (AbstractSubHyperplane<S, T>) other;
+ return buildNew(hyperplane,
+ new RegionFactory<T>().union(remainingRegion, o.remainingRegion));
+ }
+
+ /** Apply a transform to the instance.
+ * <p>The instance must be a (D-1)-dimension sub-hyperplane with
+ * respect to the transform <em>not</em> a (D-2)-dimension
+ * sub-hyperplane the transform knows how to transform by
+ * itself. The transform will consist in transforming first the
+ * hyperplane and then the all region using the various methods
+ * provided by the transform.</p>
+ * @param transform D-dimension transform to apply
+ * @return the transformed instance
+ */
+ public AbstractSubHyperplane<S, T> applyTransform(final Transform<S, T> transform) {
+ final Hyperplane<S> tHyperplane = transform.apply(hyperplane);
+
+ // transform the tree, except for boundary attribute splitters
+ final Map<BSPTree<T>, BSPTree<T>> map = new HashMap<BSPTree<T>, BSPTree<T>>();
+ final BSPTree<T> tTree =
+ recurseTransform(remainingRegion.getTree(false), tHyperplane, transform, map);
+
+ // set up the boundary attributes splitters
+ for (final Map.Entry<BSPTree<T>, BSPTree<T>> entry : map.entrySet()) {
+ if (entry.getKey().getCut() != null) {
+ @SuppressWarnings("unchecked")
+ BoundaryAttribute<T> original = (BoundaryAttribute<T>) entry.getKey().getAttribute();
+ if (original != null) {
+ @SuppressWarnings("unchecked")
+ BoundaryAttribute<T> transformed = (BoundaryAttribute<T>) entry.getValue().getAttribute();
+ for (final BSPTree<T> splitter : original.getSplitters()) {
+ transformed.getSplitters().add(map.get(splitter));
+ }
+ }
+ }
+ }
+
+ return buildNew(tHyperplane, remainingRegion.buildNew(tTree));
+
+ }
+
+ /** Recursively transform a BSP-tree from a sub-hyperplane.
+ * @param node current BSP tree node
+ * @param transformed image of the instance hyperplane by the transform
+ * @param transform transform to apply
+ * @param map transformed nodes map
+ * @return a new tree
+ */
+ private BSPTree<T> recurseTransform(final BSPTree<T> node,
+ final Hyperplane<S> transformed,
+ final Transform<S, T> transform,
+ final Map<BSPTree<T>, BSPTree<T>> map) {
+
+ final BSPTree<T> transformedNode;
+ if (node.getCut() == null) {
+ transformedNode = new BSPTree<T>(node.getAttribute());
+ } else {
+
+ @SuppressWarnings("unchecked")
+ BoundaryAttribute<T> attribute = (BoundaryAttribute<T>) node.getAttribute();
+ if (attribute != null) {
+ final SubHyperplane<T> tPO = (attribute.getPlusOutside() == null) ?
+ null : transform.apply(attribute.getPlusOutside(), hyperplane, transformed);
+ final SubHyperplane<T> tPI = (attribute.getPlusInside() == null) ?
+ null : transform.apply(attribute.getPlusInside(), hyperplane, transformed);
+ // we start with an empty list of splitters, it will be filled in out of recursion
+ attribute = new BoundaryAttribute<T>(tPO, tPI, new NodesSet<T>());
+ }
+
+ transformedNode = new BSPTree<T>(transform.apply(node.getCut(), hyperplane, transformed),
+ recurseTransform(node.getPlus(), transformed, transform, map),
+ recurseTransform(node.getMinus(), transformed, transform, map),
+ attribute);
+ }
+
+ map.put(node, transformedNode);
+ return transformedNode;
+
+ }
+
+ /** {@inheritDoc} */
+ @Deprecated
+ public Side side(Hyperplane<S> hyper) {
+ return split(hyper).getSide();
+ }
+
+ /** {@inheritDoc} */
+ public abstract SplitSubHyperplane<S> split(Hyperplane<S> hyper);
+
+ /** {@inheritDoc} */
+ public boolean isEmpty() {
+ return remainingRegion.isEmpty();
+ }
+
+}
diff --git a/src/main/java/org/apache/commons/math3/geometry/partitioning/BSPTree.java b/src/main/java/org/apache/commons/math3/geometry/partitioning/BSPTree.java
new file mode 100644
index 0000000..1f1a6ea
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/geometry/partitioning/BSPTree.java
@@ -0,0 +1,821 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math3.geometry.partitioning;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.apache.commons.math3.exception.MathIllegalStateException;
+import org.apache.commons.math3.exception.MathInternalError;
+import org.apache.commons.math3.exception.util.LocalizedFormats;
+import org.apache.commons.math3.geometry.Point;
+import org.apache.commons.math3.geometry.Space;
+import org.apache.commons.math3.geometry.Vector;
+import org.apache.commons.math3.util.FastMath;
+
+/** This class represent a Binary Space Partition tree.
+
+ * <p>BSP trees are an efficient way to represent space partitions and
+ * to associate attributes with each cell. Each node in a BSP tree
+ * represents a convex region which is partitioned in two convex
+ * sub-regions at each side of a cut hyperplane. The root tree
+ * contains the complete space.</p>
+
+ * <p>The main use of such partitions is to use a boolean attribute to
+ * define an inside/outside property, hence representing arbitrary
+ * polytopes (line segments in 1D, polygons in 2D and polyhedrons in
+ * 3D) and to operate on them.</p>
+
+ * <p>Another example would be to represent Voronoi tesselations, the
+ * attribute of each cell holding the defining point of the cell.</p>
+
+ * <p>The application-defined attributes are shared among copied
+ * instances and propagated to split parts. These attributes are not
+ * used by the BSP-tree algorithms themselves, so the application can
+ * use them for any purpose. Since the tree visiting method holds
+ * internal and leaf nodes differently, it is possible to use
+ * different classes for internal nodes attributes and leaf nodes
+ * attributes. This should be used with care, though, because if the
+ * tree is modified in any way after attributes have been set, some
+ * internal nodes may become leaf nodes and some leaf nodes may become
+ * internal nodes.</p>
+
+ * <p>One of the main sources for the development of this package was
+ * Bruce Naylor, John Amanatides and William Thibault paper <a
+ * href="http://www.cs.yorku.ca/~amana/research/bsptSetOp.pdf">Merging
+ * BSP Trees Yields Polyhedral Set Operations</a> Proc. Siggraph '90,
+ * Computer Graphics 24(4), August 1990, pp 115-124, published by the
+ * Association for Computing Machinery (ACM).</p>
+
+ * @param <S> Type of the space.
+
+ * @since 3.0
+ */
+public class BSPTree<S extends Space> {
+
+ /** Cut sub-hyperplane. */
+ private SubHyperplane<S> cut;
+
+ /** Tree at the plus side of the cut hyperplane. */
+ private BSPTree<S> plus;
+
+ /** Tree at the minus side of the cut hyperplane. */
+ private BSPTree<S> minus;
+
+ /** Parent tree. */
+ private BSPTree<S> parent;
+
+ /** Application-defined attribute. */
+ private Object attribute;
+
+ /** Build a tree having only one root cell representing the whole space.
+ */
+ public BSPTree() {
+ cut = null;
+ plus = null;
+ minus = null;
+ parent = null;
+ attribute = null;
+ }
+
+ /** Build a tree having only one root cell representing the whole space.
+ * @param attribute attribute of the tree (may be null)
+ */
+ public BSPTree(final Object attribute) {
+ cut = null;
+ plus = null;
+ minus = null;
+ parent = null;
+ this.attribute = attribute;
+ }
+
+ /** Build a BSPTree from its underlying elements.
+ * <p>This method does <em>not</em> perform any verification on
+ * consistency of its arguments, it should therefore be used only
+ * when then caller knows what it is doing.</p>
+ * <p>This method is mainly useful to build trees
+ * bottom-up. Building trees top-down is realized with the help of
+ * method {@link #insertCut insertCut}.</p>
+ * @param cut cut sub-hyperplane for the tree
+ * @param plus plus side sub-tree
+ * @param minus minus side sub-tree
+ * @param attribute attribute associated with the node (may be null)
+ * @see #insertCut
+ */
+ public BSPTree(final SubHyperplane<S> cut, final BSPTree<S> plus, final BSPTree<S> minus,
+ final Object attribute) {
+ this.cut = cut;
+ this.plus = plus;
+ this.minus = minus;
+ this.parent = null;
+ this.attribute = attribute;
+ plus.parent = this;
+ minus.parent = this;
+ }
+
+ /** Insert a cut sub-hyperplane in a node.
+ * <p>The sub-tree starting at this node will be completely
+ * overwritten. The new cut sub-hyperplane will be built from the
+ * intersection of the provided hyperplane with the cell. If the
+ * hyperplane does intersect the cell, the cell will have two
+ * children cells with {@code null} attributes on each side of
+ * the inserted cut sub-hyperplane. If the hyperplane does not
+ * intersect the cell then <em>no</em> cut hyperplane will be
+ * inserted and the cell will be changed to a leaf cell. The
+ * attribute of the node is never changed.</p>
+ * <p>This method is mainly useful when called on leaf nodes
+ * (i.e. nodes for which {@link #getCut getCut} returns
+ * {@code null}), in this case it provides a way to build a
+ * tree top-down (whereas the {@link #BSPTree(SubHyperplane,
+ * BSPTree, BSPTree, Object) 4 arguments constructor} is devoted to
+ * build trees bottom-up).</p>
+ * @param hyperplane hyperplane to insert, it will be chopped in
+ * order to fit in the cell defined by the parent nodes of the
+ * instance
+ * @return true if a cut sub-hyperplane has been inserted (i.e. if
+ * the cell now has two leaf child nodes)
+ * @see #BSPTree(SubHyperplane, BSPTree, BSPTree, Object)
+ */
+ public boolean insertCut(final Hyperplane<S> hyperplane) {
+
+ if (cut != null) {
+ plus.parent = null;
+ minus.parent = null;
+ }
+
+ final SubHyperplane<S> chopped = fitToCell(hyperplane.wholeHyperplane());
+ if (chopped == null || chopped.isEmpty()) {
+ cut = null;
+ plus = null;
+ minus = null;
+ return false;
+ }
+
+ cut = chopped;
+ plus = new BSPTree<S>();
+ plus.parent = this;
+ minus = new BSPTree<S>();
+ minus.parent = this;
+ return true;
+
+ }
+
+ /** Copy the instance.
+ * <p>The instance created is completely independent of the original
+ * one. A deep copy is used, none of the underlying objects are
+ * shared (except for the nodes attributes and immutable
+ * objects).</p>
+ * @return a new tree, copy of the instance
+ */
+ public BSPTree<S> copySelf() {
+
+ if (cut == null) {
+ return new BSPTree<S>(attribute);
+ }
+
+ return new BSPTree<S>(cut.copySelf(), plus.copySelf(), minus.copySelf(),
+ attribute);
+
+ }
+
+ /** Get the cut sub-hyperplane.
+ * @return cut sub-hyperplane, null if this is a leaf tree
+ */
+ public SubHyperplane<S> getCut() {
+ return cut;
+ }
+
+ /** Get the tree on the plus side of the cut hyperplane.
+ * @return tree on the plus side of the cut hyperplane, null if this
+ * is a leaf tree
+ */
+ public BSPTree<S> getPlus() {
+ return plus;
+ }
+
+ /** Get the tree on the minus side of the cut hyperplane.
+ * @return tree on the minus side of the cut hyperplane, null if this
+ * is a leaf tree
+ */
+ public BSPTree<S> getMinus() {
+ return minus;
+ }
+
+ /** Get the parent node.
+ * @return parent node, null if the node has no parents
+ */
+ public BSPTree<S> getParent() {
+ return parent;
+ }
+
+ /** Associate an attribute with the instance.
+ * @param attribute attribute to associate with the node
+ * @see #getAttribute
+ */
+ public void setAttribute(final Object attribute) {
+ this.attribute = attribute;
+ }
+
+ /** Get the attribute associated with the instance.
+ * @return attribute associated with the node or null if no
+ * attribute has been explicitly set using the {@link #setAttribute
+ * setAttribute} method
+ * @see #setAttribute
+ */
+ public Object getAttribute() {
+ return attribute;
+ }
+
+ /** Visit the BSP tree nodes.
+ * @param visitor object visiting the tree nodes
+ */
+ public void visit(final BSPTreeVisitor<S> visitor) {
+ if (cut == null) {
+ visitor.visitLeafNode(this);
+ } else {
+ switch (visitor.visitOrder(this)) {
+ case PLUS_MINUS_SUB:
+ plus.visit(visitor);
+ minus.visit(visitor);
+ visitor.visitInternalNode(this);
+ break;
+ case PLUS_SUB_MINUS:
+ plus.visit(visitor);
+ visitor.visitInternalNode(this);
+ minus.visit(visitor);
+ break;
+ case MINUS_PLUS_SUB:
+ minus.visit(visitor);
+ plus.visit(visitor);
+ visitor.visitInternalNode(this);
+ break;
+ case MINUS_SUB_PLUS:
+ minus.visit(visitor);
+ visitor.visitInternalNode(this);
+ plus.visit(visitor);
+ break;
+ case SUB_PLUS_MINUS:
+ visitor.visitInternalNode(this);
+ plus.visit(visitor);
+ minus.visit(visitor);
+ break;
+ case SUB_MINUS_PLUS:
+ visitor.visitInternalNode(this);
+ minus.visit(visitor);
+ plus.visit(visitor);
+ break;
+ default:
+ throw new MathInternalError();
+ }
+
+ }
+ }
+
+ /** Fit a sub-hyperplane inside the cell defined by the instance.
+ * <p>Fitting is done by chopping off the parts of the
+ * sub-hyperplane that lie outside of the cell using the
+ * cut-hyperplanes of the parent nodes of the instance.</p>
+ * @param sub sub-hyperplane to fit
+ * @return a new sub-hyperplane, guaranteed to have no part outside
+ * of the instance cell
+ */
+ private SubHyperplane<S> fitToCell(final SubHyperplane<S> sub) {
+ SubHyperplane<S> s = sub;
+ for (BSPTree<S> tree = this; tree.parent != null && s != null; tree = tree.parent) {
+ if (tree == tree.parent.plus) {
+ s = s.split(tree.parent.cut.getHyperplane()).getPlus();
+ } else {
+ s = s.split(tree.parent.cut.getHyperplane()).getMinus();
+ }
+ }
+ return s;
+ }
+
+ /** Get the cell to which a point belongs.
+ * <p>If the returned cell is a leaf node the points belongs to the
+ * interior of the node, if the cell is an internal node the points
+ * belongs to the node cut sub-hyperplane.</p>
+ * @param point point to check
+ * @return the tree cell to which the point belongs
+ * @deprecated as of 3.3, replaced with {@link #getCell(Point, double)}
+ */
+ @Deprecated
+ public BSPTree<S> getCell(final Vector<S> point) {
+ return getCell((Point<S>) point, 1.0e-10);
+ }
+
+ /** Get the cell to which a point belongs.
+ * <p>If the returned cell is a leaf node the points belongs to the
+ * interior of the node, if the cell is an internal node the points
+ * belongs to the node cut sub-hyperplane.</p>
+ * @param point point to check
+ * @param tolerance tolerance below which points close to a cut hyperplane
+ * are considered to belong to the hyperplane itself
+ * @return the tree cell to which the point belongs
+ */
+ public BSPTree<S> getCell(final Point<S> point, final double tolerance) {
+
+ if (cut == null) {
+ return this;
+ }
+
+ // position of the point with respect to the cut hyperplane
+ final double offset = cut.getHyperplane().getOffset(point);
+
+ if (FastMath.abs(offset) < tolerance) {
+ return this;
+ } else if (offset <= 0) {
+ // point is on the minus side of the cut hyperplane
+ return minus.getCell(point, tolerance);
+ } else {
+ // point is on the plus side of the cut hyperplane
+ return plus.getCell(point, tolerance);
+ }
+
+ }
+
+ /** Get the cells whose cut sub-hyperplanes are close to the point.
+ * @param point point to check
+ * @param maxOffset offset below which a cut sub-hyperplane is considered
+ * close to the point (in absolute value)
+ * @return close cells (may be empty if all cut sub-hyperplanes are farther
+ * than maxOffset from the point)
+ */
+ public List<BSPTree<S>> getCloseCuts(final Point<S> point, final double maxOffset) {
+ final List<BSPTree<S>> close = new ArrayList<BSPTree<S>>();
+ recurseCloseCuts(point, maxOffset, close);
+ return close;
+ }
+
+ /** Get the cells whose cut sub-hyperplanes are close to the point.
+ * @param point point to check
+ * @param maxOffset offset below which a cut sub-hyperplane is considered
+ * close to the point (in absolute value)
+ * @param close list to fill
+ */
+ private void recurseCloseCuts(final Point<S> point, final double maxOffset,
+ final List<BSPTree<S>> close) {
+ if (cut != null) {
+
+ // position of the point with respect to the cut hyperplane
+ final double offset = cut.getHyperplane().getOffset(point);
+
+ if (offset < -maxOffset) {
+ // point is on the minus side of the cut hyperplane
+ minus.recurseCloseCuts(point, maxOffset, close);
+ } else if (offset > maxOffset) {
+ // point is on the plus side of the cut hyperplane
+ plus.recurseCloseCuts(point, maxOffset, close);
+ } else {
+ // point is close to the cut hyperplane
+ close.add(this);
+ minus.recurseCloseCuts(point, maxOffset, close);
+ plus.recurseCloseCuts(point, maxOffset, close);
+ }
+
+ }
+ }
+
+ /** Perform condensation on a tree.
+ * <p>The condensation operation is not recursive, it must be called
+ * explicitly from leaves to root.</p>
+ */
+ private void condense() {
+ if ((cut != null) && (plus.cut == null) && (minus.cut == null) &&
+ (((plus.attribute == null) && (minus.attribute == null)) ||
+ ((plus.attribute != null) && plus.attribute.equals(minus.attribute)))) {
+ attribute = (plus.attribute == null) ? minus.attribute : plus.attribute;
+ cut = null;
+ plus = null;
+ minus = null;
+ }
+ }
+
+ /** Merge a BSP tree with the instance.
+ * <p>All trees are modified (parts of them are reused in the new
+ * tree), it is the responsibility of the caller to ensure a copy
+ * has been done before if any of the former tree should be
+ * preserved, <em>no</em> such copy is done here!</p>
+ * <p>The algorithm used here is directly derived from the one
+ * described in the Naylor, Amanatides and Thibault paper (section
+ * III, Binary Partitioning of a BSP Tree).</p>
+ * @param tree other tree to merge with the instance (will be
+ * <em>unusable</em> after the operation, as well as the
+ * instance itself)
+ * @param leafMerger object implementing the final merging phase
+ * (this is where the semantic of the operation occurs, generally
+ * depending on the attribute of the leaf node)
+ * @return a new tree, result of <code>instance &lt;op&gt;
+ * tree</code>, this value can be ignored if parentTree is not null
+ * since all connections have already been established
+ */
+ public BSPTree<S> merge(final BSPTree<S> tree, final LeafMerger<S> leafMerger) {
+ return merge(tree, leafMerger, null, false);
+ }
+
+ /** Merge a BSP tree with the instance.
+ * @param tree other tree to merge with the instance (will be
+ * <em>unusable</em> after the operation, as well as the
+ * instance itself)
+ * @param leafMerger object implementing the final merging phase
+ * (this is where the semantic of the operation occurs, generally
+ * depending on the attribute of the leaf node)
+ * @param parentTree parent tree to connect to (may be null)
+ * @param isPlusChild if true and if parentTree is not null, the
+ * resulting tree should be the plus child of its parent, ignored if
+ * parentTree is null
+ * @return a new tree, result of <code>instance &lt;op&gt;
+ * tree</code>, this value can be ignored if parentTree is not null
+ * since all connections have already been established
+ */
+ private BSPTree<S> merge(final BSPTree<S> tree, final LeafMerger<S> leafMerger,
+ final BSPTree<S> parentTree, final boolean isPlusChild) {
+ if (cut == null) {
+ // cell/tree operation
+ return leafMerger.merge(this, tree, parentTree, isPlusChild, true);
+ } else if (tree.cut == null) {
+ // tree/cell operation
+ return leafMerger.merge(tree, this, parentTree, isPlusChild, false);
+ } else {
+ // tree/tree operation
+ final BSPTree<S> merged = tree.split(cut);
+ if (parentTree != null) {
+ merged.parent = parentTree;
+ if (isPlusChild) {
+ parentTree.plus = merged;
+ } else {
+ parentTree.minus = merged;
+ }
+ }
+
+ // merging phase
+ plus.merge(merged.plus, leafMerger, merged, true);
+ minus.merge(merged.minus, leafMerger, merged, false);
+ merged.condense();
+ if (merged.cut != null) {
+ merged.cut = merged.fitToCell(merged.cut.getHyperplane().wholeHyperplane());
+ }
+
+ return merged;
+
+ }
+ }
+
+ /** This interface gather the merging operations between a BSP tree
+ * leaf and another BSP tree.
+ * <p>As explained in Bruce Naylor, John Amanatides and William
+ * Thibault paper <a
+ * href="http://www.cs.yorku.ca/~amana/research/bsptSetOp.pdf">Merging
+ * BSP Trees Yields Polyhedral Set Operations</a>,
+ * the operations on {@link BSPTree BSP trees} can be expressed as a
+ * generic recursive merging operation where only the final part,
+ * when one of the operand is a leaf, is specific to the real
+ * operation semantics. For example, a tree representing a region
+ * using a boolean attribute to identify inside cells and outside
+ * cells would use four different objects to implement the final
+ * merging phase of the four set operations union, intersection,
+ * difference and symmetric difference (exclusive or).</p>
+ * @param <S> Type of the space.
+ */
+ public interface LeafMerger<S extends Space> {
+
+ /** Merge a leaf node and a tree node.
+ * <p>This method is called at the end of a recursive merging
+ * resulting from a {@code tree1.merge(tree2, leafMerger)}
+ * call, when one of the sub-trees involved is a leaf (i.e. when
+ * its cut-hyperplane is null). This is the only place where the
+ * precise semantics of the operation are required. For all upper
+ * level nodes in the tree, the merging operation is only a
+ * generic partitioning algorithm.</p>
+ * <p>Since the final operation may be non-commutative, it is
+ * important to know if the leaf node comes from the instance tree
+ * ({@code tree1}) or the argument tree
+ * ({@code tree2}). The third argument of the method is
+ * devoted to this. It can be ignored for commutative
+ * operations.</p>
+ * <p>The {@link BSPTree#insertInTree BSPTree.insertInTree} method
+ * may be useful to implement this method.</p>
+ * @param leaf leaf node (its cut hyperplane is guaranteed to be
+ * null)
+ * @param tree tree node (its cut hyperplane may be null or not)
+ * @param parentTree parent tree to connect to (may be null)
+ * @param isPlusChild if true and if parentTree is not null, the
+ * resulting tree should be the plus child of its parent, ignored if
+ * parentTree is null
+ * @param leafFromInstance if true, the leaf node comes from the
+ * instance tree ({@code tree1}) and the tree node comes from
+ * the argument tree ({@code tree2})
+ * @return the BSP tree resulting from the merging (may be one of
+ * the arguments)
+ */
+ BSPTree<S> merge(BSPTree<S> leaf, BSPTree<S> tree, BSPTree<S> parentTree,
+ boolean isPlusChild, boolean leafFromInstance);
+
+ }
+
+ /** This interface handles the corner cases when an internal node cut sub-hyperplane vanishes.
+ * <p>
+ * Such cases happens for example when a cut sub-hyperplane is inserted into
+ * another tree (during a merge operation), and is split in several parts,
+ * some of which becomes smaller than the tolerance. The corresponding node
+ * as then no cut sub-hyperplane anymore, but does have children. This interface
+ * specifies how to handle this situation.
+ * setting
+ * </p>
+ * @param <S> Type of the space.
+ * @since 3.4
+ */
+ public interface VanishingCutHandler<S extends Space> {
+
+ /** Fix a node with both vanished cut and children.
+ * @param node node to fix
+ * @return fixed node
+ */
+ BSPTree<S> fixNode(BSPTree<S> node);
+
+ }
+
+ /** Split a BSP tree by an external sub-hyperplane.
+ * <p>Split a tree in two halves, on each side of the
+ * sub-hyperplane. The instance is not modified.</p>
+ * <p>The tree returned is not upward-consistent: despite all of its
+ * sub-trees cut sub-hyperplanes (including its own cut
+ * sub-hyperplane) are bounded to the current cell, it is <em>not</em>
+ * attached to any parent tree yet. This tree is intended to be
+ * later inserted into an higher level tree.</p>
+ * <p>The algorithm used here is the one given in Naylor, Amanatides
+ * and Thibault paper (section III, Binary Partitioning of a BSP
+ * Tree).</p>
+ * @param sub partitioning sub-hyperplane, must be already clipped
+ * to the convex region represented by the instance, will be used as
+ * the cut sub-hyperplane of the returned tree
+ * @return a tree having the specified sub-hyperplane as its cut
+ * sub-hyperplane, the two parts of the split instance as its two
+ * sub-trees and a null parent
+ */
+ public BSPTree<S> split(final SubHyperplane<S> sub) {
+
+ if (cut == null) {
+ return new BSPTree<S>(sub, copySelf(), new BSPTree<S>(attribute), null);
+ }
+
+ final Hyperplane<S> cHyperplane = cut.getHyperplane();
+ final Hyperplane<S> sHyperplane = sub.getHyperplane();
+ final SubHyperplane.SplitSubHyperplane<S> subParts = sub.split(cHyperplane);
+ switch (subParts.getSide()) {
+ case PLUS :
+ { // the partitioning sub-hyperplane is entirely in the plus sub-tree
+ final BSPTree<S> split = plus.split(sub);
+ if (cut.split(sHyperplane).getSide() == Side.PLUS) {
+ split.plus =
+ new BSPTree<S>(cut.copySelf(), split.plus, minus.copySelf(), attribute);
+ split.plus.condense();
+ split.plus.parent = split;
+ } else {
+ split.minus =
+ new BSPTree<S>(cut.copySelf(), split.minus, minus.copySelf(), attribute);
+ split.minus.condense();
+ split.minus.parent = split;
+ }
+ return split;
+ }
+ case MINUS :
+ { // the partitioning sub-hyperplane is entirely in the minus sub-tree
+ final BSPTree<S> split = minus.split(sub);
+ if (cut.split(sHyperplane).getSide() == Side.PLUS) {
+ split.plus =
+ new BSPTree<S>(cut.copySelf(), plus.copySelf(), split.plus, attribute);
+ split.plus.condense();
+ split.plus.parent = split;
+ } else {
+ split.minus =
+ new BSPTree<S>(cut.copySelf(), plus.copySelf(), split.minus, attribute);
+ split.minus.condense();
+ split.minus.parent = split;
+ }
+ return split;
+ }
+ case BOTH :
+ {
+ final SubHyperplane.SplitSubHyperplane<S> cutParts = cut.split(sHyperplane);
+ final BSPTree<S> split =
+ new BSPTree<S>(sub, plus.split(subParts.getPlus()), minus.split(subParts.getMinus()),
+ null);
+ split.plus.cut = cutParts.getPlus();
+ split.minus.cut = cutParts.getMinus();
+ final BSPTree<S> tmp = split.plus.minus;
+ split.plus.minus = split.minus.plus;
+ split.plus.minus.parent = split.plus;
+ split.minus.plus = tmp;
+ split.minus.plus.parent = split.minus;
+ split.plus.condense();
+ split.minus.condense();
+ return split;
+ }
+ default :
+ return cHyperplane.sameOrientationAs(sHyperplane) ?
+ new BSPTree<S>(sub, plus.copySelf(), minus.copySelf(), attribute) :
+ new BSPTree<S>(sub, minus.copySelf(), plus.copySelf(), attribute);
+ }
+
+ }
+
+ /** Insert the instance into another tree.
+ * <p>The instance itself is modified so its former parent should
+ * not be used anymore.</p>
+ * @param parentTree parent tree to connect to (may be null)
+ * @param isPlusChild if true and if parentTree is not null, the
+ * resulting tree should be the plus child of its parent, ignored if
+ * parentTree is null
+ * @see LeafMerger
+ * @deprecated as of 3.4, replaced with {@link #insertInTree(BSPTree, boolean, VanishingCutHandler)}
+ */
+ @Deprecated
+ public void insertInTree(final BSPTree<S> parentTree, final boolean isPlusChild) {
+ insertInTree(parentTree, isPlusChild, new VanishingCutHandler<S>() {
+ /** {@inheritDoc} */
+ public BSPTree<S> fixNode(BSPTree<S> node) {
+ // the cut should not be null
+ throw new MathIllegalStateException(LocalizedFormats.NULL_NOT_ALLOWED);
+ }
+ });
+ }
+
+ /** Insert the instance into another tree.
+ * <p>The instance itself is modified so its former parent should
+ * not be used anymore.</p>
+ * @param parentTree parent tree to connect to (may be null)
+ * @param isPlusChild if true and if parentTree is not null, the
+ * resulting tree should be the plus child of its parent, ignored if
+ * parentTree is null
+ * @param vanishingHandler handler to use for handling very rare corner
+ * cases of vanishing cut sub-hyperplanes in internal nodes during merging
+ * @see LeafMerger
+ * @since 3.4
+ */
+ public void insertInTree(final BSPTree<S> parentTree, final boolean isPlusChild,
+ final VanishingCutHandler<S> vanishingHandler) {
+
+ // set up parent/child links
+ parent = parentTree;
+ if (parentTree != null) {
+ if (isPlusChild) {
+ parentTree.plus = this;
+ } else {
+ parentTree.minus = this;
+ }
+ }
+
+ // make sure the inserted tree lies in the cell defined by its parent nodes
+ if (cut != null) {
+
+ // explore the parent nodes from here towards tree root
+ for (BSPTree<S> tree = this; tree.parent != null; tree = tree.parent) {
+
+ // this is an hyperplane of some parent node
+ final Hyperplane<S> hyperplane = tree.parent.cut.getHyperplane();
+
+ // chop off the parts of the inserted tree that extend
+ // on the wrong side of this parent hyperplane
+ if (tree == tree.parent.plus) {
+ cut = cut.split(hyperplane).getPlus();
+ plus.chopOffMinus(hyperplane, vanishingHandler);
+ minus.chopOffMinus(hyperplane, vanishingHandler);
+ } else {
+ cut = cut.split(hyperplane).getMinus();
+ plus.chopOffPlus(hyperplane, vanishingHandler);
+ minus.chopOffPlus(hyperplane, vanishingHandler);
+ }
+
+ if (cut == null) {
+ // the cut sub-hyperplane has vanished
+ final BSPTree<S> fixed = vanishingHandler.fixNode(this);
+ cut = fixed.cut;
+ plus = fixed.plus;
+ minus = fixed.minus;
+ attribute = fixed.attribute;
+ if (cut == null) {
+ break;
+ }
+ }
+
+ }
+
+ // since we may have drop some parts of the inserted tree,
+ // perform a condensation pass to keep the tree structure simple
+ condense();
+
+ }
+
+ }
+
+ /** Prune a tree around a cell.
+ * <p>
+ * This method can be used to extract a convex cell from a tree.
+ * The original cell may either be a leaf node or an internal node.
+ * If it is an internal node, it's subtree will be ignored (i.e. the
+ * extracted cell will be a leaf node in all cases). The original
+ * tree to which the original cell belongs is not touched at all,
+ * a new independent tree will be built.
+ * </p>
+ * @param cellAttribute attribute to set for the leaf node
+ * corresponding to the initial instance cell
+ * @param otherLeafsAttributes attribute to set for the other leaf
+ * nodes
+ * @param internalAttributes attribute to set for the internal nodes
+ * @return a new tree (the original tree is left untouched) containing
+ * a single branch with the cell as a leaf node, and other leaf nodes
+ * as the remnants of the pruned branches
+ * @since 3.3
+ */
+ public BSPTree<S> pruneAroundConvexCell(final Object cellAttribute,
+ final Object otherLeafsAttributes,
+ final Object internalAttributes) {
+
+ // build the current cell leaf
+ BSPTree<S> tree = new BSPTree<S>(cellAttribute);
+
+ // build the pruned tree bottom-up
+ for (BSPTree<S> current = this; current.parent != null; current = current.parent) {
+ final SubHyperplane<S> parentCut = current.parent.cut.copySelf();
+ final BSPTree<S> sibling = new BSPTree<S>(otherLeafsAttributes);
+ if (current == current.parent.plus) {
+ tree = new BSPTree<S>(parentCut, tree, sibling, internalAttributes);
+ } else {
+ tree = new BSPTree<S>(parentCut, sibling, tree, internalAttributes);
+ }
+ }
+
+ return tree;
+
+ }
+
+ /** Chop off parts of the tree.
+ * <p>The instance is modified in place, all the parts that are on
+ * the minus side of the chopping hyperplane are discarded, only the
+ * parts on the plus side remain.</p>
+ * @param hyperplane chopping hyperplane
+ * @param vanishingHandler handler to use for handling very rare corner
+ * cases of vanishing cut sub-hyperplanes in internal nodes during merging
+ */
+ private void chopOffMinus(final Hyperplane<S> hyperplane, final VanishingCutHandler<S> vanishingHandler) {
+ if (cut != null) {
+
+ cut = cut.split(hyperplane).getPlus();
+ plus.chopOffMinus(hyperplane, vanishingHandler);
+ minus.chopOffMinus(hyperplane, vanishingHandler);
+
+ if (cut == null) {
+ // the cut sub-hyperplane has vanished
+ final BSPTree<S> fixed = vanishingHandler.fixNode(this);
+ cut = fixed.cut;
+ plus = fixed.plus;
+ minus = fixed.minus;
+ attribute = fixed.attribute;
+ }
+
+ }
+ }
+
+ /** Chop off parts of the tree.
+ * <p>The instance is modified in place, all the parts that are on
+ * the plus side of the chopping hyperplane are discarded, only the
+ * parts on the minus side remain.</p>
+ * @param hyperplane chopping hyperplane
+ * @param vanishingHandler handler to use for handling very rare corner
+ * cases of vanishing cut sub-hyperplanes in internal nodes during merging
+ */
+ private void chopOffPlus(final Hyperplane<S> hyperplane, final VanishingCutHandler<S> vanishingHandler) {
+ if (cut != null) {
+
+ cut = cut.split(hyperplane).getMinus();
+ plus.chopOffPlus(hyperplane, vanishingHandler);
+ minus.chopOffPlus(hyperplane, vanishingHandler);
+
+ if (cut == null) {
+ // the cut sub-hyperplane has vanished
+ final BSPTree<S> fixed = vanishingHandler.fixNode(this);
+ cut = fixed.cut;
+ plus = fixed.plus;
+ minus = fixed.minus;
+ attribute = fixed.attribute;
+ }
+
+ }
+ }
+
+}
diff --git a/src/main/java/org/apache/commons/math3/geometry/partitioning/BSPTreeVisitor.java b/src/main/java/org/apache/commons/math3/geometry/partitioning/BSPTreeVisitor.java
new file mode 100644
index 0000000..3d09939
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/geometry/partitioning/BSPTreeVisitor.java
@@ -0,0 +1,114 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math3.geometry.partitioning;
+
+import org.apache.commons.math3.geometry.Space;
+
+/** This interface is used to visit {@link BSPTree BSP tree} nodes.
+
+ * <p>Navigation through {@link BSPTree BSP trees} can be done using
+ * two different point of views:</p>
+ * <ul>
+ * <li>
+ * the first one is in a node-oriented way using the {@link
+ * BSPTree#getPlus}, {@link BSPTree#getMinus} and {@link
+ * BSPTree#getParent} methods. Terminal nodes without associated
+ * {@link SubHyperplane sub-hyperplanes} can be visited this way,
+ * there is no constraint in the visit order, and it is possible
+ * to visit either all nodes or only a subset of the nodes
+ * </li>
+ * <li>
+ * the second one is in a sub-hyperplane-oriented way using
+ * classes implementing this interface which obeys the visitor
+ * design pattern. The visit order is provided by the visitor as
+ * each node is first encountered. Each node is visited exactly
+ * once.
+ * </li>
+ * </ul>
+
+ * @param <S> Type of the space.
+
+ * @see BSPTree
+ * @see SubHyperplane
+
+ * @since 3.0
+ */
+public interface BSPTreeVisitor<S extends Space> {
+
+ /** Enumerate for visit order with respect to plus sub-tree, minus sub-tree and cut sub-hyperplane. */
+ enum Order {
+ /** Indicator for visit order plus sub-tree, then minus sub-tree,
+ * and last cut sub-hyperplane.
+ */
+ PLUS_MINUS_SUB,
+
+ /** Indicator for visit order plus sub-tree, then cut sub-hyperplane,
+ * and last minus sub-tree.
+ */
+ PLUS_SUB_MINUS,
+
+ /** Indicator for visit order minus sub-tree, then plus sub-tree,
+ * and last cut sub-hyperplane.
+ */
+ MINUS_PLUS_SUB,
+
+ /** Indicator for visit order minus sub-tree, then cut sub-hyperplane,
+ * and last plus sub-tree.
+ */
+ MINUS_SUB_PLUS,
+
+ /** Indicator for visit order cut sub-hyperplane, then plus sub-tree,
+ * and last minus sub-tree.
+ */
+ SUB_PLUS_MINUS,
+
+ /** Indicator for visit order cut sub-hyperplane, then minus sub-tree,
+ * and last plus sub-tree.
+ */
+ SUB_MINUS_PLUS;
+ }
+
+ /** Determine the visit order for this node.
+ * <p>Before attempting to visit an internal node, this method is
+ * called to determine the desired ordering of the visit. It is
+ * guaranteed that this method will be called before {@link
+ * #visitInternalNode visitInternalNode} for a given node, it will be
+ * called exactly once for each internal node.</p>
+ * @param node BSP node guaranteed to have a non null cut sub-hyperplane
+ * @return desired visit order, must be one of
+ * {@link Order#PLUS_MINUS_SUB}, {@link Order#PLUS_SUB_MINUS},
+ * {@link Order#MINUS_PLUS_SUB}, {@link Order#MINUS_SUB_PLUS},
+ * {@link Order#SUB_PLUS_MINUS}, {@link Order#SUB_MINUS_PLUS}
+ */
+ Order visitOrder(BSPTree<S> node);
+
+ /** Visit a BSP tree node node having a non-null sub-hyperplane.
+ * <p>It is guaranteed that this method will be called after {@link
+ * #visitOrder visitOrder} has been called for a given node,
+ * it wil be called exactly once for each internal node.</p>
+ * @param node BSP node guaranteed to have a non null cut sub-hyperplane
+ * @see #visitLeafNode
+ */
+ void visitInternalNode(BSPTree<S> node);
+
+ /** Visit a leaf BSP tree node node having a null sub-hyperplane.
+ * @param node leaf BSP node having a null sub-hyperplane
+ * @see #visitInternalNode
+ */
+ void visitLeafNode(BSPTree<S> node);
+
+}
diff --git a/src/main/java/org/apache/commons/math3/geometry/partitioning/BoundaryAttribute.java b/src/main/java/org/apache/commons/math3/geometry/partitioning/BoundaryAttribute.java
new file mode 100644
index 0000000..dad884c
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/geometry/partitioning/BoundaryAttribute.java
@@ -0,0 +1,116 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math3.geometry.partitioning;
+
+import org.apache.commons.math3.geometry.Space;
+
+/** Class holding boundary attributes.
+ * <p>This class is used for the attributes associated with the
+ * nodes of region boundary shell trees returned by the {@link
+ * Region#getTree(boolean) Region.getTree(includeBoundaryAttributes)}
+ * when the boolean {@code includeBoundaryAttributes} parameter is
+ * set to {@code true}. It contains the parts of the node cut
+ * sub-hyperplane that belong to the boundary.</p>
+ * <p>This class is a simple placeholder, it does not provide any
+ * processing methods.</p>
+ * @param <S> Type of the space.
+ * @see Region#getTree
+ * @since 3.0
+ */
+public class BoundaryAttribute<S extends Space> {
+
+ /** Part of the node cut sub-hyperplane that belongs to the
+ * boundary and has the outside of the region on the plus side of
+ * its underlying hyperplane (may be null).
+ */
+ private final SubHyperplane<S> plusOutside;
+
+ /** Part of the node cut sub-hyperplane that belongs to the
+ * boundary and has the inside of the region on the plus side of
+ * its underlying hyperplane (may be null).
+ */
+ private final SubHyperplane<S> plusInside;
+
+ /** Sub-hyperplanes that were used to split the boundary part. */
+ private final NodesSet<S> splitters;
+
+ /** Simple constructor.
+ * @param plusOutside part of the node cut sub-hyperplane that
+ * belongs to the boundary and has the outside of the region on
+ * the plus side of its underlying hyperplane (may be null)
+ * @param plusInside part of the node cut sub-hyperplane that
+ * belongs to the boundary and has the inside of the region on the
+ * plus side of its underlying hyperplane (may be null)
+ * @deprecated as of 3.4, the constructor has been replaced by a new one
+ * which is not public anymore, as it is intended to be used only by
+ * {@link BoundaryBuilder}
+ */
+ @Deprecated
+ public BoundaryAttribute(final SubHyperplane<S> plusOutside,
+ final SubHyperplane<S> plusInside) {
+ this(plusOutside, plusInside, null);
+ }
+
+ /** Simple constructor.
+ * @param plusOutside part of the node cut sub-hyperplane that
+ * belongs to the boundary and has the outside of the region on
+ * the plus side of its underlying hyperplane (may be null)
+ * @param plusInside part of the node cut sub-hyperplane that
+ * belongs to the boundary and has the inside of the region on the
+ * plus side of its underlying hyperplane (may be null)
+ * @param splitters sub-hyperplanes that were used to
+ * split the boundary part (may be null)
+ * @since 3.4
+ */
+ BoundaryAttribute(final SubHyperplane<S> plusOutside,
+ final SubHyperplane<S> plusInside,
+ final NodesSet<S> splitters) {
+ this.plusOutside = plusOutside;
+ this.plusInside = plusInside;
+ this.splitters = splitters;
+ }
+
+ /** Get the part of the node cut sub-hyperplane that belongs to the
+ * boundary and has the outside of the region on the plus side of
+ * its underlying hyperplane.
+ * @return part of the node cut sub-hyperplane that belongs to the
+ * boundary and has the outside of the region on the plus side of
+ * its underlying hyperplane
+ */
+ public SubHyperplane<S> getPlusOutside() {
+ return plusOutside;
+ }
+
+ /** Get the part of the node cut sub-hyperplane that belongs to the
+ * boundary and has the inside of the region on the plus side of
+ * its underlying hyperplane.
+ * @return part of the node cut sub-hyperplane that belongs to the
+ * boundary and has the inside of the region on the plus side of
+ * its underlying hyperplane
+ */
+ public SubHyperplane<S> getPlusInside() {
+ return plusInside;
+ }
+
+ /** Get the sub-hyperplanes that were used to split the boundary part.
+ * @return sub-hyperplanes that were used to split the boundary part
+ */
+ public NodesSet<S> getSplitters() {
+ return splitters;
+ }
+
+}
diff --git a/src/main/java/org/apache/commons/math3/geometry/partitioning/BoundaryBuilder.java b/src/main/java/org/apache/commons/math3/geometry/partitioning/BoundaryBuilder.java
new file mode 100644
index 0000000..cea4de3
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/geometry/partitioning/BoundaryBuilder.java
@@ -0,0 +1,95 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math3.geometry.partitioning;
+
+import org.apache.commons.math3.geometry.Space;
+
+/** Visitor building boundary shell tree.
+ * <p>
+ * The boundary shell is represented as {@link BoundaryAttribute boundary attributes}
+ * at each internal node.
+ * </p>
+ * @param <S> Type of the space.
+ * @since 3.4
+ */
+class BoundaryBuilder<S extends Space> implements BSPTreeVisitor<S> {
+
+ /** {@inheritDoc} */
+ public Order visitOrder(BSPTree<S> node) {
+ return Order.PLUS_MINUS_SUB;
+ }
+
+ /** {@inheritDoc} */
+ public void visitInternalNode(BSPTree<S> node) {
+
+ SubHyperplane<S> plusOutside = null;
+ SubHyperplane<S> plusInside = null;
+ NodesSet<S> splitters = null;
+
+ // characterize the cut sub-hyperplane,
+ // first with respect to the plus sub-tree
+ final Characterization<S> plusChar = new Characterization<S>(node.getPlus(), node.getCut().copySelf());
+
+ if (plusChar.touchOutside()) {
+ // plusChar.outsideTouching() corresponds to a subset of the cut sub-hyperplane
+ // known to have outside cells on its plus side, we want to check if parts
+ // of this subset do have inside cells on their minus side
+ final Characterization<S> minusChar = new Characterization<S>(node.getMinus(), plusChar.outsideTouching());
+ if (minusChar.touchInside()) {
+ // this part belongs to the boundary,
+ // it has the outside on its plus side and the inside on its minus side
+ plusOutside = minusChar.insideTouching();
+ splitters = new NodesSet<S>();
+ splitters.addAll(minusChar.getInsideSplitters());
+ splitters.addAll(plusChar.getOutsideSplitters());
+ }
+ }
+
+ if (plusChar.touchInside()) {
+ // plusChar.insideTouching() corresponds to a subset of the cut sub-hyperplane
+ // known to have inside cells on its plus side, we want to check if parts
+ // of this subset do have outside cells on their minus side
+ final Characterization<S> minusChar = new Characterization<S>(node.getMinus(), plusChar.insideTouching());
+ if (minusChar.touchOutside()) {
+ // this part belongs to the boundary,
+ // it has the inside on its plus side and the outside on its minus side
+ plusInside = minusChar.outsideTouching();
+ if (splitters == null) {
+ splitters = new NodesSet<S>();
+ }
+ splitters.addAll(minusChar.getOutsideSplitters());
+ splitters.addAll(plusChar.getInsideSplitters());
+ }
+ }
+
+ if (splitters != null) {
+ // the parent nodes are natural splitters for boundary sub-hyperplanes
+ for (BSPTree<S> up = node.getParent(); up != null; up = up.getParent()) {
+ splitters.add(up);
+ }
+ }
+
+ // set the boundary attribute at non-leaf nodes
+ node.setAttribute(new BoundaryAttribute<S>(plusOutside, plusInside, splitters));
+
+ }
+
+ /** {@inheritDoc} */
+ public void visitLeafNode(BSPTree<S> node) {
+ }
+
+}
diff --git a/src/main/java/org/apache/commons/math3/geometry/partitioning/BoundaryProjection.java b/src/main/java/org/apache/commons/math3/geometry/partitioning/BoundaryProjection.java
new file mode 100644
index 0000000..03526e4
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/geometry/partitioning/BoundaryProjection.java
@@ -0,0 +1,83 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math3.geometry.partitioning;
+
+import org.apache.commons.math3.geometry.Point;
+import org.apache.commons.math3.geometry.Space;
+
+/** Class holding the result of point projection on region boundary.
+ * <p>This class is a simple placeholder, it does not provide any
+ * processing methods.</p>
+ * <p>Instances of this class are guaranteed to be immutable</p>
+ * @param <S> Type of the space.
+ * @since 3.3
+ * @see AbstractRegion#projectToBoundary(Point)
+ */
+public class BoundaryProjection<S extends Space> {
+
+ /** Original point. */
+ private final Point<S> original;
+
+ /** Projected point. */
+ private final Point<S> projected;
+
+ /** Offset of the point with respect to the boundary it is projected on. */
+ private final double offset;
+
+ /** Constructor from raw elements.
+ * @param original original point
+ * @param projected projected point
+ * @param offset offset of the point with respect to the boundary it is projected on
+ */
+ public BoundaryProjection(final Point<S> original, final Point<S> projected, final double offset) {
+ this.original = original;
+ this.projected = projected;
+ this.offset = offset;
+ }
+
+ /** Get the original point.
+ * @return original point
+ */
+ public Point<S> getOriginal() {
+ return original;
+ }
+
+ /** Projected point.
+ * @return projected point, or null if there are no boundary
+ */
+ public Point<S> getProjected() {
+ return projected;
+ }
+
+ /** Offset of the point with respect to the boundary it is projected on.
+ * <p>
+ * The offset with respect to the boundary is negative if the {@link
+ * #getOriginal() original point} is inside the region, and positive otherwise.
+ * </p>
+ * <p>
+ * If there are no boundary, the value is set to either {@code
+ * Double.POSITIVE_INFINITY} if the region is empty (i.e. all points are
+ * outside of the region) or {@code Double.NEGATIVE_INFINITY} if the region
+ * covers the whole space (i.e. all points are inside of the region).
+ * </p>
+ * @return offset of the point with respect to the boundary it is projected on
+ */
+ public double getOffset() {
+ return offset;
+ }
+
+}
diff --git a/src/main/java/org/apache/commons/math3/geometry/partitioning/BoundaryProjector.java b/src/main/java/org/apache/commons/math3/geometry/partitioning/BoundaryProjector.java
new file mode 100644
index 0000000..486bbf1
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/geometry/partitioning/BoundaryProjector.java
@@ -0,0 +1,200 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math3.geometry.partitioning;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.apache.commons.math3.geometry.Point;
+import org.apache.commons.math3.geometry.Space;
+import org.apache.commons.math3.geometry.partitioning.Region.Location;
+import org.apache.commons.math3.util.FastMath;
+
+/** Local tree visitor to compute projection on boundary.
+ * @param <S> Type of the space.
+ * @param <T> Type of the sub-space.
+ * @since 3.3
+ */
+class BoundaryProjector<S extends Space, T extends Space> implements BSPTreeVisitor<S> {
+
+ /** Original point. */
+ private final Point<S> original;
+
+ /** Current best projected point. */
+ private Point<S> projected;
+
+ /** Leaf node closest to the test point. */
+ private BSPTree<S> leaf;
+
+ /** Current offset. */
+ private double offset;
+
+ /** Simple constructor.
+ * @param original original point
+ */
+ BoundaryProjector(final Point<S> original) {
+ this.original = original;
+ this.projected = null;
+ this.leaf = null;
+ this.offset = Double.POSITIVE_INFINITY;
+ }
+
+ /** {@inheritDoc} */
+ public Order visitOrder(final BSPTree<S> node) {
+ // we want to visit the tree so that the first encountered
+ // leaf is the one closest to the test point
+ if (node.getCut().getHyperplane().getOffset(original) <= 0) {
+ return Order.MINUS_SUB_PLUS;
+ } else {
+ return Order.PLUS_SUB_MINUS;
+ }
+ }
+
+ /** {@inheritDoc} */
+ public void visitInternalNode(final BSPTree<S> node) {
+
+ // project the point on the cut sub-hyperplane
+ final Hyperplane<S> hyperplane = node.getCut().getHyperplane();
+ final double signedOffset = hyperplane.getOffset(original);
+ if (FastMath.abs(signedOffset) < offset) {
+
+ // project point
+ final Point<S> regular = hyperplane.project(original);
+
+ // get boundary parts
+ final List<Region<T>> boundaryParts = boundaryRegions(node);
+
+ // check if regular projection really belongs to the boundary
+ boolean regularFound = false;
+ for (final Region<T> part : boundaryParts) {
+ if (!regularFound && belongsToPart(regular, hyperplane, part)) {
+ // the projected point lies in the boundary
+ projected = regular;
+ offset = FastMath.abs(signedOffset);
+ regularFound = true;
+ }
+ }
+
+ if (!regularFound) {
+ // the regular projected point is not on boundary,
+ // so we have to check further if a singular point
+ // (i.e. a vertex in 2D case) is a possible projection
+ for (final Region<T> part : boundaryParts) {
+ final Point<S> spI = singularProjection(regular, hyperplane, part);
+ if (spI != null) {
+ final double distance = original.distance(spI);
+ if (distance < offset) {
+ projected = spI;
+ offset = distance;
+ }
+ }
+ }
+
+ }
+
+ }
+
+ }
+
+ /** {@inheritDoc} */
+ public void visitLeafNode(final BSPTree<S> node) {
+ if (leaf == null) {
+ // this is the first leaf we visit,
+ // it is the closest one to the original point
+ leaf = node;
+ }
+ }
+
+ /** Get the projection.
+ * @return projection
+ */
+ public BoundaryProjection<S> getProjection() {
+
+ // fix offset sign
+ offset = FastMath.copySign(offset, (Boolean) leaf.getAttribute() ? -1 : +1);
+
+ return new BoundaryProjection<S>(original, projected, offset);
+
+ }
+
+ /** Extract the regions of the boundary on an internal node.
+ * @param node internal node
+ * @return regions in the node sub-hyperplane
+ */
+ private List<Region<T>> boundaryRegions(final BSPTree<S> node) {
+
+ final List<Region<T>> regions = new ArrayList<Region<T>>(2);
+
+ @SuppressWarnings("unchecked")
+ final BoundaryAttribute<S> ba = (BoundaryAttribute<S>) node.getAttribute();
+ addRegion(ba.getPlusInside(), regions);
+ addRegion(ba.getPlusOutside(), regions);
+
+ return regions;
+
+ }
+
+ /** Add a boundary region to a list.
+ * @param sub sub-hyperplane defining the region
+ * @param list to fill up
+ */
+ private void addRegion(final SubHyperplane<S> sub, final List<Region<T>> list) {
+ if (sub != null) {
+ @SuppressWarnings("unchecked")
+ final Region<T> region = ((AbstractSubHyperplane<S, T>) sub).getRemainingRegion();
+ if (region != null) {
+ list.add(region);
+ }
+ }
+ }
+
+ /** Check if a projected point lies on a boundary part.
+ * @param point projected point to check
+ * @param hyperplane hyperplane into which the point was projected
+ * @param part boundary part
+ * @return true if point lies on the boundary part
+ */
+ private boolean belongsToPart(final Point<S> point, final Hyperplane<S> hyperplane,
+ final Region<T> part) {
+
+ // there is a non-null sub-space, we can dive into smaller dimensions
+ @SuppressWarnings("unchecked")
+ final Embedding<S, T> embedding = (Embedding<S, T>) hyperplane;
+ return part.checkPoint(embedding.toSubSpace(point)) != Location.OUTSIDE;
+
+ }
+
+ /** Get the projection to the closest boundary singular point.
+ * @param point projected point to check
+ * @param hyperplane hyperplane into which the point was projected
+ * @param part boundary part
+ * @return projection to a singular point of boundary part (may be null)
+ */
+ private Point<S> singularProjection(final Point<S> point, final Hyperplane<S> hyperplane,
+ final Region<T> part) {
+
+ // there is a non-null sub-space, we can dive into smaller dimensions
+ @SuppressWarnings("unchecked")
+ final Embedding<S, T> embedding = (Embedding<S, T>) hyperplane;
+ final BoundaryProjection<T> bp = part.projectToBoundary(embedding.toSubSpace(point));
+
+ // back to initial dimension
+ return (bp.getProjected() == null) ? null : embedding.toSpace(bp.getProjected());
+
+ }
+
+}
diff --git a/src/main/java/org/apache/commons/math3/geometry/partitioning/BoundarySizeVisitor.java b/src/main/java/org/apache/commons/math3/geometry/partitioning/BoundarySizeVisitor.java
new file mode 100644
index 0000000..054838b
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/geometry/partitioning/BoundarySizeVisitor.java
@@ -0,0 +1,65 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math3.geometry.partitioning;
+
+import org.apache.commons.math3.geometry.Space;
+
+/** Visitor computing the boundary size.
+ * @param <S> Type of the space.
+ * @since 3.0
+ */
+class BoundarySizeVisitor<S extends Space> implements BSPTreeVisitor<S> {
+
+ /** Size of the boundary. */
+ private double boundarySize;
+
+ /** Simple constructor.
+ */
+ BoundarySizeVisitor() {
+ boundarySize = 0;
+ }
+
+ /** {@inheritDoc}*/
+ public Order visitOrder(final BSPTree<S> node) {
+ return Order.MINUS_SUB_PLUS;
+ }
+
+ /** {@inheritDoc}*/
+ public void visitInternalNode(final BSPTree<S> node) {
+ @SuppressWarnings("unchecked")
+ final BoundaryAttribute<S> attribute =
+ (BoundaryAttribute<S>) node.getAttribute();
+ if (attribute.getPlusOutside() != null) {
+ boundarySize += attribute.getPlusOutside().getSize();
+ }
+ if (attribute.getPlusInside() != null) {
+ boundarySize += attribute.getPlusInside().getSize();
+ }
+ }
+
+ /** {@inheritDoc}*/
+ public void visitLeafNode(final BSPTree<S> node) {
+ }
+
+ /** Get the size of the boundary.
+ * @return size of the boundary
+ */
+ public double getSize() {
+ return boundarySize;
+ }
+
+}
diff --git a/src/main/java/org/apache/commons/math3/geometry/partitioning/Characterization.java b/src/main/java/org/apache/commons/math3/geometry/partitioning/Characterization.java
new file mode 100644
index 0000000..f8ec2f9
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/geometry/partitioning/Characterization.java
@@ -0,0 +1,190 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math3.geometry.partitioning;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.apache.commons.math3.exception.MathInternalError;
+import org.apache.commons.math3.geometry.Space;
+
+/** Cut sub-hyperplanes characterization with respect to inside/outside cells.
+ * @see BoundaryBuilder
+ * @param <S> Type of the space.
+ * @since 3.4
+ */
+class Characterization<S extends Space> {
+
+ /** Part of the cut sub-hyperplane that touch outside cells. */
+ private SubHyperplane<S> outsideTouching;
+
+ /** Part of the cut sub-hyperplane that touch inside cells. */
+ private SubHyperplane<S> insideTouching;
+
+ /** Nodes that were used to split the outside touching part. */
+ private final NodesSet<S> outsideSplitters;
+
+ /** Nodes that were used to split the outside touching part. */
+ private final NodesSet<S> insideSplitters;
+
+ /** Simple constructor.
+ * <p>Characterization consists in splitting the specified
+ * sub-hyperplane into several parts lying in inside and outside
+ * cells of the tree. The principle is to compute characterization
+ * twice for each cut sub-hyperplane in the tree, once on the plus
+ * node and once on the minus node. The parts that have the same flag
+ * (inside/inside or outside/outside) do not belong to the boundary
+ * while parts that have different flags (inside/outside or
+ * outside/inside) do belong to the boundary.</p>
+ * @param node current BSP tree node
+ * @param sub sub-hyperplane to characterize
+ */
+ Characterization(final BSPTree<S> node, final SubHyperplane<S> sub) {
+ outsideTouching = null;
+ insideTouching = null;
+ outsideSplitters = new NodesSet<S>();
+ insideSplitters = new NodesSet<S>();
+ characterize(node, sub, new ArrayList<BSPTree<S>>());
+ }
+
+ /** Filter the parts of an hyperplane belonging to the boundary.
+ * <p>The filtering consist in splitting the specified
+ * sub-hyperplane into several parts lying in inside and outside
+ * cells of the tree. The principle is to call this method twice for
+ * each cut sub-hyperplane in the tree, once on the plus node and
+ * once on the minus node. The parts that have the same flag
+ * (inside/inside or outside/outside) do not belong to the boundary
+ * while parts that have different flags (inside/outside or
+ * outside/inside) do belong to the boundary.</p>
+ * @param node current BSP tree node
+ * @param sub sub-hyperplane to characterize
+ * @param splitters nodes that did split the current one
+ */
+ private void characterize(final BSPTree<S> node, final SubHyperplane<S> sub,
+ final List<BSPTree<S>> splitters) {
+ if (node.getCut() == null) {
+ // we have reached a leaf node
+ final boolean inside = (Boolean) node.getAttribute();
+ if (inside) {
+ addInsideTouching(sub, splitters);
+ } else {
+ addOutsideTouching(sub, splitters);
+ }
+ } else {
+ final Hyperplane<S> hyperplane = node.getCut().getHyperplane();
+ final SubHyperplane.SplitSubHyperplane<S> split = sub.split(hyperplane);
+ switch (split.getSide()) {
+ case PLUS:
+ characterize(node.getPlus(), sub, splitters);
+ break;
+ case MINUS:
+ characterize(node.getMinus(), sub, splitters);
+ break;
+ case BOTH:
+ splitters.add(node);
+ characterize(node.getPlus(), split.getPlus(), splitters);
+ characterize(node.getMinus(), split.getMinus(), splitters);
+ splitters.remove(splitters.size() - 1);
+ break;
+ default:
+ // this should not happen
+ throw new MathInternalError();
+ }
+ }
+ }
+
+ /** Add a part of the cut sub-hyperplane known to touch an outside cell.
+ * @param sub part of the cut sub-hyperplane known to touch an outside cell
+ * @param splitters sub-hyperplanes that did split the current one
+ */
+ private void addOutsideTouching(final SubHyperplane<S> sub,
+ final List<BSPTree<S>> splitters) {
+ if (outsideTouching == null) {
+ outsideTouching = sub;
+ } else {
+ outsideTouching = outsideTouching.reunite(sub);
+ }
+ outsideSplitters.addAll(splitters);
+ }
+
+ /** Add a part of the cut sub-hyperplane known to touch an inside cell.
+ * @param sub part of the cut sub-hyperplane known to touch an inside cell
+ * @param splitters sub-hyperplanes that did split the current one
+ */
+ private void addInsideTouching(final SubHyperplane<S> sub,
+ final List<BSPTree<S>> splitters) {
+ if (insideTouching == null) {
+ insideTouching = sub;
+ } else {
+ insideTouching = insideTouching.reunite(sub);
+ }
+ insideSplitters.addAll(splitters);
+ }
+
+ /** Check if the cut sub-hyperplane touches outside cells.
+ * @return true if the cut sub-hyperplane touches outside cells
+ */
+ public boolean touchOutside() {
+ return outsideTouching != null && !outsideTouching.isEmpty();
+ }
+
+ /** Get all the parts of the cut sub-hyperplane known to touch outside cells.
+ * @return parts of the cut sub-hyperplane known to touch outside cells
+ * (may be null or empty)
+ */
+ public SubHyperplane<S> outsideTouching() {
+ return outsideTouching;
+ }
+
+ /** Get the nodes that were used to split the outside touching part.
+ * <p>
+ * Splitting nodes are internal nodes (i.e. they have a non-null
+ * cut sub-hyperplane).
+ * </p>
+ * @return nodes that were used to split the outside touching part
+ */
+ public NodesSet<S> getOutsideSplitters() {
+ return outsideSplitters;
+ }
+
+ /** Check if the cut sub-hyperplane touches inside cells.
+ * @return true if the cut sub-hyperplane touches inside cells
+ */
+ public boolean touchInside() {
+ return insideTouching != null && !insideTouching.isEmpty();
+ }
+
+ /** Get all the parts of the cut sub-hyperplane known to touch inside cells.
+ * @return parts of the cut sub-hyperplane known to touch inside cells
+ * (may be null or empty)
+ */
+ public SubHyperplane<S> insideTouching() {
+ return insideTouching;
+ }
+
+ /** Get the nodes that were used to split the inside touching part.
+ * <p>
+ * Splitting nodes are internal nodes (i.e. they have a non-null
+ * cut sub-hyperplane).
+ * </p>
+ * @return nodes that were used to split the inside touching part
+ */
+ public NodesSet<S> getInsideSplitters() {
+ return insideSplitters;
+ }
+
+}
diff --git a/src/main/java/org/apache/commons/math3/geometry/partitioning/Embedding.java b/src/main/java/org/apache/commons/math3/geometry/partitioning/Embedding.java
new file mode 100644
index 0000000..74e2c00
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/geometry/partitioning/Embedding.java
@@ -0,0 +1,68 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math3.geometry.partitioning;
+
+import org.apache.commons.math3.geometry.Point;
+import org.apache.commons.math3.geometry.Space;
+
+/** This interface defines mappers between a space and one of its sub-spaces.
+
+ * <p>Sub-spaces are the lower dimensions subsets of a n-dimensions
+ * space. The (n-1)-dimension sub-spaces are specific sub-spaces known
+ * as {@link Hyperplane hyperplanes}. This interface can be used regardless
+ * of the dimensions differences. As an example, {@link
+ * org.apache.commons.math3.geometry.euclidean.threed.Line Line} in 3D
+ * implements Embedding<{@link
+ * org.apache.commons.math3.geometry.euclidean.threed.Vector3D Vector3D}, {link
+ * org.apache.commons.math3.geometry.euclidean.oned.Vector1D Vector1D>, i.e. it
+ * maps directly dimensions 3 and 1.</p>
+
+ * <p>In the 3D euclidean space, hyperplanes are 2D planes, and the 1D
+ * sub-spaces are lines.</p>
+
+ * <p>
+ * Note that this interface is <em>not</em> intended to be implemented
+ * by Apache Commons Math users, it is only intended to be implemented
+ * within the library itself. New methods may be added even for minor
+ * versions, which breaks compatibility for external implementations.
+ * </p>
+
+ * @param <S> Type of the embedding space.
+ * @param <T> Type of the embedded sub-space.
+
+ * @see Hyperplane
+ * @since 3.0
+ */
+public interface Embedding<S extends Space, T extends Space> {
+
+ /** Transform a space point into a sub-space point.
+ * @param point n-dimension point of the space
+ * @return (n-1)-dimension point of the sub-space corresponding to
+ * the specified space point
+ * @see #toSpace
+ */
+ Point<T> toSubSpace(Point<S> point);
+
+ /** Transform a sub-space point into a space point.
+ * @param point (n-1)-dimension point of the sub-space
+ * @return n-dimension point of the space corresponding to the
+ * specified sub-space point
+ * @see #toSubSpace
+ */
+ Point<S> toSpace(Point<T> point);
+
+}
diff --git a/src/main/java/org/apache/commons/math3/geometry/partitioning/Hyperplane.java b/src/main/java/org/apache/commons/math3/geometry/partitioning/Hyperplane.java
new file mode 100644
index 0000000..f90c3bc
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/geometry/partitioning/Hyperplane.java
@@ -0,0 +1,98 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math3.geometry.partitioning;
+
+import org.apache.commons.math3.geometry.Point;
+import org.apache.commons.math3.geometry.Space;
+
+/** This interface represents an hyperplane of a space.
+
+ * <p>The most prominent place where hyperplane appears in space
+ * partitioning is as cutters. Each partitioning node in a {@link
+ * BSPTree BSP tree} has a cut {@link SubHyperplane sub-hyperplane}
+ * which is either an hyperplane or a part of an hyperplane. In an
+ * n-dimensions euclidean space, an hyperplane is an (n-1)-dimensions
+ * hyperplane (for example a traditional plane in the 3D euclidean
+ * space). They can be more exotic objects in specific fields, for
+ * example a circle on the surface of the unit sphere.</p>
+
+ * <p>
+ * Note that this interface is <em>not</em> intended to be implemented
+ * by Apache Commons Math users, it is only intended to be implemented
+ * within the library itself. New methods may be added even for minor
+ * versions, which breaks compatibility for external implementations.
+ * </p>
+
+ * @param <S> Type of the space.
+
+ * @since 3.0
+ */
+public interface Hyperplane<S extends Space> {
+
+ /** Copy the instance.
+ * <p>The instance created is completely independant of the original
+ * one. A deep copy is used, none of the underlying objects are
+ * shared (except for immutable objects).</p>
+ * @return a new hyperplane, copy of the instance
+ */
+ Hyperplane<S> copySelf();
+
+ /** Get the offset (oriented distance) of a point.
+ * <p>The offset is 0 if the point is on the underlying hyperplane,
+ * it is positive if the point is on one particular side of the
+ * hyperplane, and it is negative if the point is on the other side,
+ * according to the hyperplane natural orientation.</p>
+ * @param point point to check
+ * @return offset of the point
+ */
+ double getOffset(Point<S> point);
+
+ /** Project a point to the hyperplane.
+ * @param point point to project
+ * @return projected point
+ * @since 3.3
+ */
+ Point<S> project(Point<S> point);
+
+ /** Get the tolerance below which points are considered to belong to the hyperplane.
+ * @return tolerance below which points are considered to belong to the hyperplane
+ * @since 3.3
+ */
+ double getTolerance();
+
+ /** Check if the instance has the same orientation as another hyperplane.
+ * <p>This method is expected to be called on parallel hyperplanes. The
+ * method should <em>not</em> re-check for parallelism, only for
+ * orientation, typically by testing something like the sign of the
+ * dot-products of normals.</p>
+ * @param other other hyperplane to check against the instance
+ * @return true if the instance and the other hyperplane have
+ * the same orientation
+ */
+ boolean sameOrientationAs(Hyperplane<S> other);
+
+ /** Build a sub-hyperplane covering the whole hyperplane.
+ * @return a sub-hyperplane covering the whole hyperplane
+ */
+ SubHyperplane<S> wholeHyperplane();
+
+ /** Build a region covering the whole space.
+ * @return a region containing the instance
+ */
+ Region<S> wholeSpace();
+
+}
diff --git a/src/main/java/org/apache/commons/math3/geometry/partitioning/InsideFinder.java b/src/main/java/org/apache/commons/math3/geometry/partitioning/InsideFinder.java
new file mode 100644
index 0000000..b1db90a
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/geometry/partitioning/InsideFinder.java
@@ -0,0 +1,150 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math3.geometry.partitioning;
+
+import org.apache.commons.math3.geometry.Space;
+
+/** Utility class checking if inside nodes can be found
+ * on the plus and minus sides of an hyperplane.
+ * @param <S> Type of the space.
+ * @since 3.4
+ */
+class InsideFinder<S extends Space> {
+
+ /** Region on which to operate. */
+ private final Region<S> region;
+
+ /** Indicator of inside leaf nodes found on the plus side. */
+ private boolean plusFound;
+
+ /** Indicator of inside leaf nodes found on the plus side. */
+ private boolean minusFound;
+
+ /** Simple constructor.
+ * @param region region on which to operate
+ */
+ InsideFinder(final Region<S> region) {
+ this.region = region;
+ plusFound = false;
+ minusFound = false;
+ }
+
+ /** Search recursively for inside leaf nodes on each side of the given hyperplane.
+
+ * <p>The algorithm used here is directly derived from the one
+ * described in section III (<i>Binary Partitioning of a BSP
+ * Tree</i>) of the Bruce Naylor, John Amanatides and William
+ * Thibault paper <a
+ * href="http://www.cs.yorku.ca/~amana/research/bsptSetOp.pdf">Merging
+ * BSP Trees Yields Polyhedral Set Operations</a> Proc. Siggraph
+ * '90, Computer Graphics 24(4), August 1990, pp 115-124, published
+ * by the Association for Computing Machinery (ACM)..</p>
+
+ * @param node current BSP tree node
+ * @param sub sub-hyperplane
+ */
+ public void recurseSides(final BSPTree<S> node, final SubHyperplane<S> sub) {
+
+ if (node.getCut() == null) {
+ if ((Boolean) node.getAttribute()) {
+ // this is an inside cell expanding across the hyperplane
+ plusFound = true;
+ minusFound = true;
+ }
+ return;
+ }
+
+ final Hyperplane<S> hyperplane = node.getCut().getHyperplane();
+ final SubHyperplane.SplitSubHyperplane<S> split = sub.split(hyperplane);
+ switch (split.getSide()) {
+ case PLUS :
+ // the sub-hyperplane is entirely in the plus sub-tree
+ if (node.getCut().split(sub.getHyperplane()).getSide() == Side.PLUS) {
+ if (!region.isEmpty(node.getMinus())) {
+ plusFound = true;
+ }
+ } else {
+ if (!region.isEmpty(node.getMinus())) {
+ minusFound = true;
+ }
+ }
+ if (!(plusFound && minusFound)) {
+ recurseSides(node.getPlus(), sub);
+ }
+ break;
+ case MINUS :
+ // the sub-hyperplane is entirely in the minus sub-tree
+ if (node.getCut().split(sub.getHyperplane()).getSide() == Side.PLUS) {
+ if (!region.isEmpty(node.getPlus())) {
+ plusFound = true;
+ }
+ } else {
+ if (!region.isEmpty(node.getPlus())) {
+ minusFound = true;
+ }
+ }
+ if (!(plusFound && minusFound)) {
+ recurseSides(node.getMinus(), sub);
+ }
+ break;
+ case BOTH :
+ // the sub-hyperplane extends in both sub-trees
+
+ // explore first the plus sub-tree
+ recurseSides(node.getPlus(), split.getPlus());
+
+ // if needed, explore the minus sub-tree
+ if (!(plusFound && minusFound)) {
+ recurseSides(node.getMinus(), split.getMinus());
+ }
+ break;
+ default :
+ // the sub-hyperplane and the cut sub-hyperplane share the same hyperplane
+ if (node.getCut().getHyperplane().sameOrientationAs(sub.getHyperplane())) {
+ if ((node.getPlus().getCut() != null) || ((Boolean) node.getPlus().getAttribute())) {
+ plusFound = true;
+ }
+ if ((node.getMinus().getCut() != null) || ((Boolean) node.getMinus().getAttribute())) {
+ minusFound = true;
+ }
+ } else {
+ if ((node.getPlus().getCut() != null) || ((Boolean) node.getPlus().getAttribute())) {
+ minusFound = true;
+ }
+ if ((node.getMinus().getCut() != null) || ((Boolean) node.getMinus().getAttribute())) {
+ plusFound = true;
+ }
+ }
+ }
+
+ }
+
+ /** Check if inside leaf nodes have been found on the plus side.
+ * @return true if inside leaf nodes have been found on the plus side
+ */
+ public boolean plusFound() {
+ return plusFound;
+ }
+
+ /** Check if inside leaf nodes have been found on the minus side.
+ * @return true if inside leaf nodes have been found on the minus side
+ */
+ public boolean minusFound() {
+ return minusFound;
+ }
+
+}
diff --git a/src/main/java/org/apache/commons/math3/geometry/partitioning/NodesSet.java b/src/main/java/org/apache/commons/math3/geometry/partitioning/NodesSet.java
new file mode 100644
index 0000000..688279a
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/geometry/partitioning/NodesSet.java
@@ -0,0 +1,72 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math3.geometry.partitioning;
+
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+
+import org.apache.commons.math3.geometry.Space;
+
+/** Set of {@link BSPTree BSP tree} nodes.
+ * @see BoundaryAttribute
+ * @param <S> Type of the space.
+ * @since 3.4
+ */
+public class NodesSet<S extends Space> implements Iterable<BSPTree<S>> {
+
+ /** List of sub-hyperplanes. */
+ private List<BSPTree<S>> list;
+
+ /** Simple constructor.
+ */
+ public NodesSet() {
+ list = new ArrayList<BSPTree<S>>();
+ }
+
+ /** Add a node if not already known.
+ * @param node node to add
+ */
+ public void add(final BSPTree<S> node) {
+
+ for (final BSPTree<S> existing : list) {
+ if (node == existing) {
+ // the node is already known, don't add it
+ return;
+ }
+ }
+
+ // the node was not known, add it
+ list.add(node);
+
+ }
+
+ /** Add nodes if they are not already known.
+ * @param iterator nodes iterator
+ */
+ public void addAll(final Iterable<BSPTree<S>> iterator) {
+ for (final BSPTree<S> node : iterator) {
+ add(node);
+ }
+ }
+
+ /** {@inheritDoc} */
+ public Iterator<BSPTree<S>> iterator() {
+ return list.iterator();
+ }
+
+}
diff --git a/src/main/java/org/apache/commons/math3/geometry/partitioning/Region.java b/src/main/java/org/apache/commons/math3/geometry/partitioning/Region.java
new file mode 100644
index 0000000..9ff3946
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/geometry/partitioning/Region.java
@@ -0,0 +1,221 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math3.geometry.partitioning;
+
+import org.apache.commons.math3.geometry.Space;
+import org.apache.commons.math3.geometry.Point;
+
+/** This interface represents a region of a space as a partition.
+
+ * <p>Region are subsets of a space, they can be infinite (whole
+ * space, half space, infinite stripe ...) or finite (polygons in 2D,
+ * polyhedrons in 3D ...). Their main characteristic is to separate
+ * points that are considered to be <em>inside</em> the region from
+ * points considered to be <em>outside</em> of it. In between, there
+ * may be points on the <em>boundary</em> of the region.</p>
+
+ * <p>This implementation is limited to regions for which the boundary
+ * is composed of several {@link SubHyperplane sub-hyperplanes},
+ * including regions with no boundary at all: the whole space and the
+ * empty region. They are not necessarily finite and not necessarily
+ * path-connected. They can contain holes.</p>
+
+ * <p>Regions can be combined using the traditional sets operations :
+ * union, intersection, difference and symetric difference (exclusive
+ * or) for the binary operations, complement for the unary
+ * operation.</p>
+
+ * <p>
+ * Note that this interface is <em>not</em> intended to be implemented
+ * by Apache Commons Math users, it is only intended to be implemented
+ * within the library itself. New methods may be added even for minor
+ * versions, which breaks compatibility for external implementations.
+ * </p>
+
+ * @param <S> Type of the space.
+
+ * @since 3.0
+ */
+public interface Region<S extends Space> {
+
+ /** Enumerate for the location of a point with respect to the region. */
+ enum Location {
+ /** Code for points inside the partition. */
+ INSIDE,
+
+ /** Code for points outside of the partition. */
+ OUTSIDE,
+
+ /** Code for points on the partition boundary. */
+ BOUNDARY;
+ }
+
+ /** Build a region using the instance as a prototype.
+ * <p>This method allow to create new instances without knowing
+ * exactly the type of the region. It is an application of the
+ * prototype design pattern.</p>
+ * <p>The leaf nodes of the BSP tree <em>must</em> have a
+ * {@code Boolean} attribute representing the inside status of
+ * the corresponding cell (true for inside cells, false for outside
+ * cells). In order to avoid building too many small objects, it is
+ * recommended to use the predefined constants
+ * {@code Boolean.TRUE} and {@code Boolean.FALSE}. The
+ * tree also <em>must</em> have either null internal nodes or
+ * internal nodes representing the boundary as specified in the
+ * {@link #getTree getTree} method).</p>
+ * @param newTree inside/outside BSP tree representing the new region
+ * @return the built region
+ */
+ Region<S> buildNew(BSPTree<S> newTree);
+
+ /** Copy the instance.
+ * <p>The instance created is completely independant of the original
+ * one. A deep copy is used, none of the underlying objects are
+ * shared (except for the underlying tree {@code Boolean}
+ * attributes and immutable objects).</p>
+ * @return a new region, copy of the instance
+ */
+ Region<S> copySelf();
+
+ /** Check if the instance is empty.
+ * @return true if the instance is empty
+ */
+ boolean isEmpty();
+
+ /** Check if the sub-tree starting at a given node is empty.
+ * @param node root node of the sub-tree (<em>must</em> have {@link
+ * Region Region} tree semantics, i.e. the leaf nodes must have
+ * {@code Boolean} attributes representing an inside/outside
+ * property)
+ * @return true if the sub-tree starting at the given node is empty
+ */
+ boolean isEmpty(final BSPTree<S> node);
+
+ /** Check if the instance covers the full space.
+ * @return true if the instance covers the full space
+ */
+ boolean isFull();
+
+ /** Check if the sub-tree starting at a given node covers the full space.
+ * @param node root node of the sub-tree (<em>must</em> have {@link
+ * Region Region} tree semantics, i.e. the leaf nodes must have
+ * {@code Boolean} attributes representing an inside/outside
+ * property)
+ * @return true if the sub-tree starting at the given node covers the full space
+ */
+ boolean isFull(final BSPTree<S> node);
+
+ /** Check if the instance entirely contains another region.
+ * @param region region to check against the instance
+ * @return true if the instance contains the specified tree
+ */
+ boolean contains(final Region<S> region);
+
+ /** Check a point with respect to the region.
+ * @param point point to check
+ * @return a code representing the point status: either {@link
+ * Location#INSIDE}, {@link Location#OUTSIDE} or {@link Location#BOUNDARY}
+ */
+ Location checkPoint(final Point<S> point);
+
+ /** Project a point on the boundary of the region.
+ * @param point point to check
+ * @return projection of the point on the boundary
+ * @since 3.3
+ */
+ BoundaryProjection<S> projectToBoundary(final Point<S> point);
+
+ /** Get the underlying BSP tree.
+
+ * <p>Regions are represented by an underlying inside/outside BSP
+ * tree whose leaf attributes are {@code Boolean} instances
+ * representing inside leaf cells if the attribute value is
+ * {@code true} and outside leaf cells if the attribute is
+ * {@code false}. These leaf attributes are always present and
+ * guaranteed to be non null.</p>
+
+ * <p>In addition to the leaf attributes, the internal nodes which
+ * correspond to cells split by cut sub-hyperplanes may contain
+ * {@link BoundaryAttribute BoundaryAttribute} objects representing
+ * the parts of the corresponding cut sub-hyperplane that belong to
+ * the boundary. When the boundary attributes have been computed,
+ * all internal nodes are guaranteed to have non-null
+ * attributes, however some {@link BoundaryAttribute
+ * BoundaryAttribute} instances may have their {@link
+ * BoundaryAttribute#getPlusInside() getPlusInside} and {@link
+ * BoundaryAttribute#getPlusOutside() getPlusOutside} methods both
+ * returning null if the corresponding cut sub-hyperplane does not
+ * have any parts belonging to the boundary.</p>
+
+ * <p>Since computing the boundary is not always required and can be
+ * time-consuming for large trees, these internal nodes attributes
+ * are computed using lazy evaluation only when required by setting
+ * the {@code includeBoundaryAttributes} argument to
+ * {@code true}. Once computed, these attributes remain in the
+ * tree, which implies that in this case, further calls to the
+ * method for the same region will always include these attributes
+ * regardless of the value of the
+ * {@code includeBoundaryAttributes} argument.</p>
+
+ * @param includeBoundaryAttributes if true, the boundary attributes
+ * at internal nodes are guaranteed to be included (they may be
+ * included even if the argument is false, if they have already been
+ * computed due to a previous call)
+ * @return underlying BSP tree
+ * @see BoundaryAttribute
+ */
+ BSPTree<S> getTree(final boolean includeBoundaryAttributes);
+
+ /** Get the size of the boundary.
+ * @return the size of the boundary (this is 0 in 1D, a length in
+ * 2D, an area in 3D ...)
+ */
+ double getBoundarySize();
+
+ /** Get the size of the instance.
+ * @return the size of the instance (this is a length in 1D, an area
+ * in 2D, a volume in 3D ...)
+ */
+ double getSize();
+
+ /** Get the barycenter of the instance.
+ * @return an object representing the barycenter
+ */
+ Point<S> getBarycenter();
+
+ /** Compute the relative position of the instance with respect to an
+ * hyperplane.
+ * @param hyperplane reference hyperplane
+ * @return one of {@link Side#PLUS Side.PLUS}, {@link Side#MINUS
+ * Side.MINUS}, {@link Side#BOTH Side.BOTH} or {@link Side#HYPER
+ * Side.HYPER} (the latter result can occur only if the tree
+ * contains only one cut hyperplane)
+ * @deprecated as of 3.6, this method which was only intended for
+ * internal use is not used anymore
+ */
+ @Deprecated
+ Side side(final Hyperplane<S> hyperplane);
+
+ /** Get the parts of a sub-hyperplane that are contained in the region.
+ * <p>The parts of the sub-hyperplane that belong to the boundary are
+ * <em>not</em> included in the resulting parts.</p>
+ * @param sub sub-hyperplane traversing the region
+ * @return filtered sub-hyperplane
+ */
+ SubHyperplane<S> intersection(final SubHyperplane<S> sub);
+
+}
diff --git a/src/main/java/org/apache/commons/math3/geometry/partitioning/RegionFactory.java b/src/main/java/org/apache/commons/math3/geometry/partitioning/RegionFactory.java
new file mode 100644
index 0000000..688ffde
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/geometry/partitioning/RegionFactory.java
@@ -0,0 +1,378 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math3.geometry.partitioning;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import org.apache.commons.math3.exception.MathIllegalArgumentException;
+import org.apache.commons.math3.exception.util.LocalizedFormats;
+import org.apache.commons.math3.geometry.Point;
+import org.apache.commons.math3.geometry.Space;
+import org.apache.commons.math3.geometry.partitioning.BSPTree.VanishingCutHandler;
+import org.apache.commons.math3.geometry.partitioning.Region.Location;
+import org.apache.commons.math3.geometry.partitioning.SubHyperplane.SplitSubHyperplane;
+
+/** This class is a factory for {@link Region}.
+
+ * @param <S> Type of the space.
+
+ * @since 3.0
+ */
+public class RegionFactory<S extends Space> {
+
+ /** Visitor removing internal nodes attributes. */
+ private final NodesCleaner nodeCleaner;
+
+ /** Simple constructor.
+ */
+ public RegionFactory() {
+ nodeCleaner = new NodesCleaner();
+ }
+
+ /** Build a convex region from a collection of bounding hyperplanes.
+ * @param hyperplanes collection of bounding hyperplanes
+ * @return a new convex region, or null if the collection is empty
+ */
+ public Region<S> buildConvex(final Hyperplane<S> ... hyperplanes) {
+ if ((hyperplanes == null) || (hyperplanes.length == 0)) {
+ return null;
+ }
+
+ // use the first hyperplane to build the right class
+ final Region<S> region = hyperplanes[0].wholeSpace();
+
+ // chop off parts of the space
+ BSPTree<S> node = region.getTree(false);
+ node.setAttribute(Boolean.TRUE);
+ for (final Hyperplane<S> hyperplane : hyperplanes) {
+ if (node.insertCut(hyperplane)) {
+ node.setAttribute(null);
+ node.getPlus().setAttribute(Boolean.FALSE);
+ node = node.getMinus();
+ node.setAttribute(Boolean.TRUE);
+ } else {
+ // the hyperplane could not be inserted in the current leaf node
+ // either it is completely outside (which means the input hyperplanes
+ // are wrong), or it is parallel to a previous hyperplane
+ SubHyperplane<S> s = hyperplane.wholeHyperplane();
+ for (BSPTree<S> tree = node; tree.getParent() != null && s != null; tree = tree.getParent()) {
+ final Hyperplane<S> other = tree.getParent().getCut().getHyperplane();
+ final SplitSubHyperplane<S> split = s.split(other);
+ switch (split.getSide()) {
+ case HYPER :
+ // the hyperplane is parallel to a previous hyperplane
+ if (!hyperplane.sameOrientationAs(other)) {
+ // this hyperplane is opposite to the other one,
+ // the region is thinner than the tolerance, we consider it empty
+ return getComplement(hyperplanes[0].wholeSpace());
+ }
+ // the hyperplane is an extension of an already known hyperplane, we just ignore it
+ break;
+ case PLUS :
+ // the hyperplane is outside of the current convex zone,
+ // the input hyperplanes are inconsistent
+ throw new MathIllegalArgumentException(LocalizedFormats.NOT_CONVEX_HYPERPLANES);
+ default :
+ s = split.getMinus();
+ }
+ }
+ }
+ }
+
+ return region;
+
+ }
+
+ /** Compute the union of two regions.
+ * @param region1 first region (will be unusable after the operation as
+ * parts of it will be reused in the new region)
+ * @param region2 second region (will be unusable after the operation as
+ * parts of it will be reused in the new region)
+ * @return a new region, result of {@code region1 union region2}
+ */
+ public Region<S> union(final Region<S> region1, final Region<S> region2) {
+ final BSPTree<S> tree =
+ region1.getTree(false).merge(region2.getTree(false), new UnionMerger());
+ tree.visit(nodeCleaner);
+ return region1.buildNew(tree);
+ }
+
+ /** Compute the intersection of two regions.
+ * @param region1 first region (will be unusable after the operation as
+ * parts of it will be reused in the new region)
+ * @param region2 second region (will be unusable after the operation as
+ * parts of it will be reused in the new region)
+ * @return a new region, result of {@code region1 intersection region2}
+ */
+ public Region<S> intersection(final Region<S> region1, final Region<S> region2) {
+ final BSPTree<S> tree =
+ region1.getTree(false).merge(region2.getTree(false), new IntersectionMerger());
+ tree.visit(nodeCleaner);
+ return region1.buildNew(tree);
+ }
+
+ /** Compute the symmetric difference (exclusive or) of two regions.
+ * @param region1 first region (will be unusable after the operation as
+ * parts of it will be reused in the new region)
+ * @param region2 second region (will be unusable after the operation as
+ * parts of it will be reused in the new region)
+ * @return a new region, result of {@code region1 xor region2}
+ */
+ public Region<S> xor(final Region<S> region1, final Region<S> region2) {
+ final BSPTree<S> tree =
+ region1.getTree(false).merge(region2.getTree(false), new XorMerger());
+ tree.visit(nodeCleaner);
+ return region1.buildNew(tree);
+ }
+
+ /** Compute the difference of two regions.
+ * @param region1 first region (will be unusable after the operation as
+ * parts of it will be reused in the new region)
+ * @param region2 second region (will be unusable after the operation as
+ * parts of it will be reused in the new region)
+ * @return a new region, result of {@code region1 minus region2}
+ */
+ public Region<S> difference(final Region<S> region1, final Region<S> region2) {
+ final BSPTree<S> tree =
+ region1.getTree(false).merge(region2.getTree(false), new DifferenceMerger(region1, region2));
+ tree.visit(nodeCleaner);
+ return region1.buildNew(tree);
+ }
+
+ /** Get the complement of the region (exchanged interior/exterior).
+ * @param region region to complement, it will not modified, a new
+ * region independent region will be built
+ * @return a new region, complement of the specified one
+ */
+ /** Get the complement of the region (exchanged interior/exterior).
+ * @param region region to complement, it will not modified, a new
+ * region independent region will be built
+ * @return a new region, complement of the specified one
+ */
+ public Region<S> getComplement(final Region<S> region) {
+ return region.buildNew(recurseComplement(region.getTree(false)));
+ }
+
+ /** Recursively build the complement of a BSP tree.
+ * @param node current node of the original tree
+ * @return new tree, complement of the node
+ */
+ private BSPTree<S> recurseComplement(final BSPTree<S> node) {
+
+ // transform the tree, except for boundary attribute splitters
+ final Map<BSPTree<S>, BSPTree<S>> map = new HashMap<BSPTree<S>, BSPTree<S>>();
+ final BSPTree<S> transformedTree = recurseComplement(node, map);
+
+ // set up the boundary attributes splitters
+ for (final Map.Entry<BSPTree<S>, BSPTree<S>> entry : map.entrySet()) {
+ if (entry.getKey().getCut() != null) {
+ @SuppressWarnings("unchecked")
+ BoundaryAttribute<S> original = (BoundaryAttribute<S>) entry.getKey().getAttribute();
+ if (original != null) {
+ @SuppressWarnings("unchecked")
+ BoundaryAttribute<S> transformed = (BoundaryAttribute<S>) entry.getValue().getAttribute();
+ for (final BSPTree<S> splitter : original.getSplitters()) {
+ transformed.getSplitters().add(map.get(splitter));
+ }
+ }
+ }
+ }
+
+ return transformedTree;
+
+ }
+
+ /** Recursively build the complement of a BSP tree.
+ * @param node current node of the original tree
+ * @param map transformed nodes map
+ * @return new tree, complement of the node
+ */
+ private BSPTree<S> recurseComplement(final BSPTree<S> node,
+ final Map<BSPTree<S>, BSPTree<S>> map) {
+
+ final BSPTree<S> transformedNode;
+ if (node.getCut() == null) {
+ transformedNode = new BSPTree<S>(((Boolean) node.getAttribute()) ? Boolean.FALSE : Boolean.TRUE);
+ } else {
+
+ @SuppressWarnings("unchecked")
+ BoundaryAttribute<S> attribute = (BoundaryAttribute<S>) node.getAttribute();
+ if (attribute != null) {
+ final SubHyperplane<S> plusOutside =
+ (attribute.getPlusInside() == null) ? null : attribute.getPlusInside().copySelf();
+ final SubHyperplane<S> plusInside =
+ (attribute.getPlusOutside() == null) ? null : attribute.getPlusOutside().copySelf();
+ // we start with an empty list of splitters, it will be filled in out of recursion
+ attribute = new BoundaryAttribute<S>(plusOutside, plusInside, new NodesSet<S>());
+ }
+
+ transformedNode = new BSPTree<S>(node.getCut().copySelf(),
+ recurseComplement(node.getPlus(), map),
+ recurseComplement(node.getMinus(), map),
+ attribute);
+ }
+
+ map.put(node, transformedNode);
+ return transformedNode;
+
+ }
+
+ /** BSP tree leaf merger computing union of two regions. */
+ private class UnionMerger implements BSPTree.LeafMerger<S> {
+ /** {@inheritDoc} */
+ public BSPTree<S> merge(final BSPTree<S> leaf, final BSPTree<S> tree,
+ final BSPTree<S> parentTree,
+ final boolean isPlusChild, final boolean leafFromInstance) {
+ if ((Boolean) leaf.getAttribute()) {
+ // the leaf node represents an inside cell
+ leaf.insertInTree(parentTree, isPlusChild, new VanishingToLeaf(true));
+ return leaf;
+ }
+ // the leaf node represents an outside cell
+ tree.insertInTree(parentTree, isPlusChild, new VanishingToLeaf(false));
+ return tree;
+ }
+ }
+
+ /** BSP tree leaf merger computing intersection of two regions. */
+ private class IntersectionMerger implements BSPTree.LeafMerger<S> {
+ /** {@inheritDoc} */
+ public BSPTree<S> merge(final BSPTree<S> leaf, final BSPTree<S> tree,
+ final BSPTree<S> parentTree,
+ final boolean isPlusChild, final boolean leafFromInstance) {
+ if ((Boolean) leaf.getAttribute()) {
+ // the leaf node represents an inside cell
+ tree.insertInTree(parentTree, isPlusChild, new VanishingToLeaf(true));
+ return tree;
+ }
+ // the leaf node represents an outside cell
+ leaf.insertInTree(parentTree, isPlusChild, new VanishingToLeaf(false));
+ return leaf;
+ }
+ }
+
+ /** BSP tree leaf merger computing symmetric difference (exclusive or) of two regions. */
+ private class XorMerger implements BSPTree.LeafMerger<S> {
+ /** {@inheritDoc} */
+ public BSPTree<S> merge(final BSPTree<S> leaf, final BSPTree<S> tree,
+ final BSPTree<S> parentTree, final boolean isPlusChild,
+ final boolean leafFromInstance) {
+ BSPTree<S> t = tree;
+ if ((Boolean) leaf.getAttribute()) {
+ // the leaf node represents an inside cell
+ t = recurseComplement(t);
+ }
+ t.insertInTree(parentTree, isPlusChild, new VanishingToLeaf(true));
+ return t;
+ }
+ }
+
+ /** BSP tree leaf merger computing difference of two regions. */
+ private class DifferenceMerger implements BSPTree.LeafMerger<S>, VanishingCutHandler<S> {
+
+ /** Region to subtract from. */
+ private final Region<S> region1;
+
+ /** Region to subtract. */
+ private final Region<S> region2;
+
+ /** Simple constructor.
+ * @param region1 region to subtract from
+ * @param region2 region to subtract
+ */
+ DifferenceMerger(final Region<S> region1, final Region<S> region2) {
+ this.region1 = region1.copySelf();
+ this.region2 = region2.copySelf();
+ }
+
+ /** {@inheritDoc} */
+ public BSPTree<S> merge(final BSPTree<S> leaf, final BSPTree<S> tree,
+ final BSPTree<S> parentTree, final boolean isPlusChild,
+ final boolean leafFromInstance) {
+ if ((Boolean) leaf.getAttribute()) {
+ // the leaf node represents an inside cell
+ final BSPTree<S> argTree =
+ recurseComplement(leafFromInstance ? tree : leaf);
+ argTree.insertInTree(parentTree, isPlusChild, this);
+ return argTree;
+ }
+ // the leaf node represents an outside cell
+ final BSPTree<S> instanceTree =
+ leafFromInstance ? leaf : tree;
+ instanceTree.insertInTree(parentTree, isPlusChild, this);
+ return instanceTree;
+ }
+
+ /** {@inheritDoc} */
+ public BSPTree<S> fixNode(final BSPTree<S> node) {
+ // get a representative point in the degenerate cell
+ final BSPTree<S> cell = node.pruneAroundConvexCell(Boolean.TRUE, Boolean.FALSE, null);
+ final Region<S> r = region1.buildNew(cell);
+ final Point<S> p = r.getBarycenter();
+ return new BSPTree<S>(region1.checkPoint(p) == Location.INSIDE &&
+ region2.checkPoint(p) == Location.OUTSIDE);
+ }
+
+ }
+
+ /** Visitor removing internal nodes attributes. */
+ private class NodesCleaner implements BSPTreeVisitor<S> {
+
+ /** {@inheritDoc} */
+ public Order visitOrder(final BSPTree<S> node) {
+ return Order.PLUS_SUB_MINUS;
+ }
+
+ /** {@inheritDoc} */
+ public void visitInternalNode(final BSPTree<S> node) {
+ node.setAttribute(null);
+ }
+
+ /** {@inheritDoc} */
+ public void visitLeafNode(final BSPTree<S> node) {
+ }
+
+ }
+
+ /** Handler replacing nodes with vanishing cuts with leaf nodes. */
+ private class VanishingToLeaf implements VanishingCutHandler<S> {
+
+ /** Inside/outside indocator to use for ambiguous nodes. */
+ private final boolean inside;
+
+ /** Simple constructor.
+ * @param inside inside/outside indicator to use for ambiguous nodes
+ */
+ VanishingToLeaf(final boolean inside) {
+ this.inside = inside;
+ }
+
+ /** {@inheritDoc} */
+ public BSPTree<S> fixNode(final BSPTree<S> node) {
+ if (node.getPlus().getAttribute().equals(node.getMinus().getAttribute())) {
+ // no ambiguity
+ return new BSPTree<S>(node.getPlus().getAttribute());
+ } else {
+ // ambiguous node
+ return new BSPTree<S>(inside);
+ }
+ }
+
+ }
+
+}
diff --git a/src/main/java/org/apache/commons/math3/geometry/partitioning/Side.java b/src/main/java/org/apache/commons/math3/geometry/partitioning/Side.java
new file mode 100644
index 0000000..c9a1357
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/geometry/partitioning/Side.java
@@ -0,0 +1,37 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math3.geometry.partitioning;
+
+/** Enumerate representing the location of an element with respect to an
+ * {@link Hyperplane hyperplane} of a space.
+ * @since 3.0
+ */
+public enum Side {
+
+ /** Code for the plus side of the hyperplane. */
+ PLUS,
+
+ /** Code for the minus side of the hyperplane. */
+ MINUS,
+
+ /** Code for elements crossing the hyperplane from plus to minus side. */
+ BOTH,
+
+ /** Code for the hyperplane itself. */
+ HYPER;
+
+}
diff --git a/src/main/java/org/apache/commons/math3/geometry/partitioning/SubHyperplane.java b/src/main/java/org/apache/commons/math3/geometry/partitioning/SubHyperplane.java
new file mode 100644
index 0000000..2069f6f
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/geometry/partitioning/SubHyperplane.java
@@ -0,0 +1,155 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math3.geometry.partitioning;
+
+import org.apache.commons.math3.geometry.Space;
+
+/** This interface represents the remaining parts of an hyperplane after
+ * other parts have been chopped off.
+
+ * <p>sub-hyperplanes are obtained when parts of an {@link
+ * Hyperplane hyperplane} are chopped off by other hyperplanes that
+ * intersect it. The remaining part is a convex region. Such objects
+ * appear in {@link BSPTree BSP trees} as the intersection of a cut
+ * hyperplane with the convex region which it splits, the chopping
+ * hyperplanes are the cut hyperplanes closer to the tree root.</p>
+
+ * <p>
+ * Note that this interface is <em>not</em> intended to be implemented
+ * by Apache Commons Math users, it is only intended to be implemented
+ * within the library itself. New methods may be added even for minor
+ * versions, which breaks compatibility for external implementations.
+ * </p>
+
+ * @param <S> Type of the embedding space.
+
+ * @since 3.0
+ */
+public interface SubHyperplane<S extends Space> {
+
+ /** Copy the instance.
+ * <p>The instance created is completely independent of the original
+ * one. A deep copy is used, none of the underlying objects are
+ * shared (except for the nodes attributes and immutable
+ * objects).</p>
+ * @return a new sub-hyperplane, copy of the instance
+ */
+ SubHyperplane<S> copySelf();
+
+ /** Get the underlying hyperplane.
+ * @return underlying hyperplane
+ */
+ Hyperplane<S> getHyperplane();
+
+ /** Check if the instance is empty.
+ * @return true if the instance is empty
+ */
+ boolean isEmpty();
+
+ /** Get the size of the instance.
+ * @return the size of the instance (this is a length in 1D, an area
+ * in 2D, a volume in 3D ...)
+ */
+ double getSize();
+
+ /** Compute the relative position of the instance with respect
+ * to an hyperplane.
+ * @param hyperplane hyperplane to check instance against
+ * @return one of {@link Side#PLUS}, {@link Side#MINUS}, {@link Side#BOTH},
+ * {@link Side#HYPER}
+ * @deprecated as of 3.6, replaced with {@link #split(Hyperplane)}.{@link SplitSubHyperplane#getSide()}
+ */
+ @Deprecated
+ Side side(Hyperplane<S> hyperplane);
+
+ /** Split the instance in two parts by an hyperplane.
+ * @param hyperplane splitting hyperplane
+ * @return an object containing both the part of the instance
+ * on the plus side of the hyperplane and the part of the
+ * instance on the minus side of the hyperplane
+ */
+ SplitSubHyperplane<S> split(Hyperplane<S> hyperplane);
+
+ /** Compute the union of the instance and another sub-hyperplane.
+ * @param other other sub-hyperplane to union (<em>must</em> be in the
+ * same hyperplane as the instance)
+ * @return a new sub-hyperplane, union of the instance and other
+ */
+ SubHyperplane<S> reunite(SubHyperplane<S> other);
+
+ /** Class holding the results of the {@link #split split} method.
+ * @param <U> Type of the embedding space.
+ */
+ class SplitSubHyperplane<U extends Space> {
+
+ /** Part of the sub-hyperplane on the plus side of the splitting hyperplane. */
+ private final SubHyperplane<U> plus;
+
+ /** Part of the sub-hyperplane on the minus side of the splitting hyperplane. */
+ private final SubHyperplane<U> minus;
+
+ /** Build a SplitSubHyperplane from its parts.
+ * @param plus part of the sub-hyperplane on the plus side of the
+ * splitting hyperplane
+ * @param minus part of the sub-hyperplane on the minus side of the
+ * splitting hyperplane
+ */
+ public SplitSubHyperplane(final SubHyperplane<U> plus,
+ final SubHyperplane<U> minus) {
+ this.plus = plus;
+ this.minus = minus;
+ }
+
+ /** Get the part of the sub-hyperplane on the plus side of the splitting hyperplane.
+ * @return part of the sub-hyperplane on the plus side of the splitting hyperplane
+ */
+ public SubHyperplane<U> getPlus() {
+ return plus;
+ }
+
+ /** Get the part of the sub-hyperplane on the minus side of the splitting hyperplane.
+ * @return part of the sub-hyperplane on the minus side of the splitting hyperplane
+ */
+ public SubHyperplane<U> getMinus() {
+ return minus;
+ }
+
+ /** Get the side of the split sub-hyperplane with respect to its splitter.
+ * @return {@link Side#PLUS} if only {@link #getPlus()} is neither null nor empty,
+ * {@link Side#MINUS} if only {@link #getMinus()} is neither null nor empty,
+ * {@link Side#BOTH} if both {@link #getPlus()} and {@link #getMinus()}
+ * are neither null nor empty or {@link Side#HYPER} if both {@link #getPlus()} and
+ * {@link #getMinus()} are either null or empty
+ * @since 3.6
+ */
+ public Side getSide() {
+ if (plus != null && !plus.isEmpty()) {
+ if (minus != null && !minus.isEmpty()) {
+ return Side.BOTH;
+ } else {
+ return Side.PLUS;
+ }
+ } else if (minus != null && !minus.isEmpty()) {
+ return Side.MINUS;
+ } else {
+ return Side.HYPER;
+ }
+ }
+
+ }
+
+}
diff --git a/src/main/java/org/apache/commons/math3/geometry/partitioning/Transform.java b/src/main/java/org/apache/commons/math3/geometry/partitioning/Transform.java
new file mode 100644
index 0000000..ba0c1dd
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/geometry/partitioning/Transform.java
@@ -0,0 +1,80 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math3.geometry.partitioning;
+
+import org.apache.commons.math3.geometry.Point;
+import org.apache.commons.math3.geometry.Space;
+
+
+/** This interface represents an inversible affine transform in a space.
+ * <p>Inversible affine transform include for example scalings,
+ * translations, rotations.</p>
+
+ * <p>Transforms are dimension-specific. The consistency rules between
+ * the three {@code apply} methods are the following ones for a
+ * transformed defined for dimension D:</p>
+ * <ul>
+ * <li>
+ * the transform can be applied to a point in the
+ * D-dimension space using its {@link #apply(Point)}
+ * method
+ * </li>
+ * <li>
+ * the transform can be applied to a (D-1)-dimension
+ * hyperplane in the D-dimension space using its
+ * {@link #apply(Hyperplane)} method
+ * </li>
+ * <li>
+ * the transform can be applied to a (D-2)-dimension
+ * sub-hyperplane in a (D-1)-dimension hyperplane using
+ * its {@link #apply(SubHyperplane, Hyperplane, Hyperplane)}
+ * method
+ * </li>
+ * </ul>
+
+ * @param <S> Type of the embedding space.
+ * @param <T> Type of the embedded sub-space.
+
+ * @since 3.0
+ */
+public interface Transform<S extends Space, T extends Space> {
+
+ /** Transform a point of a space.
+ * @param point point to transform
+ * @return a new object representing the transformed point
+ */
+ Point<S> apply(Point<S> point);
+
+ /** Transform an hyperplane of a space.
+ * @param hyperplane hyperplane to transform
+ * @return a new object representing the transformed hyperplane
+ */
+ Hyperplane<S> apply(Hyperplane<S> hyperplane);
+
+ /** Transform a sub-hyperplane embedded in an hyperplane.
+ * @param sub sub-hyperplane to transform
+ * @param original hyperplane in which the sub-hyperplane is
+ * defined (this is the original hyperplane, the transform has
+ * <em>not</em> been applied to it)
+ * @param transformed hyperplane in which the sub-hyperplane is
+ * defined (this is the transformed hyperplane, the transform
+ * <em>has</em> been applied to it)
+ * @return a new object representing the transformed sub-hyperplane
+ */
+ SubHyperplane<T> apply(SubHyperplane<T> sub, Hyperplane<S> original, Hyperplane<S> transformed);
+
+}
diff --git a/src/main/java/org/apache/commons/math3/geometry/partitioning/package-info.java b/src/main/java/org/apache/commons/math3/geometry/partitioning/package-info.java
new file mode 100644
index 0000000..6e63c73
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/geometry/partitioning/package-info.java
@@ -0,0 +1,114 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/**
+ *
+ * This package provides classes to implement Binary Space Partition trees.
+ *
+ * <p>
+ * {@link org.apache.commons.math3.geometry.partitioning.BSPTree BSP trees}
+ * are an efficient way to represent parts of space and in particular
+ * polytopes (line segments in 1D, polygons in 2D and polyhedrons in 3D)
+ * and to operate on them. The main principle is to recursively subdivide
+ * the space using simple hyperplanes (points in 1D, lines in 2D, planes
+ * in 3D).
+ * </p>
+ *
+ * <p>
+ * We start with a tree composed of a single node without any cut
+ * hyperplane: it represents the complete space, which is a convex
+ * part. If we add a cut hyperplane to this node, this represents a
+ * partition with the hyperplane at the node level and two half spaces at
+ * each side of the cut hyperplane. These half-spaces are represented by
+ * two child nodes without any cut hyperplanes associated, the plus child
+ * which represents the half space on the plus side of the cut hyperplane
+ * and the minus child on the other side. Continuing the subdivisions, we
+ * end up with a tree having internal nodes that are associated with a
+ * cut hyperplane and leaf nodes without any hyperplane which correspond
+ * to convex parts.
+ * </p>
+ *
+ * <p>
+ * When BSP trees are used to represent polytopes, the convex parts are
+ * known to be completely inside or outside the polytope as long as there
+ * is no facet in the part (which is obviously the case if the cut
+ * hyperplanes have been chosen as the underlying hyperplanes of the
+ * facets (this is called an autopartition) and if the subdivision
+ * process has been continued until all facets have been processed. It is
+ * important to note that the polytope is <em>not</em> defined by a
+ * single part, but by several convex ones. This is the property that
+ * allows BSP-trees to represent non-convex polytopes despites all parts
+ * are convex. The {@link
+ * org.apache.commons.math3.geometry.partitioning.Region Region} class is
+ * devoted to this representation, it is build on top of the {@link
+ * org.apache.commons.math3.geometry.partitioning.BSPTree BSPTree} class using
+ * boolean objects as the leaf nodes attributes to represent the
+ * inside/outside property of each leaf part, and also adds various
+ * methods dealing with boundaries (i.e. the separation between the
+ * inside and the outside parts).
+ * </p>
+ *
+ * <p>
+ * Rather than simply associating the internal nodes with an hyperplane,
+ * we consider <em>sub-hyperplanes</em> which correspond to the part of
+ * the hyperplane that is inside the convex part defined by all the
+ * parent nodes (this implies that the sub-hyperplane at root node is in
+ * fact a complete hyperplane, because there is no parent to bound
+ * it). Since the parts are convex, the sub-hyperplanes are convex, in
+ * 3D the convex parts are convex polyhedrons, and the sub-hyperplanes
+ * are convex polygons that cut these polyhedrons in two
+ * sub-polyhedrons. Using this definition, a BSP tree completely
+ * partitions the space. Each point either belongs to one of the
+ * sub-hyperplanes in an internal node or belongs to one of the leaf
+ * convex parts.
+ * </p>
+ *
+ * <p>
+ * In order to determine where a point is, it is sufficient to check its
+ * position with respect to the root cut hyperplane, to select the
+ * corresponding child tree and to repeat the procedure recursively,
+ * until either the point appears to be exactly on one of the hyperplanes
+ * in the middle of the tree or to be in one of the leaf parts. For
+ * this operation, it is sufficient to consider the complete hyperplanes,
+ * there is no need to check the points with the boundary of the
+ * sub-hyperplanes, because this check has in fact already been realized
+ * by the recursive descent in the tree. This is very easy to do and very
+ * efficient, especially if the tree is well balanced (the cost is
+ * <code>O(log(n))</code> where <code>n</code> is the number of facets)
+ * or if the first tree levels close to the root discriminate large parts
+ * of the total space.
+ * </p>
+ *
+ * <p>
+ * One of the main sources for the development of this package was Bruce
+ * Naylor, John Amanatides and William Thibault paper <a
+ * href="http://www.cs.yorku.ca/~amana/research/bsptSetOp.pdf">Merging
+ * BSP Trees Yields Polyhedral Set Operations</a> Proc. Siggraph '90,
+ * Computer Graphics 24(4), August 1990, pp 115-124, published by the
+ * Association for Computing Machinery (ACM). The same paper can also be
+ * found <a
+ * href="http://www.cs.utexas.edu/users/fussell/courses/cs384g/bsp_treemerge.pdf">here</a>.
+ * </p>
+ *
+ * <p>
+ * Note that the interfaces defined in this package are <em>not</em> intended to
+ * be implemented by Apache Commons Math users, they are only intended to be
+ * implemented within the library itself. New methods may be added even for
+ * minor versions, which breaks compatibility for external implementations.
+ * </p>
+ *
+ */
+package org.apache.commons.math3.geometry.partitioning;
diff --git a/src/main/java/org/apache/commons/math3/geometry/partitioning/utilities/AVLTree.java b/src/main/java/org/apache/commons/math3/geometry/partitioning/utilities/AVLTree.java
new file mode 100644
index 0000000..00c9d3e
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/geometry/partitioning/utilities/AVLTree.java
@@ -0,0 +1,634 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math3.geometry.partitioning.utilities;
+
+/** This class implements AVL trees.
+ *
+ * <p>The purpose of this class is to sort elements while allowing
+ * duplicate elements (i.e. such that {@code a.equals(b)} is
+ * true). The {@code SortedSet} interface does not allow this, so
+ * a specific class is needed. Null elements are not allowed.</p>
+ *
+ * <p>Since the {@code equals} method is not sufficient to
+ * differentiate elements, the {@link #delete delete} method is
+ * implemented using the equality operator.</p>
+ *
+ * <p>In order to clearly mark the methods provided here do not have
+ * the same semantics as the ones specified in the
+ * {@code SortedSet} interface, different names are used
+ * ({@code add} has been replaced by {@link #insert insert} and
+ * {@code remove} has been replaced by {@link #delete
+ * delete}).</p>
+ *
+ * <p>This class is based on the C implementation Georg Kraml has put
+ * in the public domain. Unfortunately, his <a
+ * href="www.purists.org/georg/avltree/index.html">page</a> seems not
+ * to exist any more.</p>
+ *
+ * @param <T> the type of the elements
+ *
+ * @since 3.0
+ * @deprecated as of 3.4, this class is not used anymore and considered
+ * to be out of scope of Apache Commons Math
+ */
+@Deprecated
+public class AVLTree<T extends Comparable<T>> {
+
+ /** Top level node. */
+ private Node top;
+
+ /** Build an empty tree.
+ */
+ public AVLTree() {
+ top = null;
+ }
+
+ /** Insert an element in the tree.
+ * @param element element to insert (silently ignored if null)
+ */
+ public void insert(final T element) {
+ if (element != null) {
+ if (top == null) {
+ top = new Node(element, null);
+ } else {
+ top.insert(element);
+ }
+ }
+ }
+
+ /** Delete an element from the tree.
+ * <p>The element is deleted only if there is a node {@code n}
+ * containing exactly the element instance specified, i.e. for which
+ * {@code n.getElement() == element}. This is purposely
+ * <em>different</em> from the specification of the
+ * {@code java.util.Set} {@code remove} method (in fact,
+ * this is the reason why a specific class has been developed).</p>
+ * @param element element to delete (silently ignored if null)
+ * @return true if the element was deleted from the tree
+ */
+ public boolean delete(final T element) {
+ if (element != null) {
+ for (Node node = getNotSmaller(element); node != null; node = node.getNext()) {
+ // loop over all elements neither smaller nor larger
+ // than the specified one
+ if (node.element == element) {
+ node.delete();
+ return true;
+ } else if (node.element.compareTo(element) > 0) {
+ // all the remaining elements are known to be larger,
+ // the element is not in the tree
+ return false;
+ }
+ }
+ }
+ return false;
+ }
+
+ /** Check if the tree is empty.
+ * @return true if the tree is empty
+ */
+ public boolean isEmpty() {
+ return top == null;
+ }
+
+
+ /** Get the number of elements of the tree.
+ * @return number of elements contained in the tree
+ */
+ public int size() {
+ return (top == null) ? 0 : top.size();
+ }
+
+ /** Get the node whose element is the smallest one in the tree.
+ * @return the tree node containing the smallest element in the tree
+ * or null if the tree is empty
+ * @see #getLargest
+ * @see #getNotSmaller
+ * @see #getNotLarger
+ * @see Node#getPrevious
+ * @see Node#getNext
+ */
+ public Node getSmallest() {
+ return (top == null) ? null : top.getSmallest();
+ }
+
+ /** Get the node whose element is the largest one in the tree.
+ * @return the tree node containing the largest element in the tree
+ * or null if the tree is empty
+ * @see #getSmallest
+ * @see #getNotSmaller
+ * @see #getNotLarger
+ * @see Node#getPrevious
+ * @see Node#getNext
+ */
+ public Node getLargest() {
+ return (top == null) ? null : top.getLargest();
+ }
+
+ /** Get the node whose element is not smaller than the reference object.
+ * @param reference reference object (may not be in the tree)
+ * @return the tree node containing the smallest element not smaller
+ * than the reference object or null if either the tree is empty or
+ * all its elements are smaller than the reference object
+ * @see #getSmallest
+ * @see #getLargest
+ * @see #getNotLarger
+ * @see Node#getPrevious
+ * @see Node#getNext
+ */
+ public Node getNotSmaller(final T reference) {
+ Node candidate = null;
+ for (Node node = top; node != null;) {
+ if (node.element.compareTo(reference) < 0) {
+ if (node.right == null) {
+ return candidate;
+ }
+ node = node.right;
+ } else {
+ candidate = node;
+ if (node.left == null) {
+ return candidate;
+ }
+ node = node.left;
+ }
+ }
+ return null;
+ }
+
+ /** Get the node whose element is not larger than the reference object.
+ * @param reference reference object (may not be in the tree)
+ * @return the tree node containing the largest element not larger
+ * than the reference object (in which case the node is guaranteed
+ * not to be empty) or null if either the tree is empty or all its
+ * elements are larger than the reference object
+ * @see #getSmallest
+ * @see #getLargest
+ * @see #getNotSmaller
+ * @see Node#getPrevious
+ * @see Node#getNext
+ */
+ public Node getNotLarger(final T reference) {
+ Node candidate = null;
+ for (Node node = top; node != null;) {
+ if (node.element.compareTo(reference) > 0) {
+ if (node.left == null) {
+ return candidate;
+ }
+ node = node.left;
+ } else {
+ candidate = node;
+ if (node.right == null) {
+ return candidate;
+ }
+ node = node.right;
+ }
+ }
+ return null;
+ }
+
+ /** Enum for tree skew factor. */
+ private enum Skew {
+ /** Code for left high trees. */
+ LEFT_HIGH,
+
+ /** Code for right high trees. */
+ RIGHT_HIGH,
+
+ /** Code for Skew.BALANCED trees. */
+ BALANCED;
+ }
+
+ /** This class implements AVL trees nodes.
+ * <p>AVL tree nodes implement all the logical structure of the
+ * tree. Nodes are created by the {@link AVLTree AVLTree} class.</p>
+ * <p>The nodes are not independant from each other but must obey
+ * specific balancing constraints and the tree structure is
+ * rearranged as elements are inserted or deleted from the tree. The
+ * creation, modification and tree-related navigation methods have
+ * therefore restricted access. Only the order-related navigation,
+ * reading and delete methods are public.</p>
+ * @see AVLTree
+ */
+ public class Node {
+
+ /** Element contained in the current node. */
+ private T element;
+
+ /** Left sub-tree. */
+ private Node left;
+
+ /** Right sub-tree. */
+ private Node right;
+
+ /** Parent tree. */
+ private Node parent;
+
+ /** Skew factor. */
+ private Skew skew;
+
+ /** Build a node for a specified element.
+ * @param element element
+ * @param parent parent node
+ */
+ Node(final T element, final Node parent) {
+ this.element = element;
+ left = null;
+ right = null;
+ this.parent = parent;
+ skew = Skew.BALANCED;
+ }
+
+ /** Get the contained element.
+ * @return element contained in the node
+ */
+ public T getElement() {
+ return element;
+ }
+
+ /** Get the number of elements of the tree rooted at this node.
+ * @return number of elements contained in the tree rooted at this node
+ */
+ int size() {
+ return 1 + ((left == null) ? 0 : left.size()) + ((right == null) ? 0 : right.size());
+ }
+
+ /** Get the node whose element is the smallest one in the tree
+ * rooted at this node.
+ * @return the tree node containing the smallest element in the
+ * tree rooted at this node or null if the tree is empty
+ * @see #getLargest
+ */
+ Node getSmallest() {
+ Node node = this;
+ while (node.left != null) {
+ node = node.left;
+ }
+ return node;
+ }
+
+ /** Get the node whose element is the largest one in the tree
+ * rooted at this node.
+ * @return the tree node containing the largest element in the
+ * tree rooted at this node or null if the tree is empty
+ * @see #getSmallest
+ */
+ Node getLargest() {
+ Node node = this;
+ while (node.right != null) {
+ node = node.right;
+ }
+ return node;
+ }
+
+ /** Get the node containing the next smaller or equal element.
+ * @return node containing the next smaller or equal element or
+ * null if there is no smaller or equal element in the tree
+ * @see #getNext
+ */
+ public Node getPrevious() {
+
+ if (left != null) {
+ final Node node = left.getLargest();
+ if (node != null) {
+ return node;
+ }
+ }
+
+ for (Node node = this; node.parent != null; node = node.parent) {
+ if (node != node.parent.left) {
+ return node.parent;
+ }
+ }
+
+ return null;
+
+ }
+
+ /** Get the node containing the next larger or equal element.
+ * @return node containing the next larger or equal element (in
+ * which case the node is guaranteed not to be empty) or null if
+ * there is no larger or equal element in the tree
+ * @see #getPrevious
+ */
+ public Node getNext() {
+
+ if (right != null) {
+ final Node node = right.getSmallest();
+ if (node != null) {
+ return node;
+ }
+ }
+
+ for (Node node = this; node.parent != null; node = node.parent) {
+ if (node != node.parent.right) {
+ return node.parent;
+ }
+ }
+
+ return null;
+
+ }
+
+ /** Insert an element in a sub-tree.
+ * @param newElement element to insert
+ * @return true if the parent tree should be re-Skew.BALANCED
+ */
+ boolean insert(final T newElement) {
+ if (newElement.compareTo(this.element) < 0) {
+ // the inserted element is smaller than the node
+ if (left == null) {
+ left = new Node(newElement, this);
+ return rebalanceLeftGrown();
+ }
+ return left.insert(newElement) ? rebalanceLeftGrown() : false;
+ }
+
+ // the inserted element is equal to or greater than the node
+ if (right == null) {
+ right = new Node(newElement, this);
+ return rebalanceRightGrown();
+ }
+ return right.insert(newElement) ? rebalanceRightGrown() : false;
+
+ }
+
+ /** Delete the node from the tree.
+ */
+ public void delete() {
+ if ((parent == null) && (left == null) && (right == null)) {
+ // this was the last node, the tree is now empty
+ element = null;
+ top = null;
+ } else {
+
+ Node node;
+ Node child;
+ boolean leftShrunk;
+ if ((left == null) && (right == null)) {
+ node = this;
+ element = null;
+ leftShrunk = node == node.parent.left;
+ child = null;
+ } else {
+ node = (left != null) ? left.getLargest() : right.getSmallest();
+ element = node.element;
+ leftShrunk = node == node.parent.left;
+ child = (node.left != null) ? node.left : node.right;
+ }
+
+ node = node.parent;
+ if (leftShrunk) {
+ node.left = child;
+ } else {
+ node.right = child;
+ }
+ if (child != null) {
+ child.parent = node;
+ }
+
+ while (leftShrunk ? node.rebalanceLeftShrunk() : node.rebalanceRightShrunk()) {
+ if (node.parent == null) {
+ return;
+ }
+ leftShrunk = node == node.parent.left;
+ node = node.parent;
+ }
+
+ }
+ }
+
+ /** Re-balance the instance as left sub-tree has grown.
+ * @return true if the parent tree should be reSkew.BALANCED too
+ */
+ private boolean rebalanceLeftGrown() {
+ switch (skew) {
+ case LEFT_HIGH:
+ if (left.skew == Skew.LEFT_HIGH) {
+ rotateCW();
+ skew = Skew.BALANCED;
+ right.skew = Skew.BALANCED;
+ } else {
+ final Skew s = left.right.skew;
+ left.rotateCCW();
+ rotateCW();
+ switch(s) {
+ case LEFT_HIGH:
+ left.skew = Skew.BALANCED;
+ right.skew = Skew.RIGHT_HIGH;
+ break;
+ case RIGHT_HIGH:
+ left.skew = Skew.LEFT_HIGH;
+ right.skew = Skew.BALANCED;
+ break;
+ default:
+ left.skew = Skew.BALANCED;
+ right.skew = Skew.BALANCED;
+ }
+ skew = Skew.BALANCED;
+ }
+ return false;
+ case RIGHT_HIGH:
+ skew = Skew.BALANCED;
+ return false;
+ default:
+ skew = Skew.LEFT_HIGH;
+ return true;
+ }
+ }
+
+ /** Re-balance the instance as right sub-tree has grown.
+ * @return true if the parent tree should be reSkew.BALANCED too
+ */
+ private boolean rebalanceRightGrown() {
+ switch (skew) {
+ case LEFT_HIGH:
+ skew = Skew.BALANCED;
+ return false;
+ case RIGHT_HIGH:
+ if (right.skew == Skew.RIGHT_HIGH) {
+ rotateCCW();
+ skew = Skew.BALANCED;
+ left.skew = Skew.BALANCED;
+ } else {
+ final Skew s = right.left.skew;
+ right.rotateCW();
+ rotateCCW();
+ switch (s) {
+ case LEFT_HIGH:
+ left.skew = Skew.BALANCED;
+ right.skew = Skew.RIGHT_HIGH;
+ break;
+ case RIGHT_HIGH:
+ left.skew = Skew.LEFT_HIGH;
+ right.skew = Skew.BALANCED;
+ break;
+ default:
+ left.skew = Skew.BALANCED;
+ right.skew = Skew.BALANCED;
+ }
+ skew = Skew.BALANCED;
+ }
+ return false;
+ default:
+ skew = Skew.RIGHT_HIGH;
+ return true;
+ }
+ }
+
+ /** Re-balance the instance as left sub-tree has shrunk.
+ * @return true if the parent tree should be reSkew.BALANCED too
+ */
+ private boolean rebalanceLeftShrunk() {
+ switch (skew) {
+ case LEFT_HIGH:
+ skew = Skew.BALANCED;
+ return true;
+ case RIGHT_HIGH:
+ if (right.skew == Skew.RIGHT_HIGH) {
+ rotateCCW();
+ skew = Skew.BALANCED;
+ left.skew = Skew.BALANCED;
+ return true;
+ } else if (right.skew == Skew.BALANCED) {
+ rotateCCW();
+ skew = Skew.LEFT_HIGH;
+ left.skew = Skew.RIGHT_HIGH;
+ return false;
+ } else {
+ final Skew s = right.left.skew;
+ right.rotateCW();
+ rotateCCW();
+ switch (s) {
+ case LEFT_HIGH:
+ left.skew = Skew.BALANCED;
+ right.skew = Skew.RIGHT_HIGH;
+ break;
+ case RIGHT_HIGH:
+ left.skew = Skew.LEFT_HIGH;
+ right.skew = Skew.BALANCED;
+ break;
+ default:
+ left.skew = Skew.BALANCED;
+ right.skew = Skew.BALANCED;
+ }
+ skew = Skew.BALANCED;
+ return true;
+ }
+ default:
+ skew = Skew.RIGHT_HIGH;
+ return false;
+ }
+ }
+
+ /** Re-balance the instance as right sub-tree has shrunk.
+ * @return true if the parent tree should be reSkew.BALANCED too
+ */
+ private boolean rebalanceRightShrunk() {
+ switch (skew) {
+ case RIGHT_HIGH:
+ skew = Skew.BALANCED;
+ return true;
+ case LEFT_HIGH:
+ if (left.skew == Skew.LEFT_HIGH) {
+ rotateCW();
+ skew = Skew.BALANCED;
+ right.skew = Skew.BALANCED;
+ return true;
+ } else if (left.skew == Skew.BALANCED) {
+ rotateCW();
+ skew = Skew.RIGHT_HIGH;
+ right.skew = Skew.LEFT_HIGH;
+ return false;
+ } else {
+ final Skew s = left.right.skew;
+ left.rotateCCW();
+ rotateCW();
+ switch (s) {
+ case LEFT_HIGH:
+ left.skew = Skew.BALANCED;
+ right.skew = Skew.RIGHT_HIGH;
+ break;
+ case RIGHT_HIGH:
+ left.skew = Skew.LEFT_HIGH;
+ right.skew = Skew.BALANCED;
+ break;
+ default:
+ left.skew = Skew.BALANCED;
+ right.skew = Skew.BALANCED;
+ }
+ skew = Skew.BALANCED;
+ return true;
+ }
+ default:
+ skew = Skew.LEFT_HIGH;
+ return false;
+ }
+ }
+
+ /** Perform a clockwise rotation rooted at the instance.
+ * <p>The skew factor are not updated by this method, they
+ * <em>must</em> be updated by the caller</p>
+ */
+ private void rotateCW() {
+
+ final T tmpElt = element;
+ element = left.element;
+ left.element = tmpElt;
+
+ final Node tmpNode = left;
+ left = tmpNode.left;
+ tmpNode.left = tmpNode.right;
+ tmpNode.right = right;
+ right = tmpNode;
+
+ if (left != null) {
+ left.parent = this;
+ }
+ if (right.right != null) {
+ right.right.parent = right;
+ }
+
+ }
+
+ /** Perform a counter-clockwise rotation rooted at the instance.
+ * <p>The skew factor are not updated by this method, they
+ * <em>must</em> be updated by the caller</p>
+ */
+ private void rotateCCW() {
+
+ final T tmpElt = element;
+ element = right.element;
+ right.element = tmpElt;
+
+ final Node tmpNode = right;
+ right = tmpNode.right;
+ tmpNode.right = tmpNode.left;
+ tmpNode.left = left;
+ left = tmpNode;
+
+ if (right != null) {
+ right.parent = this;
+ }
+ if (left.left != null) {
+ left.left.parent = left;
+ }
+
+ }
+
+ }
+
+}
diff --git a/src/main/java/org/apache/commons/math3/geometry/partitioning/utilities/OrderedTuple.java b/src/main/java/org/apache/commons/math3/geometry/partitioning/utilities/OrderedTuple.java
new file mode 100644
index 0000000..2dad2d7
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/geometry/partitioning/utilities/OrderedTuple.java
@@ -0,0 +1,431 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math3.geometry.partitioning.utilities;
+
+import java.util.Arrays;
+
+import org.apache.commons.math3.util.FastMath;
+
+/** This class implements an ordering operation for T-uples.
+ *
+ * <p>Ordering is done by encoding all components of the T-uple into a
+ * single scalar value and using this value as the sorting
+ * key. Encoding is performed using the method invented by Georg
+ * Cantor in 1877 when he proved it was possible to establish a
+ * bijection between a line and a plane. The binary representations of
+ * the components of the T-uple are mixed together to form a single
+ * scalar. This means that the 2<sup>k</sup> bit of component 0 is
+ * followed by the 2<sup>k</sup> bit of component 1, then by the
+ * 2<sup>k</sup> bit of component 2 up to the 2<sup>k</sup> bit of
+ * component {@code t}, which is followed by the 2<sup>k-1</sup>
+ * bit of component 0, followed by the 2<sup>k-1</sup> bit of
+ * component 1 ... The binary representations are extended as needed
+ * to handle numbers with different scales and a suitable
+ * 2<sup>p</sup> offset is added to the components in order to avoid
+ * negative numbers (this offset is adjusted as needed during the
+ * comparison operations).</p>
+ *
+ * <p>The more interesting property of the encoding method for our
+ * purpose is that it allows to select all the points that are in a
+ * given range. This is depicted in dimension 2 by the following
+ * picture:</p>
+ *
+ * <img src="doc-files/OrderedTuple.png" />
+ *
+ * <p>This picture shows a set of 100000 random 2-D pairs having their
+ * first component between -50 and +150 and their second component
+ * between -350 and +50. We wanted to extract all pairs having their
+ * first component between +30 and +70 and their second component
+ * between -120 and -30. We built the lower left point at coordinates
+ * (30, -120) and the upper right point at coordinates (70, -30). All
+ * points smaller than the lower left point are drawn in red and all
+ * points larger than the upper right point are drawn in blue. The
+ * green points are between the two limits. This picture shows that
+ * all the desired points are selected, along with spurious points. In
+ * this case, we get 15790 points, 4420 of which really belonging to
+ * the desired rectangle. It is possible to extract very small
+ * subsets. As an example extracting from the same 100000 points set
+ * the points having their first component between +30 and +31 and
+ * their second component between -91 and -90, we get a subset of 11
+ * points, 2 of which really belonging to the desired rectangle.</p>
+ *
+ * <p>the previous selection technique can be applied in all
+ * dimensions, still using two points to define the interval. The
+ * first point will have all its components set to their lower bounds
+ * while the second point will have all its components set to their
+ * upper bounds.</p>
+ *
+ * <p>T-uples with negative infinite or positive infinite components
+ * are sorted logically.</p>
+ *
+ * <p>Since the specification of the {@code Comparator} interface
+ * allows only {@code ClassCastException} errors, some arbitrary
+ * choices have been made to handle specific cases. The rationale for
+ * these choices is to keep <em>regular</em> and consistent T-uples
+ * together.</p>
+ * <ul>
+ * <li>instances with different dimensions are sorted according to
+ * their dimension regardless of their components values</li>
+ * <li>instances with {@code Double.NaN} components are sorted
+ * after all other ones (even after instances with positive infinite
+ * components</li>
+ * <li>instances with both positive and negative infinite components
+ * are considered as if they had {@code Double.NaN}
+ * components</li>
+ * </ul>
+ *
+ * @since 3.0
+ * @deprecated as of 3.4, this class is not used anymore and considered
+ * to be out of scope of Apache Commons Math
+ */
+@Deprecated
+public class OrderedTuple implements Comparable<OrderedTuple> {
+
+ /** Sign bit mask. */
+ private static final long SIGN_MASK = 0x8000000000000000L;
+
+ /** Exponent bits mask. */
+ private static final long EXPONENT_MASK = 0x7ff0000000000000L;
+
+ /** Mantissa bits mask. */
+ private static final long MANTISSA_MASK = 0x000fffffffffffffL;
+
+ /** Implicit MSB for normalized numbers. */
+ private static final long IMPLICIT_ONE = 0x0010000000000000L;
+
+ /** Double components of the T-uple. */
+ private double[] components;
+
+ /** Offset scale. */
+ private int offset;
+
+ /** Least Significant Bit scale. */
+ private int lsb;
+
+ /** Ordering encoding of the double components. */
+ private long[] encoding;
+
+ /** Positive infinity marker. */
+ private boolean posInf;
+
+ /** Negative infinity marker. */
+ private boolean negInf;
+
+ /** Not A Number marker. */
+ private boolean nan;
+
+ /** Build an ordered T-uple from its components.
+ * @param components double components of the T-uple
+ */
+ public OrderedTuple(final double ... components) {
+ this.components = components.clone();
+ int msb = Integer.MIN_VALUE;
+ lsb = Integer.MAX_VALUE;
+ posInf = false;
+ negInf = false;
+ nan = false;
+ for (int i = 0; i < components.length; ++i) {
+ if (Double.isInfinite(components[i])) {
+ if (components[i] < 0) {
+ negInf = true;
+ } else {
+ posInf = true;
+ }
+ } else if (Double.isNaN(components[i])) {
+ nan = true;
+ } else {
+ final long b = Double.doubleToLongBits(components[i]);
+ final long m = mantissa(b);
+ if (m != 0) {
+ final int e = exponent(b);
+ msb = FastMath.max(msb, e + computeMSB(m));
+ lsb = FastMath.min(lsb, e + computeLSB(m));
+ }
+ }
+ }
+
+ if (posInf && negInf) {
+ // instance cannot be sorted logically
+ posInf = false;
+ negInf = false;
+ nan = true;
+ }
+
+ if (lsb <= msb) {
+ // encode the T-upple with the specified offset
+ encode(msb + 16);
+ } else {
+ encoding = new long[] {
+ 0x0L
+ };
+ }
+
+ }
+
+ /** Encode the T-uple with a given offset.
+ * @param minOffset minimal scale of the offset to add to all
+ * components (must be greater than the MSBs of all components)
+ */
+ private void encode(final int minOffset) {
+
+ // choose an offset with some margins
+ offset = minOffset + 31;
+ offset -= offset % 32;
+
+ if ((encoding != null) && (encoding.length == 1) && (encoding[0] == 0x0L)) {
+ // the components are all zeroes
+ return;
+ }
+
+ // allocate an integer array to encode the components (we use only
+ // 63 bits per element because there is no unsigned long in Java)
+ final int neededBits = offset + 1 - lsb;
+ final int neededLongs = (neededBits + 62) / 63;
+ encoding = new long[components.length * neededLongs];
+
+ // mix the bits from all components
+ int eIndex = 0;
+ int shift = 62;
+ long word = 0x0L;
+ for (int k = offset; eIndex < encoding.length; --k) {
+ for (int vIndex = 0; vIndex < components.length; ++vIndex) {
+ if (getBit(vIndex, k) != 0) {
+ word |= 0x1L << shift;
+ }
+ if (shift-- == 0) {
+ encoding[eIndex++] = word;
+ word = 0x0L;
+ shift = 62;
+ }
+ }
+ }
+
+ }
+
+ /** Compares this ordered T-uple with the specified object.
+
+ * <p>The ordering method is detailed in the general description of
+ * the class. Its main property is to be consistent with distance:
+ * geometrically close T-uples stay close to each other when stored
+ * in a sorted collection using this comparison method.</p>
+
+ * <p>T-uples with negative infinite, positive infinite are sorted
+ * logically.</p>
+
+ * <p>Some arbitrary choices have been made to handle specific
+ * cases. The rationale for these choices is to keep
+ * <em>normal</em> and consistent T-uples together.</p>
+ * <ul>
+ * <li>instances with different dimensions are sorted according to
+ * their dimension regardless of their components values</li>
+ * <li>instances with {@code Double.NaN} components are sorted
+ * after all other ones (evan after instances with positive infinite
+ * components</li>
+ * <li>instances with both positive and negative infinite components
+ * are considered as if they had {@code Double.NaN}
+ * components</li>
+ * </ul>
+
+ * @param ot T-uple to compare instance with
+ * @return a negative integer if the instance is less than the
+ * object, zero if they are equal, or a positive integer if the
+ * instance is greater than the object
+
+ */
+ public int compareTo(final OrderedTuple ot) {
+ if (components.length == ot.components.length) {
+ if (nan) {
+ return +1;
+ } else if (ot.nan) {
+ return -1;
+ } else if (negInf || ot.posInf) {
+ return -1;
+ } else if (posInf || ot.negInf) {
+ return +1;
+ } else {
+
+ if (offset < ot.offset) {
+ encode(ot.offset);
+ } else if (offset > ot.offset) {
+ ot.encode(offset);
+ }
+
+ final int limit = FastMath.min(encoding.length, ot.encoding.length);
+ for (int i = 0; i < limit; ++i) {
+ if (encoding[i] < ot.encoding[i]) {
+ return -1;
+ } else if (encoding[i] > ot.encoding[i]) {
+ return +1;
+ }
+ }
+
+ if (encoding.length < ot.encoding.length) {
+ return -1;
+ } else if (encoding.length > ot.encoding.length) {
+ return +1;
+ } else {
+ return 0;
+ }
+
+ }
+ }
+
+ return components.length - ot.components.length;
+
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public boolean equals(final Object other) {
+ if (this == other) {
+ return true;
+ } else if (other instanceof OrderedTuple) {
+ return compareTo((OrderedTuple) other) == 0;
+ } else {
+ return false;
+ }
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public int hashCode() {
+ // the following constants are arbitrary small primes
+ final int multiplier = 37;
+ final int trueHash = 97;
+ final int falseHash = 71;
+
+ // hash fields and combine them
+ // (we rely on the multiplier to have different combined weights
+ // for all int fields and all boolean fields)
+ int hash = Arrays.hashCode(components);
+ hash = hash * multiplier + offset;
+ hash = hash * multiplier + lsb;
+ hash = hash * multiplier + (posInf ? trueHash : falseHash);
+ hash = hash * multiplier + (negInf ? trueHash : falseHash);
+ hash = hash * multiplier + (nan ? trueHash : falseHash);
+
+ return hash;
+
+ }
+
+ /** Get the components array.
+ * @return array containing the T-uple components
+ */
+ public double[] getComponents() {
+ return components.clone();
+ }
+
+ /** Extract the sign from the bits of a double.
+ * @param bits binary representation of the double
+ * @return sign bit (zero if positive, non zero if negative)
+ */
+ private static long sign(final long bits) {
+ return bits & SIGN_MASK;
+ }
+
+ /** Extract the exponent from the bits of a double.
+ * @param bits binary representation of the double
+ * @return exponent
+ */
+ private static int exponent(final long bits) {
+ return ((int) ((bits & EXPONENT_MASK) >> 52)) - 1075;
+ }
+
+ /** Extract the mantissa from the bits of a double.
+ * @param bits binary representation of the double
+ * @return mantissa
+ */
+ private static long mantissa(final long bits) {
+ return ((bits & EXPONENT_MASK) == 0) ?
+ ((bits & MANTISSA_MASK) << 1) : // subnormal number
+ (IMPLICIT_ONE | (bits & MANTISSA_MASK)); // normal number
+ }
+
+ /** Compute the most significant bit of a long.
+ * @param l long from which the most significant bit is requested
+ * @return scale of the most significant bit of {@code l},
+ * or 0 if {@code l} is zero
+ * @see #computeLSB
+ */
+ private static int computeMSB(final long l) {
+
+ long ll = l;
+ long mask = 0xffffffffL;
+ int scale = 32;
+ int msb = 0;
+
+ while (scale != 0) {
+ if ((ll & mask) != ll) {
+ msb |= scale;
+ ll >>= scale;
+ }
+ scale >>= 1;
+ mask >>= scale;
+ }
+
+ return msb;
+
+ }
+
+ /** Compute the least significant bit of a long.
+ * @param l long from which the least significant bit is requested
+ * @return scale of the least significant bit of {@code l},
+ * or 63 if {@code l} is zero
+ * @see #computeMSB
+ */
+ private static int computeLSB(final long l) {
+
+ long ll = l;
+ long mask = 0xffffffff00000000L;
+ int scale = 32;
+ int lsb = 0;
+
+ while (scale != 0) {
+ if ((ll & mask) == ll) {
+ lsb |= scale;
+ ll >>= scale;
+ }
+ scale >>= 1;
+ mask >>= scale;
+ }
+
+ return lsb;
+
+ }
+
+ /** Get a bit from the mantissa of a double.
+ * @param i index of the component
+ * @param k scale of the requested bit
+ * @return the specified bit (either 0 or 1), after the offset has
+ * been added to the double
+ */
+ private int getBit(final int i, final int k) {
+ final long bits = Double.doubleToLongBits(components[i]);
+ final int e = exponent(bits);
+ if ((k < e) || (k > offset)) {
+ return 0;
+ } else if (k == offset) {
+ return (sign(bits) == 0L) ? 1 : 0;
+ } else if (k > (e + 52)) {
+ return (sign(bits) == 0L) ? 0 : 1;
+ } else {
+ final long m = (sign(bits) == 0L) ? mantissa(bits) : -mantissa(bits);
+ return (int) ((m >> (k - e)) & 0x1L);
+ }
+ }
+
+}
diff --git a/src/main/java/org/apache/commons/math3/geometry/partitioning/utilities/doc-files/OrderedTuple.png b/src/main/java/org/apache/commons/math3/geometry/partitioning/utilities/doc-files/OrderedTuple.png
new file mode 100644
index 0000000..4eca233
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/geometry/partitioning/utilities/doc-files/OrderedTuple.png
Binary files differ
diff --git a/src/main/java/org/apache/commons/math3/geometry/partitioning/utilities/package-info.java b/src/main/java/org/apache/commons/math3/geometry/partitioning/utilities/package-info.java
new file mode 100644
index 0000000..31f57f1
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/geometry/partitioning/utilities/package-info.java
@@ -0,0 +1,24 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/**
+ *
+ * <p>
+ * This package provides multidimensional ordering features for partitioning.
+ * </p>
+ *
+ */
+package org.apache.commons.math3.geometry.partitioning.utilities;
diff --git a/src/main/java/org/apache/commons/math3/geometry/spherical/oned/Arc.java b/src/main/java/org/apache/commons/math3/geometry/spherical/oned/Arc.java
new file mode 100644
index 0000000..af0388e
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/geometry/spherical/oned/Arc.java
@@ -0,0 +1,132 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math3.geometry.spherical.oned;
+
+import org.apache.commons.math3.exception.NumberIsTooLargeException;
+import org.apache.commons.math3.exception.util.LocalizedFormats;
+import org.apache.commons.math3.geometry.partitioning.Region.Location;
+import org.apache.commons.math3.util.FastMath;
+import org.apache.commons.math3.util.MathUtils;
+import org.apache.commons.math3.util.Precision;
+
+
+/** This class represents an arc on a circle.
+ * @see ArcsSet
+ * @since 3.3
+ */
+public class Arc {
+
+ /** The lower angular bound of the arc. */
+ private final double lower;
+
+ /** The upper angular bound of the arc. */
+ private final double upper;
+
+ /** Middle point of the arc. */
+ private final double middle;
+
+ /** Tolerance below which angles are considered identical. */
+ private final double tolerance;
+
+ /** Simple constructor.
+ * <p>
+ * If either {@code lower} is equals to {@code upper} or
+ * the interval exceeds \( 2 \pi \), the arc is considered
+ * to be the full circle and its initial defining boundaries
+ * will be forgotten. {@code lower} is not allowed to be
+ * greater than {@code upper} (an exception is thrown in this case).
+ * {@code lower} will be canonicalized between 0 and \( 2 \pi \), and
+ * upper shifted accordingly, so the {@link #getInf()} and {@link #getSup()}
+ * may not return the value used at instance construction.
+ * </p>
+ * @param lower lower angular bound of the arc
+ * @param upper upper angular bound of the arc
+ * @param tolerance tolerance below which angles are considered identical
+ * @exception NumberIsTooLargeException if lower is greater than upper
+ */
+ public Arc(final double lower, final double upper, final double tolerance)
+ throws NumberIsTooLargeException {
+ this.tolerance = tolerance;
+ if (Precision.equals(lower, upper, 0) || (upper - lower) >= MathUtils.TWO_PI) {
+ // the arc must cover the whole circle
+ this.lower = 0;
+ this.upper = MathUtils.TWO_PI;
+ this.middle = FastMath.PI;
+ } else if (lower <= upper) {
+ this.lower = MathUtils.normalizeAngle(lower, FastMath.PI);
+ this.upper = this.lower + (upper - lower);
+ this.middle = 0.5 * (this.lower + this.upper);
+ } else {
+ throw new NumberIsTooLargeException(LocalizedFormats.ENDPOINTS_NOT_AN_INTERVAL,
+ lower, upper, true);
+ }
+ }
+
+ /** Get the lower angular bound of the arc.
+ * @return lower angular bound of the arc,
+ * always between 0 and \( 2 \pi \)
+ */
+ public double getInf() {
+ return lower;
+ }
+
+ /** Get the upper angular bound of the arc.
+ * @return upper angular bound of the arc,
+ * always between {@link #getInf()} and {@link #getInf()} \( + 2 \pi \)
+ */
+ public double getSup() {
+ return upper;
+ }
+
+ /** Get the angular size of the arc.
+ * @return angular size of the arc
+ */
+ public double getSize() {
+ return upper - lower;
+ }
+
+ /** Get the barycenter of the arc.
+ * @return barycenter of the arc
+ */
+ public double getBarycenter() {
+ return middle;
+ }
+
+ /** Get the tolerance below which angles are considered identical.
+ * @return tolerance below which angles are considered identical
+ */
+ public double getTolerance() {
+ return tolerance;
+ }
+
+ /** Check a point with respect to the arc.
+ * @param point point to check
+ * @return a code representing the point status: either {@link
+ * Location#INSIDE}, {@link Location#OUTSIDE} or {@link Location#BOUNDARY}
+ */
+ public Location checkPoint(final double point) {
+ final double normalizedPoint = MathUtils.normalizeAngle(point, middle);
+ if (normalizedPoint < lower - tolerance || normalizedPoint > upper + tolerance) {
+ return Location.OUTSIDE;
+ } else if (normalizedPoint > lower + tolerance && normalizedPoint < upper - tolerance) {
+ return Location.INSIDE;
+ } else {
+ return (getSize() >= MathUtils.TWO_PI - tolerance) ? Location.INSIDE : Location.BOUNDARY;
+ }
+ }
+
+}
diff --git a/src/main/java/org/apache/commons/math3/geometry/spherical/oned/ArcsSet.java b/src/main/java/org/apache/commons/math3/geometry/spherical/oned/ArcsSet.java
new file mode 100644
index 0000000..0a00aa7
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/geometry/spherical/oned/ArcsSet.java
@@ -0,0 +1,955 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math3.geometry.spherical.oned;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.List;
+import java.util.NoSuchElementException;
+
+import org.apache.commons.math3.exception.MathIllegalArgumentException;
+import org.apache.commons.math3.exception.MathInternalError;
+import org.apache.commons.math3.exception.NumberIsTooLargeException;
+import org.apache.commons.math3.exception.util.LocalizedFormats;
+import org.apache.commons.math3.geometry.Point;
+import org.apache.commons.math3.geometry.partitioning.AbstractRegion;
+import org.apache.commons.math3.geometry.partitioning.BSPTree;
+import org.apache.commons.math3.geometry.partitioning.BoundaryProjection;
+import org.apache.commons.math3.geometry.partitioning.Side;
+import org.apache.commons.math3.geometry.partitioning.SubHyperplane;
+import org.apache.commons.math3.util.FastMath;
+import org.apache.commons.math3.util.MathUtils;
+import org.apache.commons.math3.util.Precision;
+
+/** This class represents a region of a circle: a set of arcs.
+ * <p>
+ * Note that due to the wrapping around \(2 \pi\), barycenter is
+ * ill-defined here. It was defined only in order to fulfill
+ * the requirements of the {@link
+ * org.apache.commons.math3.geometry.partitioning.Region Region}
+ * interface, but its use is discouraged.
+ * </p>
+ * @since 3.3
+ */
+public class ArcsSet extends AbstractRegion<Sphere1D, Sphere1D> implements Iterable<double[]> {
+
+ /** Build an arcs set representing the whole circle.
+ * @param tolerance tolerance below which close sub-arcs are merged together
+ */
+ public ArcsSet(final double tolerance) {
+ super(tolerance);
+ }
+
+ /** Build an arcs set corresponding to a single arc.
+ * <p>
+ * If either {@code lower} is equals to {@code upper} or
+ * the interval exceeds \( 2 \pi \), the arc is considered
+ * to be the full circle and its initial defining boundaries
+ * will be forgotten. {@code lower} is not allowed to be greater
+ * than {@code upper} (an exception is thrown in this case).
+ * </p>
+ * @param lower lower bound of the arc
+ * @param upper upper bound of the arc
+ * @param tolerance tolerance below which close sub-arcs are merged together
+ * @exception NumberIsTooLargeException if lower is greater than upper
+ */
+ public ArcsSet(final double lower, final double upper, final double tolerance)
+ throws NumberIsTooLargeException {
+ super(buildTree(lower, upper, tolerance), tolerance);
+ }
+
+ /** Build an arcs set from an inside/outside BSP tree.
+ * <p>The leaf nodes of the BSP tree <em>must</em> have a
+ * {@code Boolean} attribute representing the inside status of
+ * the corresponding cell (true for inside cells, false for outside
+ * cells). In order to avoid building too many small objects, it is
+ * recommended to use the predefined constants
+ * {@code Boolean.TRUE} and {@code Boolean.FALSE}</p>
+ * @param tree inside/outside BSP tree representing the arcs set
+ * @param tolerance tolerance below which close sub-arcs are merged together
+ * @exception InconsistentStateAt2PiWrapping if the tree leaf nodes are not
+ * consistent across the \( 0, 2 \pi \) crossing
+ */
+ public ArcsSet(final BSPTree<Sphere1D> tree, final double tolerance)
+ throws InconsistentStateAt2PiWrapping {
+ super(tree, tolerance);
+ check2PiConsistency();
+ }
+
+ /** Build an arcs set from a Boundary REPresentation (B-rep).
+ * <p>The boundary is provided as a collection of {@link
+ * SubHyperplane sub-hyperplanes}. Each sub-hyperplane has the
+ * interior part of the region on its minus side and the exterior on
+ * its plus side.</p>
+ * <p>The boundary elements can be in any order, and can form
+ * several non-connected sets (like for example polygons with holes
+ * or a set of disjoints polyhedrons considered as a whole). In
+ * fact, the elements do not even need to be connected together
+ * (their topological connections are not used here). However, if the
+ * boundary does not really separate an inside open from an outside
+ * open (open having here its topological meaning), then subsequent
+ * calls to the {@link
+ * org.apache.commons.math3.geometry.partitioning.Region#checkPoint(org.apache.commons.math3.geometry.Point)
+ * checkPoint} method will not be meaningful anymore.</p>
+ * <p>If the boundary is empty, the region will represent the whole
+ * space.</p>
+ * @param boundary collection of boundary elements
+ * @param tolerance tolerance below which close sub-arcs are merged together
+ * @exception InconsistentStateAt2PiWrapping if the tree leaf nodes are not
+ * consistent across the \( 0, 2 \pi \) crossing
+ */
+ public ArcsSet(final Collection<SubHyperplane<Sphere1D>> boundary, final double tolerance)
+ throws InconsistentStateAt2PiWrapping {
+ super(boundary, tolerance);
+ check2PiConsistency();
+ }
+
+ /** Build an inside/outside tree representing a single arc.
+ * @param lower lower angular bound of the arc
+ * @param upper upper angular bound of the arc
+ * @param tolerance tolerance below which close sub-arcs are merged together
+ * @return the built tree
+ * @exception NumberIsTooLargeException if lower is greater than upper
+ */
+ private static BSPTree<Sphere1D> buildTree(final double lower, final double upper,
+ final double tolerance)
+ throws NumberIsTooLargeException {
+
+ if (Precision.equals(lower, upper, 0) || (upper - lower) >= MathUtils.TWO_PI) {
+ // the tree must cover the whole circle
+ return new BSPTree<Sphere1D>(Boolean.TRUE);
+ } else if (lower > upper) {
+ throw new NumberIsTooLargeException(LocalizedFormats.ENDPOINTS_NOT_AN_INTERVAL,
+ lower, upper, true);
+ }
+
+ // this is a regular arc, covering only part of the circle
+ final double normalizedLower = MathUtils.normalizeAngle(lower, FastMath.PI);
+ final double normalizedUpper = normalizedLower + (upper - lower);
+ final SubHyperplane<Sphere1D> lowerCut =
+ new LimitAngle(new S1Point(normalizedLower), false, tolerance).wholeHyperplane();
+
+ if (normalizedUpper <= MathUtils.TWO_PI) {
+ // simple arc starting after 0 and ending before 2 \pi
+ final SubHyperplane<Sphere1D> upperCut =
+ new LimitAngle(new S1Point(normalizedUpper), true, tolerance).wholeHyperplane();
+ return new BSPTree<Sphere1D>(lowerCut,
+ new BSPTree<Sphere1D>(Boolean.FALSE),
+ new BSPTree<Sphere1D>(upperCut,
+ new BSPTree<Sphere1D>(Boolean.FALSE),
+ new BSPTree<Sphere1D>(Boolean.TRUE),
+ null),
+ null);
+ } else {
+ // arc wrapping around 2 \pi
+ final SubHyperplane<Sphere1D> upperCut =
+ new LimitAngle(new S1Point(normalizedUpper - MathUtils.TWO_PI), true, tolerance).wholeHyperplane();
+ return new BSPTree<Sphere1D>(lowerCut,
+ new BSPTree<Sphere1D>(upperCut,
+ new BSPTree<Sphere1D>(Boolean.FALSE),
+ new BSPTree<Sphere1D>(Boolean.TRUE),
+ null),
+ new BSPTree<Sphere1D>(Boolean.TRUE),
+ null);
+ }
+
+ }
+
+ /** Check consistency.
+ * @exception InconsistentStateAt2PiWrapping if the tree leaf nodes are not
+ * consistent across the \( 0, 2 \pi \) crossing
+ */
+ private void check2PiConsistency() throws InconsistentStateAt2PiWrapping {
+
+ // start search at the tree root
+ BSPTree<Sphere1D> root = getTree(false);
+ if (root.getCut() == null) {
+ return;
+ }
+
+ // find the inside/outside state before the smallest internal node
+ final Boolean stateBefore = (Boolean) getFirstLeaf(root).getAttribute();
+
+ // find the inside/outside state after the largest internal node
+ final Boolean stateAfter = (Boolean) getLastLeaf(root).getAttribute();
+
+ if (stateBefore ^ stateAfter) {
+ throw new InconsistentStateAt2PiWrapping();
+ }
+
+ }
+
+ /** Get the first leaf node of a tree.
+ * @param root tree root
+ * @return first leaf node (i.e. node corresponding to the region just after 0.0 radians)
+ */
+ private BSPTree<Sphere1D> getFirstLeaf(final BSPTree<Sphere1D> root) {
+
+ if (root.getCut() == null) {
+ return root;
+ }
+
+ // find the smallest internal node
+ BSPTree<Sphere1D> smallest = null;
+ for (BSPTree<Sphere1D> n = root; n != null; n = previousInternalNode(n)) {
+ smallest = n;
+ }
+
+ return leafBefore(smallest);
+
+ }
+
+ /** Get the last leaf node of a tree.
+ * @param root tree root
+ * @return last leaf node (i.e. node corresponding to the region just before \( 2 \pi \) radians)
+ */
+ private BSPTree<Sphere1D> getLastLeaf(final BSPTree<Sphere1D> root) {
+
+ if (root.getCut() == null) {
+ return root;
+ }
+
+ // find the largest internal node
+ BSPTree<Sphere1D> largest = null;
+ for (BSPTree<Sphere1D> n = root; n != null; n = nextInternalNode(n)) {
+ largest = n;
+ }
+
+ return leafAfter(largest);
+
+ }
+
+ /** Get the node corresponding to the first arc start.
+ * @return smallest internal node (i.e. first after 0.0 radians, in trigonometric direction),
+ * or null if there are no internal nodes (i.e. the set is either empty or covers the full circle)
+ */
+ private BSPTree<Sphere1D> getFirstArcStart() {
+
+ // start search at the tree root
+ BSPTree<Sphere1D> node = getTree(false);
+ if (node.getCut() == null) {
+ return null;
+ }
+
+ // walk tree until we find the smallest internal node
+ node = getFirstLeaf(node).getParent();
+
+ // walk tree until we find an arc start
+ while (node != null && !isArcStart(node)) {
+ node = nextInternalNode(node);
+ }
+
+ return node;
+
+ }
+
+ /** Check if an internal node corresponds to the start angle of an arc.
+ * @param node internal node to check
+ * @return true if the node corresponds to the start angle of an arc
+ */
+ private boolean isArcStart(final BSPTree<Sphere1D> node) {
+
+ if ((Boolean) leafBefore(node).getAttribute()) {
+ // it has an inside cell before it, it may end an arc but not start it
+ return false;
+ }
+
+ if (!(Boolean) leafAfter(node).getAttribute()) {
+ // it has an outside cell after it, it is a dummy cut away from real arcs
+ return false;
+ }
+
+ // the cell has an outside before and an inside after it
+ // it is the start of an arc
+ return true;
+
+ }
+
+ /** Check if an internal node corresponds to the end angle of an arc.
+ * @param node internal node to check
+ * @return true if the node corresponds to the end angle of an arc
+ */
+ private boolean isArcEnd(final BSPTree<Sphere1D> node) {
+
+ if (!(Boolean) leafBefore(node).getAttribute()) {
+ // it has an outside cell before it, it may start an arc but not end it
+ return false;
+ }
+
+ if ((Boolean) leafAfter(node).getAttribute()) {
+ // it has an inside cell after it, it is a dummy cut in the middle of an arc
+ return false;
+ }
+
+ // the cell has an inside before and an outside after it
+ // it is the end of an arc
+ return true;
+
+ }
+
+ /** Get the next internal node.
+ * @param node current internal node
+ * @return next internal node in trigonometric order, or null
+ * if this is the last internal node
+ */
+ private BSPTree<Sphere1D> nextInternalNode(BSPTree<Sphere1D> node) {
+
+ if (childAfter(node).getCut() != null) {
+ // the next node is in the sub-tree
+ return leafAfter(node).getParent();
+ }
+
+ // there is nothing left deeper in the tree, we backtrack
+ while (isAfterParent(node)) {
+ node = node.getParent();
+ }
+ return node.getParent();
+
+ }
+
+ /** Get the previous internal node.
+ * @param node current internal node
+ * @return previous internal node in trigonometric order, or null
+ * if this is the first internal node
+ */
+ private BSPTree<Sphere1D> previousInternalNode(BSPTree<Sphere1D> node) {
+
+ if (childBefore(node).getCut() != null) {
+ // the next node is in the sub-tree
+ return leafBefore(node).getParent();
+ }
+
+ // there is nothing left deeper in the tree, we backtrack
+ while (isBeforeParent(node)) {
+ node = node.getParent();
+ }
+ return node.getParent();
+
+ }
+
+ /** Find the leaf node just before an internal node.
+ * @param node internal node at which the sub-tree starts
+ * @return leaf node just before the internal node
+ */
+ private BSPTree<Sphere1D> leafBefore(BSPTree<Sphere1D> node) {
+
+ node = childBefore(node);
+ while (node.getCut() != null) {
+ node = childAfter(node);
+ }
+
+ return node;
+
+ }
+
+ /** Find the leaf node just after an internal node.
+ * @param node internal node at which the sub-tree starts
+ * @return leaf node just after the internal node
+ */
+ private BSPTree<Sphere1D> leafAfter(BSPTree<Sphere1D> node) {
+
+ node = childAfter(node);
+ while (node.getCut() != null) {
+ node = childBefore(node);
+ }
+
+ return node;
+
+ }
+
+ /** Check if a node is the child before its parent in trigonometric order.
+ * @param node child node considered
+ * @return true is the node has a parent end is before it in trigonometric order
+ */
+ private boolean isBeforeParent(final BSPTree<Sphere1D> node) {
+ final BSPTree<Sphere1D> parent = node.getParent();
+ if (parent == null) {
+ return false;
+ } else {
+ return node == childBefore(parent);
+ }
+ }
+
+ /** Check if a node is the child after its parent in trigonometric order.
+ * @param node child node considered
+ * @return true is the node has a parent end is after it in trigonometric order
+ */
+ private boolean isAfterParent(final BSPTree<Sphere1D> node) {
+ final BSPTree<Sphere1D> parent = node.getParent();
+ if (parent == null) {
+ return false;
+ } else {
+ return node == childAfter(parent);
+ }
+ }
+
+ /** Find the child node just before an internal node.
+ * @param node internal node at which the sub-tree starts
+ * @return child node just before the internal node
+ */
+ private BSPTree<Sphere1D> childBefore(BSPTree<Sphere1D> node) {
+ if (isDirect(node)) {
+ // smaller angles are on minus side, larger angles are on plus side
+ return node.getMinus();
+ } else {
+ // smaller angles are on plus side, larger angles are on minus side
+ return node.getPlus();
+ }
+ }
+
+ /** Find the child node just after an internal node.
+ * @param node internal node at which the sub-tree starts
+ * @return child node just after the internal node
+ */
+ private BSPTree<Sphere1D> childAfter(BSPTree<Sphere1D> node) {
+ if (isDirect(node)) {
+ // smaller angles are on minus side, larger angles are on plus side
+ return node.getPlus();
+ } else {
+ // smaller angles are on plus side, larger angles are on minus side
+ return node.getMinus();
+ }
+ }
+
+ /** Check if an internal node has a direct limit angle.
+ * @param node internal node to check
+ * @return true if the limit angle is direct
+ */
+ private boolean isDirect(final BSPTree<Sphere1D> node) {
+ return ((LimitAngle) node.getCut().getHyperplane()).isDirect();
+ }
+
+ /** Get the limit angle of an internal node.
+ * @param node internal node to check
+ * @return limit angle
+ */
+ private double getAngle(final BSPTree<Sphere1D> node) {
+ return ((LimitAngle) node.getCut().getHyperplane()).getLocation().getAlpha();
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public ArcsSet buildNew(final BSPTree<Sphere1D> tree) {
+ return new ArcsSet(tree, getTolerance());
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ protected void computeGeometricalProperties() {
+ if (getTree(false).getCut() == null) {
+ setBarycenter(S1Point.NaN);
+ setSize(((Boolean) getTree(false).getAttribute()) ? MathUtils.TWO_PI : 0);
+ } else {
+ double size = 0.0;
+ double sum = 0.0;
+ for (final double[] a : this) {
+ final double length = a[1] - a[0];
+ size += length;
+ sum += length * (a[0] + a[1]);
+ }
+ setSize(size);
+ if (Precision.equals(size, MathUtils.TWO_PI, 0)) {
+ setBarycenter(S1Point.NaN);
+ } else if (size >= Precision.SAFE_MIN) {
+ setBarycenter(new S1Point(sum / (2 * size)));
+ } else {
+ final LimitAngle limit = (LimitAngle) getTree(false).getCut().getHyperplane();
+ setBarycenter(limit.getLocation());
+ }
+ }
+ }
+
+ /** {@inheritDoc}
+ * @since 3.3
+ */
+ @Override
+ public BoundaryProjection<Sphere1D> projectToBoundary(final Point<Sphere1D> point) {
+
+ // get position of test point
+ final double alpha = ((S1Point) point).getAlpha();
+
+ boolean wrapFirst = false;
+ double first = Double.NaN;
+ double previous = Double.NaN;
+ for (final double[] a : this) {
+
+ if (Double.isNaN(first)) {
+ // remember the first angle in case we need it later
+ first = a[0];
+ }
+
+ if (!wrapFirst) {
+ if (alpha < a[0]) {
+ // the test point lies between the previous and the current arcs
+ // offset will be positive
+ if (Double.isNaN(previous)) {
+ // we need to wrap around the circle
+ wrapFirst = true;
+ } else {
+ final double previousOffset = alpha - previous;
+ final double currentOffset = a[0] - alpha;
+ if (previousOffset < currentOffset) {
+ return new BoundaryProjection<Sphere1D>(point, new S1Point(previous), previousOffset);
+ } else {
+ return new BoundaryProjection<Sphere1D>(point, new S1Point(a[0]), currentOffset);
+ }
+ }
+ } else if (alpha <= a[1]) {
+ // the test point lies within the current arc
+ // offset will be negative
+ final double offset0 = a[0] - alpha;
+ final double offset1 = alpha - a[1];
+ if (offset0 < offset1) {
+ return new BoundaryProjection<Sphere1D>(point, new S1Point(a[1]), offset1);
+ } else {
+ return new BoundaryProjection<Sphere1D>(point, new S1Point(a[0]), offset0);
+ }
+ }
+ }
+ previous = a[1];
+ }
+
+ if (Double.isNaN(previous)) {
+
+ // there are no points at all in the arcs set
+ return new BoundaryProjection<Sphere1D>(point, null, MathUtils.TWO_PI);
+
+ } else {
+
+ // the test point if before first arc and after last arc,
+ // somewhere around the 0/2 \pi crossing
+ if (wrapFirst) {
+ // the test point is between 0 and first
+ final double previousOffset = alpha - (previous - MathUtils.TWO_PI);
+ final double currentOffset = first - alpha;
+ if (previousOffset < currentOffset) {
+ return new BoundaryProjection<Sphere1D>(point, new S1Point(previous), previousOffset);
+ } else {
+ return new BoundaryProjection<Sphere1D>(point, new S1Point(first), currentOffset);
+ }
+ } else {
+ // the test point is between last and 2\pi
+ final double previousOffset = alpha - previous;
+ final double currentOffset = first + MathUtils.TWO_PI - alpha;
+ if (previousOffset < currentOffset) {
+ return new BoundaryProjection<Sphere1D>(point, new S1Point(previous), previousOffset);
+ } else {
+ return new BoundaryProjection<Sphere1D>(point, new S1Point(first), currentOffset);
+ }
+ }
+
+ }
+
+ }
+
+ /** Build an ordered list of arcs representing the instance.
+ * <p>This method builds this arcs set as an ordered list of
+ * {@link Arc Arc} elements. An empty tree will build an empty list
+ * while a tree representing the whole circle will build a one
+ * element list with bounds set to \( 0 and 2 \pi \).</p>
+ * @return a new ordered list containing {@link Arc Arc} elements
+ */
+ public List<Arc> asList() {
+ final List<Arc> list = new ArrayList<Arc>();
+ for (final double[] a : this) {
+ list.add(new Arc(a[0], a[1], getTolerance()));
+ }
+ return list;
+ }
+
+ /** {@inheritDoc}
+ * <p>
+ * The iterator returns the limit angles pairs of sub-arcs in trigonometric order.
+ * </p>
+ * <p>
+ * The iterator does <em>not</em> support the optional {@code remove} operation.
+ * </p>
+ */
+ public Iterator<double[]> iterator() {
+ return new SubArcsIterator();
+ }
+
+ /** Local iterator for sub-arcs. */
+ private class SubArcsIterator implements Iterator<double[]> {
+
+ /** Start of the first arc. */
+ private final BSPTree<Sphere1D> firstStart;
+
+ /** Current node. */
+ private BSPTree<Sphere1D> current;
+
+ /** Sub-arc no yet returned. */
+ private double[] pending;
+
+ /** Simple constructor.
+ */
+ SubArcsIterator() {
+
+ firstStart = getFirstArcStart();
+ current = firstStart;
+
+ if (firstStart == null) {
+ // all the leaf tree nodes share the same inside/outside status
+ if ((Boolean) getFirstLeaf(getTree(false)).getAttribute()) {
+ // it is an inside node, it represents the full circle
+ pending = new double[] {
+ 0, MathUtils.TWO_PI
+ };
+ } else {
+ pending = null;
+ }
+ } else {
+ selectPending();
+ }
+ }
+
+ /** Walk the tree to select the pending sub-arc.
+ */
+ private void selectPending() {
+
+ // look for the start of the arc
+ BSPTree<Sphere1D> start = current;
+ while (start != null && !isArcStart(start)) {
+ start = nextInternalNode(start);
+ }
+
+ if (start == null) {
+ // we have exhausted the iterator
+ current = null;
+ pending = null;
+ return;
+ }
+
+ // look for the end of the arc
+ BSPTree<Sphere1D> end = start;
+ while (end != null && !isArcEnd(end)) {
+ end = nextInternalNode(end);
+ }
+
+ if (end != null) {
+
+ // we have identified the arc
+ pending = new double[] {
+ getAngle(start), getAngle(end)
+ };
+
+ // prepare search for next arc
+ current = end;
+
+ } else {
+
+ // the final arc wraps around 2\pi, its end is before the first start
+ end = firstStart;
+ while (end != null && !isArcEnd(end)) {
+ end = previousInternalNode(end);
+ }
+ if (end == null) {
+ // this should never happen
+ throw new MathInternalError();
+ }
+
+ // we have identified the last arc
+ pending = new double[] {
+ getAngle(start), getAngle(end) + MathUtils.TWO_PI
+ };
+
+ // there won't be any other arcs
+ current = null;
+
+ }
+
+ }
+
+ /** {@inheritDoc} */
+ public boolean hasNext() {
+ return pending != null;
+ }
+
+ /** {@inheritDoc} */
+ public double[] next() {
+ if (pending == null) {
+ throw new NoSuchElementException();
+ }
+ final double[] next = pending;
+ selectPending();
+ return next;
+ }
+
+ /** {@inheritDoc} */
+ public void remove() {
+ throw new UnsupportedOperationException();
+ }
+
+ }
+
+ /** Compute the relative position of the instance with respect
+ * to an arc.
+ * <p>
+ * The {@link Side#MINUS} side of the arc is the one covered by the arc.
+ * </p>
+ * @param arc arc to check instance against
+ * @return one of {@link Side#PLUS}, {@link Side#MINUS}, {@link Side#BOTH}
+ * or {@link Side#HYPER}
+ * @deprecated as of 3.6, replaced with {@link #split(Arc)}.{@link Split#getSide()}
+ */
+ @Deprecated
+ public Side side(final Arc arc) {
+ return split(arc).getSide();
+ }
+
+ /** Split the instance in two parts by an arc.
+ * @param arc splitting arc
+ * @return an object containing both the part of the instance
+ * on the plus side of the arc and the part of the
+ * instance on the minus side of the arc
+ */
+ public Split split(final Arc arc) {
+
+ final List<Double> minus = new ArrayList<Double>();
+ final List<Double> plus = new ArrayList<Double>();
+
+ final double reference = FastMath.PI + arc.getInf();
+ final double arcLength = arc.getSup() - arc.getInf();
+
+ for (final double[] a : this) {
+ final double syncedStart = MathUtils.normalizeAngle(a[0], reference) - arc.getInf();
+ final double arcOffset = a[0] - syncedStart;
+ final double syncedEnd = a[1] - arcOffset;
+ if (syncedStart < arcLength) {
+ // the start point a[0] is in the minus part of the arc
+ minus.add(a[0]);
+ if (syncedEnd > arcLength) {
+ // the end point a[1] is past the end of the arc
+ // so we leave the minus part and enter the plus part
+ final double minusToPlus = arcLength + arcOffset;
+ minus.add(minusToPlus);
+ plus.add(minusToPlus);
+ if (syncedEnd > MathUtils.TWO_PI) {
+ // in fact the end point a[1] goes far enough that we
+ // leave the plus part of the arc and enter the minus part again
+ final double plusToMinus = MathUtils.TWO_PI + arcOffset;
+ plus.add(plusToMinus);
+ minus.add(plusToMinus);
+ minus.add(a[1]);
+ } else {
+ // the end point a[1] is in the plus part of the arc
+ plus.add(a[1]);
+ }
+ } else {
+ // the end point a[1] is in the minus part of the arc
+ minus.add(a[1]);
+ }
+ } else {
+ // the start point a[0] is in the plus part of the arc
+ plus.add(a[0]);
+ if (syncedEnd > MathUtils.TWO_PI) {
+ // the end point a[1] wraps around to the start of the arc
+ // so we leave the plus part and enter the minus part
+ final double plusToMinus = MathUtils.TWO_PI + arcOffset;
+ plus.add(plusToMinus);
+ minus.add(plusToMinus);
+ if (syncedEnd > MathUtils.TWO_PI + arcLength) {
+ // in fact the end point a[1] goes far enough that we
+ // leave the minus part of the arc and enter the plus part again
+ final double minusToPlus = MathUtils.TWO_PI + arcLength + arcOffset;
+ minus.add(minusToPlus);
+ plus.add(minusToPlus);
+ plus.add(a[1]);
+ } else {
+ // the end point a[1] is in the minus part of the arc
+ minus.add(a[1]);
+ }
+ } else {
+ // the end point a[1] is in the plus part of the arc
+ plus.add(a[1]);
+ }
+ }
+ }
+
+ return new Split(createSplitPart(plus), createSplitPart(minus));
+
+ }
+
+ /** Add an arc limit to a BSP tree under construction.
+ * @param tree BSP tree under construction
+ * @param alpha arc limit
+ * @param isStart if true, the limit is the start of an arc
+ */
+ private void addArcLimit(final BSPTree<Sphere1D> tree, final double alpha, final boolean isStart) {
+
+ final LimitAngle limit = new LimitAngle(new S1Point(alpha), !isStart, getTolerance());
+ final BSPTree<Sphere1D> node = tree.getCell(limit.getLocation(), getTolerance());
+ if (node.getCut() != null) {
+ // this should never happen
+ throw new MathInternalError();
+ }
+
+ node.insertCut(limit);
+ node.setAttribute(null);
+ node.getPlus().setAttribute(Boolean.FALSE);
+ node.getMinus().setAttribute(Boolean.TRUE);
+
+ }
+
+ /** Create a split part.
+ * <p>
+ * As per construction, the list of limit angles is known to have
+ * an even number of entries, with start angles at even indices and
+ * end angles at odd indices.
+ * </p>
+ * @param limits limit angles of the split part
+ * @return split part (may be null)
+ */
+ private ArcsSet createSplitPart(final List<Double> limits) {
+ if (limits.isEmpty()) {
+ return null;
+ } else {
+
+ // collapse close limit angles
+ for (int i = 0; i < limits.size(); ++i) {
+ final int j = (i + 1) % limits.size();
+ final double lA = limits.get(i);
+ final double lB = MathUtils.normalizeAngle(limits.get(j), lA);
+ if (FastMath.abs(lB - lA) <= getTolerance()) {
+ // the two limits are too close to each other, we remove both of them
+ if (j > 0) {
+ // regular case, the two entries are consecutive ones
+ limits.remove(j);
+ limits.remove(i);
+ i = i - 1;
+ } else {
+ // special case, i the the last entry and j is the first entry
+ // we have wrapped around list end
+ final double lEnd = limits.remove(limits.size() - 1);
+ final double lStart = limits.remove(0);
+ if (limits.isEmpty()) {
+ // the ends were the only limits, is it a full circle or an empty circle?
+ if (lEnd - lStart > FastMath.PI) {
+ // it was full circle
+ return new ArcsSet(new BSPTree<Sphere1D>(Boolean.TRUE), getTolerance());
+ } else {
+ // it was an empty circle
+ return null;
+ }
+ } else {
+ // we have removed the first interval start, so our list
+ // currently starts with an interval end, which is wrong
+ // we need to move this interval end to the end of the list
+ limits.add(limits.remove(0) + MathUtils.TWO_PI);
+ }
+ }
+ }
+ }
+
+ // build the tree by adding all angular sectors
+ BSPTree<Sphere1D> tree = new BSPTree<Sphere1D>(Boolean.FALSE);
+ for (int i = 0; i < limits.size() - 1; i += 2) {
+ addArcLimit(tree, limits.get(i), true);
+ addArcLimit(tree, limits.get(i + 1), false);
+ }
+
+ if (tree.getCut() == null) {
+ // we did not insert anything
+ return null;
+ }
+
+ return new ArcsSet(tree, getTolerance());
+
+ }
+ }
+
+ /** Class holding the results of the {@link #split split} method.
+ */
+ public static class Split {
+
+ /** Part of the arcs set on the plus side of the splitting arc. */
+ private final ArcsSet plus;
+
+ /** Part of the arcs set on the minus side of the splitting arc. */
+ private final ArcsSet minus;
+
+ /** Build a Split from its parts.
+ * @param plus part of the arcs set on the plus side of the
+ * splitting arc
+ * @param minus part of the arcs set on the minus side of the
+ * splitting arc
+ */
+ private Split(final ArcsSet plus, final ArcsSet minus) {
+ this.plus = plus;
+ this.minus = minus;
+ }
+
+ /** Get the part of the arcs set on the plus side of the splitting arc.
+ * @return part of the arcs set on the plus side of the splitting arc
+ */
+ public ArcsSet getPlus() {
+ return plus;
+ }
+
+ /** Get the part of the arcs set on the minus side of the splitting arc.
+ * @return part of the arcs set on the minus side of the splitting arc
+ */
+ public ArcsSet getMinus() {
+ return minus;
+ }
+
+ /** Get the side of the split arc with respect to its splitter.
+ * @return {@link Side#PLUS} if only {@link #getPlus()} returns non-null,
+ * {@link Side#MINUS} if only {@link #getMinus()} returns non-null,
+ * {@link Side#BOTH} if both {@link #getPlus()} and {@link #getMinus()}
+ * return non-null or {@link Side#HYPER} if both {@link #getPlus()} and
+ * {@link #getMinus()} return null
+ * @since 3.6
+ */
+ public Side getSide() {
+ if (plus != null) {
+ if (minus != null) {
+ return Side.BOTH;
+ } else {
+ return Side.PLUS;
+ }
+ } else if (minus != null) {
+ return Side.MINUS;
+ } else {
+ return Side.HYPER;
+ }
+ }
+
+ }
+
+ /** Specialized exception for inconsistent BSP tree state inconsistency.
+ * <p>
+ * This exception is thrown at {@link ArcsSet} construction time when the
+ * {@link org.apache.commons.math3.geometry.partitioning.Region.Location inside/outside}
+ * state is not consistent at the 0, \(2 \pi \) crossing.
+ * </p>
+ */
+ public static class InconsistentStateAt2PiWrapping extends MathIllegalArgumentException {
+
+ /** Serializable UID. */
+ private static final long serialVersionUID = 20140107L;
+
+ /** Simple constructor.
+ */
+ public InconsistentStateAt2PiWrapping() {
+ super(LocalizedFormats.INCONSISTENT_STATE_AT_2_PI_WRAPPING);
+ }
+
+ }
+
+}
diff --git a/src/main/java/org/apache/commons/math3/geometry/spherical/oned/LimitAngle.java b/src/main/java/org/apache/commons/math3/geometry/spherical/oned/LimitAngle.java
new file mode 100644
index 0000000..748a142
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/geometry/spherical/oned/LimitAngle.java
@@ -0,0 +1,127 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math3.geometry.spherical.oned;
+
+import org.apache.commons.math3.geometry.Point;
+import org.apache.commons.math3.geometry.partitioning.Hyperplane;
+
+/** This class represents a 1D oriented hyperplane on the circle.
+ * <p>An hyperplane on the 1-sphere is an angle with an orientation.</p>
+ * <p>Instances of this class are guaranteed to be immutable.</p>
+ * @since 3.3
+ */
+public class LimitAngle implements Hyperplane<Sphere1D> {
+
+ /** Angle location. */
+ private S1Point location;
+
+ /** Orientation. */
+ private boolean direct;
+
+ /** Tolerance below which angles are considered identical. */
+ private final double tolerance;
+
+ /** Simple constructor.
+ * @param location location of the hyperplane
+ * @param direct if true, the plus side of the hyperplane is towards
+ * angles greater than {@code location}
+ * @param tolerance tolerance below which angles are considered identical
+ */
+ public LimitAngle(final S1Point location, final boolean direct, final double tolerance) {
+ this.location = location;
+ this.direct = direct;
+ this.tolerance = tolerance;
+ }
+
+ /** Copy the instance.
+ * <p>Since instances are immutable, this method directly returns
+ * the instance.</p>
+ * @return the instance itself
+ */
+ public LimitAngle copySelf() {
+ return this;
+ }
+
+ /** {@inheritDoc} */
+ public double getOffset(final Point<Sphere1D> point) {
+ final double delta = ((S1Point) point).getAlpha() - location.getAlpha();
+ return direct ? delta : -delta;
+ }
+
+ /** Check if the hyperplane orientation is direct.
+ * @return true if the plus side of the hyperplane is towards
+ * angles greater than hyperplane location
+ */
+ public boolean isDirect() {
+ return direct;
+ }
+
+ /** Get the reverse of the instance.
+ * <p>Get a limit angle with reversed orientation with respect to the
+ * instance. A new object is built, the instance is untouched.</p>
+ * @return a new limit angle, with orientation opposite to the instance orientation
+ */
+ public LimitAngle getReverse() {
+ return new LimitAngle(location, !direct, tolerance);
+ }
+
+ /** Build a region covering the whole hyperplane.
+ * <p>Since this class represent zero dimension spaces which does
+ * not have lower dimension sub-spaces, this method returns a dummy
+ * implementation of a {@link
+ * org.apache.commons.math3.geometry.partitioning.SubHyperplane SubHyperplane}.
+ * This implementation is only used to allow the {@link
+ * org.apache.commons.math3.geometry.partitioning.SubHyperplane
+ * SubHyperplane} class implementation to work properly, it should
+ * <em>not</em> be used otherwise.</p>
+ * @return a dummy sub hyperplane
+ */
+ public SubLimitAngle wholeHyperplane() {
+ return new SubLimitAngle(this, null);
+ }
+
+ /** Build a region covering the whole space.
+ * @return a region containing the instance (really an {@link
+ * ArcsSet IntervalsSet} instance)
+ */
+ public ArcsSet wholeSpace() {
+ return new ArcsSet(tolerance);
+ }
+
+ /** {@inheritDoc} */
+ public boolean sameOrientationAs(final Hyperplane<Sphere1D> other) {
+ return !(direct ^ ((LimitAngle) other).direct);
+ }
+
+ /** Get the hyperplane location on the circle.
+ * @return the hyperplane location
+ */
+ public S1Point getLocation() {
+ return location;
+ }
+
+ /** {@inheritDoc} */
+ public Point<Sphere1D> project(Point<Sphere1D> point) {
+ return location;
+ }
+
+ /** {@inheritDoc} */
+ public double getTolerance() {
+ return tolerance;
+ }
+
+}
diff --git a/src/main/java/org/apache/commons/math3/geometry/spherical/oned/S1Point.java b/src/main/java/org/apache/commons/math3/geometry/spherical/oned/S1Point.java
new file mode 100644
index 0000000..263a559
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/geometry/spherical/oned/S1Point.java
@@ -0,0 +1,157 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math3.geometry.spherical.oned;
+
+import org.apache.commons.math3.geometry.Point;
+import org.apache.commons.math3.geometry.Space;
+import org.apache.commons.math3.geometry.euclidean.twod.Vector2D;
+import org.apache.commons.math3.util.FastMath;
+import org.apache.commons.math3.util.MathUtils;
+
+/** This class represents a point on the 1-sphere.
+ * <p>Instances of this class are guaranteed to be immutable.</p>
+ * @since 3.3
+ */
+public class S1Point implements Point<Sphere1D> {
+
+ // CHECKSTYLE: stop ConstantName
+ /** A vector with all coordinates set to NaN. */
+ public static final S1Point NaN = new S1Point(Double.NaN, Vector2D.NaN);
+ // CHECKSTYLE: resume ConstantName
+
+ /** Serializable UID. */
+ private static final long serialVersionUID = 20131218L;
+
+ /** Azimuthal angle \( \alpha \). */
+ private final double alpha;
+
+ /** Corresponding 2D normalized vector. */
+ private final Vector2D vector;
+
+ /** Simple constructor.
+ * Build a vector from its coordinates
+ * @param alpha azimuthal angle \( \alpha \)
+ * @see #getAlpha()
+ */
+ public S1Point(final double alpha) {
+ this(MathUtils.normalizeAngle(alpha, FastMath.PI),
+ new Vector2D(FastMath.cos(alpha), FastMath.sin(alpha)));
+ }
+
+ /** Build a point from its internal components.
+ * @param alpha azimuthal angle \( \alpha \)
+ * @param vector corresponding vector
+ */
+ private S1Point(final double alpha, final Vector2D vector) {
+ this.alpha = alpha;
+ this.vector = vector;
+ }
+
+ /** Get the azimuthal angle \( \alpha \).
+ * @return azimuthal angle \( \alpha \)
+ * @see #S1Point(double)
+ */
+ public double getAlpha() {
+ return alpha;
+ }
+
+ /** Get the corresponding normalized vector in the 2D euclidean space.
+ * @return normalized vector
+ */
+ public Vector2D getVector() {
+ return vector;
+ }
+
+ /** {@inheritDoc} */
+ public Space getSpace() {
+ return Sphere1D.getInstance();
+ }
+
+ /** {@inheritDoc} */
+ public boolean isNaN() {
+ return Double.isNaN(alpha);
+ }
+
+ /** {@inheritDoc} */
+ public double distance(final Point<Sphere1D> point) {
+ return distance(this, (S1Point) point);
+ }
+
+ /** Compute the distance (angular separation) between two points.
+ * @param p1 first vector
+ * @param p2 second vector
+ * @return the angular separation between p1 and p2
+ */
+ public static double distance(S1Point p1, S1Point p2) {
+ return Vector2D.angle(p1.vector, p2.vector);
+ }
+
+ /**
+ * Test for the equality of two points on the 2-sphere.
+ * <p>
+ * If all coordinates of two points are exactly the same, and none are
+ * <code>Double.NaN</code>, the two points are considered to be equal.
+ * </p>
+ * <p>
+ * <code>NaN</code> coordinates are considered to affect globally the vector
+ * and be equals to each other - i.e, if either (or all) coordinates of the
+ * 2D vector are equal to <code>Double.NaN</code>, the 2D vector is equal to
+ * {@link #NaN}.
+ * </p>
+ *
+ * @param other Object to test for equality to this
+ * @return true if two points on the 2-sphere objects are equal, false if
+ * object is null, not an instance of S2Point, or
+ * not equal to this S2Point instance
+ *
+ */
+ @Override
+ public boolean equals(Object other) {
+
+ if (this == other) {
+ return true;
+ }
+
+ if (other instanceof S1Point) {
+ final S1Point rhs = (S1Point) other;
+ if (rhs.isNaN()) {
+ return this.isNaN();
+ }
+
+ return alpha == rhs.alpha;
+ }
+
+ return false;
+
+ }
+
+ /**
+ * Get a hashCode for the 2D vector.
+ * <p>
+ * All NaN values have the same hash code.</p>
+ *
+ * @return a hash code value for this object
+ */
+ @Override
+ public int hashCode() {
+ if (isNaN()) {
+ return 542;
+ }
+ return 1759 * MathUtils.hash(alpha);
+ }
+
+}
diff --git a/src/main/java/org/apache/commons/math3/geometry/spherical/oned/Sphere1D.java b/src/main/java/org/apache/commons/math3/geometry/spherical/oned/Sphere1D.java
new file mode 100644
index 0000000..ce5c7cd
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/geometry/spherical/oned/Sphere1D.java
@@ -0,0 +1,106 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math3.geometry.spherical.oned;
+
+import java.io.Serializable;
+
+import org.apache.commons.math3.exception.MathUnsupportedOperationException;
+import org.apache.commons.math3.exception.util.LocalizedFormats;
+import org.apache.commons.math3.geometry.Space;
+
+/**
+ * This class implements a one-dimensional sphere (i.e. a circle).
+ * <p>
+ * We use here the topologists definition of the 1-sphere (see
+ * <a href="http://mathworld.wolfram.com/Sphere.html">Sphere</a> on
+ * MathWorld), i.e. the 1-sphere is the one-dimensional closed curve
+ * defined in 2D as x<sup>2</sup>+y<sup>2</sup>=1.
+ * </p>
+ * @since 3.3
+ */
+public class Sphere1D implements Serializable, Space {
+
+ /** Serializable version identifier. */
+ private static final long serialVersionUID = 20131218L;
+
+ /** Private constructor for the singleton.
+ */
+ private Sphere1D() {
+ }
+
+ /** Get the unique instance.
+ * @return the unique instance
+ */
+ public static Sphere1D getInstance() {
+ return LazyHolder.INSTANCE;
+ }
+
+ /** {@inheritDoc} */
+ public int getDimension() {
+ return 1;
+ }
+
+ /** {@inheritDoc}
+ * <p>
+ * As the 1-dimension sphere does not have proper sub-spaces,
+ * this method always throws a {@link NoSubSpaceException}
+ * </p>
+ * @return nothing
+ * @throws NoSubSpaceException in all cases
+ */
+ public Space getSubSpace() throws NoSubSpaceException {
+ throw new NoSubSpaceException();
+ }
+
+ // CHECKSTYLE: stop HideUtilityClassConstructor
+ /** Holder for the instance.
+ * <p>We use here the Initialization On Demand Holder Idiom.</p>
+ */
+ private static class LazyHolder {
+ /** Cached field instance. */
+ private static final Sphere1D INSTANCE = new Sphere1D();
+ }
+ // CHECKSTYLE: resume HideUtilityClassConstructor
+
+ /** Handle deserialization of the singleton.
+ * @return the singleton instance
+ */
+ private Object readResolve() {
+ // return the singleton instance
+ return LazyHolder.INSTANCE;
+ }
+
+ /** Specialized exception for inexistent sub-space.
+ * <p>
+ * This exception is thrown when attempting to get the sub-space of a one-dimensional space
+ * </p>
+ */
+ public static class NoSubSpaceException extends MathUnsupportedOperationException {
+
+ /** Serializable UID. */
+ private static final long serialVersionUID = 20140225L;
+
+ /** Simple constructor.
+ */
+ public NoSubSpaceException() {
+ super(LocalizedFormats.NOT_SUPPORTED_IN_DIMENSION_N, 1);
+ }
+
+ }
+
+}
diff --git a/src/main/java/org/apache/commons/math3/geometry/spherical/oned/SubLimitAngle.java b/src/main/java/org/apache/commons/math3/geometry/spherical/oned/SubLimitAngle.java
new file mode 100644
index 0000000..ebd3627
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/geometry/spherical/oned/SubLimitAngle.java
@@ -0,0 +1,66 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math3.geometry.spherical.oned;
+
+import org.apache.commons.math3.geometry.partitioning.AbstractSubHyperplane;
+import org.apache.commons.math3.geometry.partitioning.Hyperplane;
+import org.apache.commons.math3.geometry.partitioning.Region;
+
+/** This class represents sub-hyperplane for {@link LimitAngle}.
+ * <p>Instances of this class are guaranteed to be immutable.</p>
+ * @since 3.3
+ */
+public class SubLimitAngle extends AbstractSubHyperplane<Sphere1D, Sphere1D> {
+
+ /** Simple constructor.
+ * @param hyperplane underlying hyperplane
+ * @param remainingRegion remaining region of the hyperplane
+ */
+ public SubLimitAngle(final Hyperplane<Sphere1D> hyperplane,
+ final Region<Sphere1D> remainingRegion) {
+ super(hyperplane, remainingRegion);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public double getSize() {
+ return 0;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public boolean isEmpty() {
+ return false;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ protected AbstractSubHyperplane<Sphere1D, Sphere1D> buildNew(final Hyperplane<Sphere1D> hyperplane,
+ final Region<Sphere1D> remainingRegion) {
+ return new SubLimitAngle(hyperplane, remainingRegion);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public SplitSubHyperplane<Sphere1D> split(final Hyperplane<Sphere1D> hyperplane) {
+ final double global = hyperplane.getOffset(((LimitAngle) getHyperplane()).getLocation());
+ return (global < -1.0e-10) ?
+ new SplitSubHyperplane<Sphere1D>(null, this) :
+ new SplitSubHyperplane<Sphere1D>(this, null);
+ }
+
+}
diff --git a/src/main/java/org/apache/commons/math3/geometry/spherical/oned/package-info.java b/src/main/java/org/apache/commons/math3/geometry/spherical/oned/package-info.java
new file mode 100644
index 0000000..d54bc0b
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/geometry/spherical/oned/package-info.java
@@ -0,0 +1,30 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/**
+ *
+ * <p>
+ * This package provides basic geometry components on the 1-sphere.
+ * </p>
+ * <p>
+ * We use here the topologists definition of the 1-sphere (see
+ * <a href="http://mathworld.wolfram.com/Sphere.html">Sphere</a> on
+ * MathWorld), i.e. the 1-sphere is the one-dimensional closed curve
+ * defined in 2D as x<sup>2</sup>+y<sup>2</sup>=1.
+ * </p>
+ *
+ */
+package org.apache.commons.math3.geometry.spherical.oned;
diff --git a/src/main/java/org/apache/commons/math3/geometry/spherical/twod/Circle.java b/src/main/java/org/apache/commons/math3/geometry/spherical/twod/Circle.java
new file mode 100644
index 0000000..a34db6d
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/geometry/spherical/twod/Circle.java
@@ -0,0 +1,326 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math3.geometry.spherical.twod;
+
+import org.apache.commons.math3.geometry.Point;
+import org.apache.commons.math3.geometry.euclidean.threed.Rotation;
+import org.apache.commons.math3.geometry.euclidean.threed.Vector3D;
+import org.apache.commons.math3.geometry.partitioning.Embedding;
+import org.apache.commons.math3.geometry.partitioning.Hyperplane;
+import org.apache.commons.math3.geometry.partitioning.SubHyperplane;
+import org.apache.commons.math3.geometry.partitioning.Transform;
+import org.apache.commons.math3.geometry.spherical.oned.Arc;
+import org.apache.commons.math3.geometry.spherical.oned.ArcsSet;
+import org.apache.commons.math3.geometry.spherical.oned.S1Point;
+import org.apache.commons.math3.geometry.spherical.oned.Sphere1D;
+import org.apache.commons.math3.util.FastMath;
+
+/** This class represents an oriented great circle on the 2-sphere.
+
+ * <p>An oriented circle can be defined by a center point. The circle
+ * is the the set of points that are in the normal plan the center.</p>
+
+ * <p>Since it is oriented the two spherical caps at its two sides are
+ * unambiguously identified as a left cap and a right cap. This can be
+ * used to identify the interior and the exterior in a simple way by
+ * local properties only when part of a line is used to define part of
+ * a spherical polygon boundary.</p>
+
+ * @since 3.3
+ */
+public class Circle implements Hyperplane<Sphere2D>, Embedding<Sphere2D, Sphere1D> {
+
+ /** Pole or circle center. */
+ private Vector3D pole;
+
+ /** First axis in the equator plane, origin of the phase angles. */
+ private Vector3D x;
+
+ /** Second axis in the equator plane, in quadrature with respect to x. */
+ private Vector3D y;
+
+ /** Tolerance below which close sub-arcs are merged together. */
+ private final double tolerance;
+
+ /** Build a great circle from its pole.
+ * <p>The circle is oriented in the trigonometric direction around pole.</p>
+ * @param pole circle pole
+ * @param tolerance tolerance below which close sub-arcs are merged together
+ */
+ public Circle(final Vector3D pole, final double tolerance) {
+ reset(pole);
+ this.tolerance = tolerance;
+ }
+
+ /** Build a great circle from two non-aligned points.
+ * <p>The circle is oriented from first to second point using the path smaller than \( \pi \).</p>
+ * @param first first point contained in the great circle
+ * @param second second point contained in the great circle
+ * @param tolerance tolerance below which close sub-arcs are merged together
+ */
+ public Circle(final S2Point first, final S2Point second, final double tolerance) {
+ reset(first.getVector().crossProduct(second.getVector()));
+ this.tolerance = tolerance;
+ }
+
+ /** Build a circle from its internal components.
+ * <p>The circle is oriented in the trigonometric direction around center.</p>
+ * @param pole circle pole
+ * @param x first axis in the equator plane
+ * @param y second axis in the equator plane
+ * @param tolerance tolerance below which close sub-arcs are merged together
+ */
+ private Circle(final Vector3D pole, final Vector3D x, final Vector3D y,
+ final double tolerance) {
+ this.pole = pole;
+ this.x = x;
+ this.y = y;
+ this.tolerance = tolerance;
+ }
+
+ /** Copy constructor.
+ * <p>The created instance is completely independent from the
+ * original instance, it is a deep copy.</p>
+ * @param circle circle to copy
+ */
+ public Circle(final Circle circle) {
+ this(circle.pole, circle.x, circle.y, circle.tolerance);
+ }
+
+ /** {@inheritDoc} */
+ public Circle copySelf() {
+ return new Circle(this);
+ }
+
+ /** Reset the instance as if built from a pole.
+ * <p>The circle is oriented in the trigonometric direction around pole.</p>
+ * @param newPole circle pole
+ */
+ public void reset(final Vector3D newPole) {
+ this.pole = newPole.normalize();
+ this.x = newPole.orthogonal();
+ this.y = Vector3D.crossProduct(newPole, x).normalize();
+ }
+
+ /** Revert the instance.
+ */
+ public void revertSelf() {
+ // x remains the same
+ y = y.negate();
+ pole = pole.negate();
+ }
+
+ /** Get the reverse of the instance.
+ * <p>Get a circle with reversed orientation with respect to the
+ * instance. A new object is built, the instance is untouched.</p>
+ * @return a new circle, with orientation opposite to the instance orientation
+ */
+ public Circle getReverse() {
+ return new Circle(pole.negate(), x, y.negate(), tolerance);
+ }
+
+ /** {@inheritDoc} */
+ public Point<Sphere2D> project(Point<Sphere2D> point) {
+ return toSpace(toSubSpace(point));
+ }
+
+ /** {@inheritDoc} */
+ public double getTolerance() {
+ return tolerance;
+ }
+
+ /** {@inheritDoc}
+ * @see #getPhase(Vector3D)
+ */
+ public S1Point toSubSpace(final Point<Sphere2D> point) {
+ return new S1Point(getPhase(((S2Point) point).getVector()));
+ }
+
+ /** Get the phase angle of a direction.
+ * <p>
+ * The direction may not belong to the circle as the
+ * phase is computed for the meridian plane between the circle
+ * pole and the direction.
+ * </p>
+ * @param direction direction for which phase is requested
+ * @return phase angle of the direction around the circle
+ * @see #toSubSpace(Point)
+ */
+ public double getPhase(final Vector3D direction) {
+ return FastMath.PI + FastMath.atan2(-direction.dotProduct(y), -direction.dotProduct(x));
+ }
+
+ /** {@inheritDoc}
+ * @see #getPointAt(double)
+ */
+ public S2Point toSpace(final Point<Sphere1D> point) {
+ return new S2Point(getPointAt(((S1Point) point).getAlpha()));
+ }
+
+ /** Get a circle point from its phase around the circle.
+ * @param alpha phase around the circle
+ * @return circle point on the sphere
+ * @see #toSpace(Point)
+ * @see #getXAxis()
+ * @see #getYAxis()
+ */
+ public Vector3D getPointAt(final double alpha) {
+ return new Vector3D(FastMath.cos(alpha), x, FastMath.sin(alpha), y);
+ }
+
+ /** Get the X axis of the circle.
+ * <p>
+ * This method returns the same value as {@link #getPointAt(double)
+ * getPointAt(0.0)} but it does not do any computation and always
+ * return the same instance.
+ * </p>
+ * @return an arbitrary x axis on the circle
+ * @see #getPointAt(double)
+ * @see #getYAxis()
+ * @see #getPole()
+ */
+ public Vector3D getXAxis() {
+ return x;
+ }
+
+ /** Get the Y axis of the circle.
+ * <p>
+ * This method returns the same value as {@link #getPointAt(double)
+ * getPointAt(0.5 * FastMath.PI)} but it does not do any computation and always
+ * return the same instance.
+ * </p>
+ * @return an arbitrary y axis point on the circle
+ * @see #getPointAt(double)
+ * @see #getXAxis()
+ * @see #getPole()
+ */
+ public Vector3D getYAxis() {
+ return y;
+ }
+
+ /** Get the pole of the circle.
+ * <p>
+ * As the circle is a great circle, the pole does <em>not</em>
+ * belong to it.
+ * </p>
+ * @return pole of the circle
+ * @see #getXAxis()
+ * @see #getYAxis()
+ */
+ public Vector3D getPole() {
+ return pole;
+ }
+
+ /** Get the arc of the instance that lies inside the other circle.
+ * @param other other circle
+ * @return arc of the instance that lies inside the other circle
+ */
+ public Arc getInsideArc(final Circle other) {
+ final double alpha = getPhase(other.pole);
+ final double halfPi = 0.5 * FastMath.PI;
+ return new Arc(alpha - halfPi, alpha + halfPi, tolerance);
+ }
+
+ /** {@inheritDoc} */
+ public SubCircle wholeHyperplane() {
+ return new SubCircle(this, new ArcsSet(tolerance));
+ }
+
+ /** Build a region covering the whole space.
+ * @return a region containing the instance (really a {@link
+ * SphericalPolygonsSet SphericalPolygonsSet} instance)
+ */
+ public SphericalPolygonsSet wholeSpace() {
+ return new SphericalPolygonsSet(tolerance);
+ }
+
+ /** {@inheritDoc}
+ * @see #getOffset(Vector3D)
+ */
+ public double getOffset(final Point<Sphere2D> point) {
+ return getOffset(((S2Point) point).getVector());
+ }
+
+ /** Get the offset (oriented distance) of a direction.
+ * <p>The offset is defined as the angular distance between the
+ * circle center and the direction minus the circle radius. It
+ * is therefore 0 on the circle, positive for directions outside of
+ * the cone delimited by the circle, and negative inside the cone.</p>
+ * @param direction direction to check
+ * @return offset of the direction
+ * @see #getOffset(Point)
+ */
+ public double getOffset(final Vector3D direction) {
+ return Vector3D.angle(pole, direction) - 0.5 * FastMath.PI;
+ }
+
+ /** {@inheritDoc} */
+ public boolean sameOrientationAs(final Hyperplane<Sphere2D> other) {
+ final Circle otherC = (Circle) other;
+ return Vector3D.dotProduct(pole, otherC.pole) >= 0.0;
+ }
+
+ /** Get a {@link org.apache.commons.math3.geometry.partitioning.Transform
+ * Transform} embedding a 3D rotation.
+ * @param rotation rotation to use
+ * @return a new transform that can be applied to either {@link
+ * Point Point}, {@link Circle Line} or {@link
+ * org.apache.commons.math3.geometry.partitioning.SubHyperplane
+ * SubHyperplane} instances
+ */
+ public static Transform<Sphere2D, Sphere1D> getTransform(final Rotation rotation) {
+ return new CircleTransform(rotation);
+ }
+
+ /** Class embedding a 3D rotation. */
+ private static class CircleTransform implements Transform<Sphere2D, Sphere1D> {
+
+ /** Underlying rotation. */
+ private final Rotation rotation;
+
+ /** Build a transform from a {@code Rotation}.
+ * @param rotation rotation to use
+ */
+ CircleTransform(final Rotation rotation) {
+ this.rotation = rotation;
+ }
+
+ /** {@inheritDoc} */
+ public S2Point apply(final Point<Sphere2D> point) {
+ return new S2Point(rotation.applyTo(((S2Point) point).getVector()));
+ }
+
+ /** {@inheritDoc} */
+ public Circle apply(final Hyperplane<Sphere2D> hyperplane) {
+ final Circle circle = (Circle) hyperplane;
+ return new Circle(rotation.applyTo(circle.pole),
+ rotation.applyTo(circle.x),
+ rotation.applyTo(circle.y),
+ circle.tolerance);
+ }
+
+ /** {@inheritDoc} */
+ public SubHyperplane<Sphere1D> apply(final SubHyperplane<Sphere1D> sub,
+ final Hyperplane<Sphere2D> original,
+ final Hyperplane<Sphere2D> transformed) {
+ // as the circle is rotated, the limit angles are rotated too
+ return sub;
+ }
+
+ }
+
+}
diff --git a/src/main/java/org/apache/commons/math3/geometry/spherical/twod/Edge.java b/src/main/java/org/apache/commons/math3/geometry/spherical/twod/Edge.java
new file mode 100644
index 0000000..a9ccb08
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/geometry/spherical/twod/Edge.java
@@ -0,0 +1,222 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math3.geometry.spherical.twod;
+
+import java.util.List;
+
+import org.apache.commons.math3.geometry.euclidean.threed.Vector3D;
+import org.apache.commons.math3.geometry.spherical.oned.Arc;
+import org.apache.commons.math3.util.FastMath;
+import org.apache.commons.math3.util.MathUtils;
+
+/** Spherical polygons boundary edge.
+ * @see SphericalPolygonsSet#getBoundaryLoops()
+ * @see Vertex
+ * @since 3.3
+ */
+public class Edge {
+
+ /** Start vertex. */
+ private final Vertex start;
+
+ /** End vertex. */
+ private Vertex end;
+
+ /** Length of the arc. */
+ private final double length;
+
+ /** Circle supporting the edge. */
+ private final Circle circle;
+
+ /** Build an edge not contained in any node yet.
+ * @param start start vertex
+ * @param end end vertex
+ * @param length length of the arc (it can be greater than \( \pi \))
+ * @param circle circle supporting the edge
+ */
+ Edge(final Vertex start, final Vertex end, final double length, final Circle circle) {
+
+ this.start = start;
+ this.end = end;
+ this.length = length;
+ this.circle = circle;
+
+ // connect the vertices back to the edge
+ start.setOutgoing(this);
+ end.setIncoming(this);
+
+ }
+
+ /** Get start vertex.
+ * @return start vertex
+ */
+ public Vertex getStart() {
+ return start;
+ }
+
+ /** Get end vertex.
+ * @return end vertex
+ */
+ public Vertex getEnd() {
+ return end;
+ }
+
+ /** Get the length of the arc.
+ * @return length of the arc (can be greater than \( \pi \))
+ */
+ public double getLength() {
+ return length;
+ }
+
+ /** Get the circle supporting this edge.
+ * @return circle supporting this edge
+ */
+ public Circle getCircle() {
+ return circle;
+ }
+
+ /** Get an intermediate point.
+ * <p>
+ * The angle along the edge should normally be between 0 and {@link #getLength()}
+ * in order to remain within edge limits. However, there are no checks on the
+ * value of the angle, so user can rebuild the full circle on which an edge is
+ * defined if they want.
+ * </p>
+ * @param alpha angle along the edge, counted from {@link #getStart()}
+ * @return an intermediate point
+ */
+ public Vector3D getPointAt(final double alpha) {
+ return circle.getPointAt(alpha + circle.getPhase(start.getLocation().getVector()));
+ }
+
+ /** Connect the instance with a following edge.
+ * @param next edge following the instance
+ */
+ void setNextEdge(final Edge next) {
+ end = next.getStart();
+ end.setIncoming(this);
+ end.bindWith(getCircle());
+ }
+
+ /** Split the edge.
+ * <p>
+ * Once split, this edge is not referenced anymore by the vertices,
+ * it is replaced by the two or three sub-edges and intermediate splitting
+ * vertices are introduced to connect these sub-edges together.
+ * </p>
+ * @param splitCircle circle splitting the edge in several parts
+ * @param outsideList list where to put parts that are outside of the split circle
+ * @param insideList list where to put parts that are inside the split circle
+ */
+ void split(final Circle splitCircle,
+ final List<Edge> outsideList, final List<Edge> insideList) {
+
+ // get the inside arc, synchronizing its phase with the edge itself
+ final double edgeStart = circle.getPhase(start.getLocation().getVector());
+ final Arc arc = circle.getInsideArc(splitCircle);
+ final double arcRelativeStart = MathUtils.normalizeAngle(arc.getInf(), edgeStart + FastMath.PI) - edgeStart;
+ final double arcRelativeEnd = arcRelativeStart + arc.getSize();
+ final double unwrappedEnd = arcRelativeEnd - MathUtils.TWO_PI;
+
+ // build the sub-edges
+ final double tolerance = circle.getTolerance();
+ Vertex previousVertex = start;
+ if (unwrappedEnd >= length - tolerance) {
+
+ // the edge is entirely contained inside the circle
+ // we don't split anything
+ insideList.add(this);
+
+ } else {
+
+ // there are at least some parts of the edge that should be outside
+ // (even is they are later be filtered out as being too small)
+ double alreadyManagedLength = 0;
+ if (unwrappedEnd >= 0) {
+ // the start of the edge is inside the circle
+ previousVertex = addSubEdge(previousVertex,
+ new Vertex(new S2Point(circle.getPointAt(edgeStart + unwrappedEnd))),
+ unwrappedEnd, insideList, splitCircle);
+ alreadyManagedLength = unwrappedEnd;
+ }
+
+ if (arcRelativeStart >= length - tolerance) {
+ // the edge ends while still outside of the circle
+ if (unwrappedEnd >= 0) {
+ previousVertex = addSubEdge(previousVertex, end,
+ length - alreadyManagedLength, outsideList, splitCircle);
+ } else {
+ // the edge is entirely outside of the circle
+ // we don't split anything
+ outsideList.add(this);
+ }
+ } else {
+ // the edge is long enough to enter inside the circle
+ previousVertex = addSubEdge(previousVertex,
+ new Vertex(new S2Point(circle.getPointAt(edgeStart + arcRelativeStart))),
+ arcRelativeStart - alreadyManagedLength, outsideList, splitCircle);
+ alreadyManagedLength = arcRelativeStart;
+
+ if (arcRelativeEnd >= length - tolerance) {
+ // the edge ends while still inside of the circle
+ previousVertex = addSubEdge(previousVertex, end,
+ length - alreadyManagedLength, insideList, splitCircle);
+ } else {
+ // the edge is long enough to exit outside of the circle
+ previousVertex = addSubEdge(previousVertex,
+ new Vertex(new S2Point(circle.getPointAt(edgeStart + arcRelativeStart))),
+ arcRelativeStart - alreadyManagedLength, insideList, splitCircle);
+ alreadyManagedLength = arcRelativeStart;
+ previousVertex = addSubEdge(previousVertex, end,
+ length - alreadyManagedLength, outsideList, splitCircle);
+ }
+ }
+
+ }
+
+ }
+
+ /** Add a sub-edge to a list if long enough.
+ * <p>
+ * If the length of the sub-edge to add is smaller than the {@link Circle#getTolerance()}
+ * tolerance of the support circle, it will be ignored.
+ * </p>
+ * @param subStart start of the sub-edge
+ * @param subEnd end of the sub-edge
+ * @param subLength length of the sub-edge
+ * @param splitCircle circle splitting the edge in several parts
+ * @param list list where to put the sub-edge
+ * @return end vertex of the edge ({@code subEnd} if the edge was long enough and really
+ * added, {@code subStart} if the edge was too small and therefore ignored)
+ */
+ private Vertex addSubEdge(final Vertex subStart, final Vertex subEnd, final double subLength,
+ final List<Edge> list, final Circle splitCircle) {
+
+ if (subLength <= circle.getTolerance()) {
+ // the edge is too short, we ignore it
+ return subStart;
+ }
+
+ // really add the edge
+ subEnd.bindWith(splitCircle);
+ final Edge edge = new Edge(subStart, subEnd, subLength, circle);
+ list.add(edge);
+ return subEnd;
+
+ }
+
+}
diff --git a/src/main/java/org/apache/commons/math3/geometry/spherical/twod/EdgesBuilder.java b/src/main/java/org/apache/commons/math3/geometry/spherical/twod/EdgesBuilder.java
new file mode 100644
index 0000000..844cfb1
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/geometry/spherical/twod/EdgesBuilder.java
@@ -0,0 +1,169 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math3.geometry.spherical.twod;
+
+import java.util.ArrayList;
+import java.util.IdentityHashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.commons.math3.exception.MathIllegalStateException;
+import org.apache.commons.math3.exception.util.LocalizedFormats;
+import org.apache.commons.math3.geometry.euclidean.threed.Vector3D;
+import org.apache.commons.math3.geometry.partitioning.BSPTree;
+import org.apache.commons.math3.geometry.partitioning.BSPTreeVisitor;
+import org.apache.commons.math3.geometry.partitioning.BoundaryAttribute;
+import org.apache.commons.math3.geometry.spherical.oned.Arc;
+import org.apache.commons.math3.geometry.spherical.oned.ArcsSet;
+import org.apache.commons.math3.geometry.spherical.oned.S1Point;
+
+/** Visitor building edges.
+ * @since 3.3
+ */
+class EdgesBuilder implements BSPTreeVisitor<Sphere2D> {
+
+ /** Root of the tree. */
+ private final BSPTree<Sphere2D> root;
+
+ /** Tolerance below which points are consider to be identical. */
+ private final double tolerance;
+
+ /** Built edges and their associated nodes. */
+ private final Map<Edge, BSPTree<Sphere2D>> edgeToNode;
+
+ /** Reversed map. */
+ private final Map<BSPTree<Sphere2D>, List<Edge>> nodeToEdgesList;
+
+ /** Simple constructor.
+ * @param root tree root
+ * @param tolerance below which points are consider to be identical
+ */
+ EdgesBuilder(final BSPTree<Sphere2D> root, final double tolerance) {
+ this.root = root;
+ this.tolerance = tolerance;
+ this.edgeToNode = new IdentityHashMap<Edge, BSPTree<Sphere2D>>();
+ this.nodeToEdgesList = new IdentityHashMap<BSPTree<Sphere2D>, List<Edge>>();
+ }
+
+ /** {@inheritDoc} */
+ public Order visitOrder(final BSPTree<Sphere2D> node) {
+ return Order.MINUS_SUB_PLUS;
+ }
+
+ /** {@inheritDoc} */
+ public void visitInternalNode(final BSPTree<Sphere2D> node) {
+ nodeToEdgesList.put(node, new ArrayList<Edge>());
+ @SuppressWarnings("unchecked")
+ final BoundaryAttribute<Sphere2D> attribute = (BoundaryAttribute<Sphere2D>) node.getAttribute();
+ if (attribute.getPlusOutside() != null) {
+ addContribution((SubCircle) attribute.getPlusOutside(), false, node);
+ }
+ if (attribute.getPlusInside() != null) {
+ addContribution((SubCircle) attribute.getPlusInside(), true, node);
+ }
+ }
+
+ /** {@inheritDoc} */
+ public void visitLeafNode(final BSPTree<Sphere2D> node) {
+ }
+
+ /** Add the contribution of a boundary edge.
+ * @param sub boundary facet
+ * @param reversed if true, the facet has the inside on its plus side
+ * @param node node to which the edge belongs
+ */
+ private void addContribution(final SubCircle sub, final boolean reversed,
+ final BSPTree<Sphere2D> node) {
+ final Circle circle = (Circle) sub.getHyperplane();
+ final List<Arc> arcs = ((ArcsSet) sub.getRemainingRegion()).asList();
+ for (final Arc a : arcs) {
+ final Vertex start = new Vertex((S2Point) circle.toSpace(new S1Point(a.getInf())));
+ final Vertex end = new Vertex((S2Point) circle.toSpace(new S1Point(a.getSup())));
+ start.bindWith(circle);
+ end.bindWith(circle);
+ final Edge edge;
+ if (reversed) {
+ edge = new Edge(end, start, a.getSize(), circle.getReverse());
+ } else {
+ edge = new Edge(start, end, a.getSize(), circle);
+ }
+ edgeToNode.put(edge, node);
+ nodeToEdgesList.get(node).add(edge);
+ }
+ }
+
+ /** Get the edge that should naturally follow another one.
+ * @param previous edge to be continued
+ * @return other edge, starting where the previous one ends (they
+ * have not been connected yet)
+ * @exception MathIllegalStateException if there is not a single other edge
+ */
+ private Edge getFollowingEdge(final Edge previous)
+ throws MathIllegalStateException {
+
+ // get the candidate nodes
+ final S2Point point = previous.getEnd().getLocation();
+ final List<BSPTree<Sphere2D>> candidates = root.getCloseCuts(point, tolerance);
+
+ // the following edge we are looking for must start from one of the candidates nodes
+ double closest = tolerance;
+ Edge following = null;
+ for (final BSPTree<Sphere2D> node : candidates) {
+ for (final Edge edge : nodeToEdgesList.get(node)) {
+ if (edge != previous && edge.getStart().getIncoming() == null) {
+ final Vector3D edgeStart = edge.getStart().getLocation().getVector();
+ final double gap = Vector3D.angle(point.getVector(), edgeStart);
+ if (gap <= closest) {
+ closest = gap;
+ following = edge;
+ }
+ }
+ }
+ }
+
+ if (following == null) {
+ final Vector3D previousStart = previous.getStart().getLocation().getVector();
+ if (Vector3D.angle(point.getVector(), previousStart) <= tolerance) {
+ // the edge connects back to itself
+ return previous;
+ }
+
+ // this should never happen
+ throw new MathIllegalStateException(LocalizedFormats.OUTLINE_BOUNDARY_LOOP_OPEN);
+
+ }
+
+ return following;
+
+ }
+
+ /** Get the boundary edges.
+ * @return boundary edges
+ * @exception MathIllegalStateException if there is not a single other edge
+ */
+ public List<Edge> getEdges() throws MathIllegalStateException {
+
+ // connect the edges
+ for (final Edge previous : edgeToNode.keySet()) {
+ previous.setNextEdge(getFollowingEdge(previous));
+ }
+
+ return new ArrayList<Edge>(edgeToNode.keySet());
+
+ }
+
+}
diff --git a/src/main/java/org/apache/commons/math3/geometry/spherical/twod/PropertiesComputer.java b/src/main/java/org/apache/commons/math3/geometry/spherical/twod/PropertiesComputer.java
new file mode 100644
index 0000000..593180f
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/geometry/spherical/twod/PropertiesComputer.java
@@ -0,0 +1,173 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math3.geometry.spherical.twod;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.apache.commons.math3.exception.MathInternalError;
+import org.apache.commons.math3.geometry.euclidean.threed.Vector3D;
+import org.apache.commons.math3.geometry.partitioning.BSPTree;
+import org.apache.commons.math3.geometry.partitioning.BSPTreeVisitor;
+import org.apache.commons.math3.util.FastMath;
+import org.apache.commons.math3.util.MathUtils;
+
+/** Visitor computing geometrical properties.
+ * @since 3.3
+ */
+class PropertiesComputer implements BSPTreeVisitor<Sphere2D> {
+
+ /** Tolerance below which points are consider to be identical. */
+ private final double tolerance;
+
+ /** Summed area. */
+ private double summedArea;
+
+ /** Summed barycenter. */
+ private Vector3D summedBarycenter;
+
+ /** List of points strictly inside convex cells. */
+ private final List<Vector3D> convexCellsInsidePoints;
+
+ /** Simple constructor.
+ * @param tolerance below which points are consider to be identical
+ */
+ PropertiesComputer(final double tolerance) {
+ this.tolerance = tolerance;
+ this.summedArea = 0;
+ this.summedBarycenter = Vector3D.ZERO;
+ this.convexCellsInsidePoints = new ArrayList<Vector3D>();
+ }
+
+ /** {@inheritDoc} */
+ public Order visitOrder(final BSPTree<Sphere2D> node) {
+ return Order.MINUS_SUB_PLUS;
+ }
+
+ /** {@inheritDoc} */
+ public void visitInternalNode(final BSPTree<Sphere2D> node) {
+ // nothing to do here
+ }
+
+ /** {@inheritDoc} */
+ public void visitLeafNode(final BSPTree<Sphere2D> node) {
+ if ((Boolean) node.getAttribute()) {
+
+ // transform this inside leaf cell into a simple convex polygon
+ final SphericalPolygonsSet convex =
+ new SphericalPolygonsSet(node.pruneAroundConvexCell(Boolean.TRUE,
+ Boolean.FALSE,
+ null),
+ tolerance);
+
+ // extract the start of the single loop boundary of the convex cell
+ final List<Vertex> boundary = convex.getBoundaryLoops();
+ if (boundary.size() != 1) {
+ // this should never happen
+ throw new MathInternalError();
+ }
+
+ // compute the geometrical properties of the convex cell
+ final double area = convexCellArea(boundary.get(0));
+ final Vector3D barycenter = convexCellBarycenter(boundary.get(0));
+ convexCellsInsidePoints.add(barycenter);
+
+ // add the cell contribution to the global properties
+ summedArea += area;
+ summedBarycenter = new Vector3D(1, summedBarycenter, area, barycenter);
+
+ }
+ }
+
+ /** Compute convex cell area.
+ * @param start start vertex of the convex cell boundary
+ * @return area
+ */
+ private double convexCellArea(final Vertex start) {
+
+ int n = 0;
+ double sum = 0;
+
+ // loop around the cell
+ for (Edge e = start.getOutgoing(); n == 0 || e.getStart() != start; e = e.getEnd().getOutgoing()) {
+
+ // find path interior angle at vertex
+ final Vector3D previousPole = e.getCircle().getPole();
+ final Vector3D nextPole = e.getEnd().getOutgoing().getCircle().getPole();
+ final Vector3D point = e.getEnd().getLocation().getVector();
+ double alpha = FastMath.atan2(Vector3D.dotProduct(nextPole, Vector3D.crossProduct(point, previousPole)),
+ -Vector3D.dotProduct(nextPole, previousPole));
+ if (alpha < 0) {
+ alpha += MathUtils.TWO_PI;
+ }
+ sum += alpha;
+ n++;
+ }
+
+ // compute area using extended Girard theorem
+ // see Spherical Trigonometry: For the Use of Colleges and Schools by I. Todhunter
+ // article 99 in chapter VIII Area Of a Spherical Triangle. Spherical Excess.
+ // book available from project Gutenberg at http://www.gutenberg.org/ebooks/19770
+ return sum - (n - 2) * FastMath.PI;
+
+ }
+
+ /** Compute convex cell barycenter.
+ * @param start start vertex of the convex cell boundary
+ * @return barycenter
+ */
+ private Vector3D convexCellBarycenter(final Vertex start) {
+
+ int n = 0;
+ Vector3D sumB = Vector3D.ZERO;
+
+ // loop around the cell
+ for (Edge e = start.getOutgoing(); n == 0 || e.getStart() != start; e = e.getEnd().getOutgoing()) {
+ sumB = new Vector3D(1, sumB, e.getLength(), e.getCircle().getPole());
+ n++;
+ }
+
+ return sumB.normalize();
+
+ }
+
+ /** Get the area.
+ * @return area
+ */
+ public double getArea() {
+ return summedArea;
+ }
+
+ /** Get the barycenter.
+ * @return barycenter
+ */
+ public S2Point getBarycenter() {
+ if (summedBarycenter.getNormSq() == 0) {
+ return S2Point.NaN;
+ } else {
+ return new S2Point(summedBarycenter);
+ }
+ }
+
+ /** Get the points strictly inside convex cells.
+ * @return points strictly inside convex cells
+ */
+ public List<Vector3D> getConvexCellsInsidePoints() {
+ return convexCellsInsidePoints;
+ }
+
+}
diff --git a/src/main/java/org/apache/commons/math3/geometry/spherical/twod/S2Point.java b/src/main/java/org/apache/commons/math3/geometry/spherical/twod/S2Point.java
new file mode 100644
index 0000000..677e830
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/geometry/spherical/twod/S2Point.java
@@ -0,0 +1,237 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math3.geometry.spherical.twod;
+
+import org.apache.commons.math3.exception.MathArithmeticException;
+import org.apache.commons.math3.exception.OutOfRangeException;
+import org.apache.commons.math3.geometry.Point;
+import org.apache.commons.math3.geometry.Space;
+import org.apache.commons.math3.geometry.euclidean.threed.Vector3D;
+import org.apache.commons.math3.util.FastMath;
+import org.apache.commons.math3.util.MathUtils;
+
+/** This class represents a point on the 2-sphere.
+ * <p>
+ * We use the mathematical convention to use the azimuthal angle \( \theta \)
+ * in the x-y plane as the first coordinate, and the polar angle \( \varphi \)
+ * as the second coordinate (see <a
+ * href="http://mathworld.wolfram.com/SphericalCoordinates.html">Spherical
+ * Coordinates</a> in MathWorld).
+ * </p>
+ * <p>Instances of this class are guaranteed to be immutable.</p>
+ * @since 3.3
+ */
+public class S2Point implements Point<Sphere2D> {
+
+ /** +I (coordinates: \( \theta = 0, \varphi = \pi/2 \)). */
+ public static final S2Point PLUS_I = new S2Point(0, 0.5 * FastMath.PI, Vector3D.PLUS_I);
+
+ /** +J (coordinates: \( \theta = \pi/2, \varphi = \pi/2 \))). */
+ public static final S2Point PLUS_J = new S2Point(0.5 * FastMath.PI, 0.5 * FastMath.PI, Vector3D.PLUS_J);
+
+ /** +K (coordinates: \( \theta = any angle, \varphi = 0 \)). */
+ public static final S2Point PLUS_K = new S2Point(0, 0, Vector3D.PLUS_K);
+
+ /** -I (coordinates: \( \theta = \pi, \varphi = \pi/2 \)). */
+ public static final S2Point MINUS_I = new S2Point(FastMath.PI, 0.5 * FastMath.PI, Vector3D.MINUS_I);
+
+ /** -J (coordinates: \( \theta = 3\pi/2, \varphi = \pi/2 \)). */
+ public static final S2Point MINUS_J = new S2Point(1.5 * FastMath.PI, 0.5 * FastMath.PI, Vector3D.MINUS_J);
+
+ /** -K (coordinates: \( \theta = any angle, \varphi = \pi \)). */
+ public static final S2Point MINUS_K = new S2Point(0, FastMath.PI, Vector3D.MINUS_K);
+
+ // CHECKSTYLE: stop ConstantName
+ /** A vector with all coordinates set to NaN. */
+ public static final S2Point NaN = new S2Point(Double.NaN, Double.NaN, Vector3D.NaN);
+ // CHECKSTYLE: resume ConstantName
+
+ /** Serializable UID. */
+ private static final long serialVersionUID = 20131218L;
+
+ /** Azimuthal angle \( \theta \) in the x-y plane. */
+ private final double theta;
+
+ /** Polar angle \( \varphi \). */
+ private final double phi;
+
+ /** Corresponding 3D normalized vector. */
+ private final Vector3D vector;
+
+ /** Simple constructor.
+ * Build a vector from its spherical coordinates
+ * @param theta azimuthal angle \( \theta \) in the x-y plane
+ * @param phi polar angle \( \varphi \)
+ * @see #getTheta()
+ * @see #getPhi()
+ * @exception OutOfRangeException if \( \varphi \) is not in the [\( 0; \pi \)] range
+ */
+ public S2Point(final double theta, final double phi)
+ throws OutOfRangeException {
+ this(theta, phi, vector(theta, phi));
+ }
+
+ /** Simple constructor.
+ * Build a vector from its underlying 3D vector
+ * @param vector 3D vector
+ * @exception MathArithmeticException if vector norm is zero
+ */
+ public S2Point(final Vector3D vector) throws MathArithmeticException {
+ this(FastMath.atan2(vector.getY(), vector.getX()), Vector3D.angle(Vector3D.PLUS_K, vector),
+ vector.normalize());
+ }
+
+ /** Build a point from its internal components.
+ * @param theta azimuthal angle \( \theta \) in the x-y plane
+ * @param phi polar angle \( \varphi \)
+ * @param vector corresponding vector
+ */
+ private S2Point(final double theta, final double phi, final Vector3D vector) {
+ this.theta = theta;
+ this.phi = phi;
+ this.vector = vector;
+ }
+
+ /** Build the normalized vector corresponding to spherical coordinates.
+ * @param theta azimuthal angle \( \theta \) in the x-y plane
+ * @param phi polar angle \( \varphi \)
+ * @return normalized vector
+ * @exception OutOfRangeException if \( \varphi \) is not in the [\( 0; \pi \)] range
+ */
+ private static Vector3D vector(final double theta, final double phi)
+ throws OutOfRangeException {
+
+ if (phi < 0 || phi > FastMath.PI) {
+ throw new OutOfRangeException(phi, 0, FastMath.PI);
+ }
+
+ final double cosTheta = FastMath.cos(theta);
+ final double sinTheta = FastMath.sin(theta);
+ final double cosPhi = FastMath.cos(phi);
+ final double sinPhi = FastMath.sin(phi);
+
+ return new Vector3D(cosTheta * sinPhi, sinTheta * sinPhi, cosPhi);
+
+ }
+
+ /** Get the azimuthal angle \( \theta \) in the x-y plane.
+ * @return azimuthal angle \( \theta \) in the x-y plane
+ * @see #S2Point(double, double)
+ */
+ public double getTheta() {
+ return theta;
+ }
+
+ /** Get the polar angle \( \varphi \).
+ * @return polar angle \( \varphi \)
+ * @see #S2Point(double, double)
+ */
+ public double getPhi() {
+ return phi;
+ }
+
+ /** Get the corresponding normalized vector in the 3D euclidean space.
+ * @return normalized vector
+ */
+ public Vector3D getVector() {
+ return vector;
+ }
+
+ /** {@inheritDoc} */
+ public Space getSpace() {
+ return Sphere2D.getInstance();
+ }
+
+ /** {@inheritDoc} */
+ public boolean isNaN() {
+ return Double.isNaN(theta) || Double.isNaN(phi);
+ }
+
+ /** Get the opposite of the instance.
+ * @return a new vector which is opposite to the instance
+ */
+ public S2Point negate() {
+ return new S2Point(-theta, FastMath.PI - phi, vector.negate());
+ }
+
+ /** {@inheritDoc} */
+ public double distance(final Point<Sphere2D> point) {
+ return distance(this, (S2Point) point);
+ }
+
+ /** Compute the distance (angular separation) between two points.
+ * @param p1 first vector
+ * @param p2 second vector
+ * @return the angular separation between p1 and p2
+ */
+ public static double distance(S2Point p1, S2Point p2) {
+ return Vector3D.angle(p1.vector, p2.vector);
+ }
+
+ /**
+ * Test for the equality of two points on the 2-sphere.
+ * <p>
+ * If all coordinates of two points are exactly the same, and none are
+ * <code>Double.NaN</code>, the two points are considered to be equal.
+ * </p>
+ * <p>
+ * <code>NaN</code> coordinates are considered to affect globally the vector
+ * and be equals to each other - i.e, if either (or all) coordinates of the
+ * 2D vector are equal to <code>Double.NaN</code>, the 2D vector is equal to
+ * {@link #NaN}.
+ * </p>
+ *
+ * @param other Object to test for equality to this
+ * @return true if two points on the 2-sphere objects are equal, false if
+ * object is null, not an instance of S2Point, or
+ * not equal to this S2Point instance
+ *
+ */
+ @Override
+ public boolean equals(Object other) {
+
+ if (this == other) {
+ return true;
+ }
+
+ if (other instanceof S2Point) {
+ final S2Point rhs = (S2Point) other;
+ if (rhs.isNaN()) {
+ return this.isNaN();
+ }
+
+ return (theta == rhs.theta) && (phi == rhs.phi);
+ }
+ return false;
+ }
+
+ /**
+ * Get a hashCode for the 2D vector.
+ * <p>
+ * All NaN values have the same hash code.</p>
+ *
+ * @return a hash code value for this object
+ */
+ @Override
+ public int hashCode() {
+ if (isNaN()) {
+ return 542;
+ }
+ return 134 * (37 * MathUtils.hash(theta) + MathUtils.hash(phi));
+ }
+
+}
diff --git a/src/main/java/org/apache/commons/math3/geometry/spherical/twod/Sphere2D.java b/src/main/java/org/apache/commons/math3/geometry/spherical/twod/Sphere2D.java
new file mode 100644
index 0000000..93ff04e
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/geometry/spherical/twod/Sphere2D.java
@@ -0,0 +1,80 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math3.geometry.spherical.twod;
+
+import java.io.Serializable;
+
+import org.apache.commons.math3.geometry.Space;
+import org.apache.commons.math3.geometry.spherical.oned.Sphere1D;
+
+/**
+ * This class implements a two-dimensional sphere (i.e. the regular sphere).
+ * <p>
+ * We use here the topologists definition of the 2-sphere (see
+ * <a href="http://mathworld.wolfram.com/Sphere.html">Sphere</a> on
+ * MathWorld), i.e. the 2-sphere is the two-dimensional surface
+ * defined in 3D as x<sup>2</sup>+y<sup>2</sup>+z<sup>2</sup>=1.
+ * </p>
+ * @since 3.3
+ */
+public class Sphere2D implements Serializable, Space {
+
+ /** Serializable version identifier. */
+ private static final long serialVersionUID = 20131218L;
+
+ /** Private constructor for the singleton.
+ */
+ private Sphere2D() {
+ }
+
+ /** Get the unique instance.
+ * @return the unique instance
+ */
+ public static Sphere2D getInstance() {
+ return LazyHolder.INSTANCE;
+ }
+
+ /** {@inheritDoc} */
+ public int getDimension() {
+ return 2;
+ }
+
+ /** {@inheritDoc} */
+ public Sphere1D getSubSpace() {
+ return Sphere1D.getInstance();
+ }
+
+ // CHECKSTYLE: stop HideUtilityClassConstructor
+ /** Holder for the instance.
+ * <p>We use here the Initialization On Demand Holder Idiom.</p>
+ */
+ private static class LazyHolder {
+ /** Cached field instance. */
+ private static final Sphere2D INSTANCE = new Sphere2D();
+ }
+ // CHECKSTYLE: resume HideUtilityClassConstructor
+
+ /** Handle deserialization of the singleton.
+ * @return the singleton instance
+ */
+ private Object readResolve() {
+ // return the singleton instance
+ return LazyHolder.INSTANCE;
+ }
+
+}
diff --git a/src/main/java/org/apache/commons/math3/geometry/spherical/twod/SphericalPolygonsSet.java b/src/main/java/org/apache/commons/math3/geometry/spherical/twod/SphericalPolygonsSet.java
new file mode 100644
index 0000000..8e41659
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/geometry/spherical/twod/SphericalPolygonsSet.java
@@ -0,0 +1,565 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math3.geometry.spherical.twod;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.List;
+
+import org.apache.commons.math3.exception.MathIllegalStateException;
+import org.apache.commons.math3.geometry.enclosing.EnclosingBall;
+import org.apache.commons.math3.geometry.enclosing.WelzlEncloser;
+import org.apache.commons.math3.geometry.euclidean.threed.Euclidean3D;
+import org.apache.commons.math3.geometry.euclidean.threed.Rotation;
+import org.apache.commons.math3.geometry.euclidean.threed.RotationConvention;
+import org.apache.commons.math3.geometry.euclidean.threed.SphereGenerator;
+import org.apache.commons.math3.geometry.euclidean.threed.Vector3D;
+import org.apache.commons.math3.geometry.partitioning.AbstractRegion;
+import org.apache.commons.math3.geometry.partitioning.BSPTree;
+import org.apache.commons.math3.geometry.partitioning.BoundaryProjection;
+import org.apache.commons.math3.geometry.partitioning.RegionFactory;
+import org.apache.commons.math3.geometry.partitioning.SubHyperplane;
+import org.apache.commons.math3.geometry.spherical.oned.Sphere1D;
+import org.apache.commons.math3.util.FastMath;
+import org.apache.commons.math3.util.MathUtils;
+
+/** This class represents a region on the 2-sphere: a set of spherical polygons.
+ * @since 3.3
+ */
+public class SphericalPolygonsSet extends AbstractRegion<Sphere2D, Sphere1D> {
+
+ /** Boundary defined as an array of closed loops start vertices. */
+ private List<Vertex> loops;
+
+ /** Build a polygons set representing the whole real 2-sphere.
+ * @param tolerance below which points are consider to be identical
+ */
+ public SphericalPolygonsSet(final double tolerance) {
+ super(tolerance);
+ }
+
+ /** Build a polygons set representing a hemisphere.
+ * @param pole pole of the hemisphere (the pole is in the inside half)
+ * @param tolerance below which points are consider to be identical
+ */
+ public SphericalPolygonsSet(final Vector3D pole, final double tolerance) {
+ super(new BSPTree<Sphere2D>(new Circle(pole, tolerance).wholeHyperplane(),
+ new BSPTree<Sphere2D>(Boolean.FALSE),
+ new BSPTree<Sphere2D>(Boolean.TRUE),
+ null),
+ tolerance);
+ }
+
+ /** Build a polygons set representing a regular polygon.
+ * @param center center of the polygon (the center is in the inside half)
+ * @param meridian point defining the reference meridian for first polygon vertex
+ * @param outsideRadius distance of the vertices to the center
+ * @param n number of sides of the polygon
+ * @param tolerance below which points are consider to be identical
+ */
+ public SphericalPolygonsSet(final Vector3D center, final Vector3D meridian,
+ final double outsideRadius, final int n,
+ final double tolerance) {
+ this(tolerance, createRegularPolygonVertices(center, meridian, outsideRadius, n));
+ }
+
+ /** Build a polygons set from a BSP tree.
+ * <p>The leaf nodes of the BSP tree <em>must</em> have a
+ * {@code Boolean} attribute representing the inside status of
+ * the corresponding cell (true for inside cells, false for outside
+ * cells). In order to avoid building too many small objects, it is
+ * recommended to use the predefined constants
+ * {@code Boolean.TRUE} and {@code Boolean.FALSE}</p>
+ * @param tree inside/outside BSP tree representing the region
+ * @param tolerance below which points are consider to be identical
+ */
+ public SphericalPolygonsSet(final BSPTree<Sphere2D> tree, final double tolerance) {
+ super(tree, tolerance);
+ }
+
+ /** Build a polygons set from a Boundary REPresentation (B-rep).
+ * <p>The boundary is provided as a collection of {@link
+ * SubHyperplane sub-hyperplanes}. Each sub-hyperplane has the
+ * interior part of the region on its minus side and the exterior on
+ * its plus side.</p>
+ * <p>The boundary elements can be in any order, and can form
+ * several non-connected sets (like for example polygons with holes
+ * or a set of disjoint polygons considered as a whole). In
+ * fact, the elements do not even need to be connected together
+ * (their topological connections are not used here). However, if the
+ * boundary does not really separate an inside open from an outside
+ * open (open having here its topological meaning), then subsequent
+ * calls to the {@link
+ * org.apache.commons.math3.geometry.partitioning.Region#checkPoint(org.apache.commons.math3.geometry.Point)
+ * checkPoint} method will not be meaningful anymore.</p>
+ * <p>If the boundary is empty, the region will represent the whole
+ * space.</p>
+ * @param boundary collection of boundary elements, as a
+ * collection of {@link SubHyperplane SubHyperplane} objects
+ * @param tolerance below which points are consider to be identical
+ */
+ public SphericalPolygonsSet(final Collection<SubHyperplane<Sphere2D>> boundary, final double tolerance) {
+ super(boundary, tolerance);
+ }
+
+ /** Build a polygon from a simple list of vertices.
+ * <p>The boundary is provided as a list of points considering to
+ * represent the vertices of a simple loop. The interior part of the
+ * region is on the left side of this path and the exterior is on its
+ * right side.</p>
+ * <p>This constructor does not handle polygons with a boundary
+ * forming several disconnected paths (such as polygons with holes).</p>
+ * <p>For cases where this simple constructor applies, it is expected to
+ * be numerically more robust than the {@link #SphericalPolygonsSet(Collection,
+ * double) general constructor} using {@link SubHyperplane subhyperplanes}.</p>
+ * <p>If the list is empty, the region will represent the whole
+ * space.</p>
+ * <p>
+ * Polygons with thin pikes or dents are inherently difficult to handle because
+ * they involve circles with almost opposite directions at some vertices. Polygons
+ * whose vertices come from some physical measurement with noise are also
+ * difficult because an edge that should be straight may be broken in lots of
+ * different pieces with almost equal directions. In both cases, computing the
+ * circles intersections is not numerically robust due to the almost 0 or almost
+ * &pi; angle. Such cases need to carefully adjust the {@code hyperplaneThickness}
+ * parameter. A too small value would often lead to completely wrong polygons
+ * with large area wrongly identified as inside or outside. Large values are
+ * often much safer. As a rule of thumb, a value slightly below the size of the
+ * most accurate detail needed is a good value for the {@code hyperplaneThickness}
+ * parameter.
+ * </p>
+ * @param hyperplaneThickness tolerance below which points are considered to
+ * belong to the hyperplane (which is therefore more a slab)
+ * @param vertices vertices of the simple loop boundary
+ */
+ public SphericalPolygonsSet(final double hyperplaneThickness, final S2Point ... vertices) {
+ super(verticesToTree(hyperplaneThickness, vertices), hyperplaneThickness);
+ }
+
+ /** Build the vertices representing a regular polygon.
+ * @param center center of the polygon (the center is in the inside half)
+ * @param meridian point defining the reference meridian for first polygon vertex
+ * @param outsideRadius distance of the vertices to the center
+ * @param n number of sides of the polygon
+ * @return vertices array
+ */
+ private static S2Point[] createRegularPolygonVertices(final Vector3D center, final Vector3D meridian,
+ final double outsideRadius, final int n) {
+ final S2Point[] array = new S2Point[n];
+ final Rotation r0 = new Rotation(Vector3D.crossProduct(center, meridian),
+ outsideRadius, RotationConvention.VECTOR_OPERATOR);
+ array[0] = new S2Point(r0.applyTo(center));
+
+ final Rotation r = new Rotation(center, MathUtils.TWO_PI / n, RotationConvention.VECTOR_OPERATOR);
+ for (int i = 1; i < n; ++i) {
+ array[i] = new S2Point(r.applyTo(array[i - 1].getVector()));
+ }
+
+ return array;
+ }
+
+ /** Build the BSP tree of a polygons set from a simple list of vertices.
+ * <p>The boundary is provided as a list of points considering to
+ * represent the vertices of a simple loop. The interior part of the
+ * region is on the left side of this path and the exterior is on its
+ * right side.</p>
+ * <p>This constructor does not handle polygons with a boundary
+ * forming several disconnected paths (such as polygons with holes).</p>
+ * <p>This constructor handles only polygons with edges strictly shorter
+ * than \( \pi \). If longer edges are needed, they need to be broken up
+ * in smaller sub-edges so this constraint holds.</p>
+ * <p>For cases where this simple constructor applies, it is expected to
+ * be numerically more robust than the {@link #PolygonsSet(Collection) general
+ * constructor} using {@link SubHyperplane subhyperplanes}.</p>
+ * @param hyperplaneThickness tolerance below which points are consider to
+ * belong to the hyperplane (which is therefore more a slab)
+ * @param vertices vertices of the simple loop boundary
+ * @return the BSP tree of the input vertices
+ */
+ private static BSPTree<Sphere2D> verticesToTree(final double hyperplaneThickness,
+ final S2Point ... vertices) {
+
+ final int n = vertices.length;
+ if (n == 0) {
+ // the tree represents the whole space
+ return new BSPTree<Sphere2D>(Boolean.TRUE);
+ }
+
+ // build the vertices
+ final Vertex[] vArray = new Vertex[n];
+ for (int i = 0; i < n; ++i) {
+ vArray[i] = new Vertex(vertices[i]);
+ }
+
+ // build the edges
+ List<Edge> edges = new ArrayList<Edge>(n);
+ Vertex end = vArray[n - 1];
+ for (int i = 0; i < n; ++i) {
+
+ // get the endpoints of the edge
+ final Vertex start = end;
+ end = vArray[i];
+
+ // get the circle supporting the edge, taking care not to recreate it
+ // if it was already created earlier due to another edge being aligned
+ // with the current one
+ Circle circle = start.sharedCircleWith(end);
+ if (circle == null) {
+ circle = new Circle(start.getLocation(), end.getLocation(), hyperplaneThickness);
+ }
+
+ // create the edge and store it
+ edges.add(new Edge(start, end,
+ Vector3D.angle(start.getLocation().getVector(),
+ end.getLocation().getVector()),
+ circle));
+
+ // check if another vertex also happens to be on this circle
+ for (final Vertex vertex : vArray) {
+ if (vertex != start && vertex != end &&
+ FastMath.abs(circle.getOffset(vertex.getLocation())) <= hyperplaneThickness) {
+ vertex.bindWith(circle);
+ }
+ }
+
+ }
+
+ // build the tree top-down
+ final BSPTree<Sphere2D> tree = new BSPTree<Sphere2D>();
+ insertEdges(hyperplaneThickness, tree, edges);
+
+ return tree;
+
+ }
+
+ /** Recursively build a tree by inserting cut sub-hyperplanes.
+ * @param hyperplaneThickness tolerance below which points are considered to
+ * belong to the hyperplane (which is therefore more a slab)
+ * @param node current tree node (it is a leaf node at the beginning
+ * of the call)
+ * @param edges list of edges to insert in the cell defined by this node
+ * (excluding edges not belonging to the cell defined by this node)
+ */
+ private static void insertEdges(final double hyperplaneThickness,
+ final BSPTree<Sphere2D> node,
+ final List<Edge> edges) {
+
+ // find an edge with an hyperplane that can be inserted in the node
+ int index = 0;
+ Edge inserted = null;
+ while (inserted == null && index < edges.size()) {
+ inserted = edges.get(index++);
+ if (!node.insertCut(inserted.getCircle())) {
+ inserted = null;
+ }
+ }
+
+ if (inserted == null) {
+ // no suitable edge was found, the node remains a leaf node
+ // we need to set its inside/outside boolean indicator
+ final BSPTree<Sphere2D> parent = node.getParent();
+ if (parent == null || node == parent.getMinus()) {
+ node.setAttribute(Boolean.TRUE);
+ } else {
+ node.setAttribute(Boolean.FALSE);
+ }
+ return;
+ }
+
+ // we have split the node by inserting an edge as a cut sub-hyperplane
+ // distribute the remaining edges in the two sub-trees
+ final List<Edge> outsideList = new ArrayList<Edge>();
+ final List<Edge> insideList = new ArrayList<Edge>();
+ for (final Edge edge : edges) {
+ if (edge != inserted) {
+ edge.split(inserted.getCircle(), outsideList, insideList);
+ }
+ }
+
+ // recurse through lower levels
+ if (!outsideList.isEmpty()) {
+ insertEdges(hyperplaneThickness, node.getPlus(), outsideList);
+ } else {
+ node.getPlus().setAttribute(Boolean.FALSE);
+ }
+ if (!insideList.isEmpty()) {
+ insertEdges(hyperplaneThickness, node.getMinus(), insideList);
+ } else {
+ node.getMinus().setAttribute(Boolean.TRUE);
+ }
+
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public SphericalPolygonsSet buildNew(final BSPTree<Sphere2D> tree) {
+ return new SphericalPolygonsSet(tree, getTolerance());
+ }
+
+ /** {@inheritDoc}
+ * @exception MathIllegalStateException if the tolerance setting does not allow to build
+ * a clean non-ambiguous boundary
+ */
+ @Override
+ protected void computeGeometricalProperties() throws MathIllegalStateException {
+
+ final BSPTree<Sphere2D> tree = getTree(true);
+
+ if (tree.getCut() == null) {
+
+ // the instance has a single cell without any boundaries
+
+ if (tree.getCut() == null && (Boolean) tree.getAttribute()) {
+ // the instance covers the whole space
+ setSize(4 * FastMath.PI);
+ setBarycenter(new S2Point(0, 0));
+ } else {
+ setSize(0);
+ setBarycenter(S2Point.NaN);
+ }
+
+ } else {
+
+ // the instance has a boundary
+ final PropertiesComputer pc = new PropertiesComputer(getTolerance());
+ tree.visit(pc);
+ setSize(pc.getArea());
+ setBarycenter(pc.getBarycenter());
+
+ }
+
+ }
+
+ /** Get the boundary loops of the polygon.
+ * <p>The polygon boundary can be represented as a list of closed loops,
+ * each loop being given by exactly one of its vertices. From each loop
+ * start vertex, one can follow the loop by finding the outgoing edge,
+ * then the end vertex, then the next outgoing edge ... until the start
+ * vertex of the loop (exactly the same instance) is found again once
+ * the full loop has been visited.</p>
+ * <p>If the polygon has no boundary at all, a zero length loop
+ * array will be returned.</p>
+ * <p>If the polygon is a simple one-piece polygon, then the returned
+ * array will contain a single vertex.
+ * </p>
+ * <p>All edges in the various loops have the inside of the region on
+ * their left side (i.e. toward their pole) and the outside on their
+ * right side (i.e. away from their pole) when moving in the underlying
+ * circle direction. This means that the closed loops obey the direct
+ * trigonometric orientation.</p>
+ * @return boundary of the polygon, organized as an unmodifiable list of loops start vertices.
+ * @exception MathIllegalStateException if the tolerance setting does not allow to build
+ * a clean non-ambiguous boundary
+ * @see Vertex
+ * @see Edge
+ */
+ public List<Vertex> getBoundaryLoops() throws MathIllegalStateException {
+
+ if (loops == null) {
+ if (getTree(false).getCut() == null) {
+ loops = Collections.emptyList();
+ } else {
+
+ // sort the arcs according to their start point
+ final BSPTree<Sphere2D> root = getTree(true);
+ final EdgesBuilder visitor = new EdgesBuilder(root, getTolerance());
+ root.visit(visitor);
+ final List<Edge> edges = visitor.getEdges();
+
+
+ // convert the list of all edges into a list of start vertices
+ loops = new ArrayList<Vertex>();
+ while (!edges.isEmpty()) {
+
+ // this is an edge belonging to a new loop, store it
+ Edge edge = edges.get(0);
+ final Vertex startVertex = edge.getStart();
+ loops.add(startVertex);
+
+ // remove all remaining edges in the same loop
+ do {
+
+ // remove one edge
+ for (final Iterator<Edge> iterator = edges.iterator(); iterator.hasNext();) {
+ if (iterator.next() == edge) {
+ iterator.remove();
+ break;
+ }
+ }
+
+ // go to next edge following the boundary loop
+ edge = edge.getEnd().getOutgoing();
+
+ } while (edge.getStart() != startVertex);
+
+ }
+
+ }
+ }
+
+ return Collections.unmodifiableList(loops);
+
+ }
+
+ /** Get a spherical cap enclosing the polygon.
+ * <p>
+ * This method is intended as a first test to quickly identify points
+ * that are guaranteed to be outside of the region, hence performing a full
+ * {@link #checkPoint(org.apache.commons.math3.geometry.Vector) checkPoint}
+ * only if the point status remains undecided after the quick check. It is
+ * is therefore mostly useful to speed up computation for small polygons with
+ * complex shapes (say a country boundary on Earth), as the spherical cap will
+ * be small and hence will reliably identify a large part of the sphere as outside,
+ * whereas the full check can be more computing intensive. A typical use case is
+ * therefore:
+ * </p>
+ * <pre>
+ * // compute region, plus an enclosing spherical cap
+ * SphericalPolygonsSet complexShape = ...;
+ * EnclosingBall<Sphere2D, S2Point> cap = complexShape.getEnclosingCap();
+ *
+ * // check lots of points
+ * for (Vector3D p : points) {
+ *
+ * final Location l;
+ * if (cap.contains(p)) {
+ * // we cannot be sure where the point is
+ * // we need to perform the full computation
+ * l = complexShape.checkPoint(v);
+ * } else {
+ * // no need to do further computation,
+ * // we already know the point is outside
+ * l = Location.OUTSIDE;
+ * }
+ *
+ * // use l ...
+ *
+ * }
+ * </pre>
+ * <p>
+ * In the special cases of empty or whole sphere polygons, special
+ * spherical caps are returned, with angular radius set to negative
+ * or positive infinity so the {@link
+ * EnclosingBall#contains(org.apache.commons.math3.geometry.Point) ball.contains(point)}
+ * method return always false or true.
+ * </p>
+ * <p>
+ * This method is <em>not</em> guaranteed to return the smallest enclosing cap.
+ * </p>
+ * @return a spherical cap enclosing the polygon
+ */
+ public EnclosingBall<Sphere2D, S2Point> getEnclosingCap() {
+
+ // handle special cases first
+ if (isEmpty()) {
+ return new EnclosingBall<Sphere2D, S2Point>(S2Point.PLUS_K, Double.NEGATIVE_INFINITY);
+ }
+ if (isFull()) {
+ return new EnclosingBall<Sphere2D, S2Point>(S2Point.PLUS_K, Double.POSITIVE_INFINITY);
+ }
+
+ // as the polygons is neither empty nor full, it has some boundaries and cut hyperplanes
+ final BSPTree<Sphere2D> root = getTree(false);
+ if (isEmpty(root.getMinus()) && isFull(root.getPlus())) {
+ // the polygon covers an hemisphere, and its boundary is one 2Ï€ long edge
+ final Circle circle = (Circle) root.getCut().getHyperplane();
+ return new EnclosingBall<Sphere2D, S2Point>(new S2Point(circle.getPole()).negate(),
+ 0.5 * FastMath.PI);
+ }
+ if (isFull(root.getMinus()) && isEmpty(root.getPlus())) {
+ // the polygon covers an hemisphere, and its boundary is one 2Ï€ long edge
+ final Circle circle = (Circle) root.getCut().getHyperplane();
+ return new EnclosingBall<Sphere2D, S2Point>(new S2Point(circle.getPole()),
+ 0.5 * FastMath.PI);
+ }
+
+ // gather some inside points, to be used by the encloser
+ final List<Vector3D> points = getInsidePoints();
+
+ // extract points from the boundary loops, to be used by the encloser as well
+ final List<Vertex> boundary = getBoundaryLoops();
+ for (final Vertex loopStart : boundary) {
+ int count = 0;
+ for (Vertex v = loopStart; count == 0 || v != loopStart; v = v.getOutgoing().getEnd()) {
+ ++count;
+ points.add(v.getLocation().getVector());
+ }
+ }
+
+ // find the smallest enclosing 3D sphere
+ final SphereGenerator generator = new SphereGenerator();
+ final WelzlEncloser<Euclidean3D, Vector3D> encloser =
+ new WelzlEncloser<Euclidean3D, Vector3D>(getTolerance(), generator);
+ EnclosingBall<Euclidean3D, Vector3D> enclosing3D = encloser.enclose(points);
+ final Vector3D[] support3D = enclosing3D.getSupport();
+
+ // convert to 3D sphere to spherical cap
+ final double r = enclosing3D.getRadius();
+ final double h = enclosing3D.getCenter().getNorm();
+ if (h < getTolerance()) {
+ // the 3D sphere is centered on the unit sphere and covers it
+ // fall back to a crude approximation, based only on outside convex cells
+ EnclosingBall<Sphere2D, S2Point> enclosingS2 =
+ new EnclosingBall<Sphere2D, S2Point>(S2Point.PLUS_K, Double.POSITIVE_INFINITY);
+ for (Vector3D outsidePoint : getOutsidePoints()) {
+ final S2Point outsideS2 = new S2Point(outsidePoint);
+ final BoundaryProjection<Sphere2D> projection = projectToBoundary(outsideS2);
+ if (FastMath.PI - projection.getOffset() < enclosingS2.getRadius()) {
+ enclosingS2 = new EnclosingBall<Sphere2D, S2Point>(outsideS2.negate(),
+ FastMath.PI - projection.getOffset(),
+ (S2Point) projection.getProjected());
+ }
+ }
+ return enclosingS2;
+ }
+ final S2Point[] support = new S2Point[support3D.length];
+ for (int i = 0; i < support3D.length; ++i) {
+ support[i] = new S2Point(support3D[i]);
+ }
+
+ final EnclosingBall<Sphere2D, S2Point> enclosingS2 =
+ new EnclosingBall<Sphere2D, S2Point>(new S2Point(enclosing3D.getCenter()),
+ FastMath.acos((1 + h * h - r * r) / (2 * h)),
+ support);
+
+ return enclosingS2;
+
+ }
+
+ /** Gather some inside points.
+ * @return list of points known to be strictly in all inside convex cells
+ */
+ private List<Vector3D> getInsidePoints() {
+ final PropertiesComputer pc = new PropertiesComputer(getTolerance());
+ getTree(true).visit(pc);
+ return pc.getConvexCellsInsidePoints();
+ }
+
+ /** Gather some outside points.
+ * @return list of points known to be strictly in all outside convex cells
+ */
+ private List<Vector3D> getOutsidePoints() {
+ final SphericalPolygonsSet complement =
+ (SphericalPolygonsSet) new RegionFactory<Sphere2D>().getComplement(this);
+ final PropertiesComputer pc = new PropertiesComputer(getTolerance());
+ complement.getTree(true).visit(pc);
+ return pc.getConvexCellsInsidePoints();
+ }
+
+}
diff --git a/src/main/java/org/apache/commons/math3/geometry/spherical/twod/SubCircle.java b/src/main/java/org/apache/commons/math3/geometry/spherical/twod/SubCircle.java
new file mode 100644
index 0000000..97164cc
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/geometry/spherical/twod/SubCircle.java
@@ -0,0 +1,72 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math3.geometry.spherical.twod;
+
+import org.apache.commons.math3.geometry.euclidean.threed.Vector3D;
+import org.apache.commons.math3.geometry.partitioning.AbstractSubHyperplane;
+import org.apache.commons.math3.geometry.partitioning.Hyperplane;
+import org.apache.commons.math3.geometry.partitioning.Region;
+import org.apache.commons.math3.geometry.spherical.oned.Arc;
+import org.apache.commons.math3.geometry.spherical.oned.ArcsSet;
+import org.apache.commons.math3.geometry.spherical.oned.Sphere1D;
+import org.apache.commons.math3.util.FastMath;
+
+/** This class represents a sub-hyperplane for {@link Circle}.
+ * @since 3.3
+ */
+public class SubCircle extends AbstractSubHyperplane<Sphere2D, Sphere1D> {
+
+ /** Simple constructor.
+ * @param hyperplane underlying hyperplane
+ * @param remainingRegion remaining region of the hyperplane
+ */
+ public SubCircle(final Hyperplane<Sphere2D> hyperplane,
+ final Region<Sphere1D> remainingRegion) {
+ super(hyperplane, remainingRegion);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ protected AbstractSubHyperplane<Sphere2D, Sphere1D> buildNew(final Hyperplane<Sphere2D> hyperplane,
+ final Region<Sphere1D> remainingRegion) {
+ return new SubCircle(hyperplane, remainingRegion);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public SplitSubHyperplane<Sphere2D> split(final Hyperplane<Sphere2D> hyperplane) {
+
+ final Circle thisCircle = (Circle) getHyperplane();
+ final Circle otherCircle = (Circle) hyperplane;
+ final double angle = Vector3D.angle(thisCircle.getPole(), otherCircle.getPole());
+
+ if (angle < thisCircle.getTolerance() || angle > FastMath.PI - thisCircle.getTolerance()) {
+ // the two circles are aligned or opposite
+ return new SplitSubHyperplane<Sphere2D>(null, null);
+ } else {
+ // the two circles intersect each other
+ final Arc arc = thisCircle.getInsideArc(otherCircle);
+ final ArcsSet.Split split = ((ArcsSet) getRemainingRegion()).split(arc);
+ final ArcsSet plus = split.getPlus();
+ final ArcsSet minus = split.getMinus();
+ return new SplitSubHyperplane<Sphere2D>(plus == null ? null : new SubCircle(thisCircle.copySelf(), plus),
+ minus == null ? null : new SubCircle(thisCircle.copySelf(), minus));
+ }
+
+ }
+
+}
diff --git a/src/main/java/org/apache/commons/math3/geometry/spherical/twod/Vertex.java b/src/main/java/org/apache/commons/math3/geometry/spherical/twod/Vertex.java
new file mode 100644
index 0000000..3003da8
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/geometry/spherical/twod/Vertex.java
@@ -0,0 +1,124 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math3.geometry.spherical.twod;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/** Spherical polygons boundary vertex.
+ * @see SphericalPolygonsSet#getBoundaryLoops()
+ * @see Edge
+ * @since 3.3
+ */
+public class Vertex {
+
+ /** Vertex location. */
+ private final S2Point location;
+
+ /** Incoming edge. */
+ private Edge incoming;
+
+ /** Outgoing edge. */
+ private Edge outgoing;
+
+ /** Circles bound with this vertex. */
+ private final List<Circle> circles;
+
+ /** Build a non-processed vertex not owned by any node yet.
+ * @param location vertex location
+ */
+ Vertex(final S2Point location) {
+ this.location = location;
+ this.incoming = null;
+ this.outgoing = null;
+ this.circles = new ArrayList<Circle>();
+ }
+
+ /** Get Vertex location.
+ * @return vertex location
+ */
+ public S2Point getLocation() {
+ return location;
+ }
+
+ /** Bind a circle considered to contain this vertex.
+ * @param circle circle to bind with this vertex
+ */
+ void bindWith(final Circle circle) {
+ circles.add(circle);
+ }
+
+ /** Get the common circle bound with both the instance and another vertex, if any.
+ * <p>
+ * When two vertices are both bound to the same circle, this means they are
+ * already handled by node associated with this circle, so there is no need
+ * to create a cut hyperplane for them.
+ * </p>
+ * @param vertex other vertex to check instance against
+ * @return circle bound with both the instance and another vertex, or null if the
+ * two vertices do not share a circle yet
+ */
+ Circle sharedCircleWith(final Vertex vertex) {
+ for (final Circle circle1 : circles) {
+ for (final Circle circle2 : vertex.circles) {
+ if (circle1 == circle2) {
+ return circle1;
+ }
+ }
+ }
+ return null;
+ }
+
+ /** Set incoming edge.
+ * <p>
+ * The circle supporting the incoming edge is automatically bound
+ * with the instance.
+ * </p>
+ * @param incoming incoming edge
+ */
+ void setIncoming(final Edge incoming) {
+ this.incoming = incoming;
+ bindWith(incoming.getCircle());
+ }
+
+ /** Get incoming edge.
+ * @return incoming edge
+ */
+ public Edge getIncoming() {
+ return incoming;
+ }
+
+ /** Set outgoing edge.
+ * <p>
+ * The circle supporting the outgoing edge is automatically bound
+ * with the instance.
+ * </p>
+ * @param outgoing outgoing edge
+ */
+ void setOutgoing(final Edge outgoing) {
+ this.outgoing = outgoing;
+ bindWith(outgoing.getCircle());
+ }
+
+ /** Get outgoing edge.
+ * @return outgoing edge
+ */
+ public Edge getOutgoing() {
+ return outgoing;
+ }
+
+}
diff --git a/src/main/java/org/apache/commons/math3/geometry/spherical/twod/package-info.java b/src/main/java/org/apache/commons/math3/geometry/spherical/twod/package-info.java
new file mode 100644
index 0000000..3f3c5b0
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/geometry/spherical/twod/package-info.java
@@ -0,0 +1,30 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/**
+ *
+ * <p>
+ * This package provides basic geometry components on the 2-sphere.
+ * </p>
+ * <p>
+ * We use here the topologists definition of the 2-sphere (see
+ * <a href="http://mathworld.wolfram.com/Sphere.html">Sphere</a> on
+ * MathWorld), i.e. the 2-sphere is the two-dimensional surface
+ * defined in 3D as x<sup>2</sup>+y<sup>2</sup>+z<sup>2</sup>=1.
+ * </p>
+ *
+ */
+package org.apache.commons.math3.geometry.spherical.twod;
diff --git a/src/main/java/org/apache/commons/math3/linear/AbstractFieldMatrix.java b/src/main/java/org/apache/commons/math3/linear/AbstractFieldMatrix.java
new file mode 100644
index 0000000..355d72d
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/linear/AbstractFieldMatrix.java
@@ -0,0 +1,1155 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math3.linear;
+
+import org.apache.commons.math3.Field;
+import org.apache.commons.math3.FieldElement;
+import org.apache.commons.math3.exception.DimensionMismatchException;
+import org.apache.commons.math3.exception.NoDataException;
+import org.apache.commons.math3.exception.NotPositiveException;
+import org.apache.commons.math3.exception.NotStrictlyPositiveException;
+import org.apache.commons.math3.exception.NullArgumentException;
+import org.apache.commons.math3.exception.NumberIsTooSmallException;
+import org.apache.commons.math3.exception.OutOfRangeException;
+import org.apache.commons.math3.exception.util.LocalizedFormats;
+import org.apache.commons.math3.util.MathArrays;
+
+import java.util.ArrayList;
+
+/**
+ * Basic implementation of {@link FieldMatrix} methods regardless of the underlying storage.
+ *
+ * <p>All the methods implemented here use {@link #getEntry(int, int)} to access matrix elements.
+ * Derived class can provide faster implementations.
+ *
+ * @param <T> Type of the field elements.
+ * @since 2.0
+ */
+public abstract class AbstractFieldMatrix<T extends FieldElement<T>> implements FieldMatrix<T> {
+ /** Field to which the elements belong. */
+ private final Field<T> field;
+
+ /** Constructor for use with Serializable */
+ protected AbstractFieldMatrix() {
+ field = null;
+ }
+
+ /**
+ * Creates a matrix with no data
+ *
+ * @param field field to which the elements belong
+ */
+ protected AbstractFieldMatrix(final Field<T> field) {
+ this.field = field;
+ }
+
+ /**
+ * Create a new FieldMatrix<T> with the supplied row and column dimensions.
+ *
+ * @param field Field to which the elements belong.
+ * @param rowDimension Number of rows in the new matrix.
+ * @param columnDimension Number of columns in the new matrix.
+ * @throws NotStrictlyPositiveException if row or column dimension is not positive.
+ */
+ protected AbstractFieldMatrix(
+ final Field<T> field, final int rowDimension, final int columnDimension)
+ throws NotStrictlyPositiveException {
+ if (rowDimension <= 0) {
+ throw new NotStrictlyPositiveException(LocalizedFormats.DIMENSION, rowDimension);
+ }
+ if (columnDimension <= 0) {
+ throw new NotStrictlyPositiveException(LocalizedFormats.DIMENSION, columnDimension);
+ }
+ this.field = field;
+ }
+
+ /**
+ * Get the elements type from an array.
+ *
+ * @param <T> Type of the field elements.
+ * @param d Data array.
+ * @return the field to which the array elements belong.
+ * @throws NullArgumentException if the array is {@code null}.
+ * @throws NoDataException if the array is empty.
+ */
+ protected static <T extends FieldElement<T>> Field<T> extractField(final T[][] d)
+ throws NoDataException, NullArgumentException {
+ if (d == null) {
+ throw new NullArgumentException();
+ }
+ if (d.length == 0) {
+ throw new NoDataException(LocalizedFormats.AT_LEAST_ONE_ROW);
+ }
+ if (d[0].length == 0) {
+ throw new NoDataException(LocalizedFormats.AT_LEAST_ONE_COLUMN);
+ }
+ return d[0][0].getField();
+ }
+
+ /**
+ * Get the elements type from an array.
+ *
+ * @param <T> Type of the field elements.
+ * @param d Data array.
+ * @return the field to which the array elements belong.
+ * @throws NoDataException if array is empty.
+ */
+ protected static <T extends FieldElement<T>> Field<T> extractField(final T[] d)
+ throws NoDataException {
+ if (d.length == 0) {
+ throw new NoDataException(LocalizedFormats.AT_LEAST_ONE_ROW);
+ }
+ return d[0].getField();
+ }
+
+ /**
+ * Build an array of elements.
+ *
+ * <p>Complete arrays are filled with field.getZero()
+ *
+ * @param <T> Type of the field elements
+ * @param field field to which array elements belong
+ * @param rows number of rows
+ * @param columns number of columns (may be negative to build partial arrays in the same way
+ * <code>new Field[rows][]</code> works)
+ * @return a new array
+ * @deprecated as of 3.2, replaced by {@link MathArrays#buildArray(Field, int, int)}
+ */
+ @Deprecated
+ protected static <T extends FieldElement<T>> T[][] buildArray(
+ final Field<T> field, final int rows, final int columns) {
+ return MathArrays.buildArray(field, rows, columns);
+ }
+
+ /**
+ * Build an array of elements.
+ *
+ * <p>Arrays are filled with field.getZero()
+ *
+ * @param <T> the type of the field elements
+ * @param field field to which array elements belong
+ * @param length of the array
+ * @return a new array
+ * @deprecated as of 3.2, replaced by {@link MathArrays#buildArray(Field, int)}
+ */
+ @Deprecated
+ protected static <T extends FieldElement<T>> T[] buildArray(
+ final Field<T> field, final int length) {
+ return MathArrays.buildArray(field, length);
+ }
+
+ /** {@inheritDoc} */
+ public Field<T> getField() {
+ return field;
+ }
+
+ /** {@inheritDoc} */
+ public abstract FieldMatrix<T> createMatrix(final int rowDimension, final int columnDimension)
+ throws NotStrictlyPositiveException;
+
+ /** {@inheritDoc} */
+ public abstract FieldMatrix<T> copy();
+
+ /** {@inheritDoc} */
+ public FieldMatrix<T> add(FieldMatrix<T> m) throws MatrixDimensionMismatchException {
+ // safety check
+ checkAdditionCompatible(m);
+
+ final int rowCount = getRowDimension();
+ final int columnCount = getColumnDimension();
+ final FieldMatrix<T> out = createMatrix(rowCount, columnCount);
+ for (int row = 0; row < rowCount; ++row) {
+ for (int col = 0; col < columnCount; ++col) {
+ out.setEntry(row, col, getEntry(row, col).add(m.getEntry(row, col)));
+ }
+ }
+
+ return out;
+ }
+
+ /** {@inheritDoc} */
+ public FieldMatrix<T> subtract(final FieldMatrix<T> m) throws MatrixDimensionMismatchException {
+ // safety check
+ checkSubtractionCompatible(m);
+
+ final int rowCount = getRowDimension();
+ final int columnCount = getColumnDimension();
+ final FieldMatrix<T> out = createMatrix(rowCount, columnCount);
+ for (int row = 0; row < rowCount; ++row) {
+ for (int col = 0; col < columnCount; ++col) {
+ out.setEntry(row, col, getEntry(row, col).subtract(m.getEntry(row, col)));
+ }
+ }
+
+ return out;
+ }
+
+ /** {@inheritDoc} */
+ public FieldMatrix<T> scalarAdd(final T d) {
+
+ final int rowCount = getRowDimension();
+ final int columnCount = getColumnDimension();
+ final FieldMatrix<T> out = createMatrix(rowCount, columnCount);
+ for (int row = 0; row < rowCount; ++row) {
+ for (int col = 0; col < columnCount; ++col) {
+ out.setEntry(row, col, getEntry(row, col).add(d));
+ }
+ }
+
+ return out;
+ }
+
+ /** {@inheritDoc} */
+ public FieldMatrix<T> scalarMultiply(final T d) {
+ final int rowCount = getRowDimension();
+ final int columnCount = getColumnDimension();
+ final FieldMatrix<T> out = createMatrix(rowCount, columnCount);
+ for (int row = 0; row < rowCount; ++row) {
+ for (int col = 0; col < columnCount; ++col) {
+ out.setEntry(row, col, getEntry(row, col).multiply(d));
+ }
+ }
+
+ return out;
+ }
+
+ /** {@inheritDoc} */
+ public FieldMatrix<T> multiply(final FieldMatrix<T> m) throws DimensionMismatchException {
+ // safety check
+ checkMultiplicationCompatible(m);
+
+ final int nRows = getRowDimension();
+ final int nCols = m.getColumnDimension();
+ final int nSum = getColumnDimension();
+ final FieldMatrix<T> out = createMatrix(nRows, nCols);
+ for (int row = 0; row < nRows; ++row) {
+ for (int col = 0; col < nCols; ++col) {
+ T sum = field.getZero();
+ for (int i = 0; i < nSum; ++i) {
+ sum = sum.add(getEntry(row, i).multiply(m.getEntry(i, col)));
+ }
+ out.setEntry(row, col, sum);
+ }
+ }
+
+ return out;
+ }
+
+ /** {@inheritDoc} */
+ public FieldMatrix<T> preMultiply(final FieldMatrix<T> m) throws DimensionMismatchException {
+ return m.multiply(this);
+ }
+
+ /** {@inheritDoc} */
+ public FieldMatrix<T> power(final int p) throws NonSquareMatrixException, NotPositiveException {
+ if (p < 0) {
+ throw new NotPositiveException(p);
+ }
+
+ if (!isSquare()) {
+ throw new NonSquareMatrixException(getRowDimension(), getColumnDimension());
+ }
+
+ if (p == 0) {
+ return MatrixUtils.createFieldIdentityMatrix(this.getField(), this.getRowDimension());
+ }
+
+ if (p == 1) {
+ return this.copy();
+ }
+
+ final int power = p - 1;
+
+ /*
+ * Only log_2(p) operations is used by doing as follows:
+ * 5^214 = 5^128 * 5^64 * 5^16 * 5^4 * 5^2
+ *
+ * In general, the same approach is used for A^p.
+ */
+
+ final char[] binaryRepresentation = Integer.toBinaryString(power).toCharArray();
+ final ArrayList<Integer> nonZeroPositions = new ArrayList<Integer>();
+
+ for (int i = 0; i < binaryRepresentation.length; ++i) {
+ if (binaryRepresentation[i] == '1') {
+ final int pos = binaryRepresentation.length - i - 1;
+ nonZeroPositions.add(pos);
+ }
+ }
+
+ ArrayList<FieldMatrix<T>> results =
+ new ArrayList<FieldMatrix<T>>(binaryRepresentation.length);
+
+ results.add(0, this.copy());
+
+ for (int i = 1; i < binaryRepresentation.length; ++i) {
+ final FieldMatrix<T> s = results.get(i - 1);
+ final FieldMatrix<T> r = s.multiply(s);
+ results.add(i, r);
+ }
+
+ FieldMatrix<T> result = this.copy();
+
+ for (Integer i : nonZeroPositions) {
+ result = result.multiply(results.get(i));
+ }
+
+ return result;
+ }
+
+ /** {@inheritDoc} */
+ public T[][] getData() {
+ final T[][] data = MathArrays.buildArray(field, getRowDimension(), getColumnDimension());
+
+ for (int i = 0; i < data.length; ++i) {
+ final T[] dataI = data[i];
+ for (int j = 0; j < dataI.length; ++j) {
+ dataI[j] = getEntry(i, j);
+ }
+ }
+
+ return data;
+ }
+
+ /** {@inheritDoc} */
+ public FieldMatrix<T> getSubMatrix(
+ final int startRow, final int endRow, final int startColumn, final int endColumn)
+ throws NumberIsTooSmallException, OutOfRangeException {
+ checkSubMatrixIndex(startRow, endRow, startColumn, endColumn);
+
+ final FieldMatrix<T> subMatrix =
+ createMatrix(endRow - startRow + 1, endColumn - startColumn + 1);
+ for (int i = startRow; i <= endRow; ++i) {
+ for (int j = startColumn; j <= endColumn; ++j) {
+ subMatrix.setEntry(i - startRow, j - startColumn, getEntry(i, j));
+ }
+ }
+
+ return subMatrix;
+ }
+
+ /** {@inheritDoc} */
+ public FieldMatrix<T> getSubMatrix(final int[] selectedRows, final int[] selectedColumns)
+ throws NoDataException, NullArgumentException, OutOfRangeException {
+
+ // safety checks
+ checkSubMatrixIndex(selectedRows, selectedColumns);
+
+ // copy entries
+ final FieldMatrix<T> subMatrix = createMatrix(selectedRows.length, selectedColumns.length);
+ subMatrix.walkInOptimizedOrder(
+ new DefaultFieldMatrixChangingVisitor<T>(field.getZero()) {
+
+ /** {@inheritDoc} */
+ @Override
+ public T visit(final int row, final int column, final T value) {
+ return getEntry(selectedRows[row], selectedColumns[column]);
+ }
+ });
+
+ return subMatrix;
+ }
+
+ /** {@inheritDoc} */
+ public void copySubMatrix(
+ final int startRow,
+ final int endRow,
+ final int startColumn,
+ final int endColumn,
+ final T[][] destination)
+ throws MatrixDimensionMismatchException,
+ NumberIsTooSmallException,
+ OutOfRangeException {
+ // safety checks
+ checkSubMatrixIndex(startRow, endRow, startColumn, endColumn);
+ final int rowsCount = endRow + 1 - startRow;
+ final int columnsCount = endColumn + 1 - startColumn;
+ if ((destination.length < rowsCount) || (destination[0].length < columnsCount)) {
+ throw new MatrixDimensionMismatchException(
+ destination.length, destination[0].length, rowsCount, columnsCount);
+ }
+
+ // copy entries
+ walkInOptimizedOrder(
+ new DefaultFieldMatrixPreservingVisitor<T>(field.getZero()) {
+
+ /** Initial row index. */
+ private int startRow;
+
+ /** Initial column index. */
+ private int startColumn;
+
+ /** {@inheritDoc} */
+ @Override
+ public void start(
+ final int rows,
+ final int columns,
+ final int startRow,
+ final int endRow,
+ final int startColumn,
+ final int endColumn) {
+ this.startRow = startRow;
+ this.startColumn = startColumn;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void visit(final int row, final int column, final T value) {
+ destination[row - startRow][column - startColumn] = value;
+ }
+ },
+ startRow,
+ endRow,
+ startColumn,
+ endColumn);
+ }
+
+ /** {@inheritDoc} */
+ public void copySubMatrix(int[] selectedRows, int[] selectedColumns, T[][] destination)
+ throws MatrixDimensionMismatchException,
+ NoDataException,
+ NullArgumentException,
+ OutOfRangeException {
+ // safety checks
+ checkSubMatrixIndex(selectedRows, selectedColumns);
+ if ((destination.length < selectedRows.length)
+ || (destination[0].length < selectedColumns.length)) {
+ throw new MatrixDimensionMismatchException(
+ destination.length,
+ destination[0].length,
+ selectedRows.length,
+ selectedColumns.length);
+ }
+
+ // copy entries
+ for (int i = 0; i < selectedRows.length; i++) {
+ final T[] destinationI = destination[i];
+ for (int j = 0; j < selectedColumns.length; j++) {
+ destinationI[j] = getEntry(selectedRows[i], selectedColumns[j]);
+ }
+ }
+ }
+
+ /** {@inheritDoc} */
+ public void setSubMatrix(final T[][] subMatrix, final int row, final int column)
+ throws DimensionMismatchException,
+ OutOfRangeException,
+ NoDataException,
+ NullArgumentException {
+ if (subMatrix == null) {
+ throw new NullArgumentException();
+ }
+ final int nRows = subMatrix.length;
+ if (nRows == 0) {
+ throw new NoDataException(LocalizedFormats.AT_LEAST_ONE_ROW);
+ }
+
+ final int nCols = subMatrix[0].length;
+ if (nCols == 0) {
+ throw new NoDataException(LocalizedFormats.AT_LEAST_ONE_COLUMN);
+ }
+
+ for (int r = 1; r < nRows; ++r) {
+ if (subMatrix[r].length != nCols) {
+ throw new DimensionMismatchException(nCols, subMatrix[r].length);
+ }
+ }
+
+ checkRowIndex(row);
+ checkColumnIndex(column);
+ checkRowIndex(nRows + row - 1);
+ checkColumnIndex(nCols + column - 1);
+
+ for (int i = 0; i < nRows; ++i) {
+ for (int j = 0; j < nCols; ++j) {
+ setEntry(row + i, column + j, subMatrix[i][j]);
+ }
+ }
+ }
+
+ /** {@inheritDoc} */
+ public FieldMatrix<T> getRowMatrix(final int row) throws OutOfRangeException {
+ checkRowIndex(row);
+ final int nCols = getColumnDimension();
+ final FieldMatrix<T> out = createMatrix(1, nCols);
+ for (int i = 0; i < nCols; ++i) {
+ out.setEntry(0, i, getEntry(row, i));
+ }
+
+ return out;
+ }
+
+ /** {@inheritDoc} */
+ public void setRowMatrix(final int row, final FieldMatrix<T> matrix)
+ throws OutOfRangeException, MatrixDimensionMismatchException {
+ checkRowIndex(row);
+ final int nCols = getColumnDimension();
+ if ((matrix.getRowDimension() != 1) || (matrix.getColumnDimension() != nCols)) {
+ throw new MatrixDimensionMismatchException(
+ matrix.getRowDimension(), matrix.getColumnDimension(), 1, nCols);
+ }
+ for (int i = 0; i < nCols; ++i) {
+ setEntry(row, i, matrix.getEntry(0, i));
+ }
+ }
+
+ /** {@inheritDoc} */
+ public FieldMatrix<T> getColumnMatrix(final int column) throws OutOfRangeException {
+
+ checkColumnIndex(column);
+ final int nRows = getRowDimension();
+ final FieldMatrix<T> out = createMatrix(nRows, 1);
+ for (int i = 0; i < nRows; ++i) {
+ out.setEntry(i, 0, getEntry(i, column));
+ }
+
+ return out;
+ }
+
+ /** {@inheritDoc} */
+ public void setColumnMatrix(final int column, final FieldMatrix<T> matrix)
+ throws OutOfRangeException, MatrixDimensionMismatchException {
+ checkColumnIndex(column);
+ final int nRows = getRowDimension();
+ if ((matrix.getRowDimension() != nRows) || (matrix.getColumnDimension() != 1)) {
+ throw new MatrixDimensionMismatchException(
+ matrix.getRowDimension(), matrix.getColumnDimension(), nRows, 1);
+ }
+ for (int i = 0; i < nRows; ++i) {
+ setEntry(i, column, matrix.getEntry(i, 0));
+ }
+ }
+
+ /** {@inheritDoc} */
+ public FieldVector<T> getRowVector(final int row) throws OutOfRangeException {
+ return new ArrayFieldVector<T>(field, getRow(row), false);
+ }
+
+ /** {@inheritDoc} */
+ public void setRowVector(final int row, final FieldVector<T> vector)
+ throws OutOfRangeException, MatrixDimensionMismatchException {
+ checkRowIndex(row);
+ final int nCols = getColumnDimension();
+ if (vector.getDimension() != nCols) {
+ throw new MatrixDimensionMismatchException(1, vector.getDimension(), 1, nCols);
+ }
+ for (int i = 0; i < nCols; ++i) {
+ setEntry(row, i, vector.getEntry(i));
+ }
+ }
+
+ /** {@inheritDoc} */
+ public FieldVector<T> getColumnVector(final int column) throws OutOfRangeException {
+ return new ArrayFieldVector<T>(field, getColumn(column), false);
+ }
+
+ /** {@inheritDoc} */
+ public void setColumnVector(final int column, final FieldVector<T> vector)
+ throws OutOfRangeException, MatrixDimensionMismatchException {
+
+ checkColumnIndex(column);
+ final int nRows = getRowDimension();
+ if (vector.getDimension() != nRows) {
+ throw new MatrixDimensionMismatchException(vector.getDimension(), 1, nRows, 1);
+ }
+ for (int i = 0; i < nRows; ++i) {
+ setEntry(i, column, vector.getEntry(i));
+ }
+ }
+
+ /** {@inheritDoc} */
+ public T[] getRow(final int row) throws OutOfRangeException {
+ checkRowIndex(row);
+ final int nCols = getColumnDimension();
+ final T[] out = MathArrays.buildArray(field, nCols);
+ for (int i = 0; i < nCols; ++i) {
+ out[i] = getEntry(row, i);
+ }
+
+ return out;
+ }
+
+ /** {@inheritDoc} */
+ public void setRow(final int row, final T[] array)
+ throws OutOfRangeException, MatrixDimensionMismatchException {
+ checkRowIndex(row);
+ final int nCols = getColumnDimension();
+ if (array.length != nCols) {
+ throw new MatrixDimensionMismatchException(1, array.length, 1, nCols);
+ }
+ for (int i = 0; i < nCols; ++i) {
+ setEntry(row, i, array[i]);
+ }
+ }
+
+ /** {@inheritDoc} */
+ public T[] getColumn(final int column) throws OutOfRangeException {
+ checkColumnIndex(column);
+ final int nRows = getRowDimension();
+ final T[] out = MathArrays.buildArray(field, nRows);
+ for (int i = 0; i < nRows; ++i) {
+ out[i] = getEntry(i, column);
+ }
+
+ return out;
+ }
+
+ /** {@inheritDoc} */
+ public void setColumn(final int column, final T[] array)
+ throws OutOfRangeException, MatrixDimensionMismatchException {
+ checkColumnIndex(column);
+ final int nRows = getRowDimension();
+ if (array.length != nRows) {
+ throw new MatrixDimensionMismatchException(array.length, 1, nRows, 1);
+ }
+ for (int i = 0; i < nRows; ++i) {
+ setEntry(i, column, array[i]);
+ }
+ }
+
+ /** {@inheritDoc} */
+ public abstract T getEntry(int row, int column) throws OutOfRangeException;
+
+ /** {@inheritDoc} */
+ public abstract void setEntry(int row, int column, T value) throws OutOfRangeException;
+
+ /** {@inheritDoc} */
+ public abstract void addToEntry(int row, int column, T increment) throws OutOfRangeException;
+
+ /** {@inheritDoc} */
+ public abstract void multiplyEntry(int row, int column, T factor) throws OutOfRangeException;
+
+ /** {@inheritDoc} */
+ public FieldMatrix<T> transpose() {
+ final int nRows = getRowDimension();
+ final int nCols = getColumnDimension();
+ final FieldMatrix<T> out = createMatrix(nCols, nRows);
+ walkInOptimizedOrder(
+ new DefaultFieldMatrixPreservingVisitor<T>(field.getZero()) {
+ /** {@inheritDoc} */
+ @Override
+ public void visit(final int row, final int column, final T value) {
+ out.setEntry(column, row, value);
+ }
+ });
+
+ return out;
+ }
+
+ /** {@inheritDoc} */
+ public boolean isSquare() {
+ return getColumnDimension() == getRowDimension();
+ }
+
+ /** {@inheritDoc} */
+ public abstract int getRowDimension();
+
+ /** {@inheritDoc} */
+ public abstract int getColumnDimension();
+
+ /** {@inheritDoc} */
+ public T getTrace() throws NonSquareMatrixException {
+ final int nRows = getRowDimension();
+ final int nCols = getColumnDimension();
+ if (nRows != nCols) {
+ throw new NonSquareMatrixException(nRows, nCols);
+ }
+ T trace = field.getZero();
+ for (int i = 0; i < nRows; ++i) {
+ trace = trace.add(getEntry(i, i));
+ }
+ return trace;
+ }
+
+ /** {@inheritDoc} */
+ public T[] operate(final T[] v) throws DimensionMismatchException {
+
+ final int nRows = getRowDimension();
+ final int nCols = getColumnDimension();
+ if (v.length != nCols) {
+ throw new DimensionMismatchException(v.length, nCols);
+ }
+
+ final T[] out = MathArrays.buildArray(field, nRows);
+ for (int row = 0; row < nRows; ++row) {
+ T sum = field.getZero();
+ for (int i = 0; i < nCols; ++i) {
+ sum = sum.add(getEntry(row, i).multiply(v[i]));
+ }
+ out[row] = sum;
+ }
+
+ return out;
+ }
+
+ /** {@inheritDoc} */
+ public FieldVector<T> operate(final FieldVector<T> v) throws DimensionMismatchException {
+ try {
+ return new ArrayFieldVector<T>(
+ field, operate(((ArrayFieldVector<T>) v).getDataRef()), false);
+ } catch (ClassCastException cce) {
+ final int nRows = getRowDimension();
+ final int nCols = getColumnDimension();
+ if (v.getDimension() != nCols) {
+ throw new DimensionMismatchException(v.getDimension(), nCols);
+ }
+
+ final T[] out = MathArrays.buildArray(field, nRows);
+ for (int row = 0; row < nRows; ++row) {
+ T sum = field.getZero();
+ for (int i = 0; i < nCols; ++i) {
+ sum = sum.add(getEntry(row, i).multiply(v.getEntry(i)));
+ }
+ out[row] = sum;
+ }
+
+ return new ArrayFieldVector<T>(field, out, false);
+ }
+ }
+
+ /** {@inheritDoc} */
+ public T[] preMultiply(final T[] v) throws DimensionMismatchException {
+
+ final int nRows = getRowDimension();
+ final int nCols = getColumnDimension();
+ if (v.length != nRows) {
+ throw new DimensionMismatchException(v.length, nRows);
+ }
+
+ final T[] out = MathArrays.buildArray(field, nCols);
+ for (int col = 0; col < nCols; ++col) {
+ T sum = field.getZero();
+ for (int i = 0; i < nRows; ++i) {
+ sum = sum.add(getEntry(i, col).multiply(v[i]));
+ }
+ out[col] = sum;
+ }
+
+ return out;
+ }
+
+ /** {@inheritDoc} */
+ public FieldVector<T> preMultiply(final FieldVector<T> v) throws DimensionMismatchException {
+ try {
+ return new ArrayFieldVector<T>(
+ field, preMultiply(((ArrayFieldVector<T>) v).getDataRef()), false);
+ } catch (ClassCastException cce) {
+ final int nRows = getRowDimension();
+ final int nCols = getColumnDimension();
+ if (v.getDimension() != nRows) {
+ throw new DimensionMismatchException(v.getDimension(), nRows);
+ }
+
+ final T[] out = MathArrays.buildArray(field, nCols);
+ for (int col = 0; col < nCols; ++col) {
+ T sum = field.getZero();
+ for (int i = 0; i < nRows; ++i) {
+ sum = sum.add(getEntry(i, col).multiply(v.getEntry(i)));
+ }
+ out[col] = sum;
+ }
+
+ return new ArrayFieldVector<T>(field, out, false);
+ }
+ }
+
+ /** {@inheritDoc} */
+ public T walkInRowOrder(final FieldMatrixChangingVisitor<T> visitor) {
+ final int rows = getRowDimension();
+ final int columns = getColumnDimension();
+ visitor.start(rows, columns, 0, rows - 1, 0, columns - 1);
+ for (int row = 0; row < rows; ++row) {
+ for (int column = 0; column < columns; ++column) {
+ final T oldValue = getEntry(row, column);
+ final T newValue = visitor.visit(row, column, oldValue);
+ setEntry(row, column, newValue);
+ }
+ }
+ return visitor.end();
+ }
+
+ /** {@inheritDoc} */
+ public T walkInRowOrder(final FieldMatrixPreservingVisitor<T> visitor) {
+ final int rows = getRowDimension();
+ final int columns = getColumnDimension();
+ visitor.start(rows, columns, 0, rows - 1, 0, columns - 1);
+ for (int row = 0; row < rows; ++row) {
+ for (int column = 0; column < columns; ++column) {
+ visitor.visit(row, column, getEntry(row, column));
+ }
+ }
+ return visitor.end();
+ }
+
+ /** {@inheritDoc} */
+ public T walkInRowOrder(
+ final FieldMatrixChangingVisitor<T> visitor,
+ final int startRow,
+ final int endRow,
+ final int startColumn,
+ final int endColumn)
+ throws NumberIsTooSmallException, OutOfRangeException {
+ checkSubMatrixIndex(startRow, endRow, startColumn, endColumn);
+ visitor.start(
+ getRowDimension(), getColumnDimension(), startRow, endRow, startColumn, endColumn);
+ for (int row = startRow; row <= endRow; ++row) {
+ for (int column = startColumn; column <= endColumn; ++column) {
+ final T oldValue = getEntry(row, column);
+ final T newValue = visitor.visit(row, column, oldValue);
+ setEntry(row, column, newValue);
+ }
+ }
+ return visitor.end();
+ }
+
+ /** {@inheritDoc} */
+ public T walkInRowOrder(
+ final FieldMatrixPreservingVisitor<T> visitor,
+ final int startRow,
+ final int endRow,
+ final int startColumn,
+ final int endColumn)
+ throws NumberIsTooSmallException, OutOfRangeException {
+ checkSubMatrixIndex(startRow, endRow, startColumn, endColumn);
+ visitor.start(
+ getRowDimension(), getColumnDimension(), startRow, endRow, startColumn, endColumn);
+ for (int row = startRow; row <= endRow; ++row) {
+ for (int column = startColumn; column <= endColumn; ++column) {
+ visitor.visit(row, column, getEntry(row, column));
+ }
+ }
+ return visitor.end();
+ }
+
+ /** {@inheritDoc} */
+ public T walkInColumnOrder(final FieldMatrixChangingVisitor<T> visitor) {
+ final int rows = getRowDimension();
+ final int columns = getColumnDimension();
+ visitor.start(rows, columns, 0, rows - 1, 0, columns - 1);
+ for (int column = 0; column < columns; ++column) {
+ for (int row = 0; row < rows; ++row) {
+ final T oldValue = getEntry(row, column);
+ final T newValue = visitor.visit(row, column, oldValue);
+ setEntry(row, column, newValue);
+ }
+ }
+ return visitor.end();
+ }
+
+ /** {@inheritDoc} */
+ public T walkInColumnOrder(final FieldMatrixPreservingVisitor<T> visitor) {
+ final int rows = getRowDimension();
+ final int columns = getColumnDimension();
+ visitor.start(rows, columns, 0, rows - 1, 0, columns - 1);
+ for (int column = 0; column < columns; ++column) {
+ for (int row = 0; row < rows; ++row) {
+ visitor.visit(row, column, getEntry(row, column));
+ }
+ }
+ return visitor.end();
+ }
+
+ /** {@inheritDoc} */
+ public T walkInColumnOrder(
+ final FieldMatrixChangingVisitor<T> visitor,
+ final int startRow,
+ final int endRow,
+ final int startColumn,
+ final int endColumn)
+ throws NumberIsTooSmallException, OutOfRangeException {
+ checkSubMatrixIndex(startRow, endRow, startColumn, endColumn);
+ visitor.start(
+ getRowDimension(), getColumnDimension(), startRow, endRow, startColumn, endColumn);
+ for (int column = startColumn; column <= endColumn; ++column) {
+ for (int row = startRow; row <= endRow; ++row) {
+ final T oldValue = getEntry(row, column);
+ final T newValue = visitor.visit(row, column, oldValue);
+ setEntry(row, column, newValue);
+ }
+ }
+ return visitor.end();
+ }
+
+ /** {@inheritDoc} */
+ public T walkInColumnOrder(
+ final FieldMatrixPreservingVisitor<T> visitor,
+ final int startRow,
+ final int endRow,
+ final int startColumn,
+ final int endColumn)
+ throws NumberIsTooSmallException, OutOfRangeException {
+ checkSubMatrixIndex(startRow, endRow, startColumn, endColumn);
+ visitor.start(
+ getRowDimension(), getColumnDimension(), startRow, endRow, startColumn, endColumn);
+ for (int column = startColumn; column <= endColumn; ++column) {
+ for (int row = startRow; row <= endRow; ++row) {
+ visitor.visit(row, column, getEntry(row, column));
+ }
+ }
+ return visitor.end();
+ }
+
+ /** {@inheritDoc} */
+ public T walkInOptimizedOrder(final FieldMatrixChangingVisitor<T> visitor) {
+ return walkInRowOrder(visitor);
+ }
+
+ /** {@inheritDoc} */
+ public T walkInOptimizedOrder(final FieldMatrixPreservingVisitor<T> visitor) {
+ return walkInRowOrder(visitor);
+ }
+
+ /** {@inheritDoc} */
+ public T walkInOptimizedOrder(
+ final FieldMatrixChangingVisitor<T> visitor,
+ final int startRow,
+ final int endRow,
+ final int startColumn,
+ final int endColumn)
+ throws NumberIsTooSmallException, OutOfRangeException {
+ return walkInRowOrder(visitor, startRow, endRow, startColumn, endColumn);
+ }
+
+ /** {@inheritDoc} */
+ public T walkInOptimizedOrder(
+ final FieldMatrixPreservingVisitor<T> visitor,
+ final int startRow,
+ final int endRow,
+ final int startColumn,
+ final int endColumn)
+ throws NumberIsTooSmallException, OutOfRangeException {
+ return walkInRowOrder(visitor, startRow, endRow, startColumn, endColumn);
+ }
+
+ /**
+ * Get a string representation for this matrix.
+ *
+ * @return a string representation for this matrix
+ */
+ @Override
+ public String toString() {
+ final int nRows = getRowDimension();
+ final int nCols = getColumnDimension();
+ final StringBuffer res = new StringBuffer();
+ String fullClassName = getClass().getName();
+ String shortClassName = fullClassName.substring(fullClassName.lastIndexOf('.') + 1);
+ res.append(shortClassName).append("{");
+
+ for (int i = 0; i < nRows; ++i) {
+ if (i > 0) {
+ res.append(",");
+ }
+ res.append("{");
+ for (int j = 0; j < nCols; ++j) {
+ if (j > 0) {
+ res.append(",");
+ }
+ res.append(getEntry(i, j));
+ }
+ res.append("}");
+ }
+
+ res.append("}");
+ return res.toString();
+ }
+
+ /**
+ * Returns true iff <code>object</code> is a <code>FieldMatrix</code> instance with the same
+ * dimensions as this and all corresponding matrix entries are equal.
+ *
+ * @param object the object to test equality against.
+ * @return true if object equals this
+ */
+ @Override
+ public boolean equals(final Object object) {
+ if (object == this) {
+ return true;
+ }
+ if (object instanceof FieldMatrix<?> == false) {
+ return false;
+ }
+ FieldMatrix<?> m = (FieldMatrix<?>) object;
+ final int nRows = getRowDimension();
+ final int nCols = getColumnDimension();
+ if (m.getColumnDimension() != nCols || m.getRowDimension() != nRows) {
+ return false;
+ }
+ for (int row = 0; row < nRows; ++row) {
+ for (int col = 0; col < nCols; ++col) {
+ if (!getEntry(row, col).equals(m.getEntry(row, col))) {
+ return false;
+ }
+ }
+ }
+ return true;
+ }
+
+ /**
+ * Computes a hashcode for the matrix.
+ *
+ * @return hashcode for matrix
+ */
+ @Override
+ public int hashCode() {
+ int ret = 322562;
+ final int nRows = getRowDimension();
+ final int nCols = getColumnDimension();
+ ret = ret * 31 + nRows;
+ ret = ret * 31 + nCols;
+ for (int row = 0; row < nRows; ++row) {
+ for (int col = 0; col < nCols; ++col) {
+ ret = ret * 31 + (11 * (row + 1) + 17 * (col + 1)) * getEntry(row, col).hashCode();
+ }
+ }
+ return ret;
+ }
+
+ /**
+ * Check if a row index is valid.
+ *
+ * @param row Row index to check.
+ * @throws OutOfRangeException if {@code index} is not valid.
+ */
+ protected void checkRowIndex(final int row) throws OutOfRangeException {
+ if (row < 0 || row >= getRowDimension()) {
+ throw new OutOfRangeException(
+ LocalizedFormats.ROW_INDEX, row, 0, getRowDimension() - 1);
+ }
+ }
+
+ /**
+ * Check if a column index is valid.
+ *
+ * @param column Column index to check.
+ * @throws OutOfRangeException if {@code index} is not valid.
+ */
+ protected void checkColumnIndex(final int column) throws OutOfRangeException {
+ if (column < 0 || column >= getColumnDimension()) {
+ throw new OutOfRangeException(
+ LocalizedFormats.COLUMN_INDEX, column, 0, getColumnDimension() - 1);
+ }
+ }
+
+ /**
+ * Check if submatrix ranges indices are valid. Rows and columns are indicated counting from 0
+ * to n-1.
+ *
+ * @param startRow Initial row index.
+ * @param endRow Final row index.
+ * @param startColumn Initial column index.
+ * @param endColumn Final column index.
+ * @throws OutOfRangeException if the indices are not valid.
+ * @throws NumberIsTooSmallException if {@code endRow < startRow} or {@code endColumn <
+ * startColumn}.
+ */
+ protected void checkSubMatrixIndex(
+ final int startRow, final int endRow, final int startColumn, final int endColumn)
+ throws NumberIsTooSmallException, OutOfRangeException {
+ checkRowIndex(startRow);
+ checkRowIndex(endRow);
+ if (endRow < startRow) {
+ throw new NumberIsTooSmallException(
+ LocalizedFormats.INITIAL_ROW_AFTER_FINAL_ROW, endRow, startRow, true);
+ }
+
+ checkColumnIndex(startColumn);
+ checkColumnIndex(endColumn);
+ if (endColumn < startColumn) {
+ throw new NumberIsTooSmallException(
+ LocalizedFormats.INITIAL_COLUMN_AFTER_FINAL_COLUMN,
+ endColumn,
+ startColumn,
+ true);
+ }
+ }
+
+ /**
+ * Check if submatrix ranges indices are valid. Rows and columns are indicated counting from 0
+ * to n-1.
+ *
+ * @param selectedRows Array of row indices.
+ * @param selectedColumns Array of column indices.
+ * @throws NullArgumentException if the arrays are {@code null}.
+ * @throws NoDataException if the arrays have zero length.
+ * @throws OutOfRangeException if row or column selections are not valid.
+ */
+ protected void checkSubMatrixIndex(final int[] selectedRows, final int[] selectedColumns)
+ throws NoDataException, NullArgumentException, OutOfRangeException {
+ if (selectedRows == null || selectedColumns == null) {
+ throw new NullArgumentException();
+ }
+ if (selectedRows.length == 0 || selectedColumns.length == 0) {
+ throw new NoDataException();
+ }
+
+ for (final int row : selectedRows) {
+ checkRowIndex(row);
+ }
+ for (final int column : selectedColumns) {
+ checkColumnIndex(column);
+ }
+ }
+
+ /**
+ * Check if a matrix is addition compatible with the instance.
+ *
+ * @param m Matrix to check.
+ * @throws MatrixDimensionMismatchException if the matrix is not addition-compatible with
+ * instance.
+ */
+ protected void checkAdditionCompatible(final FieldMatrix<T> m)
+ throws MatrixDimensionMismatchException {
+ if ((getRowDimension() != m.getRowDimension())
+ || (getColumnDimension() != m.getColumnDimension())) {
+ throw new MatrixDimensionMismatchException(
+ m.getRowDimension(), m.getColumnDimension(),
+ getRowDimension(), getColumnDimension());
+ }
+ }
+
+ /**
+ * Check if a matrix is subtraction compatible with the instance.
+ *
+ * @param m Matrix to check.
+ * @throws MatrixDimensionMismatchException if the matrix is not subtraction-compatible with
+ * instance.
+ */
+ protected void checkSubtractionCompatible(final FieldMatrix<T> m)
+ throws MatrixDimensionMismatchException {
+ if ((getRowDimension() != m.getRowDimension())
+ || (getColumnDimension() != m.getColumnDimension())) {
+ throw new MatrixDimensionMismatchException(
+ m.getRowDimension(), m.getColumnDimension(),
+ getRowDimension(), getColumnDimension());
+ }
+ }
+
+ /**
+ * Check if a matrix is multiplication compatible with the instance.
+ *
+ * @param m Matrix to check.
+ * @throws DimensionMismatchException if the matrix is not multiplication-compatible with
+ * instance.
+ */
+ protected void checkMultiplicationCompatible(final FieldMatrix<T> m)
+ throws DimensionMismatchException {
+ if (getColumnDimension() != m.getRowDimension()) {
+ throw new DimensionMismatchException(m.getRowDimension(), getColumnDimension());
+ }
+ }
+}
diff --git a/src/main/java/org/apache/commons/math3/linear/AbstractRealMatrix.java b/src/main/java/org/apache/commons/math3/linear/AbstractRealMatrix.java
new file mode 100644
index 0000000..e3fd838
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/linear/AbstractRealMatrix.java
@@ -0,0 +1,1010 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math3.linear;
+
+import org.apache.commons.math3.exception.DimensionMismatchException;
+import org.apache.commons.math3.exception.NoDataException;
+import org.apache.commons.math3.exception.NotPositiveException;
+import org.apache.commons.math3.exception.NotStrictlyPositiveException;
+import org.apache.commons.math3.exception.NullArgumentException;
+import org.apache.commons.math3.exception.NumberIsTooSmallException;
+import org.apache.commons.math3.exception.OutOfRangeException;
+import org.apache.commons.math3.exception.util.LocalizedFormats;
+import org.apache.commons.math3.util.FastMath;
+import org.apache.commons.math3.util.MathUtils;
+
+import java.util.ArrayList;
+import java.util.Locale;
+
+/**
+ * Basic implementation of RealMatrix methods regardless of the underlying storage.
+ *
+ * <p>All the methods implemented here use {@link #getEntry(int, int)} to access matrix elements.
+ * Derived class can provide faster implementations.
+ *
+ * @since 2.0
+ */
+public abstract class AbstractRealMatrix extends RealLinearOperator implements RealMatrix {
+
+ /** Default format. */
+ private static final RealMatrixFormat DEFAULT_FORMAT = RealMatrixFormat.getInstance(Locale.US);
+
+ static {
+ // set the minimum fraction digits to 1 to keep compatibility
+ DEFAULT_FORMAT.getFormat().setMinimumFractionDigits(1);
+ }
+
+ /** Creates a matrix with no data */
+ protected AbstractRealMatrix() {}
+
+ /**
+ * Create a new RealMatrix with the supplied row and column dimensions.
+ *
+ * @param rowDimension the number of rows in the new matrix
+ * @param columnDimension the number of columns in the new matrix
+ * @throws NotStrictlyPositiveException if row or column dimension is not positive
+ */
+ protected AbstractRealMatrix(final int rowDimension, final int columnDimension)
+ throws NotStrictlyPositiveException {
+ if (rowDimension < 1) {
+ throw new NotStrictlyPositiveException(rowDimension);
+ }
+ if (columnDimension < 1) {
+ throw new NotStrictlyPositiveException(columnDimension);
+ }
+ }
+
+ /** {@inheritDoc} */
+ public RealMatrix add(RealMatrix m) throws MatrixDimensionMismatchException {
+ MatrixUtils.checkAdditionCompatible(this, m);
+
+ final int rowCount = getRowDimension();
+ final int columnCount = getColumnDimension();
+ final RealMatrix out = createMatrix(rowCount, columnCount);
+ for (int row = 0; row < rowCount; ++row) {
+ for (int col = 0; col < columnCount; ++col) {
+ out.setEntry(row, col, getEntry(row, col) + m.getEntry(row, col));
+ }
+ }
+
+ return out;
+ }
+
+ /** {@inheritDoc} */
+ public RealMatrix subtract(final RealMatrix m) throws MatrixDimensionMismatchException {
+ MatrixUtils.checkSubtractionCompatible(this, m);
+
+ final int rowCount = getRowDimension();
+ final int columnCount = getColumnDimension();
+ final RealMatrix out = createMatrix(rowCount, columnCount);
+ for (int row = 0; row < rowCount; ++row) {
+ for (int col = 0; col < columnCount; ++col) {
+ out.setEntry(row, col, getEntry(row, col) - m.getEntry(row, col));
+ }
+ }
+
+ return out;
+ }
+
+ /** {@inheritDoc} */
+ public RealMatrix scalarAdd(final double d) {
+ final int rowCount = getRowDimension();
+ final int columnCount = getColumnDimension();
+ final RealMatrix out = createMatrix(rowCount, columnCount);
+ for (int row = 0; row < rowCount; ++row) {
+ for (int col = 0; col < columnCount; ++col) {
+ out.setEntry(row, col, getEntry(row, col) + d);
+ }
+ }
+
+ return out;
+ }
+
+ /** {@inheritDoc} */
+ public RealMatrix scalarMultiply(final double d) {
+ final int rowCount = getRowDimension();
+ final int columnCount = getColumnDimension();
+ final RealMatrix out = createMatrix(rowCount, columnCount);
+ for (int row = 0; row < rowCount; ++row) {
+ for (int col = 0; col < columnCount; ++col) {
+ out.setEntry(row, col, getEntry(row, col) * d);
+ }
+ }
+
+ return out;
+ }
+
+ /** {@inheritDoc} */
+ public RealMatrix multiply(final RealMatrix m) throws DimensionMismatchException {
+ MatrixUtils.checkMultiplicationCompatible(this, m);
+
+ final int nRows = getRowDimension();
+ final int nCols = m.getColumnDimension();
+ final int nSum = getColumnDimension();
+ final RealMatrix out = createMatrix(nRows, nCols);
+ for (int row = 0; row < nRows; ++row) {
+ for (int col = 0; col < nCols; ++col) {
+ double sum = 0;
+ for (int i = 0; i < nSum; ++i) {
+ sum += getEntry(row, i) * m.getEntry(i, col);
+ }
+ out.setEntry(row, col, sum);
+ }
+ }
+
+ return out;
+ }
+
+ /** {@inheritDoc} */
+ public RealMatrix preMultiply(final RealMatrix m) throws DimensionMismatchException {
+ return m.multiply(this);
+ }
+
+ /** {@inheritDoc} */
+ public RealMatrix power(final int p) throws NotPositiveException, NonSquareMatrixException {
+ if (p < 0) {
+ throw new NotPositiveException(LocalizedFormats.NOT_POSITIVE_EXPONENT, p);
+ }
+
+ if (!isSquare()) {
+ throw new NonSquareMatrixException(getRowDimension(), getColumnDimension());
+ }
+
+ if (p == 0) {
+ return MatrixUtils.createRealIdentityMatrix(this.getRowDimension());
+ }
+
+ if (p == 1) {
+ return this.copy();
+ }
+
+ final int power = p - 1;
+
+ /*
+ * Only log_2(p) operations is used by doing as follows:
+ * 5^214 = 5^128 * 5^64 * 5^16 * 5^4 * 5^2
+ *
+ * In general, the same approach is used for A^p.
+ */
+
+ final char[] binaryRepresentation = Integer.toBinaryString(power).toCharArray();
+ final ArrayList<Integer> nonZeroPositions = new ArrayList<Integer>();
+ int maxI = -1;
+
+ for (int i = 0; i < binaryRepresentation.length; ++i) {
+ if (binaryRepresentation[i] == '1') {
+ final int pos = binaryRepresentation.length - i - 1;
+ nonZeroPositions.add(pos);
+
+ // The positions are taken in turn, so maxI is only changed once
+ if (maxI == -1) {
+ maxI = pos;
+ }
+ }
+ }
+
+ RealMatrix[] results = new RealMatrix[maxI + 1];
+ results[0] = this.copy();
+
+ for (int i = 1; i <= maxI; ++i) {
+ results[i] = results[i - 1].multiply(results[i - 1]);
+ }
+
+ RealMatrix result = this.copy();
+
+ for (Integer i : nonZeroPositions) {
+ result = result.multiply(results[i]);
+ }
+
+ return result;
+ }
+
+ /** {@inheritDoc} */
+ public double[][] getData() {
+ final double[][] data = new double[getRowDimension()][getColumnDimension()];
+
+ for (int i = 0; i < data.length; ++i) {
+ final double[] dataI = data[i];
+ for (int j = 0; j < dataI.length; ++j) {
+ dataI[j] = getEntry(i, j);
+ }
+ }
+
+ return data;
+ }
+
+ /** {@inheritDoc} */
+ public double getNorm() {
+ return walkInColumnOrder(
+ new RealMatrixPreservingVisitor() {
+
+ /** Last row index. */
+ private double endRow;
+
+ /** Sum of absolute values on one column. */
+ private double columnSum;
+
+ /** Maximal sum across all columns. */
+ private double maxColSum;
+
+ /** {@inheritDoc} */
+ public void start(
+ final int rows,
+ final int columns,
+ final int startRow,
+ final int endRow,
+ final int startColumn,
+ final int endColumn) {
+ this.endRow = endRow;
+ columnSum = 0;
+ maxColSum = 0;
+ }
+
+ /** {@inheritDoc} */
+ public void visit(final int row, final int column, final double value) {
+ columnSum += FastMath.abs(value);
+ if (row == endRow) {
+ maxColSum = FastMath.max(maxColSum, columnSum);
+ columnSum = 0;
+ }
+ }
+
+ /** {@inheritDoc} */
+ public double end() {
+ return maxColSum;
+ }
+ });
+ }
+
+ /** {@inheritDoc} */
+ public double getFrobeniusNorm() {
+ return walkInOptimizedOrder(
+ new RealMatrixPreservingVisitor() {
+
+ /** Sum of squared entries. */
+ private double sum;
+
+ /** {@inheritDoc} */
+ public void start(
+ final int rows,
+ final int columns,
+ final int startRow,
+ final int endRow,
+ final int startColumn,
+ final int endColumn) {
+ sum = 0;
+ }
+
+ /** {@inheritDoc} */
+ public void visit(final int row, final int column, final double value) {
+ sum += value * value;
+ }
+
+ /** {@inheritDoc} */
+ public double end() {
+ return FastMath.sqrt(sum);
+ }
+ });
+ }
+
+ /** {@inheritDoc} */
+ public RealMatrix getSubMatrix(
+ final int startRow, final int endRow, final int startColumn, final int endColumn)
+ throws OutOfRangeException, NumberIsTooSmallException {
+ MatrixUtils.checkSubMatrixIndex(this, startRow, endRow, startColumn, endColumn);
+
+ final RealMatrix subMatrix =
+ createMatrix(endRow - startRow + 1, endColumn - startColumn + 1);
+ for (int i = startRow; i <= endRow; ++i) {
+ for (int j = startColumn; j <= endColumn; ++j) {
+ subMatrix.setEntry(i - startRow, j - startColumn, getEntry(i, j));
+ }
+ }
+
+ return subMatrix;
+ }
+
+ /** {@inheritDoc} */
+ public RealMatrix getSubMatrix(final int[] selectedRows, final int[] selectedColumns)
+ throws NullArgumentException, NoDataException, OutOfRangeException {
+ MatrixUtils.checkSubMatrixIndex(this, selectedRows, selectedColumns);
+
+ final RealMatrix subMatrix = createMatrix(selectedRows.length, selectedColumns.length);
+ subMatrix.walkInOptimizedOrder(
+ new DefaultRealMatrixChangingVisitor() {
+
+ /** {@inheritDoc} */
+ @Override
+ public double visit(final int row, final int column, final double value) {
+ return getEntry(selectedRows[row], selectedColumns[column]);
+ }
+ });
+
+ return subMatrix;
+ }
+
+ /** {@inheritDoc} */
+ public void copySubMatrix(
+ final int startRow,
+ final int endRow,
+ final int startColumn,
+ final int endColumn,
+ final double[][] destination)
+ throws OutOfRangeException,
+ NumberIsTooSmallException,
+ MatrixDimensionMismatchException {
+ MatrixUtils.checkSubMatrixIndex(this, startRow, endRow, startColumn, endColumn);
+ final int rowsCount = endRow + 1 - startRow;
+ final int columnsCount = endColumn + 1 - startColumn;
+ if ((destination.length < rowsCount) || (destination[0].length < columnsCount)) {
+ throw new MatrixDimensionMismatchException(
+ destination.length, destination[0].length, rowsCount, columnsCount);
+ }
+
+ for (int i = 1; i < rowsCount; i++) {
+ if (destination[i].length < columnsCount) {
+ throw new MatrixDimensionMismatchException(
+ destination.length, destination[i].length, rowsCount, columnsCount);
+ }
+ }
+
+ walkInOptimizedOrder(
+ new DefaultRealMatrixPreservingVisitor() {
+
+ /** Initial row index. */
+ private int startRow;
+
+ /** Initial column index. */
+ private int startColumn;
+
+ /** {@inheritDoc} */
+ @Override
+ public void start(
+ final int rows,
+ final int columns,
+ final int startRow,
+ final int endRow,
+ final int startColumn,
+ final int endColumn) {
+ this.startRow = startRow;
+ this.startColumn = startColumn;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void visit(final int row, final int column, final double value) {
+ destination[row - startRow][column - startColumn] = value;
+ }
+ },
+ startRow,
+ endRow,
+ startColumn,
+ endColumn);
+ }
+
+ /** {@inheritDoc} */
+ public void copySubMatrix(int[] selectedRows, int[] selectedColumns, double[][] destination)
+ throws OutOfRangeException,
+ NullArgumentException,
+ NoDataException,
+ MatrixDimensionMismatchException {
+ MatrixUtils.checkSubMatrixIndex(this, selectedRows, selectedColumns);
+ final int nCols = selectedColumns.length;
+ if ((destination.length < selectedRows.length) || (destination[0].length < nCols)) {
+ throw new MatrixDimensionMismatchException(
+ destination.length, destination[0].length,
+ selectedRows.length, selectedColumns.length);
+ }
+
+ for (int i = 0; i < selectedRows.length; i++) {
+ final double[] destinationI = destination[i];
+ if (destinationI.length < nCols) {
+ throw new MatrixDimensionMismatchException(
+ destination.length, destinationI.length,
+ selectedRows.length, selectedColumns.length);
+ }
+ for (int j = 0; j < selectedColumns.length; j++) {
+ destinationI[j] = getEntry(selectedRows[i], selectedColumns[j]);
+ }
+ }
+ }
+
+ /** {@inheritDoc} */
+ public void setSubMatrix(final double[][] subMatrix, final int row, final int column)
+ throws NoDataException,
+ OutOfRangeException,
+ DimensionMismatchException,
+ NullArgumentException {
+ MathUtils.checkNotNull(subMatrix);
+ final int nRows = subMatrix.length;
+ if (nRows == 0) {
+ throw new NoDataException(LocalizedFormats.AT_LEAST_ONE_ROW);
+ }
+
+ final int nCols = subMatrix[0].length;
+ if (nCols == 0) {
+ throw new NoDataException(LocalizedFormats.AT_LEAST_ONE_COLUMN);
+ }
+
+ for (int r = 1; r < nRows; ++r) {
+ if (subMatrix[r].length != nCols) {
+ throw new DimensionMismatchException(nCols, subMatrix[r].length);
+ }
+ }
+
+ MatrixUtils.checkRowIndex(this, row);
+ MatrixUtils.checkColumnIndex(this, column);
+ MatrixUtils.checkRowIndex(this, nRows + row - 1);
+ MatrixUtils.checkColumnIndex(this, nCols + column - 1);
+
+ for (int i = 0; i < nRows; ++i) {
+ for (int j = 0; j < nCols; ++j) {
+ setEntry(row + i, column + j, subMatrix[i][j]);
+ }
+ }
+ }
+
+ /** {@inheritDoc} */
+ public RealMatrix getRowMatrix(final int row) throws OutOfRangeException {
+ MatrixUtils.checkRowIndex(this, row);
+ final int nCols = getColumnDimension();
+ final RealMatrix out = createMatrix(1, nCols);
+ for (int i = 0; i < nCols; ++i) {
+ out.setEntry(0, i, getEntry(row, i));
+ }
+
+ return out;
+ }
+
+ /** {@inheritDoc} */
+ public void setRowMatrix(final int row, final RealMatrix matrix)
+ throws OutOfRangeException, MatrixDimensionMismatchException {
+ MatrixUtils.checkRowIndex(this, row);
+ final int nCols = getColumnDimension();
+ if ((matrix.getRowDimension() != 1) || (matrix.getColumnDimension() != nCols)) {
+ throw new MatrixDimensionMismatchException(
+ matrix.getRowDimension(), matrix.getColumnDimension(), 1, nCols);
+ }
+ for (int i = 0; i < nCols; ++i) {
+ setEntry(row, i, matrix.getEntry(0, i));
+ }
+ }
+
+ /** {@inheritDoc} */
+ public RealMatrix getColumnMatrix(final int column) throws OutOfRangeException {
+ MatrixUtils.checkColumnIndex(this, column);
+ final int nRows = getRowDimension();
+ final RealMatrix out = createMatrix(nRows, 1);
+ for (int i = 0; i < nRows; ++i) {
+ out.setEntry(i, 0, getEntry(i, column));
+ }
+
+ return out;
+ }
+
+ /** {@inheritDoc} */
+ public void setColumnMatrix(final int column, final RealMatrix matrix)
+ throws OutOfRangeException, MatrixDimensionMismatchException {
+ MatrixUtils.checkColumnIndex(this, column);
+ final int nRows = getRowDimension();
+ if ((matrix.getRowDimension() != nRows) || (matrix.getColumnDimension() != 1)) {
+ throw new MatrixDimensionMismatchException(
+ matrix.getRowDimension(), matrix.getColumnDimension(), nRows, 1);
+ }
+ for (int i = 0; i < nRows; ++i) {
+ setEntry(i, column, matrix.getEntry(i, 0));
+ }
+ }
+
+ /** {@inheritDoc} */
+ public RealVector getRowVector(final int row) throws OutOfRangeException {
+ return new ArrayRealVector(getRow(row), false);
+ }
+
+ /** {@inheritDoc} */
+ public void setRowVector(final int row, final RealVector vector)
+ throws OutOfRangeException, MatrixDimensionMismatchException {
+ MatrixUtils.checkRowIndex(this, row);
+ final int nCols = getColumnDimension();
+ if (vector.getDimension() != nCols) {
+ throw new MatrixDimensionMismatchException(1, vector.getDimension(), 1, nCols);
+ }
+ for (int i = 0; i < nCols; ++i) {
+ setEntry(row, i, vector.getEntry(i));
+ }
+ }
+
+ /** {@inheritDoc} */
+ public RealVector getColumnVector(final int column) throws OutOfRangeException {
+ return new ArrayRealVector(getColumn(column), false);
+ }
+
+ /** {@inheritDoc} */
+ public void setColumnVector(final int column, final RealVector vector)
+ throws OutOfRangeException, MatrixDimensionMismatchException {
+ MatrixUtils.checkColumnIndex(this, column);
+ final int nRows = getRowDimension();
+ if (vector.getDimension() != nRows) {
+ throw new MatrixDimensionMismatchException(vector.getDimension(), 1, nRows, 1);
+ }
+ for (int i = 0; i < nRows; ++i) {
+ setEntry(i, column, vector.getEntry(i));
+ }
+ }
+
+ /** {@inheritDoc} */
+ public double[] getRow(final int row) throws OutOfRangeException {
+ MatrixUtils.checkRowIndex(this, row);
+ final int nCols = getColumnDimension();
+ final double[] out = new double[nCols];
+ for (int i = 0; i < nCols; ++i) {
+ out[i] = getEntry(row, i);
+ }
+
+ return out;
+ }
+
+ /** {@inheritDoc} */
+ public void setRow(final int row, final double[] array)
+ throws OutOfRangeException, MatrixDimensionMismatchException {
+ MatrixUtils.checkRowIndex(this, row);
+ final int nCols = getColumnDimension();
+ if (array.length != nCols) {
+ throw new MatrixDimensionMismatchException(1, array.length, 1, nCols);
+ }
+ for (int i = 0; i < nCols; ++i) {
+ setEntry(row, i, array[i]);
+ }
+ }
+
+ /** {@inheritDoc} */
+ public double[] getColumn(final int column) throws OutOfRangeException {
+ MatrixUtils.checkColumnIndex(this, column);
+ final int nRows = getRowDimension();
+ final double[] out = new double[nRows];
+ for (int i = 0; i < nRows; ++i) {
+ out[i] = getEntry(i, column);
+ }
+
+ return out;
+ }
+
+ /** {@inheritDoc} */
+ public void setColumn(final int column, final double[] array)
+ throws OutOfRangeException, MatrixDimensionMismatchException {
+ MatrixUtils.checkColumnIndex(this, column);
+ final int nRows = getRowDimension();
+ if (array.length != nRows) {
+ throw new MatrixDimensionMismatchException(array.length, 1, nRows, 1);
+ }
+ for (int i = 0; i < nRows; ++i) {
+ setEntry(i, column, array[i]);
+ }
+ }
+
+ /** {@inheritDoc} */
+ public void addToEntry(int row, int column, double increment) throws OutOfRangeException {
+ MatrixUtils.checkMatrixIndex(this, row, column);
+ setEntry(row, column, getEntry(row, column) + increment);
+ }
+
+ /** {@inheritDoc} */
+ public void multiplyEntry(int row, int column, double factor) throws OutOfRangeException {
+ MatrixUtils.checkMatrixIndex(this, row, column);
+ setEntry(row, column, getEntry(row, column) * factor);
+ }
+
+ /** {@inheritDoc} */
+ public RealMatrix transpose() {
+ final int nRows = getRowDimension();
+ final int nCols = getColumnDimension();
+ final RealMatrix out = createMatrix(nCols, nRows);
+ walkInOptimizedOrder(
+ new DefaultRealMatrixPreservingVisitor() {
+
+ /** {@inheritDoc} */
+ @Override
+ public void visit(final int row, final int column, final double value) {
+ out.setEntry(column, row, value);
+ }
+ });
+
+ return out;
+ }
+
+ /** {@inheritDoc} */
+ public boolean isSquare() {
+ return getColumnDimension() == getRowDimension();
+ }
+
+ /**
+ * Returns the number of rows of this matrix.
+ *
+ * @return the number of rows.
+ */
+ @Override
+ public abstract int getRowDimension();
+
+ /**
+ * Returns the number of columns of this matrix.
+ *
+ * @return the number of columns.
+ */
+ @Override
+ public abstract int getColumnDimension();
+
+ /** {@inheritDoc} */
+ public double getTrace() throws NonSquareMatrixException {
+ final int nRows = getRowDimension();
+ final int nCols = getColumnDimension();
+ if (nRows != nCols) {
+ throw new NonSquareMatrixException(nRows, nCols);
+ }
+ double trace = 0;
+ for (int i = 0; i < nRows; ++i) {
+ trace += getEntry(i, i);
+ }
+ return trace;
+ }
+
+ /** {@inheritDoc} */
+ public double[] operate(final double[] v) throws DimensionMismatchException {
+ final int nRows = getRowDimension();
+ final int nCols = getColumnDimension();
+ if (v.length != nCols) {
+ throw new DimensionMismatchException(v.length, nCols);
+ }
+
+ final double[] out = new double[nRows];
+ for (int row = 0; row < nRows; ++row) {
+ double sum = 0;
+ for (int i = 0; i < nCols; ++i) {
+ sum += getEntry(row, i) * v[i];
+ }
+ out[row] = sum;
+ }
+
+ return out;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public RealVector operate(final RealVector v) throws DimensionMismatchException {
+ try {
+ return new ArrayRealVector(operate(((ArrayRealVector) v).getDataRef()), false);
+ } catch (ClassCastException cce) {
+ final int nRows = getRowDimension();
+ final int nCols = getColumnDimension();
+ if (v.getDimension() != nCols) {
+ throw new DimensionMismatchException(v.getDimension(), nCols);
+ }
+
+ final double[] out = new double[nRows];
+ for (int row = 0; row < nRows; ++row) {
+ double sum = 0;
+ for (int i = 0; i < nCols; ++i) {
+ sum += getEntry(row, i) * v.getEntry(i);
+ }
+ out[row] = sum;
+ }
+
+ return new ArrayRealVector(out, false);
+ }
+ }
+
+ /** {@inheritDoc} */
+ public double[] preMultiply(final double[] v) throws DimensionMismatchException {
+
+ final int nRows = getRowDimension();
+ final int nCols = getColumnDimension();
+ if (v.length != nRows) {
+ throw new DimensionMismatchException(v.length, nRows);
+ }
+
+ final double[] out = new double[nCols];
+ for (int col = 0; col < nCols; ++col) {
+ double sum = 0;
+ for (int i = 0; i < nRows; ++i) {
+ sum += getEntry(i, col) * v[i];
+ }
+ out[col] = sum;
+ }
+
+ return out;
+ }
+
+ /** {@inheritDoc} */
+ public RealVector preMultiply(final RealVector v) throws DimensionMismatchException {
+ try {
+ return new ArrayRealVector(preMultiply(((ArrayRealVector) v).getDataRef()), false);
+ } catch (ClassCastException cce) {
+
+ final int nRows = getRowDimension();
+ final int nCols = getColumnDimension();
+ if (v.getDimension() != nRows) {
+ throw new DimensionMismatchException(v.getDimension(), nRows);
+ }
+
+ final double[] out = new double[nCols];
+ for (int col = 0; col < nCols; ++col) {
+ double sum = 0;
+ for (int i = 0; i < nRows; ++i) {
+ sum += getEntry(i, col) * v.getEntry(i);
+ }
+ out[col] = sum;
+ }
+
+ return new ArrayRealVector(out, false);
+ }
+ }
+
+ /** {@inheritDoc} */
+ public double walkInRowOrder(final RealMatrixChangingVisitor visitor) {
+ final int rows = getRowDimension();
+ final int columns = getColumnDimension();
+ visitor.start(rows, columns, 0, rows - 1, 0, columns - 1);
+ for (int row = 0; row < rows; ++row) {
+ for (int column = 0; column < columns; ++column) {
+ final double oldValue = getEntry(row, column);
+ final double newValue = visitor.visit(row, column, oldValue);
+ setEntry(row, column, newValue);
+ }
+ }
+ return visitor.end();
+ }
+
+ /** {@inheritDoc} */
+ public double walkInRowOrder(final RealMatrixPreservingVisitor visitor) {
+ final int rows = getRowDimension();
+ final int columns = getColumnDimension();
+ visitor.start(rows, columns, 0, rows - 1, 0, columns - 1);
+ for (int row = 0; row < rows; ++row) {
+ for (int column = 0; column < columns; ++column) {
+ visitor.visit(row, column, getEntry(row, column));
+ }
+ }
+ return visitor.end();
+ }
+
+ /** {@inheritDoc} */
+ public double walkInRowOrder(
+ final RealMatrixChangingVisitor visitor,
+ final int startRow,
+ final int endRow,
+ final int startColumn,
+ final int endColumn)
+ throws OutOfRangeException, NumberIsTooSmallException {
+ MatrixUtils.checkSubMatrixIndex(this, startRow, endRow, startColumn, endColumn);
+ visitor.start(
+ getRowDimension(), getColumnDimension(), startRow, endRow, startColumn, endColumn);
+ for (int row = startRow; row <= endRow; ++row) {
+ for (int column = startColumn; column <= endColumn; ++column) {
+ final double oldValue = getEntry(row, column);
+ final double newValue = visitor.visit(row, column, oldValue);
+ setEntry(row, column, newValue);
+ }
+ }
+ return visitor.end();
+ }
+
+ /** {@inheritDoc} */
+ public double walkInRowOrder(
+ final RealMatrixPreservingVisitor visitor,
+ final int startRow,
+ final int endRow,
+ final int startColumn,
+ final int endColumn)
+ throws OutOfRangeException, NumberIsTooSmallException {
+ MatrixUtils.checkSubMatrixIndex(this, startRow, endRow, startColumn, endColumn);
+ visitor.start(
+ getRowDimension(), getColumnDimension(), startRow, endRow, startColumn, endColumn);
+ for (int row = startRow; row <= endRow; ++row) {
+ for (int column = startColumn; column <= endColumn; ++column) {
+ visitor.visit(row, column, getEntry(row, column));
+ }
+ }
+ return visitor.end();
+ }
+
+ /** {@inheritDoc} */
+ public double walkInColumnOrder(final RealMatrixChangingVisitor visitor) {
+ final int rows = getRowDimension();
+ final int columns = getColumnDimension();
+ visitor.start(rows, columns, 0, rows - 1, 0, columns - 1);
+ for (int column = 0; column < columns; ++column) {
+ for (int row = 0; row < rows; ++row) {
+ final double oldValue = getEntry(row, column);
+ final double newValue = visitor.visit(row, column, oldValue);
+ setEntry(row, column, newValue);
+ }
+ }
+ return visitor.end();
+ }
+
+ /** {@inheritDoc} */
+ public double walkInColumnOrder(final RealMatrixPreservingVisitor visitor) {
+ final int rows = getRowDimension();
+ final int columns = getColumnDimension();
+ visitor.start(rows, columns, 0, rows - 1, 0, columns - 1);
+ for (int column = 0; column < columns; ++column) {
+ for (int row = 0; row < rows; ++row) {
+ visitor.visit(row, column, getEntry(row, column));
+ }
+ }
+ return visitor.end();
+ }
+
+ /** {@inheritDoc} */
+ public double walkInColumnOrder(
+ final RealMatrixChangingVisitor visitor,
+ final int startRow,
+ final int endRow,
+ final int startColumn,
+ final int endColumn)
+ throws OutOfRangeException, NumberIsTooSmallException {
+ MatrixUtils.checkSubMatrixIndex(this, startRow, endRow, startColumn, endColumn);
+ visitor.start(
+ getRowDimension(), getColumnDimension(), startRow, endRow, startColumn, endColumn);
+ for (int column = startColumn; column <= endColumn; ++column) {
+ for (int row = startRow; row <= endRow; ++row) {
+ final double oldValue = getEntry(row, column);
+ final double newValue = visitor.visit(row, column, oldValue);
+ setEntry(row, column, newValue);
+ }
+ }
+ return visitor.end();
+ }
+
+ /** {@inheritDoc} */
+ public double walkInColumnOrder(
+ final RealMatrixPreservingVisitor visitor,
+ final int startRow,
+ final int endRow,
+ final int startColumn,
+ final int endColumn)
+ throws OutOfRangeException, NumberIsTooSmallException {
+ MatrixUtils.checkSubMatrixIndex(this, startRow, endRow, startColumn, endColumn);
+ visitor.start(
+ getRowDimension(), getColumnDimension(), startRow, endRow, startColumn, endColumn);
+ for (int column = startColumn; column <= endColumn; ++column) {
+ for (int row = startRow; row <= endRow; ++row) {
+ visitor.visit(row, column, getEntry(row, column));
+ }
+ }
+ return visitor.end();
+ }
+
+ /** {@inheritDoc} */
+ public double walkInOptimizedOrder(final RealMatrixChangingVisitor visitor) {
+ return walkInRowOrder(visitor);
+ }
+
+ /** {@inheritDoc} */
+ public double walkInOptimizedOrder(final RealMatrixPreservingVisitor visitor) {
+ return walkInRowOrder(visitor);
+ }
+
+ /** {@inheritDoc} */
+ public double walkInOptimizedOrder(
+ final RealMatrixChangingVisitor visitor,
+ final int startRow,
+ final int endRow,
+ final int startColumn,
+ final int endColumn)
+ throws OutOfRangeException, NumberIsTooSmallException {
+ return walkInRowOrder(visitor, startRow, endRow, startColumn, endColumn);
+ }
+
+ /** {@inheritDoc} */
+ public double walkInOptimizedOrder(
+ final RealMatrixPreservingVisitor visitor,
+ final int startRow,
+ final int endRow,
+ final int startColumn,
+ final int endColumn)
+ throws OutOfRangeException, NumberIsTooSmallException {
+ return walkInRowOrder(visitor, startRow, endRow, startColumn, endColumn);
+ }
+
+ /**
+ * Get a string representation for this matrix.
+ *
+ * @return a string representation for this matrix
+ */
+ @Override
+ public String toString() {
+ final StringBuilder res = new StringBuilder();
+ String fullClassName = getClass().getName();
+ String shortClassName = fullClassName.substring(fullClassName.lastIndexOf('.') + 1);
+ res.append(shortClassName);
+ res.append(DEFAULT_FORMAT.format(this));
+ return res.toString();
+ }
+
+ /**
+ * Returns true iff <code>object</code> is a <code>RealMatrix</code> instance with the same
+ * dimensions as this and all corresponding matrix entries are equal.
+ *
+ * @param object the object to test equality against.
+ * @return true if object equals this
+ */
+ @Override
+ public boolean equals(final Object object) {
+ if (object == this) {
+ return true;
+ }
+ if (object instanceof RealMatrix == false) {
+ return false;
+ }
+ RealMatrix m = (RealMatrix) object;
+ final int nRows = getRowDimension();
+ final int nCols = getColumnDimension();
+ if (m.getColumnDimension() != nCols || m.getRowDimension() != nRows) {
+ return false;
+ }
+ for (int row = 0; row < nRows; ++row) {
+ for (int col = 0; col < nCols; ++col) {
+ if (getEntry(row, col) != m.getEntry(row, col)) {
+ return false;
+ }
+ }
+ }
+ return true;
+ }
+
+ /**
+ * Computes a hashcode for the matrix.
+ *
+ * @return hashcode for matrix
+ */
+ @Override
+ public int hashCode() {
+ int ret = 7;
+ final int nRows = getRowDimension();
+ final int nCols = getColumnDimension();
+ ret = ret * 31 + nRows;
+ ret = ret * 31 + nCols;
+ for (int row = 0; row < nRows; ++row) {
+ for (int col = 0; col < nCols; ++col) {
+ ret =
+ ret * 31
+ + (11 * (row + 1) + 17 * (col + 1))
+ * MathUtils.hash(getEntry(row, col));
+ }
+ }
+ return ret;
+ }
+
+ /*
+ * Empty implementations of these methods are provided in order to allow for
+ * the use of the @Override tag with Java 1.5.
+ */
+
+ /** {@inheritDoc} */
+ public abstract RealMatrix createMatrix(int rowDimension, int columnDimension)
+ throws NotStrictlyPositiveException;
+
+ /** {@inheritDoc} */
+ public abstract RealMatrix copy();
+
+ /** {@inheritDoc} */
+ public abstract double getEntry(int row, int column) throws OutOfRangeException;
+
+ /** {@inheritDoc} */
+ public abstract void setEntry(int row, int column, double value) throws OutOfRangeException;
+}
diff --git a/src/main/java/org/apache/commons/math3/linear/AnyMatrix.java b/src/main/java/org/apache/commons/math3/linear/AnyMatrix.java
new file mode 100644
index 0000000..1dcce88
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/linear/AnyMatrix.java
@@ -0,0 +1,47 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math3.linear;
+
+/**
+ * Interface defining very basic matrix operations.
+ *
+ * @since 2.0
+ */
+public interface AnyMatrix {
+
+ /**
+ * Is this a square matrix?
+ *
+ * @return true if the matrix is square (rowDimension = columnDimension)
+ */
+ boolean isSquare();
+
+ /**
+ * Returns the number of rows in the matrix.
+ *
+ * @return rowDimension
+ */
+ int getRowDimension();
+
+ /**
+ * Returns the number of columns in the matrix.
+ *
+ * @return columnDimension
+ */
+ int getColumnDimension();
+}
diff --git a/src/main/java/org/apache/commons/math3/linear/Array2DRowFieldMatrix.java b/src/main/java/org/apache/commons/math3/linear/Array2DRowFieldMatrix.java
new file mode 100644
index 0000000..1d9b4c5
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/linear/Array2DRowFieldMatrix.java
@@ -0,0 +1,612 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math3.linear;
+
+import org.apache.commons.math3.Field;
+import org.apache.commons.math3.FieldElement;
+import org.apache.commons.math3.exception.DimensionMismatchException;
+import org.apache.commons.math3.exception.MathIllegalStateException;
+import org.apache.commons.math3.exception.NoDataException;
+import org.apache.commons.math3.exception.NotStrictlyPositiveException;
+import org.apache.commons.math3.exception.NullArgumentException;
+import org.apache.commons.math3.exception.NumberIsTooSmallException;
+import org.apache.commons.math3.exception.OutOfRangeException;
+import org.apache.commons.math3.exception.util.LocalizedFormats;
+import org.apache.commons.math3.util.MathArrays;
+import org.apache.commons.math3.util.MathUtils;
+
+import java.io.Serializable;
+
+/**
+ * Implementation of FieldMatrix<T> using a {@link FieldElement}[][] array to store entries.
+ *
+ * <p>As specified in the {@link FieldMatrix} interface, matrix element indexing is 0-based -- e.g.,
+ * <code>getEntry(0, 0)</code> returns the element in the first row, first column of the matrix.
+ * </ul>
+ *
+ * @param <T> the type of the field elements
+ */
+public class Array2DRowFieldMatrix<T extends FieldElement<T>> extends AbstractFieldMatrix<T>
+ implements Serializable {
+ /** Serializable version identifier */
+ private static final long serialVersionUID = 7260756672015356458L;
+
+ /** Entries of the matrix */
+ private T[][] data;
+
+ /**
+ * Creates a matrix with no data
+ *
+ * @param field field to which the elements belong
+ */
+ public Array2DRowFieldMatrix(final Field<T> field) {
+ super(field);
+ }
+
+ /**
+ * Create a new {@code FieldMatrix<T>} with the supplied row and column dimensions.
+ *
+ * @param field Field to which the elements belong.
+ * @param rowDimension Number of rows in the new matrix.
+ * @param columnDimension Number of columns in the new matrix.
+ * @throws NotStrictlyPositiveException if row or column dimension is not positive.
+ */
+ public Array2DRowFieldMatrix(
+ final Field<T> field, final int rowDimension, final int columnDimension)
+ throws NotStrictlyPositiveException {
+ super(field, rowDimension, columnDimension);
+ data = MathArrays.buildArray(field, rowDimension, columnDimension);
+ }
+
+ /**
+ * Create a new {@code FieldMatrix<T>} using the input array as the underlying data array.
+ *
+ * <p>The input array is copied, not referenced. This constructor has the same effect as calling
+ * {@link #Array2DRowFieldMatrix(FieldElement[][], boolean)} with the second argument set to
+ * {@code true}.
+ *
+ * @param d Data for the new matrix.
+ * @throws DimensionMismatchException if {@code d} is not rectangular.
+ * @throws NullArgumentException if {@code d} is {@code null}.
+ * @throws NoDataException if there are not at least one row and one column.
+ * @see #Array2DRowFieldMatrix(FieldElement[][], boolean)
+ */
+ public Array2DRowFieldMatrix(final T[][] d)
+ throws DimensionMismatchException, NullArgumentException, NoDataException {
+ this(extractField(d), d);
+ }
+
+ /**
+ * Create a new {@code FieldMatrix<T>} using the input array as the underlying data array.
+ *
+ * <p>The input array is copied, not referenced. This constructor has the same effect as calling
+ * {@link #Array2DRowFieldMatrix(FieldElement[][], boolean)} with the second argument set to
+ * {@code true}.
+ *
+ * @param field Field to which the elements belong.
+ * @param d Data for the new matrix.
+ * @throws DimensionMismatchException if {@code d} is not rectangular.
+ * @throws NullArgumentException if {@code d} is {@code null}.
+ * @throws NoDataException if there are not at least one row and one column.
+ * @see #Array2DRowFieldMatrix(FieldElement[][], boolean)
+ */
+ public Array2DRowFieldMatrix(final Field<T> field, final T[][] d)
+ throws DimensionMismatchException, NullArgumentException, NoDataException {
+ super(field);
+ copyIn(d);
+ }
+
+ /**
+ * Create a new {@code FieldMatrix<T>} using the input array as the underlying data array.
+ *
+ * <p>If an array is built specially in order to be embedded in a {@code FieldMatrix<T>} and not
+ * used directly, the {@code copyArray} may be set to {@code false}. This will prevent the
+ * copying and improve performance as no new array will be built and no data will be copied.
+ *
+ * @param d Data for the new matrix.
+ * @param copyArray Whether to copy or reference the input array.
+ * @throws DimensionMismatchException if {@code d} is not rectangular.
+ * @throws NoDataException if there are not at least one row and one column.
+ * @throws NullArgumentException if {@code d} is {@code null}.
+ * @see #Array2DRowFieldMatrix(FieldElement[][])
+ */
+ public Array2DRowFieldMatrix(final T[][] d, final boolean copyArray)
+ throws DimensionMismatchException, NoDataException, NullArgumentException {
+ this(extractField(d), d, copyArray);
+ }
+
+ /**
+ * Create a new {@code FieldMatrix<T>} using the input array as the underlying data array.
+ *
+ * <p>If an array is built specially in order to be embedded in a {@code FieldMatrix<T>} and not
+ * used directly, the {@code copyArray} may be set to {@code false}. This will prevent the
+ * copying and improve performance as no new array will be built and no data will be copied.
+ *
+ * @param field Field to which the elements belong.
+ * @param d Data for the new matrix.
+ * @param copyArray Whether to copy or reference the input array.
+ * @throws DimensionMismatchException if {@code d} is not rectangular.
+ * @throws NoDataException if there are not at least one row and one column.
+ * @throws NullArgumentException if {@code d} is {@code null}.
+ * @see #Array2DRowFieldMatrix(FieldElement[][])
+ */
+ public Array2DRowFieldMatrix(final Field<T> field, final T[][] d, final boolean copyArray)
+ throws DimensionMismatchException, NoDataException, NullArgumentException {
+ super(field);
+ if (copyArray) {
+ copyIn(d);
+ } else {
+ MathUtils.checkNotNull(d);
+ final int nRows = d.length;
+ if (nRows == 0) {
+ throw new NoDataException(LocalizedFormats.AT_LEAST_ONE_ROW);
+ }
+ final int nCols = d[0].length;
+ if (nCols == 0) {
+ throw new NoDataException(LocalizedFormats.AT_LEAST_ONE_COLUMN);
+ }
+ for (int r = 1; r < nRows; r++) {
+ if (d[r].length != nCols) {
+ throw new DimensionMismatchException(nCols, d[r].length);
+ }
+ }
+ data = d;
+ }
+ }
+
+ /**
+ * Create a new (column) {@code FieldMatrix<T>} using {@code v} as the data for the unique
+ * column of the created matrix. The input array is copied.
+ *
+ * @param v Column vector holding data for new matrix.
+ * @throws NoDataException if v is empty
+ */
+ public Array2DRowFieldMatrix(final T[] v) throws NoDataException {
+ this(extractField(v), v);
+ }
+
+ /**
+ * Create a new (column) {@code FieldMatrix<T>} using {@code v} as the data for the unique
+ * column of the created matrix. The input array is copied.
+ *
+ * @param field Field to which the elements belong.
+ * @param v Column vector holding data for new matrix.
+ */
+ public Array2DRowFieldMatrix(final Field<T> field, final T[] v) {
+ super(field);
+ final int nRows = v.length;
+ data = MathArrays.buildArray(getField(), nRows, 1);
+ for (int row = 0; row < nRows; row++) {
+ data[row][0] = v[row];
+ }
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public FieldMatrix<T> createMatrix(final int rowDimension, final int columnDimension)
+ throws NotStrictlyPositiveException {
+ return new Array2DRowFieldMatrix<T>(getField(), rowDimension, columnDimension);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public FieldMatrix<T> copy() {
+ return new Array2DRowFieldMatrix<T>(getField(), copyOut(), false);
+ }
+
+ /**
+ * Add {@code m} to this matrix.
+ *
+ * @param m Matrix to be added.
+ * @return {@code this} + m.
+ * @throws MatrixDimensionMismatchException if {@code m} is not the same size as this matrix.
+ */
+ public Array2DRowFieldMatrix<T> add(final Array2DRowFieldMatrix<T> m)
+ throws MatrixDimensionMismatchException {
+ // safety check
+ checkAdditionCompatible(m);
+
+ final int rowCount = getRowDimension();
+ final int columnCount = getColumnDimension();
+ final T[][] outData = MathArrays.buildArray(getField(), rowCount, columnCount);
+ for (int row = 0; row < rowCount; row++) {
+ final T[] dataRow = data[row];
+ final T[] mRow = m.data[row];
+ final T[] outDataRow = outData[row];
+ for (int col = 0; col < columnCount; col++) {
+ outDataRow[col] = dataRow[col].add(mRow[col]);
+ }
+ }
+
+ return new Array2DRowFieldMatrix<T>(getField(), outData, false);
+ }
+
+ /**
+ * Subtract {@code m} from this matrix.
+ *
+ * @param m Matrix to be subtracted.
+ * @return {@code this} + m.
+ * @throws MatrixDimensionMismatchException if {@code m} is not the same size as this matrix.
+ */
+ public Array2DRowFieldMatrix<T> subtract(final Array2DRowFieldMatrix<T> m)
+ throws MatrixDimensionMismatchException {
+ // safety check
+ checkSubtractionCompatible(m);
+
+ final int rowCount = getRowDimension();
+ final int columnCount = getColumnDimension();
+ final T[][] outData = MathArrays.buildArray(getField(), rowCount, columnCount);
+ for (int row = 0; row < rowCount; row++) {
+ final T[] dataRow = data[row];
+ final T[] mRow = m.data[row];
+ final T[] outDataRow = outData[row];
+ for (int col = 0; col < columnCount; col++) {
+ outDataRow[col] = dataRow[col].subtract(mRow[col]);
+ }
+ }
+
+ return new Array2DRowFieldMatrix<T>(getField(), outData, false);
+ }
+
+ /**
+ * Postmultiplying this matrix by {@code m}.
+ *
+ * @param m Matrix to postmultiply by.
+ * @return {@code this} * m.
+ * @throws DimensionMismatchException if the number of columns of this matrix is not equal to
+ * the number of rows of {@code m}.
+ */
+ public Array2DRowFieldMatrix<T> multiply(final Array2DRowFieldMatrix<T> m)
+ throws DimensionMismatchException {
+ // safety check
+ checkMultiplicationCompatible(m);
+
+ final int nRows = this.getRowDimension();
+ final int nCols = m.getColumnDimension();
+ final int nSum = this.getColumnDimension();
+ final T[][] outData = MathArrays.buildArray(getField(), nRows, nCols);
+ for (int row = 0; row < nRows; row++) {
+ final T[] dataRow = data[row];
+ final T[] outDataRow = outData[row];
+ for (int col = 0; col < nCols; col++) {
+ T sum = getField().getZero();
+ for (int i = 0; i < nSum; i++) {
+ sum = sum.add(dataRow[i].multiply(m.data[i][col]));
+ }
+ outDataRow[col] = sum;
+ }
+ }
+
+ return new Array2DRowFieldMatrix<T>(getField(), outData, false);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public T[][] getData() {
+ return copyOut();
+ }
+
+ /**
+ * Get a reference to the underlying data array. This methods returns internal data,
+ * <strong>not</strong> fresh copy of it.
+ *
+ * @return the 2-dimensional array of entries.
+ */
+ public T[][] getDataRef() {
+ return data;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void setSubMatrix(final T[][] subMatrix, final int row, final int column)
+ throws OutOfRangeException,
+ NullArgumentException,
+ NoDataException,
+ DimensionMismatchException {
+ if (data == null) {
+ if (row > 0) {
+ throw new MathIllegalStateException(
+ LocalizedFormats.FIRST_ROWS_NOT_INITIALIZED_YET, row);
+ }
+ if (column > 0) {
+ throw new MathIllegalStateException(
+ LocalizedFormats.FIRST_COLUMNS_NOT_INITIALIZED_YET, column);
+ }
+ final int nRows = subMatrix.length;
+ if (nRows == 0) {
+ throw new NoDataException(LocalizedFormats.AT_LEAST_ONE_ROW);
+ }
+
+ final int nCols = subMatrix[0].length;
+ if (nCols == 0) {
+ throw new NoDataException(LocalizedFormats.AT_LEAST_ONE_COLUMN);
+ }
+ data = MathArrays.buildArray(getField(), subMatrix.length, nCols);
+ for (int i = 0; i < data.length; ++i) {
+ if (subMatrix[i].length != nCols) {
+ throw new DimensionMismatchException(nCols, subMatrix[i].length);
+ }
+ System.arraycopy(subMatrix[i], 0, data[i + row], column, nCols);
+ }
+ } else {
+ super.setSubMatrix(subMatrix, row, column);
+ }
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public T getEntry(final int row, final int column) throws OutOfRangeException {
+ checkRowIndex(row);
+ checkColumnIndex(column);
+
+ return data[row][column];
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void setEntry(final int row, final int column, final T value)
+ throws OutOfRangeException {
+ checkRowIndex(row);
+ checkColumnIndex(column);
+
+ data[row][column] = value;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void addToEntry(final int row, final int column, final T increment)
+ throws OutOfRangeException {
+ checkRowIndex(row);
+ checkColumnIndex(column);
+
+ data[row][column] = data[row][column].add(increment);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void multiplyEntry(final int row, final int column, final T factor)
+ throws OutOfRangeException {
+ checkRowIndex(row);
+ checkColumnIndex(column);
+
+ data[row][column] = data[row][column].multiply(factor);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public int getRowDimension() {
+ return (data == null) ? 0 : data.length;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public int getColumnDimension() {
+ return ((data == null) || (data[0] == null)) ? 0 : data[0].length;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public T[] operate(final T[] v) throws DimensionMismatchException {
+ final int nRows = this.getRowDimension();
+ final int nCols = this.getColumnDimension();
+ if (v.length != nCols) {
+ throw new DimensionMismatchException(v.length, nCols);
+ }
+ final T[] out = MathArrays.buildArray(getField(), nRows);
+ for (int row = 0; row < nRows; row++) {
+ final T[] dataRow = data[row];
+ T sum = getField().getZero();
+ for (int i = 0; i < nCols; i++) {
+ sum = sum.add(dataRow[i].multiply(v[i]));
+ }
+ out[row] = sum;
+ }
+ return out;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public T[] preMultiply(final T[] v) throws DimensionMismatchException {
+ final int nRows = getRowDimension();
+ final int nCols = getColumnDimension();
+ if (v.length != nRows) {
+ throw new DimensionMismatchException(v.length, nRows);
+ }
+
+ final T[] out = MathArrays.buildArray(getField(), nCols);
+ for (int col = 0; col < nCols; ++col) {
+ T sum = getField().getZero();
+ for (int i = 0; i < nRows; ++i) {
+ sum = sum.add(data[i][col].multiply(v[i]));
+ }
+ out[col] = sum;
+ }
+
+ return out;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public T walkInRowOrder(final FieldMatrixChangingVisitor<T> visitor) {
+ final int rows = getRowDimension();
+ final int columns = getColumnDimension();
+ visitor.start(rows, columns, 0, rows - 1, 0, columns - 1);
+ for (int i = 0; i < rows; ++i) {
+ final T[] rowI = data[i];
+ for (int j = 0; j < columns; ++j) {
+ rowI[j] = visitor.visit(i, j, rowI[j]);
+ }
+ }
+ return visitor.end();
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public T walkInRowOrder(final FieldMatrixPreservingVisitor<T> visitor) {
+ final int rows = getRowDimension();
+ final int columns = getColumnDimension();
+ visitor.start(rows, columns, 0, rows - 1, 0, columns - 1);
+ for (int i = 0; i < rows; ++i) {
+ final T[] rowI = data[i];
+ for (int j = 0; j < columns; ++j) {
+ visitor.visit(i, j, rowI[j]);
+ }
+ }
+ return visitor.end();
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public T walkInRowOrder(
+ final FieldMatrixChangingVisitor<T> visitor,
+ final int startRow,
+ final int endRow,
+ final int startColumn,
+ final int endColumn)
+ throws OutOfRangeException, NumberIsTooSmallException {
+ checkSubMatrixIndex(startRow, endRow, startColumn, endColumn);
+ visitor.start(
+ getRowDimension(), getColumnDimension(), startRow, endRow, startColumn, endColumn);
+ for (int i = startRow; i <= endRow; ++i) {
+ final T[] rowI = data[i];
+ for (int j = startColumn; j <= endColumn; ++j) {
+ rowI[j] = visitor.visit(i, j, rowI[j]);
+ }
+ }
+ return visitor.end();
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public T walkInRowOrder(
+ final FieldMatrixPreservingVisitor<T> visitor,
+ final int startRow,
+ final int endRow,
+ final int startColumn,
+ final int endColumn)
+ throws OutOfRangeException, NumberIsTooSmallException {
+ checkSubMatrixIndex(startRow, endRow, startColumn, endColumn);
+ visitor.start(
+ getRowDimension(), getColumnDimension(), startRow, endRow, startColumn, endColumn);
+ for (int i = startRow; i <= endRow; ++i) {
+ final T[] rowI = data[i];
+ for (int j = startColumn; j <= endColumn; ++j) {
+ visitor.visit(i, j, rowI[j]);
+ }
+ }
+ return visitor.end();
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public T walkInColumnOrder(final FieldMatrixChangingVisitor<T> visitor) {
+ final int rows = getRowDimension();
+ final int columns = getColumnDimension();
+ visitor.start(rows, columns, 0, rows - 1, 0, columns - 1);
+ for (int j = 0; j < columns; ++j) {
+ for (int i = 0; i < rows; ++i) {
+ final T[] rowI = data[i];
+ rowI[j] = visitor.visit(i, j, rowI[j]);
+ }
+ }
+ return visitor.end();
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public T walkInColumnOrder(final FieldMatrixPreservingVisitor<T> visitor) {
+ final int rows = getRowDimension();
+ final int columns = getColumnDimension();
+ visitor.start(rows, columns, 0, rows - 1, 0, columns - 1);
+ for (int j = 0; j < columns; ++j) {
+ for (int i = 0; i < rows; ++i) {
+ visitor.visit(i, j, data[i][j]);
+ }
+ }
+ return visitor.end();
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public T walkInColumnOrder(
+ final FieldMatrixChangingVisitor<T> visitor,
+ final int startRow,
+ final int endRow,
+ final int startColumn,
+ final int endColumn)
+ throws OutOfRangeException, NumberIsTooSmallException {
+ checkSubMatrixIndex(startRow, endRow, startColumn, endColumn);
+ visitor.start(
+ getRowDimension(), getColumnDimension(), startRow, endRow, startColumn, endColumn);
+ for (int j = startColumn; j <= endColumn; ++j) {
+ for (int i = startRow; i <= endRow; ++i) {
+ final T[] rowI = data[i];
+ rowI[j] = visitor.visit(i, j, rowI[j]);
+ }
+ }
+ return visitor.end();
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public T walkInColumnOrder(
+ final FieldMatrixPreservingVisitor<T> visitor,
+ final int startRow,
+ final int endRow,
+ final int startColumn,
+ final int endColumn)
+ throws OutOfRangeException, NumberIsTooSmallException {
+ checkSubMatrixIndex(startRow, endRow, startColumn, endColumn);
+ visitor.start(
+ getRowDimension(), getColumnDimension(), startRow, endRow, startColumn, endColumn);
+ for (int j = startColumn; j <= endColumn; ++j) {
+ for (int i = startRow; i <= endRow; ++i) {
+ visitor.visit(i, j, data[i][j]);
+ }
+ }
+ return visitor.end();
+ }
+
+ /**
+ * Get a fresh copy of the underlying data array.
+ *
+ * @return a copy of the underlying data array.
+ */
+ private T[][] copyOut() {
+ final int nRows = this.getRowDimension();
+ final T[][] out = MathArrays.buildArray(getField(), nRows, getColumnDimension());
+ // can't copy 2-d array in one shot, otherwise get row references
+ for (int i = 0; i < nRows; i++) {
+ System.arraycopy(data[i], 0, out[i], 0, data[i].length);
+ }
+ return out;
+ }
+
+ /**
+ * Replace data with a fresh copy of the input array.
+ *
+ * @param in Data to copy.
+ * @throws NoDataException if the input array is empty.
+ * @throws DimensionMismatchException if the input array is not rectangular.
+ * @throws NullArgumentException if the input array is {@code null}.
+ */
+ private void copyIn(final T[][] in)
+ throws NullArgumentException, NoDataException, DimensionMismatchException {
+ setSubMatrix(in, 0, 0);
+ }
+}
diff --git a/src/main/java/org/apache/commons/math3/linear/Array2DRowRealMatrix.java b/src/main/java/org/apache/commons/math3/linear/Array2DRowRealMatrix.java
new file mode 100644
index 0000000..1780663
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/linear/Array2DRowRealMatrix.java
@@ -0,0 +1,540 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math3.linear;
+
+import org.apache.commons.math3.exception.DimensionMismatchException;
+import org.apache.commons.math3.exception.MathIllegalStateException;
+import org.apache.commons.math3.exception.NoDataException;
+import org.apache.commons.math3.exception.NotStrictlyPositiveException;
+import org.apache.commons.math3.exception.NullArgumentException;
+import org.apache.commons.math3.exception.NumberIsTooSmallException;
+import org.apache.commons.math3.exception.OutOfRangeException;
+import org.apache.commons.math3.exception.util.LocalizedFormats;
+import org.apache.commons.math3.util.MathUtils;
+
+import java.io.Serializable;
+
+/** Implementation of {@link RealMatrix} using a {@code double[][]} array to store entries. */
+public class Array2DRowRealMatrix extends AbstractRealMatrix implements Serializable {
+ /** Serializable version identifier. */
+ private static final long serialVersionUID = -1067294169172445528L;
+
+ /** Entries of the matrix. */
+ private double data[][];
+
+ /** Creates a matrix with no data */
+ public Array2DRowRealMatrix() {}
+
+ /**
+ * Create a new RealMatrix with the supplied row and column dimensions.
+ *
+ * @param rowDimension Number of rows in the new matrix.
+ * @param columnDimension Number of columns in the new matrix.
+ * @throws NotStrictlyPositiveException if the row or column dimension is not positive.
+ */
+ public Array2DRowRealMatrix(final int rowDimension, final int columnDimension)
+ throws NotStrictlyPositiveException {
+ super(rowDimension, columnDimension);
+ data = new double[rowDimension][columnDimension];
+ }
+
+ /**
+ * Create a new {@code RealMatrix} using the input array as the underlying data array.
+ *
+ * <p>The input array is copied, not referenced. This constructor has the same effect as calling
+ * {@link #Array2DRowRealMatrix(double[][], boolean)} with the second argument set to {@code
+ * true}.
+ *
+ * @param d Data for the new matrix.
+ * @throws DimensionMismatchException if {@code d} is not rectangular.
+ * @throws NoDataException if {@code d} row or column dimension is zero.
+ * @throws NullArgumentException if {@code d} is {@code null}.
+ * @see #Array2DRowRealMatrix(double[][], boolean)
+ */
+ public Array2DRowRealMatrix(final double[][] d)
+ throws DimensionMismatchException, NoDataException, NullArgumentException {
+ copyIn(d);
+ }
+
+ /**
+ * Create a new RealMatrix using the input array as the underlying data array. If an array is
+ * built specially in order to be embedded in a RealMatrix and not used directly, the {@code
+ * copyArray} may be set to {@code false}. This will prevent the copying and improve performance
+ * as no new array will be built and no data will be copied.
+ *
+ * @param d Data for new matrix.
+ * @param copyArray if {@code true}, the input array will be copied, otherwise it will be
+ * referenced.
+ * @throws DimensionMismatchException if {@code d} is not rectangular.
+ * @throws NoDataException if {@code d} row or column dimension is zero.
+ * @throws NullArgumentException if {@code d} is {@code null}.
+ * @see #Array2DRowRealMatrix(double[][])
+ */
+ public Array2DRowRealMatrix(final double[][] d, final boolean copyArray)
+ throws DimensionMismatchException, NoDataException, NullArgumentException {
+ if (copyArray) {
+ copyIn(d);
+ } else {
+ if (d == null) {
+ throw new NullArgumentException();
+ }
+ final int nRows = d.length;
+ if (nRows == 0) {
+ throw new NoDataException(LocalizedFormats.AT_LEAST_ONE_ROW);
+ }
+ final int nCols = d[0].length;
+ if (nCols == 0) {
+ throw new NoDataException(LocalizedFormats.AT_LEAST_ONE_COLUMN);
+ }
+ for (int r = 1; r < nRows; r++) {
+ if (d[r].length != nCols) {
+ throw new DimensionMismatchException(d[r].length, nCols);
+ }
+ }
+ data = d;
+ }
+ }
+
+ /**
+ * Create a new (column) RealMatrix using {@code v} as the data for the unique column of the
+ * created matrix. The input array is copied.
+ *
+ * @param v Column vector holding data for new matrix.
+ */
+ public Array2DRowRealMatrix(final double[] v) {
+ final int nRows = v.length;
+ data = new double[nRows][1];
+ for (int row = 0; row < nRows; row++) {
+ data[row][0] = v[row];
+ }
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public RealMatrix createMatrix(final int rowDimension, final int columnDimension)
+ throws NotStrictlyPositiveException {
+ return new Array2DRowRealMatrix(rowDimension, columnDimension);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public RealMatrix copy() {
+ return new Array2DRowRealMatrix(copyOut(), false);
+ }
+
+ /**
+ * Compute the sum of {@code this} and {@code m}.
+ *
+ * @param m Matrix to be added.
+ * @return {@code this + m}.
+ * @throws MatrixDimensionMismatchException if {@code m} is not the same size as {@code this}.
+ */
+ public Array2DRowRealMatrix add(final Array2DRowRealMatrix m)
+ throws MatrixDimensionMismatchException {
+ // Safety check.
+ MatrixUtils.checkAdditionCompatible(this, m);
+
+ final int rowCount = getRowDimension();
+ final int columnCount = getColumnDimension();
+ final double[][] outData = new double[rowCount][columnCount];
+ for (int row = 0; row < rowCount; row++) {
+ final double[] dataRow = data[row];
+ final double[] mRow = m.data[row];
+ final double[] outDataRow = outData[row];
+ for (int col = 0; col < columnCount; col++) {
+ outDataRow[col] = dataRow[col] + mRow[col];
+ }
+ }
+
+ return new Array2DRowRealMatrix(outData, false);
+ }
+
+ /**
+ * Returns {@code this} minus {@code m}.
+ *
+ * @param m Matrix to be subtracted.
+ * @return {@code this - m}
+ * @throws MatrixDimensionMismatchException if {@code m} is not the same size as {@code this}.
+ */
+ public Array2DRowRealMatrix subtract(final Array2DRowRealMatrix m)
+ throws MatrixDimensionMismatchException {
+ MatrixUtils.checkSubtractionCompatible(this, m);
+
+ final int rowCount = getRowDimension();
+ final int columnCount = getColumnDimension();
+ final double[][] outData = new double[rowCount][columnCount];
+ for (int row = 0; row < rowCount; row++) {
+ final double[] dataRow = data[row];
+ final double[] mRow = m.data[row];
+ final double[] outDataRow = outData[row];
+ for (int col = 0; col < columnCount; col++) {
+ outDataRow[col] = dataRow[col] - mRow[col];
+ }
+ }
+
+ return new Array2DRowRealMatrix(outData, false);
+ }
+
+ /**
+ * Returns the result of postmultiplying {@code this} by {@code m}.
+ *
+ * @param m matrix to postmultiply by
+ * @return {@code this * m}
+ * @throws DimensionMismatchException if {@code columnDimension(this) != rowDimension(m)}
+ */
+ public Array2DRowRealMatrix multiply(final Array2DRowRealMatrix m)
+ throws DimensionMismatchException {
+ MatrixUtils.checkMultiplicationCompatible(this, m);
+
+ final int nRows = this.getRowDimension();
+ final int nCols = m.getColumnDimension();
+ final int nSum = this.getColumnDimension();
+
+ final double[][] outData = new double[nRows][nCols];
+ // Will hold a column of "m".
+ final double[] mCol = new double[nSum];
+ final double[][] mData = m.data;
+
+ // Multiply.
+ for (int col = 0; col < nCols; col++) {
+ // Copy all elements of column "col" of "m" so that
+ // will be in contiguous memory.
+ for (int mRow = 0; mRow < nSum; mRow++) {
+ mCol[mRow] = mData[mRow][col];
+ }
+
+ for (int row = 0; row < nRows; row++) {
+ final double[] dataRow = data[row];
+ double sum = 0;
+ for (int i = 0; i < nSum; i++) {
+ sum += dataRow[i] * mCol[i];
+ }
+ outData[row][col] = sum;
+ }
+ }
+
+ return new Array2DRowRealMatrix(outData, false);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public double[][] getData() {
+ return copyOut();
+ }
+
+ /**
+ * Get a reference to the underlying data array.
+ *
+ * @return 2-dimensional array of entries.
+ */
+ public double[][] getDataRef() {
+ return data;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void setSubMatrix(final double[][] subMatrix, final int row, final int column)
+ throws NoDataException,
+ OutOfRangeException,
+ DimensionMismatchException,
+ NullArgumentException {
+ if (data == null) {
+ if (row > 0) {
+ throw new MathIllegalStateException(
+ LocalizedFormats.FIRST_ROWS_NOT_INITIALIZED_YET, row);
+ }
+ if (column > 0) {
+ throw new MathIllegalStateException(
+ LocalizedFormats.FIRST_COLUMNS_NOT_INITIALIZED_YET, column);
+ }
+ MathUtils.checkNotNull(subMatrix);
+ final int nRows = subMatrix.length;
+ if (nRows == 0) {
+ throw new NoDataException(LocalizedFormats.AT_LEAST_ONE_ROW);
+ }
+
+ final int nCols = subMatrix[0].length;
+ if (nCols == 0) {
+ throw new NoDataException(LocalizedFormats.AT_LEAST_ONE_COLUMN);
+ }
+ data = new double[subMatrix.length][nCols];
+ for (int i = 0; i < data.length; ++i) {
+ if (subMatrix[i].length != nCols) {
+ throw new DimensionMismatchException(subMatrix[i].length, nCols);
+ }
+ System.arraycopy(subMatrix[i], 0, data[i + row], column, nCols);
+ }
+ } else {
+ super.setSubMatrix(subMatrix, row, column);
+ }
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public double getEntry(final int row, final int column) throws OutOfRangeException {
+ MatrixUtils.checkMatrixIndex(this, row, column);
+ return data[row][column];
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void setEntry(final int row, final int column, final double value)
+ throws OutOfRangeException {
+ MatrixUtils.checkMatrixIndex(this, row, column);
+ data[row][column] = value;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void addToEntry(final int row, final int column, final double increment)
+ throws OutOfRangeException {
+ MatrixUtils.checkMatrixIndex(this, row, column);
+ data[row][column] += increment;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void multiplyEntry(final int row, final int column, final double factor)
+ throws OutOfRangeException {
+ MatrixUtils.checkMatrixIndex(this, row, column);
+ data[row][column] *= factor;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public int getRowDimension() {
+ return (data == null) ? 0 : data.length;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public int getColumnDimension() {
+ return ((data == null) || (data[0] == null)) ? 0 : data[0].length;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public double[] operate(final double[] v) throws DimensionMismatchException {
+ final int nRows = this.getRowDimension();
+ final int nCols = this.getColumnDimension();
+ if (v.length != nCols) {
+ throw new DimensionMismatchException(v.length, nCols);
+ }
+ final double[] out = new double[nRows];
+ for (int row = 0; row < nRows; row++) {
+ final double[] dataRow = data[row];
+ double sum = 0;
+ for (int i = 0; i < nCols; i++) {
+ sum += dataRow[i] * v[i];
+ }
+ out[row] = sum;
+ }
+ return out;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public double[] preMultiply(final double[] v) throws DimensionMismatchException {
+ final int nRows = getRowDimension();
+ final int nCols = getColumnDimension();
+ if (v.length != nRows) {
+ throw new DimensionMismatchException(v.length, nRows);
+ }
+
+ final double[] out = new double[nCols];
+ for (int col = 0; col < nCols; ++col) {
+ double sum = 0;
+ for (int i = 0; i < nRows; ++i) {
+ sum += data[i][col] * v[i];
+ }
+ out[col] = sum;
+ }
+
+ return out;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public double walkInRowOrder(final RealMatrixChangingVisitor visitor) {
+ final int rows = getRowDimension();
+ final int columns = getColumnDimension();
+ visitor.start(rows, columns, 0, rows - 1, 0, columns - 1);
+ for (int i = 0; i < rows; ++i) {
+ final double[] rowI = data[i];
+ for (int j = 0; j < columns; ++j) {
+ rowI[j] = visitor.visit(i, j, rowI[j]);
+ }
+ }
+ return visitor.end();
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public double walkInRowOrder(final RealMatrixPreservingVisitor visitor) {
+ final int rows = getRowDimension();
+ final int columns = getColumnDimension();
+ visitor.start(rows, columns, 0, rows - 1, 0, columns - 1);
+ for (int i = 0; i < rows; ++i) {
+ final double[] rowI = data[i];
+ for (int j = 0; j < columns; ++j) {
+ visitor.visit(i, j, rowI[j]);
+ }
+ }
+ return visitor.end();
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public double walkInRowOrder(
+ final RealMatrixChangingVisitor visitor,
+ final int startRow,
+ final int endRow,
+ final int startColumn,
+ final int endColumn)
+ throws OutOfRangeException, NumberIsTooSmallException {
+ MatrixUtils.checkSubMatrixIndex(this, startRow, endRow, startColumn, endColumn);
+ visitor.start(
+ getRowDimension(), getColumnDimension(), startRow, endRow, startColumn, endColumn);
+ for (int i = startRow; i <= endRow; ++i) {
+ final double[] rowI = data[i];
+ for (int j = startColumn; j <= endColumn; ++j) {
+ rowI[j] = visitor.visit(i, j, rowI[j]);
+ }
+ }
+ return visitor.end();
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public double walkInRowOrder(
+ final RealMatrixPreservingVisitor visitor,
+ final int startRow,
+ final int endRow,
+ final int startColumn,
+ final int endColumn)
+ throws OutOfRangeException, NumberIsTooSmallException {
+ MatrixUtils.checkSubMatrixIndex(this, startRow, endRow, startColumn, endColumn);
+ visitor.start(
+ getRowDimension(), getColumnDimension(), startRow, endRow, startColumn, endColumn);
+ for (int i = startRow; i <= endRow; ++i) {
+ final double[] rowI = data[i];
+ for (int j = startColumn; j <= endColumn; ++j) {
+ visitor.visit(i, j, rowI[j]);
+ }
+ }
+ return visitor.end();
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public double walkInColumnOrder(final RealMatrixChangingVisitor visitor) {
+ final int rows = getRowDimension();
+ final int columns = getColumnDimension();
+ visitor.start(rows, columns, 0, rows - 1, 0, columns - 1);
+ for (int j = 0; j < columns; ++j) {
+ for (int i = 0; i < rows; ++i) {
+ final double[] rowI = data[i];
+ rowI[j] = visitor.visit(i, j, rowI[j]);
+ }
+ }
+ return visitor.end();
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public double walkInColumnOrder(final RealMatrixPreservingVisitor visitor) {
+ final int rows = getRowDimension();
+ final int columns = getColumnDimension();
+ visitor.start(rows, columns, 0, rows - 1, 0, columns - 1);
+ for (int j = 0; j < columns; ++j) {
+ for (int i = 0; i < rows; ++i) {
+ visitor.visit(i, j, data[i][j]);
+ }
+ }
+ return visitor.end();
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public double walkInColumnOrder(
+ final RealMatrixChangingVisitor visitor,
+ final int startRow,
+ final int endRow,
+ final int startColumn,
+ final int endColumn)
+ throws OutOfRangeException, NumberIsTooSmallException {
+ MatrixUtils.checkSubMatrixIndex(this, startRow, endRow, startColumn, endColumn);
+ visitor.start(
+ getRowDimension(), getColumnDimension(), startRow, endRow, startColumn, endColumn);
+ for (int j = startColumn; j <= endColumn; ++j) {
+ for (int i = startRow; i <= endRow; ++i) {
+ final double[] rowI = data[i];
+ rowI[j] = visitor.visit(i, j, rowI[j]);
+ }
+ }
+ return visitor.end();
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public double walkInColumnOrder(
+ final RealMatrixPreservingVisitor visitor,
+ final int startRow,
+ final int endRow,
+ final int startColumn,
+ final int endColumn)
+ throws OutOfRangeException, NumberIsTooSmallException {
+ MatrixUtils.checkSubMatrixIndex(this, startRow, endRow, startColumn, endColumn);
+ visitor.start(
+ getRowDimension(), getColumnDimension(), startRow, endRow, startColumn, endColumn);
+ for (int j = startColumn; j <= endColumn; ++j) {
+ for (int i = startRow; i <= endRow; ++i) {
+ visitor.visit(i, j, data[i][j]);
+ }
+ }
+ return visitor.end();
+ }
+
+ /**
+ * Get a fresh copy of the underlying data array.
+ *
+ * @return a copy of the underlying data array.
+ */
+ private double[][] copyOut() {
+ final int nRows = this.getRowDimension();
+ final double[][] out = new double[nRows][this.getColumnDimension()];
+ // can't copy 2-d array in one shot, otherwise get row references
+ for (int i = 0; i < nRows; i++) {
+ System.arraycopy(data[i], 0, out[i], 0, data[i].length);
+ }
+ return out;
+ }
+
+ /**
+ * Replace data with a fresh copy of the input array.
+ *
+ * @param in Data to copy.
+ * @throws NoDataException if the input array is empty.
+ * @throws DimensionMismatchException if the input array is not rectangular.
+ * @throws NullArgumentException if the input array is {@code null}.
+ */
+ private void copyIn(final double[][] in)
+ throws DimensionMismatchException, NoDataException, NullArgumentException {
+ setSubMatrix(in, 0, 0);
+ }
+}
diff --git a/src/main/java/org/apache/commons/math3/linear/ArrayFieldVector.java b/src/main/java/org/apache/commons/math3/linear/ArrayFieldVector.java
new file mode 100644
index 0000000..db90aaf
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/linear/ArrayFieldVector.java
@@ -0,0 +1,1091 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math3.linear;
+
+import org.apache.commons.math3.Field;
+import org.apache.commons.math3.FieldElement;
+import org.apache.commons.math3.exception.DimensionMismatchException;
+import org.apache.commons.math3.exception.MathArithmeticException;
+import org.apache.commons.math3.exception.NotPositiveException;
+import org.apache.commons.math3.exception.NullArgumentException;
+import org.apache.commons.math3.exception.NumberIsTooLargeException;
+import org.apache.commons.math3.exception.NumberIsTooSmallException;
+import org.apache.commons.math3.exception.OutOfRangeException;
+import org.apache.commons.math3.exception.ZeroException;
+import org.apache.commons.math3.exception.util.LocalizedFormats;
+import org.apache.commons.math3.util.MathArrays;
+import org.apache.commons.math3.util.MathUtils;
+
+import java.io.Serializable;
+import java.util.Arrays;
+
+/**
+ * This class implements the {@link FieldVector} interface with a {@link FieldElement} array.
+ *
+ * @param <T> the type of the field elements
+ * @since 2.0
+ */
+public class ArrayFieldVector<T extends FieldElement<T>> implements FieldVector<T>, Serializable {
+ /** Serializable version identifier. */
+ private static final long serialVersionUID = 7648186910365927050L;
+
+ /** Entries of the vector. */
+ private T[] data;
+
+ /** Field to which the elements belong. */
+ private final Field<T> field;
+
+ /**
+ * Build a 0-length vector. Zero-length vectors may be used to initialize construction of
+ * vectors by data gathering. We start with zero-length and use either the {@link
+ * #ArrayFieldVector(ArrayFieldVector, ArrayFieldVector)} constructor or one of the {@code
+ * append} methods ({@link #add(FieldVector)} or {@link #append(ArrayFieldVector)}) to gather
+ * data into this vector.
+ *
+ * @param field field to which the elements belong
+ */
+ public ArrayFieldVector(final Field<T> field) {
+ this(field, 0);
+ }
+
+ /**
+ * Construct a vector of zeroes.
+ *
+ * @param field Field to which the elements belong.
+ * @param size Size of the vector.
+ */
+ public ArrayFieldVector(Field<T> field, int size) {
+ this.field = field;
+ this.data = MathArrays.buildArray(field, size);
+ }
+
+ /**
+ * Construct a vector with preset values.
+ *
+ * @param size Size of the vector.
+ * @param preset All entries will be set with this value.
+ */
+ public ArrayFieldVector(int size, T preset) {
+ this(preset.getField(), size);
+ Arrays.fill(data, preset);
+ }
+
+ /**
+ * Construct a vector from an array, copying the input array. This constructor needs a non-empty
+ * {@code d} array to retrieve the field from its first element. This implies it cannot build 0
+ * length vectors. To build vectors from any size, one should use the {@link
+ * #ArrayFieldVector(Field, FieldElement[])} constructor.
+ *
+ * @param d Array.
+ * @throws NullArgumentException if {@code d} is {@code null}.
+ * @throws ZeroException if {@code d} is empty.
+ * @see #ArrayFieldVector(Field, FieldElement[])
+ */
+ public ArrayFieldVector(T[] d) throws NullArgumentException, ZeroException {
+ MathUtils.checkNotNull(d);
+ try {
+ field = d[0].getField();
+ data = d.clone();
+ } catch (ArrayIndexOutOfBoundsException e) {
+ throw new ZeroException(LocalizedFormats.VECTOR_MUST_HAVE_AT_LEAST_ONE_ELEMENT);
+ }
+ }
+
+ /**
+ * Construct a vector from an array, copying the input array.
+ *
+ * @param field Field to which the elements belong.
+ * @param d Array.
+ * @throws NullArgumentException if {@code d} is {@code null}.
+ * @see #ArrayFieldVector(FieldElement[])
+ */
+ public ArrayFieldVector(Field<T> field, T[] d) throws NullArgumentException {
+ MathUtils.checkNotNull(d);
+ this.field = field;
+ data = d.clone();
+ }
+
+ /**
+ * Create a new ArrayFieldVector using the input array as the underlying data array. If an array
+ * is built specially in order to be embedded in a ArrayFieldVector and not used directly, the
+ * {@code copyArray} may be set to {@code false}. This will prevent the copying and improve
+ * performance as no new array will be built and no data will be copied. This constructor needs
+ * a non-empty {@code d} array to retrieve the field from its first element. This implies it
+ * cannot build 0 length vectors. To build vectors from any size, one should use the {@link
+ * #ArrayFieldVector(Field, FieldElement[], boolean)} constructor.
+ *
+ * @param d Data for the new vector.
+ * @param copyArray If {@code true}, the input array will be copied, otherwise it will be
+ * referenced.
+ * @throws NullArgumentException if {@code d} is {@code null}.
+ * @throws ZeroException if {@code d} is empty.
+ * @see #ArrayFieldVector(FieldElement[])
+ * @see #ArrayFieldVector(Field, FieldElement[], boolean)
+ */
+ public ArrayFieldVector(T[] d, boolean copyArray) throws NullArgumentException, ZeroException {
+ MathUtils.checkNotNull(d);
+ if (d.length == 0) {
+ throw new ZeroException(LocalizedFormats.VECTOR_MUST_HAVE_AT_LEAST_ONE_ELEMENT);
+ }
+ field = d[0].getField();
+ data = copyArray ? d.clone() : d;
+ }
+
+ /**
+ * Create a new ArrayFieldVector using the input array as the underlying data array. If an array
+ * is built specially in order to be embedded in a ArrayFieldVector and not used directly, the
+ * {@code copyArray} may be set to {@code false}. This will prevent the copying and improve
+ * performance as no new array will be built and no data will be copied.
+ *
+ * @param field Field to which the elements belong.
+ * @param d Data for the new vector.
+ * @param copyArray If {@code true}, the input array will be copied, otherwise it will be
+ * referenced.
+ * @throws NullArgumentException if {@code d} is {@code null}.
+ * @see #ArrayFieldVector(FieldElement[], boolean)
+ */
+ public ArrayFieldVector(Field<T> field, T[] d, boolean copyArray) throws NullArgumentException {
+ MathUtils.checkNotNull(d);
+ this.field = field;
+ data = copyArray ? d.clone() : d;
+ }
+
+ /**
+ * Construct a vector from part of a array.
+ *
+ * @param d Array.
+ * @param pos Position of the first entry.
+ * @param size Number of entries to copy.
+ * @throws NullArgumentException if {@code d} is {@code null}.
+ * @throws NumberIsTooLargeException if the size of {@code d} is less than {@code pos + size}.
+ */
+ public ArrayFieldVector(T[] d, int pos, int size)
+ throws NullArgumentException, NumberIsTooLargeException {
+ MathUtils.checkNotNull(d);
+ if (d.length < pos + size) {
+ throw new NumberIsTooLargeException(pos + size, d.length, true);
+ }
+ field = d[0].getField();
+ data = MathArrays.buildArray(field, size);
+ System.arraycopy(d, pos, data, 0, size);
+ }
+
+ /**
+ * Construct a vector from part of a array.
+ *
+ * @param field Field to which the elements belong.
+ * @param d Array.
+ * @param pos Position of the first entry.
+ * @param size Number of entries to copy.
+ * @throws NullArgumentException if {@code d} is {@code null}.
+ * @throws NumberIsTooLargeException if the size of {@code d} is less than {@code pos + size}.
+ */
+ public ArrayFieldVector(Field<T> field, T[] d, int pos, int size)
+ throws NullArgumentException, NumberIsTooLargeException {
+ MathUtils.checkNotNull(d);
+ if (d.length < pos + size) {
+ throw new NumberIsTooLargeException(pos + size, d.length, true);
+ }
+ this.field = field;
+ data = MathArrays.buildArray(field, size);
+ System.arraycopy(d, pos, data, 0, size);
+ }
+
+ /**
+ * Construct a vector from another vector, using a deep copy.
+ *
+ * @param v Vector to copy.
+ * @throws NullArgumentException if {@code v} is {@code null}.
+ */
+ public ArrayFieldVector(FieldVector<T> v) throws NullArgumentException {
+ MathUtils.checkNotNull(v);
+ field = v.getField();
+ data = MathArrays.buildArray(field, v.getDimension());
+ for (int i = 0; i < data.length; ++i) {
+ data[i] = v.getEntry(i);
+ }
+ }
+
+ /**
+ * Construct a vector from another vector, using a deep copy.
+ *
+ * @param v Vector to copy.
+ * @throws NullArgumentException if {@code v} is {@code null}.
+ */
+ public ArrayFieldVector(ArrayFieldVector<T> v) throws NullArgumentException {
+ MathUtils.checkNotNull(v);
+ field = v.getField();
+ data = v.data.clone();
+ }
+
+ /**
+ * Construct a vector from another vector.
+ *
+ * @param v Vector to copy.
+ * @param deep If {@code true} perform a deep copy, otherwise perform a shallow copy
+ * @throws NullArgumentException if {@code v} is {@code null}.
+ */
+ public ArrayFieldVector(ArrayFieldVector<T> v, boolean deep) throws NullArgumentException {
+ MathUtils.checkNotNull(v);
+ field = v.getField();
+ data = deep ? v.data.clone() : v.data;
+ }
+
+ /**
+ * Construct a vector by appending one vector to another vector.
+ *
+ * @param v1 First vector (will be put in front of the new vector).
+ * @param v2 Second vector (will be put at back of the new vector).
+ * @throws NullArgumentException if {@code v1} or {@code v2} is {@code null}.
+ * @deprecated as of 3.2, replaced by {@link #ArrayFieldVector(FieldVector, FieldVector)}
+ */
+ @Deprecated
+ public ArrayFieldVector(ArrayFieldVector<T> v1, ArrayFieldVector<T> v2)
+ throws NullArgumentException {
+ this((FieldVector<T>) v1, (FieldVector<T>) v2);
+ }
+
+ /**
+ * Construct a vector by appending one vector to another vector.
+ *
+ * @param v1 First vector (will be put in front of the new vector).
+ * @param v2 Second vector (will be put at back of the new vector).
+ * @throws NullArgumentException if {@code v1} or {@code v2} is {@code null}.
+ * @since 3.2
+ */
+ public ArrayFieldVector(FieldVector<T> v1, FieldVector<T> v2) throws NullArgumentException {
+ MathUtils.checkNotNull(v1);
+ MathUtils.checkNotNull(v2);
+ field = v1.getField();
+ final T[] v1Data =
+ (v1 instanceof ArrayFieldVector) ? ((ArrayFieldVector<T>) v1).data : v1.toArray();
+ final T[] v2Data =
+ (v2 instanceof ArrayFieldVector) ? ((ArrayFieldVector<T>) v2).data : v2.toArray();
+ data = MathArrays.buildArray(field, v1Data.length + v2Data.length);
+ System.arraycopy(v1Data, 0, data, 0, v1Data.length);
+ System.arraycopy(v2Data, 0, data, v1Data.length, v2Data.length);
+ }
+
+ /**
+ * Construct a vector by appending one vector to another vector.
+ *
+ * @param v1 First vector (will be put in front of the new vector).
+ * @param v2 Second vector (will be put at back of the new vector).
+ * @throws NullArgumentException if {@code v1} or {@code v2} is {@code null}.
+ * @deprecated as of 3.2, replaced by {@link #ArrayFieldVector(FieldVector, FieldElement[])}
+ */
+ @Deprecated
+ public ArrayFieldVector(ArrayFieldVector<T> v1, T[] v2) throws NullArgumentException {
+ this((FieldVector<T>) v1, v2);
+ }
+
+ /**
+ * Construct a vector by appending one vector to another vector.
+ *
+ * @param v1 First vector (will be put in front of the new vector).
+ * @param v2 Second vector (will be put at back of the new vector).
+ * @throws NullArgumentException if {@code v1} or {@code v2} is {@code null}.
+ * @since 3.2
+ */
+ public ArrayFieldVector(FieldVector<T> v1, T[] v2) throws NullArgumentException {
+ MathUtils.checkNotNull(v1);
+ MathUtils.checkNotNull(v2);
+ field = v1.getField();
+ final T[] v1Data =
+ (v1 instanceof ArrayFieldVector) ? ((ArrayFieldVector<T>) v1).data : v1.toArray();
+ data = MathArrays.buildArray(field, v1Data.length + v2.length);
+ System.arraycopy(v1Data, 0, data, 0, v1Data.length);
+ System.arraycopy(v2, 0, data, v1Data.length, v2.length);
+ }
+
+ /**
+ * Construct a vector by appending one vector to another vector.
+ *
+ * @param v1 First vector (will be put in front of the new vector).
+ * @param v2 Second vector (will be put at back of the new vector).
+ * @throws NullArgumentException if {@code v1} or {@code v2} is {@code null}.
+ * @deprecated as of 3.2, replaced by {@link #ArrayFieldVector(FieldElement[], FieldVector)}
+ */
+ @Deprecated
+ public ArrayFieldVector(T[] v1, ArrayFieldVector<T> v2) throws NullArgumentException {
+ this(v1, (FieldVector<T>) v2);
+ }
+
+ /**
+ * Construct a vector by appending one vector to another vector.
+ *
+ * @param v1 First vector (will be put in front of the new vector).
+ * @param v2 Second vector (will be put at back of the new vector).
+ * @throws NullArgumentException if {@code v1} or {@code v2} is {@code null}.
+ * @since 3.2
+ */
+ public ArrayFieldVector(T[] v1, FieldVector<T> v2) throws NullArgumentException {
+ MathUtils.checkNotNull(v1);
+ MathUtils.checkNotNull(v2);
+ field = v2.getField();
+ final T[] v2Data =
+ (v2 instanceof ArrayFieldVector) ? ((ArrayFieldVector<T>) v2).data : v2.toArray();
+ data = MathArrays.buildArray(field, v1.length + v2Data.length);
+ System.arraycopy(v1, 0, data, 0, v1.length);
+ System.arraycopy(v2Data, 0, data, v1.length, v2Data.length);
+ }
+
+ /**
+ * Construct a vector by appending one vector to another vector. This constructor needs at least
+ * one non-empty array to retrieve the field from its first element. This implies it cannot
+ * build 0 length vectors. To build vectors from any size, one should use the {@link
+ * #ArrayFieldVector(Field, FieldElement[], FieldElement[])} constructor.
+ *
+ * @param v1 First vector (will be put in front of the new vector).
+ * @param v2 Second vector (will be put at back of the new vector).
+ * @throws NullArgumentException if {@code v1} or {@code v2} is {@code null}.
+ * @throws ZeroException if both arrays are empty.
+ * @see #ArrayFieldVector(Field, FieldElement[], FieldElement[])
+ */
+ public ArrayFieldVector(T[] v1, T[] v2) throws NullArgumentException, ZeroException {
+ MathUtils.checkNotNull(v1);
+ MathUtils.checkNotNull(v2);
+ if (v1.length + v2.length == 0) {
+ throw new ZeroException(LocalizedFormats.VECTOR_MUST_HAVE_AT_LEAST_ONE_ELEMENT);
+ }
+ data = MathArrays.buildArray(v1[0].getField(), v1.length + v2.length);
+ System.arraycopy(v1, 0, data, 0, v1.length);
+ System.arraycopy(v2, 0, data, v1.length, v2.length);
+ field = data[0].getField();
+ }
+
+ /**
+ * Construct a vector by appending one vector to another vector.
+ *
+ * @param field Field to which the elements belong.
+ * @param v1 First vector (will be put in front of the new vector).
+ * @param v2 Second vector (will be put at back of the new vector).
+ * @throws NullArgumentException if {@code v1} or {@code v2} is {@code null}.
+ * @throws ZeroException if both arrays are empty.
+ * @see #ArrayFieldVector(FieldElement[], FieldElement[])
+ */
+ public ArrayFieldVector(Field<T> field, T[] v1, T[] v2)
+ throws NullArgumentException, ZeroException {
+ MathUtils.checkNotNull(v1);
+ MathUtils.checkNotNull(v2);
+ if (v1.length + v2.length == 0) {
+ throw new ZeroException(LocalizedFormats.VECTOR_MUST_HAVE_AT_LEAST_ONE_ELEMENT);
+ }
+ data = MathArrays.buildArray(field, v1.length + v2.length);
+ System.arraycopy(v1, 0, data, 0, v1.length);
+ System.arraycopy(v2, 0, data, v1.length, v2.length);
+ this.field = field;
+ }
+
+ /** {@inheritDoc} */
+ public Field<T> getField() {
+ return field;
+ }
+
+ /** {@inheritDoc} */
+ public FieldVector<T> copy() {
+ return new ArrayFieldVector<T>(this, true);
+ }
+
+ /** {@inheritDoc} */
+ public FieldVector<T> add(FieldVector<T> v) throws DimensionMismatchException {
+ try {
+ return add((ArrayFieldVector<T>) v);
+ } catch (ClassCastException cce) {
+ checkVectorDimensions(v);
+ T[] out = MathArrays.buildArray(field, data.length);
+ for (int i = 0; i < data.length; i++) {
+ out[i] = data[i].add(v.getEntry(i));
+ }
+ return new ArrayFieldVector<T>(field, out, false);
+ }
+ }
+
+ /**
+ * Compute the sum of {@code this} and {@code v}.
+ *
+ * @param v vector to be added
+ * @return {@code this + v}
+ * @throws DimensionMismatchException if {@code v} is not the same size as {@code this}
+ */
+ public ArrayFieldVector<T> add(ArrayFieldVector<T> v) throws DimensionMismatchException {
+ checkVectorDimensions(v.data.length);
+ T[] out = MathArrays.buildArray(field, data.length);
+ for (int i = 0; i < data.length; i++) {
+ out[i] = data[i].add(v.data[i]);
+ }
+ return new ArrayFieldVector<T>(field, out, false);
+ }
+
+ /** {@inheritDoc} */
+ public FieldVector<T> subtract(FieldVector<T> v) throws DimensionMismatchException {
+ try {
+ return subtract((ArrayFieldVector<T>) v);
+ } catch (ClassCastException cce) {
+ checkVectorDimensions(v);
+ T[] out = MathArrays.buildArray(field, data.length);
+ for (int i = 0; i < data.length; i++) {
+ out[i] = data[i].subtract(v.getEntry(i));
+ }
+ return new ArrayFieldVector<T>(field, out, false);
+ }
+ }
+
+ /**
+ * Compute {@code this} minus {@code v}.
+ *
+ * @param v vector to be subtracted
+ * @return {@code this - v}
+ * @throws DimensionMismatchException if {@code v} is not the same size as {@code this}
+ */
+ public ArrayFieldVector<T> subtract(ArrayFieldVector<T> v) throws DimensionMismatchException {
+ checkVectorDimensions(v.data.length);
+ T[] out = MathArrays.buildArray(field, data.length);
+ for (int i = 0; i < data.length; i++) {
+ out[i] = data[i].subtract(v.data[i]);
+ }
+ return new ArrayFieldVector<T>(field, out, false);
+ }
+
+ /** {@inheritDoc} */
+ public FieldVector<T> mapAdd(T d) throws NullArgumentException {
+ T[] out = MathArrays.buildArray(field, data.length);
+ for (int i = 0; i < data.length; i++) {
+ out[i] = data[i].add(d);
+ }
+ return new ArrayFieldVector<T>(field, out, false);
+ }
+
+ /** {@inheritDoc} */
+ public FieldVector<T> mapAddToSelf(T d) throws NullArgumentException {
+ for (int i = 0; i < data.length; i++) {
+ data[i] = data[i].add(d);
+ }
+ return this;
+ }
+
+ /** {@inheritDoc} */
+ public FieldVector<T> mapSubtract(T d) throws NullArgumentException {
+ T[] out = MathArrays.buildArray(field, data.length);
+ for (int i = 0; i < data.length; i++) {
+ out[i] = data[i].subtract(d);
+ }
+ return new ArrayFieldVector<T>(field, out, false);
+ }
+
+ /** {@inheritDoc} */
+ public FieldVector<T> mapSubtractToSelf(T d) throws NullArgumentException {
+ for (int i = 0; i < data.length; i++) {
+ data[i] = data[i].subtract(d);
+ }
+ return this;
+ }
+
+ /** {@inheritDoc} */
+ public FieldVector<T> mapMultiply(T d) throws NullArgumentException {
+ T[] out = MathArrays.buildArray(field, data.length);
+ for (int i = 0; i < data.length; i++) {
+ out[i] = data[i].multiply(d);
+ }
+ return new ArrayFieldVector<T>(field, out, false);
+ }
+
+ /** {@inheritDoc} */
+ public FieldVector<T> mapMultiplyToSelf(T d) throws NullArgumentException {
+ for (int i = 0; i < data.length; i++) {
+ data[i] = data[i].multiply(d);
+ }
+ return this;
+ }
+
+ /** {@inheritDoc} */
+ public FieldVector<T> mapDivide(T d) throws NullArgumentException, MathArithmeticException {
+ MathUtils.checkNotNull(d);
+ T[] out = MathArrays.buildArray(field, data.length);
+ for (int i = 0; i < data.length; i++) {
+ out[i] = data[i].divide(d);
+ }
+ return new ArrayFieldVector<T>(field, out, false);
+ }
+
+ /** {@inheritDoc} */
+ public FieldVector<T> mapDivideToSelf(T d)
+ throws NullArgumentException, MathArithmeticException {
+ MathUtils.checkNotNull(d);
+ for (int i = 0; i < data.length; i++) {
+ data[i] = data[i].divide(d);
+ }
+ return this;
+ }
+
+ /** {@inheritDoc} */
+ public FieldVector<T> mapInv() throws MathArithmeticException {
+ T[] out = MathArrays.buildArray(field, data.length);
+ final T one = field.getOne();
+ for (int i = 0; i < data.length; i++) {
+ try {
+ out[i] = one.divide(data[i]);
+ } catch (final MathArithmeticException e) {
+ throw new MathArithmeticException(LocalizedFormats.INDEX, i);
+ }
+ }
+ return new ArrayFieldVector<T>(field, out, false);
+ }
+
+ /** {@inheritDoc} */
+ public FieldVector<T> mapInvToSelf() throws MathArithmeticException {
+ final T one = field.getOne();
+ for (int i = 0; i < data.length; i++) {
+ try {
+ data[i] = one.divide(data[i]);
+ } catch (final MathArithmeticException e) {
+ throw new MathArithmeticException(LocalizedFormats.INDEX, i);
+ }
+ }
+ return this;
+ }
+
+ /** {@inheritDoc} */
+ public FieldVector<T> ebeMultiply(FieldVector<T> v) throws DimensionMismatchException {
+ try {
+ return ebeMultiply((ArrayFieldVector<T>) v);
+ } catch (ClassCastException cce) {
+ checkVectorDimensions(v);
+ T[] out = MathArrays.buildArray(field, data.length);
+ for (int i = 0; i < data.length; i++) {
+ out[i] = data[i].multiply(v.getEntry(i));
+ }
+ return new ArrayFieldVector<T>(field, out, false);
+ }
+ }
+
+ /**
+ * Element-by-element multiplication.
+ *
+ * @param v vector by which instance elements must be multiplied
+ * @return a vector containing {@code this[i] * v[i]} for all {@code i}
+ * @throws DimensionMismatchException if {@code v} is not the same size as {@code this}
+ */
+ public ArrayFieldVector<T> ebeMultiply(ArrayFieldVector<T> v)
+ throws DimensionMismatchException {
+ checkVectorDimensions(v.data.length);
+ T[] out = MathArrays.buildArray(field, data.length);
+ for (int i = 0; i < data.length; i++) {
+ out[i] = data[i].multiply(v.data[i]);
+ }
+ return new ArrayFieldVector<T>(field, out, false);
+ }
+
+ /** {@inheritDoc} */
+ public FieldVector<T> ebeDivide(FieldVector<T> v)
+ throws DimensionMismatchException, MathArithmeticException {
+ try {
+ return ebeDivide((ArrayFieldVector<T>) v);
+ } catch (ClassCastException cce) {
+ checkVectorDimensions(v);
+ T[] out = MathArrays.buildArray(field, data.length);
+ for (int i = 0; i < data.length; i++) {
+ try {
+ out[i] = data[i].divide(v.getEntry(i));
+ } catch (final MathArithmeticException e) {
+ throw new MathArithmeticException(LocalizedFormats.INDEX, i);
+ }
+ }
+ return new ArrayFieldVector<T>(field, out, false);
+ }
+ }
+
+ /**
+ * Element-by-element division.
+ *
+ * @param v vector by which instance elements must be divided
+ * @return a vector containing {@code this[i] / v[i]} for all {@code i}
+ * @throws DimensionMismatchException if {@code v} is not the same size as {@code this}
+ * @throws MathArithmeticException if one entry of {@code v} is zero.
+ */
+ public ArrayFieldVector<T> ebeDivide(ArrayFieldVector<T> v)
+ throws DimensionMismatchException, MathArithmeticException {
+ checkVectorDimensions(v.data.length);
+ T[] out = MathArrays.buildArray(field, data.length);
+ for (int i = 0; i < data.length; i++) {
+ try {
+ out[i] = data[i].divide(v.data[i]);
+ } catch (final MathArithmeticException e) {
+ throw new MathArithmeticException(LocalizedFormats.INDEX, i);
+ }
+ }
+ return new ArrayFieldVector<T>(field, out, false);
+ }
+
+ /** {@inheritDoc} */
+ public T[] getData() {
+ return data.clone();
+ }
+
+ /**
+ * Returns a reference to the underlying data array.
+ *
+ * <p>Does not make a fresh copy of the underlying data.
+ *
+ * @return array of entries
+ */
+ public T[] getDataRef() {
+ return data;
+ }
+
+ /** {@inheritDoc} */
+ public T dotProduct(FieldVector<T> v) throws DimensionMismatchException {
+ try {
+ return dotProduct((ArrayFieldVector<T>) v);
+ } catch (ClassCastException cce) {
+ checkVectorDimensions(v);
+ T dot = field.getZero();
+ for (int i = 0; i < data.length; i++) {
+ dot = dot.add(data[i].multiply(v.getEntry(i)));
+ }
+ return dot;
+ }
+ }
+
+ /**
+ * Compute the dot product.
+ *
+ * @param v vector with which dot product should be computed
+ * @return the scalar dot product of {@code this} and {@code v}
+ * @throws DimensionMismatchException if {@code v} is not the same size as {@code this}
+ */
+ public T dotProduct(ArrayFieldVector<T> v) throws DimensionMismatchException {
+ checkVectorDimensions(v.data.length);
+ T dot = field.getZero();
+ for (int i = 0; i < data.length; i++) {
+ dot = dot.add(data[i].multiply(v.data[i]));
+ }
+ return dot;
+ }
+
+ /** {@inheritDoc} */
+ public FieldVector<T> projection(FieldVector<T> v)
+ throws DimensionMismatchException, MathArithmeticException {
+ return v.mapMultiply(dotProduct(v).divide(v.dotProduct(v)));
+ }
+
+ /**
+ * Find the orthogonal projection of this vector onto another vector.
+ *
+ * @param v vector onto which {@code this} must be projected
+ * @return projection of {@code this} onto {@code v}
+ * @throws DimensionMismatchException if {@code v} is not the same size as {@code this}
+ * @throws MathArithmeticException if {@code v} is the null vector.
+ */
+ public ArrayFieldVector<T> projection(ArrayFieldVector<T> v)
+ throws DimensionMismatchException, MathArithmeticException {
+ return (ArrayFieldVector<T>) v.mapMultiply(dotProduct(v).divide(v.dotProduct(v)));
+ }
+
+ /** {@inheritDoc} */
+ public FieldMatrix<T> outerProduct(FieldVector<T> v) {
+ try {
+ return outerProduct((ArrayFieldVector<T>) v);
+ } catch (ClassCastException cce) {
+ final int m = data.length;
+ final int n = v.getDimension();
+ final FieldMatrix<T> out = new Array2DRowFieldMatrix<T>(field, m, n);
+ for (int i = 0; i < m; i++) {
+ for (int j = 0; j < n; j++) {
+ out.setEntry(i, j, data[i].multiply(v.getEntry(j)));
+ }
+ }
+ return out;
+ }
+ }
+
+ /**
+ * Compute the outer product.
+ *
+ * @param v vector with which outer product should be computed
+ * @return the matrix outer product between instance and v
+ */
+ public FieldMatrix<T> outerProduct(ArrayFieldVector<T> v) {
+ final int m = data.length;
+ final int n = v.data.length;
+ final FieldMatrix<T> out = new Array2DRowFieldMatrix<T>(field, m, n);
+ for (int i = 0; i < m; i++) {
+ for (int j = 0; j < n; j++) {
+ out.setEntry(i, j, data[i].multiply(v.data[j]));
+ }
+ }
+ return out;
+ }
+
+ /** {@inheritDoc} */
+ public T getEntry(int index) {
+ return data[index];
+ }
+
+ /** {@inheritDoc} */
+ public int getDimension() {
+ return data.length;
+ }
+
+ /** {@inheritDoc} */
+ public FieldVector<T> append(FieldVector<T> v) {
+ try {
+ return append((ArrayFieldVector<T>) v);
+ } catch (ClassCastException cce) {
+ return new ArrayFieldVector<T>(this, new ArrayFieldVector<T>(v));
+ }
+ }
+
+ /**
+ * Construct a vector by appending a vector to this vector.
+ *
+ * @param v vector to append to this one.
+ * @return a new vector
+ */
+ public ArrayFieldVector<T> append(ArrayFieldVector<T> v) {
+ return new ArrayFieldVector<T>(this, v);
+ }
+
+ /** {@inheritDoc} */
+ public FieldVector<T> append(T in) {
+ final T[] out = MathArrays.buildArray(field, data.length + 1);
+ System.arraycopy(data, 0, out, 0, data.length);
+ out[data.length] = in;
+ return new ArrayFieldVector<T>(field, out, false);
+ }
+
+ /** {@inheritDoc} */
+ public FieldVector<T> getSubVector(int index, int n)
+ throws OutOfRangeException, NotPositiveException {
+ if (n < 0) {
+ throw new NotPositiveException(
+ LocalizedFormats.NUMBER_OF_ELEMENTS_SHOULD_BE_POSITIVE, n);
+ }
+ ArrayFieldVector<T> out = new ArrayFieldVector<T>(field, n);
+ try {
+ System.arraycopy(data, index, out.data, 0, n);
+ } catch (IndexOutOfBoundsException e) {
+ checkIndex(index);
+ checkIndex(index + n - 1);
+ }
+ return out;
+ }
+
+ /** {@inheritDoc} */
+ public void setEntry(int index, T value) {
+ try {
+ data[index] = value;
+ } catch (IndexOutOfBoundsException e) {
+ checkIndex(index);
+ }
+ }
+
+ /** {@inheritDoc} */
+ public void setSubVector(int index, FieldVector<T> v) throws OutOfRangeException {
+ try {
+ try {
+ set(index, (ArrayFieldVector<T>) v);
+ } catch (ClassCastException cce) {
+ for (int i = index; i < index + v.getDimension(); ++i) {
+ data[i] = v.getEntry(i - index);
+ }
+ }
+ } catch (IndexOutOfBoundsException e) {
+ checkIndex(index);
+ checkIndex(index + v.getDimension() - 1);
+ }
+ }
+
+ /**
+ * Set a set of consecutive elements.
+ *
+ * @param index index of first element to be set.
+ * @param v vector containing the values to set.
+ * @throws OutOfRangeException if the index is invalid.
+ */
+ public void set(int index, ArrayFieldVector<T> v) throws OutOfRangeException {
+ try {
+ System.arraycopy(v.data, 0, data, index, v.data.length);
+ } catch (IndexOutOfBoundsException e) {
+ checkIndex(index);
+ checkIndex(index + v.data.length - 1);
+ }
+ }
+
+ /** {@inheritDoc} */
+ public void set(T value) {
+ Arrays.fill(data, value);
+ }
+
+ /** {@inheritDoc} */
+ public T[] toArray() {
+ return data.clone();
+ }
+
+ /**
+ * Check if instance and specified vectors have the same dimension.
+ *
+ * @param v vector to compare instance with
+ * @exception DimensionMismatchException if the vectors do not have the same dimensions
+ */
+ protected void checkVectorDimensions(FieldVector<T> v) throws DimensionMismatchException {
+ checkVectorDimensions(v.getDimension());
+ }
+
+ /**
+ * Check if instance dimension is equal to some expected value.
+ *
+ * @param n Expected dimension.
+ * @throws DimensionMismatchException if the dimension is not equal to the size of {@code this}
+ * vector.
+ */
+ protected void checkVectorDimensions(int n) throws DimensionMismatchException {
+ if (data.length != n) {
+ throw new DimensionMismatchException(data.length, n);
+ }
+ }
+
+ /**
+ * Visits (but does not alter) all entries of this vector in default order (increasing index).
+ *
+ * @param visitor the visitor to be used to process the entries of this vector
+ * @return the value returned by {@link FieldVectorPreservingVisitor#end()} at the end of the
+ * walk
+ * @since 3.3
+ */
+ public T walkInDefaultOrder(final FieldVectorPreservingVisitor<T> visitor) {
+ final int dim = getDimension();
+ visitor.start(dim, 0, dim - 1);
+ for (int i = 0; i < dim; i++) {
+ visitor.visit(i, getEntry(i));
+ }
+ return visitor.end();
+ }
+
+ /**
+ * Visits (but does not alter) some entries of this vector in default order (increasing index).
+ *
+ * @param visitor visitor to be used to process the entries of this vector
+ * @param start the index of the first entry to be visited
+ * @param end the index of the last entry to be visited (inclusive)
+ * @return the value returned by {@link FieldVectorPreservingVisitor#end()} at the end of the
+ * walk
+ * @throws NumberIsTooSmallException if {@code end < start}.
+ * @throws OutOfRangeException if the indices are not valid.
+ * @since 3.3
+ */
+ public T walkInDefaultOrder(
+ final FieldVectorPreservingVisitor<T> visitor, final int start, final int end)
+ throws NumberIsTooSmallException, OutOfRangeException {
+ checkIndices(start, end);
+ visitor.start(getDimension(), start, end);
+ for (int i = start; i <= end; i++) {
+ visitor.visit(i, getEntry(i));
+ }
+ return visitor.end();
+ }
+
+ /**
+ * Visits (but does not alter) all entries of this vector in optimized order. The order in which
+ * the entries are visited is selected so as to lead to the most efficient implementation; it
+ * might depend on the concrete implementation of this abstract class.
+ *
+ * @param visitor the visitor to be used to process the entries of this vector
+ * @return the value returned by {@link FieldVectorPreservingVisitor#end()} at the end of the
+ * walk
+ * @since 3.3
+ */
+ public T walkInOptimizedOrder(final FieldVectorPreservingVisitor<T> visitor) {
+ return walkInDefaultOrder(visitor);
+ }
+
+ /**
+ * Visits (but does not alter) some entries of this vector in optimized order. The order in
+ * which the entries are visited is selected so as to lead to the most efficient implementation;
+ * it might depend on the concrete implementation of this abstract class.
+ *
+ * @param visitor visitor to be used to process the entries of this vector
+ * @param start the index of the first entry to be visited
+ * @param end the index of the last entry to be visited (inclusive)
+ * @return the value returned by {@link FieldVectorPreservingVisitor#end()} at the end of the
+ * walk
+ * @throws NumberIsTooSmallException if {@code end < start}.
+ * @throws OutOfRangeException if the indices are not valid.
+ * @since 3.3
+ */
+ public T walkInOptimizedOrder(
+ final FieldVectorPreservingVisitor<T> visitor, final int start, final int end)
+ throws NumberIsTooSmallException, OutOfRangeException {
+ return walkInDefaultOrder(visitor, start, end);
+ }
+
+ /**
+ * Visits (and possibly alters) all entries of this vector in default order (increasing index).
+ *
+ * @param visitor the visitor to be used to process and modify the entries of this vector
+ * @return the value returned by {@link FieldVectorChangingVisitor#end()} at the end of the walk
+ * @since 3.3
+ */
+ public T walkInDefaultOrder(final FieldVectorChangingVisitor<T> visitor) {
+ final int dim = getDimension();
+ visitor.start(dim, 0, dim - 1);
+ for (int i = 0; i < dim; i++) {
+ setEntry(i, visitor.visit(i, getEntry(i)));
+ }
+ return visitor.end();
+ }
+
+ /**
+ * Visits (and possibly alters) some entries of this vector in default order (increasing index).
+ *
+ * @param visitor visitor to be used to process the entries of this vector
+ * @param start the index of the first entry to be visited
+ * @param end the index of the last entry to be visited (inclusive)
+ * @return the value returned by {@link FieldVectorChangingVisitor#end()} at the end of the walk
+ * @throws NumberIsTooSmallException if {@code end < start}.
+ * @throws OutOfRangeException if the indices are not valid.
+ * @since 3.3
+ */
+ public T walkInDefaultOrder(
+ final FieldVectorChangingVisitor<T> visitor, final int start, final int end)
+ throws NumberIsTooSmallException, OutOfRangeException {
+ checkIndices(start, end);
+ visitor.start(getDimension(), start, end);
+ for (int i = start; i <= end; i++) {
+ setEntry(i, visitor.visit(i, getEntry(i)));
+ }
+ return visitor.end();
+ }
+
+ /**
+ * Visits (and possibly alters) all entries of this vector in optimized order. The order in
+ * which the entries are visited is selected so as to lead to the most efficient implementation;
+ * it might depend on the concrete implementation of this abstract class.
+ *
+ * @param visitor the visitor to be used to process the entries of this vector
+ * @return the value returned by {@link FieldVectorChangingVisitor#end()} at the end of the walk
+ * @since 3.3
+ */
+ public T walkInOptimizedOrder(final FieldVectorChangingVisitor<T> visitor) {
+ return walkInDefaultOrder(visitor);
+ }
+
+ /**
+ * Visits (and possibly change) some entries of this vector in optimized order. The order in
+ * which the entries are visited is selected so as to lead to the most efficient implementation;
+ * it might depend on the concrete implementation of this abstract class.
+ *
+ * @param visitor visitor to be used to process the entries of this vector
+ * @param start the index of the first entry to be visited
+ * @param end the index of the last entry to be visited (inclusive)
+ * @return the value returned by {@link FieldVectorChangingVisitor#end()} at the end of the walk
+ * @throws NumberIsTooSmallException if {@code end < start}.
+ * @throws OutOfRangeException if the indices are not valid.
+ * @since 3.3
+ */
+ public T walkInOptimizedOrder(
+ final FieldVectorChangingVisitor<T> visitor, final int start, final int end)
+ throws NumberIsTooSmallException, OutOfRangeException {
+ return walkInDefaultOrder(visitor, start, end);
+ }
+
+ /**
+ * Test for the equality of two vectors.
+ *
+ * @param other Object to test for equality.
+ * @return {@code true} if two vector objects are equal, {@code false} otherwise.
+ */
+ @Override
+ public boolean equals(Object other) {
+ if (this == other) {
+ return true;
+ }
+ if (other == null) {
+ return false;
+ }
+
+ try {
+ @SuppressWarnings("unchecked") // May fail, but we ignore ClassCastException
+ FieldVector<T> rhs = (FieldVector<T>) other;
+ if (data.length != rhs.getDimension()) {
+ return false;
+ }
+
+ for (int i = 0; i < data.length; ++i) {
+ if (!data[i].equals(rhs.getEntry(i))) {
+ return false;
+ }
+ }
+ return true;
+ } catch (ClassCastException ex) {
+ // ignore exception
+ return false;
+ }
+ }
+
+ /**
+ * Get a hashCode for the real vector.
+ *
+ * <p>All NaN values have the same hash code.
+ *
+ * @return a hash code value for this object
+ */
+ @Override
+ public int hashCode() {
+ int h = 3542;
+ for (final T a : data) {
+ h ^= a.hashCode();
+ }
+ return h;
+ }
+
+ /**
+ * Check if an index is valid.
+ *
+ * @param index Index to check.
+ * @exception OutOfRangeException if the index is not valid.
+ */
+ private void checkIndex(final int index) throws OutOfRangeException {
+ if (index < 0 || index >= getDimension()) {
+ throw new OutOfRangeException(LocalizedFormats.INDEX, index, 0, getDimension() - 1);
+ }
+ }
+
+ /**
+ * Checks that the indices of a subvector are valid.
+ *
+ * @param start the index of the first entry of the subvector
+ * @param end the index of the last entry of the subvector (inclusive)
+ * @throws OutOfRangeException if {@code start} of {@code end} are not valid
+ * @throws NumberIsTooSmallException if {@code end < start}
+ * @since 3.3
+ */
+ private void checkIndices(final int start, final int end)
+ throws NumberIsTooSmallException, OutOfRangeException {
+ final int dim = getDimension();
+ if ((start < 0) || (start >= dim)) {
+ throw new OutOfRangeException(LocalizedFormats.INDEX, start, 0, dim - 1);
+ }
+ if ((end < 0) || (end >= dim)) {
+ throw new OutOfRangeException(LocalizedFormats.INDEX, end, 0, dim - 1);
+ }
+ if (end < start) {
+ throw new NumberIsTooSmallException(
+ LocalizedFormats.INITIAL_ROW_AFTER_FINAL_ROW, end, start, false);
+ }
+ }
+}
diff --git a/src/main/java/org/apache/commons/math3/linear/ArrayRealVector.java b/src/main/java/org/apache/commons/math3/linear/ArrayRealVector.java
new file mode 100644
index 0000000..f4d0e46
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/linear/ArrayRealVector.java
@@ -0,0 +1,937 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math3.linear;
+
+import org.apache.commons.math3.analysis.UnivariateFunction;
+import org.apache.commons.math3.exception.DimensionMismatchException;
+import org.apache.commons.math3.exception.NotPositiveException;
+import org.apache.commons.math3.exception.NullArgumentException;
+import org.apache.commons.math3.exception.NumberIsTooLargeException;
+import org.apache.commons.math3.exception.NumberIsTooSmallException;
+import org.apache.commons.math3.exception.OutOfRangeException;
+import org.apache.commons.math3.exception.util.LocalizedFormats;
+import org.apache.commons.math3.util.FastMath;
+import org.apache.commons.math3.util.MathUtils;
+
+import java.io.Serializable;
+import java.util.Arrays;
+import java.util.Iterator;
+
+/**
+ * This class implements the {@link RealVector} interface with a double array.
+ *
+ * @since 2.0
+ */
+public class ArrayRealVector extends RealVector implements Serializable {
+ /** Serializable version identifier. */
+ private static final long serialVersionUID = -1097961340710804027L;
+
+ /** Default format. */
+ private static final RealVectorFormat DEFAULT_FORMAT = RealVectorFormat.getInstance();
+
+ /** Entries of the vector. */
+ private double data[];
+
+ /**
+ * Build a 0-length vector. Zero-length vectors may be used to initialized construction of
+ * vectors by data gathering. We start with zero-length and use either the {@link
+ * #ArrayRealVector(ArrayRealVector, ArrayRealVector)} constructor or one of the {@code append}
+ * method ({@link #append(double)}, {@link #append(ArrayRealVector)}) to gather data into this
+ * vector.
+ */
+ public ArrayRealVector() {
+ data = new double[0];
+ }
+
+ /**
+ * Construct a vector of zeroes.
+ *
+ * @param size Size of the vector.
+ */
+ public ArrayRealVector(int size) {
+ data = new double[size];
+ }
+
+ /**
+ * Construct a vector with preset values.
+ *
+ * @param size Size of the vector
+ * @param preset All entries will be set with this value.
+ */
+ public ArrayRealVector(int size, double preset) {
+ data = new double[size];
+ Arrays.fill(data, preset);
+ }
+
+ /**
+ * Construct a vector from an array, copying the input array.
+ *
+ * @param d Array.
+ */
+ public ArrayRealVector(double[] d) {
+ data = d.clone();
+ }
+
+ /**
+ * Create a new ArrayRealVector using the input array as the underlying data array. If an array
+ * is built specially in order to be embedded in a ArrayRealVector and not used directly, the
+ * {@code copyArray} may be set to {@code false}. This will prevent the copying and improve
+ * performance as no new array will be built and no data will be copied.
+ *
+ * @param d Data for the new vector.
+ * @param copyArray if {@code true}, the input array will be copied, otherwise it will be
+ * referenced.
+ * @throws NullArgumentException if {@code d} is {@code null}.
+ * @see #ArrayRealVector(double[])
+ */
+ public ArrayRealVector(double[] d, boolean copyArray) throws NullArgumentException {
+ if (d == null) {
+ throw new NullArgumentException();
+ }
+ data = copyArray ? d.clone() : d;
+ }
+
+ /**
+ * Construct a vector from part of a array.
+ *
+ * @param d Array.
+ * @param pos Position of first entry.
+ * @param size Number of entries to copy.
+ * @throws NullArgumentException if {@code d} is {@code null}.
+ * @throws NumberIsTooLargeException if the size of {@code d} is less than {@code pos + size}.
+ */
+ public ArrayRealVector(double[] d, int pos, int size)
+ throws NullArgumentException, NumberIsTooLargeException {
+ if (d == null) {
+ throw new NullArgumentException();
+ }
+ if (d.length < pos + size) {
+ throw new NumberIsTooLargeException(pos + size, d.length, true);
+ }
+ data = new double[size];
+ System.arraycopy(d, pos, data, 0, size);
+ }
+
+ /**
+ * Construct a vector from an array.
+ *
+ * @param d Array of {@code Double}s.
+ */
+ public ArrayRealVector(Double[] d) {
+ data = new double[d.length];
+ for (int i = 0; i < d.length; i++) {
+ data[i] = d[i].doubleValue();
+ }
+ }
+
+ /**
+ * Construct a vector from part of an array.
+ *
+ * @param d Array.
+ * @param pos Position of first entry.
+ * @param size Number of entries to copy.
+ * @throws NullArgumentException if {@code d} is {@code null}.
+ * @throws NumberIsTooLargeException if the size of {@code d} is less than {@code pos + size}.
+ */
+ public ArrayRealVector(Double[] d, int pos, int size)
+ throws NullArgumentException, NumberIsTooLargeException {
+ if (d == null) {
+ throw new NullArgumentException();
+ }
+ if (d.length < pos + size) {
+ throw new NumberIsTooLargeException(pos + size, d.length, true);
+ }
+ data = new double[size];
+ for (int i = pos; i < pos + size; i++) {
+ data[i - pos] = d[i].doubleValue();
+ }
+ }
+
+ /**
+ * Construct a vector from another vector, using a deep copy.
+ *
+ * @param v vector to copy.
+ * @throws NullArgumentException if {@code v} is {@code null}.
+ */
+ public ArrayRealVector(RealVector v) throws NullArgumentException {
+ if (v == null) {
+ throw new NullArgumentException();
+ }
+ data = new double[v.getDimension()];
+ for (int i = 0; i < data.length; ++i) {
+ data[i] = v.getEntry(i);
+ }
+ }
+
+ /**
+ * Construct a vector from another vector, using a deep copy.
+ *
+ * @param v Vector to copy.
+ * @throws NullArgumentException if {@code v} is {@code null}.
+ */
+ public ArrayRealVector(ArrayRealVector v) throws NullArgumentException {
+ this(v, true);
+ }
+
+ /**
+ * Construct a vector from another vector.
+ *
+ * @param v Vector to copy.
+ * @param deep If {@code true} perform a deep copy, otherwise perform a shallow copy.
+ */
+ public ArrayRealVector(ArrayRealVector v, boolean deep) {
+ data = deep ? v.data.clone() : v.data;
+ }
+
+ /**
+ * Construct a vector by appending one vector to another vector.
+ *
+ * @param v1 First vector (will be put in front of the new vector).
+ * @param v2 Second vector (will be put at back of the new vector).
+ */
+ public ArrayRealVector(ArrayRealVector v1, ArrayRealVector v2) {
+ data = new double[v1.data.length + v2.data.length];
+ System.arraycopy(v1.data, 0, data, 0, v1.data.length);
+ System.arraycopy(v2.data, 0, data, v1.data.length, v2.data.length);
+ }
+
+ /**
+ * Construct a vector by appending one vector to another vector.
+ *
+ * @param v1 First vector (will be put in front of the new vector).
+ * @param v2 Second vector (will be put at back of the new vector).
+ */
+ public ArrayRealVector(ArrayRealVector v1, RealVector v2) {
+ final int l1 = v1.data.length;
+ final int l2 = v2.getDimension();
+ data = new double[l1 + l2];
+ System.arraycopy(v1.data, 0, data, 0, l1);
+ for (int i = 0; i < l2; ++i) {
+ data[l1 + i] = v2.getEntry(i);
+ }
+ }
+
+ /**
+ * Construct a vector by appending one vector to another vector.
+ *
+ * @param v1 First vector (will be put in front of the new vector).
+ * @param v2 Second vector (will be put at back of the new vector).
+ */
+ public ArrayRealVector(RealVector v1, ArrayRealVector v2) {
+ final int l1 = v1.getDimension();
+ final int l2 = v2.data.length;
+ data = new double[l1 + l2];
+ for (int i = 0; i < l1; ++i) {
+ data[i] = v1.getEntry(i);
+ }
+ System.arraycopy(v2.data, 0, data, l1, l2);
+ }
+
+ /**
+ * Construct a vector by appending one vector to another vector.
+ *
+ * @param v1 First vector (will be put in front of the new vector).
+ * @param v2 Second vector (will be put at back of the new vector).
+ */
+ public ArrayRealVector(ArrayRealVector v1, double[] v2) {
+ final int l1 = v1.getDimension();
+ final int l2 = v2.length;
+ data = new double[l1 + l2];
+ System.arraycopy(v1.data, 0, data, 0, l1);
+ System.arraycopy(v2, 0, data, l1, l2);
+ }
+
+ /**
+ * Construct a vector by appending one vector to another vector.
+ *
+ * @param v1 First vector (will be put in front of the new vector).
+ * @param v2 Second vector (will be put at back of the new vector).
+ */
+ public ArrayRealVector(double[] v1, ArrayRealVector v2) {
+ final int l1 = v1.length;
+ final int l2 = v2.getDimension();
+ data = new double[l1 + l2];
+ System.arraycopy(v1, 0, data, 0, l1);
+ System.arraycopy(v2.data, 0, data, l1, l2);
+ }
+
+ /**
+ * Construct a vector by appending one vector to another vector.
+ *
+ * @param v1 first vector (will be put in front of the new vector)
+ * @param v2 second vector (will be put at back of the new vector)
+ */
+ public ArrayRealVector(double[] v1, double[] v2) {
+ final int l1 = v1.length;
+ final int l2 = v2.length;
+ data = new double[l1 + l2];
+ System.arraycopy(v1, 0, data, 0, l1);
+ System.arraycopy(v2, 0, data, l1, l2);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public ArrayRealVector copy() {
+ return new ArrayRealVector(this, true);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public ArrayRealVector add(RealVector v) throws DimensionMismatchException {
+ if (v instanceof ArrayRealVector) {
+ final double[] vData = ((ArrayRealVector) v).data;
+ final int dim = vData.length;
+ checkVectorDimensions(dim);
+ ArrayRealVector result = new ArrayRealVector(dim);
+ double[] resultData = result.data;
+ for (int i = 0; i < dim; i++) {
+ resultData[i] = data[i] + vData[i];
+ }
+ return result;
+ } else {
+ checkVectorDimensions(v);
+ double[] out = data.clone();
+ Iterator<Entry> it = v.iterator();
+ while (it.hasNext()) {
+ final Entry e = it.next();
+ out[e.getIndex()] += e.getValue();
+ }
+ return new ArrayRealVector(out, false);
+ }
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public ArrayRealVector subtract(RealVector v) throws DimensionMismatchException {
+ if (v instanceof ArrayRealVector) {
+ final double[] vData = ((ArrayRealVector) v).data;
+ final int dim = vData.length;
+ checkVectorDimensions(dim);
+ ArrayRealVector result = new ArrayRealVector(dim);
+ double[] resultData = result.data;
+ for (int i = 0; i < dim; i++) {
+ resultData[i] = data[i] - vData[i];
+ }
+ return result;
+ } else {
+ checkVectorDimensions(v);
+ double[] out = data.clone();
+ Iterator<Entry> it = v.iterator();
+ while (it.hasNext()) {
+ final Entry e = it.next();
+ out[e.getIndex()] -= e.getValue();
+ }
+ return new ArrayRealVector(out, false);
+ }
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public ArrayRealVector map(UnivariateFunction function) {
+ return copy().mapToSelf(function);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public ArrayRealVector mapToSelf(UnivariateFunction function) {
+ for (int i = 0; i < data.length; i++) {
+ data[i] = function.value(data[i]);
+ }
+ return this;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public RealVector mapAddToSelf(double d) {
+ for (int i = 0; i < data.length; i++) {
+ data[i] += d;
+ }
+ return this;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public RealVector mapSubtractToSelf(double d) {
+ for (int i = 0; i < data.length; i++) {
+ data[i] -= d;
+ }
+ return this;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public RealVector mapMultiplyToSelf(double d) {
+ for (int i = 0; i < data.length; i++) {
+ data[i] *= d;
+ }
+ return this;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public RealVector mapDivideToSelf(double d) {
+ for (int i = 0; i < data.length; i++) {
+ data[i] /= d;
+ }
+ return this;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public ArrayRealVector ebeMultiply(RealVector v) throws DimensionMismatchException {
+ if (v instanceof ArrayRealVector) {
+ final double[] vData = ((ArrayRealVector) v).data;
+ final int dim = vData.length;
+ checkVectorDimensions(dim);
+ ArrayRealVector result = new ArrayRealVector(dim);
+ double[] resultData = result.data;
+ for (int i = 0; i < dim; i++) {
+ resultData[i] = data[i] * vData[i];
+ }
+ return result;
+ } else {
+ checkVectorDimensions(v);
+ double[] out = data.clone();
+ for (int i = 0; i < data.length; i++) {
+ out[i] *= v.getEntry(i);
+ }
+ return new ArrayRealVector(out, false);
+ }
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public ArrayRealVector ebeDivide(RealVector v) throws DimensionMismatchException {
+ if (v instanceof ArrayRealVector) {
+ final double[] vData = ((ArrayRealVector) v).data;
+ final int dim = vData.length;
+ checkVectorDimensions(dim);
+ ArrayRealVector result = new ArrayRealVector(dim);
+ double[] resultData = result.data;
+ for (int i = 0; i < dim; i++) {
+ resultData[i] = data[i] / vData[i];
+ }
+ return result;
+ } else {
+ checkVectorDimensions(v);
+ double[] out = data.clone();
+ for (int i = 0; i < data.length; i++) {
+ out[i] /= v.getEntry(i);
+ }
+ return new ArrayRealVector(out, false);
+ }
+ }
+
+ /**
+ * Get a reference to the underlying data array. This method does not make a fresh copy of the
+ * underlying data.
+ *
+ * @return the array of entries.
+ */
+ public double[] getDataRef() {
+ return data;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public double dotProduct(RealVector v) throws DimensionMismatchException {
+ if (v instanceof ArrayRealVector) {
+ final double[] vData = ((ArrayRealVector) v).data;
+ checkVectorDimensions(vData.length);
+ double dot = 0;
+ for (int i = 0; i < data.length; i++) {
+ dot += data[i] * vData[i];
+ }
+ return dot;
+ }
+ return super.dotProduct(v);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public double getNorm() {
+ double sum = 0;
+ for (double a : data) {
+ sum += a * a;
+ }
+ return FastMath.sqrt(sum);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public double getL1Norm() {
+ double sum = 0;
+ for (double a : data) {
+ sum += FastMath.abs(a);
+ }
+ return sum;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public double getLInfNorm() {
+ double max = 0;
+ for (double a : data) {
+ max = FastMath.max(max, FastMath.abs(a));
+ }
+ return max;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public double getDistance(RealVector v) throws DimensionMismatchException {
+ if (v instanceof ArrayRealVector) {
+ final double[] vData = ((ArrayRealVector) v).data;
+ checkVectorDimensions(vData.length);
+ double sum = 0;
+ for (int i = 0; i < data.length; ++i) {
+ final double delta = data[i] - vData[i];
+ sum += delta * delta;
+ }
+ return FastMath.sqrt(sum);
+ } else {
+ checkVectorDimensions(v);
+ double sum = 0;
+ for (int i = 0; i < data.length; ++i) {
+ final double delta = data[i] - v.getEntry(i);
+ sum += delta * delta;
+ }
+ return FastMath.sqrt(sum);
+ }
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public double getL1Distance(RealVector v) throws DimensionMismatchException {
+ if (v instanceof ArrayRealVector) {
+ final double[] vData = ((ArrayRealVector) v).data;
+ checkVectorDimensions(vData.length);
+ double sum = 0;
+ for (int i = 0; i < data.length; ++i) {
+ final double delta = data[i] - vData[i];
+ sum += FastMath.abs(delta);
+ }
+ return sum;
+ } else {
+ checkVectorDimensions(v);
+ double sum = 0;
+ for (int i = 0; i < data.length; ++i) {
+ final double delta = data[i] - v.getEntry(i);
+ sum += FastMath.abs(delta);
+ }
+ return sum;
+ }
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public double getLInfDistance(RealVector v) throws DimensionMismatchException {
+ if (v instanceof ArrayRealVector) {
+ final double[] vData = ((ArrayRealVector) v).data;
+ checkVectorDimensions(vData.length);
+ double max = 0;
+ for (int i = 0; i < data.length; ++i) {
+ final double delta = data[i] - vData[i];
+ max = FastMath.max(max, FastMath.abs(delta));
+ }
+ return max;
+ } else {
+ checkVectorDimensions(v);
+ double max = 0;
+ for (int i = 0; i < data.length; ++i) {
+ final double delta = data[i] - v.getEntry(i);
+ max = FastMath.max(max, FastMath.abs(delta));
+ }
+ return max;
+ }
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public RealMatrix outerProduct(RealVector v) {
+ if (v instanceof ArrayRealVector) {
+ final double[] vData = ((ArrayRealVector) v).data;
+ final int m = data.length;
+ final int n = vData.length;
+ final RealMatrix out = MatrixUtils.createRealMatrix(m, n);
+ for (int i = 0; i < m; i++) {
+ for (int j = 0; j < n; j++) {
+ out.setEntry(i, j, data[i] * vData[j]);
+ }
+ }
+ return out;
+ } else {
+ final int m = data.length;
+ final int n = v.getDimension();
+ final RealMatrix out = MatrixUtils.createRealMatrix(m, n);
+ for (int i = 0; i < m; i++) {
+ for (int j = 0; j < n; j++) {
+ out.setEntry(i, j, data[i] * v.getEntry(j));
+ }
+ }
+ return out;
+ }
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public double getEntry(int index) throws OutOfRangeException {
+ try {
+ return data[index];
+ } catch (IndexOutOfBoundsException e) {
+ throw new OutOfRangeException(LocalizedFormats.INDEX, index, 0, getDimension() - 1);
+ }
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public int getDimension() {
+ return data.length;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public RealVector append(RealVector v) {
+ try {
+ return new ArrayRealVector(this, (ArrayRealVector) v);
+ } catch (ClassCastException cce) {
+ return new ArrayRealVector(this, v);
+ }
+ }
+
+ /**
+ * Construct a vector by appending a vector to this vector.
+ *
+ * @param v Vector to append to this one.
+ * @return a new vector.
+ */
+ public ArrayRealVector append(ArrayRealVector v) {
+ return new ArrayRealVector(this, v);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public RealVector append(double in) {
+ final double[] out = new double[data.length + 1];
+ System.arraycopy(data, 0, out, 0, data.length);
+ out[data.length] = in;
+ return new ArrayRealVector(out, false);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public RealVector getSubVector(int index, int n)
+ throws OutOfRangeException, NotPositiveException {
+ if (n < 0) {
+ throw new NotPositiveException(
+ LocalizedFormats.NUMBER_OF_ELEMENTS_SHOULD_BE_POSITIVE, n);
+ }
+ ArrayRealVector out = new ArrayRealVector(n);
+ try {
+ System.arraycopy(data, index, out.data, 0, n);
+ } catch (IndexOutOfBoundsException e) {
+ checkIndex(index);
+ checkIndex(index + n - 1);
+ }
+ return out;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void setEntry(int index, double value) throws OutOfRangeException {
+ try {
+ data[index] = value;
+ } catch (IndexOutOfBoundsException e) {
+ checkIndex(index);
+ }
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void addToEntry(int index, double increment) throws OutOfRangeException {
+ try {
+ data[index] += increment;
+ } catch (IndexOutOfBoundsException e) {
+ throw new OutOfRangeException(LocalizedFormats.INDEX, index, 0, data.length - 1);
+ }
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void setSubVector(int index, RealVector v) throws OutOfRangeException {
+ if (v instanceof ArrayRealVector) {
+ setSubVector(index, ((ArrayRealVector) v).data);
+ } else {
+ try {
+ for (int i = index; i < index + v.getDimension(); ++i) {
+ data[i] = v.getEntry(i - index);
+ }
+ } catch (IndexOutOfBoundsException e) {
+ checkIndex(index);
+ checkIndex(index + v.getDimension() - 1);
+ }
+ }
+ }
+
+ /**
+ * Set a set of consecutive elements.
+ *
+ * @param index Index of first element to be set.
+ * @param v Vector containing the values to set.
+ * @throws OutOfRangeException if the index is inconsistent with the vector size.
+ */
+ public void setSubVector(int index, double[] v) throws OutOfRangeException {
+ try {
+ System.arraycopy(v, 0, data, index, v.length);
+ } catch (IndexOutOfBoundsException e) {
+ checkIndex(index);
+ checkIndex(index + v.length - 1);
+ }
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void set(double value) {
+ Arrays.fill(data, value);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public double[] toArray() {
+ return data.clone();
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public String toString() {
+ return DEFAULT_FORMAT.format(this);
+ }
+
+ /**
+ * Check if instance and specified vectors have the same dimension.
+ *
+ * @param v Vector to compare instance with.
+ * @throws DimensionMismatchException if the vectors do not have the same dimension.
+ */
+ @Override
+ protected void checkVectorDimensions(RealVector v) throws DimensionMismatchException {
+ checkVectorDimensions(v.getDimension());
+ }
+
+ /**
+ * Check if instance dimension is equal to some expected value.
+ *
+ * @param n Expected dimension.
+ * @throws DimensionMismatchException if the dimension is inconsistent with vector size.
+ */
+ @Override
+ protected void checkVectorDimensions(int n) throws DimensionMismatchException {
+ if (data.length != n) {
+ throw new DimensionMismatchException(data.length, n);
+ }
+ }
+
+ /**
+ * Check if any coordinate of this vector is {@code NaN}.
+ *
+ * @return {@code true} if any coordinate of this vector is {@code NaN}, {@code false}
+ * otherwise.
+ */
+ @Override
+ public boolean isNaN() {
+ for (double v : data) {
+ if (Double.isNaN(v)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Check whether any coordinate of this vector is infinite and none are {@code NaN}.
+ *
+ * @return {@code true} if any coordinate of this vector is infinite and none are {@code NaN},
+ * {@code false} otherwise.
+ */
+ @Override
+ public boolean isInfinite() {
+ if (isNaN()) {
+ return false;
+ }
+
+ for (double v : data) {
+ if (Double.isInfinite(v)) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public boolean equals(Object other) {
+ if (this == other) {
+ return true;
+ }
+
+ if (!(other instanceof RealVector)) {
+ return false;
+ }
+
+ RealVector rhs = (RealVector) other;
+ if (data.length != rhs.getDimension()) {
+ return false;
+ }
+
+ if (rhs.isNaN()) {
+ return this.isNaN();
+ }
+
+ for (int i = 0; i < data.length; ++i) {
+ if (data[i] != rhs.getEntry(i)) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ /** {@inheritDoc} All {@code NaN} values have the same hash code. */
+ @Override
+ public int hashCode() {
+ if (isNaN()) {
+ return 9;
+ }
+ return MathUtils.hash(data);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public ArrayRealVector combine(double a, double b, RealVector y)
+ throws DimensionMismatchException {
+ return copy().combineToSelf(a, b, y);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public ArrayRealVector combineToSelf(double a, double b, RealVector y)
+ throws DimensionMismatchException {
+ if (y instanceof ArrayRealVector) {
+ final double[] yData = ((ArrayRealVector) y).data;
+ checkVectorDimensions(yData.length);
+ for (int i = 0; i < this.data.length; i++) {
+ data[i] = a * data[i] + b * yData[i];
+ }
+ } else {
+ checkVectorDimensions(y);
+ for (int i = 0; i < this.data.length; i++) {
+ data[i] = a * data[i] + b * y.getEntry(i);
+ }
+ }
+ return this;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public double walkInDefaultOrder(final RealVectorPreservingVisitor visitor) {
+ visitor.start(data.length, 0, data.length - 1);
+ for (int i = 0; i < data.length; i++) {
+ visitor.visit(i, data[i]);
+ }
+ return visitor.end();
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public double walkInDefaultOrder(
+ final RealVectorPreservingVisitor visitor, final int start, final int end)
+ throws NumberIsTooSmallException, OutOfRangeException {
+ checkIndices(start, end);
+ visitor.start(data.length, start, end);
+ for (int i = start; i <= end; i++) {
+ visitor.visit(i, data[i]);
+ }
+ return visitor.end();
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * <p>In this implementation, the optimized order is the default order.
+ */
+ @Override
+ public double walkInOptimizedOrder(final RealVectorPreservingVisitor visitor) {
+ return walkInDefaultOrder(visitor);
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * <p>In this implementation, the optimized order is the default order.
+ */
+ @Override
+ public double walkInOptimizedOrder(
+ final RealVectorPreservingVisitor visitor, final int start, final int end)
+ throws NumberIsTooSmallException, OutOfRangeException {
+ return walkInDefaultOrder(visitor, start, end);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public double walkInDefaultOrder(final RealVectorChangingVisitor visitor) {
+ visitor.start(data.length, 0, data.length - 1);
+ for (int i = 0; i < data.length; i++) {
+ data[i] = visitor.visit(i, data[i]);
+ }
+ return visitor.end();
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public double walkInDefaultOrder(
+ final RealVectorChangingVisitor visitor, final int start, final int end)
+ throws NumberIsTooSmallException, OutOfRangeException {
+ checkIndices(start, end);
+ visitor.start(data.length, start, end);
+ for (int i = start; i <= end; i++) {
+ data[i] = visitor.visit(i, data[i]);
+ }
+ return visitor.end();
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * <p>In this implementation, the optimized order is the default order.
+ */
+ @Override
+ public double walkInOptimizedOrder(final RealVectorChangingVisitor visitor) {
+ return walkInDefaultOrder(visitor);
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * <p>In this implementation, the optimized order is the default order.
+ */
+ @Override
+ public double walkInOptimizedOrder(
+ final RealVectorChangingVisitor visitor, final int start, final int end)
+ throws NumberIsTooSmallException, OutOfRangeException {
+ return walkInDefaultOrder(visitor, start, end);
+ }
+}
diff --git a/src/main/java/org/apache/commons/math3/linear/BiDiagonalTransformer.java b/src/main/java/org/apache/commons/math3/linear/BiDiagonalTransformer.java
new file mode 100644
index 0000000..063b202
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/linear/BiDiagonalTransformer.java
@@ -0,0 +1,388 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math3.linear;
+
+import org.apache.commons.math3.util.FastMath;
+
+/**
+ * Class transforming any matrix to bi-diagonal shape.
+ *
+ * <p>Any m &times; n matrix A can be written as the product of three matrices: A = U &times; B
+ * &times; V<sup>T</sup> with U an m &times; m orthogonal matrix, B an m &times; n bi-diagonal
+ * matrix (lower diagonal if m &lt; n, upper diagonal otherwise), and V an n &times; n orthogonal
+ * matrix.
+ *
+ * <p>Transformation to bi-diagonal shape is often not a goal by itself, but it is an intermediate
+ * step in more general decomposition algorithms like {@link SingularValueDecomposition Singular
+ * Value Decomposition}. This class is therefore intended for internal use by the library and is not
+ * public. As a consequence of this explicitly limited scope, many methods directly returns
+ * references to internal arrays, not copies.
+ *
+ * @since 2.0
+ */
+class BiDiagonalTransformer {
+
+ /** Householder vectors. */
+ private final double householderVectors[][];
+
+ /** Main diagonal. */
+ private final double[] main;
+
+ /** Secondary diagonal. */
+ private final double[] secondary;
+
+ /** Cached value of U. */
+ private RealMatrix cachedU;
+
+ /** Cached value of B. */
+ private RealMatrix cachedB;
+
+ /** Cached value of V. */
+ private RealMatrix cachedV;
+
+ /**
+ * Build the transformation to bi-diagonal shape of a matrix.
+ *
+ * @param matrix the matrix to transform.
+ */
+ BiDiagonalTransformer(RealMatrix matrix) {
+
+ final int m = matrix.getRowDimension();
+ final int n = matrix.getColumnDimension();
+ final int p = FastMath.min(m, n);
+ householderVectors = matrix.getData();
+ main = new double[p];
+ secondary = new double[p - 1];
+ cachedU = null;
+ cachedB = null;
+ cachedV = null;
+
+ // transform matrix
+ if (m >= n) {
+ transformToUpperBiDiagonal();
+ } else {
+ transformToLowerBiDiagonal();
+ }
+ }
+
+ /**
+ * Returns the matrix U of the transform.
+ *
+ * <p>U is an orthogonal matrix, i.e. its transpose is also its inverse.
+ *
+ * @return the U matrix
+ */
+ public RealMatrix getU() {
+
+ if (cachedU == null) {
+
+ final int m = householderVectors.length;
+ final int n = householderVectors[0].length;
+ final int p = main.length;
+ final int diagOffset = (m >= n) ? 0 : 1;
+ final double[] diagonal = (m >= n) ? main : secondary;
+ double[][] ua = new double[m][m];
+
+ // fill up the part of the matrix not affected by Householder transforms
+ for (int k = m - 1; k >= p; --k) {
+ ua[k][k] = 1;
+ }
+
+ // build up first part of the matrix by applying Householder transforms
+ for (int k = p - 1; k >= diagOffset; --k) {
+ final double[] hK = householderVectors[k];
+ ua[k][k] = 1;
+ if (hK[k - diagOffset] != 0.0) {
+ for (int j = k; j < m; ++j) {
+ double alpha = 0;
+ for (int i = k; i < m; ++i) {
+ alpha -= ua[i][j] * householderVectors[i][k - diagOffset];
+ }
+ alpha /= diagonal[k - diagOffset] * hK[k - diagOffset];
+
+ for (int i = k; i < m; ++i) {
+ ua[i][j] += -alpha * householderVectors[i][k - diagOffset];
+ }
+ }
+ }
+ }
+ if (diagOffset > 0) {
+ ua[0][0] = 1;
+ }
+ cachedU = MatrixUtils.createRealMatrix(ua);
+ }
+
+ // return the cached matrix
+ return cachedU;
+ }
+
+ /**
+ * Returns the bi-diagonal matrix B of the transform.
+ *
+ * @return the B matrix
+ */
+ public RealMatrix getB() {
+
+ if (cachedB == null) {
+
+ final int m = householderVectors.length;
+ final int n = householderVectors[0].length;
+ double[][] ba = new double[m][n];
+ for (int i = 0; i < main.length; ++i) {
+ ba[i][i] = main[i];
+ if (m < n) {
+ if (i > 0) {
+ ba[i][i - 1] = secondary[i - 1];
+ }
+ } else {
+ if (i < main.length - 1) {
+ ba[i][i + 1] = secondary[i];
+ }
+ }
+ }
+ cachedB = MatrixUtils.createRealMatrix(ba);
+ }
+
+ // return the cached matrix
+ return cachedB;
+ }
+
+ /**
+ * Returns the matrix V of the transform.
+ *
+ * <p>V is an orthogonal matrix, i.e. its transpose is also its inverse.
+ *
+ * @return the V matrix
+ */
+ public RealMatrix getV() {
+
+ if (cachedV == null) {
+
+ final int m = householderVectors.length;
+ final int n = householderVectors[0].length;
+ final int p = main.length;
+ final int diagOffset = (m >= n) ? 1 : 0;
+ final double[] diagonal = (m >= n) ? secondary : main;
+ double[][] va = new double[n][n];
+
+ // fill up the part of the matrix not affected by Householder transforms
+ for (int k = n - 1; k >= p; --k) {
+ va[k][k] = 1;
+ }
+
+ // build up first part of the matrix by applying Householder transforms
+ for (int k = p - 1; k >= diagOffset; --k) {
+ final double[] hK = householderVectors[k - diagOffset];
+ va[k][k] = 1;
+ if (hK[k] != 0.0) {
+ for (int j = k; j < n; ++j) {
+ double beta = 0;
+ for (int i = k; i < n; ++i) {
+ beta -= va[i][j] * hK[i];
+ }
+ beta /= diagonal[k - diagOffset] * hK[k];
+
+ for (int i = k; i < n; ++i) {
+ va[i][j] += -beta * hK[i];
+ }
+ }
+ }
+ }
+ if (diagOffset > 0) {
+ va[0][0] = 1;
+ }
+ cachedV = MatrixUtils.createRealMatrix(va);
+ }
+
+ // return the cached matrix
+ return cachedV;
+ }
+
+ /**
+ * Get the Householder vectors of the transform.
+ *
+ * <p>Note that since this class is only intended for internal use, it returns directly a
+ * reference to its internal arrays, not a copy.
+ *
+ * @return the main diagonal elements of the B matrix
+ */
+ double[][] getHouseholderVectorsRef() {
+ return householderVectors;
+ }
+
+ /**
+ * Get the main diagonal elements of the matrix B of the transform.
+ *
+ * <p>Note that since this class is only intended for internal use, it returns directly a
+ * reference to its internal arrays, not a copy.
+ *
+ * @return the main diagonal elements of the B matrix
+ */
+ double[] getMainDiagonalRef() {
+ return main;
+ }
+
+ /**
+ * Get the secondary diagonal elements of the matrix B of the transform.
+ *
+ * <p>Note that since this class is only intended for internal use, it returns directly a
+ * reference to its internal arrays, not a copy.
+ *
+ * @return the secondary diagonal elements of the B matrix
+ */
+ double[] getSecondaryDiagonalRef() {
+ return secondary;
+ }
+
+ /**
+ * Check if the matrix is transformed to upper bi-diagonal.
+ *
+ * @return true if the matrix is transformed to upper bi-diagonal
+ */
+ boolean isUpperBiDiagonal() {
+ return householderVectors.length >= householderVectors[0].length;
+ }
+
+ /**
+ * Transform original matrix to upper bi-diagonal form.
+ *
+ * <p>Transformation is done using alternate Householder transforms on columns and rows.
+ */
+ private void transformToUpperBiDiagonal() {
+
+ final int m = householderVectors.length;
+ final int n = householderVectors[0].length;
+ for (int k = 0; k < n; k++) {
+
+ // zero-out a column
+ double xNormSqr = 0;
+ for (int i = k; i < m; ++i) {
+ final double c = householderVectors[i][k];
+ xNormSqr += c * c;
+ }
+ final double[] hK = householderVectors[k];
+ final double a = (hK[k] > 0) ? -FastMath.sqrt(xNormSqr) : FastMath.sqrt(xNormSqr);
+ main[k] = a;
+ if (a != 0.0) {
+ hK[k] -= a;
+ for (int j = k + 1; j < n; ++j) {
+ double alpha = 0;
+ for (int i = k; i < m; ++i) {
+ final double[] hI = householderVectors[i];
+ alpha -= hI[j] * hI[k];
+ }
+ alpha /= a * householderVectors[k][k];
+ for (int i = k; i < m; ++i) {
+ final double[] hI = householderVectors[i];
+ hI[j] -= alpha * hI[k];
+ }
+ }
+ }
+
+ if (k < n - 1) {
+ // zero-out a row
+ xNormSqr = 0;
+ for (int j = k + 1; j < n; ++j) {
+ final double c = hK[j];
+ xNormSqr += c * c;
+ }
+ final double b =
+ (hK[k + 1] > 0) ? -FastMath.sqrt(xNormSqr) : FastMath.sqrt(xNormSqr);
+ secondary[k] = b;
+ if (b != 0.0) {
+ hK[k + 1] -= b;
+ for (int i = k + 1; i < m; ++i) {
+ final double[] hI = householderVectors[i];
+ double beta = 0;
+ for (int j = k + 1; j < n; ++j) {
+ beta -= hI[j] * hK[j];
+ }
+ beta /= b * hK[k + 1];
+ for (int j = k + 1; j < n; ++j) {
+ hI[j] -= beta * hK[j];
+ }
+ }
+ }
+ }
+ }
+ }
+
+ /**
+ * Transform original matrix to lower bi-diagonal form.
+ *
+ * <p>Transformation is done using alternate Householder transforms on rows and columns.
+ */
+ private void transformToLowerBiDiagonal() {
+
+ final int m = householderVectors.length;
+ final int n = householderVectors[0].length;
+ for (int k = 0; k < m; k++) {
+
+ // zero-out a row
+ final double[] hK = householderVectors[k];
+ double xNormSqr = 0;
+ for (int j = k; j < n; ++j) {
+ final double c = hK[j];
+ xNormSqr += c * c;
+ }
+ final double a = (hK[k] > 0) ? -FastMath.sqrt(xNormSqr) : FastMath.sqrt(xNormSqr);
+ main[k] = a;
+ if (a != 0.0) {
+ hK[k] -= a;
+ for (int i = k + 1; i < m; ++i) {
+ final double[] hI = householderVectors[i];
+ double alpha = 0;
+ for (int j = k; j < n; ++j) {
+ alpha -= hI[j] * hK[j];
+ }
+ alpha /= a * householderVectors[k][k];
+ for (int j = k; j < n; ++j) {
+ hI[j] -= alpha * hK[j];
+ }
+ }
+ }
+
+ if (k < m - 1) {
+ // zero-out a column
+ final double[] hKp1 = householderVectors[k + 1];
+ xNormSqr = 0;
+ for (int i = k + 1; i < m; ++i) {
+ final double c = householderVectors[i][k];
+ xNormSqr += c * c;
+ }
+ final double b = (hKp1[k] > 0) ? -FastMath.sqrt(xNormSqr) : FastMath.sqrt(xNormSqr);
+ secondary[k] = b;
+ if (b != 0.0) {
+ hKp1[k] -= b;
+ for (int j = k + 1; j < n; ++j) {
+ double beta = 0;
+ for (int i = k + 1; i < m; ++i) {
+ final double[] hI = householderVectors[i];
+ beta -= hI[j] * hI[k];
+ }
+ beta /= b * hKp1[k];
+ for (int i = k + 1; i < m; ++i) {
+ final double[] hI = householderVectors[i];
+ hI[j] -= beta * hI[k];
+ }
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/src/main/java/org/apache/commons/math3/linear/BlockFieldMatrix.java b/src/main/java/org/apache/commons/math3/linear/BlockFieldMatrix.java
new file mode 100644
index 0000000..419a25f
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/linear/BlockFieldMatrix.java
@@ -0,0 +1,1664 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math3.linear;
+
+import org.apache.commons.math3.Field;
+import org.apache.commons.math3.FieldElement;
+import org.apache.commons.math3.exception.DimensionMismatchException;
+import org.apache.commons.math3.exception.NoDataException;
+import org.apache.commons.math3.exception.NotStrictlyPositiveException;
+import org.apache.commons.math3.exception.NullArgumentException;
+import org.apache.commons.math3.exception.NumberIsTooSmallException;
+import org.apache.commons.math3.exception.OutOfRangeException;
+import org.apache.commons.math3.exception.util.LocalizedFormats;
+import org.apache.commons.math3.util.FastMath;
+import org.apache.commons.math3.util.MathArrays;
+import org.apache.commons.math3.util.MathUtils;
+
+import java.io.Serializable;
+
+/**
+ * Cache-friendly implementation of FieldMatrix using a flat arrays to store square blocks of the
+ * matrix.
+ *
+ * <p>This implementation is specially designed to be cache-friendly. Square blocks are stored as
+ * small arrays and allow efficient traversal of data both in row major direction and columns major
+ * direction, one block at a time. This greatly increases performances for algorithms that use
+ * crossed directions loops like multiplication or transposition.
+ *
+ * <p>The size of square blocks is a static parameter. It may be tuned according to the cache size
+ * of the target computer processor. As a rule of thumbs, it should be the largest value that allows
+ * three blocks to be simultaneously cached (this is necessary for example for matrix
+ * multiplication). The default value is to use 36x36 blocks.
+ *
+ * <p>The regular blocks represent {@link #BLOCK_SIZE} x {@link #BLOCK_SIZE} squares. Blocks at
+ * right hand side and bottom side which may be smaller to fit matrix dimensions. The square blocks
+ * are flattened in row major order in single dimension arrays which are therefore {@link
+ * #BLOCK_SIZE}<sup>2</sup> elements long for regular blocks. The blocks are themselves organized in
+ * row major order.
+ *
+ * <p>As an example, for a block size of 36x36, a 100x60 matrix would be stored in 6 blocks. Block 0
+ * would be a Field[1296] array holding the upper left 36x36 square, block 1 would be a Field[1296]
+ * array holding the upper center 36x36 square, block 2 would be a Field[1008] array holding the
+ * upper right 36x28 rectangle, block 3 would be a Field[864] array holding the lower left 24x36
+ * rectangle, block 4 would be a Field[864] array holding the lower center 24x36 rectangle and block
+ * 5 would be a Field[672] array holding the lower right 24x28 rectangle.
+ *
+ * <p>The layout complexity overhead versus simple mapping of matrices to java arrays is negligible
+ * for small matrices (about 1%). The gain from cache efficiency leads to up to 3-fold improvements
+ * for matrices of moderate to large size.
+ *
+ * @param <T> the type of the field elements
+ * @since 2.0
+ */
+public class BlockFieldMatrix<T extends FieldElement<T>> extends AbstractFieldMatrix<T>
+ implements Serializable {
+ /** Block size. */
+ public static final int BLOCK_SIZE = 36;
+
+ /** Serializable version identifier. */
+ private static final long serialVersionUID = -4602336630143123183L;
+
+ /** Blocks of matrix entries. */
+ private final T blocks[][];
+
+ /** Number of rows of the matrix. */
+ private final int rows;
+
+ /** Number of columns of the matrix. */
+ private final int columns;
+
+ /** Number of block rows of the matrix. */
+ private final int blockRows;
+
+ /** Number of block columns of the matrix. */
+ private final int blockColumns;
+
+ /**
+ * Create a new matrix with the supplied row and column dimensions.
+ *
+ * @param field Field to which the elements belong.
+ * @param rows Number of rows in the new matrix.
+ * @param columns Number of columns in the new matrix.
+ * @throws NotStrictlyPositiveException if row or column dimension is not positive.
+ */
+ public BlockFieldMatrix(final Field<T> field, final int rows, final int columns)
+ throws NotStrictlyPositiveException {
+ super(field, rows, columns);
+ this.rows = rows;
+ this.columns = columns;
+
+ // number of blocks
+ blockRows = (rows + BLOCK_SIZE - 1) / BLOCK_SIZE;
+ blockColumns = (columns + BLOCK_SIZE - 1) / BLOCK_SIZE;
+
+ // allocate storage blocks, taking care of smaller ones at right and bottom
+ blocks = createBlocksLayout(field, rows, columns);
+ }
+
+ /**
+ * Create a new dense matrix copying entries from raw layout data.
+ *
+ * <p>The input array <em>must</em> already be in raw layout.
+ *
+ * <p>Calling this constructor is equivalent to call:
+ *
+ * <pre>matrix = new BlockFieldMatrix<T>(getField(), rawData.length, rawData[0].length,
+ * toBlocksLayout(rawData), false);</pre>
+ *
+ * @param rawData Data for the new matrix, in raw layout.
+ * @throws DimensionMismatchException if the {@code blockData} shape is inconsistent with block
+ * layout.
+ * @see #BlockFieldMatrix(int, int, FieldElement[][], boolean)
+ */
+ public BlockFieldMatrix(final T[][] rawData) throws DimensionMismatchException {
+ this(rawData.length, rawData[0].length, toBlocksLayout(rawData), false);
+ }
+
+ /**
+ * Create a new dense matrix copying entries from block layout data.
+ *
+ * <p>The input array <em>must</em> already be in blocks layout.
+ *
+ * @param rows the number of rows in the new matrix
+ * @param columns the number of columns in the new matrix
+ * @param blockData data for new matrix
+ * @param copyArray if true, the input array will be copied, otherwise it will be referenced
+ * @throws DimensionMismatchException if the {@code blockData} shape is inconsistent with block
+ * layout.
+ * @throws NotStrictlyPositiveException if row or column dimension is not positive.
+ * @see #createBlocksLayout(Field, int, int)
+ * @see #toBlocksLayout(FieldElement[][])
+ * @see #BlockFieldMatrix(FieldElement[][])
+ */
+ public BlockFieldMatrix(
+ final int rows, final int columns, final T[][] blockData, final boolean copyArray)
+ throws DimensionMismatchException, NotStrictlyPositiveException {
+ super(extractField(blockData), rows, columns);
+ this.rows = rows;
+ this.columns = columns;
+
+ // number of blocks
+ blockRows = (rows + BLOCK_SIZE - 1) / BLOCK_SIZE;
+ blockColumns = (columns + BLOCK_SIZE - 1) / BLOCK_SIZE;
+
+ if (copyArray) {
+ // allocate storage blocks, taking care of smaller ones at right and bottom
+ blocks = MathArrays.buildArray(getField(), blockRows * blockColumns, -1);
+ } else {
+ // reference existing array
+ blocks = blockData;
+ }
+
+ int index = 0;
+ for (int iBlock = 0; iBlock < blockRows; ++iBlock) {
+ final int iHeight = blockHeight(iBlock);
+ for (int jBlock = 0; jBlock < blockColumns; ++jBlock, ++index) {
+ if (blockData[index].length != iHeight * blockWidth(jBlock)) {
+ throw new DimensionMismatchException(
+ blockData[index].length, iHeight * blockWidth(jBlock));
+ }
+ if (copyArray) {
+ blocks[index] = blockData[index].clone();
+ }
+ }
+ }
+ }
+
+ /**
+ * Convert a data array from raw layout to blocks layout.
+ *
+ * <p>Raw layout is the straightforward layout where element at row i and column j is in array
+ * element <code>rawData[i][j]</code>. Blocks layout is the layout used in {@link
+ * BlockFieldMatrix} instances, where the matrix is split in square blocks (except at right and
+ * bottom side where blocks may be rectangular to fit matrix size) and each block is stored in a
+ * flattened one-dimensional array.
+ *
+ * <p>This method creates an array in blocks layout from an input array in raw layout. It can be
+ * used to provide the array argument of the {@link #BlockFieldMatrix(int, int,
+ * FieldElement[][], boolean)} constructor.
+ *
+ * @param <T> Type of the field elements.
+ * @param rawData Data array in raw layout.
+ * @return a new data array containing the same entries but in blocks layout
+ * @throws DimensionMismatchException if {@code rawData} is not rectangular (not all rows have
+ * the same length).
+ * @see #createBlocksLayout(Field, int, int)
+ * @see #BlockFieldMatrix(int, int, FieldElement[][], boolean)
+ */
+ public static <T extends FieldElement<T>> T[][] toBlocksLayout(final T[][] rawData)
+ throws DimensionMismatchException {
+
+ final int rows = rawData.length;
+ final int columns = rawData[0].length;
+ final int blockRows = (rows + BLOCK_SIZE - 1) / BLOCK_SIZE;
+ final int blockColumns = (columns + BLOCK_SIZE - 1) / BLOCK_SIZE;
+
+ // safety checks
+ for (int i = 0; i < rawData.length; ++i) {
+ final int length = rawData[i].length;
+ if (length != columns) {
+ throw new DimensionMismatchException(columns, length);
+ }
+ }
+
+ // convert array
+ final Field<T> field = extractField(rawData);
+ final T[][] blocks = MathArrays.buildArray(field, blockRows * blockColumns, -1);
+ int blockIndex = 0;
+ for (int iBlock = 0; iBlock < blockRows; ++iBlock) {
+ final int pStart = iBlock * BLOCK_SIZE;
+ final int pEnd = FastMath.min(pStart + BLOCK_SIZE, rows);
+ final int iHeight = pEnd - pStart;
+ for (int jBlock = 0; jBlock < blockColumns; ++jBlock) {
+ final int qStart = jBlock * BLOCK_SIZE;
+ final int qEnd = FastMath.min(qStart + BLOCK_SIZE, columns);
+ final int jWidth = qEnd - qStart;
+
+ // allocate new block
+ final T[] block = MathArrays.buildArray(field, iHeight * jWidth);
+ blocks[blockIndex] = block;
+
+ // copy data
+ int index = 0;
+ for (int p = pStart; p < pEnd; ++p) {
+ System.arraycopy(rawData[p], qStart, block, index, jWidth);
+ index += jWidth;
+ }
+
+ ++blockIndex;
+ }
+ }
+
+ return blocks;
+ }
+
+ /**
+ * Create a data array in blocks layout.
+ *
+ * <p>This method can be used to create the array argument of the {@link #BlockFieldMatrix(int,
+ * int, FieldElement[][], boolean)} constructor.
+ *
+ * @param <T> Type of the field elements.
+ * @param field Field to which the elements belong.
+ * @param rows Number of rows in the new matrix.
+ * @param columns Number of columns in the new matrix.
+ * @return a new data array in blocks layout.
+ * @see #toBlocksLayout(FieldElement[][])
+ * @see #BlockFieldMatrix(int, int, FieldElement[][], boolean)
+ */
+ public static <T extends FieldElement<T>> T[][] createBlocksLayout(
+ final Field<T> field, final int rows, final int columns) {
+ final int blockRows = (rows + BLOCK_SIZE - 1) / BLOCK_SIZE;
+ final int blockColumns = (columns + BLOCK_SIZE - 1) / BLOCK_SIZE;
+
+ final T[][] blocks = MathArrays.buildArray(field, blockRows * blockColumns, -1);
+ int blockIndex = 0;
+ for (int iBlock = 0; iBlock < blockRows; ++iBlock) {
+ final int pStart = iBlock * BLOCK_SIZE;
+ final int pEnd = FastMath.min(pStart + BLOCK_SIZE, rows);
+ final int iHeight = pEnd - pStart;
+ for (int jBlock = 0; jBlock < blockColumns; ++jBlock) {
+ final int qStart = jBlock * BLOCK_SIZE;
+ final int qEnd = FastMath.min(qStart + BLOCK_SIZE, columns);
+ final int jWidth = qEnd - qStart;
+ blocks[blockIndex] = MathArrays.buildArray(field, iHeight * jWidth);
+ ++blockIndex;
+ }
+ }
+
+ return blocks;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public FieldMatrix<T> createMatrix(final int rowDimension, final int columnDimension)
+ throws NotStrictlyPositiveException {
+ return new BlockFieldMatrix<T>(getField(), rowDimension, columnDimension);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public FieldMatrix<T> copy() {
+
+ // create an empty matrix
+ BlockFieldMatrix<T> copied = new BlockFieldMatrix<T>(getField(), rows, columns);
+
+ // copy the blocks
+ for (int i = 0; i < blocks.length; ++i) {
+ System.arraycopy(blocks[i], 0, copied.blocks[i], 0, blocks[i].length);
+ }
+
+ return copied;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public FieldMatrix<T> add(final FieldMatrix<T> m) throws MatrixDimensionMismatchException {
+ try {
+ return add((BlockFieldMatrix<T>) m);
+ } catch (ClassCastException cce) {
+
+ // safety check
+ checkAdditionCompatible(m);
+
+ final BlockFieldMatrix<T> out = new BlockFieldMatrix<T>(getField(), rows, columns);
+
+ // perform addition block-wise, to ensure good cache behavior
+ int blockIndex = 0;
+ for (int iBlock = 0; iBlock < out.blockRows; ++iBlock) {
+ for (int jBlock = 0; jBlock < out.blockColumns; ++jBlock) {
+
+ // perform addition on the current block
+ final T[] outBlock = out.blocks[blockIndex];
+ final T[] tBlock = blocks[blockIndex];
+ final int pStart = iBlock * BLOCK_SIZE;
+ final int pEnd = FastMath.min(pStart + BLOCK_SIZE, rows);
+ final int qStart = jBlock * BLOCK_SIZE;
+ final int qEnd = FastMath.min(qStart + BLOCK_SIZE, columns);
+ int k = 0;
+ for (int p = pStart; p < pEnd; ++p) {
+ for (int q = qStart; q < qEnd; ++q) {
+ outBlock[k] = tBlock[k].add(m.getEntry(p, q));
+ ++k;
+ }
+ }
+
+ // go to next block
+ ++blockIndex;
+ }
+ }
+
+ return out;
+ }
+ }
+
+ /**
+ * Compute the sum of {@code this} and {@code m}.
+ *
+ * @param m matrix to be added
+ * @return {@code this + m}
+ * @throws MatrixDimensionMismatchException if {@code m} is not the same size as {@code this}
+ */
+ public BlockFieldMatrix<T> add(final BlockFieldMatrix<T> m)
+ throws MatrixDimensionMismatchException {
+
+ // safety check
+ checkAdditionCompatible(m);
+
+ final BlockFieldMatrix<T> out = new BlockFieldMatrix<T>(getField(), rows, columns);
+
+ // perform addition block-wise, to ensure good cache behavior
+ for (int blockIndex = 0; blockIndex < out.blocks.length; ++blockIndex) {
+ final T[] outBlock = out.blocks[blockIndex];
+ final T[] tBlock = blocks[blockIndex];
+ final T[] mBlock = m.blocks[blockIndex];
+ for (int k = 0; k < outBlock.length; ++k) {
+ outBlock[k] = tBlock[k].add(mBlock[k]);
+ }
+ }
+
+ return out;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public FieldMatrix<T> subtract(final FieldMatrix<T> m) throws MatrixDimensionMismatchException {
+ try {
+ return subtract((BlockFieldMatrix<T>) m);
+ } catch (ClassCastException cce) {
+
+ // safety check
+ checkSubtractionCompatible(m);
+
+ final BlockFieldMatrix<T> out = new BlockFieldMatrix<T>(getField(), rows, columns);
+
+ // perform subtraction block-wise, to ensure good cache behavior
+ int blockIndex = 0;
+ for (int iBlock = 0; iBlock < out.blockRows; ++iBlock) {
+ for (int jBlock = 0; jBlock < out.blockColumns; ++jBlock) {
+
+ // perform subtraction on the current block
+ final T[] outBlock = out.blocks[blockIndex];
+ final T[] tBlock = blocks[blockIndex];
+ final int pStart = iBlock * BLOCK_SIZE;
+ final int pEnd = FastMath.min(pStart + BLOCK_SIZE, rows);
+ final int qStart = jBlock * BLOCK_SIZE;
+ final int qEnd = FastMath.min(qStart + BLOCK_SIZE, columns);
+ int k = 0;
+ for (int p = pStart; p < pEnd; ++p) {
+ for (int q = qStart; q < qEnd; ++q) {
+ outBlock[k] = tBlock[k].subtract(m.getEntry(p, q));
+ ++k;
+ }
+ }
+
+ // go to next block
+ ++blockIndex;
+ }
+ }
+
+ return out;
+ }
+ }
+
+ /**
+ * Compute {@code this - m}.
+ *
+ * @param m matrix to be subtracted
+ * @return {@code this - m}
+ * @throws MatrixDimensionMismatchException if {@code m} is not the same size as {@code this}
+ */
+ public BlockFieldMatrix<T> subtract(final BlockFieldMatrix<T> m)
+ throws MatrixDimensionMismatchException {
+ // safety check
+ checkSubtractionCompatible(m);
+
+ final BlockFieldMatrix<T> out = new BlockFieldMatrix<T>(getField(), rows, columns);
+
+ // perform subtraction block-wise, to ensure good cache behavior
+ for (int blockIndex = 0; blockIndex < out.blocks.length; ++blockIndex) {
+ final T[] outBlock = out.blocks[blockIndex];
+ final T[] tBlock = blocks[blockIndex];
+ final T[] mBlock = m.blocks[blockIndex];
+ for (int k = 0; k < outBlock.length; ++k) {
+ outBlock[k] = tBlock[k].subtract(mBlock[k]);
+ }
+ }
+
+ return out;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public FieldMatrix<T> scalarAdd(final T d) {
+ final BlockFieldMatrix<T> out = new BlockFieldMatrix<T>(getField(), rows, columns);
+
+ // perform subtraction block-wise, to ensure good cache behavior
+ for (int blockIndex = 0; blockIndex < out.blocks.length; ++blockIndex) {
+ final T[] outBlock = out.blocks[blockIndex];
+ final T[] tBlock = blocks[blockIndex];
+ for (int k = 0; k < outBlock.length; ++k) {
+ outBlock[k] = tBlock[k].add(d);
+ }
+ }
+
+ return out;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public FieldMatrix<T> scalarMultiply(final T d) {
+
+ final BlockFieldMatrix<T> out = new BlockFieldMatrix<T>(getField(), rows, columns);
+
+ // perform subtraction block-wise, to ensure good cache behavior
+ for (int blockIndex = 0; blockIndex < out.blocks.length; ++blockIndex) {
+ final T[] outBlock = out.blocks[blockIndex];
+ final T[] tBlock = blocks[blockIndex];
+ for (int k = 0; k < outBlock.length; ++k) {
+ outBlock[k] = tBlock[k].multiply(d);
+ }
+ }
+
+ return out;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public FieldMatrix<T> multiply(final FieldMatrix<T> m) throws DimensionMismatchException {
+ try {
+ return multiply((BlockFieldMatrix<T>) m);
+ } catch (ClassCastException cce) {
+
+ // safety check
+ checkMultiplicationCompatible(m);
+
+ final BlockFieldMatrix<T> out =
+ new BlockFieldMatrix<T>(getField(), rows, m.getColumnDimension());
+ final T zero = getField().getZero();
+
+ // perform multiplication block-wise, to ensure good cache behavior
+ int blockIndex = 0;
+ for (int iBlock = 0; iBlock < out.blockRows; ++iBlock) {
+
+ final int pStart = iBlock * BLOCK_SIZE;
+ final int pEnd = FastMath.min(pStart + BLOCK_SIZE, rows);
+
+ for (int jBlock = 0; jBlock < out.blockColumns; ++jBlock) {
+
+ final int qStart = jBlock * BLOCK_SIZE;
+ final int qEnd = FastMath.min(qStart + BLOCK_SIZE, m.getColumnDimension());
+
+ // select current block
+ final T[] outBlock = out.blocks[blockIndex];
+
+ // perform multiplication on current block
+ for (int kBlock = 0; kBlock < blockColumns; ++kBlock) {
+ final int kWidth = blockWidth(kBlock);
+ final T[] tBlock = blocks[iBlock * blockColumns + kBlock];
+ final int rStart = kBlock * BLOCK_SIZE;
+ int k = 0;
+ for (int p = pStart; p < pEnd; ++p) {
+ final int lStart = (p - pStart) * kWidth;
+ final int lEnd = lStart + kWidth;
+ for (int q = qStart; q < qEnd; ++q) {
+ T sum = zero;
+ int r = rStart;
+ for (int l = lStart; l < lEnd; ++l) {
+ sum = sum.add(tBlock[l].multiply(m.getEntry(r, q)));
+ ++r;
+ }
+ outBlock[k] = outBlock[k].add(sum);
+ ++k;
+ }
+ }
+ }
+
+ // go to next block
+ ++blockIndex;
+ }
+ }
+
+ return out;
+ }
+ }
+
+ /**
+ * Returns the result of postmultiplying {@code this} by {@code m}.
+ *
+ * @param m matrix to postmultiply by
+ * @return {@code this * m}
+ * @throws DimensionMismatchException if the matrices are not compatible.
+ */
+ public BlockFieldMatrix<T> multiply(BlockFieldMatrix<T> m) throws DimensionMismatchException {
+
+ // safety check
+ checkMultiplicationCompatible(m);
+
+ final BlockFieldMatrix<T> out = new BlockFieldMatrix<T>(getField(), rows, m.columns);
+ final T zero = getField().getZero();
+
+ // perform multiplication block-wise, to ensure good cache behavior
+ int blockIndex = 0;
+ for (int iBlock = 0; iBlock < out.blockRows; ++iBlock) {
+
+ final int pStart = iBlock * BLOCK_SIZE;
+ final int pEnd = FastMath.min(pStart + BLOCK_SIZE, rows);
+
+ for (int jBlock = 0; jBlock < out.blockColumns; ++jBlock) {
+ final int jWidth = out.blockWidth(jBlock);
+ final int jWidth2 = jWidth + jWidth;
+ final int jWidth3 = jWidth2 + jWidth;
+ final int jWidth4 = jWidth3 + jWidth;
+
+ // select current block
+ final T[] outBlock = out.blocks[blockIndex];
+
+ // perform multiplication on current block
+ for (int kBlock = 0; kBlock < blockColumns; ++kBlock) {
+ final int kWidth = blockWidth(kBlock);
+ final T[] tBlock = blocks[iBlock * blockColumns + kBlock];
+ final T[] mBlock = m.blocks[kBlock * m.blockColumns + jBlock];
+ int k = 0;
+ for (int p = pStart; p < pEnd; ++p) {
+ final int lStart = (p - pStart) * kWidth;
+ final int lEnd = lStart + kWidth;
+ for (int nStart = 0; nStart < jWidth; ++nStart) {
+ T sum = zero;
+ int l = lStart;
+ int n = nStart;
+ while (l < lEnd - 3) {
+ sum =
+ sum.add(tBlock[l].multiply(mBlock[n]))
+ .add(tBlock[l + 1].multiply(mBlock[n + jWidth]))
+ .add(tBlock[l + 2].multiply(mBlock[n + jWidth2]))
+ .add(tBlock[l + 3].multiply(mBlock[n + jWidth3]));
+ l += 4;
+ n += jWidth4;
+ }
+ while (l < lEnd) {
+ sum = sum.add(tBlock[l++].multiply(mBlock[n]));
+ n += jWidth;
+ }
+ outBlock[k] = outBlock[k].add(sum);
+ ++k;
+ }
+ }
+ }
+
+ // go to next block
+ ++blockIndex;
+ }
+ }
+
+ return out;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public T[][] getData() {
+
+ final T[][] data =
+ MathArrays.buildArray(getField(), getRowDimension(), getColumnDimension());
+ final int lastColumns = columns - (blockColumns - 1) * BLOCK_SIZE;
+
+ for (int iBlock = 0; iBlock < blockRows; ++iBlock) {
+ final int pStart = iBlock * BLOCK_SIZE;
+ final int pEnd = FastMath.min(pStart + BLOCK_SIZE, rows);
+ int regularPos = 0;
+ int lastPos = 0;
+ for (int p = pStart; p < pEnd; ++p) {
+ final T[] dataP = data[p];
+ int blockIndex = iBlock * blockColumns;
+ int dataPos = 0;
+ for (int jBlock = 0; jBlock < blockColumns - 1; ++jBlock) {
+ System.arraycopy(blocks[blockIndex++], regularPos, dataP, dataPos, BLOCK_SIZE);
+ dataPos += BLOCK_SIZE;
+ }
+ System.arraycopy(blocks[blockIndex], lastPos, dataP, dataPos, lastColumns);
+ regularPos += BLOCK_SIZE;
+ lastPos += lastColumns;
+ }
+ }
+
+ return data;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public FieldMatrix<T> getSubMatrix(
+ final int startRow, final int endRow, final int startColumn, final int endColumn)
+ throws OutOfRangeException, NumberIsTooSmallException {
+ // safety checks
+ checkSubMatrixIndex(startRow, endRow, startColumn, endColumn);
+
+ // create the output matrix
+ final BlockFieldMatrix<T> out =
+ new BlockFieldMatrix<T>(
+ getField(), endRow - startRow + 1, endColumn - startColumn + 1);
+
+ // compute blocks shifts
+ final int blockStartRow = startRow / BLOCK_SIZE;
+ final int rowsShift = startRow % BLOCK_SIZE;
+ final int blockStartColumn = startColumn / BLOCK_SIZE;
+ final int columnsShift = startColumn % BLOCK_SIZE;
+
+ // perform extraction block-wise, to ensure good cache behavior
+ int pBlock = blockStartRow;
+ for (int iBlock = 0; iBlock < out.blockRows; ++iBlock) {
+ final int iHeight = out.blockHeight(iBlock);
+ int qBlock = blockStartColumn;
+ for (int jBlock = 0; jBlock < out.blockColumns; ++jBlock) {
+ final int jWidth = out.blockWidth(jBlock);
+
+ // handle one block of the output matrix
+ final int outIndex = iBlock * out.blockColumns + jBlock;
+ final T[] outBlock = out.blocks[outIndex];
+ final int index = pBlock * blockColumns + qBlock;
+ final int width = blockWidth(qBlock);
+
+ final int heightExcess = iHeight + rowsShift - BLOCK_SIZE;
+ final int widthExcess = jWidth + columnsShift - BLOCK_SIZE;
+ if (heightExcess > 0) {
+ // the submatrix block spans on two blocks rows from the original matrix
+ if (widthExcess > 0) {
+ // the submatrix block spans on two blocks columns from the original matrix
+ final int width2 = blockWidth(qBlock + 1);
+ copyBlockPart(
+ blocks[index],
+ width,
+ rowsShift,
+ BLOCK_SIZE,
+ columnsShift,
+ BLOCK_SIZE,
+ outBlock,
+ jWidth,
+ 0,
+ 0);
+ copyBlockPart(
+ blocks[index + 1],
+ width2,
+ rowsShift,
+ BLOCK_SIZE,
+ 0,
+ widthExcess,
+ outBlock,
+ jWidth,
+ 0,
+ jWidth - widthExcess);
+ copyBlockPart(
+ blocks[index + blockColumns],
+ width,
+ 0,
+ heightExcess,
+ columnsShift,
+ BLOCK_SIZE,
+ outBlock,
+ jWidth,
+ iHeight - heightExcess,
+ 0);
+ copyBlockPart(
+ blocks[index + blockColumns + 1],
+ width2,
+ 0,
+ heightExcess,
+ 0,
+ widthExcess,
+ outBlock,
+ jWidth,
+ iHeight - heightExcess,
+ jWidth - widthExcess);
+ } else {
+ // the submatrix block spans on one block column from the original matrix
+ copyBlockPart(
+ blocks[index],
+ width,
+ rowsShift,
+ BLOCK_SIZE,
+ columnsShift,
+ jWidth + columnsShift,
+ outBlock,
+ jWidth,
+ 0,
+ 0);
+ copyBlockPart(
+ blocks[index + blockColumns],
+ width,
+ 0,
+ heightExcess,
+ columnsShift,
+ jWidth + columnsShift,
+ outBlock,
+ jWidth,
+ iHeight - heightExcess,
+ 0);
+ }
+ } else {
+ // the submatrix block spans on one block row from the original matrix
+ if (widthExcess > 0) {
+ // the submatrix block spans on two blocks columns from the original matrix
+ final int width2 = blockWidth(qBlock + 1);
+ copyBlockPart(
+ blocks[index],
+ width,
+ rowsShift,
+ iHeight + rowsShift,
+ columnsShift,
+ BLOCK_SIZE,
+ outBlock,
+ jWidth,
+ 0,
+ 0);
+ copyBlockPart(
+ blocks[index + 1],
+ width2,
+ rowsShift,
+ iHeight + rowsShift,
+ 0,
+ widthExcess,
+ outBlock,
+ jWidth,
+ 0,
+ jWidth - widthExcess);
+ } else {
+ // the submatrix block spans on one block column from the original matrix
+ copyBlockPart(
+ blocks[index],
+ width,
+ rowsShift,
+ iHeight + rowsShift,
+ columnsShift,
+ jWidth + columnsShift,
+ outBlock,
+ jWidth,
+ 0,
+ 0);
+ }
+ }
+ ++qBlock;
+ }
+ ++pBlock;
+ }
+
+ return out;
+ }
+
+ /**
+ * Copy a part of a block into another one
+ *
+ * <p>This method can be called only when the specified part fits in both blocks, no
+ * verification is done here.
+ *
+ * @param srcBlock source block
+ * @param srcWidth source block width ({@link #BLOCK_SIZE} or smaller)
+ * @param srcStartRow start row in the source block
+ * @param srcEndRow end row (exclusive) in the source block
+ * @param srcStartColumn start column in the source block
+ * @param srcEndColumn end column (exclusive) in the source block
+ * @param dstBlock destination block
+ * @param dstWidth destination block width ({@link #BLOCK_SIZE} or smaller)
+ * @param dstStartRow start row in the destination block
+ * @param dstStartColumn start column in the destination block
+ */
+ private void copyBlockPart(
+ final T[] srcBlock,
+ final int srcWidth,
+ final int srcStartRow,
+ final int srcEndRow,
+ final int srcStartColumn,
+ final int srcEndColumn,
+ final T[] dstBlock,
+ final int dstWidth,
+ final int dstStartRow,
+ final int dstStartColumn) {
+ final int length = srcEndColumn - srcStartColumn;
+ int srcPos = srcStartRow * srcWidth + srcStartColumn;
+ int dstPos = dstStartRow * dstWidth + dstStartColumn;
+ for (int srcRow = srcStartRow; srcRow < srcEndRow; ++srcRow) {
+ System.arraycopy(srcBlock, srcPos, dstBlock, dstPos, length);
+ srcPos += srcWidth;
+ dstPos += dstWidth;
+ }
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void setSubMatrix(final T[][] subMatrix, final int row, final int column)
+ throws DimensionMismatchException,
+ OutOfRangeException,
+ NoDataException,
+ NullArgumentException {
+ // safety checks
+ MathUtils.checkNotNull(subMatrix);
+ final int refLength = subMatrix[0].length;
+ if (refLength == 0) {
+ throw new NoDataException(LocalizedFormats.AT_LEAST_ONE_COLUMN);
+ }
+ final int endRow = row + subMatrix.length - 1;
+ final int endColumn = column + refLength - 1;
+ checkSubMatrixIndex(row, endRow, column, endColumn);
+ for (final T[] subRow : subMatrix) {
+ if (subRow.length != refLength) {
+ throw new DimensionMismatchException(refLength, subRow.length);
+ }
+ }
+
+ // compute blocks bounds
+ final int blockStartRow = row / BLOCK_SIZE;
+ final int blockEndRow = (endRow + BLOCK_SIZE) / BLOCK_SIZE;
+ final int blockStartColumn = column / BLOCK_SIZE;
+ final int blockEndColumn = (endColumn + BLOCK_SIZE) / BLOCK_SIZE;
+
+ // perform copy block-wise, to ensure good cache behavior
+ for (int iBlock = blockStartRow; iBlock < blockEndRow; ++iBlock) {
+ final int iHeight = blockHeight(iBlock);
+ final int firstRow = iBlock * BLOCK_SIZE;
+ final int iStart = FastMath.max(row, firstRow);
+ final int iEnd = FastMath.min(endRow + 1, firstRow + iHeight);
+
+ for (int jBlock = blockStartColumn; jBlock < blockEndColumn; ++jBlock) {
+ final int jWidth = blockWidth(jBlock);
+ final int firstColumn = jBlock * BLOCK_SIZE;
+ final int jStart = FastMath.max(column, firstColumn);
+ final int jEnd = FastMath.min(endColumn + 1, firstColumn + jWidth);
+ final int jLength = jEnd - jStart;
+
+ // handle one block, row by row
+ final T[] block = blocks[iBlock * blockColumns + jBlock];
+ for (int i = iStart; i < iEnd; ++i) {
+ System.arraycopy(
+ subMatrix[i - row],
+ jStart - column,
+ block,
+ (i - firstRow) * jWidth + (jStart - firstColumn),
+ jLength);
+ }
+ }
+ }
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public FieldMatrix<T> getRowMatrix(final int row) throws OutOfRangeException {
+ checkRowIndex(row);
+ final BlockFieldMatrix<T> out = new BlockFieldMatrix<T>(getField(), 1, columns);
+
+ // perform copy block-wise, to ensure good cache behavior
+ final int iBlock = row / BLOCK_SIZE;
+ final int iRow = row - iBlock * BLOCK_SIZE;
+ int outBlockIndex = 0;
+ int outIndex = 0;
+ T[] outBlock = out.blocks[outBlockIndex];
+ for (int jBlock = 0; jBlock < blockColumns; ++jBlock) {
+ final int jWidth = blockWidth(jBlock);
+ final T[] block = blocks[iBlock * blockColumns + jBlock];
+ final int available = outBlock.length - outIndex;
+ if (jWidth > available) {
+ System.arraycopy(block, iRow * jWidth, outBlock, outIndex, available);
+ outBlock = out.blocks[++outBlockIndex];
+ System.arraycopy(block, iRow * jWidth, outBlock, 0, jWidth - available);
+ outIndex = jWidth - available;
+ } else {
+ System.arraycopy(block, iRow * jWidth, outBlock, outIndex, jWidth);
+ outIndex += jWidth;
+ }
+ }
+
+ return out;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void setRowMatrix(final int row, final FieldMatrix<T> matrix)
+ throws MatrixDimensionMismatchException, OutOfRangeException {
+ try {
+ setRowMatrix(row, (BlockFieldMatrix<T>) matrix);
+ } catch (ClassCastException cce) {
+ super.setRowMatrix(row, matrix);
+ }
+ }
+
+ /**
+ * Sets the entries in row number <code>row</code> as a row matrix. Row indices start at 0.
+ *
+ * @param row the row to be set
+ * @param matrix row matrix (must have one row and the same number of columns as the instance)
+ * @throws MatrixDimensionMismatchException if the matrix dimensions do not match one instance
+ * row.
+ * @throws OutOfRangeException if the specified row index is invalid.
+ */
+ public void setRowMatrix(final int row, final BlockFieldMatrix<T> matrix)
+ throws MatrixDimensionMismatchException, OutOfRangeException {
+ checkRowIndex(row);
+ final int nCols = getColumnDimension();
+ if ((matrix.getRowDimension() != 1) || (matrix.getColumnDimension() != nCols)) {
+ throw new MatrixDimensionMismatchException(
+ matrix.getRowDimension(), matrix.getColumnDimension(), 1, nCols);
+ }
+
+ // perform copy block-wise, to ensure good cache behavior
+ final int iBlock = row / BLOCK_SIZE;
+ final int iRow = row - iBlock * BLOCK_SIZE;
+ int mBlockIndex = 0;
+ int mIndex = 0;
+ T[] mBlock = matrix.blocks[mBlockIndex];
+ for (int jBlock = 0; jBlock < blockColumns; ++jBlock) {
+ final int jWidth = blockWidth(jBlock);
+ final T[] block = blocks[iBlock * blockColumns + jBlock];
+ final int available = mBlock.length - mIndex;
+ if (jWidth > available) {
+ System.arraycopy(mBlock, mIndex, block, iRow * jWidth, available);
+ mBlock = matrix.blocks[++mBlockIndex];
+ System.arraycopy(mBlock, 0, block, iRow * jWidth, jWidth - available);
+ mIndex = jWidth - available;
+ } else {
+ System.arraycopy(mBlock, mIndex, block, iRow * jWidth, jWidth);
+ mIndex += jWidth;
+ }
+ }
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public FieldMatrix<T> getColumnMatrix(final int column) throws OutOfRangeException {
+ checkColumnIndex(column);
+ final BlockFieldMatrix<T> out = new BlockFieldMatrix<T>(getField(), rows, 1);
+
+ // perform copy block-wise, to ensure good cache behavior
+ final int jBlock = column / BLOCK_SIZE;
+ final int jColumn = column - jBlock * BLOCK_SIZE;
+ final int jWidth = blockWidth(jBlock);
+ int outBlockIndex = 0;
+ int outIndex = 0;
+ T[] outBlock = out.blocks[outBlockIndex];
+ for (int iBlock = 0; iBlock < blockRows; ++iBlock) {
+ final int iHeight = blockHeight(iBlock);
+ final T[] block = blocks[iBlock * blockColumns + jBlock];
+ for (int i = 0; i < iHeight; ++i) {
+ if (outIndex >= outBlock.length) {
+ outBlock = out.blocks[++outBlockIndex];
+ outIndex = 0;
+ }
+ outBlock[outIndex++] = block[i * jWidth + jColumn];
+ }
+ }
+
+ return out;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void setColumnMatrix(final int column, final FieldMatrix<T> matrix)
+ throws MatrixDimensionMismatchException, OutOfRangeException {
+ try {
+ setColumnMatrix(column, (BlockFieldMatrix<T>) matrix);
+ } catch (ClassCastException cce) {
+ super.setColumnMatrix(column, matrix);
+ }
+ }
+
+ /**
+ * Sets the entries in column number {@code column} as a column matrix. Column indices start at
+ * 0.
+ *
+ * @param column Column to be set.
+ * @param matrix Column matrix (must have one column and the same number of rows as the
+ * instance).
+ * @throws MatrixDimensionMismatchException if the matrix dimensions do not match one instance
+ * column.
+ * @throws OutOfRangeException if the specified column index is invalid.
+ */
+ void setColumnMatrix(final int column, final BlockFieldMatrix<T> matrix)
+ throws MatrixDimensionMismatchException, OutOfRangeException {
+ checkColumnIndex(column);
+ final int nRows = getRowDimension();
+ if ((matrix.getRowDimension() != nRows) || (matrix.getColumnDimension() != 1)) {
+ throw new MatrixDimensionMismatchException(
+ matrix.getRowDimension(), matrix.getColumnDimension(), nRows, 1);
+ }
+
+ // perform copy block-wise, to ensure good cache behavior
+ final int jBlock = column / BLOCK_SIZE;
+ final int jColumn = column - jBlock * BLOCK_SIZE;
+ final int jWidth = blockWidth(jBlock);
+ int mBlockIndex = 0;
+ int mIndex = 0;
+ T[] mBlock = matrix.blocks[mBlockIndex];
+ for (int iBlock = 0; iBlock < blockRows; ++iBlock) {
+ final int iHeight = blockHeight(iBlock);
+ final T[] block = blocks[iBlock * blockColumns + jBlock];
+ for (int i = 0; i < iHeight; ++i) {
+ if (mIndex >= mBlock.length) {
+ mBlock = matrix.blocks[++mBlockIndex];
+ mIndex = 0;
+ }
+ block[i * jWidth + jColumn] = mBlock[mIndex++];
+ }
+ }
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public FieldVector<T> getRowVector(final int row) throws OutOfRangeException {
+ checkRowIndex(row);
+ final T[] outData = MathArrays.buildArray(getField(), columns);
+
+ // perform copy block-wise, to ensure good cache behavior
+ final int iBlock = row / BLOCK_SIZE;
+ final int iRow = row - iBlock * BLOCK_SIZE;
+ int outIndex = 0;
+ for (int jBlock = 0; jBlock < blockColumns; ++jBlock) {
+ final int jWidth = blockWidth(jBlock);
+ final T[] block = blocks[iBlock * blockColumns + jBlock];
+ System.arraycopy(block, iRow * jWidth, outData, outIndex, jWidth);
+ outIndex += jWidth;
+ }
+
+ return new ArrayFieldVector<T>(getField(), outData, false);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void setRowVector(final int row, final FieldVector<T> vector)
+ throws MatrixDimensionMismatchException, OutOfRangeException {
+ try {
+ setRow(row, ((ArrayFieldVector<T>) vector).getDataRef());
+ } catch (ClassCastException cce) {
+ super.setRowVector(row, vector);
+ }
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public FieldVector<T> getColumnVector(final int column) throws OutOfRangeException {
+ checkColumnIndex(column);
+ final T[] outData = MathArrays.buildArray(getField(), rows);
+
+ // perform copy block-wise, to ensure good cache behavior
+ final int jBlock = column / BLOCK_SIZE;
+ final int jColumn = column - jBlock * BLOCK_SIZE;
+ final int jWidth = blockWidth(jBlock);
+ int outIndex = 0;
+ for (int iBlock = 0; iBlock < blockRows; ++iBlock) {
+ final int iHeight = blockHeight(iBlock);
+ final T[] block = blocks[iBlock * blockColumns + jBlock];
+ for (int i = 0; i < iHeight; ++i) {
+ outData[outIndex++] = block[i * jWidth + jColumn];
+ }
+ }
+
+ return new ArrayFieldVector<T>(getField(), outData, false);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void setColumnVector(final int column, final FieldVector<T> vector)
+ throws OutOfRangeException, MatrixDimensionMismatchException {
+ try {
+ setColumn(column, ((ArrayFieldVector<T>) vector).getDataRef());
+ } catch (ClassCastException cce) {
+ super.setColumnVector(column, vector);
+ }
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public T[] getRow(final int row) throws OutOfRangeException {
+ checkRowIndex(row);
+ final T[] out = MathArrays.buildArray(getField(), columns);
+
+ // perform copy block-wise, to ensure good cache behavior
+ final int iBlock = row / BLOCK_SIZE;
+ final int iRow = row - iBlock * BLOCK_SIZE;
+ int outIndex = 0;
+ for (int jBlock = 0; jBlock < blockColumns; ++jBlock) {
+ final int jWidth = blockWidth(jBlock);
+ final T[] block = blocks[iBlock * blockColumns + jBlock];
+ System.arraycopy(block, iRow * jWidth, out, outIndex, jWidth);
+ outIndex += jWidth;
+ }
+
+ return out;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void setRow(final int row, final T[] array)
+ throws OutOfRangeException, MatrixDimensionMismatchException {
+ checkRowIndex(row);
+ final int nCols = getColumnDimension();
+ if (array.length != nCols) {
+ throw new MatrixDimensionMismatchException(1, array.length, 1, nCols);
+ }
+
+ // perform copy block-wise, to ensure good cache behavior
+ final int iBlock = row / BLOCK_SIZE;
+ final int iRow = row - iBlock * BLOCK_SIZE;
+ int outIndex = 0;
+ for (int jBlock = 0; jBlock < blockColumns; ++jBlock) {
+ final int jWidth = blockWidth(jBlock);
+ final T[] block = blocks[iBlock * blockColumns + jBlock];
+ System.arraycopy(array, outIndex, block, iRow * jWidth, jWidth);
+ outIndex += jWidth;
+ }
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public T[] getColumn(final int column) throws OutOfRangeException {
+ checkColumnIndex(column);
+ final T[] out = MathArrays.buildArray(getField(), rows);
+
+ // perform copy block-wise, to ensure good cache behavior
+ final int jBlock = column / BLOCK_SIZE;
+ final int jColumn = column - jBlock * BLOCK_SIZE;
+ final int jWidth = blockWidth(jBlock);
+ int outIndex = 0;
+ for (int iBlock = 0; iBlock < blockRows; ++iBlock) {
+ final int iHeight = blockHeight(iBlock);
+ final T[] block = blocks[iBlock * blockColumns + jBlock];
+ for (int i = 0; i < iHeight; ++i) {
+ out[outIndex++] = block[i * jWidth + jColumn];
+ }
+ }
+
+ return out;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void setColumn(final int column, final T[] array)
+ throws MatrixDimensionMismatchException, OutOfRangeException {
+ checkColumnIndex(column);
+ final int nRows = getRowDimension();
+ if (array.length != nRows) {
+ throw new MatrixDimensionMismatchException(array.length, 1, nRows, 1);
+ }
+
+ // perform copy block-wise, to ensure good cache behavior
+ final int jBlock = column / BLOCK_SIZE;
+ final int jColumn = column - jBlock * BLOCK_SIZE;
+ final int jWidth = blockWidth(jBlock);
+ int outIndex = 0;
+ for (int iBlock = 0; iBlock < blockRows; ++iBlock) {
+ final int iHeight = blockHeight(iBlock);
+ final T[] block = blocks[iBlock * blockColumns + jBlock];
+ for (int i = 0; i < iHeight; ++i) {
+ block[i * jWidth + jColumn] = array[outIndex++];
+ }
+ }
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public T getEntry(final int row, final int column) throws OutOfRangeException {
+ checkRowIndex(row);
+ checkColumnIndex(column);
+
+ final int iBlock = row / BLOCK_SIZE;
+ final int jBlock = column / BLOCK_SIZE;
+ final int k =
+ (row - iBlock * BLOCK_SIZE) * blockWidth(jBlock) + (column - jBlock * BLOCK_SIZE);
+
+ return blocks[iBlock * blockColumns + jBlock][k];
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void setEntry(final int row, final int column, final T value)
+ throws OutOfRangeException {
+ checkRowIndex(row);
+ checkColumnIndex(column);
+
+ final int iBlock = row / BLOCK_SIZE;
+ final int jBlock = column / BLOCK_SIZE;
+ final int k =
+ (row - iBlock * BLOCK_SIZE) * blockWidth(jBlock) + (column - jBlock * BLOCK_SIZE);
+
+ blocks[iBlock * blockColumns + jBlock][k] = value;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void addToEntry(final int row, final int column, final T increment)
+ throws OutOfRangeException {
+ checkRowIndex(row);
+ checkColumnIndex(column);
+
+ final int iBlock = row / BLOCK_SIZE;
+ final int jBlock = column / BLOCK_SIZE;
+ final int k =
+ (row - iBlock * BLOCK_SIZE) * blockWidth(jBlock) + (column - jBlock * BLOCK_SIZE);
+ final T[] blockIJ = blocks[iBlock * blockColumns + jBlock];
+
+ blockIJ[k] = blockIJ[k].add(increment);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void multiplyEntry(final int row, final int column, final T factor)
+ throws OutOfRangeException {
+ checkRowIndex(row);
+ checkColumnIndex(column);
+
+ final int iBlock = row / BLOCK_SIZE;
+ final int jBlock = column / BLOCK_SIZE;
+ final int k =
+ (row - iBlock * BLOCK_SIZE) * blockWidth(jBlock) + (column - jBlock * BLOCK_SIZE);
+ final T[] blockIJ = blocks[iBlock * blockColumns + jBlock];
+
+ blockIJ[k] = blockIJ[k].multiply(factor);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public FieldMatrix<T> transpose() {
+ final int nRows = getRowDimension();
+ final int nCols = getColumnDimension();
+ final BlockFieldMatrix<T> out = new BlockFieldMatrix<T>(getField(), nCols, nRows);
+
+ // perform transpose block-wise, to ensure good cache behavior
+ int blockIndex = 0;
+ for (int iBlock = 0; iBlock < blockColumns; ++iBlock) {
+ for (int jBlock = 0; jBlock < blockRows; ++jBlock) {
+
+ // transpose current block
+ final T[] outBlock = out.blocks[blockIndex];
+ final T[] tBlock = blocks[jBlock * blockColumns + iBlock];
+ final int pStart = iBlock * BLOCK_SIZE;
+ final int pEnd = FastMath.min(pStart + BLOCK_SIZE, columns);
+ final int qStart = jBlock * BLOCK_SIZE;
+ final int qEnd = FastMath.min(qStart + BLOCK_SIZE, rows);
+ int k = 0;
+ for (int p = pStart; p < pEnd; ++p) {
+ final int lInc = pEnd - pStart;
+ int l = p - pStart;
+ for (int q = qStart; q < qEnd; ++q) {
+ outBlock[k] = tBlock[l];
+ ++k;
+ l += lInc;
+ }
+ }
+
+ // go to next block
+ ++blockIndex;
+ }
+ }
+
+ return out;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public int getRowDimension() {
+ return rows;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public int getColumnDimension() {
+ return columns;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public T[] operate(final T[] v) throws DimensionMismatchException {
+ if (v.length != columns) {
+ throw new DimensionMismatchException(v.length, columns);
+ }
+ final T[] out = MathArrays.buildArray(getField(), rows);
+ final T zero = getField().getZero();
+
+ // perform multiplication block-wise, to ensure good cache behavior
+ for (int iBlock = 0; iBlock < blockRows; ++iBlock) {
+ final int pStart = iBlock * BLOCK_SIZE;
+ final int pEnd = FastMath.min(pStart + BLOCK_SIZE, rows);
+ for (int jBlock = 0; jBlock < blockColumns; ++jBlock) {
+ final T[] block = blocks[iBlock * blockColumns + jBlock];
+ final int qStart = jBlock * BLOCK_SIZE;
+ final int qEnd = FastMath.min(qStart + BLOCK_SIZE, columns);
+ int k = 0;
+ for (int p = pStart; p < pEnd; ++p) {
+ T sum = zero;
+ int q = qStart;
+ while (q < qEnd - 3) {
+ sum =
+ sum.add(block[k].multiply(v[q]))
+ .add(block[k + 1].multiply(v[q + 1]))
+ .add(block[k + 2].multiply(v[q + 2]))
+ .add(block[k + 3].multiply(v[q + 3]));
+ k += 4;
+ q += 4;
+ }
+ while (q < qEnd) {
+ sum = sum.add(block[k++].multiply(v[q++]));
+ }
+ out[p] = out[p].add(sum);
+ }
+ }
+ }
+
+ return out;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public T[] preMultiply(final T[] v) throws DimensionMismatchException {
+
+ if (v.length != rows) {
+ throw new DimensionMismatchException(v.length, rows);
+ }
+ final T[] out = MathArrays.buildArray(getField(), columns);
+ final T zero = getField().getZero();
+
+ // perform multiplication block-wise, to ensure good cache behavior
+ for (int jBlock = 0; jBlock < blockColumns; ++jBlock) {
+ final int jWidth = blockWidth(jBlock);
+ final int jWidth2 = jWidth + jWidth;
+ final int jWidth3 = jWidth2 + jWidth;
+ final int jWidth4 = jWidth3 + jWidth;
+ final int qStart = jBlock * BLOCK_SIZE;
+ final int qEnd = FastMath.min(qStart + BLOCK_SIZE, columns);
+ for (int iBlock = 0; iBlock < blockRows; ++iBlock) {
+ final T[] block = blocks[iBlock * blockColumns + jBlock];
+ final int pStart = iBlock * BLOCK_SIZE;
+ final int pEnd = FastMath.min(pStart + BLOCK_SIZE, rows);
+ for (int q = qStart; q < qEnd; ++q) {
+ int k = q - qStart;
+ T sum = zero;
+ int p = pStart;
+ while (p < pEnd - 3) {
+ sum =
+ sum.add(block[k].multiply(v[p]))
+ .add(block[k + jWidth].multiply(v[p + 1]))
+ .add(block[k + jWidth2].multiply(v[p + 2]))
+ .add(block[k + jWidth3].multiply(v[p + 3]));
+ k += jWidth4;
+ p += 4;
+ }
+ while (p < pEnd) {
+ sum = sum.add(block[k].multiply(v[p++]));
+ k += jWidth;
+ }
+ out[q] = out[q].add(sum);
+ }
+ }
+ }
+
+ return out;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public T walkInRowOrder(final FieldMatrixChangingVisitor<T> visitor) {
+ visitor.start(rows, columns, 0, rows - 1, 0, columns - 1);
+ for (int iBlock = 0; iBlock < blockRows; ++iBlock) {
+ final int pStart = iBlock * BLOCK_SIZE;
+ final int pEnd = FastMath.min(pStart + BLOCK_SIZE, rows);
+ for (int p = pStart; p < pEnd; ++p) {
+ for (int jBlock = 0; jBlock < blockColumns; ++jBlock) {
+ final int jWidth = blockWidth(jBlock);
+ final int qStart = jBlock * BLOCK_SIZE;
+ final int qEnd = FastMath.min(qStart + BLOCK_SIZE, columns);
+ final T[] block = blocks[iBlock * blockColumns + jBlock];
+ int k = (p - pStart) * jWidth;
+ for (int q = qStart; q < qEnd; ++q) {
+ block[k] = visitor.visit(p, q, block[k]);
+ ++k;
+ }
+ }
+ }
+ }
+ return visitor.end();
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public T walkInRowOrder(final FieldMatrixPreservingVisitor<T> visitor) {
+ visitor.start(rows, columns, 0, rows - 1, 0, columns - 1);
+ for (int iBlock = 0; iBlock < blockRows; ++iBlock) {
+ final int pStart = iBlock * BLOCK_SIZE;
+ final int pEnd = FastMath.min(pStart + BLOCK_SIZE, rows);
+ for (int p = pStart; p < pEnd; ++p) {
+ for (int jBlock = 0; jBlock < blockColumns; ++jBlock) {
+ final int jWidth = blockWidth(jBlock);
+ final int qStart = jBlock * BLOCK_SIZE;
+ final int qEnd = FastMath.min(qStart + BLOCK_SIZE, columns);
+ final T[] block = blocks[iBlock * blockColumns + jBlock];
+ int k = (p - pStart) * jWidth;
+ for (int q = qStart; q < qEnd; ++q) {
+ visitor.visit(p, q, block[k]);
+ ++k;
+ }
+ }
+ }
+ }
+ return visitor.end();
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public T walkInRowOrder(
+ final FieldMatrixChangingVisitor<T> visitor,
+ final int startRow,
+ final int endRow,
+ final int startColumn,
+ final int endColumn)
+ throws OutOfRangeException, NumberIsTooSmallException {
+ checkSubMatrixIndex(startRow, endRow, startColumn, endColumn);
+ visitor.start(rows, columns, startRow, endRow, startColumn, endColumn);
+ for (int iBlock = startRow / BLOCK_SIZE; iBlock < 1 + endRow / BLOCK_SIZE; ++iBlock) {
+ final int p0 = iBlock * BLOCK_SIZE;
+ final int pStart = FastMath.max(startRow, p0);
+ final int pEnd = FastMath.min((iBlock + 1) * BLOCK_SIZE, 1 + endRow);
+ for (int p = pStart; p < pEnd; ++p) {
+ for (int jBlock = startColumn / BLOCK_SIZE;
+ jBlock < 1 + endColumn / BLOCK_SIZE;
+ ++jBlock) {
+ final int jWidth = blockWidth(jBlock);
+ final int q0 = jBlock * BLOCK_SIZE;
+ final int qStart = FastMath.max(startColumn, q0);
+ final int qEnd = FastMath.min((jBlock + 1) * BLOCK_SIZE, 1 + endColumn);
+ final T[] block = blocks[iBlock * blockColumns + jBlock];
+ int k = (p - p0) * jWidth + qStart - q0;
+ for (int q = qStart; q < qEnd; ++q) {
+ block[k] = visitor.visit(p, q, block[k]);
+ ++k;
+ }
+ }
+ }
+ }
+ return visitor.end();
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public T walkInRowOrder(
+ final FieldMatrixPreservingVisitor<T> visitor,
+ final int startRow,
+ final int endRow,
+ final int startColumn,
+ final int endColumn)
+ throws OutOfRangeException, NumberIsTooSmallException {
+ checkSubMatrixIndex(startRow, endRow, startColumn, endColumn);
+ visitor.start(rows, columns, startRow, endRow, startColumn, endColumn);
+ for (int iBlock = startRow / BLOCK_SIZE; iBlock < 1 + endRow / BLOCK_SIZE; ++iBlock) {
+ final int p0 = iBlock * BLOCK_SIZE;
+ final int pStart = FastMath.max(startRow, p0);
+ final int pEnd = FastMath.min((iBlock + 1) * BLOCK_SIZE, 1 + endRow);
+ for (int p = pStart; p < pEnd; ++p) {
+ for (int jBlock = startColumn / BLOCK_SIZE;
+ jBlock < 1 + endColumn / BLOCK_SIZE;
+ ++jBlock) {
+ final int jWidth = blockWidth(jBlock);
+ final int q0 = jBlock * BLOCK_SIZE;
+ final int qStart = FastMath.max(startColumn, q0);
+ final int qEnd = FastMath.min((jBlock + 1) * BLOCK_SIZE, 1 + endColumn);
+ final T[] block = blocks[iBlock * blockColumns + jBlock];
+ int k = (p - p0) * jWidth + qStart - q0;
+ for (int q = qStart; q < qEnd; ++q) {
+ visitor.visit(p, q, block[k]);
+ ++k;
+ }
+ }
+ }
+ }
+ return visitor.end();
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public T walkInOptimizedOrder(final FieldMatrixChangingVisitor<T> visitor) {
+ visitor.start(rows, columns, 0, rows - 1, 0, columns - 1);
+ int blockIndex = 0;
+ for (int iBlock = 0; iBlock < blockRows; ++iBlock) {
+ final int pStart = iBlock * BLOCK_SIZE;
+ final int pEnd = FastMath.min(pStart + BLOCK_SIZE, rows);
+ for (int jBlock = 0; jBlock < blockColumns; ++jBlock) {
+ final int qStart = jBlock * BLOCK_SIZE;
+ final int qEnd = FastMath.min(qStart + BLOCK_SIZE, columns);
+ final T[] block = blocks[blockIndex];
+ int k = 0;
+ for (int p = pStart; p < pEnd; ++p) {
+ for (int q = qStart; q < qEnd; ++q) {
+ block[k] = visitor.visit(p, q, block[k]);
+ ++k;
+ }
+ }
+ ++blockIndex;
+ }
+ }
+ return visitor.end();
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public T walkInOptimizedOrder(final FieldMatrixPreservingVisitor<T> visitor) {
+ visitor.start(rows, columns, 0, rows - 1, 0, columns - 1);
+ int blockIndex = 0;
+ for (int iBlock = 0; iBlock < blockRows; ++iBlock) {
+ final int pStart = iBlock * BLOCK_SIZE;
+ final int pEnd = FastMath.min(pStart + BLOCK_SIZE, rows);
+ for (int jBlock = 0; jBlock < blockColumns; ++jBlock) {
+ final int qStart = jBlock * BLOCK_SIZE;
+ final int qEnd = FastMath.min(qStart + BLOCK_SIZE, columns);
+ final T[] block = blocks[blockIndex];
+ int k = 0;
+ for (int p = pStart; p < pEnd; ++p) {
+ for (int q = qStart; q < qEnd; ++q) {
+ visitor.visit(p, q, block[k]);
+ ++k;
+ }
+ }
+ ++blockIndex;
+ }
+ }
+ return visitor.end();
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public T walkInOptimizedOrder(
+ final FieldMatrixChangingVisitor<T> visitor,
+ final int startRow,
+ final int endRow,
+ final int startColumn,
+ final int endColumn)
+ throws OutOfRangeException, NumberIsTooSmallException {
+ checkSubMatrixIndex(startRow, endRow, startColumn, endColumn);
+ visitor.start(rows, columns, startRow, endRow, startColumn, endColumn);
+ for (int iBlock = startRow / BLOCK_SIZE; iBlock < 1 + endRow / BLOCK_SIZE; ++iBlock) {
+ final int p0 = iBlock * BLOCK_SIZE;
+ final int pStart = FastMath.max(startRow, p0);
+ final int pEnd = FastMath.min((iBlock + 1) * BLOCK_SIZE, 1 + endRow);
+ for (int jBlock = startColumn / BLOCK_SIZE;
+ jBlock < 1 + endColumn / BLOCK_SIZE;
+ ++jBlock) {
+ final int jWidth = blockWidth(jBlock);
+ final int q0 = jBlock * BLOCK_SIZE;
+ final int qStart = FastMath.max(startColumn, q0);
+ final int qEnd = FastMath.min((jBlock + 1) * BLOCK_SIZE, 1 + endColumn);
+ final T[] block = blocks[iBlock * blockColumns + jBlock];
+ for (int p = pStart; p < pEnd; ++p) {
+ int k = (p - p0) * jWidth + qStart - q0;
+ for (int q = qStart; q < qEnd; ++q) {
+ block[k] = visitor.visit(p, q, block[k]);
+ ++k;
+ }
+ }
+ }
+ }
+ return visitor.end();
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public T walkInOptimizedOrder(
+ final FieldMatrixPreservingVisitor<T> visitor,
+ final int startRow,
+ final int endRow,
+ final int startColumn,
+ final int endColumn)
+ throws OutOfRangeException, NumberIsTooSmallException {
+ checkSubMatrixIndex(startRow, endRow, startColumn, endColumn);
+ visitor.start(rows, columns, startRow, endRow, startColumn, endColumn);
+ for (int iBlock = startRow / BLOCK_SIZE; iBlock < 1 + endRow / BLOCK_SIZE; ++iBlock) {
+ final int p0 = iBlock * BLOCK_SIZE;
+ final int pStart = FastMath.max(startRow, p0);
+ final int pEnd = FastMath.min((iBlock + 1) * BLOCK_SIZE, 1 + endRow);
+ for (int jBlock = startColumn / BLOCK_SIZE;
+ jBlock < 1 + endColumn / BLOCK_SIZE;
+ ++jBlock) {
+ final int jWidth = blockWidth(jBlock);
+ final int q0 = jBlock * BLOCK_SIZE;
+ final int qStart = FastMath.max(startColumn, q0);
+ final int qEnd = FastMath.min((jBlock + 1) * BLOCK_SIZE, 1 + endColumn);
+ final T[] block = blocks[iBlock * blockColumns + jBlock];
+ for (int p = pStart; p < pEnd; ++p) {
+ int k = (p - p0) * jWidth + qStart - q0;
+ for (int q = qStart; q < qEnd; ++q) {
+ visitor.visit(p, q, block[k]);
+ ++k;
+ }
+ }
+ }
+ }
+ return visitor.end();
+ }
+
+ /**
+ * Get the height of a block.
+ *
+ * @param blockRow row index (in block sense) of the block
+ * @return height (number of rows) of the block
+ */
+ private int blockHeight(final int blockRow) {
+ return (blockRow == blockRows - 1) ? rows - blockRow * BLOCK_SIZE : BLOCK_SIZE;
+ }
+
+ /**
+ * Get the width of a block.
+ *
+ * @param blockColumn column index (in block sense) of the block
+ * @return width (number of columns) of the block
+ */
+ private int blockWidth(final int blockColumn) {
+ return (blockColumn == blockColumns - 1) ? columns - blockColumn * BLOCK_SIZE : BLOCK_SIZE;
+ }
+}
diff --git a/src/main/java/org/apache/commons/math3/linear/BlockRealMatrix.java b/src/main/java/org/apache/commons/math3/linear/BlockRealMatrix.java
new file mode 100644
index 0000000..a1346be
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/linear/BlockRealMatrix.java
@@ -0,0 +1,1653 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math3.linear;
+
+import org.apache.commons.math3.exception.DimensionMismatchException;
+import org.apache.commons.math3.exception.NoDataException;
+import org.apache.commons.math3.exception.NotStrictlyPositiveException;
+import org.apache.commons.math3.exception.NullArgumentException;
+import org.apache.commons.math3.exception.NumberIsTooSmallException;
+import org.apache.commons.math3.exception.OutOfRangeException;
+import org.apache.commons.math3.exception.util.LocalizedFormats;
+import org.apache.commons.math3.util.FastMath;
+import org.apache.commons.math3.util.MathUtils;
+
+import java.io.Serializable;
+import java.util.Arrays;
+
+/**
+ * Cache-friendly implementation of RealMatrix using a flat arrays to store square blocks of the
+ * matrix.
+ *
+ * <p>This implementation is specially designed to be cache-friendly. Square blocks are stored as
+ * small arrays and allow efficient traversal of data both in row major direction and columns major
+ * direction, one block at a time. This greatly increases performances for algorithms that use
+ * crossed directions loops like multiplication or transposition.
+ *
+ * <p>The size of square blocks is a static parameter. It may be tuned according to the cache size
+ * of the target computer processor. As a rule of thumbs, it should be the largest value that allows
+ * three blocks to be simultaneously cached (this is necessary for example for matrix
+ * multiplication). The default value is to use 52x52 blocks which is well suited for processors
+ * with 64k L1 cache (one block holds 2704 values or 21632 bytes). This value could be lowered to
+ * 36x36 for processors with 32k L1 cache.
+ *
+ * <p>The regular blocks represent {@link #BLOCK_SIZE} x {@link #BLOCK_SIZE} squares. Blocks at
+ * right hand side and bottom side which may be smaller to fit matrix dimensions. The square blocks
+ * are flattened in row major order in single dimension arrays which are therefore {@link
+ * #BLOCK_SIZE}<sup>2</sup> elements long for regular blocks. The blocks are themselves organized in
+ * row major order.
+ *
+ * <p>As an example, for a block size of 52x52, a 100x60 matrix would be stored in 4 blocks. Block 0
+ * would be a double[2704] array holding the upper left 52x52 square, block 1 would be a double[416]
+ * array holding the upper right 52x8 rectangle, block 2 would be a double[2496] array holding the
+ * lower left 48x52 rectangle and block 3 would be a double[384] array holding the lower right 48x8
+ * rectangle.
+ *
+ * <p>The layout complexity overhead versus simple mapping of matrices to java arrays is negligible
+ * for small matrices (about 1%). The gain from cache efficiency leads to up to 3-fold improvements
+ * for matrices of moderate to large size.
+ *
+ * @since 2.0
+ */
+public class BlockRealMatrix extends AbstractRealMatrix implements Serializable {
+ /** Block size. */
+ public static final int BLOCK_SIZE = 52;
+
+ /** Serializable version identifier */
+ private static final long serialVersionUID = 4991895511313664478L;
+
+ /** Blocks of matrix entries. */
+ private final double blocks[][];
+
+ /** Number of rows of the matrix. */
+ private final int rows;
+
+ /** Number of columns of the matrix. */
+ private final int columns;
+
+ /** Number of block rows of the matrix. */
+ private final int blockRows;
+
+ /** Number of block columns of the matrix. */
+ private final int blockColumns;
+
+ /**
+ * Create a new matrix with the supplied row and column dimensions.
+ *
+ * @param rows the number of rows in the new matrix
+ * @param columns the number of columns in the new matrix
+ * @throws NotStrictlyPositiveException if row or column dimension is not positive.
+ */
+ public BlockRealMatrix(final int rows, final int columns) throws NotStrictlyPositiveException {
+ super(rows, columns);
+ this.rows = rows;
+ this.columns = columns;
+
+ // number of blocks
+ blockRows = (rows + BLOCK_SIZE - 1) / BLOCK_SIZE;
+ blockColumns = (columns + BLOCK_SIZE - 1) / BLOCK_SIZE;
+
+ // allocate storage blocks, taking care of smaller ones at right and bottom
+ blocks = createBlocksLayout(rows, columns);
+ }
+
+ /**
+ * Create a new dense matrix copying entries from raw layout data.
+ *
+ * <p>The input array <em>must</em> already be in raw layout.
+ *
+ * <p>Calling this constructor is equivalent to call:
+ *
+ * <pre>matrix = new BlockRealMatrix(rawData.length, rawData[0].length,
+ * toBlocksLayout(rawData), false);</pre>
+ *
+ * @param rawData data for new matrix, in raw layout
+ * @throws DimensionMismatchException if the shape of {@code blockData} is inconsistent with
+ * block layout.
+ * @throws NotStrictlyPositiveException if row or column dimension is not positive.
+ * @see #BlockRealMatrix(int, int, double[][], boolean)
+ */
+ public BlockRealMatrix(final double[][] rawData)
+ throws DimensionMismatchException, NotStrictlyPositiveException {
+ this(rawData.length, rawData[0].length, toBlocksLayout(rawData), false);
+ }
+
+ /**
+ * Create a new dense matrix copying entries from block layout data.
+ *
+ * <p>The input array <em>must</em> already be in blocks layout.
+ *
+ * @param rows Number of rows in the new matrix.
+ * @param columns Number of columns in the new matrix.
+ * @param blockData data for new matrix
+ * @param copyArray Whether the input array will be copied or referenced.
+ * @throws DimensionMismatchException if the shape of {@code blockData} is inconsistent with
+ * block layout.
+ * @throws NotStrictlyPositiveException if row or column dimension is not positive.
+ * @see #createBlocksLayout(int, int)
+ * @see #toBlocksLayout(double[][])
+ * @see #BlockRealMatrix(double[][])
+ */
+ public BlockRealMatrix(
+ final int rows, final int columns, final double[][] blockData, final boolean copyArray)
+ throws DimensionMismatchException, NotStrictlyPositiveException {
+ super(rows, columns);
+ this.rows = rows;
+ this.columns = columns;
+
+ // number of blocks
+ blockRows = (rows + BLOCK_SIZE - 1) / BLOCK_SIZE;
+ blockColumns = (columns + BLOCK_SIZE - 1) / BLOCK_SIZE;
+
+ if (copyArray) {
+ // allocate storage blocks, taking care of smaller ones at right and bottom
+ blocks = new double[blockRows * blockColumns][];
+ } else {
+ // reference existing array
+ blocks = blockData;
+ }
+
+ int index = 0;
+ for (int iBlock = 0; iBlock < blockRows; ++iBlock) {
+ final int iHeight = blockHeight(iBlock);
+ for (int jBlock = 0; jBlock < blockColumns; ++jBlock, ++index) {
+ if (blockData[index].length != iHeight * blockWidth(jBlock)) {
+ throw new DimensionMismatchException(
+ blockData[index].length, iHeight * blockWidth(jBlock));
+ }
+ if (copyArray) {
+ blocks[index] = blockData[index].clone();
+ }
+ }
+ }
+ }
+
+ /**
+ * Convert a data array from raw layout to blocks layout.
+ *
+ * <p>Raw layout is the straightforward layout where element at row i and column j is in array
+ * element <code>rawData[i][j]</code>. Blocks layout is the layout used in {@link
+ * BlockRealMatrix} instances, where the matrix is split in square blocks (except at right and
+ * bottom side where blocks may be rectangular to fit matrix size) and each block is stored in a
+ * flattened one-dimensional array.
+ *
+ * <p>This method creates an array in blocks layout from an input array in raw layout. It can be
+ * used to provide the array argument of the {@link #BlockRealMatrix(int, int, double[][],
+ * boolean)} constructor.
+ *
+ * @param rawData Data array in raw layout.
+ * @return a new data array containing the same entries but in blocks layout.
+ * @throws DimensionMismatchException if {@code rawData} is not rectangular.
+ * @see #createBlocksLayout(int, int)
+ * @see #BlockRealMatrix(int, int, double[][], boolean)
+ */
+ public static double[][] toBlocksLayout(final double[][] rawData)
+ throws DimensionMismatchException {
+ final int rows = rawData.length;
+ final int columns = rawData[0].length;
+ final int blockRows = (rows + BLOCK_SIZE - 1) / BLOCK_SIZE;
+ final int blockColumns = (columns + BLOCK_SIZE - 1) / BLOCK_SIZE;
+
+ // safety checks
+ for (int i = 0; i < rawData.length; ++i) {
+ final int length = rawData[i].length;
+ if (length != columns) {
+ throw new DimensionMismatchException(columns, length);
+ }
+ }
+
+ // convert array
+ final double[][] blocks = new double[blockRows * blockColumns][];
+ int blockIndex = 0;
+ for (int iBlock = 0; iBlock < blockRows; ++iBlock) {
+ final int pStart = iBlock * BLOCK_SIZE;
+ final int pEnd = FastMath.min(pStart + BLOCK_SIZE, rows);
+ final int iHeight = pEnd - pStart;
+ for (int jBlock = 0; jBlock < blockColumns; ++jBlock) {
+ final int qStart = jBlock * BLOCK_SIZE;
+ final int qEnd = FastMath.min(qStart + BLOCK_SIZE, columns);
+ final int jWidth = qEnd - qStart;
+
+ // allocate new block
+ final double[] block = new double[iHeight * jWidth];
+ blocks[blockIndex] = block;
+
+ // copy data
+ int index = 0;
+ for (int p = pStart; p < pEnd; ++p) {
+ System.arraycopy(rawData[p], qStart, block, index, jWidth);
+ index += jWidth;
+ }
+ ++blockIndex;
+ }
+ }
+
+ return blocks;
+ }
+
+ /**
+ * Create a data array in blocks layout.
+ *
+ * <p>This method can be used to create the array argument of the {@link #BlockRealMatrix(int,
+ * int, double[][], boolean)} constructor.
+ *
+ * @param rows Number of rows in the new matrix.
+ * @param columns Number of columns in the new matrix.
+ * @return a new data array in blocks layout.
+ * @see #toBlocksLayout(double[][])
+ * @see #BlockRealMatrix(int, int, double[][], boolean)
+ */
+ public static double[][] createBlocksLayout(final int rows, final int columns) {
+ final int blockRows = (rows + BLOCK_SIZE - 1) / BLOCK_SIZE;
+ final int blockColumns = (columns + BLOCK_SIZE - 1) / BLOCK_SIZE;
+
+ final double[][] blocks = new double[blockRows * blockColumns][];
+ int blockIndex = 0;
+ for (int iBlock = 0; iBlock < blockRows; ++iBlock) {
+ final int pStart = iBlock * BLOCK_SIZE;
+ final int pEnd = FastMath.min(pStart + BLOCK_SIZE, rows);
+ final int iHeight = pEnd - pStart;
+ for (int jBlock = 0; jBlock < blockColumns; ++jBlock) {
+ final int qStart = jBlock * BLOCK_SIZE;
+ final int qEnd = FastMath.min(qStart + BLOCK_SIZE, columns);
+ final int jWidth = qEnd - qStart;
+ blocks[blockIndex] = new double[iHeight * jWidth];
+ ++blockIndex;
+ }
+ }
+
+ return blocks;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public BlockRealMatrix createMatrix(final int rowDimension, final int columnDimension)
+ throws NotStrictlyPositiveException {
+ return new BlockRealMatrix(rowDimension, columnDimension);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public BlockRealMatrix copy() {
+ // create an empty matrix
+ BlockRealMatrix copied = new BlockRealMatrix(rows, columns);
+
+ // copy the blocks
+ for (int i = 0; i < blocks.length; ++i) {
+ System.arraycopy(blocks[i], 0, copied.blocks[i], 0, blocks[i].length);
+ }
+
+ return copied;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public BlockRealMatrix add(final RealMatrix m) throws MatrixDimensionMismatchException {
+ try {
+ return add((BlockRealMatrix) m);
+ } catch (ClassCastException cce) {
+ // safety check
+ MatrixUtils.checkAdditionCompatible(this, m);
+
+ final BlockRealMatrix out = new BlockRealMatrix(rows, columns);
+
+ // perform addition block-wise, to ensure good cache behavior
+ int blockIndex = 0;
+ for (int iBlock = 0; iBlock < out.blockRows; ++iBlock) {
+ for (int jBlock = 0; jBlock < out.blockColumns; ++jBlock) {
+
+ // perform addition on the current block
+ final double[] outBlock = out.blocks[blockIndex];
+ final double[] tBlock = blocks[blockIndex];
+ final int pStart = iBlock * BLOCK_SIZE;
+ final int pEnd = FastMath.min(pStart + BLOCK_SIZE, rows);
+ final int qStart = jBlock * BLOCK_SIZE;
+ final int qEnd = FastMath.min(qStart + BLOCK_SIZE, columns);
+ int k = 0;
+ for (int p = pStart; p < pEnd; ++p) {
+ for (int q = qStart; q < qEnd; ++q) {
+ outBlock[k] = tBlock[k] + m.getEntry(p, q);
+ ++k;
+ }
+ }
+ // go to next block
+ ++blockIndex;
+ }
+ }
+
+ return out;
+ }
+ }
+
+ /**
+ * Compute the sum of this matrix and {@code m}.
+ *
+ * @param m Matrix to be added.
+ * @return {@code this} + m.
+ * @throws MatrixDimensionMismatchException if {@code m} is not the same size as this matrix.
+ */
+ public BlockRealMatrix add(final BlockRealMatrix m) throws MatrixDimensionMismatchException {
+ // safety check
+ MatrixUtils.checkAdditionCompatible(this, m);
+
+ final BlockRealMatrix out = new BlockRealMatrix(rows, columns);
+
+ // perform addition block-wise, to ensure good cache behavior
+ for (int blockIndex = 0; blockIndex < out.blocks.length; ++blockIndex) {
+ final double[] outBlock = out.blocks[blockIndex];
+ final double[] tBlock = blocks[blockIndex];
+ final double[] mBlock = m.blocks[blockIndex];
+ for (int k = 0; k < outBlock.length; ++k) {
+ outBlock[k] = tBlock[k] + mBlock[k];
+ }
+ }
+
+ return out;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public BlockRealMatrix subtract(final RealMatrix m) throws MatrixDimensionMismatchException {
+ try {
+ return subtract((BlockRealMatrix) m);
+ } catch (ClassCastException cce) {
+ // safety check
+ MatrixUtils.checkSubtractionCompatible(this, m);
+
+ final BlockRealMatrix out = new BlockRealMatrix(rows, columns);
+
+ // perform subtraction block-wise, to ensure good cache behavior
+ int blockIndex = 0;
+ for (int iBlock = 0; iBlock < out.blockRows; ++iBlock) {
+ for (int jBlock = 0; jBlock < out.blockColumns; ++jBlock) {
+
+ // perform subtraction on the current block
+ final double[] outBlock = out.blocks[blockIndex];
+ final double[] tBlock = blocks[blockIndex];
+ final int pStart = iBlock * BLOCK_SIZE;
+ final int pEnd = FastMath.min(pStart + BLOCK_SIZE, rows);
+ final int qStart = jBlock * BLOCK_SIZE;
+ final int qEnd = FastMath.min(qStart + BLOCK_SIZE, columns);
+ int k = 0;
+ for (int p = pStart; p < pEnd; ++p) {
+ for (int q = qStart; q < qEnd; ++q) {
+ outBlock[k] = tBlock[k] - m.getEntry(p, q);
+ ++k;
+ }
+ }
+ // go to next block
+ ++blockIndex;
+ }
+ }
+
+ return out;
+ }
+ }
+
+ /**
+ * Subtract {@code m} from this matrix.
+ *
+ * @param m Matrix to be subtracted.
+ * @return {@code this} - m.
+ * @throws MatrixDimensionMismatchException if {@code m} is not the same size as this matrix.
+ */
+ public BlockRealMatrix subtract(final BlockRealMatrix m)
+ throws MatrixDimensionMismatchException {
+ // safety check
+ MatrixUtils.checkSubtractionCompatible(this, m);
+
+ final BlockRealMatrix out = new BlockRealMatrix(rows, columns);
+
+ // perform subtraction block-wise, to ensure good cache behavior
+ for (int blockIndex = 0; blockIndex < out.blocks.length; ++blockIndex) {
+ final double[] outBlock = out.blocks[blockIndex];
+ final double[] tBlock = blocks[blockIndex];
+ final double[] mBlock = m.blocks[blockIndex];
+ for (int k = 0; k < outBlock.length; ++k) {
+ outBlock[k] = tBlock[k] - mBlock[k];
+ }
+ }
+
+ return out;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public BlockRealMatrix scalarAdd(final double d) {
+
+ final BlockRealMatrix out = new BlockRealMatrix(rows, columns);
+
+ // perform subtraction block-wise, to ensure good cache behavior
+ for (int blockIndex = 0; blockIndex < out.blocks.length; ++blockIndex) {
+ final double[] outBlock = out.blocks[blockIndex];
+ final double[] tBlock = blocks[blockIndex];
+ for (int k = 0; k < outBlock.length; ++k) {
+ outBlock[k] = tBlock[k] + d;
+ }
+ }
+
+ return out;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public RealMatrix scalarMultiply(final double d) {
+ final BlockRealMatrix out = new BlockRealMatrix(rows, columns);
+
+ // perform subtraction block-wise, to ensure good cache behavior
+ for (int blockIndex = 0; blockIndex < out.blocks.length; ++blockIndex) {
+ final double[] outBlock = out.blocks[blockIndex];
+ final double[] tBlock = blocks[blockIndex];
+ for (int k = 0; k < outBlock.length; ++k) {
+ outBlock[k] = tBlock[k] * d;
+ }
+ }
+
+ return out;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public BlockRealMatrix multiply(final RealMatrix m) throws DimensionMismatchException {
+ try {
+ return multiply((BlockRealMatrix) m);
+ } catch (ClassCastException cce) {
+ // safety check
+ MatrixUtils.checkMultiplicationCompatible(this, m);
+
+ final BlockRealMatrix out = new BlockRealMatrix(rows, m.getColumnDimension());
+
+ // perform multiplication block-wise, to ensure good cache behavior
+ int blockIndex = 0;
+ for (int iBlock = 0; iBlock < out.blockRows; ++iBlock) {
+ final int pStart = iBlock * BLOCK_SIZE;
+ final int pEnd = FastMath.min(pStart + BLOCK_SIZE, rows);
+
+ for (int jBlock = 0; jBlock < out.blockColumns; ++jBlock) {
+ final int qStart = jBlock * BLOCK_SIZE;
+ final int qEnd = FastMath.min(qStart + BLOCK_SIZE, m.getColumnDimension());
+
+ // select current block
+ final double[] outBlock = out.blocks[blockIndex];
+
+ // perform multiplication on current block
+ for (int kBlock = 0; kBlock < blockColumns; ++kBlock) {
+ final int kWidth = blockWidth(kBlock);
+ final double[] tBlock = blocks[iBlock * blockColumns + kBlock];
+ final int rStart = kBlock * BLOCK_SIZE;
+ int k = 0;
+ for (int p = pStart; p < pEnd; ++p) {
+ final int lStart = (p - pStart) * kWidth;
+ final int lEnd = lStart + kWidth;
+ for (int q = qStart; q < qEnd; ++q) {
+ double sum = 0;
+ int r = rStart;
+ for (int l = lStart; l < lEnd; ++l) {
+ sum += tBlock[l] * m.getEntry(r, q);
+ ++r;
+ }
+ outBlock[k] += sum;
+ ++k;
+ }
+ }
+ }
+ // go to next block
+ ++blockIndex;
+ }
+ }
+
+ return out;
+ }
+ }
+
+ /**
+ * Returns the result of postmultiplying this by {@code m}.
+ *
+ * @param m Matrix to postmultiply by.
+ * @return {@code this} * m.
+ * @throws DimensionMismatchException if the matrices are not compatible.
+ */
+ public BlockRealMatrix multiply(BlockRealMatrix m) throws DimensionMismatchException {
+ // safety check
+ MatrixUtils.checkMultiplicationCompatible(this, m);
+
+ final BlockRealMatrix out = new BlockRealMatrix(rows, m.columns);
+
+ // perform multiplication block-wise, to ensure good cache behavior
+ int blockIndex = 0;
+ for (int iBlock = 0; iBlock < out.blockRows; ++iBlock) {
+
+ final int pStart = iBlock * BLOCK_SIZE;
+ final int pEnd = FastMath.min(pStart + BLOCK_SIZE, rows);
+
+ for (int jBlock = 0; jBlock < out.blockColumns; ++jBlock) {
+ final int jWidth = out.blockWidth(jBlock);
+ final int jWidth2 = jWidth + jWidth;
+ final int jWidth3 = jWidth2 + jWidth;
+ final int jWidth4 = jWidth3 + jWidth;
+
+ // select current block
+ final double[] outBlock = out.blocks[blockIndex];
+
+ // perform multiplication on current block
+ for (int kBlock = 0; kBlock < blockColumns; ++kBlock) {
+ final int kWidth = blockWidth(kBlock);
+ final double[] tBlock = blocks[iBlock * blockColumns + kBlock];
+ final double[] mBlock = m.blocks[kBlock * m.blockColumns + jBlock];
+ int k = 0;
+ for (int p = pStart; p < pEnd; ++p) {
+ final int lStart = (p - pStart) * kWidth;
+ final int lEnd = lStart + kWidth;
+ for (int nStart = 0; nStart < jWidth; ++nStart) {
+ double sum = 0;
+ int l = lStart;
+ int n = nStart;
+ while (l < lEnd - 3) {
+ sum +=
+ tBlock[l] * mBlock[n]
+ + tBlock[l + 1] * mBlock[n + jWidth]
+ + tBlock[l + 2] * mBlock[n + jWidth2]
+ + tBlock[l + 3] * mBlock[n + jWidth3];
+ l += 4;
+ n += jWidth4;
+ }
+ while (l < lEnd) {
+ sum += tBlock[l++] * mBlock[n];
+ n += jWidth;
+ }
+ outBlock[k] += sum;
+ ++k;
+ }
+ }
+ }
+ // go to next block
+ ++blockIndex;
+ }
+ }
+
+ return out;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public double[][] getData() {
+ final double[][] data = new double[getRowDimension()][getColumnDimension()];
+ final int lastColumns = columns - (blockColumns - 1) * BLOCK_SIZE;
+
+ for (int iBlock = 0; iBlock < blockRows; ++iBlock) {
+ final int pStart = iBlock * BLOCK_SIZE;
+ final int pEnd = FastMath.min(pStart + BLOCK_SIZE, rows);
+ int regularPos = 0;
+ int lastPos = 0;
+ for (int p = pStart; p < pEnd; ++p) {
+ final double[] dataP = data[p];
+ int blockIndex = iBlock * blockColumns;
+ int dataPos = 0;
+ for (int jBlock = 0; jBlock < blockColumns - 1; ++jBlock) {
+ System.arraycopy(blocks[blockIndex++], regularPos, dataP, dataPos, BLOCK_SIZE);
+ dataPos += BLOCK_SIZE;
+ }
+ System.arraycopy(blocks[blockIndex], lastPos, dataP, dataPos, lastColumns);
+ regularPos += BLOCK_SIZE;
+ lastPos += lastColumns;
+ }
+ }
+
+ return data;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public double getNorm() {
+ final double[] colSums = new double[BLOCK_SIZE];
+ double maxColSum = 0;
+ for (int jBlock = 0; jBlock < blockColumns; jBlock++) {
+ final int jWidth = blockWidth(jBlock);
+ Arrays.fill(colSums, 0, jWidth, 0.0);
+ for (int iBlock = 0; iBlock < blockRows; ++iBlock) {
+ final int iHeight = blockHeight(iBlock);
+ final double[] block = blocks[iBlock * blockColumns + jBlock];
+ for (int j = 0; j < jWidth; ++j) {
+ double sum = 0;
+ for (int i = 0; i < iHeight; ++i) {
+ sum += FastMath.abs(block[i * jWidth + j]);
+ }
+ colSums[j] += sum;
+ }
+ }
+ for (int j = 0; j < jWidth; ++j) {
+ maxColSum = FastMath.max(maxColSum, colSums[j]);
+ }
+ }
+ return maxColSum;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public double getFrobeniusNorm() {
+ double sum2 = 0;
+ for (int blockIndex = 0; blockIndex < blocks.length; ++blockIndex) {
+ for (final double entry : blocks[blockIndex]) {
+ sum2 += entry * entry;
+ }
+ }
+ return FastMath.sqrt(sum2);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public BlockRealMatrix getSubMatrix(
+ final int startRow, final int endRow, final int startColumn, final int endColumn)
+ throws OutOfRangeException, NumberIsTooSmallException {
+ // safety checks
+ MatrixUtils.checkSubMatrixIndex(this, startRow, endRow, startColumn, endColumn);
+
+ // create the output matrix
+ final BlockRealMatrix out =
+ new BlockRealMatrix(endRow - startRow + 1, endColumn - startColumn + 1);
+
+ // compute blocks shifts
+ final int blockStartRow = startRow / BLOCK_SIZE;
+ final int rowsShift = startRow % BLOCK_SIZE;
+ final int blockStartColumn = startColumn / BLOCK_SIZE;
+ final int columnsShift = startColumn % BLOCK_SIZE;
+
+ // perform extraction block-wise, to ensure good cache behavior
+ int pBlock = blockStartRow;
+ for (int iBlock = 0; iBlock < out.blockRows; ++iBlock) {
+ final int iHeight = out.blockHeight(iBlock);
+ int qBlock = blockStartColumn;
+ for (int jBlock = 0; jBlock < out.blockColumns; ++jBlock) {
+ final int jWidth = out.blockWidth(jBlock);
+
+ // handle one block of the output matrix
+ final int outIndex = iBlock * out.blockColumns + jBlock;
+ final double[] outBlock = out.blocks[outIndex];
+ final int index = pBlock * blockColumns + qBlock;
+ final int width = blockWidth(qBlock);
+
+ final int heightExcess = iHeight + rowsShift - BLOCK_SIZE;
+ final int widthExcess = jWidth + columnsShift - BLOCK_SIZE;
+ if (heightExcess > 0) {
+ // the submatrix block spans on two blocks rows from the original matrix
+ if (widthExcess > 0) {
+ // the submatrix block spans on two blocks columns from the original matrix
+ final int width2 = blockWidth(qBlock + 1);
+ copyBlockPart(
+ blocks[index],
+ width,
+ rowsShift,
+ BLOCK_SIZE,
+ columnsShift,
+ BLOCK_SIZE,
+ outBlock,
+ jWidth,
+ 0,
+ 0);
+ copyBlockPart(
+ blocks[index + 1],
+ width2,
+ rowsShift,
+ BLOCK_SIZE,
+ 0,
+ widthExcess,
+ outBlock,
+ jWidth,
+ 0,
+ jWidth - widthExcess);
+ copyBlockPart(
+ blocks[index + blockColumns],
+ width,
+ 0,
+ heightExcess,
+ columnsShift,
+ BLOCK_SIZE,
+ outBlock,
+ jWidth,
+ iHeight - heightExcess,
+ 0);
+ copyBlockPart(
+ blocks[index + blockColumns + 1],
+ width2,
+ 0,
+ heightExcess,
+ 0,
+ widthExcess,
+ outBlock,
+ jWidth,
+ iHeight - heightExcess,
+ jWidth - widthExcess);
+ } else {
+ // the submatrix block spans on one block column from the original matrix
+ copyBlockPart(
+ blocks[index],
+ width,
+ rowsShift,
+ BLOCK_SIZE,
+ columnsShift,
+ jWidth + columnsShift,
+ outBlock,
+ jWidth,
+ 0,
+ 0);
+ copyBlockPart(
+ blocks[index + blockColumns],
+ width,
+ 0,
+ heightExcess,
+ columnsShift,
+ jWidth + columnsShift,
+ outBlock,
+ jWidth,
+ iHeight - heightExcess,
+ 0);
+ }
+ } else {
+ // the submatrix block spans on one block row from the original matrix
+ if (widthExcess > 0) {
+ // the submatrix block spans on two blocks columns from the original matrix
+ final int width2 = blockWidth(qBlock + 1);
+ copyBlockPart(
+ blocks[index],
+ width,
+ rowsShift,
+ iHeight + rowsShift,
+ columnsShift,
+ BLOCK_SIZE,
+ outBlock,
+ jWidth,
+ 0,
+ 0);
+ copyBlockPart(
+ blocks[index + 1],
+ width2,
+ rowsShift,
+ iHeight + rowsShift,
+ 0,
+ widthExcess,
+ outBlock,
+ jWidth,
+ 0,
+ jWidth - widthExcess);
+ } else {
+ // the submatrix block spans on one block column from the original matrix
+ copyBlockPart(
+ blocks[index],
+ width,
+ rowsShift,
+ iHeight + rowsShift,
+ columnsShift,
+ jWidth + columnsShift,
+ outBlock,
+ jWidth,
+ 0,
+ 0);
+ }
+ }
+ ++qBlock;
+ }
+ ++pBlock;
+ }
+
+ return out;
+ }
+
+ /**
+ * Copy a part of a block into another one
+ *
+ * <p>This method can be called only when the specified part fits in both blocks, no
+ * verification is done here.
+ *
+ * @param srcBlock source block
+ * @param srcWidth source block width ({@link #BLOCK_SIZE} or smaller)
+ * @param srcStartRow start row in the source block
+ * @param srcEndRow end row (exclusive) in the source block
+ * @param srcStartColumn start column in the source block
+ * @param srcEndColumn end column (exclusive) in the source block
+ * @param dstBlock destination block
+ * @param dstWidth destination block width ({@link #BLOCK_SIZE} or smaller)
+ * @param dstStartRow start row in the destination block
+ * @param dstStartColumn start column in the destination block
+ */
+ private void copyBlockPart(
+ final double[] srcBlock,
+ final int srcWidth,
+ final int srcStartRow,
+ final int srcEndRow,
+ final int srcStartColumn,
+ final int srcEndColumn,
+ final double[] dstBlock,
+ final int dstWidth,
+ final int dstStartRow,
+ final int dstStartColumn) {
+ final int length = srcEndColumn - srcStartColumn;
+ int srcPos = srcStartRow * srcWidth + srcStartColumn;
+ int dstPos = dstStartRow * dstWidth + dstStartColumn;
+ for (int srcRow = srcStartRow; srcRow < srcEndRow; ++srcRow) {
+ System.arraycopy(srcBlock, srcPos, dstBlock, dstPos, length);
+ srcPos += srcWidth;
+ dstPos += dstWidth;
+ }
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void setSubMatrix(final double[][] subMatrix, final int row, final int column)
+ throws OutOfRangeException,
+ NoDataException,
+ NullArgumentException,
+ DimensionMismatchException {
+ // safety checks
+ MathUtils.checkNotNull(subMatrix);
+ final int refLength = subMatrix[0].length;
+ if (refLength == 0) {
+ throw new NoDataException(LocalizedFormats.AT_LEAST_ONE_COLUMN);
+ }
+ final int endRow = row + subMatrix.length - 1;
+ final int endColumn = column + refLength - 1;
+ MatrixUtils.checkSubMatrixIndex(this, row, endRow, column, endColumn);
+ for (final double[] subRow : subMatrix) {
+ if (subRow.length != refLength) {
+ throw new DimensionMismatchException(refLength, subRow.length);
+ }
+ }
+
+ // compute blocks bounds
+ final int blockStartRow = row / BLOCK_SIZE;
+ final int blockEndRow = (endRow + BLOCK_SIZE) / BLOCK_SIZE;
+ final int blockStartColumn = column / BLOCK_SIZE;
+ final int blockEndColumn = (endColumn + BLOCK_SIZE) / BLOCK_SIZE;
+
+ // perform copy block-wise, to ensure good cache behavior
+ for (int iBlock = blockStartRow; iBlock < blockEndRow; ++iBlock) {
+ final int iHeight = blockHeight(iBlock);
+ final int firstRow = iBlock * BLOCK_SIZE;
+ final int iStart = FastMath.max(row, firstRow);
+ final int iEnd = FastMath.min(endRow + 1, firstRow + iHeight);
+
+ for (int jBlock = blockStartColumn; jBlock < blockEndColumn; ++jBlock) {
+ final int jWidth = blockWidth(jBlock);
+ final int firstColumn = jBlock * BLOCK_SIZE;
+ final int jStart = FastMath.max(column, firstColumn);
+ final int jEnd = FastMath.min(endColumn + 1, firstColumn + jWidth);
+ final int jLength = jEnd - jStart;
+
+ // handle one block, row by row
+ final double[] block = blocks[iBlock * blockColumns + jBlock];
+ for (int i = iStart; i < iEnd; ++i) {
+ System.arraycopy(
+ subMatrix[i - row],
+ jStart - column,
+ block,
+ (i - firstRow) * jWidth + (jStart - firstColumn),
+ jLength);
+ }
+ }
+ }
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public BlockRealMatrix getRowMatrix(final int row) throws OutOfRangeException {
+ MatrixUtils.checkRowIndex(this, row);
+ final BlockRealMatrix out = new BlockRealMatrix(1, columns);
+
+ // perform copy block-wise, to ensure good cache behavior
+ final int iBlock = row / BLOCK_SIZE;
+ final int iRow = row - iBlock * BLOCK_SIZE;
+ int outBlockIndex = 0;
+ int outIndex = 0;
+ double[] outBlock = out.blocks[outBlockIndex];
+ for (int jBlock = 0; jBlock < blockColumns; ++jBlock) {
+ final int jWidth = blockWidth(jBlock);
+ final double[] block = blocks[iBlock * blockColumns + jBlock];
+ final int available = outBlock.length - outIndex;
+ if (jWidth > available) {
+ System.arraycopy(block, iRow * jWidth, outBlock, outIndex, available);
+ outBlock = out.blocks[++outBlockIndex];
+ System.arraycopy(block, iRow * jWidth, outBlock, 0, jWidth - available);
+ outIndex = jWidth - available;
+ } else {
+ System.arraycopy(block, iRow * jWidth, outBlock, outIndex, jWidth);
+ outIndex += jWidth;
+ }
+ }
+
+ return out;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void setRowMatrix(final int row, final RealMatrix matrix)
+ throws OutOfRangeException, MatrixDimensionMismatchException {
+ try {
+ setRowMatrix(row, (BlockRealMatrix) matrix);
+ } catch (ClassCastException cce) {
+ super.setRowMatrix(row, matrix);
+ }
+ }
+
+ /**
+ * Sets the entries in row number <code>row</code> as a row matrix. Row indices start at 0.
+ *
+ * @param row the row to be set
+ * @param matrix row matrix (must have one row and the same number of columns as the instance)
+ * @throws OutOfRangeException if the specified row index is invalid.
+ * @throws MatrixDimensionMismatchException if the matrix dimensions do not match one instance
+ * row.
+ */
+ public void setRowMatrix(final int row, final BlockRealMatrix matrix)
+ throws OutOfRangeException, MatrixDimensionMismatchException {
+ MatrixUtils.checkRowIndex(this, row);
+ final int nCols = getColumnDimension();
+ if ((matrix.getRowDimension() != 1) || (matrix.getColumnDimension() != nCols)) {
+ throw new MatrixDimensionMismatchException(
+ matrix.getRowDimension(), matrix.getColumnDimension(), 1, nCols);
+ }
+
+ // perform copy block-wise, to ensure good cache behavior
+ final int iBlock = row / BLOCK_SIZE;
+ final int iRow = row - iBlock * BLOCK_SIZE;
+ int mBlockIndex = 0;
+ int mIndex = 0;
+ double[] mBlock = matrix.blocks[mBlockIndex];
+ for (int jBlock = 0; jBlock < blockColumns; ++jBlock) {
+ final int jWidth = blockWidth(jBlock);
+ final double[] block = blocks[iBlock * blockColumns + jBlock];
+ final int available = mBlock.length - mIndex;
+ if (jWidth > available) {
+ System.arraycopy(mBlock, mIndex, block, iRow * jWidth, available);
+ mBlock = matrix.blocks[++mBlockIndex];
+ System.arraycopy(mBlock, 0, block, iRow * jWidth, jWidth - available);
+ mIndex = jWidth - available;
+ } else {
+ System.arraycopy(mBlock, mIndex, block, iRow * jWidth, jWidth);
+ mIndex += jWidth;
+ }
+ }
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public BlockRealMatrix getColumnMatrix(final int column) throws OutOfRangeException {
+ MatrixUtils.checkColumnIndex(this, column);
+ final BlockRealMatrix out = new BlockRealMatrix(rows, 1);
+
+ // perform copy block-wise, to ensure good cache behavior
+ final int jBlock = column / BLOCK_SIZE;
+ final int jColumn = column - jBlock * BLOCK_SIZE;
+ final int jWidth = blockWidth(jBlock);
+ int outBlockIndex = 0;
+ int outIndex = 0;
+ double[] outBlock = out.blocks[outBlockIndex];
+ for (int iBlock = 0; iBlock < blockRows; ++iBlock) {
+ final int iHeight = blockHeight(iBlock);
+ final double[] block = blocks[iBlock * blockColumns + jBlock];
+ for (int i = 0; i < iHeight; ++i) {
+ if (outIndex >= outBlock.length) {
+ outBlock = out.blocks[++outBlockIndex];
+ outIndex = 0;
+ }
+ outBlock[outIndex++] = block[i * jWidth + jColumn];
+ }
+ }
+
+ return out;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void setColumnMatrix(final int column, final RealMatrix matrix)
+ throws OutOfRangeException, MatrixDimensionMismatchException {
+ try {
+ setColumnMatrix(column, (BlockRealMatrix) matrix);
+ } catch (ClassCastException cce) {
+ super.setColumnMatrix(column, matrix);
+ }
+ }
+
+ /**
+ * Sets the entries in column number <code>column</code> as a column matrix. Column indices
+ * start at 0.
+ *
+ * @param column the column to be set
+ * @param matrix column matrix (must have one column and the same number of rows as the
+ * instance)
+ * @throws OutOfRangeException if the specified column index is invalid.
+ * @throws MatrixDimensionMismatchException if the matrix dimensions do not match one instance
+ * column.
+ */
+ void setColumnMatrix(final int column, final BlockRealMatrix matrix)
+ throws OutOfRangeException, MatrixDimensionMismatchException {
+ MatrixUtils.checkColumnIndex(this, column);
+ final int nRows = getRowDimension();
+ if ((matrix.getRowDimension() != nRows) || (matrix.getColumnDimension() != 1)) {
+ throw new MatrixDimensionMismatchException(
+ matrix.getRowDimension(), matrix.getColumnDimension(), nRows, 1);
+ }
+
+ // perform copy block-wise, to ensure good cache behavior
+ final int jBlock = column / BLOCK_SIZE;
+ final int jColumn = column - jBlock * BLOCK_SIZE;
+ final int jWidth = blockWidth(jBlock);
+ int mBlockIndex = 0;
+ int mIndex = 0;
+ double[] mBlock = matrix.blocks[mBlockIndex];
+ for (int iBlock = 0; iBlock < blockRows; ++iBlock) {
+ final int iHeight = blockHeight(iBlock);
+ final double[] block = blocks[iBlock * blockColumns + jBlock];
+ for (int i = 0; i < iHeight; ++i) {
+ if (mIndex >= mBlock.length) {
+ mBlock = matrix.blocks[++mBlockIndex];
+ mIndex = 0;
+ }
+ block[i * jWidth + jColumn] = mBlock[mIndex++];
+ }
+ }
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public RealVector getRowVector(final int row) throws OutOfRangeException {
+ MatrixUtils.checkRowIndex(this, row);
+ final double[] outData = new double[columns];
+
+ // perform copy block-wise, to ensure good cache behavior
+ final int iBlock = row / BLOCK_SIZE;
+ final int iRow = row - iBlock * BLOCK_SIZE;
+ int outIndex = 0;
+ for (int jBlock = 0; jBlock < blockColumns; ++jBlock) {
+ final int jWidth = blockWidth(jBlock);
+ final double[] block = blocks[iBlock * blockColumns + jBlock];
+ System.arraycopy(block, iRow * jWidth, outData, outIndex, jWidth);
+ outIndex += jWidth;
+ }
+
+ return new ArrayRealVector(outData, false);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void setRowVector(final int row, final RealVector vector)
+ throws OutOfRangeException, MatrixDimensionMismatchException {
+ try {
+ setRow(row, ((ArrayRealVector) vector).getDataRef());
+ } catch (ClassCastException cce) {
+ super.setRowVector(row, vector);
+ }
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public RealVector getColumnVector(final int column) throws OutOfRangeException {
+ MatrixUtils.checkColumnIndex(this, column);
+ final double[] outData = new double[rows];
+
+ // perform copy block-wise, to ensure good cache behavior
+ final int jBlock = column / BLOCK_SIZE;
+ final int jColumn = column - jBlock * BLOCK_SIZE;
+ final int jWidth = blockWidth(jBlock);
+ int outIndex = 0;
+ for (int iBlock = 0; iBlock < blockRows; ++iBlock) {
+ final int iHeight = blockHeight(iBlock);
+ final double[] block = blocks[iBlock * blockColumns + jBlock];
+ for (int i = 0; i < iHeight; ++i) {
+ outData[outIndex++] = block[i * jWidth + jColumn];
+ }
+ }
+
+ return new ArrayRealVector(outData, false);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void setColumnVector(final int column, final RealVector vector)
+ throws OutOfRangeException, MatrixDimensionMismatchException {
+ try {
+ setColumn(column, ((ArrayRealVector) vector).getDataRef());
+ } catch (ClassCastException cce) {
+ super.setColumnVector(column, vector);
+ }
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public double[] getRow(final int row) throws OutOfRangeException {
+ MatrixUtils.checkRowIndex(this, row);
+ final double[] out = new double[columns];
+
+ // perform copy block-wise, to ensure good cache behavior
+ final int iBlock = row / BLOCK_SIZE;
+ final int iRow = row - iBlock * BLOCK_SIZE;
+ int outIndex = 0;
+ for (int jBlock = 0; jBlock < blockColumns; ++jBlock) {
+ final int jWidth = blockWidth(jBlock);
+ final double[] block = blocks[iBlock * blockColumns + jBlock];
+ System.arraycopy(block, iRow * jWidth, out, outIndex, jWidth);
+ outIndex += jWidth;
+ }
+
+ return out;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void setRow(final int row, final double[] array)
+ throws OutOfRangeException, MatrixDimensionMismatchException {
+ MatrixUtils.checkRowIndex(this, row);
+ final int nCols = getColumnDimension();
+ if (array.length != nCols) {
+ throw new MatrixDimensionMismatchException(1, array.length, 1, nCols);
+ }
+
+ // perform copy block-wise, to ensure good cache behavior
+ final int iBlock = row / BLOCK_SIZE;
+ final int iRow = row - iBlock * BLOCK_SIZE;
+ int outIndex = 0;
+ for (int jBlock = 0; jBlock < blockColumns; ++jBlock) {
+ final int jWidth = blockWidth(jBlock);
+ final double[] block = blocks[iBlock * blockColumns + jBlock];
+ System.arraycopy(array, outIndex, block, iRow * jWidth, jWidth);
+ outIndex += jWidth;
+ }
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public double[] getColumn(final int column) throws OutOfRangeException {
+ MatrixUtils.checkColumnIndex(this, column);
+ final double[] out = new double[rows];
+
+ // perform copy block-wise, to ensure good cache behavior
+ final int jBlock = column / BLOCK_SIZE;
+ final int jColumn = column - jBlock * BLOCK_SIZE;
+ final int jWidth = blockWidth(jBlock);
+ int outIndex = 0;
+ for (int iBlock = 0; iBlock < blockRows; ++iBlock) {
+ final int iHeight = blockHeight(iBlock);
+ final double[] block = blocks[iBlock * blockColumns + jBlock];
+ for (int i = 0; i < iHeight; ++i) {
+ out[outIndex++] = block[i * jWidth + jColumn];
+ }
+ }
+
+ return out;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void setColumn(final int column, final double[] array)
+ throws OutOfRangeException, MatrixDimensionMismatchException {
+ MatrixUtils.checkColumnIndex(this, column);
+ final int nRows = getRowDimension();
+ if (array.length != nRows) {
+ throw new MatrixDimensionMismatchException(array.length, 1, nRows, 1);
+ }
+
+ // perform copy block-wise, to ensure good cache behavior
+ final int jBlock = column / BLOCK_SIZE;
+ final int jColumn = column - jBlock * BLOCK_SIZE;
+ final int jWidth = blockWidth(jBlock);
+ int outIndex = 0;
+ for (int iBlock = 0; iBlock < blockRows; ++iBlock) {
+ final int iHeight = blockHeight(iBlock);
+ final double[] block = blocks[iBlock * blockColumns + jBlock];
+ for (int i = 0; i < iHeight; ++i) {
+ block[i * jWidth + jColumn] = array[outIndex++];
+ }
+ }
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public double getEntry(final int row, final int column) throws OutOfRangeException {
+ MatrixUtils.checkMatrixIndex(this, row, column);
+ final int iBlock = row / BLOCK_SIZE;
+ final int jBlock = column / BLOCK_SIZE;
+ final int k =
+ (row - iBlock * BLOCK_SIZE) * blockWidth(jBlock) + (column - jBlock * BLOCK_SIZE);
+ return blocks[iBlock * blockColumns + jBlock][k];
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void setEntry(final int row, final int column, final double value)
+ throws OutOfRangeException {
+ MatrixUtils.checkMatrixIndex(this, row, column);
+ final int iBlock = row / BLOCK_SIZE;
+ final int jBlock = column / BLOCK_SIZE;
+ final int k =
+ (row - iBlock * BLOCK_SIZE) * blockWidth(jBlock) + (column - jBlock * BLOCK_SIZE);
+ blocks[iBlock * blockColumns + jBlock][k] = value;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void addToEntry(final int row, final int column, final double increment)
+ throws OutOfRangeException {
+ MatrixUtils.checkMatrixIndex(this, row, column);
+ final int iBlock = row / BLOCK_SIZE;
+ final int jBlock = column / BLOCK_SIZE;
+ final int k =
+ (row - iBlock * BLOCK_SIZE) * blockWidth(jBlock) + (column - jBlock * BLOCK_SIZE);
+ blocks[iBlock * blockColumns + jBlock][k] += increment;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void multiplyEntry(final int row, final int column, final double factor)
+ throws OutOfRangeException {
+ MatrixUtils.checkMatrixIndex(this, row, column);
+ final int iBlock = row / BLOCK_SIZE;
+ final int jBlock = column / BLOCK_SIZE;
+ final int k =
+ (row - iBlock * BLOCK_SIZE) * blockWidth(jBlock) + (column - jBlock * BLOCK_SIZE);
+ blocks[iBlock * blockColumns + jBlock][k] *= factor;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public BlockRealMatrix transpose() {
+ final int nRows = getRowDimension();
+ final int nCols = getColumnDimension();
+ final BlockRealMatrix out = new BlockRealMatrix(nCols, nRows);
+
+ // perform transpose block-wise, to ensure good cache behavior
+ int blockIndex = 0;
+ for (int iBlock = 0; iBlock < blockColumns; ++iBlock) {
+ for (int jBlock = 0; jBlock < blockRows; ++jBlock) {
+ // transpose current block
+ final double[] outBlock = out.blocks[blockIndex];
+ final double[] tBlock = blocks[jBlock * blockColumns + iBlock];
+ final int pStart = iBlock * BLOCK_SIZE;
+ final int pEnd = FastMath.min(pStart + BLOCK_SIZE, columns);
+ final int qStart = jBlock * BLOCK_SIZE;
+ final int qEnd = FastMath.min(qStart + BLOCK_SIZE, rows);
+ int k = 0;
+ for (int p = pStart; p < pEnd; ++p) {
+ final int lInc = pEnd - pStart;
+ int l = p - pStart;
+ for (int q = qStart; q < qEnd; ++q) {
+ outBlock[k] = tBlock[l];
+ ++k;
+ l += lInc;
+ }
+ }
+ // go to next block
+ ++blockIndex;
+ }
+ }
+
+ return out;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public int getRowDimension() {
+ return rows;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public int getColumnDimension() {
+ return columns;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public double[] operate(final double[] v) throws DimensionMismatchException {
+ if (v.length != columns) {
+ throw new DimensionMismatchException(v.length, columns);
+ }
+ final double[] out = new double[rows];
+
+ // perform multiplication block-wise, to ensure good cache behavior
+ for (int iBlock = 0; iBlock < blockRows; ++iBlock) {
+ final int pStart = iBlock * BLOCK_SIZE;
+ final int pEnd = FastMath.min(pStart + BLOCK_SIZE, rows);
+ for (int jBlock = 0; jBlock < blockColumns; ++jBlock) {
+ final double[] block = blocks[iBlock * blockColumns + jBlock];
+ final int qStart = jBlock * BLOCK_SIZE;
+ final int qEnd = FastMath.min(qStart + BLOCK_SIZE, columns);
+ int k = 0;
+ for (int p = pStart; p < pEnd; ++p) {
+ double sum = 0;
+ int q = qStart;
+ while (q < qEnd - 3) {
+ sum +=
+ block[k] * v[q]
+ + block[k + 1] * v[q + 1]
+ + block[k + 2] * v[q + 2]
+ + block[k + 3] * v[q + 3];
+ k += 4;
+ q += 4;
+ }
+ while (q < qEnd) {
+ sum += block[k++] * v[q++];
+ }
+ out[p] += sum;
+ }
+ }
+ }
+
+ return out;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public double[] preMultiply(final double[] v) throws DimensionMismatchException {
+ if (v.length != rows) {
+ throw new DimensionMismatchException(v.length, rows);
+ }
+ final double[] out = new double[columns];
+
+ // perform multiplication block-wise, to ensure good cache behavior
+ for (int jBlock = 0; jBlock < blockColumns; ++jBlock) {
+ final int jWidth = blockWidth(jBlock);
+ final int jWidth2 = jWidth + jWidth;
+ final int jWidth3 = jWidth2 + jWidth;
+ final int jWidth4 = jWidth3 + jWidth;
+ final int qStart = jBlock * BLOCK_SIZE;
+ final int qEnd = FastMath.min(qStart + BLOCK_SIZE, columns);
+ for (int iBlock = 0; iBlock < blockRows; ++iBlock) {
+ final double[] block = blocks[iBlock * blockColumns + jBlock];
+ final int pStart = iBlock * BLOCK_SIZE;
+ final int pEnd = FastMath.min(pStart + BLOCK_SIZE, rows);
+ for (int q = qStart; q < qEnd; ++q) {
+ int k = q - qStart;
+ double sum = 0;
+ int p = pStart;
+ while (p < pEnd - 3) {
+ sum +=
+ block[k] * v[p]
+ + block[k + jWidth] * v[p + 1]
+ + block[k + jWidth2] * v[p + 2]
+ + block[k + jWidth3] * v[p + 3];
+ k += jWidth4;
+ p += 4;
+ }
+ while (p < pEnd) {
+ sum += block[k] * v[p++];
+ k += jWidth;
+ }
+ out[q] += sum;
+ }
+ }
+ }
+
+ return out;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public double walkInRowOrder(final RealMatrixChangingVisitor visitor) {
+ visitor.start(rows, columns, 0, rows - 1, 0, columns - 1);
+ for (int iBlock = 0; iBlock < blockRows; ++iBlock) {
+ final int pStart = iBlock * BLOCK_SIZE;
+ final int pEnd = FastMath.min(pStart + BLOCK_SIZE, rows);
+ for (int p = pStart; p < pEnd; ++p) {
+ for (int jBlock = 0; jBlock < blockColumns; ++jBlock) {
+ final int jWidth = blockWidth(jBlock);
+ final int qStart = jBlock * BLOCK_SIZE;
+ final int qEnd = FastMath.min(qStart + BLOCK_SIZE, columns);
+ final double[] block = blocks[iBlock * blockColumns + jBlock];
+ int k = (p - pStart) * jWidth;
+ for (int q = qStart; q < qEnd; ++q) {
+ block[k] = visitor.visit(p, q, block[k]);
+ ++k;
+ }
+ }
+ }
+ }
+ return visitor.end();
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public double walkInRowOrder(final RealMatrixPreservingVisitor visitor) {
+ visitor.start(rows, columns, 0, rows - 1, 0, columns - 1);
+ for (int iBlock = 0; iBlock < blockRows; ++iBlock) {
+ final int pStart = iBlock * BLOCK_SIZE;
+ final int pEnd = FastMath.min(pStart + BLOCK_SIZE, rows);
+ for (int p = pStart; p < pEnd; ++p) {
+ for (int jBlock = 0; jBlock < blockColumns; ++jBlock) {
+ final int jWidth = blockWidth(jBlock);
+ final int qStart = jBlock * BLOCK_SIZE;
+ final int qEnd = FastMath.min(qStart + BLOCK_SIZE, columns);
+ final double[] block = blocks[iBlock * blockColumns + jBlock];
+ int k = (p - pStart) * jWidth;
+ for (int q = qStart; q < qEnd; ++q) {
+ visitor.visit(p, q, block[k]);
+ ++k;
+ }
+ }
+ }
+ }
+ return visitor.end();
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public double walkInRowOrder(
+ final RealMatrixChangingVisitor visitor,
+ final int startRow,
+ final int endRow,
+ final int startColumn,
+ final int endColumn)
+ throws OutOfRangeException, NumberIsTooSmallException {
+ MatrixUtils.checkSubMatrixIndex(this, startRow, endRow, startColumn, endColumn);
+ visitor.start(rows, columns, startRow, endRow, startColumn, endColumn);
+ for (int iBlock = startRow / BLOCK_SIZE; iBlock < 1 + endRow / BLOCK_SIZE; ++iBlock) {
+ final int p0 = iBlock * BLOCK_SIZE;
+ final int pStart = FastMath.max(startRow, p0);
+ final int pEnd = FastMath.min((iBlock + 1) * BLOCK_SIZE, 1 + endRow);
+ for (int p = pStart; p < pEnd; ++p) {
+ for (int jBlock = startColumn / BLOCK_SIZE;
+ jBlock < 1 + endColumn / BLOCK_SIZE;
+ ++jBlock) {
+ final int jWidth = blockWidth(jBlock);
+ final int q0 = jBlock * BLOCK_SIZE;
+ final int qStart = FastMath.max(startColumn, q0);
+ final int qEnd = FastMath.min((jBlock + 1) * BLOCK_SIZE, 1 + endColumn);
+ final double[] block = blocks[iBlock * blockColumns + jBlock];
+ int k = (p - p0) * jWidth + qStart - q0;
+ for (int q = qStart; q < qEnd; ++q) {
+ block[k] = visitor.visit(p, q, block[k]);
+ ++k;
+ }
+ }
+ }
+ }
+ return visitor.end();
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public double walkInRowOrder(
+ final RealMatrixPreservingVisitor visitor,
+ final int startRow,
+ final int endRow,
+ final int startColumn,
+ final int endColumn)
+ throws OutOfRangeException, NumberIsTooSmallException {
+ MatrixUtils.checkSubMatrixIndex(this, startRow, endRow, startColumn, endColumn);
+ visitor.start(rows, columns, startRow, endRow, startColumn, endColumn);
+ for (int iBlock = startRow / BLOCK_SIZE; iBlock < 1 + endRow / BLOCK_SIZE; ++iBlock) {
+ final int p0 = iBlock * BLOCK_SIZE;
+ final int pStart = FastMath.max(startRow, p0);
+ final int pEnd = FastMath.min((iBlock + 1) * BLOCK_SIZE, 1 + endRow);
+ for (int p = pStart; p < pEnd; ++p) {
+ for (int jBlock = startColumn / BLOCK_SIZE;
+ jBlock < 1 + endColumn / BLOCK_SIZE;
+ ++jBlock) {
+ final int jWidth = blockWidth(jBlock);
+ final int q0 = jBlock * BLOCK_SIZE;
+ final int qStart = FastMath.max(startColumn, q0);
+ final int qEnd = FastMath.min((jBlock + 1) * BLOCK_SIZE, 1 + endColumn);
+ final double[] block = blocks[iBlock * blockColumns + jBlock];
+ int k = (p - p0) * jWidth + qStart - q0;
+ for (int q = qStart; q < qEnd; ++q) {
+ visitor.visit(p, q, block[k]);
+ ++k;
+ }
+ }
+ }
+ }
+ return visitor.end();
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public double walkInOptimizedOrder(final RealMatrixChangingVisitor visitor) {
+ visitor.start(rows, columns, 0, rows - 1, 0, columns - 1);
+ int blockIndex = 0;
+ for (int iBlock = 0; iBlock < blockRows; ++iBlock) {
+ final int pStart = iBlock * BLOCK_SIZE;
+ final int pEnd = FastMath.min(pStart + BLOCK_SIZE, rows);
+ for (int jBlock = 0; jBlock < blockColumns; ++jBlock) {
+ final int qStart = jBlock * BLOCK_SIZE;
+ final int qEnd = FastMath.min(qStart + BLOCK_SIZE, columns);
+ final double[] block = blocks[blockIndex];
+ int k = 0;
+ for (int p = pStart; p < pEnd; ++p) {
+ for (int q = qStart; q < qEnd; ++q) {
+ block[k] = visitor.visit(p, q, block[k]);
+ ++k;
+ }
+ }
+ ++blockIndex;
+ }
+ }
+ return visitor.end();
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public double walkInOptimizedOrder(final RealMatrixPreservingVisitor visitor) {
+ visitor.start(rows, columns, 0, rows - 1, 0, columns - 1);
+ int blockIndex = 0;
+ for (int iBlock = 0; iBlock < blockRows; ++iBlock) {
+ final int pStart = iBlock * BLOCK_SIZE;
+ final int pEnd = FastMath.min(pStart + BLOCK_SIZE, rows);
+ for (int jBlock = 0; jBlock < blockColumns; ++jBlock) {
+ final int qStart = jBlock * BLOCK_SIZE;
+ final int qEnd = FastMath.min(qStart + BLOCK_SIZE, columns);
+ final double[] block = blocks[blockIndex];
+ int k = 0;
+ for (int p = pStart; p < pEnd; ++p) {
+ for (int q = qStart; q < qEnd; ++q) {
+ visitor.visit(p, q, block[k]);
+ ++k;
+ }
+ }
+ ++blockIndex;
+ }
+ }
+ return visitor.end();
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public double walkInOptimizedOrder(
+ final RealMatrixChangingVisitor visitor,
+ final int startRow,
+ final int endRow,
+ final int startColumn,
+ final int endColumn)
+ throws OutOfRangeException, NumberIsTooSmallException {
+ MatrixUtils.checkSubMatrixIndex(this, startRow, endRow, startColumn, endColumn);
+ visitor.start(rows, columns, startRow, endRow, startColumn, endColumn);
+ for (int iBlock = startRow / BLOCK_SIZE; iBlock < 1 + endRow / BLOCK_SIZE; ++iBlock) {
+ final int p0 = iBlock * BLOCK_SIZE;
+ final int pStart = FastMath.max(startRow, p0);
+ final int pEnd = FastMath.min((iBlock + 1) * BLOCK_SIZE, 1 + endRow);
+ for (int jBlock = startColumn / BLOCK_SIZE;
+ jBlock < 1 + endColumn / BLOCK_SIZE;
+ ++jBlock) {
+ final int jWidth = blockWidth(jBlock);
+ final int q0 = jBlock * BLOCK_SIZE;
+ final int qStart = FastMath.max(startColumn, q0);
+ final int qEnd = FastMath.min((jBlock + 1) * BLOCK_SIZE, 1 + endColumn);
+ final double[] block = blocks[iBlock * blockColumns + jBlock];
+ for (int p = pStart; p < pEnd; ++p) {
+ int k = (p - p0) * jWidth + qStart - q0;
+ for (int q = qStart; q < qEnd; ++q) {
+ block[k] = visitor.visit(p, q, block[k]);
+ ++k;
+ }
+ }
+ }
+ }
+ return visitor.end();
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public double walkInOptimizedOrder(
+ final RealMatrixPreservingVisitor visitor,
+ final int startRow,
+ final int endRow,
+ final int startColumn,
+ final int endColumn)
+ throws OutOfRangeException, NumberIsTooSmallException {
+ MatrixUtils.checkSubMatrixIndex(this, startRow, endRow, startColumn, endColumn);
+ visitor.start(rows, columns, startRow, endRow, startColumn, endColumn);
+ for (int iBlock = startRow / BLOCK_SIZE; iBlock < 1 + endRow / BLOCK_SIZE; ++iBlock) {
+ final int p0 = iBlock * BLOCK_SIZE;
+ final int pStart = FastMath.max(startRow, p0);
+ final int pEnd = FastMath.min((iBlock + 1) * BLOCK_SIZE, 1 + endRow);
+ for (int jBlock = startColumn / BLOCK_SIZE;
+ jBlock < 1 + endColumn / BLOCK_SIZE;
+ ++jBlock) {
+ final int jWidth = blockWidth(jBlock);
+ final int q0 = jBlock * BLOCK_SIZE;
+ final int qStart = FastMath.max(startColumn, q0);
+ final int qEnd = FastMath.min((jBlock + 1) * BLOCK_SIZE, 1 + endColumn);
+ final double[] block = blocks[iBlock * blockColumns + jBlock];
+ for (int p = pStart; p < pEnd; ++p) {
+ int k = (p - p0) * jWidth + qStart - q0;
+ for (int q = qStart; q < qEnd; ++q) {
+ visitor.visit(p, q, block[k]);
+ ++k;
+ }
+ }
+ }
+ }
+ return visitor.end();
+ }
+
+ /**
+ * Get the height of a block.
+ *
+ * @param blockRow row index (in block sense) of the block
+ * @return height (number of rows) of the block
+ */
+ private int blockHeight(final int blockRow) {
+ return (blockRow == blockRows - 1) ? rows - blockRow * BLOCK_SIZE : BLOCK_SIZE;
+ }
+
+ /**
+ * Get the width of a block.
+ *
+ * @param blockColumn column index (in block sense) of the block
+ * @return width (number of columns) of the block
+ */
+ private int blockWidth(final int blockColumn) {
+ return (blockColumn == blockColumns - 1) ? columns - blockColumn * BLOCK_SIZE : BLOCK_SIZE;
+ }
+}
diff --git a/src/main/java/org/apache/commons/math3/linear/CholeskyDecomposition.java b/src/main/java/org/apache/commons/math3/linear/CholeskyDecomposition.java
new file mode 100644
index 0000000..907ace9
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/linear/CholeskyDecomposition.java
@@ -0,0 +1,319 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math3.linear;
+
+import org.apache.commons.math3.exception.DimensionMismatchException;
+import org.apache.commons.math3.util.FastMath;
+
+/**
+ * Calculates the Cholesky decomposition of a matrix.
+ *
+ * <p>The Cholesky decomposition of a real symmetric positive-definite matrix A consists of a lower
+ * triangular matrix L with same size such that: A = LL<sup>T</sup>. In a sense, this is the square
+ * root of A.
+ *
+ * <p>This class is based on the class with similar name from the <a
+ * href="http://math.nist.gov/javanumerics/jama/">JAMA</a> library, with the following changes:
+ *
+ * <ul>
+ * <li>a {@link #getLT() getLT} method has been added,
+ * <li>the {@code isspd} method has been removed, since the constructor of this class throws a
+ * {@link NonPositiveDefiniteMatrixException} when a matrix cannot be decomposed,
+ * <li>a {@link #getDeterminant() getDeterminant} method has been added,
+ * <li>the {@code solve} method has been replaced by a {@link #getSolver() getSolver} method and
+ * the equivalent method provided by the returned {@link DecompositionSolver}.
+ * </ul>
+ *
+ * @see <a href="http://mathworld.wolfram.com/CholeskyDecomposition.html">MathWorld</a>
+ * @see <a href="http://en.wikipedia.org/wiki/Cholesky_decomposition">Wikipedia</a>
+ * @since 2.0 (changed to concrete class in 3.0)
+ */
+public class CholeskyDecomposition {
+ /**
+ * Default threshold above which off-diagonal elements are considered too different and matrix
+ * not symmetric.
+ */
+ public static final double DEFAULT_RELATIVE_SYMMETRY_THRESHOLD = 1.0e-15;
+
+ /**
+ * Default threshold below which diagonal elements are considered null and matrix not positive
+ * definite.
+ */
+ public static final double DEFAULT_ABSOLUTE_POSITIVITY_THRESHOLD = 1.0e-10;
+
+ /** Row-oriented storage for L<sup>T</sup> matrix data. */
+ private double[][] lTData;
+
+ /** Cached value of L. */
+ private RealMatrix cachedL;
+
+ /** Cached value of LT. */
+ private RealMatrix cachedLT;
+
+ /**
+ * Calculates the Cholesky decomposition of the given matrix.
+ *
+ * <p>Calling this constructor is equivalent to call {@link #CholeskyDecomposition(RealMatrix,
+ * double, double)} with the thresholds set to the default values {@link
+ * #DEFAULT_RELATIVE_SYMMETRY_THRESHOLD} and {@link #DEFAULT_ABSOLUTE_POSITIVITY_THRESHOLD}
+ *
+ * @param matrix the matrix to decompose
+ * @throws NonSquareMatrixException if the matrix is not square.
+ * @throws NonSymmetricMatrixException if the matrix is not symmetric.
+ * @throws NonPositiveDefiniteMatrixException if the matrix is not strictly positive definite.
+ * @see #CholeskyDecomposition(RealMatrix, double, double)
+ * @see #DEFAULT_RELATIVE_SYMMETRY_THRESHOLD
+ * @see #DEFAULT_ABSOLUTE_POSITIVITY_THRESHOLD
+ */
+ public CholeskyDecomposition(final RealMatrix matrix) {
+ this(matrix, DEFAULT_RELATIVE_SYMMETRY_THRESHOLD, DEFAULT_ABSOLUTE_POSITIVITY_THRESHOLD);
+ }
+
+ /**
+ * Calculates the Cholesky decomposition of the given matrix.
+ *
+ * @param matrix the matrix to decompose
+ * @param relativeSymmetryThreshold threshold above which off-diagonal elements are considered
+ * too different and matrix not symmetric
+ * @param absolutePositivityThreshold threshold below which diagonal elements are considered
+ * null and matrix not positive definite
+ * @throws NonSquareMatrixException if the matrix is not square.
+ * @throws NonSymmetricMatrixException if the matrix is not symmetric.
+ * @throws NonPositiveDefiniteMatrixException if the matrix is not strictly positive definite.
+ * @see #CholeskyDecomposition(RealMatrix)
+ * @see #DEFAULT_RELATIVE_SYMMETRY_THRESHOLD
+ * @see #DEFAULT_ABSOLUTE_POSITIVITY_THRESHOLD
+ */
+ public CholeskyDecomposition(
+ final RealMatrix matrix,
+ final double relativeSymmetryThreshold,
+ final double absolutePositivityThreshold) {
+ if (!matrix.isSquare()) {
+ throw new NonSquareMatrixException(
+ matrix.getRowDimension(), matrix.getColumnDimension());
+ }
+
+ final int order = matrix.getRowDimension();
+ lTData = matrix.getData();
+ cachedL = null;
+ cachedLT = null;
+
+ // check the matrix before transformation
+ for (int i = 0; i < order; ++i) {
+ final double[] lI = lTData[i];
+
+ // check off-diagonal elements (and reset them to 0)
+ for (int j = i + 1; j < order; ++j) {
+ final double[] lJ = lTData[j];
+ final double lIJ = lI[j];
+ final double lJI = lJ[i];
+ final double maxDelta =
+ relativeSymmetryThreshold
+ * FastMath.max(FastMath.abs(lIJ), FastMath.abs(lJI));
+ if (FastMath.abs(lIJ - lJI) > maxDelta) {
+ throw new NonSymmetricMatrixException(i, j, relativeSymmetryThreshold);
+ }
+ lJ[i] = 0;
+ }
+ }
+
+ // transform the matrix
+ for (int i = 0; i < order; ++i) {
+
+ final double[] ltI = lTData[i];
+
+ // check diagonal element
+ if (ltI[i] <= absolutePositivityThreshold) {
+ throw new NonPositiveDefiniteMatrixException(
+ ltI[i], i, absolutePositivityThreshold);
+ }
+
+ ltI[i] = FastMath.sqrt(ltI[i]);
+ final double inverse = 1.0 / ltI[i];
+
+ for (int q = order - 1; q > i; --q) {
+ ltI[q] *= inverse;
+ final double[] ltQ = lTData[q];
+ for (int p = q; p < order; ++p) {
+ ltQ[p] -= ltI[q] * ltI[p];
+ }
+ }
+ }
+ }
+
+ /**
+ * Returns the matrix L of the decomposition.
+ *
+ * <p>L is an lower-triangular matrix
+ *
+ * @return the L matrix
+ */
+ public RealMatrix getL() {
+ if (cachedL == null) {
+ cachedL = getLT().transpose();
+ }
+ return cachedL;
+ }
+
+ /**
+ * Returns the transpose of the matrix L of the decomposition.
+ *
+ * <p>L<sup>T</sup> is an upper-triangular matrix
+ *
+ * @return the transpose of the matrix L of the decomposition
+ */
+ public RealMatrix getLT() {
+
+ if (cachedLT == null) {
+ cachedLT = MatrixUtils.createRealMatrix(lTData);
+ }
+
+ // return the cached matrix
+ return cachedLT;
+ }
+
+ /**
+ * Return the determinant of the matrix
+ *
+ * @return determinant of the matrix
+ */
+ public double getDeterminant() {
+ double determinant = 1.0;
+ for (int i = 0; i < lTData.length; ++i) {
+ double lTii = lTData[i][i];
+ determinant *= lTii * lTii;
+ }
+ return determinant;
+ }
+
+ /**
+ * Get a solver for finding the A &times; X = B solution in least square sense.
+ *
+ * @return a solver
+ */
+ public DecompositionSolver getSolver() {
+ return new Solver(lTData);
+ }
+
+ /** Specialized solver. */
+ private static class Solver implements DecompositionSolver {
+ /** Row-oriented storage for L<sup>T</sup> matrix data. */
+ private final double[][] lTData;
+
+ /**
+ * Build a solver from decomposed matrix.
+ *
+ * @param lTData row-oriented storage for L<sup>T</sup> matrix data
+ */
+ private Solver(final double[][] lTData) {
+ this.lTData = lTData;
+ }
+
+ /** {@inheritDoc} */
+ public boolean isNonSingular() {
+ // if we get this far, the matrix was positive definite, hence non-singular
+ return true;
+ }
+
+ /** {@inheritDoc} */
+ public RealVector solve(final RealVector b) {
+ final int m = lTData.length;
+ if (b.getDimension() != m) {
+ throw new DimensionMismatchException(b.getDimension(), m);
+ }
+
+ final double[] x = b.toArray();
+
+ // Solve LY = b
+ for (int j = 0; j < m; j++) {
+ final double[] lJ = lTData[j];
+ x[j] /= lJ[j];
+ final double xJ = x[j];
+ for (int i = j + 1; i < m; i++) {
+ x[i] -= xJ * lJ[i];
+ }
+ }
+
+ // Solve LTX = Y
+ for (int j = m - 1; j >= 0; j--) {
+ x[j] /= lTData[j][j];
+ final double xJ = x[j];
+ for (int i = 0; i < j; i++) {
+ x[i] -= xJ * lTData[i][j];
+ }
+ }
+
+ return new ArrayRealVector(x, false);
+ }
+
+ /** {@inheritDoc} */
+ public RealMatrix solve(RealMatrix b) {
+ final int m = lTData.length;
+ if (b.getRowDimension() != m) {
+ throw new DimensionMismatchException(b.getRowDimension(), m);
+ }
+
+ final int nColB = b.getColumnDimension();
+ final double[][] x = b.getData();
+
+ // Solve LY = b
+ for (int j = 0; j < m; j++) {
+ final double[] lJ = lTData[j];
+ final double lJJ = lJ[j];
+ final double[] xJ = x[j];
+ for (int k = 0; k < nColB; ++k) {
+ xJ[k] /= lJJ;
+ }
+ for (int i = j + 1; i < m; i++) {
+ final double[] xI = x[i];
+ final double lJI = lJ[i];
+ for (int k = 0; k < nColB; ++k) {
+ xI[k] -= xJ[k] * lJI;
+ }
+ }
+ }
+
+ // Solve LTX = Y
+ for (int j = m - 1; j >= 0; j--) {
+ final double lJJ = lTData[j][j];
+ final double[] xJ = x[j];
+ for (int k = 0; k < nColB; ++k) {
+ xJ[k] /= lJJ;
+ }
+ for (int i = 0; i < j; i++) {
+ final double[] xI = x[i];
+ final double lIJ = lTData[i][j];
+ for (int k = 0; k < nColB; ++k) {
+ xI[k] -= xJ[k] * lIJ;
+ }
+ }
+ }
+
+ return new Array2DRowRealMatrix(x);
+ }
+
+ /**
+ * Get the inverse of the decomposed matrix.
+ *
+ * @return the inverse matrix.
+ */
+ public RealMatrix getInverse() {
+ return solve(MatrixUtils.createRealIdentityMatrix(lTData.length));
+ }
+ }
+}
diff --git a/src/main/java/org/apache/commons/math3/linear/ConjugateGradient.java b/src/main/java/org/apache/commons/math3/linear/ConjugateGradient.java
new file mode 100644
index 0000000..61dab67
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/linear/ConjugateGradient.java
@@ -0,0 +1,231 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math3.linear;
+
+import org.apache.commons.math3.exception.DimensionMismatchException;
+import org.apache.commons.math3.exception.MaxCountExceededException;
+import org.apache.commons.math3.exception.NullArgumentException;
+import org.apache.commons.math3.exception.util.ExceptionContext;
+import org.apache.commons.math3.util.IterationManager;
+
+/**
+ * This is an implementation of the conjugate gradient method for {@link RealLinearOperator}. It
+ * follows closely the template by <a href="#BARR1994">Barrett et al. (1994)</a> (figure 2.5). The
+ * linear system at hand is A &middot; x = b, and the residual is r = b - A &middot; x.
+ *
+ * <h3><a id="stopcrit">Default stopping criterion</a></h3>
+ *
+ * <p>A default stopping criterion is implemented. The iterations stop when || r || &le; &delta; ||
+ * b ||, where b is the right-hand side vector, r the current estimate of the residual, and &delta;
+ * a user-specified tolerance. It should be noted that r is the so-called <em>updated</em> residual,
+ * which might differ from the true residual due to rounding-off errors (see e.g. <a
+ * href="#STRA2002">Strakos and Tichy, 2002</a>).
+ *
+ * <h3>Iteration count</h3>
+ *
+ * <p>In the present context, an iteration should be understood as one evaluation of the
+ * matrix-vector product A &middot; x. The initialization phase therefore counts as one iteration.
+ *
+ * <h3><a id="context">Exception context</a></h3>
+ *
+ * <p>Besides standard {@link DimensionMismatchException}, this class might throw {@link
+ * NonPositiveDefiniteOperatorException} if the linear operator or the preconditioner are not
+ * positive definite. In this case, the {@link ExceptionContext} provides some more information
+ *
+ * <ul>
+ * <li>key {@code "operator"} points to the offending linear operator, say L,
+ * <li>key {@code "vector"} points to the offending vector, say x, such that x<sup>T</sup>
+ * &middot; L &middot; x < 0.
+ * </ul>
+ *
+ * <h3>References</h3>
+ *
+ * <dl>
+ * <dt><a id="BARR1994">Barret et al. (1994)</a>
+ * <dd>R. Barrett, M. Berry, T. F. Chan, J. Demmel, J. M. Donato, J. Dongarra, V. Eijkhout, R.
+ * Pozo, C. Romine and H. Van der Vorst, <a
+ * href="http://www.netlib.org/linalg/html_templates/Templates.html"><em> Templates for the
+ * Solution of Linear Systems: Building Blocks for Iterative Methods</em></a>, SIAM
+ * <dt><a id="STRA2002">Strakos and Tichy (2002)
+ * <dt>
+ * <dd>Z. Strakos and P. Tichy, <a
+ * href="http://etna.mcs.kent.edu/vol.13.2002/pp56-80.dir/pp56-80.pdf"><em>On error estimation
+ * in the conjugate gradient method and why it works in finite precision
+ * computations</em></a>, Electronic Transactions on Numerical Analysis 13: 56-80, 2002
+ * </dl>
+ *
+ * @since 3.0
+ */
+public class ConjugateGradient extends PreconditionedIterativeLinearSolver {
+
+ /** Key for the <a href="#context">exception context</a>. */
+ public static final String OPERATOR = "operator";
+
+ /** Key for the <a href="#context">exception context</a>. */
+ public static final String VECTOR = "vector";
+
+ /** {@code true} if positive-definiteness of matrix and preconditioner should be checked. */
+ private boolean check;
+
+ /** The value of &delta;, for the default stopping criterion. */
+ private final double delta;
+
+ /**
+ * Creates a new instance of this class, with <a href="#stopcrit">default stopping
+ * criterion</a>.
+ *
+ * @param maxIterations the maximum number of iterations
+ * @param delta the &delta; parameter for the default stopping criterion
+ * @param check {@code true} if positive definiteness of both matrix and preconditioner should
+ * be checked
+ */
+ public ConjugateGradient(final int maxIterations, final double delta, final boolean check) {
+ super(maxIterations);
+ this.delta = delta;
+ this.check = check;
+ }
+
+ /**
+ * Creates a new instance of this class, with <a href="#stopcrit">default stopping criterion</a>
+ * and custom iteration manager.
+ *
+ * @param manager the custom iteration manager
+ * @param delta the &delta; parameter for the default stopping criterion
+ * @param check {@code true} if positive definiteness of both matrix and preconditioner should
+ * be checked
+ * @throws NullArgumentException if {@code manager} is {@code null}
+ */
+ public ConjugateGradient(
+ final IterationManager manager, final double delta, final boolean check)
+ throws NullArgumentException {
+ super(manager);
+ this.delta = delta;
+ this.check = check;
+ }
+
+ /**
+ * Returns {@code true} if positive-definiteness should be checked for both matrix and
+ * preconditioner.
+ *
+ * @return {@code true} if the tests are to be performed
+ */
+ public final boolean getCheck() {
+ return check;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @throws NonPositiveDefiniteOperatorException if {@code a} or {@code m} is not positive
+ * definite
+ */
+ @Override
+ public RealVector solveInPlace(
+ final RealLinearOperator a,
+ final RealLinearOperator m,
+ final RealVector b,
+ final RealVector x0)
+ throws NullArgumentException,
+ NonPositiveDefiniteOperatorException,
+ NonSquareOperatorException,
+ DimensionMismatchException,
+ MaxCountExceededException {
+ checkParameters(a, m, b, x0);
+ final IterationManager manager = getIterationManager();
+ // Initialization of default stopping criterion
+ manager.resetIterationCount();
+ final double rmax = delta * b.getNorm();
+ final RealVector bro = RealVector.unmodifiableRealVector(b);
+
+ // Initialization phase counts as one iteration.
+ manager.incrementIterationCount();
+ // p and x are constructed as copies of x0, since presumably, the type
+ // of x is optimized for the calculation of the matrix-vector product
+ // A.x.
+ final RealVector x = x0;
+ final RealVector xro = RealVector.unmodifiableRealVector(x);
+ final RealVector p = x.copy();
+ RealVector q = a.operate(p);
+
+ final RealVector r = b.combine(1, -1, q);
+ final RealVector rro = RealVector.unmodifiableRealVector(r);
+ double rnorm = r.getNorm();
+ RealVector z;
+ if (m == null) {
+ z = r;
+ } else {
+ z = null;
+ }
+ IterativeLinearSolverEvent evt;
+ evt =
+ new DefaultIterativeLinearSolverEvent(
+ this, manager.getIterations(), xro, bro, rro, rnorm);
+ manager.fireInitializationEvent(evt);
+ if (rnorm <= rmax) {
+ manager.fireTerminationEvent(evt);
+ return x;
+ }
+ double rhoPrev = 0.;
+ while (true) {
+ manager.incrementIterationCount();
+ evt =
+ new DefaultIterativeLinearSolverEvent(
+ this, manager.getIterations(), xro, bro, rro, rnorm);
+ manager.fireIterationStartedEvent(evt);
+ if (m != null) {
+ z = m.operate(r);
+ }
+ final double rhoNext = r.dotProduct(z);
+ if (check && (rhoNext <= 0.)) {
+ final NonPositiveDefiniteOperatorException e;
+ e = new NonPositiveDefiniteOperatorException();
+ final ExceptionContext context = e.getContext();
+ context.setValue(OPERATOR, m);
+ context.setValue(VECTOR, r);
+ throw e;
+ }
+ if (manager.getIterations() == 2) {
+ p.setSubVector(0, z);
+ } else {
+ p.combineToSelf(rhoNext / rhoPrev, 1., z);
+ }
+ q = a.operate(p);
+ final double pq = p.dotProduct(q);
+ if (check && (pq <= 0.)) {
+ final NonPositiveDefiniteOperatorException e;
+ e = new NonPositiveDefiniteOperatorException();
+ final ExceptionContext context = e.getContext();
+ context.setValue(OPERATOR, a);
+ context.setValue(VECTOR, p);
+ throw e;
+ }
+ final double alpha = rhoNext / pq;
+ x.combineToSelf(1., alpha, p);
+ r.combineToSelf(1., -alpha, q);
+ rhoPrev = rhoNext;
+ rnorm = r.getNorm();
+ evt =
+ new DefaultIterativeLinearSolverEvent(
+ this, manager.getIterations(), xro, bro, rro, rnorm);
+ manager.fireIterationPerformedEvent(evt);
+ if (rnorm <= rmax) {
+ manager.fireTerminationEvent(evt);
+ return x;
+ }
+ }
+ }
+}
diff --git a/src/main/java/org/apache/commons/math3/linear/DecompositionSolver.java b/src/main/java/org/apache/commons/math3/linear/DecompositionSolver.java
new file mode 100644
index 0000000..a7e1777
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/linear/DecompositionSolver.java
@@ -0,0 +1,97 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math3.linear;
+
+/**
+ * Interface handling decomposition algorithms that can solve A &times; X = B.
+ *
+ * <p>Decomposition algorithms decompose an A matrix has a product of several specific matrices from
+ * which they can solve A &times; X = B in least squares sense: they find X such that ||A &times; X
+ * - B|| is minimal.
+ *
+ * <p>Some solvers like {@link LUDecomposition} can only find the solution for square matrices and
+ * when the solution is an exact linear solution, i.e. when ||A &times; X - B|| is exactly 0. Other
+ * solvers can also find solutions with non-square matrix A and with non-null minimal norm. If an
+ * exact linear solution exists it is also the minimal norm solution.
+ *
+ * @since 2.0
+ */
+public interface DecompositionSolver {
+
+ /**
+ * Solve the linear equation A &times; X = B for matrices A.
+ *
+ * <p>The A matrix is implicit, it is provided by the underlying decomposition algorithm.
+ *
+ * @param b right-hand side of the equation A &times; X = B
+ * @return a vector X that minimizes the two norm of A &times; X - B
+ * @throws org.apache.commons.math3.exception.DimensionMismatchException if the matrices
+ * dimensions do not match.
+ * @throws SingularMatrixException if the decomposed matrix is singular.
+ */
+ RealVector solve(final RealVector b) throws SingularMatrixException;
+
+ /**
+ * Solve the linear equation A &times; X = B for matrices A.
+ *
+ * <p>The A matrix is implicit, it is provided by the underlying decomposition algorithm.
+ *
+ * @param b right-hand side of the equation A &times; X = B
+ * @return a matrix X that minimizes the two norm of A &times; X - B
+ * @throws org.apache.commons.math3.exception.DimensionMismatchException if the matrices
+ * dimensions do not match.
+ * @throws SingularMatrixException if the decomposed matrix is singular.
+ */
+ RealMatrix solve(final RealMatrix b) throws SingularMatrixException;
+
+ /**
+ * Check if the decomposed matrix is non-singular.
+ *
+ * @return true if the decomposed matrix is non-singular.
+ */
+ boolean isNonSingular();
+
+ /**
+ * Get the <a
+ * href="http://en.wikipedia.org/wiki/Moore%E2%80%93Penrose_pseudoinverse">pseudo-inverse</a> of
+ * the decomposed matrix.
+ *
+ * <p><em>This is equal to the inverse of the decomposed matrix, if such an inverse exists.</em>
+ *
+ * <p>If no such inverse exists, then the result has properties that resemble that of an
+ * inverse.
+ *
+ * <p>In particular, in this case, if the decomposed matrix is A, then the system of equations
+ * \( A x = b \) may have no solutions, or many. If it has no solutions, then the pseudo-inverse
+ * \( A^+ \) gives the "closest" solution \( z = A^+ b \), meaning \( \left \| A z - b \right
+ * \|_2 \) is minimized. If there are many solutions, then \( z = A^+ b \) is the smallest
+ * solution, meaning \( \left \| z \right \|_2 \) is minimized.
+ *
+ * <p>Note however that some decompositions cannot compute a pseudo-inverse for all matrices.
+ * For example, the {@link LUDecomposition} is not defined for non-square matrices to begin
+ * with. The {@link QRDecomposition} can operate on non-square matrices, but will throw {@link
+ * SingularMatrixException} if the decomposed matrix is singular. Refer to the javadoc of
+ * specific decomposition implementations for more details.
+ *
+ * @return pseudo-inverse matrix (which is the inverse, if it exists), if the decomposition can
+ * pseudo-invert the decomposed matrix
+ * @throws SingularMatrixException if the decomposed matrix is singular and the decomposition
+ * can not compute a pseudo-inverse
+ */
+ RealMatrix getInverse() throws SingularMatrixException;
+}
diff --git a/src/main/java/org/apache/commons/math3/linear/DefaultFieldMatrixChangingVisitor.java b/src/main/java/org/apache/commons/math3/linear/DefaultFieldMatrixChangingVisitor.java
new file mode 100644
index 0000000..dbf309e
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/linear/DefaultFieldMatrixChangingVisitor.java
@@ -0,0 +1,58 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math3.linear;
+
+import org.apache.commons.math3.FieldElement;
+
+/**
+ * Default implementation of the {@link FieldMatrixChangingVisitor} interface.
+ *
+ * <p>This class is a convenience to create custom visitors without defining all methods. This class
+ * provides default implementations that do nothing.
+ *
+ * @param <T> the type of the field elements
+ * @since 2.0
+ */
+public class DefaultFieldMatrixChangingVisitor<T extends FieldElement<T>>
+ implements FieldMatrixChangingVisitor<T> {
+ /** Zero element of the field. */
+ private final T zero;
+
+ /**
+ * Build a new instance.
+ *
+ * @param zero additive identity of the field
+ */
+ public DefaultFieldMatrixChangingVisitor(final T zero) {
+ this.zero = zero;
+ }
+
+ /** {@inheritDoc} */
+ public void start(
+ int rows, int columns, int startRow, int endRow, int startColumn, int endColumn) {}
+
+ /** {@inheritDoc} */
+ public T visit(int row, int column, T value) {
+ return value;
+ }
+
+ /** {@inheritDoc} */
+ public T end() {
+ return zero;
+ }
+}
diff --git a/src/main/java/org/apache/commons/math3/linear/DefaultFieldMatrixPreservingVisitor.java b/src/main/java/org/apache/commons/math3/linear/DefaultFieldMatrixPreservingVisitor.java
new file mode 100644
index 0000000..ed11851
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/linear/DefaultFieldMatrixPreservingVisitor.java
@@ -0,0 +1,56 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math3.linear;
+
+import org.apache.commons.math3.FieldElement;
+
+/**
+ * Default implementation of the {@link FieldMatrixPreservingVisitor} interface.
+ *
+ * <p>This class is a convenience to create custom visitors without defining all methods. This class
+ * provides default implementations that do nothing.
+ *
+ * @param <T> the type of the field elements
+ * @since 2.0
+ */
+public class DefaultFieldMatrixPreservingVisitor<T extends FieldElement<T>>
+ implements FieldMatrixPreservingVisitor<T> {
+ /** Zero element of the field. */
+ private final T zero;
+
+ /**
+ * Build a new instance.
+ *
+ * @param zero additive identity of the field
+ */
+ public DefaultFieldMatrixPreservingVisitor(final T zero) {
+ this.zero = zero;
+ }
+
+ /** {@inheritDoc} */
+ public void start(
+ int rows, int columns, int startRow, int endRow, int startColumn, int endColumn) {}
+
+ /** {@inheritDoc} */
+ public void visit(int row, int column, T value) {}
+
+ /** {@inheritDoc} */
+ public T end() {
+ return zero;
+ }
+}
diff --git a/src/main/java/org/apache/commons/math3/linear/DefaultIterativeLinearSolverEvent.java b/src/main/java/org/apache/commons/math3/linear/DefaultIterativeLinearSolverEvent.java
new file mode 100644
index 0000000..fee346e
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/linear/DefaultIterativeLinearSolverEvent.java
@@ -0,0 +1,142 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math3.linear;
+
+import org.apache.commons.math3.exception.MathUnsupportedOperationException;
+
+/** A default concrete implementation of the abstract class {@link IterativeLinearSolverEvent}. */
+public class DefaultIterativeLinearSolverEvent extends IterativeLinearSolverEvent {
+
+ /** */
+ private static final long serialVersionUID = 20120129L;
+
+ /** The right-hand side vector. */
+ private final RealVector b;
+
+ /** The current estimate of the residual. */
+ private final RealVector r;
+
+ /** The current estimate of the norm of the residual. */
+ private final double rnorm;
+
+ /** The current estimate of the solution. */
+ private final RealVector x;
+
+ /**
+ * Creates a new instance of this class. This implementation does <em>not</em> deep copy the
+ * specified vectors {@code x}, {@code b}, {@code r}. Therefore the user must make sure that
+ * these vectors are either unmodifiable views or deep copies of the same vectors actually used
+ * by the {@code source}. Failure to do so may compromise subsequent iterations of the {@code
+ * source}. If the residual vector {@code r} is {@code null}, then {@link #getResidual()} throws
+ * a {@link MathUnsupportedOperationException}, and {@link #providesResidual()} returns {@code
+ * false}.
+ *
+ * @param source the iterative solver which fired this event
+ * @param iterations the number of iterations performed at the time {@code this} event is
+ * created
+ * @param x the current estimate of the solution
+ * @param b the right-hand side vector
+ * @param r the current estimate of the residual (can be {@code null})
+ * @param rnorm the norm of the current estimate of the residual
+ */
+ public DefaultIterativeLinearSolverEvent(
+ final Object source,
+ final int iterations,
+ final RealVector x,
+ final RealVector b,
+ final RealVector r,
+ final double rnorm) {
+ super(source, iterations);
+ this.x = x;
+ this.b = b;
+ this.r = r;
+ this.rnorm = rnorm;
+ }
+
+ /**
+ * Creates a new instance of this class. This implementation does <em>not</em> deep copy the
+ * specified vectors {@code x}, {@code b}. Therefore the user must make sure that these vectors
+ * are either unmodifiable views or deep copies of the same vectors actually used by the {@code
+ * source}. Failure to do so may compromise subsequent iterations of the {@code source}.
+ * Callling {@link #getResidual()} on instances returned by this constructor throws a {@link
+ * MathUnsupportedOperationException}, while {@link #providesResidual()} returns {@code false}.
+ *
+ * @param source the iterative solver which fired this event
+ * @param iterations the number of iterations performed at the time {@code this} event is
+ * created
+ * @param x the current estimate of the solution
+ * @param b the right-hand side vector
+ * @param rnorm the norm of the current estimate of the residual
+ */
+ public DefaultIterativeLinearSolverEvent(
+ final Object source,
+ final int iterations,
+ final RealVector x,
+ final RealVector b,
+ final double rnorm) {
+ super(source, iterations);
+ this.x = x;
+ this.b = b;
+ this.r = null;
+ this.rnorm = rnorm;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public double getNormOfResidual() {
+ return rnorm;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * <p>This implementation throws an {@link MathUnsupportedOperationException} if no residual
+ * vector {@code r} was provided at construction time.
+ */
+ @Override
+ public RealVector getResidual() {
+ if (r != null) {
+ return r;
+ }
+ throw new MathUnsupportedOperationException();
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public RealVector getRightHandSideVector() {
+ return b;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public RealVector getSolution() {
+ return x;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * <p>This implementation returns {@code true} if a non-{@code null} value was specified for the
+ * residual vector {@code r} at construction time.
+ *
+ * @return {@code true} if {@code r != null}
+ */
+ @Override
+ public boolean providesResidual() {
+ return r != null;
+ }
+}
diff --git a/src/main/java/org/apache/commons/math3/linear/DefaultRealMatrixChangingVisitor.java b/src/main/java/org/apache/commons/math3/linear/DefaultRealMatrixChangingVisitor.java
new file mode 100644
index 0000000..ce561cb
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/linear/DefaultRealMatrixChangingVisitor.java
@@ -0,0 +1,42 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math3.linear;
+
+/**
+ * Default implementation of the {@link RealMatrixChangingVisitor} interface.
+ *
+ * <p>This class is a convenience to create custom visitors without defining all methods. This class
+ * provides default implementations that do nothing.
+ *
+ * @since 2.0
+ */
+public class DefaultRealMatrixChangingVisitor implements RealMatrixChangingVisitor {
+ /** {@inheritDoc} */
+ public void start(
+ int rows, int columns, int startRow, int endRow, int startColumn, int endColumn) {}
+
+ /** {@inheritDoc} */
+ public double visit(int row, int column, double value) {
+ return value;
+ }
+
+ /** {@inheritDoc} */
+ public double end() {
+ return 0;
+ }
+}
diff --git a/src/main/java/org/apache/commons/math3/linear/DefaultRealMatrixPreservingVisitor.java b/src/main/java/org/apache/commons/math3/linear/DefaultRealMatrixPreservingVisitor.java
new file mode 100644
index 0000000..bd16ad3
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/linear/DefaultRealMatrixPreservingVisitor.java
@@ -0,0 +1,40 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math3.linear;
+
+/**
+ * Default implementation of the {@link RealMatrixPreservingVisitor} interface.
+ *
+ * <p>This class is a convenience to create custom visitors without defining all methods. This class
+ * provides default implementations that do nothing.
+ *
+ * @since 2.0
+ */
+public class DefaultRealMatrixPreservingVisitor implements RealMatrixPreservingVisitor {
+ /** {@inheritDoc} */
+ public void start(
+ int rows, int columns, int startRow, int endRow, int startColumn, int endColumn) {}
+
+ /** {@inheritDoc} */
+ public void visit(int row, int column, double value) {}
+
+ /** {@inheritDoc} */
+ public double end() {
+ return 0;
+ }
+}
diff --git a/src/main/java/org/apache/commons/math3/linear/DiagonalMatrix.java b/src/main/java/org/apache/commons/math3/linear/DiagonalMatrix.java
new file mode 100644
index 0000000..6246700
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/linear/DiagonalMatrix.java
@@ -0,0 +1,353 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math3.linear;
+
+import org.apache.commons.math3.exception.DimensionMismatchException;
+import org.apache.commons.math3.exception.NotStrictlyPositiveException;
+import org.apache.commons.math3.exception.NullArgumentException;
+import org.apache.commons.math3.exception.NumberIsTooLargeException;
+import org.apache.commons.math3.exception.OutOfRangeException;
+import org.apache.commons.math3.util.FastMath;
+import org.apache.commons.math3.util.MathUtils;
+import org.apache.commons.math3.util.Precision;
+
+import java.io.Serializable;
+
+/**
+ * Implementation of a diagonal matrix.
+ *
+ * @since 3.1.1
+ */
+public class DiagonalMatrix extends AbstractRealMatrix implements Serializable {
+ /** Serializable version identifier. */
+ private static final long serialVersionUID = 20121229L;
+
+ /** Entries of the diagonal. */
+ private final double[] data;
+
+ /**
+ * Creates a matrix with the supplied dimension.
+ *
+ * @param dimension Number of rows and columns in the new matrix.
+ * @throws NotStrictlyPositiveException if the dimension is not positive.
+ */
+ public DiagonalMatrix(final int dimension) throws NotStrictlyPositiveException {
+ super(dimension, dimension);
+ data = new double[dimension];
+ }
+
+ /**
+ * Creates a matrix using the input array as the underlying data. <br>
+ * The input array is copied, not referenced.
+ *
+ * @param d Data for the new matrix.
+ */
+ public DiagonalMatrix(final double[] d) {
+ this(d, true);
+ }
+
+ /**
+ * Creates a matrix using the input array as the underlying data. <br>
+ * If an array is created specially in order to be embedded in a this instance and not used
+ * directly, the {@code copyArray} may be set to {@code false}. This will prevent the copying
+ * and improve performance as no new array will be built and no data will be copied.
+ *
+ * @param d Data for new matrix.
+ * @param copyArray if {@code true}, the input array will be copied, otherwise it will be
+ * referenced.
+ * @exception NullArgumentException if d is null
+ */
+ public DiagonalMatrix(final double[] d, final boolean copyArray) throws NullArgumentException {
+ MathUtils.checkNotNull(d);
+ data = copyArray ? d.clone() : d;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @throws DimensionMismatchException if the requested dimensions are not equal.
+ */
+ @Override
+ public RealMatrix createMatrix(final int rowDimension, final int columnDimension)
+ throws NotStrictlyPositiveException, DimensionMismatchException {
+ if (rowDimension != columnDimension) {
+ throw new DimensionMismatchException(rowDimension, columnDimension);
+ }
+
+ return new DiagonalMatrix(rowDimension);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public RealMatrix copy() {
+ return new DiagonalMatrix(data);
+ }
+
+ /**
+ * Compute the sum of {@code this} and {@code m}.
+ *
+ * @param m Matrix to be added.
+ * @return {@code this + m}.
+ * @throws MatrixDimensionMismatchException if {@code m} is not the same size as {@code this}.
+ */
+ public DiagonalMatrix add(final DiagonalMatrix m) throws MatrixDimensionMismatchException {
+ // Safety check.
+ MatrixUtils.checkAdditionCompatible(this, m);
+
+ final int dim = getRowDimension();
+ final double[] outData = new double[dim];
+ for (int i = 0; i < dim; i++) {
+ outData[i] = data[i] + m.data[i];
+ }
+
+ return new DiagonalMatrix(outData, false);
+ }
+
+ /**
+ * Returns {@code this} minus {@code m}.
+ *
+ * @param m Matrix to be subtracted.
+ * @return {@code this - m}
+ * @throws MatrixDimensionMismatchException if {@code m} is not the same size as {@code this}.
+ */
+ public DiagonalMatrix subtract(final DiagonalMatrix m) throws MatrixDimensionMismatchException {
+ MatrixUtils.checkSubtractionCompatible(this, m);
+
+ final int dim = getRowDimension();
+ final double[] outData = new double[dim];
+ for (int i = 0; i < dim; i++) {
+ outData[i] = data[i] - m.data[i];
+ }
+
+ return new DiagonalMatrix(outData, false);
+ }
+
+ /**
+ * Returns the result of postmultiplying {@code this} by {@code m}.
+ *
+ * @param m matrix to postmultiply by
+ * @return {@code this * m}
+ * @throws DimensionMismatchException if {@code columnDimension(this) != rowDimension(m)}
+ */
+ public DiagonalMatrix multiply(final DiagonalMatrix m) throws DimensionMismatchException {
+ MatrixUtils.checkMultiplicationCompatible(this, m);
+
+ final int dim = getRowDimension();
+ final double[] outData = new double[dim];
+ for (int i = 0; i < dim; i++) {
+ outData[i] = data[i] * m.data[i];
+ }
+
+ return new DiagonalMatrix(outData, false);
+ }
+
+ /**
+ * Returns the result of postmultiplying {@code this} by {@code m}.
+ *
+ * @param m matrix to postmultiply by
+ * @return {@code this * m}
+ * @throws DimensionMismatchException if {@code columnDimension(this) != rowDimension(m)}
+ */
+ @Override
+ public RealMatrix multiply(final RealMatrix m) throws DimensionMismatchException {
+ if (m instanceof DiagonalMatrix) {
+ return multiply((DiagonalMatrix) m);
+ } else {
+ MatrixUtils.checkMultiplicationCompatible(this, m);
+ final int nRows = m.getRowDimension();
+ final int nCols = m.getColumnDimension();
+ final double[][] product = new double[nRows][nCols];
+ for (int r = 0; r < nRows; r++) {
+ for (int c = 0; c < nCols; c++) {
+ product[r][c] = data[r] * m.getEntry(r, c);
+ }
+ }
+ return new Array2DRowRealMatrix(product, false);
+ }
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public double[][] getData() {
+ final int dim = getRowDimension();
+ final double[][] out = new double[dim][dim];
+
+ for (int i = 0; i < dim; i++) {
+ out[i][i] = data[i];
+ }
+
+ return out;
+ }
+
+ /**
+ * Gets a reference to the underlying data array.
+ *
+ * @return 1-dimensional array of entries.
+ */
+ public double[] getDataRef() {
+ return data;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public double getEntry(final int row, final int column) throws OutOfRangeException {
+ MatrixUtils.checkMatrixIndex(this, row, column);
+ return row == column ? data[row] : 0;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @throws NumberIsTooLargeException if {@code row != column} and value is non-zero.
+ */
+ @Override
+ public void setEntry(final int row, final int column, final double value)
+ throws OutOfRangeException, NumberIsTooLargeException {
+ if (row == column) {
+ MatrixUtils.checkRowIndex(this, row);
+ data[row] = value;
+ } else {
+ ensureZero(value);
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @throws NumberIsTooLargeException if {@code row != column} and increment is non-zero.
+ */
+ @Override
+ public void addToEntry(final int row, final int column, final double increment)
+ throws OutOfRangeException, NumberIsTooLargeException {
+ if (row == column) {
+ MatrixUtils.checkRowIndex(this, row);
+ data[row] += increment;
+ } else {
+ ensureZero(increment);
+ }
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void multiplyEntry(final int row, final int column, final double factor)
+ throws OutOfRangeException {
+ // we don't care about non-diagonal elements for multiplication
+ if (row == column) {
+ MatrixUtils.checkRowIndex(this, row);
+ data[row] *= factor;
+ }
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public int getRowDimension() {
+ return data.length;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public int getColumnDimension() {
+ return data.length;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public double[] operate(final double[] v) throws DimensionMismatchException {
+ return multiply(new DiagonalMatrix(v, false)).getDataRef();
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public double[] preMultiply(final double[] v) throws DimensionMismatchException {
+ return operate(v);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public RealVector preMultiply(final RealVector v) throws DimensionMismatchException {
+ final double[] vectorData;
+ if (v instanceof ArrayRealVector) {
+ vectorData = ((ArrayRealVector) v).getDataRef();
+ } else {
+ vectorData = v.toArray();
+ }
+ return MatrixUtils.createRealVector(preMultiply(vectorData));
+ }
+
+ /**
+ * Ensure a value is zero.
+ *
+ * @param value value to check
+ * @exception NumberIsTooLargeException if value is not zero
+ */
+ private void ensureZero(final double value) throws NumberIsTooLargeException {
+ if (!Precision.equals(0.0, value, 1)) {
+ throw new NumberIsTooLargeException(FastMath.abs(value), 0, true);
+ }
+ }
+
+ /**
+ * Computes the inverse of this diagonal matrix.
+ *
+ * <p>Note: this method will use a singularity threshold of 0, use {@link #inverse(double)} if a
+ * different threshold is needed.
+ *
+ * @return the inverse of {@code m}
+ * @throws SingularMatrixException if the matrix is singular
+ * @since 3.3
+ */
+ public DiagonalMatrix inverse() throws SingularMatrixException {
+ return inverse(0);
+ }
+
+ /**
+ * Computes the inverse of this diagonal matrix.
+ *
+ * @param threshold Singularity threshold.
+ * @return the inverse of {@code m}
+ * @throws SingularMatrixException if the matrix is singular
+ * @since 3.3
+ */
+ public DiagonalMatrix inverse(double threshold) throws SingularMatrixException {
+ if (isSingular(threshold)) {
+ throw new SingularMatrixException();
+ }
+
+ final double[] result = new double[data.length];
+ for (int i = 0; i < data.length; i++) {
+ result[i] = 1.0 / data[i];
+ }
+ return new DiagonalMatrix(result, false);
+ }
+
+ /**
+ * Returns whether this diagonal matrix is singular, i.e. any diagonal entry is equal to {@code
+ * 0} within the given threshold.
+ *
+ * @param threshold Singularity threshold.
+ * @return {@code true} if the matrix is singular, {@code false} otherwise
+ * @since 3.3
+ */
+ public boolean isSingular(double threshold) {
+ for (int i = 0; i < data.length; i++) {
+ if (Precision.equals(data[i], 0.0, threshold)) {
+ return true;
+ }
+ }
+ return false;
+ }
+}
diff --git a/src/main/java/org/apache/commons/math3/linear/EigenDecomposition.java b/src/main/java/org/apache/commons/math3/linear/EigenDecomposition.java
new file mode 100644
index 0000000..505897f
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/linear/EigenDecomposition.java
@@ -0,0 +1,968 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math3.linear;
+
+import org.apache.commons.math3.complex.Complex;
+import org.apache.commons.math3.exception.DimensionMismatchException;
+import org.apache.commons.math3.exception.MathArithmeticException;
+import org.apache.commons.math3.exception.MathUnsupportedOperationException;
+import org.apache.commons.math3.exception.MaxCountExceededException;
+import org.apache.commons.math3.exception.util.LocalizedFormats;
+import org.apache.commons.math3.util.FastMath;
+import org.apache.commons.math3.util.Precision;
+
+/**
+ * Calculates the eigen decomposition of a real matrix.
+ *
+ * <p>The eigen decomposition of matrix A is a set of two matrices: V and D such that A = V &times;
+ * D &times; V<sup>T</sup>. A, V and D are all m &times; m matrices.
+ *
+ * <p>This class is similar in spirit to the <code>EigenvalueDecomposition</code> class from the <a
+ * href="http://math.nist.gov/javanumerics/jama/">JAMA</a> library, with the following changes:
+ *
+ * <ul>
+ * <li>a {@link #getVT() getVt} method has been added,
+ * <li>two {@link #getRealEigenvalue(int) getRealEigenvalue} and {@link #getImagEigenvalue(int)
+ * getImagEigenvalue} methods to pick up a single eigenvalue have been added,
+ * <li>a {@link #getEigenvector(int) getEigenvector} method to pick up a single eigenvector has
+ * been added,
+ * <li>a {@link #getDeterminant() getDeterminant} method has been added.
+ * <li>a {@link #getSolver() getSolver} method has been added.
+ * </ul>
+ *
+ * <p>As of 3.1, this class supports general real matrices (both symmetric and non-symmetric):
+ *
+ * <p>If A is symmetric, then A = V*D*V' where the eigenvalue matrix D is diagonal and the
+ * eigenvector matrix V is orthogonal, i.e. A = V.multiply(D.multiply(V.transpose())) and
+ * V.multiply(V.transpose()) equals the identity matrix.
+ *
+ * <p>If A is not symmetric, then the eigenvalue matrix D is block diagonal with the real
+ * eigenvalues in 1-by-1 blocks and any complex eigenvalues, lambda + i*mu, in 2-by-2 blocks:
+ *
+ * <pre>
+ * [lambda, mu ]
+ * [ -mu, lambda]
+ * </pre>
+ *
+ * The columns of V represent the eigenvectors in the sense that A*V = V*D, i.e. A.multiply(V)
+ * equals V.multiply(D). The matrix V may be badly conditioned, or even singular, so the validity of
+ * the equation A = V*D*inverse(V) depends upon the condition of V.
+ *
+ * <p>This implementation is based on the paper by A. Drubrulle, R.S. Martin and J.H. Wilkinson "The
+ * Implicit QL Algorithm" in Wilksinson and Reinsch (1971) Handbook for automatic computation, vol.
+ * 2, Linear algebra, Springer-Verlag, New-York
+ *
+ * @see <a href="http://mathworld.wolfram.com/EigenDecomposition.html">MathWorld</a>
+ * @see <a href="http://en.wikipedia.org/wiki/Eigendecomposition_of_a_matrix">Wikipedia</a>
+ * @since 2.0 (changed to concrete class in 3.0)
+ */
+public class EigenDecomposition {
+ /** Internally used epsilon criteria. */
+ private static final double EPSILON = 1e-12;
+
+ /** Maximum number of iterations accepted in the implicit QL transformation */
+ private byte maxIter = 30;
+
+ /** Main diagonal of the tridiagonal matrix. */
+ private double[] main;
+
+ /** Secondary diagonal of the tridiagonal matrix. */
+ private double[] secondary;
+
+ /** Transformer to tridiagonal (may be null if matrix is already tridiagonal). */
+ private TriDiagonalTransformer transformer;
+
+ /** Real part of the realEigenvalues. */
+ private double[] realEigenvalues;
+
+ /** Imaginary part of the realEigenvalues. */
+ private double[] imagEigenvalues;
+
+ /** Eigenvectors. */
+ private ArrayRealVector[] eigenvectors;
+
+ /** Cached value of V. */
+ private RealMatrix cachedV;
+
+ /** Cached value of D. */
+ private RealMatrix cachedD;
+
+ /** Cached value of Vt. */
+ private RealMatrix cachedVt;
+
+ /** Whether the matrix is symmetric. */
+ private final boolean isSymmetric;
+
+ /**
+ * Calculates the eigen decomposition of the given real matrix.
+ *
+ * <p>Supports decomposition of a general matrix since 3.1.
+ *
+ * @param matrix Matrix to decompose.
+ * @throws MaxCountExceededException if the algorithm fails to converge.
+ * @throws MathArithmeticException if the decomposition of a general matrix results in a matrix
+ * with zero norm
+ * @since 3.1
+ */
+ public EigenDecomposition(final RealMatrix matrix) throws MathArithmeticException {
+ final double symTol =
+ 10 * matrix.getRowDimension() * matrix.getColumnDimension() * Precision.EPSILON;
+ isSymmetric = MatrixUtils.isSymmetric(matrix, symTol);
+ if (isSymmetric) {
+ transformToTridiagonal(matrix);
+ findEigenVectors(transformer.getQ().getData());
+ } else {
+ final SchurTransformer t = transformToSchur(matrix);
+ findEigenVectorsFromSchur(t);
+ }
+ }
+
+ /**
+ * Calculates the eigen decomposition of the given real matrix.
+ *
+ * @param matrix Matrix to decompose.
+ * @param splitTolerance Dummy parameter (present for backward compatibility only).
+ * @throws MathArithmeticException if the decomposition of a general matrix results in a matrix
+ * with zero norm
+ * @throws MaxCountExceededException if the algorithm fails to converge.
+ * @deprecated in 3.1 (to be removed in 4.0) due to unused parameter
+ */
+ @Deprecated
+ public EigenDecomposition(final RealMatrix matrix, final double splitTolerance)
+ throws MathArithmeticException {
+ this(matrix);
+ }
+
+ /**
+ * Calculates the eigen decomposition of the symmetric tridiagonal matrix. The Householder
+ * matrix is assumed to be the identity matrix.
+ *
+ * @param main Main diagonal of the symmetric tridiagonal form.
+ * @param secondary Secondary of the tridiagonal form.
+ * @throws MaxCountExceededException if the algorithm fails to converge.
+ * @since 3.1
+ */
+ public EigenDecomposition(final double[] main, final double[] secondary) {
+ isSymmetric = true;
+ this.main = main.clone();
+ this.secondary = secondary.clone();
+ transformer = null;
+ final int size = main.length;
+ final double[][] z = new double[size][size];
+ for (int i = 0; i < size; i++) {
+ z[i][i] = 1.0;
+ }
+ findEigenVectors(z);
+ }
+
+ /**
+ * Calculates the eigen decomposition of the symmetric tridiagonal matrix. The Householder
+ * matrix is assumed to be the identity matrix.
+ *
+ * @param main Main diagonal of the symmetric tridiagonal form.
+ * @param secondary Secondary of the tridiagonal form.
+ * @param splitTolerance Dummy parameter (present for backward compatibility only).
+ * @throws MaxCountExceededException if the algorithm fails to converge.
+ * @deprecated in 3.1 (to be removed in 4.0) due to unused parameter
+ */
+ @Deprecated
+ public EigenDecomposition(
+ final double[] main, final double[] secondary, final double splitTolerance) {
+ this(main, secondary);
+ }
+
+ /**
+ * Gets the matrix V of the decomposition. V is an orthogonal matrix, i.e. its transpose is also
+ * its inverse. The columns of V are the eigenvectors of the original matrix. No assumption is
+ * made about the orientation of the system axes formed by the columns of V (e.g. in a
+ * 3-dimension space, V can form a left- or right-handed system).
+ *
+ * @return the V matrix.
+ */
+ public RealMatrix getV() {
+
+ if (cachedV == null) {
+ final int m = eigenvectors.length;
+ cachedV = MatrixUtils.createRealMatrix(m, m);
+ for (int k = 0; k < m; ++k) {
+ cachedV.setColumnVector(k, eigenvectors[k]);
+ }
+ }
+ // return the cached matrix
+ return cachedV;
+ }
+
+ /**
+ * Gets the block diagonal matrix D of the decomposition. D is a block diagonal matrix. Real
+ * eigenvalues are on the diagonal while complex values are on 2x2 blocks { {real +imaginary},
+ * {-imaginary, real} }.
+ *
+ * @return the D matrix.
+ * @see #getRealEigenvalues()
+ * @see #getImagEigenvalues()
+ */
+ public RealMatrix getD() {
+
+ if (cachedD == null) {
+ // cache the matrix for subsequent calls
+ cachedD = MatrixUtils.createRealDiagonalMatrix(realEigenvalues);
+
+ for (int i = 0; i < imagEigenvalues.length; i++) {
+ if (Precision.compareTo(imagEigenvalues[i], 0.0, EPSILON) > 0) {
+ cachedD.setEntry(i, i + 1, imagEigenvalues[i]);
+ } else if (Precision.compareTo(imagEigenvalues[i], 0.0, EPSILON) < 0) {
+ cachedD.setEntry(i, i - 1, imagEigenvalues[i]);
+ }
+ }
+ }
+ return cachedD;
+ }
+
+ /**
+ * Gets the transpose of the matrix V of the decomposition. V is an orthogonal matrix, i.e. its
+ * transpose is also its inverse. The columns of V are the eigenvectors of the original matrix.
+ * No assumption is made about the orientation of the system axes formed by the columns of V
+ * (e.g. in a 3-dimension space, V can form a left- or right-handed system).
+ *
+ * @return the transpose of the V matrix.
+ */
+ public RealMatrix getVT() {
+
+ if (cachedVt == null) {
+ final int m = eigenvectors.length;
+ cachedVt = MatrixUtils.createRealMatrix(m, m);
+ for (int k = 0; k < m; ++k) {
+ cachedVt.setRowVector(k, eigenvectors[k]);
+ }
+ }
+
+ // return the cached matrix
+ return cachedVt;
+ }
+
+ /**
+ * Returns whether the calculated eigen values are complex or real.
+ *
+ * <p>The method performs a zero check for each element of the {@link #getImagEigenvalues()}
+ * array and returns {@code true} if any element is not equal to zero.
+ *
+ * @return {@code true} if the eigen values are complex, {@code false} otherwise
+ * @since 3.1
+ */
+ public boolean hasComplexEigenvalues() {
+ for (int i = 0; i < imagEigenvalues.length; i++) {
+ if (!Precision.equals(imagEigenvalues[i], 0.0, EPSILON)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Gets a copy of the real parts of the eigenvalues of the original matrix.
+ *
+ * @return a copy of the real parts of the eigenvalues of the original matrix.
+ * @see #getD()
+ * @see #getRealEigenvalue(int)
+ * @see #getImagEigenvalues()
+ */
+ public double[] getRealEigenvalues() {
+ return realEigenvalues.clone();
+ }
+
+ /**
+ * Returns the real part of the i<sup>th</sup> eigenvalue of the original matrix.
+ *
+ * @param i index of the eigenvalue (counting from 0)
+ * @return real part of the i<sup>th</sup> eigenvalue of the original matrix.
+ * @see #getD()
+ * @see #getRealEigenvalues()
+ * @see #getImagEigenvalue(int)
+ */
+ public double getRealEigenvalue(final int i) {
+ return realEigenvalues[i];
+ }
+
+ /**
+ * Gets a copy of the imaginary parts of the eigenvalues of the original matrix.
+ *
+ * @return a copy of the imaginary parts of the eigenvalues of the original matrix.
+ * @see #getD()
+ * @see #getImagEigenvalue(int)
+ * @see #getRealEigenvalues()
+ */
+ public double[] getImagEigenvalues() {
+ return imagEigenvalues.clone();
+ }
+
+ /**
+ * Gets the imaginary part of the i<sup>th</sup> eigenvalue of the original matrix.
+ *
+ * @param i Index of the eigenvalue (counting from 0).
+ * @return the imaginary part of the i<sup>th</sup> eigenvalue of the original matrix.
+ * @see #getD()
+ * @see #getImagEigenvalues()
+ * @see #getRealEigenvalue(int)
+ */
+ public double getImagEigenvalue(final int i) {
+ return imagEigenvalues[i];
+ }
+
+ /**
+ * Gets a copy of the i<sup>th</sup> eigenvector of the original matrix.
+ *
+ * @param i Index of the eigenvector (counting from 0).
+ * @return a copy of the i<sup>th</sup> eigenvector of the original matrix.
+ * @see #getD()
+ */
+ public RealVector getEigenvector(final int i) {
+ return eigenvectors[i].copy();
+ }
+
+ /**
+ * Computes the determinant of the matrix.
+ *
+ * @return the determinant of the matrix.
+ */
+ public double getDeterminant() {
+ double determinant = 1;
+ for (double lambda : realEigenvalues) {
+ determinant *= lambda;
+ }
+ return determinant;
+ }
+
+ /**
+ * Computes the square-root of the matrix. This implementation assumes that the matrix is
+ * symmetric and positive definite.
+ *
+ * @return the square-root of the matrix.
+ * @throws MathUnsupportedOperationException if the matrix is not symmetric or not positive
+ * definite.
+ * @since 3.1
+ */
+ public RealMatrix getSquareRoot() {
+ if (!isSymmetric) {
+ throw new MathUnsupportedOperationException();
+ }
+
+ final double[] sqrtEigenValues = new double[realEigenvalues.length];
+ for (int i = 0; i < realEigenvalues.length; i++) {
+ final double eigen = realEigenvalues[i];
+ if (eigen <= 0) {
+ throw new MathUnsupportedOperationException();
+ }
+ sqrtEigenValues[i] = FastMath.sqrt(eigen);
+ }
+ final RealMatrix sqrtEigen = MatrixUtils.createRealDiagonalMatrix(sqrtEigenValues);
+ final RealMatrix v = getV();
+ final RealMatrix vT = getVT();
+
+ return v.multiply(sqrtEigen).multiply(vT);
+ }
+
+ /**
+ * Gets a solver for finding the A &times; X = B solution in exact linear sense.
+ *
+ * <p>Since 3.1, eigen decomposition of a general matrix is supported, but the {@link
+ * DecompositionSolver} only supports real eigenvalues.
+ *
+ * @return a solver
+ * @throws MathUnsupportedOperationException if the decomposition resulted in complex
+ * eigenvalues
+ */
+ public DecompositionSolver getSolver() {
+ if (hasComplexEigenvalues()) {
+ throw new MathUnsupportedOperationException();
+ }
+ return new Solver(realEigenvalues, imagEigenvalues, eigenvectors);
+ }
+
+ /** Specialized solver. */
+ private static class Solver implements DecompositionSolver {
+ /** Real part of the realEigenvalues. */
+ private double[] realEigenvalues;
+
+ /** Imaginary part of the realEigenvalues. */
+ private double[] imagEigenvalues;
+
+ /** Eigenvectors. */
+ private final ArrayRealVector[] eigenvectors;
+
+ /**
+ * Builds a solver from decomposed matrix.
+ *
+ * @param realEigenvalues Real parts of the eigenvalues.
+ * @param imagEigenvalues Imaginary parts of the eigenvalues.
+ * @param eigenvectors Eigenvectors.
+ */
+ private Solver(
+ final double[] realEigenvalues,
+ final double[] imagEigenvalues,
+ final ArrayRealVector[] eigenvectors) {
+ this.realEigenvalues = realEigenvalues;
+ this.imagEigenvalues = imagEigenvalues;
+ this.eigenvectors = eigenvectors;
+ }
+
+ /**
+ * Solves the linear equation A &times; X = B for symmetric matrices A.
+ *
+ * <p>This method only finds exact linear solutions, i.e. solutions for which ||A &times; X
+ * - B|| is exactly 0.
+ *
+ * @param b Right-hand side of the equation A &times; X = B.
+ * @return a Vector X that minimizes the two norm of A &times; X - B.
+ * @throws DimensionMismatchException if the matrices dimensions do not match.
+ * @throws SingularMatrixException if the decomposed matrix is singular.
+ */
+ public RealVector solve(final RealVector b) {
+ if (!isNonSingular()) {
+ throw new SingularMatrixException();
+ }
+
+ final int m = realEigenvalues.length;
+ if (b.getDimension() != m) {
+ throw new DimensionMismatchException(b.getDimension(), m);
+ }
+
+ final double[] bp = new double[m];
+ for (int i = 0; i < m; ++i) {
+ final ArrayRealVector v = eigenvectors[i];
+ final double[] vData = v.getDataRef();
+ final double s = v.dotProduct(b) / realEigenvalues[i];
+ for (int j = 0; j < m; ++j) {
+ bp[j] += s * vData[j];
+ }
+ }
+
+ return new ArrayRealVector(bp, false);
+ }
+
+ /** {@inheritDoc} */
+ public RealMatrix solve(RealMatrix b) {
+
+ if (!isNonSingular()) {
+ throw new SingularMatrixException();
+ }
+
+ final int m = realEigenvalues.length;
+ if (b.getRowDimension() != m) {
+ throw new DimensionMismatchException(b.getRowDimension(), m);
+ }
+
+ final int nColB = b.getColumnDimension();
+ final double[][] bp = new double[m][nColB];
+ final double[] tmpCol = new double[m];
+ for (int k = 0; k < nColB; ++k) {
+ for (int i = 0; i < m; ++i) {
+ tmpCol[i] = b.getEntry(i, k);
+ bp[i][k] = 0;
+ }
+ for (int i = 0; i < m; ++i) {
+ final ArrayRealVector v = eigenvectors[i];
+ final double[] vData = v.getDataRef();
+ double s = 0;
+ for (int j = 0; j < m; ++j) {
+ s += v.getEntry(j) * tmpCol[j];
+ }
+ s /= realEigenvalues[i];
+ for (int j = 0; j < m; ++j) {
+ bp[j][k] += s * vData[j];
+ }
+ }
+ }
+
+ return new Array2DRowRealMatrix(bp, false);
+ }
+
+ /**
+ * Checks whether the decomposed matrix is non-singular.
+ *
+ * @return true if the decomposed matrix is non-singular.
+ */
+ public boolean isNonSingular() {
+ double largestEigenvalueNorm = 0.0;
+ // Looping over all values (in case they are not sorted in decreasing
+ // order of their norm).
+ for (int i = 0; i < realEigenvalues.length; ++i) {
+ largestEigenvalueNorm = FastMath.max(largestEigenvalueNorm, eigenvalueNorm(i));
+ }
+ // Corner case: zero matrix, all exactly 0 eigenvalues
+ if (largestEigenvalueNorm == 0.0) {
+ return false;
+ }
+ for (int i = 0; i < realEigenvalues.length; ++i) {
+ // Looking for eigenvalues that are 0, where we consider anything much much smaller
+ // than the largest eigenvalue to be effectively 0.
+ if (Precision.equals(eigenvalueNorm(i) / largestEigenvalueNorm, 0, EPSILON)) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ /**
+ * @param i which eigenvalue to find the norm of
+ * @return the norm of ith (complex) eigenvalue.
+ */
+ private double eigenvalueNorm(int i) {
+ final double re = realEigenvalues[i];
+ final double im = imagEigenvalues[i];
+ return FastMath.sqrt(re * re + im * im);
+ }
+
+ /**
+ * Get the inverse of the decomposed matrix.
+ *
+ * @return the inverse matrix.
+ * @throws SingularMatrixException if the decomposed matrix is singular.
+ */
+ public RealMatrix getInverse() {
+ if (!isNonSingular()) {
+ throw new SingularMatrixException();
+ }
+
+ final int m = realEigenvalues.length;
+ final double[][] invData = new double[m][m];
+
+ for (int i = 0; i < m; ++i) {
+ final double[] invI = invData[i];
+ for (int j = 0; j < m; ++j) {
+ double invIJ = 0;
+ for (int k = 0; k < m; ++k) {
+ final double[] vK = eigenvectors[k].getDataRef();
+ invIJ += vK[i] * vK[j] / realEigenvalues[k];
+ }
+ invI[j] = invIJ;
+ }
+ }
+ return MatrixUtils.createRealMatrix(invData);
+ }
+ }
+
+ /**
+ * Transforms the matrix to tridiagonal form.
+ *
+ * @param matrix Matrix to transform.
+ */
+ private void transformToTridiagonal(final RealMatrix matrix) {
+ // transform the matrix to tridiagonal
+ transformer = new TriDiagonalTransformer(matrix);
+ main = transformer.getMainDiagonalRef();
+ secondary = transformer.getSecondaryDiagonalRef();
+ }
+
+ /**
+ * Find eigenvalues and eigenvectors (Dubrulle et al., 1971)
+ *
+ * @param householderMatrix Householder matrix of the transformation to tridiagonal form.
+ */
+ private void findEigenVectors(final double[][] householderMatrix) {
+ final double[][] z = householderMatrix.clone();
+ final int n = main.length;
+ realEigenvalues = new double[n];
+ imagEigenvalues = new double[n];
+ final double[] e = new double[n];
+ for (int i = 0; i < n - 1; i++) {
+ realEigenvalues[i] = main[i];
+ e[i] = secondary[i];
+ }
+ realEigenvalues[n - 1] = main[n - 1];
+ e[n - 1] = 0;
+
+ // Determine the largest main and secondary value in absolute term.
+ double maxAbsoluteValue = 0;
+ for (int i = 0; i < n; i++) {
+ if (FastMath.abs(realEigenvalues[i]) > maxAbsoluteValue) {
+ maxAbsoluteValue = FastMath.abs(realEigenvalues[i]);
+ }
+ if (FastMath.abs(e[i]) > maxAbsoluteValue) {
+ maxAbsoluteValue = FastMath.abs(e[i]);
+ }
+ }
+ // Make null any main and secondary value too small to be significant
+ if (maxAbsoluteValue != 0) {
+ for (int i = 0; i < n; i++) {
+ if (FastMath.abs(realEigenvalues[i]) <= Precision.EPSILON * maxAbsoluteValue) {
+ realEigenvalues[i] = 0;
+ }
+ if (FastMath.abs(e[i]) <= Precision.EPSILON * maxAbsoluteValue) {
+ e[i] = 0;
+ }
+ }
+ }
+
+ for (int j = 0; j < n; j++) {
+ int its = 0;
+ int m;
+ do {
+ for (m = j; m < n - 1; m++) {
+ double delta =
+ FastMath.abs(realEigenvalues[m]) + FastMath.abs(realEigenvalues[m + 1]);
+ if (FastMath.abs(e[m]) + delta == delta) {
+ break;
+ }
+ }
+ if (m != j) {
+ if (its == maxIter) {
+ throw new MaxCountExceededException(
+ LocalizedFormats.CONVERGENCE_FAILED, maxIter);
+ }
+ its++;
+ double q = (realEigenvalues[j + 1] - realEigenvalues[j]) / (2 * e[j]);
+ double t = FastMath.sqrt(1 + q * q);
+ if (q < 0.0) {
+ q = realEigenvalues[m] - realEigenvalues[j] + e[j] / (q - t);
+ } else {
+ q = realEigenvalues[m] - realEigenvalues[j] + e[j] / (q + t);
+ }
+ double u = 0.0;
+ double s = 1.0;
+ double c = 1.0;
+ int i;
+ for (i = m - 1; i >= j; i--) {
+ double p = s * e[i];
+ double h = c * e[i];
+ if (FastMath.abs(p) >= FastMath.abs(q)) {
+ c = q / p;
+ t = FastMath.sqrt(c * c + 1.0);
+ e[i + 1] = p * t;
+ s = 1.0 / t;
+ c *= s;
+ } else {
+ s = p / q;
+ t = FastMath.sqrt(s * s + 1.0);
+ e[i + 1] = q * t;
+ c = 1.0 / t;
+ s *= c;
+ }
+ if (e[i + 1] == 0.0) {
+ realEigenvalues[i + 1] -= u;
+ e[m] = 0.0;
+ break;
+ }
+ q = realEigenvalues[i + 1] - u;
+ t = (realEigenvalues[i] - q) * s + 2.0 * c * h;
+ u = s * t;
+ realEigenvalues[i + 1] = q + u;
+ q = c * t - h;
+ for (int ia = 0; ia < n; ia++) {
+ p = z[ia][i + 1];
+ z[ia][i + 1] = s * z[ia][i] + c * p;
+ z[ia][i] = c * z[ia][i] - s * p;
+ }
+ }
+ if (t == 0.0 && i >= j) {
+ continue;
+ }
+ realEigenvalues[j] -= u;
+ e[j] = q;
+ e[m] = 0.0;
+ }
+ } while (m != j);
+ }
+
+ // Sort the eigen values (and vectors) in increase order
+ for (int i = 0; i < n; i++) {
+ int k = i;
+ double p = realEigenvalues[i];
+ for (int j = i + 1; j < n; j++) {
+ if (realEigenvalues[j] > p) {
+ k = j;
+ p = realEigenvalues[j];
+ }
+ }
+ if (k != i) {
+ realEigenvalues[k] = realEigenvalues[i];
+ realEigenvalues[i] = p;
+ for (int j = 0; j < n; j++) {
+ p = z[j][i];
+ z[j][i] = z[j][k];
+ z[j][k] = p;
+ }
+ }
+ }
+
+ // Determine the largest eigen value in absolute term.
+ maxAbsoluteValue = 0;
+ for (int i = 0; i < n; i++) {
+ if (FastMath.abs(realEigenvalues[i]) > maxAbsoluteValue) {
+ maxAbsoluteValue = FastMath.abs(realEigenvalues[i]);
+ }
+ }
+ // Make null any eigen value too small to be significant
+ if (maxAbsoluteValue != 0.0) {
+ for (int i = 0; i < n; i++) {
+ if (FastMath.abs(realEigenvalues[i]) < Precision.EPSILON * maxAbsoluteValue) {
+ realEigenvalues[i] = 0;
+ }
+ }
+ }
+ eigenvectors = new ArrayRealVector[n];
+ final double[] tmp = new double[n];
+ for (int i = 0; i < n; i++) {
+ for (int j = 0; j < n; j++) {
+ tmp[j] = z[j][i];
+ }
+ eigenvectors[i] = new ArrayRealVector(tmp);
+ }
+ }
+
+ /**
+ * Transforms the matrix to Schur form and calculates the eigenvalues.
+ *
+ * @param matrix Matrix to transform.
+ * @return the {@link SchurTransformer Shur transform} for this matrix
+ */
+ private SchurTransformer transformToSchur(final RealMatrix matrix) {
+ final SchurTransformer schurTransform = new SchurTransformer(matrix);
+ final double[][] matT = schurTransform.getT().getData();
+
+ realEigenvalues = new double[matT.length];
+ imagEigenvalues = new double[matT.length];
+
+ for (int i = 0; i < realEigenvalues.length; i++) {
+ if (i == (realEigenvalues.length - 1)
+ || Precision.equals(matT[i + 1][i], 0.0, EPSILON)) {
+ realEigenvalues[i] = matT[i][i];
+ } else {
+ final double x = matT[i + 1][i + 1];
+ final double p = 0.5 * (matT[i][i] - x);
+ final double z =
+ FastMath.sqrt(FastMath.abs(p * p + matT[i + 1][i] * matT[i][i + 1]));
+ realEigenvalues[i] = x + p;
+ imagEigenvalues[i] = z;
+ realEigenvalues[i + 1] = x + p;
+ imagEigenvalues[i + 1] = -z;
+ i++;
+ }
+ }
+ return schurTransform;
+ }
+
+ /**
+ * Performs a division of two complex numbers.
+ *
+ * @param xr real part of the first number
+ * @param xi imaginary part of the first number
+ * @param yr real part of the second number
+ * @param yi imaginary part of the second number
+ * @return result of the complex division
+ */
+ private Complex cdiv(final double xr, final double xi, final double yr, final double yi) {
+ return new Complex(xr, xi).divide(new Complex(yr, yi));
+ }
+
+ /**
+ * Find eigenvectors from a matrix transformed to Schur form.
+ *
+ * @param schur the schur transformation of the matrix
+ * @throws MathArithmeticException if the Schur form has a norm of zero
+ */
+ private void findEigenVectorsFromSchur(final SchurTransformer schur)
+ throws MathArithmeticException {
+ final double[][] matrixT = schur.getT().getData();
+ final double[][] matrixP = schur.getP().getData();
+
+ final int n = matrixT.length;
+
+ // compute matrix norm
+ double norm = 0.0;
+ for (int i = 0; i < n; i++) {
+ for (int j = FastMath.max(i - 1, 0); j < n; j++) {
+ norm += FastMath.abs(matrixT[i][j]);
+ }
+ }
+
+ // we can not handle a matrix with zero norm
+ if (Precision.equals(norm, 0.0, EPSILON)) {
+ throw new MathArithmeticException(LocalizedFormats.ZERO_NORM);
+ }
+
+ // Backsubstitute to find vectors of upper triangular form
+
+ double r = 0.0;
+ double s = 0.0;
+ double z = 0.0;
+
+ for (int idx = n - 1; idx >= 0; idx--) {
+ double p = realEigenvalues[idx];
+ double q = imagEigenvalues[idx];
+
+ if (Precision.equals(q, 0.0)) {
+ // Real vector
+ int l = idx;
+ matrixT[idx][idx] = 1.0;
+ for (int i = idx - 1; i >= 0; i--) {
+ double w = matrixT[i][i] - p;
+ r = 0.0;
+ for (int j = l; j <= idx; j++) {
+ r += matrixT[i][j] * matrixT[j][idx];
+ }
+ if (Precision.compareTo(imagEigenvalues[i], 0.0, EPSILON) < 0) {
+ z = w;
+ s = r;
+ } else {
+ l = i;
+ if (Precision.equals(imagEigenvalues[i], 0.0)) {
+ if (w != 0.0) {
+ matrixT[i][idx] = -r / w;
+ } else {
+ matrixT[i][idx] = -r / (Precision.EPSILON * norm);
+ }
+ } else {
+ // Solve real equations
+ double x = matrixT[i][i + 1];
+ double y = matrixT[i + 1][i];
+ q =
+ (realEigenvalues[i] - p) * (realEigenvalues[i] - p)
+ + imagEigenvalues[i] * imagEigenvalues[i];
+ double t = (x * s - z * r) / q;
+ matrixT[i][idx] = t;
+ if (FastMath.abs(x) > FastMath.abs(z)) {
+ matrixT[i + 1][idx] = (-r - w * t) / x;
+ } else {
+ matrixT[i + 1][idx] = (-s - y * t) / z;
+ }
+ }
+
+ // Overflow control
+ double t = FastMath.abs(matrixT[i][idx]);
+ if ((Precision.EPSILON * t) * t > 1) {
+ for (int j = i; j <= idx; j++) {
+ matrixT[j][idx] /= t;
+ }
+ }
+ }
+ }
+ } else if (q < 0.0) {
+ // Complex vector
+ int l = idx - 1;
+
+ // Last vector component imaginary so matrix is triangular
+ if (FastMath.abs(matrixT[idx][idx - 1]) > FastMath.abs(matrixT[idx - 1][idx])) {
+ matrixT[idx - 1][idx - 1] = q / matrixT[idx][idx - 1];
+ matrixT[idx - 1][idx] = -(matrixT[idx][idx] - p) / matrixT[idx][idx - 1];
+ } else {
+ final Complex result =
+ cdiv(0.0, -matrixT[idx - 1][idx], matrixT[idx - 1][idx - 1] - p, q);
+ matrixT[idx - 1][idx - 1] = result.getReal();
+ matrixT[idx - 1][idx] = result.getImaginary();
+ }
+
+ matrixT[idx][idx - 1] = 0.0;
+ matrixT[idx][idx] = 1.0;
+
+ for (int i = idx - 2; i >= 0; i--) {
+ double ra = 0.0;
+ double sa = 0.0;
+ for (int j = l; j <= idx; j++) {
+ ra += matrixT[i][j] * matrixT[j][idx - 1];
+ sa += matrixT[i][j] * matrixT[j][idx];
+ }
+ double w = matrixT[i][i] - p;
+
+ if (Precision.compareTo(imagEigenvalues[i], 0.0, EPSILON) < 0) {
+ z = w;
+ r = ra;
+ s = sa;
+ } else {
+ l = i;
+ if (Precision.equals(imagEigenvalues[i], 0.0)) {
+ final Complex c = cdiv(-ra, -sa, w, q);
+ matrixT[i][idx - 1] = c.getReal();
+ matrixT[i][idx] = c.getImaginary();
+ } else {
+ // Solve complex equations
+ double x = matrixT[i][i + 1];
+ double y = matrixT[i + 1][i];
+ double vr =
+ (realEigenvalues[i] - p) * (realEigenvalues[i] - p)
+ + imagEigenvalues[i] * imagEigenvalues[i]
+ - q * q;
+ final double vi = (realEigenvalues[i] - p) * 2.0 * q;
+ if (Precision.equals(vr, 0.0) && Precision.equals(vi, 0.0)) {
+ vr =
+ Precision.EPSILON
+ * norm
+ * (FastMath.abs(w)
+ + FastMath.abs(q)
+ + FastMath.abs(x)
+ + FastMath.abs(y)
+ + FastMath.abs(z));
+ }
+ final Complex c =
+ cdiv(x * r - z * ra + q * sa, x * s - z * sa - q * ra, vr, vi);
+ matrixT[i][idx - 1] = c.getReal();
+ matrixT[i][idx] = c.getImaginary();
+
+ if (FastMath.abs(x) > (FastMath.abs(z) + FastMath.abs(q))) {
+ matrixT[i + 1][idx - 1] =
+ (-ra - w * matrixT[i][idx - 1] + q * matrixT[i][idx]) / x;
+ matrixT[i + 1][idx] =
+ (-sa - w * matrixT[i][idx] - q * matrixT[i][idx - 1]) / x;
+ } else {
+ final Complex c2 =
+ cdiv(
+ -r - y * matrixT[i][idx - 1],
+ -s - y * matrixT[i][idx],
+ z,
+ q);
+ matrixT[i + 1][idx - 1] = c2.getReal();
+ matrixT[i + 1][idx] = c2.getImaginary();
+ }
+ }
+
+ // Overflow control
+ double t =
+ FastMath.max(
+ FastMath.abs(matrixT[i][idx - 1]),
+ FastMath.abs(matrixT[i][idx]));
+ if ((Precision.EPSILON * t) * t > 1) {
+ for (int j = i; j <= idx; j++) {
+ matrixT[j][idx - 1] /= t;
+ matrixT[j][idx] /= t;
+ }
+ }
+ }
+ }
+ }
+ }
+
+ // Back transformation to get eigenvectors of original matrix
+ for (int j = n - 1; j >= 0; j--) {
+ for (int i = 0; i <= n - 1; i++) {
+ z = 0.0;
+ for (int k = 0; k <= FastMath.min(j, n - 1); k++) {
+ z += matrixP[i][k] * matrixT[k][j];
+ }
+ matrixP[i][j] = z;
+ }
+ }
+
+ eigenvectors = new ArrayRealVector[n];
+ final double[] tmp = new double[n];
+ for (int i = 0; i < n; i++) {
+ for (int j = 0; j < n; j++) {
+ tmp[j] = matrixP[j][i];
+ }
+ eigenvectors[i] = new ArrayRealVector(tmp);
+ }
+ }
+}
diff --git a/src/main/java/org/apache/commons/math3/linear/FieldDecompositionSolver.java b/src/main/java/org/apache/commons/math3/linear/FieldDecompositionSolver.java
new file mode 100644
index 0000000..ed5e863
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/linear/FieldDecompositionSolver.java
@@ -0,0 +1,79 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math3.linear;
+
+import org.apache.commons.math3.FieldElement;
+
+/**
+ * Interface handling decomposition algorithms that can solve A &times; X = B.
+ *
+ * <p>Decomposition algorithms decompose an A matrix has a product of several specific matrices from
+ * which they can solve A &times; X = B in least squares sense: they find X such that ||A &times; X
+ * - B|| is minimal.
+ *
+ * <p>Some solvers like {@link FieldLUDecomposition} can only find the solution for square matrices
+ * and when the solution is an exact linear solution, i.e. when ||A &times; X - B|| is exactly 0.
+ * Other solvers can also find solutions with non-square matrix A and with non-null minimal norm. If
+ * an exact linear solution exists it is also the minimal norm solution.
+ *
+ * @param <T> the type of the field elements
+ * @since 2.0
+ */
+public interface FieldDecompositionSolver<T extends FieldElement<T>> {
+
+ /**
+ * Solve the linear equation A &times; X = B for matrices A.
+ *
+ * <p>The A matrix is implicit, it is provided by the underlying decomposition algorithm.
+ *
+ * @param b right-hand side of the equation A &times; X = B
+ * @return a vector X that minimizes the two norm of A &times; X - B
+ * @throws org.apache.commons.math3.exception.DimensionMismatchException if the matrices
+ * dimensions do not match.
+ * @throws SingularMatrixException if the decomposed matrix is singular.
+ */
+ FieldVector<T> solve(final FieldVector<T> b);
+
+ /**
+ * Solve the linear equation A &times; X = B for matrices A.
+ *
+ * <p>The A matrix is implicit, it is provided by the underlying decomposition algorithm.
+ *
+ * @param b right-hand side of the equation A &times; X = B
+ * @return a matrix X that minimizes the two norm of A &times; X - B
+ * @throws org.apache.commons.math3.exception.DimensionMismatchException if the matrices
+ * dimensions do not match.
+ * @throws SingularMatrixException if the decomposed matrix is singular.
+ */
+ FieldMatrix<T> solve(final FieldMatrix<T> b);
+
+ /**
+ * Check if the decomposed matrix is non-singular.
+ *
+ * @return true if the decomposed matrix is non-singular
+ */
+ boolean isNonSingular();
+
+ /**
+ * Get the inverse (or pseudo-inverse) of the decomposed matrix.
+ *
+ * @return inverse matrix
+ * @throws SingularMatrixException if the decomposed matrix is singular.
+ */
+ FieldMatrix<T> getInverse();
+}
diff --git a/src/main/java/org/apache/commons/math3/linear/FieldLUDecomposition.java b/src/main/java/org/apache/commons/math3/linear/FieldLUDecomposition.java
new file mode 100644
index 0000000..4976651
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/linear/FieldLUDecomposition.java
@@ -0,0 +1,461 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math3.linear;
+
+import org.apache.commons.math3.Field;
+import org.apache.commons.math3.FieldElement;
+import org.apache.commons.math3.exception.DimensionMismatchException;
+import org.apache.commons.math3.util.MathArrays;
+
+/**
+ * Calculates the LUP-decomposition of a square matrix.
+ *
+ * <p>The LUP-decomposition of a matrix A consists of three matrices L, U and P that satisfy: PA =
+ * LU, L is lower triangular, and U is upper triangular and P is a permutation matrix. All matrices
+ * are m&times;m.
+ *
+ * <p>Since {@link FieldElement field elements} do not provide an ordering operator, the permutation
+ * matrix is computed here only in order to avoid a zero pivot element, no attempt is done to get
+ * the largest pivot element.
+ *
+ * <p>This class is based on the class with similar name from the <a
+ * href="http://math.nist.gov/javanumerics/jama/">JAMA</a> library.
+ *
+ * <ul>
+ * <li>a {@link #getP() getP} method has been added,
+ * <li>the {@code det} method has been renamed as {@link #getDeterminant() getDeterminant},
+ * <li>the {@code getDoublePivot} method has been removed (but the int based {@link #getPivot()
+ * getPivot} method has been kept),
+ * <li>the {@code solve} and {@code isNonSingular} methods have been replaced by a {@link
+ * #getSolver() getSolver} method and the equivalent methods provided by the returned {@link
+ * DecompositionSolver}.
+ * </ul>
+ *
+ * @param <T> the type of the field elements
+ * @see <a href="http://mathworld.wolfram.com/LUDecomposition.html">MathWorld</a>
+ * @see <a href="http://en.wikipedia.org/wiki/LU_decomposition">Wikipedia</a>
+ * @since 2.0 (changed to concrete class in 3.0)
+ */
+public class FieldLUDecomposition<T extends FieldElement<T>> {
+
+ /** Field to which the elements belong. */
+ private final Field<T> field;
+
+ /** Entries of LU decomposition. */
+ private T[][] lu;
+
+ /** Pivot permutation associated with LU decomposition. */
+ private int[] pivot;
+
+ /** Parity of the permutation associated with the LU decomposition. */
+ private boolean even;
+
+ /** Singularity indicator. */
+ private boolean singular;
+
+ /** Cached value of L. */
+ private FieldMatrix<T> cachedL;
+
+ /** Cached value of U. */
+ private FieldMatrix<T> cachedU;
+
+ /** Cached value of P. */
+ private FieldMatrix<T> cachedP;
+
+ /**
+ * Calculates the LU-decomposition of the given matrix.
+ *
+ * @param matrix The matrix to decompose.
+ * @throws NonSquareMatrixException if matrix is not square
+ */
+ public FieldLUDecomposition(FieldMatrix<T> matrix) {
+ if (!matrix.isSquare()) {
+ throw new NonSquareMatrixException(
+ matrix.getRowDimension(), matrix.getColumnDimension());
+ }
+
+ final int m = matrix.getColumnDimension();
+ field = matrix.getField();
+ lu = matrix.getData();
+ pivot = new int[m];
+ cachedL = null;
+ cachedU = null;
+ cachedP = null;
+
+ // Initialize permutation array and parity
+ for (int row = 0; row < m; row++) {
+ pivot[row] = row;
+ }
+ even = true;
+ singular = false;
+
+ // Loop over columns
+ for (int col = 0; col < m; col++) {
+
+ T sum = field.getZero();
+
+ // upper
+ for (int row = 0; row < col; row++) {
+ final T[] luRow = lu[row];
+ sum = luRow[col];
+ for (int i = 0; i < row; i++) {
+ sum = sum.subtract(luRow[i].multiply(lu[i][col]));
+ }
+ luRow[col] = sum;
+ }
+
+ // lower
+ int nonZero = col; // permutation row
+ for (int row = col; row < m; row++) {
+ final T[] luRow = lu[row];
+ sum = luRow[col];
+ for (int i = 0; i < col; i++) {
+ sum = sum.subtract(luRow[i].multiply(lu[i][col]));
+ }
+ luRow[col] = sum;
+
+ if (lu[nonZero][col].equals(field.getZero())) {
+ // try to select a better permutation choice
+ ++nonZero;
+ }
+ }
+
+ // Singularity check
+ if (nonZero >= m) {
+ singular = true;
+ return;
+ }
+
+ // Pivot if necessary
+ if (nonZero != col) {
+ T tmp = field.getZero();
+ for (int i = 0; i < m; i++) {
+ tmp = lu[nonZero][i];
+ lu[nonZero][i] = lu[col][i];
+ lu[col][i] = tmp;
+ }
+ int temp = pivot[nonZero];
+ pivot[nonZero] = pivot[col];
+ pivot[col] = temp;
+ even = !even;
+ }
+
+ // Divide the lower elements by the "winning" diagonal elt.
+ final T luDiag = lu[col][col];
+ for (int row = col + 1; row < m; row++) {
+ final T[] luRow = lu[row];
+ luRow[col] = luRow[col].divide(luDiag);
+ }
+ }
+ }
+
+ /**
+ * Returns the matrix L of the decomposition.
+ *
+ * <p>L is a lower-triangular matrix
+ *
+ * @return the L matrix (or null if decomposed matrix is singular)
+ */
+ public FieldMatrix<T> getL() {
+ if ((cachedL == null) && !singular) {
+ final int m = pivot.length;
+ cachedL = new Array2DRowFieldMatrix<T>(field, m, m);
+ for (int i = 0; i < m; ++i) {
+ final T[] luI = lu[i];
+ for (int j = 0; j < i; ++j) {
+ cachedL.setEntry(i, j, luI[j]);
+ }
+ cachedL.setEntry(i, i, field.getOne());
+ }
+ }
+ return cachedL;
+ }
+
+ /**
+ * Returns the matrix U of the decomposition.
+ *
+ * <p>U is an upper-triangular matrix
+ *
+ * @return the U matrix (or null if decomposed matrix is singular)
+ */
+ public FieldMatrix<T> getU() {
+ if ((cachedU == null) && !singular) {
+ final int m = pivot.length;
+ cachedU = new Array2DRowFieldMatrix<T>(field, m, m);
+ for (int i = 0; i < m; ++i) {
+ final T[] luI = lu[i];
+ for (int j = i; j < m; ++j) {
+ cachedU.setEntry(i, j, luI[j]);
+ }
+ }
+ }
+ return cachedU;
+ }
+
+ /**
+ * Returns the P rows permutation matrix.
+ *
+ * <p>P is a sparse matrix with exactly one element set to 1.0 in each row and each column, all
+ * other elements being set to 0.0.
+ *
+ * <p>The positions of the 1 elements are given by the {@link #getPivot() pivot permutation
+ * vector}.
+ *
+ * @return the P rows permutation matrix (or null if decomposed matrix is singular)
+ * @see #getPivot()
+ */
+ public FieldMatrix<T> getP() {
+ if ((cachedP == null) && !singular) {
+ final int m = pivot.length;
+ cachedP = new Array2DRowFieldMatrix<T>(field, m, m);
+ for (int i = 0; i < m; ++i) {
+ cachedP.setEntry(i, pivot[i], field.getOne());
+ }
+ }
+ return cachedP;
+ }
+
+ /**
+ * Returns the pivot permutation vector.
+ *
+ * @return the pivot permutation vector
+ * @see #getP()
+ */
+ public int[] getPivot() {
+ return pivot.clone();
+ }
+
+ /**
+ * Return the determinant of the matrix.
+ *
+ * @return determinant of the matrix
+ */
+ public T getDeterminant() {
+ if (singular) {
+ return field.getZero();
+ } else {
+ final int m = pivot.length;
+ T determinant = even ? field.getOne() : field.getZero().subtract(field.getOne());
+ for (int i = 0; i < m; i++) {
+ determinant = determinant.multiply(lu[i][i]);
+ }
+ return determinant;
+ }
+ }
+
+ /**
+ * Get a solver for finding the A &times; X = B solution in exact linear sense.
+ *
+ * @return a solver
+ */
+ public FieldDecompositionSolver<T> getSolver() {
+ return new Solver<T>(field, lu, pivot, singular);
+ }
+
+ /**
+ * Specialized solver.
+ *
+ * @param <T> the type of the field elements
+ */
+ private static class Solver<T extends FieldElement<T>> implements FieldDecompositionSolver<T> {
+
+ /** Field to which the elements belong. */
+ private final Field<T> field;
+
+ /** Entries of LU decomposition. */
+ private final T[][] lu;
+
+ /** Pivot permutation associated with LU decomposition. */
+ private final int[] pivot;
+
+ /** Singularity indicator. */
+ private final boolean singular;
+
+ /**
+ * Build a solver from decomposed matrix.
+ *
+ * @param field field to which the matrix elements belong
+ * @param lu entries of LU decomposition
+ * @param pivot pivot permutation associated with LU decomposition
+ * @param singular singularity indicator
+ */
+ private Solver(
+ final Field<T> field, final T[][] lu, final int[] pivot, final boolean singular) {
+ this.field = field;
+ this.lu = lu;
+ this.pivot = pivot;
+ this.singular = singular;
+ }
+
+ /** {@inheritDoc} */
+ public boolean isNonSingular() {
+ return !singular;
+ }
+
+ /** {@inheritDoc} */
+ public FieldVector<T> solve(FieldVector<T> b) {
+ try {
+ return solve((ArrayFieldVector<T>) b);
+ } catch (ClassCastException cce) {
+
+ final int m = pivot.length;
+ if (b.getDimension() != m) {
+ throw new DimensionMismatchException(b.getDimension(), m);
+ }
+ if (singular) {
+ throw new SingularMatrixException();
+ }
+
+ // Apply permutations to b
+ final T[] bp = MathArrays.buildArray(field, m);
+ for (int row = 0; row < m; row++) {
+ bp[row] = b.getEntry(pivot[row]);
+ }
+
+ // Solve LY = b
+ for (int col = 0; col < m; col++) {
+ final T bpCol = bp[col];
+ for (int i = col + 1; i < m; i++) {
+ bp[i] = bp[i].subtract(bpCol.multiply(lu[i][col]));
+ }
+ }
+
+ // Solve UX = Y
+ for (int col = m - 1; col >= 0; col--) {
+ bp[col] = bp[col].divide(lu[col][col]);
+ final T bpCol = bp[col];
+ for (int i = 0; i < col; i++) {
+ bp[i] = bp[i].subtract(bpCol.multiply(lu[i][col]));
+ }
+ }
+
+ return new ArrayFieldVector<T>(field, bp, false);
+ }
+ }
+
+ /**
+ * Solve the linear equation A &times; X = B.
+ *
+ * <p>The A matrix is implicit here. It is
+ *
+ * @param b right-hand side of the equation A &times; X = B
+ * @return a vector X such that A &times; X = B
+ * @throws DimensionMismatchException if the matrices dimensions do not match.
+ * @throws SingularMatrixException if the decomposed matrix is singular.
+ */
+ public ArrayFieldVector<T> solve(ArrayFieldVector<T> b) {
+ final int m = pivot.length;
+ final int length = b.getDimension();
+ if (length != m) {
+ throw new DimensionMismatchException(length, m);
+ }
+ if (singular) {
+ throw new SingularMatrixException();
+ }
+
+ // Apply permutations to b
+ final T[] bp = MathArrays.buildArray(field, m);
+ for (int row = 0; row < m; row++) {
+ bp[row] = b.getEntry(pivot[row]);
+ }
+
+ // Solve LY = b
+ for (int col = 0; col < m; col++) {
+ final T bpCol = bp[col];
+ for (int i = col + 1; i < m; i++) {
+ bp[i] = bp[i].subtract(bpCol.multiply(lu[i][col]));
+ }
+ }
+
+ // Solve UX = Y
+ for (int col = m - 1; col >= 0; col--) {
+ bp[col] = bp[col].divide(lu[col][col]);
+ final T bpCol = bp[col];
+ for (int i = 0; i < col; i++) {
+ bp[i] = bp[i].subtract(bpCol.multiply(lu[i][col]));
+ }
+ }
+
+ return new ArrayFieldVector<T>(bp, false);
+ }
+
+ /** {@inheritDoc} */
+ public FieldMatrix<T> solve(FieldMatrix<T> b) {
+ final int m = pivot.length;
+ if (b.getRowDimension() != m) {
+ throw new DimensionMismatchException(b.getRowDimension(), m);
+ }
+ if (singular) {
+ throw new SingularMatrixException();
+ }
+
+ final int nColB = b.getColumnDimension();
+
+ // Apply permutations to b
+ final T[][] bp = MathArrays.buildArray(field, m, nColB);
+ for (int row = 0; row < m; row++) {
+ final T[] bpRow = bp[row];
+ final int pRow = pivot[row];
+ for (int col = 0; col < nColB; col++) {
+ bpRow[col] = b.getEntry(pRow, col);
+ }
+ }
+
+ // Solve LY = b
+ for (int col = 0; col < m; col++) {
+ final T[] bpCol = bp[col];
+ for (int i = col + 1; i < m; i++) {
+ final T[] bpI = bp[i];
+ final T luICol = lu[i][col];
+ for (int j = 0; j < nColB; j++) {
+ bpI[j] = bpI[j].subtract(bpCol[j].multiply(luICol));
+ }
+ }
+ }
+
+ // Solve UX = Y
+ for (int col = m - 1; col >= 0; col--) {
+ final T[] bpCol = bp[col];
+ final T luDiag = lu[col][col];
+ for (int j = 0; j < nColB; j++) {
+ bpCol[j] = bpCol[j].divide(luDiag);
+ }
+ for (int i = 0; i < col; i++) {
+ final T[] bpI = bp[i];
+ final T luICol = lu[i][col];
+ for (int j = 0; j < nColB; j++) {
+ bpI[j] = bpI[j].subtract(bpCol[j].multiply(luICol));
+ }
+ }
+ }
+
+ return new Array2DRowFieldMatrix<T>(field, bp, false);
+ }
+
+ /** {@inheritDoc} */
+ public FieldMatrix<T> getInverse() {
+ final int m = pivot.length;
+ final T one = field.getOne();
+ FieldMatrix<T> identity = new Array2DRowFieldMatrix<T>(field, m, m);
+ for (int i = 0; i < m; ++i) {
+ identity.setEntry(i, i, one);
+ }
+ return solve(identity);
+ }
+ }
+}
diff --git a/src/main/java/org/apache/commons/math3/linear/FieldMatrix.java b/src/main/java/org/apache/commons/math3/linear/FieldMatrix.java
new file mode 100644
index 0000000..b738dd8
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/linear/FieldMatrix.java
@@ -0,0 +1,818 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math3.linear;
+
+import org.apache.commons.math3.Field;
+import org.apache.commons.math3.FieldElement;
+import org.apache.commons.math3.exception.DimensionMismatchException;
+import org.apache.commons.math3.exception.NoDataException;
+import org.apache.commons.math3.exception.NotPositiveException;
+import org.apache.commons.math3.exception.NotStrictlyPositiveException;
+import org.apache.commons.math3.exception.NullArgumentException;
+import org.apache.commons.math3.exception.NumberIsTooSmallException;
+import org.apache.commons.math3.exception.OutOfRangeException;
+
+/**
+ * Interface defining field-valued matrix with basic algebraic operations.
+ *
+ * <p>Matrix element indexing is 0-based -- e.g., <code>getEntry(0, 0)</code> returns the element in
+ * the first row, first column of the matrix.
+ *
+ * @param <T> the type of the field elements
+ */
+public interface FieldMatrix<T extends FieldElement<T>> extends AnyMatrix {
+ /**
+ * Get the type of field elements of the matrix.
+ *
+ * @return the type of field elements of the matrix.
+ */
+ Field<T> getField();
+
+ /**
+ * Create a new FieldMatrix<T> of the same type as the instance with the supplied row and column
+ * dimensions.
+ *
+ * @param rowDimension the number of rows in the new matrix
+ * @param columnDimension the number of columns in the new matrix
+ * @return a new matrix of the same type as the instance
+ * @throws NotStrictlyPositiveException if row or column dimension is not positive.
+ * @since 2.0
+ */
+ FieldMatrix<T> createMatrix(final int rowDimension, final int columnDimension)
+ throws NotStrictlyPositiveException;
+
+ /**
+ * Make a (deep) copy of this.
+ *
+ * @return a copy of this matrix.
+ */
+ FieldMatrix<T> copy();
+
+ /**
+ * Compute the sum of this and m.
+ *
+ * @param m Matrix to be added.
+ * @return {@code this} + {@code m}.
+ * @throws MatrixDimensionMismatchException if {@code m} is not the same size as {@code this}
+ * matrix.
+ */
+ FieldMatrix<T> add(FieldMatrix<T> m) throws MatrixDimensionMismatchException;
+
+ /**
+ * Subtract {@code m} from this matrix.
+ *
+ * @param m Matrix to be subtracted.
+ * @return {@code this} - {@code m}.
+ * @throws MatrixDimensionMismatchException if {@code m} is not the same size as {@code this}
+ * matrix.
+ */
+ FieldMatrix<T> subtract(FieldMatrix<T> m) throws MatrixDimensionMismatchException;
+
+ /**
+ * Increment each entry of this matrix.
+ *
+ * @param d Value to be added to each entry.
+ * @return {@code d} + {@code this}.
+ */
+ FieldMatrix<T> scalarAdd(T d);
+
+ /**
+ * Multiply each entry by {@code d}.
+ *
+ * @param d Value to multiply all entries by.
+ * @return {@code d} * {@code this}.
+ */
+ FieldMatrix<T> scalarMultiply(T d);
+
+ /**
+ * Postmultiply this matrix by {@code m}.
+ *
+ * @param m Matrix to postmultiply by.
+ * @return {@code this} * {@code m}.
+ * @throws DimensionMismatchException if the number of columns of {@code this} matrix is not
+ * equal to the number of rows of matrix {@code m}.
+ */
+ FieldMatrix<T> multiply(FieldMatrix<T> m) throws DimensionMismatchException;
+
+ /**
+ * Premultiply this matrix by {@code m}.
+ *
+ * @param m Matrix to premultiply by.
+ * @return {@code m} * {@code this}.
+ * @throws DimensionMismatchException if the number of columns of {@code m} differs from the
+ * number of rows of {@code this} matrix.
+ */
+ FieldMatrix<T> preMultiply(FieldMatrix<T> m) throws DimensionMismatchException;
+
+ /**
+ * Returns the result multiplying this with itself <code>p</code> times. Depending on the type
+ * of the field elements, T, instability for high powers might occur.
+ *
+ * @param p raise this to power p
+ * @return this^p
+ * @throws NotPositiveException if {@code p < 0}
+ * @throws NonSquareMatrixException if {@code this matrix} is not square
+ */
+ FieldMatrix<T> power(final int p) throws NonSquareMatrixException, NotPositiveException;
+
+ /**
+ * Returns matrix entries as a two-dimensional array.
+ *
+ * @return a 2-dimensional array of entries.
+ */
+ T[][] getData();
+
+ /**
+ * Get a submatrix. Rows and columns are indicated counting from 0 to n - 1.
+ *
+ * @param startRow Initial row index
+ * @param endRow Final row index (inclusive)
+ * @param startColumn Initial column index
+ * @param endColumn Final column index (inclusive)
+ * @return the matrix containing the data of the specified rows and columns.
+ * @throws NumberIsTooSmallException is {@code endRow < startRow} of {@code endColumn <
+ * startColumn}.
+ * @throws OutOfRangeException if the indices are not valid.
+ */
+ FieldMatrix<T> getSubMatrix(int startRow, int endRow, int startColumn, int endColumn)
+ throws NumberIsTooSmallException, OutOfRangeException;
+
+ /**
+ * Get a submatrix. Rows and columns are indicated counting from 0 to n - 1.
+ *
+ * @param selectedRows Array of row indices.
+ * @param selectedColumns Array of column indices.
+ * @return the matrix containing the data in the specified rows and columns.
+ * @throws NoDataException if {@code selectedRows} or {@code selectedColumns} is empty
+ * @throws NullArgumentException if {@code selectedRows} or {@code selectedColumns} is {@code
+ * null}.
+ * @throws OutOfRangeException if row or column selections are not valid.
+ */
+ FieldMatrix<T> getSubMatrix(int[] selectedRows, int[] selectedColumns)
+ throws NoDataException, NullArgumentException, OutOfRangeException;
+
+ /**
+ * Copy a submatrix. Rows and columns are 0-based. The designated submatrix is copied into the
+ * top left portion of the destination array.
+ *
+ * @param startRow Initial row index.
+ * @param endRow Final row index (inclusive).
+ * @param startColumn Initial column index.
+ * @param endColumn Final column index (inclusive).
+ * @param destination The array where the submatrix data should be copied (if larger than
+ * rows/columns counts, only the upper-left part will be modified).
+ * @throws MatrixDimensionMismatchException if the dimensions of {@code destination} are not
+ * large enough to hold the submatrix.
+ * @throws NumberIsTooSmallException if {@code endRow < startRow} or {@code endColumn <
+ * startColumn}.
+ * @throws OutOfRangeException if the indices are not valid.
+ */
+ void copySubMatrix(int startRow, int endRow, int startColumn, int endColumn, T[][] destination)
+ throws MatrixDimensionMismatchException, NumberIsTooSmallException, OutOfRangeException;
+
+ /**
+ * Copy a submatrix. Rows and columns are indicated counting from 0 to n - 1.
+ *
+ * @param selectedRows Array of row indices.
+ * @param selectedColumns Array of column indices.
+ * @param destination Arrays where the submatrix data should be copied (if larger than
+ * rows/columns counts, only the upper-left part will be used)
+ * @throws MatrixDimensionMismatchException if the dimensions of {@code destination} do not
+ * match those of {@code this}.
+ * @throws NoDataException if {@code selectedRows} or {@code selectedColumns} is empty
+ * @throws NullArgumentException if {@code selectedRows} or {@code selectedColumns} is {@code
+ * null}.
+ * @throws OutOfRangeException if the indices are not valid.
+ */
+ void copySubMatrix(int[] selectedRows, int[] selectedColumns, T[][] destination)
+ throws MatrixDimensionMismatchException,
+ NoDataException,
+ NullArgumentException,
+ OutOfRangeException;
+
+ /**
+ * Replace the submatrix starting at {@code (row, column)} using data in the input {@code
+ * subMatrix} array. Indexes are 0-based.
+ *
+ * <p>Example:<br>
+ * Starting with
+ *
+ * <pre>
+ * 1 2 3 4
+ * 5 6 7 8
+ * 9 0 1 2
+ * </pre>
+ *
+ * and <code>subMatrix = {{3, 4} {5,6}}</code>, invoking <code>setSubMatrix(subMatrix,1,1))
+ * </code> will result in
+ *
+ * <pre>
+ * 1 2 3 4
+ * 5 3 4 8
+ * 9 5 6 2
+ * </pre>
+ *
+ * @param subMatrix Array containing the submatrix replacement data.
+ * @param row Row coordinate of the top-left element to be replaced.
+ * @param column Column coordinate of the top-left element to be replaced.
+ * @throws OutOfRangeException if {@code subMatrix} does not fit into this matrix from element
+ * in {@code (row, column)}.
+ * @throws NoDataException if a row or column of {@code subMatrix} is empty.
+ * @throws DimensionMismatchException if {@code subMatrix} is not rectangular (not all rows have
+ * the same length).
+ * @throws NullArgumentException if {@code subMatrix} is {@code null}.
+ * @since 2.0
+ */
+ void setSubMatrix(T[][] subMatrix, int row, int column)
+ throws DimensionMismatchException,
+ OutOfRangeException,
+ NoDataException,
+ NullArgumentException;
+
+ /**
+ * Get the entries in row number {@code row} as a row matrix.
+ *
+ * @param row Row to be fetched.
+ * @return a row matrix.
+ * @throws OutOfRangeException if the specified row index is invalid.
+ */
+ FieldMatrix<T> getRowMatrix(int row) throws OutOfRangeException;
+
+ /**
+ * Set the entries in row number {@code row} as a row matrix.
+ *
+ * @param row Row to be set.
+ * @param matrix Row matrix (must have one row and the same number of columns as the instance).
+ * @throws OutOfRangeException if the specified row index is invalid.
+ * @throws MatrixDimensionMismatchException if the matrix dimensions do not match one instance
+ * row.
+ */
+ void setRowMatrix(int row, FieldMatrix<T> matrix)
+ throws MatrixDimensionMismatchException, OutOfRangeException;
+
+ /**
+ * Get the entries in column number {@code column} as a column matrix.
+ *
+ * @param column Column to be fetched.
+ * @return a column matrix.
+ * @throws OutOfRangeException if the specified column index is invalid.
+ */
+ FieldMatrix<T> getColumnMatrix(int column) throws OutOfRangeException;
+
+ /**
+ * Set the entries in column number {@code column} as a column matrix.
+ *
+ * @param column Column to be set.
+ * @param matrix column matrix (must have one column and the same number of rows as the
+ * instance).
+ * @throws OutOfRangeException if the specified column index is invalid.
+ * @throws MatrixDimensionMismatchException if the matrix dimensions do not match one instance
+ * column.
+ */
+ void setColumnMatrix(int column, FieldMatrix<T> matrix)
+ throws MatrixDimensionMismatchException, OutOfRangeException;
+
+ /**
+ * Get the entries in row number {@code row} as a vector.
+ *
+ * @param row Row to be fetched
+ * @return a row vector.
+ * @throws OutOfRangeException if the specified row index is invalid.
+ */
+ FieldVector<T> getRowVector(int row) throws OutOfRangeException;
+
+ /**
+ * Set the entries in row number {@code row} as a vector.
+ *
+ * @param row Row to be set.
+ * @param vector row vector (must have the same number of columns as the instance).
+ * @throws OutOfRangeException if the specified row index is invalid.
+ * @throws MatrixDimensionMismatchException if the vector dimension does not match one instance
+ * row.
+ */
+ void setRowVector(int row, FieldVector<T> vector)
+ throws MatrixDimensionMismatchException, OutOfRangeException;
+
+ /**
+ * Returns the entries in column number {@code column} as a vector.
+ *
+ * @param column Column to be fetched.
+ * @return a column vector.
+ * @throws OutOfRangeException if the specified column index is invalid.
+ */
+ FieldVector<T> getColumnVector(int column) throws OutOfRangeException;
+
+ /**
+ * Set the entries in column number {@code column} as a vector.
+ *
+ * @param column Column to be set.
+ * @param vector Column vector (must have the same number of rows as the instance).
+ * @throws OutOfRangeException if the specified column index is invalid.
+ * @throws MatrixDimensionMismatchException if the vector dimension does not match one instance
+ * column.
+ */
+ void setColumnVector(int column, FieldVector<T> vector)
+ throws MatrixDimensionMismatchException, OutOfRangeException;
+
+ /**
+ * Get the entries in row number {@code row} as an array.
+ *
+ * @param row Row to be fetched.
+ * @return array of entries in the row.
+ * @throws OutOfRangeException if the specified row index is not valid.
+ */
+ T[] getRow(int row) throws OutOfRangeException;
+
+ /**
+ * Set the entries in row number {@code row} as a row matrix.
+ *
+ * @param row Row to be set.
+ * @param array Row matrix (must have the same number of columns as the instance).
+ * @throws OutOfRangeException if the specified row index is invalid.
+ * @throws MatrixDimensionMismatchException if the array size does not match one instance row.
+ */
+ void setRow(int row, T[] array) throws MatrixDimensionMismatchException, OutOfRangeException;
+
+ /**
+ * Get the entries in column number {@code col} as an array.
+ *
+ * @param column the column to be fetched
+ * @return array of entries in the column
+ * @throws OutOfRangeException if the specified column index is not valid.
+ */
+ T[] getColumn(int column) throws OutOfRangeException;
+
+ /**
+ * Set the entries in column number {@code column} as a column matrix.
+ *
+ * @param column the column to be set
+ * @param array column array (must have the same number of rows as the instance)
+ * @throws OutOfRangeException if the specified column index is invalid.
+ * @throws MatrixDimensionMismatchException if the array size does not match one instance
+ * column.
+ */
+ void setColumn(int column, T[] array)
+ throws MatrixDimensionMismatchException, OutOfRangeException;
+
+ /**
+ * Returns the entry in the specified row and column.
+ *
+ * @param row row location of entry to be fetched
+ * @param column column location of entry to be fetched
+ * @return matrix entry in row,column
+ * @throws OutOfRangeException if the row or column index is not valid.
+ */
+ T getEntry(int row, int column) throws OutOfRangeException;
+
+ /**
+ * Set the entry in the specified row and column.
+ *
+ * @param row row location of entry to be set
+ * @param column column location of entry to be set
+ * @param value matrix entry to be set in row,column
+ * @throws OutOfRangeException if the row or column index is not valid.
+ * @since 2.0
+ */
+ void setEntry(int row, int column, T value) throws OutOfRangeException;
+
+ /**
+ * Change an entry in the specified row and column.
+ *
+ * @param row Row location of entry to be set.
+ * @param column Column location of entry to be set.
+ * @param increment Value to add to the current matrix entry in {@code (row, column)}.
+ * @throws OutOfRangeException if the row or column index is not valid.
+ * @since 2.0
+ */
+ void addToEntry(int row, int column, T increment) throws OutOfRangeException;
+
+ /**
+ * Change an entry in the specified row and column.
+ *
+ * @param row Row location of entry to be set.
+ * @param column Column location of entry to be set.
+ * @param factor Multiplication factor for the current matrix entry in {@code (row,column)}
+ * @throws OutOfRangeException if the row or column index is not valid.
+ * @since 2.0
+ */
+ void multiplyEntry(int row, int column, T factor) throws OutOfRangeException;
+
+ /**
+ * Returns the transpose of this matrix.
+ *
+ * @return transpose matrix
+ */
+ FieldMatrix<T> transpose();
+
+ /**
+ * Returns the <a href="http://mathworld.wolfram.com/MatrixTrace.html">trace</a> of the matrix
+ * (the sum of the elements on the main diagonal).
+ *
+ * @return trace
+ * @throws NonSquareMatrixException if the matrix is not square.
+ */
+ T getTrace() throws NonSquareMatrixException;
+
+ /**
+ * Returns the result of multiplying this by the vector {@code v}.
+ *
+ * @param v the vector to operate on
+ * @return {@code this * v}
+ * @throws DimensionMismatchException if the number of columns of {@code this} matrix is not
+ * equal to the size of the vector {@code v}.
+ */
+ T[] operate(T[] v) throws DimensionMismatchException;
+
+ /**
+ * Returns the result of multiplying this by the vector {@code v}.
+ *
+ * @param v the vector to operate on
+ * @return {@code this * v}
+ * @throws DimensionMismatchException if the number of columns of {@code this} matrix is not
+ * equal to the size of the vector {@code v}.
+ */
+ FieldVector<T> operate(FieldVector<T> v) throws DimensionMismatchException;
+
+ /**
+ * Returns the (row) vector result of premultiplying this by the vector {@code v}.
+ *
+ * @param v the row vector to premultiply by
+ * @return {@code v * this}
+ * @throws DimensionMismatchException if the number of rows of {@code this} matrix is not equal
+ * to the size of the vector {@code v}
+ */
+ T[] preMultiply(T[] v) throws DimensionMismatchException;
+
+ /**
+ * Returns the (row) vector result of premultiplying this by the vector {@code v}.
+ *
+ * @param v the row vector to premultiply by
+ * @return {@code v * this}
+ * @throws DimensionMismatchException if the number of rows of {@code this} matrix is not equal
+ * to the size of the vector {@code v}
+ */
+ FieldVector<T> preMultiply(FieldVector<T> v) throws DimensionMismatchException;
+
+ /**
+ * Visit (and possibly change) all matrix entries in row order.
+ *
+ * <p>Row order starts at upper left and iterating through all elements of a row from left to
+ * right before going to the leftmost element of the next row.
+ *
+ * @param visitor visitor used to process all matrix entries
+ * @see #walkInRowOrder(FieldMatrixPreservingVisitor)
+ * @see #walkInRowOrder(FieldMatrixChangingVisitor, int, int, int, int)
+ * @see #walkInRowOrder(FieldMatrixPreservingVisitor, int, int, int, int)
+ * @see #walkInColumnOrder(FieldMatrixChangingVisitor)
+ * @see #walkInColumnOrder(FieldMatrixPreservingVisitor)
+ * @see #walkInColumnOrder(FieldMatrixChangingVisitor, int, int, int, int)
+ * @see #walkInColumnOrder(FieldMatrixPreservingVisitor, int, int, int, int)
+ * @see #walkInOptimizedOrder(FieldMatrixChangingVisitor)
+ * @see #walkInOptimizedOrder(FieldMatrixPreservingVisitor)
+ * @see #walkInOptimizedOrder(FieldMatrixChangingVisitor, int, int, int, int)
+ * @see #walkInOptimizedOrder(FieldMatrixPreservingVisitor, int, int, int, int)
+ * @return the value returned by {@link FieldMatrixChangingVisitor#end()} at the end of the walk
+ */
+ T walkInRowOrder(FieldMatrixChangingVisitor<T> visitor);
+
+ /**
+ * Visit (but don't change) all matrix entries in row order.
+ *
+ * <p>Row order starts at upper left and iterating through all elements of a row from left to
+ * right before going to the leftmost element of the next row.
+ *
+ * @param visitor visitor used to process all matrix entries
+ * @see #walkInRowOrder(FieldMatrixChangingVisitor)
+ * @see #walkInRowOrder(FieldMatrixChangingVisitor, int, int, int, int)
+ * @see #walkInRowOrder(FieldMatrixPreservingVisitor, int, int, int, int)
+ * @see #walkInColumnOrder(FieldMatrixChangingVisitor)
+ * @see #walkInColumnOrder(FieldMatrixPreservingVisitor)
+ * @see #walkInColumnOrder(FieldMatrixChangingVisitor, int, int, int, int)
+ * @see #walkInColumnOrder(FieldMatrixPreservingVisitor, int, int, int, int)
+ * @see #walkInOptimizedOrder(FieldMatrixChangingVisitor)
+ * @see #walkInOptimizedOrder(FieldMatrixPreservingVisitor)
+ * @see #walkInOptimizedOrder(FieldMatrixChangingVisitor, int, int, int, int)
+ * @see #walkInOptimizedOrder(FieldMatrixPreservingVisitor, int, int, int, int)
+ * @return the value returned by {@link FieldMatrixPreservingVisitor#end()} at the end of the
+ * walk
+ */
+ T walkInRowOrder(FieldMatrixPreservingVisitor<T> visitor);
+
+ /**
+ * Visit (and possibly change) some matrix entries in row order.
+ *
+ * <p>Row order starts at upper left and iterating through all elements of a row from left to
+ * right before going to the leftmost element of the next row.
+ *
+ * @param visitor visitor used to process all matrix entries
+ * @param startRow Initial row index
+ * @param endRow Final row index (inclusive)
+ * @param startColumn Initial column index
+ * @param endColumn Final column index
+ * @throws OutOfRangeException if the indices are not valid.
+ * @throws NumberIsTooSmallException if {@code endRow < startRow} or {@code endColumn <
+ * startColumn}.
+ * @see #walkInRowOrder(FieldMatrixChangingVisitor)
+ * @see #walkInRowOrder(FieldMatrixPreservingVisitor)
+ * @see #walkInRowOrder(FieldMatrixPreservingVisitor, int, int, int, int)
+ * @see #walkInColumnOrder(FieldMatrixChangingVisitor)
+ * @see #walkInColumnOrder(FieldMatrixPreservingVisitor)
+ * @see #walkInColumnOrder(FieldMatrixChangingVisitor, int, int, int, int)
+ * @see #walkInColumnOrder(FieldMatrixPreservingVisitor, int, int, int, int)
+ * @see #walkInOptimizedOrder(FieldMatrixChangingVisitor)
+ * @see #walkInOptimizedOrder(FieldMatrixPreservingVisitor)
+ * @see #walkInOptimizedOrder(FieldMatrixChangingVisitor, int, int, int, int)
+ * @see #walkInOptimizedOrder(FieldMatrixPreservingVisitor, int, int, int, int)
+ * @return the value returned by {@link FieldMatrixChangingVisitor#end()} at the end of the walk
+ */
+ T walkInRowOrder(
+ FieldMatrixChangingVisitor<T> visitor,
+ int startRow,
+ int endRow,
+ int startColumn,
+ int endColumn)
+ throws OutOfRangeException, NumberIsTooSmallException;
+
+ /**
+ * Visit (but don't change) some matrix entries in row order.
+ *
+ * <p>Row order starts at upper left and iterating through all elements of a row from left to
+ * right before going to the leftmost element of the next row.
+ *
+ * @param visitor visitor used to process all matrix entries
+ * @param startRow Initial row index
+ * @param endRow Final row index (inclusive)
+ * @param startColumn Initial column index
+ * @param endColumn Final column index
+ * @throws OutOfRangeException if the indices are not valid.
+ * @throws NumberIsTooSmallException if {@code endRow < startRow} or {@code endColumn <
+ * startColumn}.
+ * @see #walkInRowOrder(FieldMatrixChangingVisitor)
+ * @see #walkInRowOrder(FieldMatrixPreservingVisitor)
+ * @see #walkInRowOrder(FieldMatrixChangingVisitor, int, int, int, int)
+ * @see #walkInColumnOrder(FieldMatrixChangingVisitor)
+ * @see #walkInColumnOrder(FieldMatrixPreservingVisitor)
+ * @see #walkInColumnOrder(FieldMatrixChangingVisitor, int, int, int, int)
+ * @see #walkInColumnOrder(FieldMatrixPreservingVisitor, int, int, int, int)
+ * @see #walkInOptimizedOrder(FieldMatrixChangingVisitor)
+ * @see #walkInOptimizedOrder(FieldMatrixPreservingVisitor)
+ * @see #walkInOptimizedOrder(FieldMatrixChangingVisitor, int, int, int, int)
+ * @see #walkInOptimizedOrder(FieldMatrixPreservingVisitor, int, int, int, int)
+ * @return the value returned by {@link FieldMatrixPreservingVisitor#end()} at the end of the
+ * walk
+ */
+ T walkInRowOrder(
+ FieldMatrixPreservingVisitor<T> visitor,
+ int startRow,
+ int endRow,
+ int startColumn,
+ int endColumn)
+ throws OutOfRangeException, NumberIsTooSmallException;
+
+ /**
+ * Visit (and possibly change) all matrix entries in column order.
+ *
+ * <p>Column order starts at upper left and iterating through all elements of a column from top
+ * to bottom before going to the topmost element of the next column.
+ *
+ * @param visitor visitor used to process all matrix entries
+ * @see #walkInRowOrder(FieldMatrixChangingVisitor)
+ * @see #walkInRowOrder(FieldMatrixPreservingVisitor)
+ * @see #walkInRowOrder(FieldMatrixChangingVisitor, int, int, int, int)
+ * @see #walkInRowOrder(FieldMatrixPreservingVisitor, int, int, int, int)
+ * @see #walkInColumnOrder(FieldMatrixPreservingVisitor)
+ * @see #walkInColumnOrder(FieldMatrixChangingVisitor, int, int, int, int)
+ * @see #walkInColumnOrder(FieldMatrixPreservingVisitor, int, int, int, int)
+ * @see #walkInOptimizedOrder(FieldMatrixChangingVisitor)
+ * @see #walkInOptimizedOrder(FieldMatrixPreservingVisitor)
+ * @see #walkInOptimizedOrder(FieldMatrixChangingVisitor, int, int, int, int)
+ * @see #walkInOptimizedOrder(FieldMatrixPreservingVisitor, int, int, int, int)
+ * @return the value returned by {@link FieldMatrixChangingVisitor#end()} at the end of the walk
+ */
+ T walkInColumnOrder(FieldMatrixChangingVisitor<T> visitor);
+
+ /**
+ * Visit (but don't change) all matrix entries in column order.
+ *
+ * <p>Column order starts at upper left and iterating through all elements of a column from top
+ * to bottom before going to the topmost element of the next column.
+ *
+ * @param visitor visitor used to process all matrix entries
+ * @see #walkInRowOrder(FieldMatrixChangingVisitor)
+ * @see #walkInRowOrder(FieldMatrixPreservingVisitor)
+ * @see #walkInRowOrder(FieldMatrixChangingVisitor, int, int, int, int)
+ * @see #walkInRowOrder(FieldMatrixPreservingVisitor, int, int, int, int)
+ * @see #walkInColumnOrder(FieldMatrixChangingVisitor)
+ * @see #walkInColumnOrder(FieldMatrixChangingVisitor, int, int, int, int)
+ * @see #walkInColumnOrder(FieldMatrixPreservingVisitor, int, int, int, int)
+ * @see #walkInOptimizedOrder(FieldMatrixChangingVisitor)
+ * @see #walkInOptimizedOrder(FieldMatrixPreservingVisitor)
+ * @see #walkInOptimizedOrder(FieldMatrixChangingVisitor, int, int, int, int)
+ * @see #walkInOptimizedOrder(FieldMatrixPreservingVisitor, int, int, int, int)
+ * @return the value returned by {@link FieldMatrixPreservingVisitor#end()} at the end of the
+ * walk
+ */
+ T walkInColumnOrder(FieldMatrixPreservingVisitor<T> visitor);
+
+ /**
+ * Visit (and possibly change) some matrix entries in column order.
+ *
+ * <p>Column order starts at upper left and iterating through all elements of a column from top
+ * to bottom before going to the topmost element of the next column.
+ *
+ * @param visitor visitor used to process all matrix entries
+ * @param startRow Initial row index
+ * @param endRow Final row index (inclusive)
+ * @param startColumn Initial column index
+ * @param endColumn Final column index
+ * @throws NumberIsTooSmallException if {@code endRow < startRow} or {@code endColumn <
+ * startColumn}.
+ * @throws OutOfRangeException if the indices are not valid.
+ * @see #walkInRowOrder(FieldMatrixChangingVisitor)
+ * @see #walkInRowOrder(FieldMatrixPreservingVisitor)
+ * @see #walkInRowOrder(FieldMatrixChangingVisitor, int, int, int, int)
+ * @see #walkInRowOrder(FieldMatrixPreservingVisitor, int, int, int, int)
+ * @see #walkInColumnOrder(FieldMatrixChangingVisitor)
+ * @see #walkInColumnOrder(FieldMatrixPreservingVisitor)
+ * @see #walkInColumnOrder(FieldMatrixPreservingVisitor, int, int, int, int)
+ * @see #walkInOptimizedOrder(FieldMatrixChangingVisitor)
+ * @see #walkInOptimizedOrder(FieldMatrixPreservingVisitor)
+ * @see #walkInOptimizedOrder(FieldMatrixChangingVisitor, int, int, int, int)
+ * @see #walkInOptimizedOrder(FieldMatrixPreservingVisitor, int, int, int, int)
+ * @return the value returned by {@link FieldMatrixChangingVisitor#end()} at the end of the walk
+ */
+ T walkInColumnOrder(
+ FieldMatrixChangingVisitor<T> visitor,
+ int startRow,
+ int endRow,
+ int startColumn,
+ int endColumn)
+ throws NumberIsTooSmallException, OutOfRangeException;
+
+ /**
+ * Visit (but don't change) some matrix entries in column order.
+ *
+ * <p>Column order starts at upper left and iterating through all elements of a column from top
+ * to bottom before going to the topmost element of the next column.
+ *
+ * @param visitor visitor used to process all matrix entries
+ * @param startRow Initial row index
+ * @param endRow Final row index (inclusive)
+ * @param startColumn Initial column index
+ * @param endColumn Final column index
+ * @throws NumberIsTooSmallException if {@code endRow < startRow} or {@code endColumn <
+ * startColumn}.
+ * @throws OutOfRangeException if the indices are not valid.
+ * @see #walkInRowOrder(FieldMatrixChangingVisitor)
+ * @see #walkInRowOrder(FieldMatrixPreservingVisitor)
+ * @see #walkInRowOrder(FieldMatrixChangingVisitor, int, int, int, int)
+ * @see #walkInRowOrder(FieldMatrixPreservingVisitor, int, int, int, int)
+ * @see #walkInColumnOrder(FieldMatrixChangingVisitor)
+ * @see #walkInColumnOrder(FieldMatrixPreservingVisitor)
+ * @see #walkInColumnOrder(FieldMatrixChangingVisitor, int, int, int, int)
+ * @see #walkInOptimizedOrder(FieldMatrixChangingVisitor)
+ * @see #walkInOptimizedOrder(FieldMatrixPreservingVisitor)
+ * @see #walkInOptimizedOrder(FieldMatrixChangingVisitor, int, int, int, int)
+ * @see #walkInOptimizedOrder(FieldMatrixPreservingVisitor, int, int, int, int)
+ * @return the value returned by {@link FieldMatrixPreservingVisitor#end()} at the end of the
+ * walk
+ */
+ T walkInColumnOrder(
+ FieldMatrixPreservingVisitor<T> visitor,
+ int startRow,
+ int endRow,
+ int startColumn,
+ int endColumn)
+ throws NumberIsTooSmallException, OutOfRangeException;
+
+ /**
+ * Visit (and possibly change) all matrix entries using the fastest possible order.
+ *
+ * <p>The fastest walking order depends on the exact matrix class. It may be different from
+ * traditional row or column orders.
+ *
+ * @param visitor visitor used to process all matrix entries
+ * @see #walkInRowOrder(FieldMatrixChangingVisitor)
+ * @see #walkInRowOrder(FieldMatrixPreservingVisitor)
+ * @see #walkInRowOrder(FieldMatrixChangingVisitor, int, int, int, int)
+ * @see #walkInRowOrder(FieldMatrixPreservingVisitor, int, int, int, int)
+ * @see #walkInColumnOrder(FieldMatrixChangingVisitor)
+ * @see #walkInColumnOrder(FieldMatrixPreservingVisitor)
+ * @see #walkInColumnOrder(FieldMatrixChangingVisitor, int, int, int, int)
+ * @see #walkInColumnOrder(FieldMatrixPreservingVisitor, int, int, int, int)
+ * @see #walkInOptimizedOrder(FieldMatrixPreservingVisitor)
+ * @see #walkInOptimizedOrder(FieldMatrixChangingVisitor, int, int, int, int)
+ * @see #walkInOptimizedOrder(FieldMatrixPreservingVisitor, int, int, int, int)
+ * @return the value returned by {@link FieldMatrixChangingVisitor#end()} at the end of the walk
+ */
+ T walkInOptimizedOrder(FieldMatrixChangingVisitor<T> visitor);
+
+ /**
+ * Visit (but don't change) all matrix entries using the fastest possible order.
+ *
+ * <p>The fastest walking order depends on the exact matrix class. It may be different from
+ * traditional row or column orders.
+ *
+ * @param visitor visitor used to process all matrix entries
+ * @see #walkInRowOrder(FieldMatrixChangingVisitor)
+ * @see #walkInRowOrder(FieldMatrixPreservingVisitor)
+ * @see #walkInRowOrder(FieldMatrixChangingVisitor, int, int, int, int)
+ * @see #walkInRowOrder(FieldMatrixPreservingVisitor, int, int, int, int)
+ * @see #walkInColumnOrder(FieldMatrixChangingVisitor)
+ * @see #walkInColumnOrder(FieldMatrixPreservingVisitor)
+ * @see #walkInColumnOrder(FieldMatrixChangingVisitor, int, int, int, int)
+ * @see #walkInColumnOrder(FieldMatrixPreservingVisitor, int, int, int, int)
+ * @see #walkInOptimizedOrder(FieldMatrixChangingVisitor)
+ * @see #walkInOptimizedOrder(FieldMatrixChangingVisitor, int, int, int, int)
+ * @see #walkInOptimizedOrder(FieldMatrixPreservingVisitor, int, int, int, int)
+ * @return the value returned by {@link FieldMatrixPreservingVisitor#end()} at the end of the
+ * walk
+ */
+ T walkInOptimizedOrder(FieldMatrixPreservingVisitor<T> visitor);
+
+ /**
+ * Visit (and possibly change) some matrix entries using the fastest possible order.
+ *
+ * <p>The fastest walking order depends on the exact matrix class. It may be different from
+ * traditional row or column orders.
+ *
+ * @param visitor visitor used to process all matrix entries
+ * @param startRow Initial row index
+ * @param endRow Final row index (inclusive)
+ * @param startColumn Initial column index
+ * @param endColumn Final column index (inclusive)
+ * @throws NumberIsTooSmallException if {@code endRow < startRow} or {@code endColumn <
+ * startColumn}.
+ * @throws OutOfRangeException if the indices are not valid.
+ * @see #walkInRowOrder(FieldMatrixChangingVisitor)
+ * @see #walkInRowOrder(FieldMatrixPreservingVisitor)
+ * @see #walkInRowOrder(FieldMatrixChangingVisitor, int, int, int, int)
+ * @see #walkInRowOrder(FieldMatrixPreservingVisitor, int, int, int, int)
+ * @see #walkInColumnOrder(FieldMatrixChangingVisitor)
+ * @see #walkInColumnOrder(FieldMatrixPreservingVisitor)
+ * @see #walkInColumnOrder(FieldMatrixChangingVisitor, int, int, int, int)
+ * @see #walkInColumnOrder(FieldMatrixPreservingVisitor, int, int, int, int)
+ * @see #walkInOptimizedOrder(FieldMatrixChangingVisitor)
+ * @see #walkInOptimizedOrder(FieldMatrixPreservingVisitor)
+ * @see #walkInOptimizedOrder(FieldMatrixPreservingVisitor, int, int, int, int)
+ * @return the value returned by {@link FieldMatrixChangingVisitor#end()} at the end of the walk
+ */
+ T walkInOptimizedOrder(
+ FieldMatrixChangingVisitor<T> visitor,
+ int startRow,
+ int endRow,
+ int startColumn,
+ int endColumn)
+ throws NumberIsTooSmallException, OutOfRangeException;
+
+ /**
+ * Visit (but don't change) some matrix entries using the fastest possible order.
+ *
+ * <p>The fastest walking order depends on the exact matrix class. It may be different from
+ * traditional row or column orders.
+ *
+ * @param visitor visitor used to process all matrix entries
+ * @param startRow Initial row index
+ * @param endRow Final row index (inclusive)
+ * @param startColumn Initial column index
+ * @param endColumn Final column index (inclusive)
+ * @throws NumberIsTooSmallException if {@code endRow < startRow} or {@code endColumn <
+ * startColumn}.
+ * @throws OutOfRangeException if the indices are not valid.
+ * @see #walkInRowOrder(FieldMatrixChangingVisitor)
+ * @see #walkInRowOrder(FieldMatrixPreservingVisitor)
+ * @see #walkInRowOrder(FieldMatrixChangingVisitor, int, int, int, int)
+ * @see #walkInRowOrder(FieldMatrixPreservingVisitor, int, int, int, int)
+ * @see #walkInColumnOrder(FieldMatrixChangingVisitor)
+ * @see #walkInColumnOrder(FieldMatrixPreservingVisitor)
+ * @see #walkInColumnOrder(FieldMatrixChangingVisitor, int, int, int, int)
+ * @see #walkInColumnOrder(FieldMatrixPreservingVisitor, int, int, int, int)
+ * @see #walkInOptimizedOrder(FieldMatrixChangingVisitor)
+ * @see #walkInOptimizedOrder(FieldMatrixPreservingVisitor)
+ * @see #walkInOptimizedOrder(FieldMatrixChangingVisitor, int, int, int, int)
+ * @return the value returned by {@link FieldMatrixPreservingVisitor#end()} at the end of the
+ * walk
+ */
+ T walkInOptimizedOrder(
+ FieldMatrixPreservingVisitor<T> visitor,
+ int startRow,
+ int endRow,
+ int startColumn,
+ int endColumn)
+ throws NumberIsTooSmallException, OutOfRangeException;
+}
diff --git a/src/main/java/org/apache/commons/math3/linear/FieldMatrixChangingVisitor.java b/src/main/java/org/apache/commons/math3/linear/FieldMatrixChangingVisitor.java
new file mode 100644
index 0000000..0a0c391
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/linear/FieldMatrixChangingVisitor.java
@@ -0,0 +1,61 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math3.linear;
+
+import org.apache.commons.math3.FieldElement;
+
+/**
+ * Interface defining a visitor for matrix entries.
+ *
+ * @param <T> the type of the field elements
+ * @since 2.0
+ */
+public interface FieldMatrixChangingVisitor<T extends FieldElement<?>> {
+ /**
+ * Start visiting a matrix.
+ *
+ * <p>This method is called once before any entry of the matrix is visited.
+ *
+ * @param rows number of rows of the matrix
+ * @param columns number of columns of the matrix
+ * @param startRow Initial row index
+ * @param endRow Final row index (inclusive)
+ * @param startColumn Initial column index
+ * @param endColumn Final column index (inclusive)
+ */
+ void start(int rows, int columns, int startRow, int endRow, int startColumn, int endColumn);
+
+ /**
+ * Visit one matrix entry.
+ *
+ * @param row row index of the entry
+ * @param column column index of the entry
+ * @param value current value of the entry
+ * @return the new value to be set for the entry
+ */
+ T visit(int row, int column, T value);
+
+ /**
+ * End visiting a matrix.
+ *
+ * <p>This method is called once after all entries of the matrix have been visited.
+ *
+ * @return the value that the <code>walkInXxxOrder</code> must return
+ */
+ T end();
+}
diff --git a/src/main/java/org/apache/commons/math3/linear/FieldMatrixPreservingVisitor.java b/src/main/java/org/apache/commons/math3/linear/FieldMatrixPreservingVisitor.java
new file mode 100644
index 0000000..7774fcd
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/linear/FieldMatrixPreservingVisitor.java
@@ -0,0 +1,60 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math3.linear;
+
+import org.apache.commons.math3.FieldElement;
+
+/**
+ * Interface defining a visitor for matrix entries.
+ *
+ * @param <T> the type of the field elements
+ * @since 2.0
+ */
+public interface FieldMatrixPreservingVisitor<T extends FieldElement<?>> {
+ /**
+ * Start visiting a matrix.
+ *
+ * <p>This method is called once before any entry of the matrix is visited.
+ *
+ * @param rows number of rows of the matrix
+ * @param columns number of columns of the matrix
+ * @param startRow Initial row index
+ * @param endRow Final row index (inclusive)
+ * @param startColumn Initial column index
+ * @param endColumn Final column index (inclusive)
+ */
+ void start(int rows, int columns, int startRow, int endRow, int startColumn, int endColumn);
+
+ /**
+ * Visit one matrix entry.
+ *
+ * @param row row index of the entry
+ * @param column column index of the entry
+ * @param value current value of the entry
+ */
+ void visit(int row, int column, T value);
+
+ /**
+ * End visiting a matrix.
+ *
+ * <p>This method is called once after all entries of the matrix have been visited.
+ *
+ * @return the value that the <code>walkInXxxOrder</code> must return
+ */
+ T end();
+}
diff --git a/src/main/java/org/apache/commons/math3/linear/FieldVector.java b/src/main/java/org/apache/commons/math3/linear/FieldVector.java
new file mode 100644
index 0000000..58e9d38
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/linear/FieldVector.java
@@ -0,0 +1,324 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math3.linear;
+
+import org.apache.commons.math3.Field;
+import org.apache.commons.math3.FieldElement;
+import org.apache.commons.math3.exception.DimensionMismatchException;
+import org.apache.commons.math3.exception.MathArithmeticException;
+import org.apache.commons.math3.exception.NotPositiveException;
+import org.apache.commons.math3.exception.NullArgumentException;
+import org.apache.commons.math3.exception.OutOfRangeException;
+
+/**
+ * Interface defining a field-valued vector with basic algebraic operations.
+ *
+ * <p>vector element indexing is 0-based -- e.g., <code>getEntry(0)</code> returns the first element
+ * of the vector.
+ *
+ * <p>The various <code>mapXxx</code> and <code>mapXxxToSelf</code> methods operate on vectors
+ * element-wise, i.e. they perform the same operation (adding a scalar, applying a function ...) on
+ * each element in turn. The <code>mapXxx</code> versions create a new vector to hold the result and
+ * do not change the instance. The <code>mapXxxToSelf</code> versions use the instance itself to
+ * store the results, so the instance is changed by these methods. In both cases, the result vector
+ * is returned by the methods, this allows to use the <i>fluent API</i> style, like this:
+ *
+ * <pre>
+ * RealVector result = v.mapAddToSelf(3.0).mapTanToSelf().mapSquareToSelf();
+ * </pre>
+ *
+ * <p>Note that as almost all operations on {@link FieldElement} throw {@link NullArgumentException}
+ * when operating on a null element, it is the responsibility of <code>FieldVector</code>
+ * implementations to make sure no null elements are inserted into the vector. This must be done in
+ * all constructors and all setters.
+ *
+ * <p>
+ *
+ * @param <T> the type of the field elements
+ * @since 2.0
+ */
+public interface FieldVector<T extends FieldElement<T>> {
+
+ /**
+ * Get the type of field elements of the vector.
+ *
+ * @return type of field elements of the vector
+ */
+ Field<T> getField();
+
+ /**
+ * Returns a (deep) copy of this.
+ *
+ * @return vector copy
+ */
+ FieldVector<T> copy();
+
+ /**
+ * Compute the sum of {@code this} and {@code v}.
+ *
+ * @param v vector to be added
+ * @return {@code this + v}
+ * @throws DimensionMismatchException if {@code v} is not the same size as {@code this}
+ */
+ FieldVector<T> add(FieldVector<T> v) throws DimensionMismatchException;
+
+ /**
+ * Compute {@code this} minus {@code v}.
+ *
+ * @param v vector to be subtracted
+ * @return {@code this - v}
+ * @throws DimensionMismatchException if {@code v} is not the same size as {@code this}
+ */
+ FieldVector<T> subtract(FieldVector<T> v) throws DimensionMismatchException;
+
+ /**
+ * Map an addition operation to each entry.
+ *
+ * @param d value to be added to each entry
+ * @return {@code this + d}
+ * @throws NullArgumentException if {@code d} is {@code null}.
+ */
+ FieldVector<T> mapAdd(T d) throws NullArgumentException;
+
+ /**
+ * Map an addition operation to each entry.
+ *
+ * <p>The instance <strong>is</strong> changed by this method.
+ *
+ * @param d value to be added to each entry
+ * @return for convenience, return {@code this}
+ * @throws NullArgumentException if {@code d} is {@code null}.
+ */
+ FieldVector<T> mapAddToSelf(T d) throws NullArgumentException;
+
+ /**
+ * Map a subtraction operation to each entry.
+ *
+ * @param d value to be subtracted to each entry
+ * @return {@code this - d}
+ * @throws NullArgumentException if {@code d} is {@code null}
+ */
+ FieldVector<T> mapSubtract(T d) throws NullArgumentException;
+
+ /**
+ * Map a subtraction operation to each entry.
+ *
+ * <p>The instance <strong>is</strong> changed by this method.
+ *
+ * @param d value to be subtracted to each entry
+ * @return for convenience, return {@code this}
+ * @throws NullArgumentException if {@code d} is {@code null}
+ */
+ FieldVector<T> mapSubtractToSelf(T d) throws NullArgumentException;
+
+ /**
+ * Map a multiplication operation to each entry.
+ *
+ * @param d value to multiply all entries by
+ * @return {@code this * d}
+ * @throws NullArgumentException if {@code d} is {@code null}.
+ */
+ FieldVector<T> mapMultiply(T d) throws NullArgumentException;
+
+ /**
+ * Map a multiplication operation to each entry.
+ *
+ * <p>The instance <strong>is</strong> changed by this method.
+ *
+ * @param d value to multiply all entries by
+ * @return for convenience, return {@code this}
+ * @throws NullArgumentException if {@code d} is {@code null}.
+ */
+ FieldVector<T> mapMultiplyToSelf(T d) throws NullArgumentException;
+
+ /**
+ * Map a division operation to each entry.
+ *
+ * @param d value to divide all entries by
+ * @return {@code this / d}
+ * @throws NullArgumentException if {@code d} is {@code null}.
+ * @throws MathArithmeticException if {@code d} is zero.
+ */
+ FieldVector<T> mapDivide(T d) throws NullArgumentException, MathArithmeticException;
+
+ /**
+ * Map a division operation to each entry.
+ *
+ * <p>The instance <strong>is</strong> changed by this method.
+ *
+ * @param d value to divide all entries by
+ * @return for convenience, return {@code this}
+ * @throws NullArgumentException if {@code d} is {@code null}.
+ * @throws MathArithmeticException if {@code d} is zero.
+ */
+ FieldVector<T> mapDivideToSelf(T d) throws NullArgumentException, MathArithmeticException;
+
+ /**
+ * Map the 1/x function to each entry.
+ *
+ * @return a vector containing the result of applying the function to each entry.
+ * @throws MathArithmeticException if one of the entries is zero.
+ */
+ FieldVector<T> mapInv() throws MathArithmeticException;
+
+ /**
+ * Map the 1/x function to each entry.
+ *
+ * <p>The instance <strong>is</strong> changed by this method.
+ *
+ * @return for convenience, return {@code this}
+ * @throws MathArithmeticException if one of the entries is zero.
+ */
+ FieldVector<T> mapInvToSelf() throws MathArithmeticException;
+
+ /**
+ * Element-by-element multiplication.
+ *
+ * @param v vector by which instance elements must be multiplied
+ * @return a vector containing {@code this[i] * v[i]} for all {@code i}
+ * @throws DimensionMismatchException if {@code v} is not the same size as {@code this}
+ */
+ FieldVector<T> ebeMultiply(FieldVector<T> v) throws DimensionMismatchException;
+
+ /**
+ * Element-by-element division.
+ *
+ * @param v vector by which instance elements must be divided
+ * @return a vector containing {@code this[i] / v[i]} for all {@code i}
+ * @throws DimensionMismatchException if {@code v} is not the same size as {@code this}
+ * @throws MathArithmeticException if one entry of {@code v} is zero.
+ */
+ FieldVector<T> ebeDivide(FieldVector<T> v)
+ throws DimensionMismatchException, MathArithmeticException;
+
+ /**
+ * Returns vector entries as a T array.
+ *
+ * @return T array of entries
+ * @deprecated as of 3.1, to be removed in 4.0. Please use the {@link #toArray()} method
+ * instead.
+ */
+ @Deprecated
+ T[] getData();
+
+ /**
+ * Compute the dot product.
+ *
+ * @param v vector with which dot product should be computed
+ * @return the scalar dot product of {@code this} and {@code v}
+ * @throws DimensionMismatchException if {@code v} is not the same size as {@code this}
+ */
+ T dotProduct(FieldVector<T> v) throws DimensionMismatchException;
+
+ /**
+ * Find the orthogonal projection of this vector onto another vector.
+ *
+ * @param v vector onto which {@code this} must be projected
+ * @return projection of {@code this} onto {@code v}
+ * @throws DimensionMismatchException if {@code v} is not the same size as {@code this}
+ * @throws MathArithmeticException if {@code v} is the null vector.
+ */
+ FieldVector<T> projection(FieldVector<T> v)
+ throws DimensionMismatchException, MathArithmeticException;
+
+ /**
+ * Compute the outer product.
+ *
+ * @param v vector with which outer product should be computed
+ * @return the matrix outer product between instance and v
+ */
+ FieldMatrix<T> outerProduct(FieldVector<T> v);
+
+ /**
+ * Returns the entry in the specified index.
+ *
+ * @param index Index location of entry to be fetched.
+ * @return the vector entry at {@code index}.
+ * @throws OutOfRangeException if the index is not valid.
+ * @see #setEntry(int, FieldElement)
+ */
+ T getEntry(int index) throws OutOfRangeException;
+
+ /**
+ * Set a single element.
+ *
+ * @param index element index.
+ * @param value new value for the element.
+ * @throws OutOfRangeException if the index is not valid.
+ * @see #getEntry(int)
+ */
+ void setEntry(int index, T value) throws OutOfRangeException;
+
+ /**
+ * Returns the size of the vector.
+ *
+ * @return size
+ */
+ int getDimension();
+
+ /**
+ * Construct a vector by appending a vector to this vector.
+ *
+ * @param v vector to append to this one.
+ * @return a new vector
+ */
+ FieldVector<T> append(FieldVector<T> v);
+
+ /**
+ * Construct a vector by appending a T to this vector.
+ *
+ * @param d T to append.
+ * @return a new vector
+ */
+ FieldVector<T> append(T d);
+
+ /**
+ * Get a subvector from consecutive elements.
+ *
+ * @param index index of first element.
+ * @param n number of elements to be retrieved.
+ * @return a vector containing n elements.
+ * @throws OutOfRangeException if the index is not valid.
+ * @throws NotPositiveException if the number of elements if not positive.
+ */
+ FieldVector<T> getSubVector(int index, int n) throws OutOfRangeException, NotPositiveException;
+
+ /**
+ * Set a set of consecutive elements.
+ *
+ * @param index index of first element to be set.
+ * @param v vector containing the values to set.
+ * @throws OutOfRangeException if the index is not valid.
+ */
+ void setSubVector(int index, FieldVector<T> v) throws OutOfRangeException;
+
+ /**
+ * Set all elements to a single value.
+ *
+ * @param value single value to set for all elements
+ */
+ void set(T value);
+
+ /**
+ * Convert the vector to a T array.
+ *
+ * <p>The array is independent from vector data, it's elements are copied.
+ *
+ * @return array containing a copy of vector elements
+ */
+ T[] toArray();
+}
diff --git a/src/main/java/org/apache/commons/math3/linear/FieldVectorChangingVisitor.java b/src/main/java/org/apache/commons/math3/linear/FieldVectorChangingVisitor.java
new file mode 100644
index 0000000..2637a1d
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/linear/FieldVectorChangingVisitor.java
@@ -0,0 +1,55 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math3.linear;
+
+import org.apache.commons.math3.FieldElement;
+
+/**
+ * This interface defines a visitor for the entries of a vector. Visitors implementing this
+ * interface may alter the entries of the vector being visited.
+ *
+ * @param <T> the type of the field elements
+ * @since 3.3
+ */
+public interface FieldVectorChangingVisitor<T extends FieldElement<?>> {
+ /**
+ * Start visiting a vector. This method is called once, before any entry of the vector is
+ * visited.
+ *
+ * @param dimension the size of the vector
+ * @param start the index of the first entry to be visited
+ * @param end the index of the last entry to be visited (inclusive)
+ */
+ void start(int dimension, int start, int end);
+
+ /**
+ * Visit one entry of the vector.
+ *
+ * @param index the index of the entry being visited
+ * @param value the value of the entry being visited
+ * @return the new value of the entry being visited
+ */
+ T visit(int index, T value);
+
+ /**
+ * End visiting a vector. This method is called once, after all entries of the vector have been
+ * visited.
+ *
+ * @return the value returned after visiting all entries
+ */
+ T end();
+}
diff --git a/src/main/java/org/apache/commons/math3/linear/FieldVectorPreservingVisitor.java b/src/main/java/org/apache/commons/math3/linear/FieldVectorPreservingVisitor.java
new file mode 100644
index 0000000..f6b8751
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/linear/FieldVectorPreservingVisitor.java
@@ -0,0 +1,54 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math3.linear;
+
+import org.apache.commons.math3.FieldElement;
+
+/**
+ * This interface defines a visitor for the entries of a vector. Visitors implementing this
+ * interface do not alter the entries of the vector being visited.
+ *
+ * @param <T> the type of the field elements
+ * @since 3.3
+ */
+public interface FieldVectorPreservingVisitor<T extends FieldElement<?>> {
+ /**
+ * Start visiting a vector. This method is called once, before any entry of the vector is
+ * visited.
+ *
+ * @param dimension the size of the vector
+ * @param start the index of the first entry to be visited
+ * @param end the index of the last entry to be visited (inclusive)
+ */
+ void start(int dimension, int start, int end);
+
+ /**
+ * Visit one entry of the vector.
+ *
+ * @param index the index of the entry being visited
+ * @param value the value of the entry being visited
+ */
+ void visit(int index, T value);
+
+ /**
+ * End visiting a vector. This method is called once, after all entries of the vector have been
+ * visited.
+ *
+ * @return the value returned after visiting all entries
+ */
+ T end();
+}
diff --git a/src/main/java/org/apache/commons/math3/linear/HessenbergTransformer.java b/src/main/java/org/apache/commons/math3/linear/HessenbergTransformer.java
new file mode 100644
index 0000000..541eb1e
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/linear/HessenbergTransformer.java
@@ -0,0 +1,243 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math3.linear;
+
+import org.apache.commons.math3.util.FastMath;
+import org.apache.commons.math3.util.Precision;
+
+/**
+ * Class transforming a general real matrix to Hessenberg form.
+ *
+ * <p>A m &times; m matrix A can be written as the product of three matrices: A = P &times; H
+ * &times; P<sup>T</sup> with P an orthogonal matrix and H a Hessenberg matrix. Both P and H are m
+ * &times; m matrices.
+ *
+ * <p>Transformation to Hessenberg form is often not a goal by itself, but it is an intermediate
+ * step in more general decomposition algorithms like {@link EigenDecomposition eigen
+ * decomposition}. This class is therefore intended for internal use by the library and is not
+ * public. As a consequence of this explicitly limited scope, many methods directly returns
+ * references to internal arrays, not copies.
+ *
+ * <p>This class is based on the method orthes in class EigenvalueDecomposition from the <a
+ * href="http://math.nist.gov/javanumerics/jama/">JAMA</a> library.
+ *
+ * @see <a href="http://mathworld.wolfram.com/HessenbergDecomposition.html">MathWorld</a>
+ * @see <a href="http://en.wikipedia.org/wiki/Householder_transformation">Householder
+ * Transformations</a>
+ * @since 3.1
+ */
+class HessenbergTransformer {
+ /** Householder vectors. */
+ private final double householderVectors[][];
+
+ /** Temporary storage vector. */
+ private final double ort[];
+
+ /** Cached value of P. */
+ private RealMatrix cachedP;
+
+ /** Cached value of Pt. */
+ private RealMatrix cachedPt;
+
+ /** Cached value of H. */
+ private RealMatrix cachedH;
+
+ /**
+ * Build the transformation to Hessenberg form of a general matrix.
+ *
+ * @param matrix matrix to transform
+ * @throws NonSquareMatrixException if the matrix is not square
+ */
+ HessenbergTransformer(final RealMatrix matrix) {
+ if (!matrix.isSquare()) {
+ throw new NonSquareMatrixException(
+ matrix.getRowDimension(), matrix.getColumnDimension());
+ }
+
+ final int m = matrix.getRowDimension();
+ householderVectors = matrix.getData();
+ ort = new double[m];
+ cachedP = null;
+ cachedPt = null;
+ cachedH = null;
+
+ // transform matrix
+ transform();
+ }
+
+ /**
+ * Returns the matrix P of the transform.
+ *
+ * <p>P is an orthogonal matrix, i.e. its inverse is also its transpose.
+ *
+ * @return the P matrix
+ */
+ public RealMatrix getP() {
+ if (cachedP == null) {
+ final int n = householderVectors.length;
+ final int high = n - 1;
+ final double[][] pa = new double[n][n];
+
+ for (int i = 0; i < n; i++) {
+ for (int j = 0; j < n; j++) {
+ pa[i][j] = (i == j) ? 1 : 0;
+ }
+ }
+
+ for (int m = high - 1; m >= 1; m--) {
+ if (householderVectors[m][m - 1] != 0.0) {
+ for (int i = m + 1; i <= high; i++) {
+ ort[i] = householderVectors[i][m - 1];
+ }
+
+ for (int j = m; j <= high; j++) {
+ double g = 0.0;
+
+ for (int i = m; i <= high; i++) {
+ g += ort[i] * pa[i][j];
+ }
+
+ // Double division avoids possible underflow
+ g = (g / ort[m]) / householderVectors[m][m - 1];
+
+ for (int i = m; i <= high; i++) {
+ pa[i][j] += g * ort[i];
+ }
+ }
+ }
+ }
+
+ cachedP = MatrixUtils.createRealMatrix(pa);
+ }
+ return cachedP;
+ }
+
+ /**
+ * Returns the transpose of the matrix P of the transform.
+ *
+ * <p>P is an orthogonal matrix, i.e. its inverse is also its transpose.
+ *
+ * @return the transpose of the P matrix
+ */
+ public RealMatrix getPT() {
+ if (cachedPt == null) {
+ cachedPt = getP().transpose();
+ }
+
+ // return the cached matrix
+ return cachedPt;
+ }
+
+ /**
+ * Returns the Hessenberg matrix H of the transform.
+ *
+ * @return the H matrix
+ */
+ public RealMatrix getH() {
+ if (cachedH == null) {
+ final int m = householderVectors.length;
+ final double[][] h = new double[m][m];
+ for (int i = 0; i < m; ++i) {
+ if (i > 0) {
+ // copy the entry of the lower sub-diagonal
+ h[i][i - 1] = householderVectors[i][i - 1];
+ }
+
+ // copy upper triangular part of the matrix
+ for (int j = i; j < m; ++j) {
+ h[i][j] = householderVectors[i][j];
+ }
+ }
+ cachedH = MatrixUtils.createRealMatrix(h);
+ }
+
+ // return the cached matrix
+ return cachedH;
+ }
+
+ /**
+ * Get the Householder vectors of the transform.
+ *
+ * <p>Note that since this class is only intended for internal use, it returns directly a
+ * reference to its internal arrays, not a copy.
+ *
+ * @return the main diagonal elements of the B matrix
+ */
+ double[][] getHouseholderVectorsRef() {
+ return householderVectors;
+ }
+
+ /**
+ * Transform original matrix to Hessenberg form.
+ *
+ * <p>Transformation is done using Householder transforms.
+ */
+ private void transform() {
+ final int n = householderVectors.length;
+ final int high = n - 1;
+
+ for (int m = 1; m <= high - 1; m++) {
+ // Scale column.
+ double scale = 0;
+ for (int i = m; i <= high; i++) {
+ scale += FastMath.abs(householderVectors[i][m - 1]);
+ }
+
+ if (!Precision.equals(scale, 0)) {
+ // Compute Householder transformation.
+ double h = 0;
+ for (int i = high; i >= m; i--) {
+ ort[i] = householderVectors[i][m - 1] / scale;
+ h += ort[i] * ort[i];
+ }
+ final double g = (ort[m] > 0) ? -FastMath.sqrt(h) : FastMath.sqrt(h);
+
+ h -= ort[m] * g;
+ ort[m] -= g;
+
+ // Apply Householder similarity transformation
+ // H = (I - u*u' / h) * H * (I - u*u' / h)
+
+ for (int j = m; j < n; j++) {
+ double f = 0;
+ for (int i = high; i >= m; i--) {
+ f += ort[i] * householderVectors[i][j];
+ }
+ f /= h;
+ for (int i = m; i <= high; i++) {
+ householderVectors[i][j] -= f * ort[i];
+ }
+ }
+
+ for (int i = 0; i <= high; i++) {
+ double f = 0;
+ for (int j = high; j >= m; j--) {
+ f += ort[j] * householderVectors[i][j];
+ }
+ f /= h;
+ for (int j = m; j <= high; j++) {
+ householderVectors[i][j] -= f * ort[j];
+ }
+ }
+
+ ort[m] = scale * ort[m];
+ householderVectors[m][m - 1] = scale * g;
+ }
+ }
+ }
+}
diff --git a/src/main/java/org/apache/commons/math3/linear/IllConditionedOperatorException.java b/src/main/java/org/apache/commons/math3/linear/IllConditionedOperatorException.java
new file mode 100644
index 0000000..7d52ec2
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/linear/IllConditionedOperatorException.java
@@ -0,0 +1,39 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math3.linear;
+
+import org.apache.commons.math3.exception.MathIllegalArgumentException;
+import org.apache.commons.math3.exception.util.LocalizedFormats;
+
+/**
+ * An exception to be thrown when the condition number of a {@link RealLinearOperator} is too high.
+ *
+ * @since 3.0
+ */
+public class IllConditionedOperatorException extends MathIllegalArgumentException {
+ /** Serializable version Id. */
+ private static final long serialVersionUID = -7883263944530490135L;
+
+ /**
+ * Creates a new instance of this class.
+ *
+ * @param cond An estimate of the condition number of the offending linear operator.
+ */
+ public IllConditionedOperatorException(final double cond) {
+ super(LocalizedFormats.ILL_CONDITIONED_OPERATOR, cond);
+ }
+}
diff --git a/src/main/java/org/apache/commons/math3/linear/IterativeLinearSolver.java b/src/main/java/org/apache/commons/math3/linear/IterativeLinearSolver.java
new file mode 100644
index 0000000..cb7fa90
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/linear/IterativeLinearSolver.java
@@ -0,0 +1,166 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math3.linear;
+
+import org.apache.commons.math3.exception.DimensionMismatchException;
+import org.apache.commons.math3.exception.MaxCountExceededException;
+import org.apache.commons.math3.exception.NullArgumentException;
+import org.apache.commons.math3.util.IterationManager;
+import org.apache.commons.math3.util.MathUtils;
+
+/**
+ * This abstract class defines an iterative solver for the linear system A &middot; x = b. In what
+ * follows, the <em>residual</em> r is defined as r = b - A &middot; x, where A is the linear
+ * operator of the linear system, b is the right-hand side vector, and x the current estimate of the
+ * solution.
+ *
+ * @since 3.0
+ */
+public abstract class IterativeLinearSolver {
+
+ /** The object in charge of managing the iterations. */
+ private final IterationManager manager;
+
+ /**
+ * Creates a new instance of this class, with default iteration manager.
+ *
+ * @param maxIterations the maximum number of iterations
+ */
+ public IterativeLinearSolver(final int maxIterations) {
+ this.manager = new IterationManager(maxIterations);
+ }
+
+ /**
+ * Creates a new instance of this class, with custom iteration manager.
+ *
+ * @param manager the custom iteration manager
+ * @throws NullArgumentException if {@code manager} is {@code null}
+ */
+ public IterativeLinearSolver(final IterationManager manager) throws NullArgumentException {
+ MathUtils.checkNotNull(manager);
+ this.manager = manager;
+ }
+
+ /**
+ * Performs all dimension checks on the parameters of {@link #solve(RealLinearOperator,
+ * RealVector, RealVector) solve} and {@link #solveInPlace(RealLinearOperator, RealVector,
+ * RealVector) solveInPlace}, and throws an exception if one of the checks fails.
+ *
+ * @param a the linear operator A of the system
+ * @param b the right-hand side vector
+ * @param x0 the initial guess of the solution
+ * @throws NullArgumentException if one of the parameters is {@code null}
+ * @throws NonSquareOperatorException if {@code a} is not square
+ * @throws DimensionMismatchException if {@code b} or {@code x0} have dimensions inconsistent
+ * with {@code a}
+ */
+ protected static void checkParameters(
+ final RealLinearOperator a, final RealVector b, final RealVector x0)
+ throws NullArgumentException, NonSquareOperatorException, DimensionMismatchException {
+ MathUtils.checkNotNull(a);
+ MathUtils.checkNotNull(b);
+ MathUtils.checkNotNull(x0);
+ if (a.getRowDimension() != a.getColumnDimension()) {
+ throw new NonSquareOperatorException(a.getRowDimension(), a.getColumnDimension());
+ }
+ if (b.getDimension() != a.getRowDimension()) {
+ throw new DimensionMismatchException(b.getDimension(), a.getRowDimension());
+ }
+ if (x0.getDimension() != a.getColumnDimension()) {
+ throw new DimensionMismatchException(x0.getDimension(), a.getColumnDimension());
+ }
+ }
+
+ /**
+ * Returns the iteration manager attached to this solver.
+ *
+ * @return the manager
+ */
+ public IterationManager getIterationManager() {
+ return manager;
+ }
+
+ /**
+ * Returns an estimate of the solution to the linear system A &middot; x = b.
+ *
+ * @param a the linear operator A of the system
+ * @param b the right-hand side vector
+ * @return a new vector containing the solution
+ * @throws NullArgumentException if one of the parameters is {@code null}
+ * @throws NonSquareOperatorException if {@code a} is not square
+ * @throws DimensionMismatchException if {@code b} has dimensions inconsistent with {@code a}
+ * @throws MaxCountExceededException at exhaustion of the iteration count, unless a custom
+ * {@link org.apache.commons.math3.util.Incrementor.MaxCountExceededCallback callback} has
+ * been set at construction of the {@link IterationManager}
+ */
+ public RealVector solve(final RealLinearOperator a, final RealVector b)
+ throws NullArgumentException,
+ NonSquareOperatorException,
+ DimensionMismatchException,
+ MaxCountExceededException {
+ MathUtils.checkNotNull(a);
+ final RealVector x = new ArrayRealVector(a.getColumnDimension());
+ x.set(0.);
+ return solveInPlace(a, b, x);
+ }
+
+ /**
+ * Returns an estimate of the solution to the linear system A &middot; x = b.
+ *
+ * @param a the linear operator A of the system
+ * @param b the right-hand side vector
+ * @param x0 the initial guess of the solution
+ * @return a new vector containing the solution
+ * @throws NullArgumentException if one of the parameters is {@code null}
+ * @throws NonSquareOperatorException if {@code a} is not square
+ * @throws DimensionMismatchException if {@code b} or {@code x0} have dimensions inconsistent
+ * with {@code a}
+ * @throws MaxCountExceededException at exhaustion of the iteration count, unless a custom
+ * {@link org.apache.commons.math3.util.Incrementor.MaxCountExceededCallback callback} has
+ * been set at construction of the {@link IterationManager}
+ */
+ public RealVector solve(RealLinearOperator a, RealVector b, RealVector x0)
+ throws NullArgumentException,
+ NonSquareOperatorException,
+ DimensionMismatchException,
+ MaxCountExceededException {
+ MathUtils.checkNotNull(x0);
+ return solveInPlace(a, b, x0.copy());
+ }
+
+ /**
+ * Returns an estimate of the solution to the linear system A &middot; x = b. The solution is
+ * computed in-place (initial guess is modified).
+ *
+ * @param a the linear operator A of the system
+ * @param b the right-hand side vector
+ * @param x0 initial guess of the solution
+ * @return a reference to {@code x0} (shallow copy) updated with the solution
+ * @throws NullArgumentException if one of the parameters is {@code null}
+ * @throws NonSquareOperatorException if {@code a} is not square
+ * @throws DimensionMismatchException if {@code b} or {@code x0} have dimensions inconsistent
+ * with {@code a}
+ * @throws MaxCountExceededException at exhaustion of the iteration count, unless a custom
+ * {@link org.apache.commons.math3.util.Incrementor.MaxCountExceededCallback callback} has
+ * been set at construction of the {@link IterationManager}
+ */
+ public abstract RealVector solveInPlace(RealLinearOperator a, RealVector b, RealVector x0)
+ throws NullArgumentException,
+ NonSquareOperatorException,
+ DimensionMismatchException,
+ MaxCountExceededException;
+}
diff --git a/src/main/java/org/apache/commons/math3/linear/IterativeLinearSolverEvent.java b/src/main/java/org/apache/commons/math3/linear/IterativeLinearSolverEvent.java
new file mode 100644
index 0000000..ad98ca3
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/linear/IterativeLinearSolverEvent.java
@@ -0,0 +1,104 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math3.linear;
+
+import org.apache.commons.math3.exception.MathUnsupportedOperationException;
+import org.apache.commons.math3.util.IterationEvent;
+
+/**
+ * This is the base class for all events occurring during the iterations of a {@link
+ * IterativeLinearSolver}.
+ *
+ * @since 3.0
+ */
+public abstract class IterativeLinearSolverEvent extends IterationEvent {
+ /** Serialization identifier. */
+ private static final long serialVersionUID = 20120129L;
+
+ /**
+ * Creates a new instance of this class.
+ *
+ * @param source the iterative algorithm on which the event initially occurred
+ * @param iterations the number of iterations performed at the time {@code this} event is
+ * created
+ */
+ public IterativeLinearSolverEvent(final Object source, final int iterations) {
+ super(source, iterations);
+ }
+
+ /**
+ * Returns the current right-hand side of the linear system to be solved. This method should
+ * return an unmodifiable view, or a deep copy of the actual right-hand side vector, in order
+ * not to compromise subsequent iterations of the source {@link IterativeLinearSolver}.
+ *
+ * @return the right-hand side vector, b
+ */
+ public abstract RealVector getRightHandSideVector();
+
+ /**
+ * Returns the norm of the residual. The returned value is not required to be <em>exact</em>.
+ * Instead, the norm of the so-called <em>updated</em> residual (if available) should be
+ * returned. For example, the {@link ConjugateGradient conjugate gradient} method computes a
+ * sequence of residuals, the norm of which is cheap to compute. However, due to accumulation of
+ * round-off errors, this residual might differ from the true residual after some iterations.
+ * See e.g. A. Greenbaum and Z. Strakos, <em>Predicting the Behavior of Finite Precision Lanzos
+ * and Conjugate Gradient Computations</em>, Technical Report 538, Department of Computer
+ * Science, New York University, 1991 (available <a
+ * href="http://www.archive.org/details/predictingbehavi00gree">here</a>).
+ *
+ * @return the norm of the residual, ||r||
+ */
+ public abstract double getNormOfResidual();
+
+ /**
+ * Returns the residual. This is an optional operation, as all iterative linear solvers do not
+ * provide cheap estimate of the updated residual vector, in which case
+ *
+ * <ul>
+ * <li>this method should throw a {@link MathUnsupportedOperationException},
+ * <li>{@link #providesResidual()} returns {@code false}.
+ * </ul>
+ *
+ * <p>The default implementation throws a {@link MathUnsupportedOperationException}. If this
+ * method is overriden, then {@link #providesResidual()} should be overriden as well.
+ *
+ * @return the updated residual, r
+ */
+ public RealVector getResidual() {
+ throw new MathUnsupportedOperationException();
+ }
+
+ /**
+ * Returns the current estimate of the solution to the linear system to be solved. This method
+ * should return an unmodifiable view, or a deep copy of the actual current solution, in order
+ * not to compromise subsequent iterations of the source {@link IterativeLinearSolver}.
+ *
+ * @return the solution, x
+ */
+ public abstract RealVector getSolution();
+
+ /**
+ * Returns {@code true} if {@link #getResidual()} is supported. The default implementation
+ * returns {@code false}.
+ *
+ * @return {@code false} if {@link #getResidual()} throws a {@link
+ * MathUnsupportedOperationException}
+ */
+ public boolean providesResidual() {
+ return false;
+ }
+}
diff --git a/src/main/java/org/apache/commons/math3/linear/JacobiPreconditioner.java b/src/main/java/org/apache/commons/math3/linear/JacobiPreconditioner.java
new file mode 100644
index 0000000..d6b0d53
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/linear/JacobiPreconditioner.java
@@ -0,0 +1,127 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math3.linear;
+
+import org.apache.commons.math3.analysis.function.Sqrt;
+import org.apache.commons.math3.util.MathArrays;
+
+/**
+ * This class implements the standard Jacobi (diagonal) preconditioner. For a matrix A<sub>ij</sub>,
+ * this preconditioner is M = diag(1 / A<sub>11</sub>, 1 / A<sub>22</sub>, &hellip;).
+ *
+ * @since 3.0
+ */
+public class JacobiPreconditioner extends RealLinearOperator {
+
+ /** The diagonal coefficients of the preconditioner. */
+ private final ArrayRealVector diag;
+
+ /**
+ * Creates a new instance of this class.
+ *
+ * @param diag the diagonal coefficients of the linear operator to be preconditioned
+ * @param deep {@code true} if a deep copy of the above array should be performed
+ */
+ public JacobiPreconditioner(final double[] diag, final boolean deep) {
+ this.diag = new ArrayRealVector(diag, deep);
+ }
+
+ /**
+ * Creates a new instance of this class. This method extracts the diagonal coefficients of the
+ * specified linear operator. If {@code a} does not extend {@link AbstractRealMatrix}, then the
+ * coefficients of the underlying matrix are not accessible, coefficient extraction is made by
+ * matrix-vector products with the basis vectors (and might therefore take some time). With
+ * matrices, direct entry access is carried out.
+ *
+ * @param a the linear operator for which the preconditioner should be built
+ * @return the diagonal preconditioner made of the inverse of the diagonal coefficients of the
+ * specified linear operator
+ * @throws NonSquareOperatorException if {@code a} is not square
+ */
+ public static JacobiPreconditioner create(final RealLinearOperator a)
+ throws NonSquareOperatorException {
+ final int n = a.getColumnDimension();
+ if (a.getRowDimension() != n) {
+ throw new NonSquareOperatorException(a.getRowDimension(), n);
+ }
+ final double[] diag = new double[n];
+ if (a instanceof AbstractRealMatrix) {
+ final AbstractRealMatrix m = (AbstractRealMatrix) a;
+ for (int i = 0; i < n; i++) {
+ diag[i] = m.getEntry(i, i);
+ }
+ } else {
+ final ArrayRealVector x = new ArrayRealVector(n);
+ for (int i = 0; i < n; i++) {
+ x.set(0.);
+ x.setEntry(i, 1.);
+ diag[i] = a.operate(x).getEntry(i);
+ }
+ }
+ return new JacobiPreconditioner(diag, false);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public int getColumnDimension() {
+ return diag.getDimension();
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public int getRowDimension() {
+ return diag.getDimension();
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public RealVector operate(final RealVector x) {
+ // Dimension check is carried out by ebeDivide
+ return new ArrayRealVector(MathArrays.ebeDivide(x.toArray(), diag.toArray()), false);
+ }
+
+ /**
+ * Returns the square root of {@code this} diagonal operator. More precisely, this method
+ * returns P = diag(1 / &radic;A<sub>11</sub>, 1 / &radic;A<sub>22</sub>, &hellip;).
+ *
+ * @return the square root of {@code this} preconditioner
+ * @since 3.1
+ */
+ public RealLinearOperator sqrt() {
+ final RealVector sqrtDiag = diag.map(new Sqrt());
+ return new RealLinearOperator() {
+ /** {@inheritDoc} */
+ @Override
+ public RealVector operate(final RealVector x) {
+ return new ArrayRealVector(
+ MathArrays.ebeDivide(x.toArray(), sqrtDiag.toArray()), false);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public int getRowDimension() {
+ return sqrtDiag.getDimension();
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public int getColumnDimension() {
+ return sqrtDiag.getDimension();
+ }
+ };
+ }
+}
diff --git a/src/main/java/org/apache/commons/math3/linear/LUDecomposition.java b/src/main/java/org/apache/commons/math3/linear/LUDecomposition.java
new file mode 100644
index 0000000..798bb61
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/linear/LUDecomposition.java
@@ -0,0 +1,409 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math3.linear;
+
+import org.apache.commons.math3.exception.DimensionMismatchException;
+import org.apache.commons.math3.util.FastMath;
+
+/**
+ * Calculates the LUP-decomposition of a square matrix.
+ *
+ * <p>The LUP-decomposition of a matrix A consists of three matrices L, U and P that satisfy:
+ * P&times;A = L&times;U. L is lower triangular (with unit diagonal terms), U is upper triangular
+ * and P is a permutation matrix. All matrices are m&times;m.
+ *
+ * <p>As shown by the presence of the P matrix, this decomposition is implemented using partial
+ * pivoting.
+ *
+ * <p>This class is based on the class with similar name from the <a
+ * href="http://math.nist.gov/javanumerics/jama/">JAMA</a> library.
+ *
+ * <ul>
+ * <li>a {@link #getP() getP} method has been added,
+ * <li>the {@code det} method has been renamed as {@link #getDeterminant() getDeterminant},
+ * <li>the {@code getDoublePivot} method has been removed (but the int based {@link #getPivot()
+ * getPivot} method has been kept),
+ * <li>the {@code solve} and {@code isNonSingular} methods have been replaced by a {@link
+ * #getSolver() getSolver} method and the equivalent methods provided by the returned {@link
+ * DecompositionSolver}.
+ * </ul>
+ *
+ * @see <a href="http://mathworld.wolfram.com/LUDecomposition.html">MathWorld</a>
+ * @see <a href="http://en.wikipedia.org/wiki/LU_decomposition">Wikipedia</a>
+ * @since 2.0 (changed to concrete class in 3.0)
+ */
+public class LUDecomposition {
+ /** Default bound to determine effective singularity in LU decomposition. */
+ private static final double DEFAULT_TOO_SMALL = 1e-11;
+
+ /** Entries of LU decomposition. */
+ private final double[][] lu;
+
+ /** Pivot permutation associated with LU decomposition. */
+ private final int[] pivot;
+
+ /** Parity of the permutation associated with the LU decomposition. */
+ private boolean even;
+
+ /** Singularity indicator. */
+ private boolean singular;
+
+ /** Cached value of L. */
+ private RealMatrix cachedL;
+
+ /** Cached value of U. */
+ private RealMatrix cachedU;
+
+ /** Cached value of P. */
+ private RealMatrix cachedP;
+
+ /**
+ * Calculates the LU-decomposition of the given matrix. This constructor uses 1e-11 as default
+ * value for the singularity threshold.
+ *
+ * @param matrix Matrix to decompose.
+ * @throws NonSquareMatrixException if matrix is not square.
+ */
+ public LUDecomposition(RealMatrix matrix) {
+ this(matrix, DEFAULT_TOO_SMALL);
+ }
+
+ /**
+ * Calculates the LU-decomposition of the given matrix.
+ *
+ * @param matrix The matrix to decompose.
+ * @param singularityThreshold threshold (based on partial row norm) under which a matrix is
+ * considered singular
+ * @throws NonSquareMatrixException if matrix is not square
+ */
+ public LUDecomposition(RealMatrix matrix, double singularityThreshold) {
+ if (!matrix.isSquare()) {
+ throw new NonSquareMatrixException(
+ matrix.getRowDimension(), matrix.getColumnDimension());
+ }
+
+ final int m = matrix.getColumnDimension();
+ lu = matrix.getData();
+ pivot = new int[m];
+ cachedL = null;
+ cachedU = null;
+ cachedP = null;
+
+ // Initialize permutation array and parity
+ for (int row = 0; row < m; row++) {
+ pivot[row] = row;
+ }
+ even = true;
+ singular = false;
+
+ // Loop over columns
+ for (int col = 0; col < m; col++) {
+
+ // upper
+ for (int row = 0; row < col; row++) {
+ final double[] luRow = lu[row];
+ double sum = luRow[col];
+ for (int i = 0; i < row; i++) {
+ sum -= luRow[i] * lu[i][col];
+ }
+ luRow[col] = sum;
+ }
+
+ // lower
+ int max = col; // permutation row
+ double largest = Double.NEGATIVE_INFINITY;
+ for (int row = col; row < m; row++) {
+ final double[] luRow = lu[row];
+ double sum = luRow[col];
+ for (int i = 0; i < col; i++) {
+ sum -= luRow[i] * lu[i][col];
+ }
+ luRow[col] = sum;
+
+ // maintain best permutation choice
+ if (FastMath.abs(sum) > largest) {
+ largest = FastMath.abs(sum);
+ max = row;
+ }
+ }
+
+ // Singularity check
+ if (FastMath.abs(lu[max][col]) < singularityThreshold) {
+ singular = true;
+ return;
+ }
+
+ // Pivot if necessary
+ if (max != col) {
+ double tmp = 0;
+ final double[] luMax = lu[max];
+ final double[] luCol = lu[col];
+ for (int i = 0; i < m; i++) {
+ tmp = luMax[i];
+ luMax[i] = luCol[i];
+ luCol[i] = tmp;
+ }
+ int temp = pivot[max];
+ pivot[max] = pivot[col];
+ pivot[col] = temp;
+ even = !even;
+ }
+
+ // Divide the lower elements by the "winning" diagonal elt.
+ final double luDiag = lu[col][col];
+ for (int row = col + 1; row < m; row++) {
+ lu[row][col] /= luDiag;
+ }
+ }
+ }
+
+ /**
+ * Returns the matrix L of the decomposition.
+ *
+ * <p>L is a lower-triangular matrix
+ *
+ * @return the L matrix (or null if decomposed matrix is singular)
+ */
+ public RealMatrix getL() {
+ if ((cachedL == null) && !singular) {
+ final int m = pivot.length;
+ cachedL = MatrixUtils.createRealMatrix(m, m);
+ for (int i = 0; i < m; ++i) {
+ final double[] luI = lu[i];
+ for (int j = 0; j < i; ++j) {
+ cachedL.setEntry(i, j, luI[j]);
+ }
+ cachedL.setEntry(i, i, 1.0);
+ }
+ }
+ return cachedL;
+ }
+
+ /**
+ * Returns the matrix U of the decomposition.
+ *
+ * <p>U is an upper-triangular matrix
+ *
+ * @return the U matrix (or null if decomposed matrix is singular)
+ */
+ public RealMatrix getU() {
+ if ((cachedU == null) && !singular) {
+ final int m = pivot.length;
+ cachedU = MatrixUtils.createRealMatrix(m, m);
+ for (int i = 0; i < m; ++i) {
+ final double[] luI = lu[i];
+ for (int j = i; j < m; ++j) {
+ cachedU.setEntry(i, j, luI[j]);
+ }
+ }
+ }
+ return cachedU;
+ }
+
+ /**
+ * Returns the P rows permutation matrix.
+ *
+ * <p>P is a sparse matrix with exactly one element set to 1.0 in each row and each column, all
+ * other elements being set to 0.0.
+ *
+ * <p>The positions of the 1 elements are given by the {@link #getPivot() pivot permutation
+ * vector}.
+ *
+ * @return the P rows permutation matrix (or null if decomposed matrix is singular)
+ * @see #getPivot()
+ */
+ public RealMatrix getP() {
+ if ((cachedP == null) && !singular) {
+ final int m = pivot.length;
+ cachedP = MatrixUtils.createRealMatrix(m, m);
+ for (int i = 0; i < m; ++i) {
+ cachedP.setEntry(i, pivot[i], 1.0);
+ }
+ }
+ return cachedP;
+ }
+
+ /**
+ * Returns the pivot permutation vector.
+ *
+ * @return the pivot permutation vector
+ * @see #getP()
+ */
+ public int[] getPivot() {
+ return pivot.clone();
+ }
+
+ /**
+ * Return the determinant of the matrix
+ *
+ * @return determinant of the matrix
+ */
+ public double getDeterminant() {
+ if (singular) {
+ return 0;
+ } else {
+ final int m = pivot.length;
+ double determinant = even ? 1 : -1;
+ for (int i = 0; i < m; i++) {
+ determinant *= lu[i][i];
+ }
+ return determinant;
+ }
+ }
+
+ /**
+ * Get a solver for finding the A &times; X = B solution in exact linear sense.
+ *
+ * @return a solver
+ */
+ public DecompositionSolver getSolver() {
+ return new Solver(lu, pivot, singular);
+ }
+
+ /** Specialized solver. */
+ private static class Solver implements DecompositionSolver {
+
+ /** Entries of LU decomposition. */
+ private final double[][] lu;
+
+ /** Pivot permutation associated with LU decomposition. */
+ private final int[] pivot;
+
+ /** Singularity indicator. */
+ private final boolean singular;
+
+ /**
+ * Build a solver from decomposed matrix.
+ *
+ * @param lu entries of LU decomposition
+ * @param pivot pivot permutation associated with LU decomposition
+ * @param singular singularity indicator
+ */
+ private Solver(final double[][] lu, final int[] pivot, final boolean singular) {
+ this.lu = lu;
+ this.pivot = pivot;
+ this.singular = singular;
+ }
+
+ /** {@inheritDoc} */
+ public boolean isNonSingular() {
+ return !singular;
+ }
+
+ /** {@inheritDoc} */
+ public RealVector solve(RealVector b) {
+ final int m = pivot.length;
+ if (b.getDimension() != m) {
+ throw new DimensionMismatchException(b.getDimension(), m);
+ }
+ if (singular) {
+ throw new SingularMatrixException();
+ }
+
+ final double[] bp = new double[m];
+
+ // Apply permutations to b
+ for (int row = 0; row < m; row++) {
+ bp[row] = b.getEntry(pivot[row]);
+ }
+
+ // Solve LY = b
+ for (int col = 0; col < m; col++) {
+ final double bpCol = bp[col];
+ for (int i = col + 1; i < m; i++) {
+ bp[i] -= bpCol * lu[i][col];
+ }
+ }
+
+ // Solve UX = Y
+ for (int col = m - 1; col >= 0; col--) {
+ bp[col] /= lu[col][col];
+ final double bpCol = bp[col];
+ for (int i = 0; i < col; i++) {
+ bp[i] -= bpCol * lu[i][col];
+ }
+ }
+
+ return new ArrayRealVector(bp, false);
+ }
+
+ /** {@inheritDoc} */
+ public RealMatrix solve(RealMatrix b) {
+
+ final int m = pivot.length;
+ if (b.getRowDimension() != m) {
+ throw new DimensionMismatchException(b.getRowDimension(), m);
+ }
+ if (singular) {
+ throw new SingularMatrixException();
+ }
+
+ final int nColB = b.getColumnDimension();
+
+ // Apply permutations to b
+ final double[][] bp = new double[m][nColB];
+ for (int row = 0; row < m; row++) {
+ final double[] bpRow = bp[row];
+ final int pRow = pivot[row];
+ for (int col = 0; col < nColB; col++) {
+ bpRow[col] = b.getEntry(pRow, col);
+ }
+ }
+
+ // Solve LY = b
+ for (int col = 0; col < m; col++) {
+ final double[] bpCol = bp[col];
+ for (int i = col + 1; i < m; i++) {
+ final double[] bpI = bp[i];
+ final double luICol = lu[i][col];
+ for (int j = 0; j < nColB; j++) {
+ bpI[j] -= bpCol[j] * luICol;
+ }
+ }
+ }
+
+ // Solve UX = Y
+ for (int col = m - 1; col >= 0; col--) {
+ final double[] bpCol = bp[col];
+ final double luDiag = lu[col][col];
+ for (int j = 0; j < nColB; j++) {
+ bpCol[j] /= luDiag;
+ }
+ for (int i = 0; i < col; i++) {
+ final double[] bpI = bp[i];
+ final double luICol = lu[i][col];
+ for (int j = 0; j < nColB; j++) {
+ bpI[j] -= bpCol[j] * luICol;
+ }
+ }
+ }
+
+ return new Array2DRowRealMatrix(bp, false);
+ }
+
+ /**
+ * Get the inverse of the decomposed matrix.
+ *
+ * @return the inverse matrix.
+ * @throws SingularMatrixException if the decomposed matrix is singular.
+ */
+ public RealMatrix getInverse() {
+ return solve(MatrixUtils.createRealIdentityMatrix(pivot.length));
+ }
+ }
+}
diff --git a/src/main/java/org/apache/commons/math3/linear/MatrixDimensionMismatchException.java b/src/main/java/org/apache/commons/math3/linear/MatrixDimensionMismatchException.java
new file mode 100644
index 0000000..20b10b1
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/linear/MatrixDimensionMismatchException.java
@@ -0,0 +1,75 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math3.linear;
+
+import org.apache.commons.math3.exception.MultiDimensionMismatchException;
+import org.apache.commons.math3.exception.util.LocalizedFormats;
+
+/**
+ * Exception to be thrown when either the number of rows or the number of columns of a matrix do not
+ * match the expected values.
+ *
+ * @since 3.0
+ */
+public class MatrixDimensionMismatchException extends MultiDimensionMismatchException {
+ /** Serializable version Id. */
+ private static final long serialVersionUID = -8415396756375798143L;
+
+ /**
+ * Construct an exception from the mismatched dimensions.
+ *
+ * @param wrongRowDim Wrong row dimension.
+ * @param wrongColDim Wrong column dimension.
+ * @param expectedRowDim Expected row dimension.
+ * @param expectedColDim Expected column dimension.
+ */
+ public MatrixDimensionMismatchException(
+ int wrongRowDim, int wrongColDim, int expectedRowDim, int expectedColDim) {
+ super(
+ LocalizedFormats.DIMENSIONS_MISMATCH_2x2,
+ new Integer[] {wrongRowDim, wrongColDim},
+ new Integer[] {expectedRowDim, expectedColDim});
+ }
+
+ /**
+ * @return the expected row dimension.
+ */
+ public int getWrongRowDimension() {
+ return getWrongDimension(0);
+ }
+
+ /**
+ * @return the expected row dimension.
+ */
+ public int getExpectedRowDimension() {
+ return getExpectedDimension(0);
+ }
+
+ /**
+ * @return the wrong column dimension.
+ */
+ public int getWrongColumnDimension() {
+ return getWrongDimension(1);
+ }
+
+ /**
+ * @return the expected column dimension.
+ */
+ public int getExpectedColumnDimension() {
+ return getExpectedDimension(1);
+ }
+}
diff --git a/src/main/java/org/apache/commons/math3/linear/MatrixUtils.java b/src/main/java/org/apache/commons/math3/linear/MatrixUtils.java
new file mode 100644
index 0000000..57ef402
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/linear/MatrixUtils.java
@@ -0,0 +1,1080 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math3.linear;
+
+import org.apache.commons.math3.Field;
+import org.apache.commons.math3.FieldElement;
+import org.apache.commons.math3.exception.DimensionMismatchException;
+import org.apache.commons.math3.exception.MathArithmeticException;
+import org.apache.commons.math3.exception.NoDataException;
+import org.apache.commons.math3.exception.NullArgumentException;
+import org.apache.commons.math3.exception.NumberIsTooSmallException;
+import org.apache.commons.math3.exception.OutOfRangeException;
+import org.apache.commons.math3.exception.ZeroException;
+import org.apache.commons.math3.exception.util.LocalizedFormats;
+import org.apache.commons.math3.fraction.BigFraction;
+import org.apache.commons.math3.fraction.Fraction;
+import org.apache.commons.math3.util.FastMath;
+import org.apache.commons.math3.util.MathArrays;
+import org.apache.commons.math3.util.MathUtils;
+import org.apache.commons.math3.util.Precision;
+
+import java.io.IOException;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+import java.util.Arrays;
+
+/** A collection of static methods that operate on or return matrices. */
+public class MatrixUtils {
+
+ /**
+ * The default format for {@link RealMatrix} objects.
+ *
+ * @since 3.1
+ */
+ public static final RealMatrixFormat DEFAULT_FORMAT = RealMatrixFormat.getInstance();
+
+ /**
+ * A format for {@link RealMatrix} objects compatible with octave.
+ *
+ * @since 3.1
+ */
+ public static final RealMatrixFormat OCTAVE_FORMAT =
+ new RealMatrixFormat("[", "]", "", "", "; ", ", ");
+
+ /** Private constructor. */
+ private MatrixUtils() {
+ super();
+ }
+
+ /**
+ * Returns a {@link RealMatrix} with specified dimensions.
+ *
+ * <p>The type of matrix returned depends on the dimension. Below 2<sup>12</sup> elements (i.e.
+ * 4096 elements or 64&times;64 for a square matrix) which can be stored in a 32kB array, a
+ * {@link Array2DRowRealMatrix} instance is built. Above this threshold a {@link
+ * BlockRealMatrix} instance is built.
+ *
+ * <p>The matrix elements are all set to 0.0.
+ *
+ * @param rows number of rows of the matrix
+ * @param columns number of columns of the matrix
+ * @return RealMatrix with specified dimensions
+ * @see #createRealMatrix(double[][])
+ */
+ public static RealMatrix createRealMatrix(final int rows, final int columns) {
+ return (rows * columns <= 4096)
+ ? new Array2DRowRealMatrix(rows, columns)
+ : new BlockRealMatrix(rows, columns);
+ }
+
+ /**
+ * Returns a {@link FieldMatrix} with specified dimensions.
+ *
+ * <p>The type of matrix returned depends on the dimension. Below 2<sup>12</sup> elements (i.e.
+ * 4096 elements or 64&times;64 for a square matrix), a {@link FieldMatrix} instance is built.
+ * Above this threshold a {@link BlockFieldMatrix} instance is built.
+ *
+ * <p>The matrix elements are all set to field.getZero().
+ *
+ * @param <T> the type of the field elements
+ * @param field field to which the matrix elements belong
+ * @param rows number of rows of the matrix
+ * @param columns number of columns of the matrix
+ * @return FieldMatrix with specified dimensions
+ * @see #createFieldMatrix(FieldElement[][])
+ * @since 2.0
+ */
+ public static <T extends FieldElement<T>> FieldMatrix<T> createFieldMatrix(
+ final Field<T> field, final int rows, final int columns) {
+ return (rows * columns <= 4096)
+ ? new Array2DRowFieldMatrix<T>(field, rows, columns)
+ : new BlockFieldMatrix<T>(field, rows, columns);
+ }
+
+ /**
+ * Returns a {@link RealMatrix} whose entries are the the values in the the input array.
+ *
+ * <p>The type of matrix returned depends on the dimension. Below 2<sup>12</sup> elements (i.e.
+ * 4096 elements or 64&times;64 for a square matrix) which can be stored in a 32kB array, a
+ * {@link Array2DRowRealMatrix} instance is built. Above this threshold a {@link
+ * BlockRealMatrix} instance is built.
+ *
+ * <p>The input array is copied, not referenced.
+ *
+ * @param data input array
+ * @return RealMatrix containing the values of the array
+ * @throws org.apache.commons.math3.exception.DimensionMismatchException if {@code data} is not
+ * rectangular (not all rows have the same length).
+ * @throws NoDataException if a row or column is empty.
+ * @throws NullArgumentException if either {@code data} or {@code data[0]} is {@code null}.
+ * @throws DimensionMismatchException if {@code data} is not rectangular.
+ * @see #createRealMatrix(int, int)
+ */
+ public static RealMatrix createRealMatrix(double[][] data)
+ throws NullArgumentException, DimensionMismatchException, NoDataException {
+ if (data == null || data[0] == null) {
+ throw new NullArgumentException();
+ }
+ return (data.length * data[0].length <= 4096)
+ ? new Array2DRowRealMatrix(data)
+ : new BlockRealMatrix(data);
+ }
+
+ /**
+ * Returns a {@link FieldMatrix} whose entries are the the values in the the input array.
+ *
+ * <p>The type of matrix returned depends on the dimension. Below 2<sup>12</sup> elements (i.e.
+ * 4096 elements or 64&times;64 for a square matrix), a {@link FieldMatrix} instance is built.
+ * Above this threshold a {@link BlockFieldMatrix} instance is built.
+ *
+ * <p>The input array is copied, not referenced.
+ *
+ * @param <T> the type of the field elements
+ * @param data input array
+ * @return a matrix containing the values of the array.
+ * @throws org.apache.commons.math3.exception.DimensionMismatchException if {@code data} is not
+ * rectangular (not all rows have the same length).
+ * @throws NoDataException if a row or column is empty.
+ * @throws NullArgumentException if either {@code data} or {@code data[0]} is {@code null}.
+ * @see #createFieldMatrix(Field, int, int)
+ * @since 2.0
+ */
+ public static <T extends FieldElement<T>> FieldMatrix<T> createFieldMatrix(T[][] data)
+ throws DimensionMismatchException, NoDataException, NullArgumentException {
+ if (data == null || data[0] == null) {
+ throw new NullArgumentException();
+ }
+ return (data.length * data[0].length <= 4096)
+ ? new Array2DRowFieldMatrix<T>(data)
+ : new BlockFieldMatrix<T>(data);
+ }
+
+ /**
+ * Returns <code>dimension x dimension</code> identity matrix.
+ *
+ * @param dimension dimension of identity matrix to generate
+ * @return identity matrix
+ * @throws IllegalArgumentException if dimension is not positive
+ * @since 1.1
+ */
+ public static RealMatrix createRealIdentityMatrix(int dimension) {
+ final RealMatrix m = createRealMatrix(dimension, dimension);
+ for (int i = 0; i < dimension; ++i) {
+ m.setEntry(i, i, 1.0);
+ }
+ return m;
+ }
+
+ /**
+ * Returns <code>dimension x dimension</code> identity matrix.
+ *
+ * @param <T> the type of the field elements
+ * @param field field to which the elements belong
+ * @param dimension dimension of identity matrix to generate
+ * @return identity matrix
+ * @throws IllegalArgumentException if dimension is not positive
+ * @since 2.0
+ */
+ public static <T extends FieldElement<T>> FieldMatrix<T> createFieldIdentityMatrix(
+ final Field<T> field, final int dimension) {
+ final T zero = field.getZero();
+ final T one = field.getOne();
+ final T[][] d = MathArrays.buildArray(field, dimension, dimension);
+ for (int row = 0; row < dimension; row++) {
+ final T[] dRow = d[row];
+ Arrays.fill(dRow, zero);
+ dRow[row] = one;
+ }
+ return new Array2DRowFieldMatrix<T>(field, d, false);
+ }
+
+ /**
+ * Returns a diagonal matrix with specified elements.
+ *
+ * @param diagonal diagonal elements of the matrix (the array elements will be copied)
+ * @return diagonal matrix
+ * @since 2.0
+ */
+ public static RealMatrix createRealDiagonalMatrix(final double[] diagonal) {
+ final RealMatrix m = createRealMatrix(diagonal.length, diagonal.length);
+ for (int i = 0; i < diagonal.length; ++i) {
+ m.setEntry(i, i, diagonal[i]);
+ }
+ return m;
+ }
+
+ /**
+ * Returns a diagonal matrix with specified elements.
+ *
+ * @param <T> the type of the field elements
+ * @param diagonal diagonal elements of the matrix (the array elements will be copied)
+ * @return diagonal matrix
+ * @since 2.0
+ */
+ public static <T extends FieldElement<T>> FieldMatrix<T> createFieldDiagonalMatrix(
+ final T[] diagonal) {
+ final FieldMatrix<T> m =
+ createFieldMatrix(diagonal[0].getField(), diagonal.length, diagonal.length);
+ for (int i = 0; i < diagonal.length; ++i) {
+ m.setEntry(i, i, diagonal[i]);
+ }
+ return m;
+ }
+
+ /**
+ * Creates a {@link RealVector} using the data from the input array.
+ *
+ * @param data the input data
+ * @return a data.length RealVector
+ * @throws NoDataException if {@code data} is empty.
+ * @throws NullArgumentException if {@code data} is {@code null}.
+ */
+ public static RealVector createRealVector(double[] data)
+ throws NoDataException, NullArgumentException {
+ if (data == null) {
+ throw new NullArgumentException();
+ }
+ return new ArrayRealVector(data, true);
+ }
+
+ /**
+ * Creates a {@link FieldVector} using the data from the input array.
+ *
+ * @param <T> the type of the field elements
+ * @param data the input data
+ * @return a data.length FieldVector
+ * @throws NoDataException if {@code data} is empty.
+ * @throws NullArgumentException if {@code data} is {@code null}.
+ * @throws ZeroException if {@code data} has 0 elements
+ */
+ public static <T extends FieldElement<T>> FieldVector<T> createFieldVector(final T[] data)
+ throws NoDataException, NullArgumentException, ZeroException {
+ if (data == null) {
+ throw new NullArgumentException();
+ }
+ if (data.length == 0) {
+ throw new ZeroException(LocalizedFormats.VECTOR_MUST_HAVE_AT_LEAST_ONE_ELEMENT);
+ }
+ return new ArrayFieldVector<T>(data[0].getField(), data, true);
+ }
+
+ /**
+ * Create a row {@link RealMatrix} using the data from the input array.
+ *
+ * @param rowData the input row data
+ * @return a 1 x rowData.length RealMatrix
+ * @throws NoDataException if {@code rowData} is empty.
+ * @throws NullArgumentException if {@code rowData} is {@code null}.
+ */
+ public static RealMatrix createRowRealMatrix(double[] rowData)
+ throws NoDataException, NullArgumentException {
+ if (rowData == null) {
+ throw new NullArgumentException();
+ }
+ final int nCols = rowData.length;
+ final RealMatrix m = createRealMatrix(1, nCols);
+ for (int i = 0; i < nCols; ++i) {
+ m.setEntry(0, i, rowData[i]);
+ }
+ return m;
+ }
+
+ /**
+ * Create a row {@link FieldMatrix} using the data from the input array.
+ *
+ * @param <T> the type of the field elements
+ * @param rowData the input row data
+ * @return a 1 x rowData.length FieldMatrix
+ * @throws NoDataException if {@code rowData} is empty.
+ * @throws NullArgumentException if {@code rowData} is {@code null}.
+ */
+ public static <T extends FieldElement<T>> FieldMatrix<T> createRowFieldMatrix(final T[] rowData)
+ throws NoDataException, NullArgumentException {
+ if (rowData == null) {
+ throw new NullArgumentException();
+ }
+ final int nCols = rowData.length;
+ if (nCols == 0) {
+ throw new NoDataException(LocalizedFormats.AT_LEAST_ONE_COLUMN);
+ }
+ final FieldMatrix<T> m = createFieldMatrix(rowData[0].getField(), 1, nCols);
+ for (int i = 0; i < nCols; ++i) {
+ m.setEntry(0, i, rowData[i]);
+ }
+ return m;
+ }
+
+ /**
+ * Creates a column {@link RealMatrix} using the data from the input array.
+ *
+ * @param columnData the input column data
+ * @return a columnData x 1 RealMatrix
+ * @throws NoDataException if {@code columnData} is empty.
+ * @throws NullArgumentException if {@code columnData} is {@code null}.
+ */
+ public static RealMatrix createColumnRealMatrix(double[] columnData)
+ throws NoDataException, NullArgumentException {
+ if (columnData == null) {
+ throw new NullArgumentException();
+ }
+ final int nRows = columnData.length;
+ final RealMatrix m = createRealMatrix(nRows, 1);
+ for (int i = 0; i < nRows; ++i) {
+ m.setEntry(i, 0, columnData[i]);
+ }
+ return m;
+ }
+
+ /**
+ * Creates a column {@link FieldMatrix} using the data from the input array.
+ *
+ * @param <T> the type of the field elements
+ * @param columnData the input column data
+ * @return a columnData x 1 FieldMatrix
+ * @throws NoDataException if {@code data} is empty.
+ * @throws NullArgumentException if {@code columnData} is {@code null}.
+ */
+ public static <T extends FieldElement<T>> FieldMatrix<T> createColumnFieldMatrix(
+ final T[] columnData) throws NoDataException, NullArgumentException {
+ if (columnData == null) {
+ throw new NullArgumentException();
+ }
+ final int nRows = columnData.length;
+ if (nRows == 0) {
+ throw new NoDataException(LocalizedFormats.AT_LEAST_ONE_ROW);
+ }
+ final FieldMatrix<T> m = createFieldMatrix(columnData[0].getField(), nRows, 1);
+ for (int i = 0; i < nRows; ++i) {
+ m.setEntry(i, 0, columnData[i]);
+ }
+ return m;
+ }
+
+ /**
+ * Checks whether a matrix is symmetric, within a given relative tolerance.
+ *
+ * @param matrix Matrix to check.
+ * @param relativeTolerance Tolerance of the symmetry check.
+ * @param raiseException If {@code true}, an exception will be raised if the matrix is not
+ * symmetric.
+ * @return {@code true} if {@code matrix} is symmetric.
+ * @throws NonSquareMatrixException if the matrix is not square.
+ * @throws NonSymmetricMatrixException if the matrix is not symmetric.
+ */
+ private static boolean isSymmetricInternal(
+ RealMatrix matrix, double relativeTolerance, boolean raiseException) {
+ final int rows = matrix.getRowDimension();
+ if (rows != matrix.getColumnDimension()) {
+ if (raiseException) {
+ throw new NonSquareMatrixException(rows, matrix.getColumnDimension());
+ } else {
+ return false;
+ }
+ }
+ for (int i = 0; i < rows; i++) {
+ for (int j = i + 1; j < rows; j++) {
+ final double mij = matrix.getEntry(i, j);
+ final double mji = matrix.getEntry(j, i);
+ if (FastMath.abs(mij - mji)
+ > FastMath.max(FastMath.abs(mij), FastMath.abs(mji)) * relativeTolerance) {
+ if (raiseException) {
+ throw new NonSymmetricMatrixException(i, j, relativeTolerance);
+ } else {
+ return false;
+ }
+ }
+ }
+ }
+ return true;
+ }
+
+ /**
+ * Checks whether a matrix is symmetric.
+ *
+ * @param matrix Matrix to check.
+ * @param eps Relative tolerance.
+ * @throws NonSquareMatrixException if the matrix is not square.
+ * @throws NonSymmetricMatrixException if the matrix is not symmetric.
+ * @since 3.1
+ */
+ public static void checkSymmetric(RealMatrix matrix, double eps) {
+ isSymmetricInternal(matrix, eps, true);
+ }
+
+ /**
+ * Checks whether a matrix is symmetric.
+ *
+ * @param matrix Matrix to check.
+ * @param eps Relative tolerance.
+ * @return {@code true} if {@code matrix} is symmetric.
+ * @since 3.1
+ */
+ public static boolean isSymmetric(RealMatrix matrix, double eps) {
+ return isSymmetricInternal(matrix, eps, false);
+ }
+
+ /**
+ * Check if matrix indices are valid.
+ *
+ * @param m Matrix.
+ * @param row Row index to check.
+ * @param column Column index to check.
+ * @throws OutOfRangeException if {@code row} or {@code column} is not a valid index.
+ */
+ public static void checkMatrixIndex(final AnyMatrix m, final int row, final int column)
+ throws OutOfRangeException {
+ checkRowIndex(m, row);
+ checkColumnIndex(m, column);
+ }
+
+ /**
+ * Check if a row index is valid.
+ *
+ * @param m Matrix.
+ * @param row Row index to check.
+ * @throws OutOfRangeException if {@code row} is not a valid index.
+ */
+ public static void checkRowIndex(final AnyMatrix m, final int row) throws OutOfRangeException {
+ if (row < 0 || row >= m.getRowDimension()) {
+ throw new OutOfRangeException(
+ LocalizedFormats.ROW_INDEX, row, 0, m.getRowDimension() - 1);
+ }
+ }
+
+ /**
+ * Check if a column index is valid.
+ *
+ * @param m Matrix.
+ * @param column Column index to check.
+ * @throws OutOfRangeException if {@code column} is not a valid index.
+ */
+ public static void checkColumnIndex(final AnyMatrix m, final int column)
+ throws OutOfRangeException {
+ if (column < 0 || column >= m.getColumnDimension()) {
+ throw new OutOfRangeException(
+ LocalizedFormats.COLUMN_INDEX, column, 0, m.getColumnDimension() - 1);
+ }
+ }
+
+ /**
+ * Check if submatrix ranges indices are valid. Rows and columns are indicated counting from 0
+ * to {@code n - 1}.
+ *
+ * @param m Matrix.
+ * @param startRow Initial row index.
+ * @param endRow Final row index.
+ * @param startColumn Initial column index.
+ * @param endColumn Final column index.
+ * @throws OutOfRangeException if the indices are invalid.
+ * @throws NumberIsTooSmallException if {@code endRow < startRow} or {@code endColumn <
+ * startColumn}.
+ */
+ public static void checkSubMatrixIndex(
+ final AnyMatrix m,
+ final int startRow,
+ final int endRow,
+ final int startColumn,
+ final int endColumn)
+ throws NumberIsTooSmallException, OutOfRangeException {
+ checkRowIndex(m, startRow);
+ checkRowIndex(m, endRow);
+ if (endRow < startRow) {
+ throw new NumberIsTooSmallException(
+ LocalizedFormats.INITIAL_ROW_AFTER_FINAL_ROW, endRow, startRow, false);
+ }
+
+ checkColumnIndex(m, startColumn);
+ checkColumnIndex(m, endColumn);
+ if (endColumn < startColumn) {
+ throw new NumberIsTooSmallException(
+ LocalizedFormats.INITIAL_COLUMN_AFTER_FINAL_COLUMN,
+ endColumn,
+ startColumn,
+ false);
+ }
+ }
+
+ /**
+ * Check if submatrix ranges indices are valid. Rows and columns are indicated counting from 0
+ * to n-1.
+ *
+ * @param m Matrix.
+ * @param selectedRows Array of row indices.
+ * @param selectedColumns Array of column indices.
+ * @throws NullArgumentException if {@code selectedRows} or {@code selectedColumns} are {@code
+ * null}.
+ * @throws NoDataException if the row or column selections are empty (zero length).
+ * @throws OutOfRangeException if row or column selections are not valid.
+ */
+ public static void checkSubMatrixIndex(
+ final AnyMatrix m, final int[] selectedRows, final int[] selectedColumns)
+ throws NoDataException, NullArgumentException, OutOfRangeException {
+ if (selectedRows == null) {
+ throw new NullArgumentException();
+ }
+ if (selectedColumns == null) {
+ throw new NullArgumentException();
+ }
+ if (selectedRows.length == 0) {
+ throw new NoDataException(LocalizedFormats.EMPTY_SELECTED_ROW_INDEX_ARRAY);
+ }
+ if (selectedColumns.length == 0) {
+ throw new NoDataException(LocalizedFormats.EMPTY_SELECTED_COLUMN_INDEX_ARRAY);
+ }
+
+ for (final int row : selectedRows) {
+ checkRowIndex(m, row);
+ }
+ for (final int column : selectedColumns) {
+ checkColumnIndex(m, column);
+ }
+ }
+
+ /**
+ * Check if matrices are addition compatible.
+ *
+ * @param left Left hand side matrix.
+ * @param right Right hand side matrix.
+ * @throws MatrixDimensionMismatchException if the matrices are not addition compatible.
+ */
+ public static void checkAdditionCompatible(final AnyMatrix left, final AnyMatrix right)
+ throws MatrixDimensionMismatchException {
+ if ((left.getRowDimension() != right.getRowDimension())
+ || (left.getColumnDimension() != right.getColumnDimension())) {
+ throw new MatrixDimensionMismatchException(
+ left.getRowDimension(), left.getColumnDimension(),
+ right.getRowDimension(), right.getColumnDimension());
+ }
+ }
+
+ /**
+ * Check if matrices are subtraction compatible
+ *
+ * @param left Left hand side matrix.
+ * @param right Right hand side matrix.
+ * @throws MatrixDimensionMismatchException if the matrices are not addition compatible.
+ */
+ public static void checkSubtractionCompatible(final AnyMatrix left, final AnyMatrix right)
+ throws MatrixDimensionMismatchException {
+ if ((left.getRowDimension() != right.getRowDimension())
+ || (left.getColumnDimension() != right.getColumnDimension())) {
+ throw new MatrixDimensionMismatchException(
+ left.getRowDimension(), left.getColumnDimension(),
+ right.getRowDimension(), right.getColumnDimension());
+ }
+ }
+
+ /**
+ * Check if matrices are multiplication compatible
+ *
+ * @param left Left hand side matrix.
+ * @param right Right hand side matrix.
+ * @throws DimensionMismatchException if matrices are not multiplication compatible.
+ */
+ public static void checkMultiplicationCompatible(final AnyMatrix left, final AnyMatrix right)
+ throws DimensionMismatchException {
+
+ if (left.getColumnDimension() != right.getRowDimension()) {
+ throw new DimensionMismatchException(
+ left.getColumnDimension(), right.getRowDimension());
+ }
+ }
+
+ /**
+ * Convert a {@link FieldMatrix}/{@link Fraction} matrix to a {@link RealMatrix}.
+ *
+ * @param m Matrix to convert.
+ * @return the converted matrix.
+ */
+ public static Array2DRowRealMatrix fractionMatrixToRealMatrix(final FieldMatrix<Fraction> m) {
+ final FractionMatrixConverter converter = new FractionMatrixConverter();
+ m.walkInOptimizedOrder(converter);
+ return converter.getConvertedMatrix();
+ }
+
+ /** Converter for {@link FieldMatrix}/{@link Fraction}. */
+ private static class FractionMatrixConverter
+ extends DefaultFieldMatrixPreservingVisitor<Fraction> {
+ /** Converted array. */
+ private double[][] data;
+
+ /** Simple constructor. */
+ FractionMatrixConverter() {
+ super(Fraction.ZERO);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void start(
+ int rows, int columns, int startRow, int endRow, int startColumn, int endColumn) {
+ data = new double[rows][columns];
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void visit(int row, int column, Fraction value) {
+ data[row][column] = value.doubleValue();
+ }
+
+ /**
+ * Get the converted matrix.
+ *
+ * @return the converted matrix.
+ */
+ Array2DRowRealMatrix getConvertedMatrix() {
+ return new Array2DRowRealMatrix(data, false);
+ }
+ }
+
+ /**
+ * Convert a {@link FieldMatrix}/{@link BigFraction} matrix to a {@link RealMatrix}.
+ *
+ * @param m Matrix to convert.
+ * @return the converted matrix.
+ */
+ public static Array2DRowRealMatrix bigFractionMatrixToRealMatrix(
+ final FieldMatrix<BigFraction> m) {
+ final BigFractionMatrixConverter converter = new BigFractionMatrixConverter();
+ m.walkInOptimizedOrder(converter);
+ return converter.getConvertedMatrix();
+ }
+
+ /** Converter for {@link FieldMatrix}/{@link BigFraction}. */
+ private static class BigFractionMatrixConverter
+ extends DefaultFieldMatrixPreservingVisitor<BigFraction> {
+ /** Converted array. */
+ private double[][] data;
+
+ /** Simple constructor. */
+ BigFractionMatrixConverter() {
+ super(BigFraction.ZERO);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void start(
+ int rows, int columns, int startRow, int endRow, int startColumn, int endColumn) {
+ data = new double[rows][columns];
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void visit(int row, int column, BigFraction value) {
+ data[row][column] = value.doubleValue();
+ }
+
+ /**
+ * Get the converted matrix.
+ *
+ * @return the converted matrix.
+ */
+ Array2DRowRealMatrix getConvertedMatrix() {
+ return new Array2DRowRealMatrix(data, false);
+ }
+ }
+
+ /**
+ * Serialize a {@link RealVector}.
+ *
+ * <p>This method is intended to be called from within a private <code>writeObject</code> method
+ * (after a call to <code>oos.defaultWriteObject()</code>) in a class that has a {@link
+ * RealVector} field, which should be declared <code>transient</code>. This way, the default
+ * handling does not serialize the vector (the {@link RealVector} interface is not serializable
+ * by default) but this method does serialize it specifically.
+ *
+ * <p>The following example shows how a simple class with a name and a real vector should be
+ * written:
+ *
+ * <pre><code>
+ * public class NamedVector implements Serializable {
+ *
+ * private final String name;
+ * private final transient RealVector coefficients;
+ *
+ * // omitted constructors, getters ...
+ *
+ * private void writeObject(ObjectOutputStream oos) throws IOException {
+ * oos.defaultWriteObject(); // takes care of name field
+ * MatrixUtils.serializeRealVector(coefficients, oos);
+ * }
+ *
+ * private void readObject(ObjectInputStream ois) throws ClassNotFoundException, IOException {
+ * ois.defaultReadObject(); // takes care of name field
+ * MatrixUtils.deserializeRealVector(this, "coefficients", ois);
+ * }
+ *
+ * }
+ * </code></pre>
+ *
+ * @param vector real vector to serialize
+ * @param oos stream where the real vector should be written
+ * @exception IOException if object cannot be written to stream
+ * @see #deserializeRealVector(Object, String, ObjectInputStream)
+ */
+ public static void serializeRealVector(final RealVector vector, final ObjectOutputStream oos)
+ throws IOException {
+ final int n = vector.getDimension();
+ oos.writeInt(n);
+ for (int i = 0; i < n; ++i) {
+ oos.writeDouble(vector.getEntry(i));
+ }
+ }
+
+ /**
+ * Deserialize a {@link RealVector} field in a class.
+ *
+ * <p>This method is intended to be called from within a private <code>readObject</code> method
+ * (after a call to <code>ois.defaultReadObject()</code>) in a class that has a {@link
+ * RealVector} field, which should be declared <code>transient</code>. This way, the default
+ * handling does not deserialize the vector (the {@link RealVector} interface is not
+ * serializable by default) but this method does deserialize it specifically.
+ *
+ * @param instance instance in which the field must be set up
+ * @param fieldName name of the field within the class (may be private and final)
+ * @param ois stream from which the real vector should be read
+ * @exception ClassNotFoundException if a class in the stream cannot be found
+ * @exception IOException if object cannot be read from the stream
+ * @see #serializeRealVector(RealVector, ObjectOutputStream)
+ */
+ public static void deserializeRealVector(
+ final Object instance, final String fieldName, final ObjectInputStream ois)
+ throws ClassNotFoundException, IOException {
+ try {
+
+ // read the vector data
+ final int n = ois.readInt();
+ final double[] data = new double[n];
+ for (int i = 0; i < n; ++i) {
+ data[i] = ois.readDouble();
+ }
+
+ // create the instance
+ final RealVector vector = new ArrayRealVector(data, false);
+
+ // set up the field
+ final java.lang.reflect.Field f = instance.getClass().getDeclaredField(fieldName);
+ f.setAccessible(true);
+ f.set(instance, vector);
+
+ } catch (NoSuchFieldException nsfe) {
+ IOException ioe = new IOException();
+ ioe.initCause(nsfe);
+ throw ioe;
+ } catch (IllegalAccessException iae) {
+ IOException ioe = new IOException();
+ ioe.initCause(iae);
+ throw ioe;
+ }
+ }
+
+ /**
+ * Serialize a {@link RealMatrix}.
+ *
+ * <p>This method is intended to be called from within a private <code>writeObject</code> method
+ * (after a call to <code>oos.defaultWriteObject()</code>) in a class that has a {@link
+ * RealMatrix} field, which should be declared <code>transient</code>. This way, the default
+ * handling does not serialize the matrix (the {@link RealMatrix} interface is not serializable
+ * by default) but this method does serialize it specifically.
+ *
+ * <p>The following example shows how a simple class with a name and a real matrix should be
+ * written:
+ *
+ * <pre><code>
+ * public class NamedMatrix implements Serializable {
+ *
+ * private final String name;
+ * private final transient RealMatrix coefficients;
+ *
+ * // omitted constructors, getters ...
+ *
+ * private void writeObject(ObjectOutputStream oos) throws IOException {
+ * oos.defaultWriteObject(); // takes care of name field
+ * MatrixUtils.serializeRealMatrix(coefficients, oos);
+ * }
+ *
+ * private void readObject(ObjectInputStream ois) throws ClassNotFoundException, IOException {
+ * ois.defaultReadObject(); // takes care of name field
+ * MatrixUtils.deserializeRealMatrix(this, "coefficients", ois);
+ * }
+ *
+ * }
+ * </code></pre>
+ *
+ * @param matrix real matrix to serialize
+ * @param oos stream where the real matrix should be written
+ * @exception IOException if object cannot be written to stream
+ * @see #deserializeRealMatrix(Object, String, ObjectInputStream)
+ */
+ public static void serializeRealMatrix(final RealMatrix matrix, final ObjectOutputStream oos)
+ throws IOException {
+ final int n = matrix.getRowDimension();
+ final int m = matrix.getColumnDimension();
+ oos.writeInt(n);
+ oos.writeInt(m);
+ for (int i = 0; i < n; ++i) {
+ for (int j = 0; j < m; ++j) {
+ oos.writeDouble(matrix.getEntry(i, j));
+ }
+ }
+ }
+
+ /**
+ * Deserialize a {@link RealMatrix} field in a class.
+ *
+ * <p>This method is intended to be called from within a private <code>readObject</code> method
+ * (after a call to <code>ois.defaultReadObject()</code>) in a class that has a {@link
+ * RealMatrix} field, which should be declared <code>transient</code>. This way, the default
+ * handling does not deserialize the matrix (the {@link RealMatrix} interface is not
+ * serializable by default) but this method does deserialize it specifically.
+ *
+ * @param instance instance in which the field must be set up
+ * @param fieldName name of the field within the class (may be private and final)
+ * @param ois stream from which the real matrix should be read
+ * @exception ClassNotFoundException if a class in the stream cannot be found
+ * @exception IOException if object cannot be read from the stream
+ * @see #serializeRealMatrix(RealMatrix, ObjectOutputStream)
+ */
+ public static void deserializeRealMatrix(
+ final Object instance, final String fieldName, final ObjectInputStream ois)
+ throws ClassNotFoundException, IOException {
+ try {
+
+ // read the matrix data
+ final int n = ois.readInt();
+ final int m = ois.readInt();
+ final double[][] data = new double[n][m];
+ for (int i = 0; i < n; ++i) {
+ final double[] dataI = data[i];
+ for (int j = 0; j < m; ++j) {
+ dataI[j] = ois.readDouble();
+ }
+ }
+
+ // create the instance
+ final RealMatrix matrix = new Array2DRowRealMatrix(data, false);
+
+ // set up the field
+ final java.lang.reflect.Field f = instance.getClass().getDeclaredField(fieldName);
+ f.setAccessible(true);
+ f.set(instance, matrix);
+
+ } catch (NoSuchFieldException nsfe) {
+ IOException ioe = new IOException();
+ ioe.initCause(nsfe);
+ throw ioe;
+ } catch (IllegalAccessException iae) {
+ IOException ioe = new IOException();
+ ioe.initCause(iae);
+ throw ioe;
+ }
+ }
+
+ /**
+ * Solve a system of composed of a Lower Triangular Matrix {@link RealMatrix}.
+ *
+ * <p>This method is called to solve systems of equations which are of the lower triangular
+ * form. The matrix {@link RealMatrix} is assumed, though not checked, to be in lower triangular
+ * form. The vector {@link RealVector} is overwritten with the solution. The matrix is checked
+ * that it is square and its dimensions match the length of the vector.
+ *
+ * @param rm RealMatrix which is lower triangular
+ * @param b RealVector this is overwritten
+ * @throws DimensionMismatchException if the matrix and vector are not conformable
+ * @throws NonSquareMatrixException if the matrix {@code rm} is not square
+ * @throws MathArithmeticException if the absolute value of one of the diagonal coefficient of
+ * {@code rm} is lower than {@link Precision#SAFE_MIN}
+ */
+ public static void solveLowerTriangularSystem(RealMatrix rm, RealVector b)
+ throws DimensionMismatchException, MathArithmeticException, NonSquareMatrixException {
+ if ((rm == null) || (b == null) || (rm.getRowDimension() != b.getDimension())) {
+ throw new DimensionMismatchException(
+ (rm == null) ? 0 : rm.getRowDimension(), (b == null) ? 0 : b.getDimension());
+ }
+ if (rm.getColumnDimension() != rm.getRowDimension()) {
+ throw new NonSquareMatrixException(rm.getRowDimension(), rm.getColumnDimension());
+ }
+ int rows = rm.getRowDimension();
+ for (int i = 0; i < rows; i++) {
+ double diag = rm.getEntry(i, i);
+ if (FastMath.abs(diag) < Precision.SAFE_MIN) {
+ throw new MathArithmeticException(LocalizedFormats.ZERO_DENOMINATOR);
+ }
+ double bi = b.getEntry(i) / diag;
+ b.setEntry(i, bi);
+ for (int j = i + 1; j < rows; j++) {
+ b.setEntry(j, b.getEntry(j) - bi * rm.getEntry(j, i));
+ }
+ }
+ }
+
+ /**
+ * Solver a system composed of an Upper Triangular Matrix {@link RealMatrix}.
+ *
+ * <p>This method is called to solve systems of equations which are of the lower triangular
+ * form. The matrix {@link RealMatrix} is assumed, though not checked, to be in upper triangular
+ * form. The vector {@link RealVector} is overwritten with the solution. The matrix is checked
+ * that it is square and its dimensions match the length of the vector.
+ *
+ * @param rm RealMatrix which is upper triangular
+ * @param b RealVector this is overwritten
+ * @throws DimensionMismatchException if the matrix and vector are not conformable
+ * @throws NonSquareMatrixException if the matrix {@code rm} is not square
+ * @throws MathArithmeticException if the absolute value of one of the diagonal coefficient of
+ * {@code rm} is lower than {@link Precision#SAFE_MIN}
+ */
+ public static void solveUpperTriangularSystem(RealMatrix rm, RealVector b)
+ throws DimensionMismatchException, MathArithmeticException, NonSquareMatrixException {
+ if ((rm == null) || (b == null) || (rm.getRowDimension() != b.getDimension())) {
+ throw new DimensionMismatchException(
+ (rm == null) ? 0 : rm.getRowDimension(), (b == null) ? 0 : b.getDimension());
+ }
+ if (rm.getColumnDimension() != rm.getRowDimension()) {
+ throw new NonSquareMatrixException(rm.getRowDimension(), rm.getColumnDimension());
+ }
+ int rows = rm.getRowDimension();
+ for (int i = rows - 1; i > -1; i--) {
+ double diag = rm.getEntry(i, i);
+ if (FastMath.abs(diag) < Precision.SAFE_MIN) {
+ throw new MathArithmeticException(LocalizedFormats.ZERO_DENOMINATOR);
+ }
+ double bi = b.getEntry(i) / diag;
+ b.setEntry(i, bi);
+ for (int j = i - 1; j > -1; j--) {
+ b.setEntry(j, b.getEntry(j) - bi * rm.getEntry(j, i));
+ }
+ }
+ }
+
+ /**
+ * Computes the inverse of the given matrix by splitting it into 4 sub-matrices.
+ *
+ * @param m Matrix whose inverse must be computed.
+ * @param splitIndex Index that determines the "split" line and column. The element
+ * corresponding to this index will part of the upper-left sub-matrix.
+ * @return the inverse of {@code m}.
+ * @throws NonSquareMatrixException if {@code m} is not square.
+ */
+ public static RealMatrix blockInverse(RealMatrix m, int splitIndex) {
+ final int n = m.getRowDimension();
+ if (m.getColumnDimension() != n) {
+ throw new NonSquareMatrixException(m.getRowDimension(), m.getColumnDimension());
+ }
+
+ final int splitIndex1 = splitIndex + 1;
+
+ final RealMatrix a = m.getSubMatrix(0, splitIndex, 0, splitIndex);
+ final RealMatrix b = m.getSubMatrix(0, splitIndex, splitIndex1, n - 1);
+ final RealMatrix c = m.getSubMatrix(splitIndex1, n - 1, 0, splitIndex);
+ final RealMatrix d = m.getSubMatrix(splitIndex1, n - 1, splitIndex1, n - 1);
+
+ final SingularValueDecomposition aDec = new SingularValueDecomposition(a);
+ final DecompositionSolver aSolver = aDec.getSolver();
+ if (!aSolver.isNonSingular()) {
+ throw new SingularMatrixException();
+ }
+ final RealMatrix aInv = aSolver.getInverse();
+
+ final SingularValueDecomposition dDec = new SingularValueDecomposition(d);
+ final DecompositionSolver dSolver = dDec.getSolver();
+ if (!dSolver.isNonSingular()) {
+ throw new SingularMatrixException();
+ }
+ final RealMatrix dInv = dSolver.getInverse();
+
+ final RealMatrix tmp1 = a.subtract(b.multiply(dInv).multiply(c));
+ final SingularValueDecomposition tmp1Dec = new SingularValueDecomposition(tmp1);
+ final DecompositionSolver tmp1Solver = tmp1Dec.getSolver();
+ if (!tmp1Solver.isNonSingular()) {
+ throw new SingularMatrixException();
+ }
+ final RealMatrix result00 = tmp1Solver.getInverse();
+
+ final RealMatrix tmp2 = d.subtract(c.multiply(aInv).multiply(b));
+ final SingularValueDecomposition tmp2Dec = new SingularValueDecomposition(tmp2);
+ final DecompositionSolver tmp2Solver = tmp2Dec.getSolver();
+ if (!tmp2Solver.isNonSingular()) {
+ throw new SingularMatrixException();
+ }
+ final RealMatrix result11 = tmp2Solver.getInverse();
+
+ final RealMatrix result01 = aInv.multiply(b).multiply(result11).scalarMultiply(-1);
+ final RealMatrix result10 = dInv.multiply(c).multiply(result00).scalarMultiply(-1);
+
+ final RealMatrix result = new Array2DRowRealMatrix(n, n);
+ result.setSubMatrix(result00.getData(), 0, 0);
+ result.setSubMatrix(result01.getData(), 0, splitIndex1);
+ result.setSubMatrix(result10.getData(), splitIndex1, 0);
+ result.setSubMatrix(result11.getData(), splitIndex1, splitIndex1);
+
+ return result;
+ }
+
+ /**
+ * Computes the inverse of the given matrix.
+ *
+ * <p>By default, the inverse of the matrix is computed using the QR-decomposition, unless a
+ * more efficient method can be determined for the input matrix.
+ *
+ * <p>Note: this method will use a singularity threshold of 0, use {@link #inverse(RealMatrix,
+ * double)} if a different threshold is needed.
+ *
+ * @param matrix Matrix whose inverse shall be computed
+ * @return the inverse of {@code matrix}
+ * @throws NullArgumentException if {@code matrix} is {@code null}
+ * @throws SingularMatrixException if m is singular
+ * @throws NonSquareMatrixException if matrix is not square
+ * @since 3.3
+ */
+ public static RealMatrix inverse(RealMatrix matrix)
+ throws NullArgumentException, SingularMatrixException, NonSquareMatrixException {
+ return inverse(matrix, 0);
+ }
+
+ /**
+ * Computes the inverse of the given matrix.
+ *
+ * <p>By default, the inverse of the matrix is computed using the QR-decomposition, unless a
+ * more efficient method can be determined for the input matrix.
+ *
+ * @param matrix Matrix whose inverse shall be computed
+ * @param threshold Singularity threshold
+ * @return the inverse of {@code m}
+ * @throws NullArgumentException if {@code matrix} is {@code null}
+ * @throws SingularMatrixException if matrix is singular
+ * @throws NonSquareMatrixException if matrix is not square
+ * @since 3.3
+ */
+ public static RealMatrix inverse(RealMatrix matrix, double threshold)
+ throws NullArgumentException, SingularMatrixException, NonSquareMatrixException {
+
+ MathUtils.checkNotNull(matrix);
+
+ if (!matrix.isSquare()) {
+ throw new NonSquareMatrixException(
+ matrix.getRowDimension(), matrix.getColumnDimension());
+ }
+
+ if (matrix instanceof DiagonalMatrix) {
+ return ((DiagonalMatrix) matrix).inverse(threshold);
+ } else {
+ QRDecomposition decomposition = new QRDecomposition(matrix, threshold);
+ return decomposition.getSolver().getInverse();
+ }
+ }
+}
diff --git a/src/main/java/org/apache/commons/math3/linear/NonPositiveDefiniteMatrixException.java b/src/main/java/org/apache/commons/math3/linear/NonPositiveDefiniteMatrixException.java
new file mode 100644
index 0000000..94d97d0
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/linear/NonPositiveDefiniteMatrixException.java
@@ -0,0 +1,75 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math3.linear;
+
+import org.apache.commons.math3.exception.NumberIsTooSmallException;
+import org.apache.commons.math3.exception.util.ExceptionContext;
+import org.apache.commons.math3.exception.util.LocalizedFormats;
+
+/**
+ * Exception to be thrown when a positive definite matrix is expected.
+ *
+ * @since 3.0
+ */
+public class NonPositiveDefiniteMatrixException extends NumberIsTooSmallException {
+ /** Serializable version Id. */
+ private static final long serialVersionUID = 1641613838113738061L;
+
+ /** Index (diagonal element). */
+ private final int index;
+
+ /** Threshold. */
+ private final double threshold;
+
+ /**
+ * Construct an exception.
+ *
+ * @param wrong Value that fails the positivity check.
+ * @param index Row (and column) index.
+ * @param threshold Absolute positivity threshold.
+ */
+ public NonPositiveDefiniteMatrixException(double wrong, int index, double threshold) {
+ super(wrong, threshold, false);
+ this.index = index;
+ this.threshold = threshold;
+
+ final ExceptionContext context = getContext();
+ context.addMessage(LocalizedFormats.NOT_POSITIVE_DEFINITE_MATRIX);
+ context.addMessage(LocalizedFormats.ARRAY_ELEMENT, wrong, index);
+ }
+
+ /**
+ * @return the row index.
+ */
+ public int getRow() {
+ return index;
+ }
+
+ /**
+ * @return the column index.
+ */
+ public int getColumn() {
+ return index;
+ }
+
+ /**
+ * @return the absolute positivity threshold.
+ */
+ public double getThreshold() {
+ return threshold;
+ }
+}
diff --git a/src/main/java/org/apache/commons/math3/linear/NonPositiveDefiniteOperatorException.java b/src/main/java/org/apache/commons/math3/linear/NonPositiveDefiniteOperatorException.java
new file mode 100644
index 0000000..6bfe001
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/linear/NonPositiveDefiniteOperatorException.java
@@ -0,0 +1,40 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math3.linear;
+
+import org.apache.commons.math3.exception.MathIllegalArgumentException;
+import org.apache.commons.math3.exception.util.LocalizedFormats;
+
+/**
+ * Exception to be thrown when a symmetric, definite positive {@link RealLinearOperator} is
+ * expected. Since the coefficients of the matrix are not accessible, the most general definition is
+ * used to check that {@code A} is not positive definite, i.e. there exists {@code x} such that
+ * {@code x' A x <= 0}. In the terminology of this exception, {@code A} is the "offending" linear
+ * operator and {@code x} the "offending" vector.
+ *
+ * @since 3.0
+ */
+public class NonPositiveDefiniteOperatorException extends MathIllegalArgumentException {
+ /** Serializable version Id. */
+ private static final long serialVersionUID = 917034489420549847L;
+
+ /** Creates a new instance of this class. */
+ public NonPositiveDefiniteOperatorException() {
+ super(LocalizedFormats.NON_POSITIVE_DEFINITE_OPERATOR);
+ }
+}
diff --git a/src/main/java/org/apache/commons/math3/linear/NonSelfAdjointOperatorException.java b/src/main/java/org/apache/commons/math3/linear/NonSelfAdjointOperatorException.java
new file mode 100644
index 0000000..f98a623
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/linear/NonSelfAdjointOperatorException.java
@@ -0,0 +1,41 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math3.linear;
+
+import org.apache.commons.math3.exception.MathIllegalArgumentException;
+import org.apache.commons.math3.exception.util.LocalizedFormats;
+
+/**
+ * Exception to be thrown when a self-adjoint {@link RealLinearOperator} is expected. Since the
+ * coefficients of the matrix are not accessible, the most general definition is used to check that
+ * A is not self-adjoint, i.e. there exist x and y such as {@code | x' A y - y' A x | >= eps}, where
+ * {@code eps} is a user-specified tolerance, and {@code x'} denotes the transpose of {@code x}. In
+ * the terminology of this exception, {@code A} is the "offending" linear operator, {@code x} and
+ * {@code y} are the first and second "offending" vectors, respectively.
+ *
+ * @since 3.0
+ */
+public class NonSelfAdjointOperatorException extends MathIllegalArgumentException {
+ /** Serializable version Id. */
+ private static final long serialVersionUID = 1784999305030258247L;
+
+ /** Creates a new instance of this class. */
+ public NonSelfAdjointOperatorException() {
+ super(LocalizedFormats.NON_SELF_ADJOINT_OPERATOR);
+ }
+}
diff --git a/src/main/java/org/apache/commons/math3/linear/NonSquareMatrixException.java b/src/main/java/org/apache/commons/math3/linear/NonSquareMatrixException.java
new file mode 100644
index 0000000..ebce448
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/linear/NonSquareMatrixException.java
@@ -0,0 +1,40 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math3.linear;
+
+import org.apache.commons.math3.exception.DimensionMismatchException;
+import org.apache.commons.math3.exception.util.LocalizedFormats;
+
+/**
+ * Exception to be thrown when a square matrix is expected.
+ *
+ * @since 3.0
+ */
+public class NonSquareMatrixException extends DimensionMismatchException {
+ /** Serializable version Id. */
+ private static final long serialVersionUID = -660069396594485772L;
+
+ /**
+ * Construct an exception from the mismatched dimensions.
+ *
+ * @param wrong Row dimension.
+ * @param expected Column dimension.
+ */
+ public NonSquareMatrixException(int wrong, int expected) {
+ super(LocalizedFormats.NON_SQUARE_MATRIX, wrong, expected);
+ }
+}
diff --git a/src/main/java/org/apache/commons/math3/linear/NonSquareOperatorException.java b/src/main/java/org/apache/commons/math3/linear/NonSquareOperatorException.java
new file mode 100644
index 0000000..7915b58
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/linear/NonSquareOperatorException.java
@@ -0,0 +1,40 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math3.linear;
+
+import org.apache.commons.math3.exception.DimensionMismatchException;
+import org.apache.commons.math3.exception.util.LocalizedFormats;
+
+/**
+ * Exception to be thrown when a square linear operator is expected.
+ *
+ * @since 3.0
+ */
+public class NonSquareOperatorException extends DimensionMismatchException {
+ /** Serializable version Id. */
+ private static final long serialVersionUID = -4145007524150846242L;
+
+ /**
+ * Construct an exception from the mismatched dimensions.
+ *
+ * @param wrong Row dimension.
+ * @param expected Column dimension.
+ */
+ public NonSquareOperatorException(int wrong, int expected) {
+ super(LocalizedFormats.NON_SQUARE_OPERATOR, wrong, expected);
+ }
+}
diff --git a/src/main/java/org/apache/commons/math3/linear/NonSymmetricMatrixException.java b/src/main/java/org/apache/commons/math3/linear/NonSymmetricMatrixException.java
new file mode 100644
index 0000000..c8e0718
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/linear/NonSymmetricMatrixException.java
@@ -0,0 +1,74 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math3.linear;
+
+import org.apache.commons.math3.exception.MathIllegalArgumentException;
+import org.apache.commons.math3.exception.util.LocalizedFormats;
+
+/**
+ * Exception to be thrown when a symmetric matrix is expected.
+ *
+ * @since 3.0
+ */
+public class NonSymmetricMatrixException extends MathIllegalArgumentException {
+ /** Serializable version Id. */
+ private static final long serialVersionUID = -7518495577824189882L;
+
+ /** Row. */
+ private final int row;
+
+ /** Column. */
+ private final int column;
+
+ /** Threshold. */
+ private final double threshold;
+
+ /**
+ * Construct an exception.
+ *
+ * @param row Row index.
+ * @param column Column index.
+ * @param threshold Relative symmetry threshold.
+ */
+ public NonSymmetricMatrixException(int row, int column, double threshold) {
+ super(LocalizedFormats.NON_SYMMETRIC_MATRIX, row, column, threshold);
+ this.row = row;
+ this.column = column;
+ this.threshold = threshold;
+ }
+
+ /**
+ * @return the row index of the entry.
+ */
+ public int getRow() {
+ return row;
+ }
+
+ /**
+ * @return the column index of the entry.
+ */
+ public int getColumn() {
+ return column;
+ }
+
+ /**
+ * @return the relative symmetry threshold.
+ */
+ public double getThreshold() {
+ return threshold;
+ }
+}
diff --git a/src/main/java/org/apache/commons/math3/linear/OpenMapRealMatrix.java b/src/main/java/org/apache/commons/math3/linear/OpenMapRealMatrix.java
new file mode 100644
index 0000000..95bc3cb
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/linear/OpenMapRealMatrix.java
@@ -0,0 +1,301 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math3.linear;
+
+import org.apache.commons.math3.exception.DimensionMismatchException;
+import org.apache.commons.math3.exception.NotStrictlyPositiveException;
+import org.apache.commons.math3.exception.NumberIsTooLargeException;
+import org.apache.commons.math3.exception.OutOfRangeException;
+import org.apache.commons.math3.util.OpenIntToDoubleHashMap;
+
+import java.io.Serializable;
+
+/**
+ * Sparse matrix implementation based on an open addressed map.
+ *
+ * <p>Caveat: This implementation assumes that, for any {@code x}, the equality {@code x * 0d == 0d}
+ * holds. But it is is not true for {@code NaN}. Moreover, zero entries will lose their sign. Some
+ * operations (that involve {@code NaN} and/or infinities) may thus give incorrect results.
+ *
+ * @since 2.0
+ */
+public class OpenMapRealMatrix extends AbstractRealMatrix
+ implements SparseRealMatrix, Serializable {
+ /** Serializable version identifier. */
+ private static final long serialVersionUID = -5962461716457143437L;
+
+ /** Number of rows of the matrix. */
+ private final int rows;
+
+ /** Number of columns of the matrix. */
+ private final int columns;
+
+ /** Storage for (sparse) matrix elements. */
+ private final OpenIntToDoubleHashMap entries;
+
+ /**
+ * Build a sparse matrix with the supplied row and column dimensions.
+ *
+ * @param rowDimension Number of rows of the matrix.
+ * @param columnDimension Number of columns of the matrix.
+ * @throws NotStrictlyPositiveException if row or column dimension is not positive.
+ * @throws NumberIsTooLargeException if the total number of entries of the matrix is larger than
+ * {@code Integer.MAX_VALUE}.
+ */
+ public OpenMapRealMatrix(int rowDimension, int columnDimension)
+ throws NotStrictlyPositiveException, NumberIsTooLargeException {
+ super(rowDimension, columnDimension);
+ long lRow = rowDimension;
+ long lCol = columnDimension;
+ if (lRow * lCol >= Integer.MAX_VALUE) {
+ throw new NumberIsTooLargeException(lRow * lCol, Integer.MAX_VALUE, false);
+ }
+ this.rows = rowDimension;
+ this.columns = columnDimension;
+ this.entries = new OpenIntToDoubleHashMap(0.0);
+ }
+
+ /**
+ * Build a matrix by copying another one.
+ *
+ * @param matrix matrix to copy.
+ */
+ public OpenMapRealMatrix(OpenMapRealMatrix matrix) {
+ this.rows = matrix.rows;
+ this.columns = matrix.columns;
+ this.entries = new OpenIntToDoubleHashMap(matrix.entries);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public OpenMapRealMatrix copy() {
+ return new OpenMapRealMatrix(this);
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @throws NumberIsTooLargeException if the total number of entries of the matrix is larger than
+ * {@code Integer.MAX_VALUE}.
+ */
+ @Override
+ public OpenMapRealMatrix createMatrix(int rowDimension, int columnDimension)
+ throws NotStrictlyPositiveException, NumberIsTooLargeException {
+ return new OpenMapRealMatrix(rowDimension, columnDimension);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public int getColumnDimension() {
+ return columns;
+ }
+
+ /**
+ * Compute the sum of this matrix and {@code m}.
+ *
+ * @param m Matrix to be added.
+ * @return {@code this} + {@code m}.
+ * @throws MatrixDimensionMismatchException if {@code m} is not the same size as {@code this}.
+ */
+ public OpenMapRealMatrix add(OpenMapRealMatrix m) throws MatrixDimensionMismatchException {
+
+ MatrixUtils.checkAdditionCompatible(this, m);
+
+ final OpenMapRealMatrix out = new OpenMapRealMatrix(this);
+ for (OpenIntToDoubleHashMap.Iterator iterator = m.entries.iterator();
+ iterator.hasNext(); ) {
+ iterator.advance();
+ final int row = iterator.key() / columns;
+ final int col = iterator.key() - row * columns;
+ out.setEntry(row, col, getEntry(row, col) + iterator.value());
+ }
+
+ return out;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public OpenMapRealMatrix subtract(final RealMatrix m) throws MatrixDimensionMismatchException {
+ try {
+ return subtract((OpenMapRealMatrix) m);
+ } catch (ClassCastException cce) {
+ return (OpenMapRealMatrix) super.subtract(m);
+ }
+ }
+
+ /**
+ * Subtract {@code m} from this matrix.
+ *
+ * @param m Matrix to be subtracted.
+ * @return {@code this} - {@code m}.
+ * @throws MatrixDimensionMismatchException if {@code m} is not the same size as {@code this}.
+ */
+ public OpenMapRealMatrix subtract(OpenMapRealMatrix m) throws MatrixDimensionMismatchException {
+ MatrixUtils.checkAdditionCompatible(this, m);
+
+ final OpenMapRealMatrix out = new OpenMapRealMatrix(this);
+ for (OpenIntToDoubleHashMap.Iterator iterator = m.entries.iterator();
+ iterator.hasNext(); ) {
+ iterator.advance();
+ final int row = iterator.key() / columns;
+ final int col = iterator.key() - row * columns;
+ out.setEntry(row, col, getEntry(row, col) - iterator.value());
+ }
+
+ return out;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @throws NumberIsTooLargeException if {@code m} is an {@code OpenMapRealMatrix}, and the total
+ * number of entries of the product is larger than {@code Integer.MAX_VALUE}.
+ */
+ @Override
+ public RealMatrix multiply(final RealMatrix m)
+ throws DimensionMismatchException, NumberIsTooLargeException {
+ try {
+ return multiply((OpenMapRealMatrix) m);
+ } catch (ClassCastException cce) {
+
+ MatrixUtils.checkMultiplicationCompatible(this, m);
+
+ final int outCols = m.getColumnDimension();
+ final BlockRealMatrix out = new BlockRealMatrix(rows, outCols);
+ for (OpenIntToDoubleHashMap.Iterator iterator = entries.iterator();
+ iterator.hasNext(); ) {
+ iterator.advance();
+ final double value = iterator.value();
+ final int key = iterator.key();
+ final int i = key / columns;
+ final int k = key % columns;
+ for (int j = 0; j < outCols; ++j) {
+ out.addToEntry(i, j, value * m.getEntry(k, j));
+ }
+ }
+
+ return out;
+ }
+ }
+
+ /**
+ * Postmultiply this matrix by {@code m}.
+ *
+ * @param m Matrix to postmultiply by.
+ * @return {@code this} * {@code m}.
+ * @throws DimensionMismatchException if the number of rows of {@code m} differ from the number
+ * of columns of {@code this} matrix.
+ * @throws NumberIsTooLargeException if the total number of entries of the product is larger
+ * than {@code Integer.MAX_VALUE}.
+ */
+ public OpenMapRealMatrix multiply(OpenMapRealMatrix m)
+ throws DimensionMismatchException, NumberIsTooLargeException {
+ // Safety check.
+ MatrixUtils.checkMultiplicationCompatible(this, m);
+
+ final int outCols = m.getColumnDimension();
+ OpenMapRealMatrix out = new OpenMapRealMatrix(rows, outCols);
+ for (OpenIntToDoubleHashMap.Iterator iterator = entries.iterator(); iterator.hasNext(); ) {
+ iterator.advance();
+ final double value = iterator.value();
+ final int key = iterator.key();
+ final int i = key / columns;
+ final int k = key % columns;
+ for (int j = 0; j < outCols; ++j) {
+ final int rightKey = m.computeKey(k, j);
+ if (m.entries.containsKey(rightKey)) {
+ final int outKey = out.computeKey(i, j);
+ final double outValue =
+ out.entries.get(outKey) + value * m.entries.get(rightKey);
+ if (outValue == 0.0) {
+ out.entries.remove(outKey);
+ } else {
+ out.entries.put(outKey, outValue);
+ }
+ }
+ }
+ }
+
+ return out;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public double getEntry(int row, int column) throws OutOfRangeException {
+ MatrixUtils.checkRowIndex(this, row);
+ MatrixUtils.checkColumnIndex(this, column);
+ return entries.get(computeKey(row, column));
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public int getRowDimension() {
+ return rows;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void setEntry(int row, int column, double value) throws OutOfRangeException {
+ MatrixUtils.checkRowIndex(this, row);
+ MatrixUtils.checkColumnIndex(this, column);
+ if (value == 0.0) {
+ entries.remove(computeKey(row, column));
+ } else {
+ entries.put(computeKey(row, column), value);
+ }
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void addToEntry(int row, int column, double increment) throws OutOfRangeException {
+ MatrixUtils.checkRowIndex(this, row);
+ MatrixUtils.checkColumnIndex(this, column);
+ final int key = computeKey(row, column);
+ final double value = entries.get(key) + increment;
+ if (value == 0.0) {
+ entries.remove(key);
+ } else {
+ entries.put(key, value);
+ }
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void multiplyEntry(int row, int column, double factor) throws OutOfRangeException {
+ MatrixUtils.checkRowIndex(this, row);
+ MatrixUtils.checkColumnIndex(this, column);
+ final int key = computeKey(row, column);
+ final double value = entries.get(key) * factor;
+ if (value == 0.0) {
+ entries.remove(key);
+ } else {
+ entries.put(key, value);
+ }
+ }
+
+ /**
+ * Compute the key to access a matrix element
+ *
+ * @param row row index of the matrix element
+ * @param column column index of the matrix element
+ * @return key within the map to access the matrix element
+ */
+ private int computeKey(int row, int column) {
+ return row * columns + column;
+ }
+}
diff --git a/src/main/java/org/apache/commons/math3/linear/OpenMapRealVector.java b/src/main/java/org/apache/commons/math3/linear/OpenMapRealVector.java
new file mode 100644
index 0000000..d13e47e
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/linear/OpenMapRealVector.java
@@ -0,0 +1,799 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math3.linear;
+
+import org.apache.commons.math3.exception.DimensionMismatchException;
+import org.apache.commons.math3.exception.MathArithmeticException;
+import org.apache.commons.math3.exception.NotPositiveException;
+import org.apache.commons.math3.exception.OutOfRangeException;
+import org.apache.commons.math3.exception.util.LocalizedFormats;
+import org.apache.commons.math3.util.FastMath;
+import org.apache.commons.math3.util.OpenIntToDoubleHashMap;
+import org.apache.commons.math3.util.OpenIntToDoubleHashMap.Iterator;
+
+import java.io.Serializable;
+
+/**
+ * This class implements the {@link RealVector} interface with a {@link OpenIntToDoubleHashMap}
+ * backing store.
+ *
+ * <p>Caveat: This implementation assumes that, for any {@code x}, the equality {@code x * 0d == 0d}
+ * holds. But it is is not true for {@code NaN}. Moreover, zero entries will lose their sign. Some
+ * operations (that involve {@code NaN} and/or infinities) may thus give incorrect results, like
+ * multiplications, divisions or functions mapping.
+ *
+ * @since 2.0
+ */
+public class OpenMapRealVector extends SparseRealVector implements Serializable {
+ /** Default Tolerance for having a value considered zero. */
+ public static final double DEFAULT_ZERO_TOLERANCE = 1.0e-12;
+
+ /** Serializable version identifier. */
+ private static final long serialVersionUID = 8772222695580707260L;
+
+ /** Entries of the vector. */
+ private final OpenIntToDoubleHashMap entries;
+
+ /** Dimension of the vector. */
+ private final int virtualSize;
+
+ /** Tolerance for having a value considered zero. */
+ private final double epsilon;
+
+ /**
+ * Build a 0-length vector. Zero-length vectors may be used to initialized construction of
+ * vectors by data gathering. We start with zero-length and use either the {@link
+ * #OpenMapRealVector(OpenMapRealVector, int)} constructor or one of the {@code append} method
+ * ({@link #append(double)}, {@link #append(RealVector)}) to gather data into this vector.
+ */
+ public OpenMapRealVector() {
+ this(0, DEFAULT_ZERO_TOLERANCE);
+ }
+
+ /**
+ * Construct a vector of zeroes.
+ *
+ * @param dimension Size of the vector.
+ */
+ public OpenMapRealVector(int dimension) {
+ this(dimension, DEFAULT_ZERO_TOLERANCE);
+ }
+
+ /**
+ * Construct a vector of zeroes, specifying zero tolerance.
+ *
+ * @param dimension Size of the vector.
+ * @param epsilon Tolerance below which a value considered zero.
+ */
+ public OpenMapRealVector(int dimension, double epsilon) {
+ virtualSize = dimension;
+ entries = new OpenIntToDoubleHashMap(0.0);
+ this.epsilon = epsilon;
+ }
+
+ /**
+ * Build a resized vector, for use with append.
+ *
+ * @param v Original vector.
+ * @param resize Amount to add.
+ */
+ protected OpenMapRealVector(OpenMapRealVector v, int resize) {
+ virtualSize = v.getDimension() + resize;
+ entries = new OpenIntToDoubleHashMap(v.entries);
+ epsilon = v.epsilon;
+ }
+
+ /**
+ * Build a vector with known the sparseness (for advanced use only).
+ *
+ * @param dimension Size of the vector.
+ * @param expectedSize The expected number of non-zero entries.
+ */
+ public OpenMapRealVector(int dimension, int expectedSize) {
+ this(dimension, expectedSize, DEFAULT_ZERO_TOLERANCE);
+ }
+
+ /**
+ * Build a vector with known the sparseness and zero tolerance setting (for advanced use only).
+ *
+ * @param dimension Size of the vector.
+ * @param expectedSize Expected number of non-zero entries.
+ * @param epsilon Tolerance below which a value is considered zero.
+ */
+ public OpenMapRealVector(int dimension, int expectedSize, double epsilon) {
+ virtualSize = dimension;
+ entries = new OpenIntToDoubleHashMap(expectedSize, 0.0);
+ this.epsilon = epsilon;
+ }
+
+ /**
+ * Create from an array. Only non-zero entries will be stored.
+ *
+ * @param values Set of values to create from.
+ */
+ public OpenMapRealVector(double[] values) {
+ this(values, DEFAULT_ZERO_TOLERANCE);
+ }
+
+ /**
+ * Create from an array, specifying zero tolerance. Only non-zero entries will be stored.
+ *
+ * @param values Set of values to create from.
+ * @param epsilon Tolerance below which a value is considered zero.
+ */
+ public OpenMapRealVector(double[] values, double epsilon) {
+ virtualSize = values.length;
+ entries = new OpenIntToDoubleHashMap(0.0);
+ this.epsilon = epsilon;
+ for (int key = 0; key < values.length; key++) {
+ double value = values[key];
+ if (!isDefaultValue(value)) {
+ entries.put(key, value);
+ }
+ }
+ }
+
+ /**
+ * Create from an array. Only non-zero entries will be stored.
+ *
+ * @param values The set of values to create from
+ */
+ public OpenMapRealVector(Double[] values) {
+ this(values, DEFAULT_ZERO_TOLERANCE);
+ }
+
+ /**
+ * Create from an array. Only non-zero entries will be stored.
+ *
+ * @param values Set of values to create from.
+ * @param epsilon Tolerance below which a value is considered zero.
+ */
+ public OpenMapRealVector(Double[] values, double epsilon) {
+ virtualSize = values.length;
+ entries = new OpenIntToDoubleHashMap(0.0);
+ this.epsilon = epsilon;
+ for (int key = 0; key < values.length; key++) {
+ double value = values[key].doubleValue();
+ if (!isDefaultValue(value)) {
+ entries.put(key, value);
+ }
+ }
+ }
+
+ /**
+ * Copy constructor.
+ *
+ * @param v Instance to copy from.
+ */
+ public OpenMapRealVector(OpenMapRealVector v) {
+ virtualSize = v.getDimension();
+ entries = new OpenIntToDoubleHashMap(v.getEntries());
+ epsilon = v.epsilon;
+ }
+
+ /**
+ * Generic copy constructor.
+ *
+ * @param v Instance to copy from.
+ */
+ public OpenMapRealVector(RealVector v) {
+ virtualSize = v.getDimension();
+ entries = new OpenIntToDoubleHashMap(0.0);
+ epsilon = DEFAULT_ZERO_TOLERANCE;
+ for (int key = 0; key < virtualSize; key++) {
+ double value = v.getEntry(key);
+ if (!isDefaultValue(value)) {
+ entries.put(key, value);
+ }
+ }
+ }
+
+ /**
+ * Get the entries of this instance.
+ *
+ * @return the entries of this instance.
+ */
+ private OpenIntToDoubleHashMap getEntries() {
+ return entries;
+ }
+
+ /**
+ * Determine if this value is within epsilon of zero.
+ *
+ * @param value Value to test
+ * @return {@code true} if this value is within epsilon to zero, {@code false} otherwise.
+ * @since 2.1
+ */
+ protected boolean isDefaultValue(double value) {
+ return FastMath.abs(value) < epsilon;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public RealVector add(RealVector v) throws DimensionMismatchException {
+ checkVectorDimensions(v.getDimension());
+ if (v instanceof OpenMapRealVector) {
+ return add((OpenMapRealVector) v);
+ } else {
+ return super.add(v);
+ }
+ }
+
+ /**
+ * Optimized method to add two OpenMapRealVectors. It copies the larger vector, then iterates
+ * over the smaller.
+ *
+ * @param v Vector to add.
+ * @return the sum of {@code this} and {@code v}.
+ * @throws DimensionMismatchException if the dimensions do not match.
+ */
+ public OpenMapRealVector add(OpenMapRealVector v) throws DimensionMismatchException {
+ checkVectorDimensions(v.getDimension());
+ boolean copyThis = entries.size() > v.entries.size();
+ OpenMapRealVector res = copyThis ? this.copy() : v.copy();
+ Iterator iter = copyThis ? v.entries.iterator() : entries.iterator();
+ OpenIntToDoubleHashMap randomAccess = copyThis ? entries : v.entries;
+ while (iter.hasNext()) {
+ iter.advance();
+ int key = iter.key();
+ if (randomAccess.containsKey(key)) {
+ res.setEntry(key, randomAccess.get(key) + iter.value());
+ } else {
+ res.setEntry(key, iter.value());
+ }
+ }
+ return res;
+ }
+
+ /**
+ * Optimized method to append a OpenMapRealVector.
+ *
+ * @param v vector to append
+ * @return The result of appending {@code v} to self
+ */
+ public OpenMapRealVector append(OpenMapRealVector v) {
+ OpenMapRealVector res = new OpenMapRealVector(this, v.getDimension());
+ Iterator iter = v.entries.iterator();
+ while (iter.hasNext()) {
+ iter.advance();
+ res.setEntry(iter.key() + virtualSize, iter.value());
+ }
+ return res;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public OpenMapRealVector append(RealVector v) {
+ if (v instanceof OpenMapRealVector) {
+ return append((OpenMapRealVector) v);
+ } else {
+ final OpenMapRealVector res = new OpenMapRealVector(this, v.getDimension());
+ for (int i = 0; i < v.getDimension(); i++) {
+ res.setEntry(i + virtualSize, v.getEntry(i));
+ }
+ return res;
+ }
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public OpenMapRealVector append(double d) {
+ OpenMapRealVector res = new OpenMapRealVector(this, 1);
+ res.setEntry(virtualSize, d);
+ return res;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @since 2.1
+ */
+ @Override
+ public OpenMapRealVector copy() {
+ return new OpenMapRealVector(this);
+ }
+
+ /**
+ * Computes the dot product. Note that the computation is now performed in the parent class: no
+ * performance improvement is to be expected from this overloaded method. The previous
+ * implementation was buggy and cannot be easily fixed (see MATH-795).
+ *
+ * @param v Vector.
+ * @return the dot product of this vector with {@code v}.
+ * @throws DimensionMismatchException if {@code v} is not the same size as {@code this} vector.
+ * @deprecated as of 3.1 (to be removed in 4.0). The computation is performed by the parent
+ * class. The method must be kept to maintain backwards compatibility.
+ */
+ @Deprecated
+ public double dotProduct(OpenMapRealVector v) throws DimensionMismatchException {
+ return dotProduct((RealVector) v);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public OpenMapRealVector ebeDivide(RealVector v) throws DimensionMismatchException {
+ checkVectorDimensions(v.getDimension());
+ OpenMapRealVector res = new OpenMapRealVector(this);
+ /*
+ * MATH-803: it is not sufficient to loop through non zero entries of
+ * this only. Indeed, if this[i] = 0d and v[i] = 0d, then
+ * this[i] / v[i] = NaN, and not 0d.
+ */
+ final int n = getDimension();
+ for (int i = 0; i < n; i++) {
+ res.setEntry(i, this.getEntry(i) / v.getEntry(i));
+ }
+ return res;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public OpenMapRealVector ebeMultiply(RealVector v) throws DimensionMismatchException {
+ checkVectorDimensions(v.getDimension());
+ OpenMapRealVector res = new OpenMapRealVector(this);
+ Iterator iter = entries.iterator();
+ while (iter.hasNext()) {
+ iter.advance();
+ res.setEntry(iter.key(), iter.value() * v.getEntry(iter.key()));
+ }
+ return res;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public OpenMapRealVector getSubVector(int index, int n)
+ throws NotPositiveException, OutOfRangeException {
+ checkIndex(index);
+ if (n < 0) {
+ throw new NotPositiveException(
+ LocalizedFormats.NUMBER_OF_ELEMENTS_SHOULD_BE_POSITIVE, n);
+ }
+ checkIndex(index + n - 1);
+ OpenMapRealVector res = new OpenMapRealVector(n);
+ int end = index + n;
+ Iterator iter = entries.iterator();
+ while (iter.hasNext()) {
+ iter.advance();
+ int key = iter.key();
+ if (key >= index && key < end) {
+ res.setEntry(key - index, iter.value());
+ }
+ }
+ return res;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public int getDimension() {
+ return virtualSize;
+ }
+
+ /**
+ * Optimized method to compute distance.
+ *
+ * @param v Vector to compute distance to.
+ * @return the distance from {@code this} and {@code v}.
+ * @throws DimensionMismatchException if the dimensions do not match.
+ */
+ public double getDistance(OpenMapRealVector v) throws DimensionMismatchException {
+ checkVectorDimensions(v.getDimension());
+ Iterator iter = entries.iterator();
+ double res = 0;
+ while (iter.hasNext()) {
+ iter.advance();
+ int key = iter.key();
+ double delta;
+ delta = iter.value() - v.getEntry(key);
+ res += delta * delta;
+ }
+ iter = v.getEntries().iterator();
+ while (iter.hasNext()) {
+ iter.advance();
+ int key = iter.key();
+ if (!entries.containsKey(key)) {
+ final double value = iter.value();
+ res += value * value;
+ }
+ }
+ return FastMath.sqrt(res);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public double getDistance(RealVector v) throws DimensionMismatchException {
+ checkVectorDimensions(v.getDimension());
+ if (v instanceof OpenMapRealVector) {
+ return getDistance((OpenMapRealVector) v);
+ } else {
+ return super.getDistance(v);
+ }
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public double getEntry(int index) throws OutOfRangeException {
+ checkIndex(index);
+ return entries.get(index);
+ }
+
+ /**
+ * Distance between two vectors. This method computes the distance consistent with L<sub>1</sub>
+ * norm, i.e. the sum of the absolute values of elements differences.
+ *
+ * @param v Vector to which distance is requested.
+ * @return distance between this vector and {@code v}.
+ * @throws DimensionMismatchException if the dimensions do not match.
+ */
+ public double getL1Distance(OpenMapRealVector v) throws DimensionMismatchException {
+ checkVectorDimensions(v.getDimension());
+ double max = 0;
+ Iterator iter = entries.iterator();
+ while (iter.hasNext()) {
+ iter.advance();
+ double delta = FastMath.abs(iter.value() - v.getEntry(iter.key()));
+ max += delta;
+ }
+ iter = v.getEntries().iterator();
+ while (iter.hasNext()) {
+ iter.advance();
+ int key = iter.key();
+ if (!entries.containsKey(key)) {
+ double delta = FastMath.abs(iter.value());
+ max += FastMath.abs(delta);
+ }
+ }
+ return max;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public double getL1Distance(RealVector v) throws DimensionMismatchException {
+ checkVectorDimensions(v.getDimension());
+ if (v instanceof OpenMapRealVector) {
+ return getL1Distance((OpenMapRealVector) v);
+ } else {
+ return super.getL1Distance(v);
+ }
+ }
+
+ /**
+ * Optimized method to compute LInfDistance.
+ *
+ * @param v Vector to compute distance from.
+ * @return the LInfDistance.
+ * @throws DimensionMismatchException if the dimensions do not match.
+ */
+ private double getLInfDistance(OpenMapRealVector v) throws DimensionMismatchException {
+ checkVectorDimensions(v.getDimension());
+ double max = 0;
+ Iterator iter = entries.iterator();
+ while (iter.hasNext()) {
+ iter.advance();
+ double delta = FastMath.abs(iter.value() - v.getEntry(iter.key()));
+ if (delta > max) {
+ max = delta;
+ }
+ }
+ iter = v.getEntries().iterator();
+ while (iter.hasNext()) {
+ iter.advance();
+ int key = iter.key();
+ if (!entries.containsKey(key) && iter.value() > max) {
+ max = iter.value();
+ }
+ }
+ return max;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public double getLInfDistance(RealVector v) throws DimensionMismatchException {
+ checkVectorDimensions(v.getDimension());
+ if (v instanceof OpenMapRealVector) {
+ return getLInfDistance((OpenMapRealVector) v);
+ } else {
+ return super.getLInfDistance(v);
+ }
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public boolean isInfinite() {
+ boolean infiniteFound = false;
+ Iterator iter = entries.iterator();
+ while (iter.hasNext()) {
+ iter.advance();
+ final double value = iter.value();
+ if (Double.isNaN(value)) {
+ return false;
+ }
+ if (Double.isInfinite(value)) {
+ infiniteFound = true;
+ }
+ }
+ return infiniteFound;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public boolean isNaN() {
+ Iterator iter = entries.iterator();
+ while (iter.hasNext()) {
+ iter.advance();
+ if (Double.isNaN(iter.value())) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public OpenMapRealVector mapAdd(double d) {
+ return copy().mapAddToSelf(d);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public OpenMapRealVector mapAddToSelf(double d) {
+ for (int i = 0; i < virtualSize; i++) {
+ setEntry(i, getEntry(i) + d);
+ }
+ return this;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void setEntry(int index, double value) throws OutOfRangeException {
+ checkIndex(index);
+ if (!isDefaultValue(value)) {
+ entries.put(index, value);
+ } else if (entries.containsKey(index)) {
+ entries.remove(index);
+ }
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void setSubVector(int index, RealVector v) throws OutOfRangeException {
+ checkIndex(index);
+ checkIndex(index + v.getDimension() - 1);
+ for (int i = 0; i < v.getDimension(); i++) {
+ setEntry(i + index, v.getEntry(i));
+ }
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void set(double value) {
+ for (int i = 0; i < virtualSize; i++) {
+ setEntry(i, value);
+ }
+ }
+
+ /**
+ * Optimized method to subtract OpenMapRealVectors.
+ *
+ * @param v Vector to subtract from {@code this}.
+ * @return the difference of {@code this} and {@code v}.
+ * @throws DimensionMismatchException if the dimensions do not match.
+ */
+ public OpenMapRealVector subtract(OpenMapRealVector v) throws DimensionMismatchException {
+ checkVectorDimensions(v.getDimension());
+ OpenMapRealVector res = copy();
+ Iterator iter = v.getEntries().iterator();
+ while (iter.hasNext()) {
+ iter.advance();
+ int key = iter.key();
+ if (entries.containsKey(key)) {
+ res.setEntry(key, entries.get(key) - iter.value());
+ } else {
+ res.setEntry(key, -iter.value());
+ }
+ }
+ return res;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public RealVector subtract(RealVector v) throws DimensionMismatchException {
+ checkVectorDimensions(v.getDimension());
+ if (v instanceof OpenMapRealVector) {
+ return subtract((OpenMapRealVector) v);
+ } else {
+ return super.subtract(v);
+ }
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public OpenMapRealVector unitVector() throws MathArithmeticException {
+ OpenMapRealVector res = copy();
+ res.unitize();
+ return res;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void unitize() throws MathArithmeticException {
+ double norm = getNorm();
+ if (isDefaultValue(norm)) {
+ throw new MathArithmeticException(LocalizedFormats.ZERO_NORM);
+ }
+ Iterator iter = entries.iterator();
+ while (iter.hasNext()) {
+ iter.advance();
+ entries.put(iter.key(), iter.value() / norm);
+ }
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public double[] toArray() {
+ double[] res = new double[virtualSize];
+ Iterator iter = entries.iterator();
+ while (iter.hasNext()) {
+ iter.advance();
+ res[iter.key()] = iter.value();
+ }
+ return res;
+ }
+
+ /**
+ * {@inheritDoc} Implementation Note: This works on exact values, and as a result it is possible
+ * for {@code a.subtract(b)} to be the zero vector, while {@code a.hashCode() != b.hashCode()}.
+ */
+ @Override
+ public int hashCode() {
+ final int prime = 31;
+ int result = 1;
+ long temp;
+ temp = Double.doubleToLongBits(epsilon);
+ result = prime * result + (int) (temp ^ (temp >>> 32));
+ result = prime * result + virtualSize;
+ Iterator iter = entries.iterator();
+ while (iter.hasNext()) {
+ iter.advance();
+ temp = Double.doubleToLongBits(iter.value());
+ result = prime * result + (int) (temp ^ (temp >> 32));
+ }
+ return result;
+ }
+
+ /**
+ * {@inheritDoc} Implementation Note: This performs an exact comparison, and as a result it is
+ * possible for {@code a.subtract(b}} to be the zero vector, while {@code a.equals(b) == false}.
+ */
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj) {
+ return true;
+ }
+ if (!(obj instanceof OpenMapRealVector)) {
+ return false;
+ }
+ OpenMapRealVector other = (OpenMapRealVector) obj;
+ if (virtualSize != other.virtualSize) {
+ return false;
+ }
+ if (Double.doubleToLongBits(epsilon) != Double.doubleToLongBits(other.epsilon)) {
+ return false;
+ }
+ Iterator iter = entries.iterator();
+ while (iter.hasNext()) {
+ iter.advance();
+ double test = other.getEntry(iter.key());
+ if (Double.doubleToLongBits(test) != Double.doubleToLongBits(iter.value())) {
+ return false;
+ }
+ }
+ iter = other.getEntries().iterator();
+ while (iter.hasNext()) {
+ iter.advance();
+ double test = iter.value();
+ if (Double.doubleToLongBits(test) != Double.doubleToLongBits(getEntry(iter.key()))) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ /**
+ * @return the percentage of none zero elements as a decimal percent.
+ * @since 2.2
+ */
+ public double getSparsity() {
+ return (double) entries.size() / (double) getDimension();
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public java.util.Iterator<Entry> sparseIterator() {
+ return new OpenMapSparseIterator();
+ }
+
+ /**
+ * Implementation of {@code Entry} optimized for OpenMap. This implementation does not allow
+ * arbitrary calls to {@code setIndex} since the order in which entries are returned is
+ * undefined.
+ */
+ protected class OpenMapEntry extends Entry {
+ /** Iterator pointing to the entry. */
+ private final Iterator iter;
+
+ /**
+ * Build an entry from an iterator point to an element.
+ *
+ * @param iter Iterator pointing to the entry.
+ */
+ protected OpenMapEntry(Iterator iter) {
+ this.iter = iter;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public double getValue() {
+ return iter.value();
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void setValue(double value) {
+ entries.put(iter.key(), value);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public int getIndex() {
+ return iter.key();
+ }
+ }
+
+ /**
+ * Iterator class to do iteration over just the non-zero elements. This implementation is
+ * fail-fast, so cannot be used to modify any zero element.
+ */
+ protected class OpenMapSparseIterator implements java.util.Iterator<Entry> {
+ /** Underlying iterator. */
+ private final Iterator iter;
+
+ /** Current entry. */
+ private final Entry current;
+
+ /** Simple constructor. */
+ protected OpenMapSparseIterator() {
+ iter = entries.iterator();
+ current = new OpenMapEntry(iter);
+ }
+
+ /** {@inheritDoc} */
+ public boolean hasNext() {
+ return iter.hasNext();
+ }
+
+ /** {@inheritDoc} */
+ public Entry next() {
+ iter.advance();
+ return current;
+ }
+
+ /** {@inheritDoc} */
+ public void remove() {
+ throw new UnsupportedOperationException("Not supported");
+ }
+ }
+}
diff --git a/src/main/java/org/apache/commons/math3/linear/PreconditionedIterativeLinearSolver.java b/src/main/java/org/apache/commons/math3/linear/PreconditionedIterativeLinearSolver.java
new file mode 100644
index 0000000..6808d46
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/linear/PreconditionedIterativeLinearSolver.java
@@ -0,0 +1,205 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math3.linear;
+
+import org.apache.commons.math3.exception.DimensionMismatchException;
+import org.apache.commons.math3.exception.MaxCountExceededException;
+import org.apache.commons.math3.exception.NullArgumentException;
+import org.apache.commons.math3.util.IterationManager;
+import org.apache.commons.math3.util.MathUtils;
+
+/**
+ * This abstract class defines preconditioned iterative solvers. When A is ill-conditioned, instead
+ * of solving system A &middot; x = b directly, it is preferable to solve either <center> (M
+ * &middot; A) &middot; x = M &middot; b </center> (left preconditioning), or <center> (A &middot;
+ * M) &middot; y = b, &nbsp;&nbsp;&nbsp;&nbsp;followed by M &middot; y = x </center> (right
+ * preconditioning), where M approximates in some way A<sup>-1</sup>, while matrix-vector products
+ * of the type M &middot; y remain comparatively easy to compute. In this library, M (not
+ * M<sup>-1</sup>!) is called the <em>preconditionner</em>.
+ *
+ * <p>Concrete implementations of this abstract class must be provided with the preconditioner M, as
+ * a {@link RealLinearOperator}.
+ *
+ * @since 3.0
+ */
+public abstract class PreconditionedIterativeLinearSolver extends IterativeLinearSolver {
+
+ /**
+ * Creates a new instance of this class, with default iteration manager.
+ *
+ * @param maxIterations the maximum number of iterations
+ */
+ public PreconditionedIterativeLinearSolver(final int maxIterations) {
+ super(maxIterations);
+ }
+
+ /**
+ * Creates a new instance of this class, with custom iteration manager.
+ *
+ * @param manager the custom iteration manager
+ * @throws NullArgumentException if {@code manager} is {@code null}
+ */
+ public PreconditionedIterativeLinearSolver(final IterationManager manager)
+ throws NullArgumentException {
+ super(manager);
+ }
+
+ /**
+ * Returns an estimate of the solution to the linear system A &middot; x = b.
+ *
+ * @param a the linear operator A of the system
+ * @param m the preconditioner, M (can be {@code null})
+ * @param b the right-hand side vector
+ * @param x0 the initial guess of the solution
+ * @return a new vector containing the solution
+ * @throws NullArgumentException if one of the parameters is {@code null}
+ * @throws NonSquareOperatorException if {@code a} or {@code m} is not square
+ * @throws DimensionMismatchException if {@code m}, {@code b} or {@code x0} have dimensions
+ * inconsistent with {@code a}
+ * @throws MaxCountExceededException at exhaustion of the iteration count, unless a custom
+ * {@link org.apache.commons.math3.util.Incrementor.MaxCountExceededCallback callback} has
+ * been set at construction of the {@link IterationManager}
+ */
+ public RealVector solve(
+ final RealLinearOperator a,
+ final RealLinearOperator m,
+ final RealVector b,
+ final RealVector x0)
+ throws NullArgumentException,
+ NonSquareOperatorException,
+ DimensionMismatchException,
+ MaxCountExceededException {
+ MathUtils.checkNotNull(x0);
+ return solveInPlace(a, m, b, x0.copy());
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public RealVector solve(final RealLinearOperator a, final RealVector b)
+ throws NullArgumentException,
+ NonSquareOperatorException,
+ DimensionMismatchException,
+ MaxCountExceededException {
+ MathUtils.checkNotNull(a);
+ final RealVector x = new ArrayRealVector(a.getColumnDimension());
+ x.set(0.);
+ return solveInPlace(a, null, b, x);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public RealVector solve(final RealLinearOperator a, final RealVector b, final RealVector x0)
+ throws NullArgumentException,
+ NonSquareOperatorException,
+ DimensionMismatchException,
+ MaxCountExceededException {
+ MathUtils.checkNotNull(x0);
+ return solveInPlace(a, null, b, x0.copy());
+ }
+
+ /**
+ * Performs all dimension checks on the parameters of {@link #solve(RealLinearOperator,
+ * RealLinearOperator, RealVector, RealVector) solve} and {@link
+ * #solveInPlace(RealLinearOperator, RealLinearOperator, RealVector, RealVector) solveInPlace},
+ * and throws an exception if one of the checks fails.
+ *
+ * @param a the linear operator A of the system
+ * @param m the preconditioner, M (can be {@code null})
+ * @param b the right-hand side vector
+ * @param x0 the initial guess of the solution
+ * @throws NullArgumentException if one of the parameters is {@code null}
+ * @throws NonSquareOperatorException if {@code a} or {@code m} is not square
+ * @throws DimensionMismatchException if {@code m}, {@code b} or {@code x0} have dimensions
+ * inconsistent with {@code a}
+ */
+ protected static void checkParameters(
+ final RealLinearOperator a,
+ final RealLinearOperator m,
+ final RealVector b,
+ final RealVector x0)
+ throws NullArgumentException, NonSquareOperatorException, DimensionMismatchException {
+ checkParameters(a, b, x0);
+ if (m != null) {
+ if (m.getColumnDimension() != m.getRowDimension()) {
+ throw new NonSquareOperatorException(m.getColumnDimension(), m.getRowDimension());
+ }
+ if (m.getRowDimension() != a.getRowDimension()) {
+ throw new DimensionMismatchException(m.getRowDimension(), a.getRowDimension());
+ }
+ }
+ }
+
+ /**
+ * Returns an estimate of the solution to the linear system A &middot; x = b.
+ *
+ * @param a the linear operator A of the system
+ * @param m the preconditioner, M (can be {@code null})
+ * @param b the right-hand side vector
+ * @return a new vector containing the solution
+ * @throws NullArgumentException if one of the parameters is {@code null}
+ * @throws NonSquareOperatorException if {@code a} or {@code m} is not square
+ * @throws DimensionMismatchException if {@code m} or {@code b} have dimensions inconsistent
+ * with {@code a}
+ * @throws MaxCountExceededException at exhaustion of the iteration count, unless a custom
+ * {@link org.apache.commons.math3.util.Incrementor.MaxCountExceededCallback callback} has
+ * been set at construction of the {@link IterationManager}
+ */
+ public RealVector solve(RealLinearOperator a, RealLinearOperator m, RealVector b)
+ throws NullArgumentException,
+ NonSquareOperatorException,
+ DimensionMismatchException,
+ MaxCountExceededException {
+ MathUtils.checkNotNull(a);
+ final RealVector x = new ArrayRealVector(a.getColumnDimension());
+ return solveInPlace(a, m, b, x);
+ }
+
+ /**
+ * Returns an estimate of the solution to the linear system A &middot; x = b. The solution is
+ * computed in-place (initial guess is modified).
+ *
+ * @param a the linear operator A of the system
+ * @param m the preconditioner, M (can be {@code null})
+ * @param b the right-hand side vector
+ * @param x0 the initial guess of the solution
+ * @return a reference to {@code x0} (shallow copy) updated with the solution
+ * @throws NullArgumentException if one of the parameters is {@code null}
+ * @throws NonSquareOperatorException if {@code a} or {@code m} is not square
+ * @throws DimensionMismatchException if {@code m}, {@code b} or {@code x0} have dimensions
+ * inconsistent with {@code a}
+ * @throws MaxCountExceededException at exhaustion of the iteration count, unless a custom
+ * {@link org.apache.commons.math3.util.Incrementor.MaxCountExceededCallback callback} has
+ * been set at construction of the {@link IterationManager}
+ */
+ public abstract RealVector solveInPlace(
+ RealLinearOperator a, RealLinearOperator m, RealVector b, RealVector x0)
+ throws NullArgumentException,
+ NonSquareOperatorException,
+ DimensionMismatchException,
+ MaxCountExceededException;
+
+ /** {@inheritDoc} */
+ @Override
+ public RealVector solveInPlace(
+ final RealLinearOperator a, final RealVector b, final RealVector x0)
+ throws NullArgumentException,
+ NonSquareOperatorException,
+ DimensionMismatchException,
+ MaxCountExceededException {
+ return solveInPlace(a, null, b, x0);
+ }
+}
diff --git a/src/main/java/org/apache/commons/math3/linear/QRDecomposition.java b/src/main/java/org/apache/commons/math3/linear/QRDecomposition.java
new file mode 100644
index 0000000..17092c6
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/linear/QRDecomposition.java
@@ -0,0 +1,492 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math3.linear;
+
+import org.apache.commons.math3.exception.DimensionMismatchException;
+import org.apache.commons.math3.util.FastMath;
+
+import java.util.Arrays;
+
+/**
+ * Calculates the QR-decomposition of a matrix.
+ *
+ * <p>The QR-decomposition of a matrix A consists of two matrices Q and R that satisfy: A = QR, Q is
+ * orthogonal (Q<sup>T</sup>Q = I), and R is upper triangular. If A is m&times;n, Q is m&times;m and
+ * R m&times;n.
+ *
+ * <p>This class compute the decomposition using Householder reflectors.
+ *
+ * <p>For efficiency purposes, the decomposition in packed form is transposed. This allows inner
+ * loop to iterate inside rows, which is much more cache-efficient in Java.
+ *
+ * <p>This class is based on the class with similar name from the <a
+ * href="http://math.nist.gov/javanumerics/jama/">JAMA</a> library, with the following changes:
+ *
+ * <ul>
+ * <li>a {@link #getQT() getQT} method has been added,
+ * <li>the {@code solve} and {@code isFullRank} methods have been replaced by a {@link
+ * #getSolver() getSolver} method and the equivalent methods provided by the returned {@link
+ * DecompositionSolver}.
+ * </ul>
+ *
+ * @see <a href="http://mathworld.wolfram.com/QRDecomposition.html">MathWorld</a>
+ * @see <a href="http://en.wikipedia.org/wiki/QR_decomposition">Wikipedia</a>
+ * @since 1.2 (changed to concrete class in 3.0)
+ */
+public class QRDecomposition {
+ /**
+ * A packed TRANSPOSED representation of the QR decomposition.
+ *
+ * <p>The elements BELOW the diagonal are the elements of the UPPER triangular matrix R, and the
+ * rows ABOVE the diagonal are the Householder reflector vectors from which an explicit form of
+ * Q can be recomputed if desired.
+ */
+ private double[][] qrt;
+
+ /** The diagonal elements of R. */
+ private double[] rDiag;
+
+ /** Cached value of Q. */
+ private RealMatrix cachedQ;
+
+ /** Cached value of QT. */
+ private RealMatrix cachedQT;
+
+ /** Cached value of R. */
+ private RealMatrix cachedR;
+
+ /** Cached value of H. */
+ private RealMatrix cachedH;
+
+ /** Singularity threshold. */
+ private final double threshold;
+
+ /**
+ * Calculates the QR-decomposition of the given matrix. The singularity threshold defaults to
+ * zero.
+ *
+ * @param matrix The matrix to decompose.
+ * @see #QRDecomposition(RealMatrix,double)
+ */
+ public QRDecomposition(RealMatrix matrix) {
+ this(matrix, 0d);
+ }
+
+ /**
+ * Calculates the QR-decomposition of the given matrix.
+ *
+ * @param matrix The matrix to decompose.
+ * @param threshold Singularity threshold.
+ */
+ public QRDecomposition(RealMatrix matrix, double threshold) {
+ this.threshold = threshold;
+
+ final int m = matrix.getRowDimension();
+ final int n = matrix.getColumnDimension();
+ qrt = matrix.transpose().getData();
+ rDiag = new double[FastMath.min(m, n)];
+ cachedQ = null;
+ cachedQT = null;
+ cachedR = null;
+ cachedH = null;
+
+ decompose(qrt);
+ }
+
+ /**
+ * Decompose matrix.
+ *
+ * @param matrix transposed matrix
+ * @since 3.2
+ */
+ protected void decompose(double[][] matrix) {
+ for (int minor = 0; minor < FastMath.min(matrix.length, matrix[0].length); minor++) {
+ performHouseholderReflection(minor, matrix);
+ }
+ }
+
+ /**
+ * Perform Householder reflection for a minor A(minor, minor) of A.
+ *
+ * @param minor minor index
+ * @param matrix transposed matrix
+ * @since 3.2
+ */
+ protected void performHouseholderReflection(int minor, double[][] matrix) {
+
+ final double[] qrtMinor = matrix[minor];
+
+ /*
+ * Let x be the first column of the minor, and a^2 = |x|^2.
+ * x will be in the positions qr[minor][minor] through qr[m][minor].
+ * The first column of the transformed minor will be (a,0,0,..)'
+ * The sign of a is chosen to be opposite to the sign of the first
+ * component of x. Let's find a:
+ */
+ double xNormSqr = 0;
+ for (int row = minor; row < qrtMinor.length; row++) {
+ final double c = qrtMinor[row];
+ xNormSqr += c * c;
+ }
+ final double a = (qrtMinor[minor] > 0) ? -FastMath.sqrt(xNormSqr) : FastMath.sqrt(xNormSqr);
+ rDiag[minor] = a;
+
+ if (a != 0.0) {
+
+ /*
+ * Calculate the normalized reflection vector v and transform
+ * the first column. We know the norm of v beforehand: v = x-ae
+ * so |v|^2 = <x-ae,x-ae> = <x,x>-2a<x,e>+a^2<e,e> =
+ * a^2+a^2-2a<x,e> = 2a*(a - <x,e>).
+ * Here <x, e> is now qr[minor][minor].
+ * v = x-ae is stored in the column at qr:
+ */
+ qrtMinor[minor] -= a; // now |v|^2 = -2a*(qr[minor][minor])
+
+ /*
+ * Transform the rest of the columns of the minor:
+ * They will be transformed by the matrix H = I-2vv'/|v|^2.
+ * If x is a column vector of the minor, then
+ * Hx = (I-2vv'/|v|^2)x = x-2vv'x/|v|^2 = x - 2<x,v>/|v|^2 v.
+ * Therefore the transformation is easily calculated by
+ * subtracting the column vector (2<x,v>/|v|^2)v from x.
+ *
+ * Let 2<x,v>/|v|^2 = alpha. From above we have
+ * |v|^2 = -2a*(qr[minor][minor]), so
+ * alpha = -<x,v>/(a*qr[minor][minor])
+ */
+ for (int col = minor + 1; col < matrix.length; col++) {
+ final double[] qrtCol = matrix[col];
+ double alpha = 0;
+ for (int row = minor; row < qrtCol.length; row++) {
+ alpha -= qrtCol[row] * qrtMinor[row];
+ }
+ alpha /= a * qrtMinor[minor];
+
+ // Subtract the column vector alpha*v from x.
+ for (int row = minor; row < qrtCol.length; row++) {
+ qrtCol[row] -= alpha * qrtMinor[row];
+ }
+ }
+ }
+ }
+
+ /**
+ * Returns the matrix R of the decomposition.
+ *
+ * <p>R is an upper-triangular matrix
+ *
+ * @return the R matrix
+ */
+ public RealMatrix getR() {
+
+ if (cachedR == null) {
+
+ // R is supposed to be m x n
+ final int n = qrt.length;
+ final int m = qrt[0].length;
+ double[][] ra = new double[m][n];
+ // copy the diagonal from rDiag and the upper triangle of qr
+ for (int row = FastMath.min(m, n) - 1; row >= 0; row--) {
+ ra[row][row] = rDiag[row];
+ for (int col = row + 1; col < n; col++) {
+ ra[row][col] = qrt[col][row];
+ }
+ }
+ cachedR = MatrixUtils.createRealMatrix(ra);
+ }
+
+ // return the cached matrix
+ return cachedR;
+ }
+
+ /**
+ * Returns the matrix Q of the decomposition.
+ *
+ * <p>Q is an orthogonal matrix
+ *
+ * @return the Q matrix
+ */
+ public RealMatrix getQ() {
+ if (cachedQ == null) {
+ cachedQ = getQT().transpose();
+ }
+ return cachedQ;
+ }
+
+ /**
+ * Returns the transpose of the matrix Q of the decomposition.
+ *
+ * <p>Q is an orthogonal matrix
+ *
+ * @return the transpose of the Q matrix, Q<sup>T</sup>
+ */
+ public RealMatrix getQT() {
+ if (cachedQT == null) {
+
+ // QT is supposed to be m x m
+ final int n = qrt.length;
+ final int m = qrt[0].length;
+ double[][] qta = new double[m][m];
+
+ /*
+ * Q = Q1 Q2 ... Q_m, so Q is formed by first constructing Q_m and then
+ * applying the Householder transformations Q_(m-1),Q_(m-2),...,Q1 in
+ * succession to the result
+ */
+ for (int minor = m - 1; minor >= FastMath.min(m, n); minor--) {
+ qta[minor][minor] = 1.0d;
+ }
+
+ for (int minor = FastMath.min(m, n) - 1; minor >= 0; minor--) {
+ final double[] qrtMinor = qrt[minor];
+ qta[minor][minor] = 1.0d;
+ if (qrtMinor[minor] != 0.0) {
+ for (int col = minor; col < m; col++) {
+ double alpha = 0;
+ for (int row = minor; row < m; row++) {
+ alpha -= qta[col][row] * qrtMinor[row];
+ }
+ alpha /= rDiag[minor] * qrtMinor[minor];
+
+ for (int row = minor; row < m; row++) {
+ qta[col][row] += -alpha * qrtMinor[row];
+ }
+ }
+ }
+ }
+ cachedQT = MatrixUtils.createRealMatrix(qta);
+ }
+
+ // return the cached matrix
+ return cachedQT;
+ }
+
+ /**
+ * Returns the Householder reflector vectors.
+ *
+ * <p>H is a lower trapezoidal matrix whose columns represent each successive Householder
+ * reflector vector. This matrix is used to compute Q.
+ *
+ * @return a matrix containing the Householder reflector vectors
+ */
+ public RealMatrix getH() {
+ if (cachedH == null) {
+
+ final int n = qrt.length;
+ final int m = qrt[0].length;
+ double[][] ha = new double[m][n];
+ for (int i = 0; i < m; ++i) {
+ for (int j = 0; j < FastMath.min(i + 1, n); ++j) {
+ ha[i][j] = qrt[j][i] / -rDiag[j];
+ }
+ }
+ cachedH = MatrixUtils.createRealMatrix(ha);
+ }
+
+ // return the cached matrix
+ return cachedH;
+ }
+
+ /**
+ * Get a solver for finding the A &times; X = B solution in least square sense.
+ *
+ * <p>Least Square sense means a solver can be computed for an overdetermined system, (i.e. a
+ * system with more equations than unknowns, which corresponds to a tall A matrix with more rows
+ * than columns). In any case, if the matrix is singular within the tolerance set at {@link
+ * QRDecomposition#QRDecomposition(RealMatrix, double) construction}, an error will be triggered
+ * when the {@link DecompositionSolver#solve(RealVector) solve} method will be called.
+ *
+ * @return a solver
+ */
+ public DecompositionSolver getSolver() {
+ return new Solver(qrt, rDiag, threshold);
+ }
+
+ /** Specialized solver. */
+ private static class Solver implements DecompositionSolver {
+ /**
+ * A packed TRANSPOSED representation of the QR decomposition.
+ *
+ * <p>The elements BELOW the diagonal are the elements of the UPPER triangular matrix R, and
+ * the rows ABOVE the diagonal are the Householder reflector vectors from which an explicit
+ * form of Q can be recomputed if desired.
+ */
+ private final double[][] qrt;
+
+ /** The diagonal elements of R. */
+ private final double[] rDiag;
+
+ /** Singularity threshold. */
+ private final double threshold;
+
+ /**
+ * Build a solver from decomposed matrix.
+ *
+ * @param qrt Packed TRANSPOSED representation of the QR decomposition.
+ * @param rDiag Diagonal elements of R.
+ * @param threshold Singularity threshold.
+ */
+ private Solver(final double[][] qrt, final double[] rDiag, final double threshold) {
+ this.qrt = qrt;
+ this.rDiag = rDiag;
+ this.threshold = threshold;
+ }
+
+ /** {@inheritDoc} */
+ public boolean isNonSingular() {
+ for (double diag : rDiag) {
+ if (FastMath.abs(diag) <= threshold) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ /** {@inheritDoc} */
+ public RealVector solve(RealVector b) {
+ final int n = qrt.length;
+ final int m = qrt[0].length;
+ if (b.getDimension() != m) {
+ throw new DimensionMismatchException(b.getDimension(), m);
+ }
+ if (!isNonSingular()) {
+ throw new SingularMatrixException();
+ }
+
+ final double[] x = new double[n];
+ final double[] y = b.toArray();
+
+ // apply Householder transforms to solve Q.y = b
+ for (int minor = 0; minor < FastMath.min(m, n); minor++) {
+
+ final double[] qrtMinor = qrt[minor];
+ double dotProduct = 0;
+ for (int row = minor; row < m; row++) {
+ dotProduct += y[row] * qrtMinor[row];
+ }
+ dotProduct /= rDiag[minor] * qrtMinor[minor];
+
+ for (int row = minor; row < m; row++) {
+ y[row] += dotProduct * qrtMinor[row];
+ }
+ }
+
+ // solve triangular system R.x = y
+ for (int row = rDiag.length - 1; row >= 0; --row) {
+ y[row] /= rDiag[row];
+ final double yRow = y[row];
+ final double[] qrtRow = qrt[row];
+ x[row] = yRow;
+ for (int i = 0; i < row; i++) {
+ y[i] -= yRow * qrtRow[i];
+ }
+ }
+
+ return new ArrayRealVector(x, false);
+ }
+
+ /** {@inheritDoc} */
+ public RealMatrix solve(RealMatrix b) {
+ final int n = qrt.length;
+ final int m = qrt[0].length;
+ if (b.getRowDimension() != m) {
+ throw new DimensionMismatchException(b.getRowDimension(), m);
+ }
+ if (!isNonSingular()) {
+ throw new SingularMatrixException();
+ }
+
+ final int columns = b.getColumnDimension();
+ final int blockSize = BlockRealMatrix.BLOCK_SIZE;
+ final int cBlocks = (columns + blockSize - 1) / blockSize;
+ final double[][] xBlocks = BlockRealMatrix.createBlocksLayout(n, columns);
+ final double[][] y = new double[b.getRowDimension()][blockSize];
+ final double[] alpha = new double[blockSize];
+
+ for (int kBlock = 0; kBlock < cBlocks; ++kBlock) {
+ final int kStart = kBlock * blockSize;
+ final int kEnd = FastMath.min(kStart + blockSize, columns);
+ final int kWidth = kEnd - kStart;
+
+ // get the right hand side vector
+ b.copySubMatrix(0, m - 1, kStart, kEnd - 1, y);
+
+ // apply Householder transforms to solve Q.y = b
+ for (int minor = 0; minor < FastMath.min(m, n); minor++) {
+ final double[] qrtMinor = qrt[minor];
+ final double factor = 1.0 / (rDiag[minor] * qrtMinor[minor]);
+
+ Arrays.fill(alpha, 0, kWidth, 0.0);
+ for (int row = minor; row < m; ++row) {
+ final double d = qrtMinor[row];
+ final double[] yRow = y[row];
+ for (int k = 0; k < kWidth; ++k) {
+ alpha[k] += d * yRow[k];
+ }
+ }
+ for (int k = 0; k < kWidth; ++k) {
+ alpha[k] *= factor;
+ }
+
+ for (int row = minor; row < m; ++row) {
+ final double d = qrtMinor[row];
+ final double[] yRow = y[row];
+ for (int k = 0; k < kWidth; ++k) {
+ yRow[k] += alpha[k] * d;
+ }
+ }
+ }
+
+ // solve triangular system R.x = y
+ for (int j = rDiag.length - 1; j >= 0; --j) {
+ final int jBlock = j / blockSize;
+ final int jStart = jBlock * blockSize;
+ final double factor = 1.0 / rDiag[j];
+ final double[] yJ = y[j];
+ final double[] xBlock = xBlocks[jBlock * cBlocks + kBlock];
+ int index = (j - jStart) * kWidth;
+ for (int k = 0; k < kWidth; ++k) {
+ yJ[k] *= factor;
+ xBlock[index++] = yJ[k];
+ }
+
+ final double[] qrtJ = qrt[j];
+ for (int i = 0; i < j; ++i) {
+ final double rIJ = qrtJ[i];
+ final double[] yI = y[i];
+ for (int k = 0; k < kWidth; ++k) {
+ yI[k] -= yJ[k] * rIJ;
+ }
+ }
+ }
+ }
+
+ return new BlockRealMatrix(n, columns, xBlocks, false);
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @throws SingularMatrixException if the decomposed matrix is singular.
+ */
+ public RealMatrix getInverse() {
+ return solve(MatrixUtils.createRealIdentityMatrix(qrt[0].length));
+ }
+ }
+}
diff --git a/src/main/java/org/apache/commons/math3/linear/RRQRDecomposition.java b/src/main/java/org/apache/commons/math3/linear/RRQRDecomposition.java
new file mode 100644
index 0000000..56c1836
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/linear/RRQRDecomposition.java
@@ -0,0 +1,244 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math3.linear;
+
+import org.apache.commons.math3.util.FastMath;
+
+/**
+ * Calculates the rank-revealing QR-decomposition of a matrix, with column pivoting.
+ *
+ * <p>The rank-revealing QR-decomposition of a matrix A consists of three matrices Q, R and P such
+ * that AP=QR. Q is orthogonal (Q<sup>T</sup>Q = I), and R is upper triangular. If A is m&times;n, Q
+ * is m&times;m and R is m&times;n and P is n&times;n.
+ *
+ * <p>QR decomposition with column pivoting produces a rank-revealing QR decomposition and the
+ * {@link #getRank(double)} method may be used to return the rank of the input matrix A.
+ *
+ * <p>This class compute the decomposition using Householder reflectors.
+ *
+ * <p>For efficiency purposes, the decomposition in packed form is transposed. This allows inner
+ * loop to iterate inside rows, which is much more cache-efficient in Java.
+ *
+ * <p>This class is based on the class with similar name from the <a
+ * href="http://math.nist.gov/javanumerics/jama/">JAMA</a> library, with the following changes:
+ *
+ * <ul>
+ * <li>a {@link #getQT() getQT} method has been added,
+ * <li>the {@code solve} and {@code isFullRank} methods have been replaced by a {@link
+ * #getSolver() getSolver} method and the equivalent methods provided by the returned {@link
+ * DecompositionSolver}.
+ * </ul>
+ *
+ * @see <a href="http://mathworld.wolfram.com/QRDecomposition.html">MathWorld</a>
+ * @see <a href="http://en.wikipedia.org/wiki/QR_decomposition">Wikipedia</a>
+ * @since 3.2
+ */
+public class RRQRDecomposition extends QRDecomposition {
+
+ /** An array to record the column pivoting for later creation of P. */
+ private int[] p;
+
+ /** Cached value of P. */
+ private RealMatrix cachedP;
+
+ /**
+ * Calculates the QR-decomposition of the given matrix. The singularity threshold defaults to
+ * zero.
+ *
+ * @param matrix The matrix to decompose.
+ * @see #RRQRDecomposition(RealMatrix, double)
+ */
+ public RRQRDecomposition(RealMatrix matrix) {
+ this(matrix, 0d);
+ }
+
+ /**
+ * Calculates the QR-decomposition of the given matrix.
+ *
+ * @param matrix The matrix to decompose.
+ * @param threshold Singularity threshold.
+ * @see #RRQRDecomposition(RealMatrix)
+ */
+ public RRQRDecomposition(RealMatrix matrix, double threshold) {
+ super(matrix, threshold);
+ }
+
+ /**
+ * Decompose matrix.
+ *
+ * @param qrt transposed matrix
+ */
+ @Override
+ protected void decompose(double[][] qrt) {
+ p = new int[qrt.length];
+ for (int i = 0; i < p.length; i++) {
+ p[i] = i;
+ }
+ super.decompose(qrt);
+ }
+
+ /**
+ * Perform Householder reflection for a minor A(minor, minor) of A.
+ *
+ * @param minor minor index
+ * @param qrt transposed matrix
+ */
+ @Override
+ protected void performHouseholderReflection(int minor, double[][] qrt) {
+
+ double l2NormSquaredMax = 0;
+ // Find the unreduced column with the greatest L2-Norm
+ int l2NormSquaredMaxIndex = minor;
+ for (int i = minor; i < qrt.length; i++) {
+ double l2NormSquared = 0;
+ for (int j = 0; j < qrt[i].length; j++) {
+ l2NormSquared += qrt[i][j] * qrt[i][j];
+ }
+ if (l2NormSquared > l2NormSquaredMax) {
+ l2NormSquaredMax = l2NormSquared;
+ l2NormSquaredMaxIndex = i;
+ }
+ }
+ // swap the current column with that with the greated L2-Norm and record in p
+ if (l2NormSquaredMaxIndex != minor) {
+ double[] tmp1 = qrt[minor];
+ qrt[minor] = qrt[l2NormSquaredMaxIndex];
+ qrt[l2NormSquaredMaxIndex] = tmp1;
+ int tmp2 = p[minor];
+ p[minor] = p[l2NormSquaredMaxIndex];
+ p[l2NormSquaredMaxIndex] = tmp2;
+ }
+
+ super.performHouseholderReflection(minor, qrt);
+ }
+
+ /**
+ * Returns the pivot matrix, P, used in the QR Decomposition of matrix A such that AP = QR.
+ *
+ * <p>If no pivoting is used in this decomposition then P is equal to the identity matrix.
+ *
+ * @return a permutation matrix.
+ */
+ public RealMatrix getP() {
+ if (cachedP == null) {
+ int n = p.length;
+ cachedP = MatrixUtils.createRealMatrix(n, n);
+ for (int i = 0; i < n; i++) {
+ cachedP.setEntry(p[i], i, 1);
+ }
+ }
+ return cachedP;
+ }
+
+ /**
+ * Return the effective numerical matrix rank.
+ *
+ * <p>The effective numerical rank is the number of non-negligible singular values.
+ *
+ * <p>This implementation looks at Frobenius norms of the sequence of bottom right submatrices.
+ * When a large fall in norm is seen, the rank is returned. The drop is computed as:
+ *
+ * <pre>
+ * (thisNorm/lastNorm) * rNorm < dropThreshold
+ * </pre>
+ *
+ * <p>where thisNorm is the Frobenius norm of the current submatrix, lastNorm is the Frobenius
+ * norm of the previous submatrix, rNorm is is the Frobenius norm of the complete matrix
+ *
+ * @param dropThreshold threshold triggering rank computation
+ * @return effective numerical matrix rank
+ */
+ public int getRank(final double dropThreshold) {
+ RealMatrix r = getR();
+ int rows = r.getRowDimension();
+ int columns = r.getColumnDimension();
+ int rank = 1;
+ double lastNorm = r.getFrobeniusNorm();
+ double rNorm = lastNorm;
+ while (rank < FastMath.min(rows, columns)) {
+ double thisNorm = r.getSubMatrix(rank, rows - 1, rank, columns - 1).getFrobeniusNorm();
+ if (thisNorm == 0 || (thisNorm / lastNorm) * rNorm < dropThreshold) {
+ break;
+ }
+ lastNorm = thisNorm;
+ rank++;
+ }
+ return rank;
+ }
+
+ /**
+ * Get a solver for finding the A &times; X = B solution in least square sense.
+ *
+ * <p>Least Square sense means a solver can be computed for an overdetermined system, (i.e. a
+ * system with more equations than unknowns, which corresponds to a tall A matrix with more rows
+ * than columns). In any case, if the matrix is singular within the tolerance set at {@link
+ * RRQRDecomposition#RRQRDecomposition(RealMatrix, double) construction}, an error will be
+ * triggered when the {@link DecompositionSolver#solve(RealVector) solve} method will be called.
+ *
+ * @return a solver
+ */
+ @Override
+ public DecompositionSolver getSolver() {
+ return new Solver(super.getSolver(), this.getP());
+ }
+
+ /** Specialized solver. */
+ private static class Solver implements DecompositionSolver {
+
+ /** Upper level solver. */
+ private final DecompositionSolver upper;
+
+ /** A permutation matrix for the pivots used in the QR decomposition */
+ private RealMatrix p;
+
+ /**
+ * Build a solver from decomposed matrix.
+ *
+ * @param upper upper level solver.
+ * @param p permutation matrix
+ */
+ private Solver(final DecompositionSolver upper, final RealMatrix p) {
+ this.upper = upper;
+ this.p = p;
+ }
+
+ /** {@inheritDoc} */
+ public boolean isNonSingular() {
+ return upper.isNonSingular();
+ }
+
+ /** {@inheritDoc} */
+ public RealVector solve(RealVector b) {
+ return p.operate(upper.solve(b));
+ }
+
+ /** {@inheritDoc} */
+ public RealMatrix solve(RealMatrix b) {
+ return p.multiply(upper.solve(b));
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @throws SingularMatrixException if the decomposed matrix is singular.
+ */
+ public RealMatrix getInverse() {
+ return solve(MatrixUtils.createRealIdentityMatrix(p.getRowDimension()));
+ }
+ }
+}
diff --git a/src/main/java/org/apache/commons/math3/linear/RealLinearOperator.java b/src/main/java/org/apache/commons/math3/linear/RealLinearOperator.java
new file mode 100644
index 0000000..0a86568
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/linear/RealLinearOperator.java
@@ -0,0 +1,103 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math3.linear;
+
+import org.apache.commons.math3.exception.DimensionMismatchException;
+
+/**
+ * This class defines a linear operator operating on real ({@code double}) vector spaces. No direct
+ * access to the coefficients of the underlying matrix is provided.
+ *
+ * <p>The motivation for such an interface is well stated by <a href="#BARR1994">Barrett et al.
+ * (1994)</a>:
+ *
+ * <blockquote>
+ *
+ * We restrict ourselves to iterative methods, which work by repeatedly improving an approximate
+ * solution until it is accurate enough. These methods access the coefficient matrix A of the linear
+ * system only via the matrix-vector product y = A &middot; x (and perhaps z = A<sup>T</sup>
+ * &middot; x). Thus the user need only supply a subroutine for computing y (and perhaps z) given x,
+ * which permits full exploitation of the sparsity or other special structure of A.
+ *
+ * </blockquote>
+ *
+ * <br>
+ *
+ * <dl>
+ * <dt><a name="BARR1994">Barret et al. (1994)</a>
+ * <dd>R. Barrett, M. Berry, T. F. Chan, J. Demmel, J. M. Donato, J. Dongarra, V. Eijkhout, R.
+ * Pozo, C. Romine and H. Van der Vorst, <em>Templates for the Solution of Linear Systems:
+ * Building Blocks for Iterative Methods</em>, SIAM
+ * </dl>
+ *
+ * @since 3.0
+ */
+public abstract class RealLinearOperator {
+ /**
+ * Returns the dimension of the codomain of this operator.
+ *
+ * @return the number of rows of the underlying matrix
+ */
+ public abstract int getRowDimension();
+
+ /**
+ * Returns the dimension of the domain of this operator.
+ *
+ * @return the number of columns of the underlying matrix
+ */
+ public abstract int getColumnDimension();
+
+ /**
+ * Returns the result of multiplying {@code this} by the vector {@code x}.
+ *
+ * @param x the vector to operate on
+ * @return the product of {@code this} instance with {@code x}
+ * @throws DimensionMismatchException if the column dimension does not match the size of {@code
+ * x}
+ */
+ public abstract RealVector operate(final RealVector x) throws DimensionMismatchException;
+
+ /**
+ * Returns the result of multiplying the transpose of {@code this} operator by the vector {@code
+ * x} (optional operation). The default implementation throws an {@link
+ * UnsupportedOperationException}. Users overriding this method must also override {@link
+ * #isTransposable()}.
+ *
+ * @param x the vector to operate on
+ * @return the product of the transpose of {@code this} instance with {@code x}
+ * @throws org.apache.commons.math3.exception.DimensionMismatchException if the row dimension
+ * does not match the size of {@code x}
+ * @throws UnsupportedOperationException if this operation is not supported by {@code this}
+ * operator
+ */
+ public RealVector operateTranspose(final RealVector x)
+ throws DimensionMismatchException, UnsupportedOperationException {
+ throw new UnsupportedOperationException();
+ }
+
+ /**
+ * Returns {@code true} if this operator supports {@link #operateTranspose(RealVector)}. If
+ * {@code true} is returned, {@link #operateTranspose(RealVector)} should not throw {@code
+ * UnsupportedOperationException}. The default implementation returns {@code false}.
+ *
+ * @return {@code false}
+ */
+ public boolean isTransposable() {
+ return false;
+ }
+}
diff --git a/src/main/java/org/apache/commons/math3/linear/RealMatrix.java b/src/main/java/org/apache/commons/math3/linear/RealMatrix.java
new file mode 100644
index 0000000..183a883
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/linear/RealMatrix.java
@@ -0,0 +1,827 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math3.linear;
+
+import org.apache.commons.math3.exception.DimensionMismatchException;
+import org.apache.commons.math3.exception.NoDataException;
+import org.apache.commons.math3.exception.NotPositiveException;
+import org.apache.commons.math3.exception.NotStrictlyPositiveException;
+import org.apache.commons.math3.exception.NullArgumentException;
+import org.apache.commons.math3.exception.NumberIsTooSmallException;
+import org.apache.commons.math3.exception.OutOfRangeException;
+
+/**
+ * Interface defining a real-valued matrix with basic algebraic operations.
+ *
+ * <p>Matrix element indexing is 0-based -- e.g., <code>getEntry(0, 0)</code> returns the element in
+ * the first row, first column of the matrix.
+ */
+public interface RealMatrix extends AnyMatrix {
+
+ /**
+ * Create a new RealMatrix of the same type as the instance with the supplied row and column
+ * dimensions.
+ *
+ * @param rowDimension the number of rows in the new matrix
+ * @param columnDimension the number of columns in the new matrix
+ * @return a new matrix of the same type as the instance
+ * @throws NotStrictlyPositiveException if row or column dimension is not positive.
+ * @since 2.0
+ */
+ RealMatrix createMatrix(int rowDimension, int columnDimension)
+ throws NotStrictlyPositiveException;
+
+ /**
+ * Returns a (deep) copy of this.
+ *
+ * @return matrix copy
+ */
+ RealMatrix copy();
+
+ /**
+ * Returns the sum of {@code this} and {@code m}.
+ *
+ * @param m matrix to be added
+ * @return {@code this + m}
+ * @throws MatrixDimensionMismatchException if {@code m} is not the same size as {@code this}.
+ */
+ RealMatrix add(RealMatrix m) throws MatrixDimensionMismatchException;
+
+ /**
+ * Returns {@code this} minus {@code m}.
+ *
+ * @param m matrix to be subtracted
+ * @return {@code this - m}
+ * @throws MatrixDimensionMismatchException if {@code m} is not the same size as {@code this}.
+ */
+ RealMatrix subtract(RealMatrix m) throws MatrixDimensionMismatchException;
+
+ /**
+ * Returns the result of adding {@code d} to each entry of {@code this}.
+ *
+ * @param d value to be added to each entry
+ * @return {@code d + this}
+ */
+ RealMatrix scalarAdd(double d);
+
+ /**
+ * Returns the result of multiplying each entry of {@code this} by {@code d}.
+ *
+ * @param d value to multiply all entries by
+ * @return {@code d * this}
+ */
+ RealMatrix scalarMultiply(double d);
+
+ /**
+ * Returns the result of postmultiplying {@code this} by {@code m}.
+ *
+ * @param m matrix to postmultiply by
+ * @return {@code this * m}
+ * @throws DimensionMismatchException if {@code columnDimension(this) != rowDimension(m)}
+ */
+ RealMatrix multiply(RealMatrix m) throws DimensionMismatchException;
+
+ /**
+ * Returns the result of premultiplying {@code this} by {@code m}.
+ *
+ * @param m matrix to premultiply by
+ * @return {@code m * this}
+ * @throws DimensionMismatchException if {@code rowDimension(this) != columnDimension(m)}
+ */
+ RealMatrix preMultiply(RealMatrix m) throws DimensionMismatchException;
+
+ /**
+ * Returns the result of multiplying {@code this} with itself {@code p} times. Depending on the
+ * underlying storage, instability for high powers might occur.
+ *
+ * @param p raise {@code this} to power {@code p}
+ * @return {@code this^p}
+ * @throws NotPositiveException if {@code p < 0}
+ * @throws NonSquareMatrixException if the matrix is not square
+ */
+ RealMatrix power(final int p) throws NotPositiveException, NonSquareMatrixException;
+
+ /**
+ * Returns matrix entries as a two-dimensional array.
+ *
+ * @return 2-dimensional array of entries
+ */
+ double[][] getData();
+
+ /**
+ * Returns the <a href="http://mathworld.wolfram.com/MaximumAbsoluteRowSumNorm.html">maximum
+ * absolute row sum norm</a> of the matrix.
+ *
+ * @return norm
+ */
+ double getNorm();
+
+ /**
+ * Returns the <a href="http://mathworld.wolfram.com/FrobeniusNorm.html">Frobenius norm</a> of
+ * the matrix.
+ *
+ * @return norm
+ */
+ double getFrobeniusNorm();
+
+ /**
+ * Gets a submatrix. Rows and columns are indicated counting from 0 to n-1.
+ *
+ * @param startRow Initial row index
+ * @param endRow Final row index (inclusive)
+ * @param startColumn Initial column index
+ * @param endColumn Final column index (inclusive)
+ * @return The subMatrix containing the data of the specified rows and columns.
+ * @throws OutOfRangeException if the indices are not valid.
+ * @throws NumberIsTooSmallException if {@code endRow < startRow} or {@code endColumn <
+ * startColumn}.
+ */
+ RealMatrix getSubMatrix(int startRow, int endRow, int startColumn, int endColumn)
+ throws OutOfRangeException, NumberIsTooSmallException;
+
+ /**
+ * Gets a submatrix. Rows and columns are indicated counting from 0 to n-1.
+ *
+ * @param selectedRows Array of row indices.
+ * @param selectedColumns Array of column indices.
+ * @return The subMatrix containing the data in the specified rows and columns
+ * @throws NullArgumentException if the row or column selections are {@code null}
+ * @throws NoDataException if the row or column selections are empty (zero length).
+ * @throws OutOfRangeException if the indices are not valid.
+ */
+ RealMatrix getSubMatrix(int[] selectedRows, int[] selectedColumns)
+ throws NullArgumentException, NoDataException, OutOfRangeException;
+
+ /**
+ * Copy a submatrix. Rows and columns are indicated counting from 0 to n-1.
+ *
+ * @param startRow Initial row index
+ * @param endRow Final row index (inclusive)
+ * @param startColumn Initial column index
+ * @param endColumn Final column index (inclusive)
+ * @param destination The arrays where the submatrix data should be copied (if larger than
+ * rows/columns counts, only the upper-left part will be used)
+ * @throws OutOfRangeException if the indices are not valid.
+ * @throws NumberIsTooSmallException if {@code endRow < startRow} or {@code endColumn <
+ * startColumn}.
+ * @throws MatrixDimensionMismatchException if the destination array is too small.
+ */
+ void copySubMatrix(
+ int startRow, int endRow, int startColumn, int endColumn, double[][] destination)
+ throws OutOfRangeException, NumberIsTooSmallException, MatrixDimensionMismatchException;
+
+ /**
+ * Copy a submatrix. Rows and columns are indicated counting from 0 to n-1.
+ *
+ * @param selectedRows Array of row indices.
+ * @param selectedColumns Array of column indices.
+ * @param destination The arrays where the submatrix data should be copied (if larger than
+ * rows/columns counts, only the upper-left part will be used)
+ * @throws NullArgumentException if the row or column selections are {@code null}
+ * @throws NoDataException if the row or column selections are empty (zero length).
+ * @throws OutOfRangeException if the indices are not valid.
+ * @throws MatrixDimensionMismatchException if the destination array is too small.
+ */
+ void copySubMatrix(int[] selectedRows, int[] selectedColumns, double[][] destination)
+ throws OutOfRangeException,
+ NullArgumentException,
+ NoDataException,
+ MatrixDimensionMismatchException;
+
+ /**
+ * Replace the submatrix starting at {@code row, column} using data in the input {@code
+ * subMatrix} array. Indexes are 0-based.
+ *
+ * <p>Example:<br>
+ * Starting with
+ *
+ * <pre>
+ * 1 2 3 4
+ * 5 6 7 8
+ * 9 0 1 2
+ * </pre>
+ *
+ * and <code>subMatrix = {{3, 4} {5,6}}</code>, invoking {@code setSubMatrix(subMatrix,1,1))}
+ * will result in
+ *
+ * <pre>
+ * 1 2 3 4
+ * 5 3 4 8
+ * 9 5 6 2
+ * </pre>
+ *
+ * @param subMatrix array containing the submatrix replacement data
+ * @param row row coordinate of the top, left element to be replaced
+ * @param column column coordinate of the top, left element to be replaced
+ * @throws NoDataException if {@code subMatrix} is empty.
+ * @throws OutOfRangeException if {@code subMatrix} does not fit into this matrix from element
+ * in {@code (row, column)}.
+ * @throws DimensionMismatchException if {@code subMatrix} is not rectangular (not all rows have
+ * the same length) or empty.
+ * @throws NullArgumentException if {@code subMatrix} is {@code null}.
+ * @since 2.0
+ */
+ void setSubMatrix(double[][] subMatrix, int row, int column)
+ throws NoDataException,
+ OutOfRangeException,
+ DimensionMismatchException,
+ NullArgumentException;
+
+ /**
+ * Get the entries at the given row index as a row matrix. Row indices start at 0.
+ *
+ * @param row Row to be fetched.
+ * @return row Matrix.
+ * @throws OutOfRangeException if the specified row index is invalid.
+ */
+ RealMatrix getRowMatrix(int row) throws OutOfRangeException;
+
+ /**
+ * Sets the specified {@code row} of {@code this} matrix to the entries of the specified row
+ * {@code matrix}. Row indices start at 0.
+ *
+ * @param row Row to be set.
+ * @param matrix Row matrix to be copied (must have one row and the same number of columns as
+ * the instance).
+ * @throws OutOfRangeException if the specified row index is invalid.
+ * @throws MatrixDimensionMismatchException if the row dimension of the {@code matrix} is not
+ * {@code 1}, or the column dimensions of {@code this} and {@code matrix} do not match.
+ */
+ void setRowMatrix(int row, RealMatrix matrix)
+ throws OutOfRangeException, MatrixDimensionMismatchException;
+
+ /**
+ * Get the entries at the given column index as a column matrix. Column indices start at 0.
+ *
+ * @param column Column to be fetched.
+ * @return column Matrix.
+ * @throws OutOfRangeException if the specified column index is invalid.
+ */
+ RealMatrix getColumnMatrix(int column) throws OutOfRangeException;
+
+ /**
+ * Sets the specified {@code column} of {@code this} matrix to the entries of the specified
+ * column {@code matrix}. Column indices start at 0.
+ *
+ * @param column Column to be set.
+ * @param matrix Column matrix to be copied (must have one column and the same number of rows as
+ * the instance).
+ * @throws OutOfRangeException if the specified column index is invalid.
+ * @throws MatrixDimensionMismatchException if the column dimension of the {@code matrix} is not
+ * {@code 1}, or the row dimensions of {@code this} and {@code matrix} do not match.
+ */
+ void setColumnMatrix(int column, RealMatrix matrix)
+ throws OutOfRangeException, MatrixDimensionMismatchException;
+
+ /**
+ * Returns the entries in row number {@code row} as a vector. Row indices start at 0.
+ *
+ * @param row Row to be fetched.
+ * @return a row vector.
+ * @throws OutOfRangeException if the specified row index is invalid.
+ */
+ RealVector getRowVector(int row) throws OutOfRangeException;
+
+ /**
+ * Sets the specified {@code row} of {@code this} matrix to the entries of the specified {@code
+ * vector}. Row indices start at 0.
+ *
+ * @param row Row to be set.
+ * @param vector row vector to be copied (must have the same number of column as the instance).
+ * @throws OutOfRangeException if the specified row index is invalid.
+ * @throws MatrixDimensionMismatchException if the {@code vector} dimension does not match the
+ * column dimension of {@code this} matrix.
+ */
+ void setRowVector(int row, RealVector vector)
+ throws OutOfRangeException, MatrixDimensionMismatchException;
+
+ /**
+ * Get the entries at the given column index as a vector. Column indices start at 0.
+ *
+ * @param column Column to be fetched.
+ * @return a column vector.
+ * @throws OutOfRangeException if the specified column index is invalid
+ */
+ RealVector getColumnVector(int column) throws OutOfRangeException;
+
+ /**
+ * Sets the specified {@code column} of {@code this} matrix to the entries of the specified
+ * {@code vector}. Column indices start at 0.
+ *
+ * @param column Column to be set.
+ * @param vector column vector to be copied (must have the same number of rows as the instance).
+ * @throws OutOfRangeException if the specified column index is invalid.
+ * @throws MatrixDimensionMismatchException if the {@code vector} dimension does not match the
+ * row dimension of {@code this} matrix.
+ */
+ void setColumnVector(int column, RealVector vector)
+ throws OutOfRangeException, MatrixDimensionMismatchException;
+
+ /**
+ * Get the entries at the given row index. Row indices start at 0.
+ *
+ * @param row Row to be fetched.
+ * @return the array of entries in the row.
+ * @throws OutOfRangeException if the specified row index is not valid.
+ */
+ double[] getRow(int row) throws OutOfRangeException;
+
+ /**
+ * Sets the specified {@code row} of {@code this} matrix to the entries of the specified {@code
+ * array}. Row indices start at 0.
+ *
+ * @param row Row to be set.
+ * @param array Row matrix to be copied (must have the same number of columns as the instance)
+ * @throws OutOfRangeException if the specified row index is invalid.
+ * @throws MatrixDimensionMismatchException if the {@code array} length does not match the
+ * column dimension of {@code this} matrix.
+ */
+ void setRow(int row, double[] array)
+ throws OutOfRangeException, MatrixDimensionMismatchException;
+
+ /**
+ * Get the entries at the given column index as an array. Column indices start at 0.
+ *
+ * @param column Column to be fetched.
+ * @return the array of entries in the column.
+ * @throws OutOfRangeException if the specified column index is not valid.
+ */
+ double[] getColumn(int column) throws OutOfRangeException;
+
+ /**
+ * Sets the specified {@code column} of {@code this} matrix to the entries of the specified
+ * {@code array}. Column indices start at 0.
+ *
+ * @param column Column to be set.
+ * @param array Column array to be copied (must have the same number of rows as the instance).
+ * @throws OutOfRangeException if the specified column index is invalid.
+ * @throws MatrixDimensionMismatchException if the {@code array} length does not match the row
+ * dimension of {@code this} matrix.
+ */
+ void setColumn(int column, double[] array)
+ throws OutOfRangeException, MatrixDimensionMismatchException;
+
+ /**
+ * Get the entry in the specified row and column. Row and column indices start at 0.
+ *
+ * @param row Row index of entry to be fetched.
+ * @param column Column index of entry to be fetched.
+ * @return the matrix entry at {@code (row, column)}.
+ * @throws OutOfRangeException if the row or column index is not valid.
+ */
+ double getEntry(int row, int column) throws OutOfRangeException;
+
+ /**
+ * Set the entry in the specified row and column. Row and column indices start at 0.
+ *
+ * @param row Row index of entry to be set.
+ * @param column Column index of entry to be set.
+ * @param value the new value of the entry.
+ * @throws OutOfRangeException if the row or column index is not valid
+ * @since 2.0
+ */
+ void setEntry(int row, int column, double value) throws OutOfRangeException;
+
+ /**
+ * Adds (in place) the specified value to the specified entry of {@code this} matrix. Row and
+ * column indices start at 0.
+ *
+ * @param row Row index of the entry to be modified.
+ * @param column Column index of the entry to be modified.
+ * @param increment value to add to the matrix entry.
+ * @throws OutOfRangeException if the row or column index is not valid.
+ * @since 2.0
+ */
+ void addToEntry(int row, int column, double increment) throws OutOfRangeException;
+
+ /**
+ * Multiplies (in place) the specified entry of {@code this} matrix by the specified value. Row
+ * and column indices start at 0.
+ *
+ * @param row Row index of the entry to be modified.
+ * @param column Column index of the entry to be modified.
+ * @param factor Multiplication factor for the matrix entry.
+ * @throws OutOfRangeException if the row or column index is not valid.
+ * @since 2.0
+ */
+ void multiplyEntry(int row, int column, double factor) throws OutOfRangeException;
+
+ /**
+ * Returns the transpose of this matrix.
+ *
+ * @return transpose matrix
+ */
+ RealMatrix transpose();
+
+ /**
+ * Returns the <a href="http://mathworld.wolfram.com/MatrixTrace.html">trace</a> of the matrix
+ * (the sum of the elements on the main diagonal).
+ *
+ * @return the trace.
+ * @throws NonSquareMatrixException if the matrix is not square.
+ */
+ double getTrace() throws NonSquareMatrixException;
+
+ /**
+ * Returns the result of multiplying this by the vector {@code v}.
+ *
+ * @param v the vector to operate on
+ * @return {@code this * v}
+ * @throws DimensionMismatchException if the length of {@code v} does not match the column
+ * dimension of {@code this}.
+ */
+ double[] operate(double[] v) throws DimensionMismatchException;
+
+ /**
+ * Returns the result of multiplying this by the vector {@code v}.
+ *
+ * @param v the vector to operate on
+ * @return {@code this * v}
+ * @throws DimensionMismatchException if the dimension of {@code v} does not match the column
+ * dimension of {@code this}.
+ */
+ RealVector operate(RealVector v) throws DimensionMismatchException;
+
+ /**
+ * Returns the (row) vector result of premultiplying this by the vector {@code v}.
+ *
+ * @param v the row vector to premultiply by
+ * @return {@code v * this}
+ * @throws DimensionMismatchException if the length of {@code v} does not match the row
+ * dimension of {@code this}.
+ */
+ double[] preMultiply(double[] v) throws DimensionMismatchException;
+
+ /**
+ * Returns the (row) vector result of premultiplying this by the vector {@code v}.
+ *
+ * @param v the row vector to premultiply by
+ * @return {@code v * this}
+ * @throws DimensionMismatchException if the dimension of {@code v} does not match the row
+ * dimension of {@code this}.
+ */
+ RealVector preMultiply(RealVector v) throws DimensionMismatchException;
+
+ /**
+ * Visit (and possibly change) all matrix entries in row order.
+ *
+ * <p>Row order starts at upper left and iterating through all elements of a row from left to
+ * right before going to the leftmost element of the next row.
+ *
+ * @param visitor visitor used to process all matrix entries
+ * @see #walkInRowOrder(RealMatrixPreservingVisitor)
+ * @see #walkInRowOrder(RealMatrixChangingVisitor, int, int, int, int)
+ * @see #walkInRowOrder(RealMatrixPreservingVisitor, int, int, int, int)
+ * @see #walkInColumnOrder(RealMatrixChangingVisitor)
+ * @see #walkInColumnOrder(RealMatrixPreservingVisitor)
+ * @see #walkInColumnOrder(RealMatrixChangingVisitor, int, int, int, int)
+ * @see #walkInColumnOrder(RealMatrixPreservingVisitor, int, int, int, int)
+ * @see #walkInOptimizedOrder(RealMatrixChangingVisitor)
+ * @see #walkInOptimizedOrder(RealMatrixPreservingVisitor)
+ * @see #walkInOptimizedOrder(RealMatrixChangingVisitor, int, int, int, int)
+ * @see #walkInOptimizedOrder(RealMatrixPreservingVisitor, int, int, int, int)
+ * @return the value returned by {@link RealMatrixChangingVisitor#end()} at the end of the walk
+ */
+ double walkInRowOrder(RealMatrixChangingVisitor visitor);
+
+ /**
+ * Visit (but don't change) all matrix entries in row order.
+ *
+ * <p>Row order starts at upper left and iterating through all elements of a row from left to
+ * right before going to the leftmost element of the next row.
+ *
+ * @param visitor visitor used to process all matrix entries
+ * @see #walkInRowOrder(RealMatrixChangingVisitor)
+ * @see #walkInRowOrder(RealMatrixChangingVisitor, int, int, int, int)
+ * @see #walkInRowOrder(RealMatrixPreservingVisitor, int, int, int, int)
+ * @see #walkInColumnOrder(RealMatrixChangingVisitor)
+ * @see #walkInColumnOrder(RealMatrixPreservingVisitor)
+ * @see #walkInColumnOrder(RealMatrixChangingVisitor, int, int, int, int)
+ * @see #walkInColumnOrder(RealMatrixPreservingVisitor, int, int, int, int)
+ * @see #walkInOptimizedOrder(RealMatrixChangingVisitor)
+ * @see #walkInOptimizedOrder(RealMatrixPreservingVisitor)
+ * @see #walkInOptimizedOrder(RealMatrixChangingVisitor, int, int, int, int)
+ * @see #walkInOptimizedOrder(RealMatrixPreservingVisitor, int, int, int, int)
+ * @return the value returned by {@link RealMatrixPreservingVisitor#end()} at the end of the
+ * walk
+ */
+ double walkInRowOrder(RealMatrixPreservingVisitor visitor);
+
+ /**
+ * Visit (and possibly change) some matrix entries in row order.
+ *
+ * <p>Row order starts at upper left and iterating through all elements of a row from left to
+ * right before going to the leftmost element of the next row.
+ *
+ * @param visitor visitor used to process all matrix entries
+ * @param startRow Initial row index
+ * @param endRow Final row index (inclusive)
+ * @param startColumn Initial column index
+ * @param endColumn Final column index
+ * @throws OutOfRangeException if the indices are not valid.
+ * @throws NumberIsTooSmallException if {@code endRow < startRow} or {@code endColumn <
+ * startColumn}.
+ * @see #walkInRowOrder(RealMatrixChangingVisitor)
+ * @see #walkInRowOrder(RealMatrixPreservingVisitor)
+ * @see #walkInRowOrder(RealMatrixPreservingVisitor, int, int, int, int)
+ * @see #walkInColumnOrder(RealMatrixChangingVisitor)
+ * @see #walkInColumnOrder(RealMatrixPreservingVisitor)
+ * @see #walkInColumnOrder(RealMatrixChangingVisitor, int, int, int, int)
+ * @see #walkInColumnOrder(RealMatrixPreservingVisitor, int, int, int, int)
+ * @see #walkInOptimizedOrder(RealMatrixChangingVisitor)
+ * @see #walkInOptimizedOrder(RealMatrixPreservingVisitor)
+ * @see #walkInOptimizedOrder(RealMatrixChangingVisitor, int, int, int, int)
+ * @see #walkInOptimizedOrder(RealMatrixPreservingVisitor, int, int, int, int)
+ * @return the value returned by {@link RealMatrixChangingVisitor#end()} at the end of the walk
+ */
+ double walkInRowOrder(
+ RealMatrixChangingVisitor visitor,
+ int startRow,
+ int endRow,
+ int startColumn,
+ int endColumn)
+ throws OutOfRangeException, NumberIsTooSmallException;
+
+ /**
+ * Visit (but don't change) some matrix entries in row order.
+ *
+ * <p>Row order starts at upper left and iterating through all elements of a row from left to
+ * right before going to the leftmost element of the next row.
+ *
+ * @param visitor visitor used to process all matrix entries
+ * @param startRow Initial row index
+ * @param endRow Final row index (inclusive)
+ * @param startColumn Initial column index
+ * @param endColumn Final column index
+ * @throws OutOfRangeException if the indices are not valid.
+ * @throws NumberIsTooSmallException if {@code endRow < startRow} or {@code endColumn <
+ * startColumn}.
+ * @see #walkInRowOrder(RealMatrixChangingVisitor)
+ * @see #walkInRowOrder(RealMatrixPreservingVisitor)
+ * @see #walkInRowOrder(RealMatrixChangingVisitor, int, int, int, int)
+ * @see #walkInColumnOrder(RealMatrixChangingVisitor)
+ * @see #walkInColumnOrder(RealMatrixPreservingVisitor)
+ * @see #walkInColumnOrder(RealMatrixChangingVisitor, int, int, int, int)
+ * @see #walkInColumnOrder(RealMatrixPreservingVisitor, int, int, int, int)
+ * @see #walkInOptimizedOrder(RealMatrixChangingVisitor)
+ * @see #walkInOptimizedOrder(RealMatrixPreservingVisitor)
+ * @see #walkInOptimizedOrder(RealMatrixChangingVisitor, int, int, int, int)
+ * @see #walkInOptimizedOrder(RealMatrixPreservingVisitor, int, int, int, int)
+ * @return the value returned by {@link RealMatrixPreservingVisitor#end()} at the end of the
+ * walk
+ */
+ double walkInRowOrder(
+ RealMatrixPreservingVisitor visitor,
+ int startRow,
+ int endRow,
+ int startColumn,
+ int endColumn)
+ throws OutOfRangeException, NumberIsTooSmallException;
+
+ /**
+ * Visit (and possibly change) all matrix entries in column order.
+ *
+ * <p>Column order starts at upper left and iterating through all elements of a column from top
+ * to bottom before going to the topmost element of the next column.
+ *
+ * @param visitor visitor used to process all matrix entries
+ * @see #walkInRowOrder(RealMatrixChangingVisitor)
+ * @see #walkInRowOrder(RealMatrixPreservingVisitor)
+ * @see #walkInRowOrder(RealMatrixChangingVisitor, int, int, int, int)
+ * @see #walkInRowOrder(RealMatrixPreservingVisitor, int, int, int, int)
+ * @see #walkInColumnOrder(RealMatrixPreservingVisitor)
+ * @see #walkInColumnOrder(RealMatrixChangingVisitor, int, int, int, int)
+ * @see #walkInColumnOrder(RealMatrixPreservingVisitor, int, int, int, int)
+ * @see #walkInOptimizedOrder(RealMatrixChangingVisitor)
+ * @see #walkInOptimizedOrder(RealMatrixPreservingVisitor)
+ * @see #walkInOptimizedOrder(RealMatrixChangingVisitor, int, int, int, int)
+ * @see #walkInOptimizedOrder(RealMatrixPreservingVisitor, int, int, int, int)
+ * @return the value returned by {@link RealMatrixChangingVisitor#end()} at the end of the walk
+ */
+ double walkInColumnOrder(RealMatrixChangingVisitor visitor);
+
+ /**
+ * Visit (but don't change) all matrix entries in column order.
+ *
+ * <p>Column order starts at upper left and iterating through all elements of a column from top
+ * to bottom before going to the topmost element of the next column.
+ *
+ * @param visitor visitor used to process all matrix entries
+ * @see #walkInRowOrder(RealMatrixChangingVisitor)
+ * @see #walkInRowOrder(RealMatrixPreservingVisitor)
+ * @see #walkInRowOrder(RealMatrixChangingVisitor, int, int, int, int)
+ * @see #walkInRowOrder(RealMatrixPreservingVisitor, int, int, int, int)
+ * @see #walkInColumnOrder(RealMatrixChangingVisitor)
+ * @see #walkInColumnOrder(RealMatrixChangingVisitor, int, int, int, int)
+ * @see #walkInColumnOrder(RealMatrixPreservingVisitor, int, int, int, int)
+ * @see #walkInOptimizedOrder(RealMatrixChangingVisitor)
+ * @see #walkInOptimizedOrder(RealMatrixPreservingVisitor)
+ * @see #walkInOptimizedOrder(RealMatrixChangingVisitor, int, int, int, int)
+ * @see #walkInOptimizedOrder(RealMatrixPreservingVisitor, int, int, int, int)
+ * @return the value returned by {@link RealMatrixPreservingVisitor#end()} at the end of the
+ * walk
+ */
+ double walkInColumnOrder(RealMatrixPreservingVisitor visitor);
+
+ /**
+ * Visit (and possibly change) some matrix entries in column order.
+ *
+ * <p>Column order starts at upper left and iterating through all elements of a column from top
+ * to bottom before going to the topmost element of the next column.
+ *
+ * @param visitor visitor used to process all matrix entries
+ * @param startRow Initial row index
+ * @param endRow Final row index (inclusive)
+ * @param startColumn Initial column index
+ * @param endColumn Final column index
+ * @throws OutOfRangeException if the indices are not valid.
+ * @throws NumberIsTooSmallException if {@code endRow < startRow} or {@code endColumn <
+ * startColumn}.
+ * @see #walkInRowOrder(RealMatrixChangingVisitor)
+ * @see #walkInRowOrder(RealMatrixPreservingVisitor)
+ * @see #walkInRowOrder(RealMatrixChangingVisitor, int, int, int, int)
+ * @see #walkInRowOrder(RealMatrixPreservingVisitor, int, int, int, int)
+ * @see #walkInColumnOrder(RealMatrixChangingVisitor)
+ * @see #walkInColumnOrder(RealMatrixPreservingVisitor)
+ * @see #walkInColumnOrder(RealMatrixPreservingVisitor, int, int, int, int)
+ * @see #walkInOptimizedOrder(RealMatrixChangingVisitor)
+ * @see #walkInOptimizedOrder(RealMatrixPreservingVisitor)
+ * @see #walkInOptimizedOrder(RealMatrixChangingVisitor, int, int, int, int)
+ * @see #walkInOptimizedOrder(RealMatrixPreservingVisitor, int, int, int, int)
+ * @return the value returned by {@link RealMatrixChangingVisitor#end()} at the end of the walk
+ */
+ double walkInColumnOrder(
+ RealMatrixChangingVisitor visitor,
+ int startRow,
+ int endRow,
+ int startColumn,
+ int endColumn)
+ throws OutOfRangeException, NumberIsTooSmallException;
+
+ /**
+ * Visit (but don't change) some matrix entries in column order.
+ *
+ * <p>Column order starts at upper left and iterating through all elements of a column from top
+ * to bottom before going to the topmost element of the next column.
+ *
+ * @param visitor visitor used to process all matrix entries
+ * @param startRow Initial row index
+ * @param endRow Final row index (inclusive)
+ * @param startColumn Initial column index
+ * @param endColumn Final column index
+ * @throws OutOfRangeException if the indices are not valid.
+ * @throws NumberIsTooSmallException if {@code endRow < startRow} or {@code endColumn <
+ * startColumn}.
+ * @see #walkInRowOrder(RealMatrixChangingVisitor)
+ * @see #walkInRowOrder(RealMatrixPreservingVisitor)
+ * @see #walkInRowOrder(RealMatrixChangingVisitor, int, int, int, int)
+ * @see #walkInRowOrder(RealMatrixPreservingVisitor, int, int, int, int)
+ * @see #walkInColumnOrder(RealMatrixChangingVisitor)
+ * @see #walkInColumnOrder(RealMatrixPreservingVisitor)
+ * @see #walkInColumnOrder(RealMatrixChangingVisitor, int, int, int, int)
+ * @see #walkInOptimizedOrder(RealMatrixChangingVisitor)
+ * @see #walkInOptimizedOrder(RealMatrixPreservingVisitor)
+ * @see #walkInOptimizedOrder(RealMatrixChangingVisitor, int, int, int, int)
+ * @see #walkInOptimizedOrder(RealMatrixPreservingVisitor, int, int, int, int)
+ * @return the value returned by {@link RealMatrixPreservingVisitor#end()} at the end of the
+ * walk
+ */
+ double walkInColumnOrder(
+ RealMatrixPreservingVisitor visitor,
+ int startRow,
+ int endRow,
+ int startColumn,
+ int endColumn)
+ throws OutOfRangeException, NumberIsTooSmallException;
+
+ /**
+ * Visit (and possibly change) all matrix entries using the fastest possible order.
+ *
+ * <p>The fastest walking order depends on the exact matrix class. It may be different from
+ * traditional row or column orders.
+ *
+ * @param visitor visitor used to process all matrix entries
+ * @see #walkInRowOrder(RealMatrixChangingVisitor)
+ * @see #walkInRowOrder(RealMatrixPreservingVisitor)
+ * @see #walkInRowOrder(RealMatrixChangingVisitor, int, int, int, int)
+ * @see #walkInRowOrder(RealMatrixPreservingVisitor, int, int, int, int)
+ * @see #walkInColumnOrder(RealMatrixChangingVisitor)
+ * @see #walkInColumnOrder(RealMatrixPreservingVisitor)
+ * @see #walkInColumnOrder(RealMatrixChangingVisitor, int, int, int, int)
+ * @see #walkInColumnOrder(RealMatrixPreservingVisitor, int, int, int, int)
+ * @see #walkInOptimizedOrder(RealMatrixPreservingVisitor)
+ * @see #walkInOptimizedOrder(RealMatrixChangingVisitor, int, int, int, int)
+ * @see #walkInOptimizedOrder(RealMatrixPreservingVisitor, int, int, int, int)
+ * @return the value returned by {@link RealMatrixChangingVisitor#end()} at the end of the walk
+ */
+ double walkInOptimizedOrder(RealMatrixChangingVisitor visitor);
+
+ /**
+ * Visit (but don't change) all matrix entries using the fastest possible order.
+ *
+ * <p>The fastest walking order depends on the exact matrix class. It may be different from
+ * traditional row or column orders.
+ *
+ * @param visitor visitor used to process all matrix entries
+ * @see #walkInRowOrder(RealMatrixChangingVisitor)
+ * @see #walkInRowOrder(RealMatrixPreservingVisitor)
+ * @see #walkInRowOrder(RealMatrixChangingVisitor, int, int, int, int)
+ * @see #walkInRowOrder(RealMatrixPreservingVisitor, int, int, int, int)
+ * @see #walkInColumnOrder(RealMatrixChangingVisitor)
+ * @see #walkInColumnOrder(RealMatrixPreservingVisitor)
+ * @see #walkInColumnOrder(RealMatrixChangingVisitor, int, int, int, int)
+ * @see #walkInColumnOrder(RealMatrixPreservingVisitor, int, int, int, int)
+ * @see #walkInOptimizedOrder(RealMatrixChangingVisitor)
+ * @see #walkInOptimizedOrder(RealMatrixChangingVisitor, int, int, int, int)
+ * @see #walkInOptimizedOrder(RealMatrixPreservingVisitor, int, int, int, int)
+ * @return the value returned by {@link RealMatrixPreservingVisitor#end()} at the end of the
+ * walk
+ */
+ double walkInOptimizedOrder(RealMatrixPreservingVisitor visitor);
+
+ /**
+ * Visit (and possibly change) some matrix entries using the fastest possible order.
+ *
+ * <p>The fastest walking order depends on the exact matrix class. It may be different from
+ * traditional row or column orders.
+ *
+ * @param visitor visitor used to process all matrix entries
+ * @param startRow Initial row index
+ * @param endRow Final row index (inclusive)
+ * @param startColumn Initial column index
+ * @param endColumn Final column index (inclusive)
+ * @throws OutOfRangeException if the indices are not valid.
+ * @throws NumberIsTooSmallException if {@code endRow < startRow} or {@code endColumn <
+ * startColumn}.
+ * @see #walkInRowOrder(RealMatrixChangingVisitor)
+ * @see #walkInRowOrder(RealMatrixPreservingVisitor)
+ * @see #walkInRowOrder(RealMatrixChangingVisitor, int, int, int, int)
+ * @see #walkInRowOrder(RealMatrixPreservingVisitor, int, int, int, int)
+ * @see #walkInColumnOrder(RealMatrixChangingVisitor)
+ * @see #walkInColumnOrder(RealMatrixPreservingVisitor)
+ * @see #walkInColumnOrder(RealMatrixChangingVisitor, int, int, int, int)
+ * @see #walkInColumnOrder(RealMatrixPreservingVisitor, int, int, int, int)
+ * @see #walkInOptimizedOrder(RealMatrixChangingVisitor)
+ * @see #walkInOptimizedOrder(RealMatrixPreservingVisitor)
+ * @see #walkInOptimizedOrder(RealMatrixPreservingVisitor, int, int, int, int)
+ * @return the value returned by {@link RealMatrixChangingVisitor#end()} at the end of the walk
+ */
+ double walkInOptimizedOrder(
+ RealMatrixChangingVisitor visitor,
+ int startRow,
+ int endRow,
+ int startColumn,
+ int endColumn)
+ throws OutOfRangeException, NumberIsTooSmallException;
+
+ /**
+ * Visit (but don't change) some matrix entries using the fastest possible order.
+ *
+ * <p>The fastest walking order depends on the exact matrix class. It may be different from
+ * traditional row or column orders.
+ *
+ * @param visitor visitor used to process all matrix entries
+ * @param startRow Initial row index
+ * @param endRow Final row index (inclusive)
+ * @param startColumn Initial column index
+ * @param endColumn Final column index (inclusive)
+ * @throws OutOfRangeException if the indices are not valid.
+ * @throws NumberIsTooSmallException if {@code endRow < startRow} or {@code endColumn <
+ * startColumn}.
+ * @see #walkInRowOrder(RealMatrixChangingVisitor)
+ * @see #walkInRowOrder(RealMatrixPreservingVisitor)
+ * @see #walkInRowOrder(RealMatrixChangingVisitor, int, int, int, int)
+ * @see #walkInRowOrder(RealMatrixPreservingVisitor, int, int, int, int)
+ * @see #walkInColumnOrder(RealMatrixChangingVisitor)
+ * @see #walkInColumnOrder(RealMatrixPreservingVisitor)
+ * @see #walkInColumnOrder(RealMatrixChangingVisitor, int, int, int, int)
+ * @see #walkInColumnOrder(RealMatrixPreservingVisitor, int, int, int, int)
+ * @see #walkInOptimizedOrder(RealMatrixChangingVisitor)
+ * @see #walkInOptimizedOrder(RealMatrixPreservingVisitor)
+ * @see #walkInOptimizedOrder(RealMatrixChangingVisitor, int, int, int, int)
+ * @return the value returned by {@link RealMatrixPreservingVisitor#end()} at the end of the
+ * walk
+ */
+ double walkInOptimizedOrder(
+ RealMatrixPreservingVisitor visitor,
+ int startRow,
+ int endRow,
+ int startColumn,
+ int endColumn)
+ throws OutOfRangeException, NumberIsTooSmallException;
+}
diff --git a/src/main/java/org/apache/commons/math3/linear/RealMatrixChangingVisitor.java b/src/main/java/org/apache/commons/math3/linear/RealMatrixChangingVisitor.java
new file mode 100644
index 0000000..6798655
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/linear/RealMatrixChangingVisitor.java
@@ -0,0 +1,59 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math3.linear;
+
+/**
+ * Interface defining a visitor for matrix entries.
+ *
+ * @see DefaultRealMatrixChangingVisitor
+ * @since 2.0
+ */
+public interface RealMatrixChangingVisitor {
+ /**
+ * Start visiting a matrix.
+ *
+ * <p>This method is called once before any entry of the matrix is visited.
+ *
+ * @param rows number of rows of the matrix
+ * @param columns number of columns of the matrix
+ * @param startRow Initial row index
+ * @param endRow Final row index (inclusive)
+ * @param startColumn Initial column index
+ * @param endColumn Final column index (inclusive)
+ */
+ void start(int rows, int columns, int startRow, int endRow, int startColumn, int endColumn);
+
+ /**
+ * Visit one matrix entry.
+ *
+ * @param row row index of the entry
+ * @param column column index of the entry
+ * @param value current value of the entry
+ * @return the new value to be set for the entry
+ */
+ double visit(int row, int column, double value);
+
+ /**
+ * End visiting a matrix.
+ *
+ * <p>This method is called once after all entries of the matrix have been visited.
+ *
+ * @return the value that the <code>walkInXxxOrder</code> must return
+ */
+ double end();
+}
diff --git a/src/main/java/org/apache/commons/math3/linear/RealMatrixFormat.java b/src/main/java/org/apache/commons/math3/linear/RealMatrixFormat.java
new file mode 100644
index 0000000..d4eee6b
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/linear/RealMatrixFormat.java
@@ -0,0 +1,442 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math3.linear;
+
+import org.apache.commons.math3.exception.MathParseException;
+import org.apache.commons.math3.util.CompositeFormat;
+
+import java.text.FieldPosition;
+import java.text.NumberFormat;
+import java.text.ParsePosition;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Locale;
+
+/**
+ * Formats a {@code nxm} matrix in components list format
+ * "{{a<sub>0</sub><sub>0</sub>,a<sub>0</sub><sub>1</sub>, ...,
+ * a<sub>0</sub><sub>m-1</sub>},{a<sub>1</sub><sub>0</sub>, a<sub>1</sub><sub>1</sub>, ...,
+ * a<sub>1</sub><sub>m-1</sub>},{...},{ a<sub>n-1</sub><sub>0</sub>, a<sub>n-1</sub><sub>1</sub>,
+ * ..., a<sub>n-1</sub><sub>m-1</sub>}}".
+ *
+ * <p>The prefix and suffix "{" and "}", the row prefix and suffix "{" and "}", the row separator
+ * "," and the column separator "," can be replaced by any user-defined strings. The number format
+ * for components can be configured.
+ *
+ * <p>White space is ignored at parse time, even if it is in the prefix, suffix or separator
+ * specifications. So even if the default separator does include a space character that is used at
+ * format time, both input string "{{1,1,1}}" and " { { 1 , 1 , 1 } } " will be parsed without error
+ * and the same matrix will be returned. In the second case, however, the parse position after
+ * parsing will be just after the closing curly brace, i.e. just before the trailing space.
+ *
+ * <p><b>Note:</b> the grouping functionality of the used {@link NumberFormat} is disabled to
+ * prevent problems when parsing (e.g. 1,345.34 would be a valid number but conflicts with the
+ * default column separator).
+ *
+ * @since 3.1
+ */
+public class RealMatrixFormat {
+
+ /** The default prefix: "{". */
+ private static final String DEFAULT_PREFIX = "{";
+
+ /** The default suffix: "}". */
+ private static final String DEFAULT_SUFFIX = "}";
+
+ /** The default row prefix: "{". */
+ private static final String DEFAULT_ROW_PREFIX = "{";
+
+ /** The default row suffix: "}". */
+ private static final String DEFAULT_ROW_SUFFIX = "}";
+
+ /** The default row separator: ",". */
+ private static final String DEFAULT_ROW_SEPARATOR = ",";
+
+ /** The default column separator: ",". */
+ private static final String DEFAULT_COLUMN_SEPARATOR = ",";
+
+ /** Prefix. */
+ private final String prefix;
+
+ /** Suffix. */
+ private final String suffix;
+
+ /** Row prefix. */
+ private final String rowPrefix;
+
+ /** Row suffix. */
+ private final String rowSuffix;
+
+ /** Row separator. */
+ private final String rowSeparator;
+
+ /** Column separator. */
+ private final String columnSeparator;
+
+ /** The format used for components. */
+ private final NumberFormat format;
+
+ /**
+ * Create an instance with default settings.
+ *
+ * <p>The instance uses the default prefix, suffix and row/column separator: "[", "]", ";" and
+ * ", " and the default number format for components.
+ */
+ public RealMatrixFormat() {
+ this(
+ DEFAULT_PREFIX,
+ DEFAULT_SUFFIX,
+ DEFAULT_ROW_PREFIX,
+ DEFAULT_ROW_SUFFIX,
+ DEFAULT_ROW_SEPARATOR,
+ DEFAULT_COLUMN_SEPARATOR,
+ CompositeFormat.getDefaultNumberFormat());
+ }
+
+ /**
+ * Create an instance with a custom number format for components.
+ *
+ * @param format the custom format for components.
+ */
+ public RealMatrixFormat(final NumberFormat format) {
+ this(
+ DEFAULT_PREFIX,
+ DEFAULT_SUFFIX,
+ DEFAULT_ROW_PREFIX,
+ DEFAULT_ROW_SUFFIX,
+ DEFAULT_ROW_SEPARATOR,
+ DEFAULT_COLUMN_SEPARATOR,
+ format);
+ }
+
+ /**
+ * Create an instance with custom prefix, suffix and separator.
+ *
+ * @param prefix prefix to use instead of the default "{"
+ * @param suffix suffix to use instead of the default "}"
+ * @param rowPrefix row prefix to use instead of the default "{"
+ * @param rowSuffix row suffix to use instead of the default "}"
+ * @param rowSeparator tow separator to use instead of the default ";"
+ * @param columnSeparator column separator to use instead of the default ", "
+ */
+ public RealMatrixFormat(
+ final String prefix,
+ final String suffix,
+ final String rowPrefix,
+ final String rowSuffix,
+ final String rowSeparator,
+ final String columnSeparator) {
+ this(
+ prefix,
+ suffix,
+ rowPrefix,
+ rowSuffix,
+ rowSeparator,
+ columnSeparator,
+ CompositeFormat.getDefaultNumberFormat());
+ }
+
+ /**
+ * Create an instance with custom prefix, suffix, separator and format for components.
+ *
+ * @param prefix prefix to use instead of the default "{"
+ * @param suffix suffix to use instead of the default "}"
+ * @param rowPrefix row prefix to use instead of the default "{"
+ * @param rowSuffix row suffix to use instead of the default "}"
+ * @param rowSeparator tow separator to use instead of the default ";"
+ * @param columnSeparator column separator to use instead of the default ", "
+ * @param format the custom format for components.
+ */
+ public RealMatrixFormat(
+ final String prefix,
+ final String suffix,
+ final String rowPrefix,
+ final String rowSuffix,
+ final String rowSeparator,
+ final String columnSeparator,
+ final NumberFormat format) {
+ this.prefix = prefix;
+ this.suffix = suffix;
+ this.rowPrefix = rowPrefix;
+ this.rowSuffix = rowSuffix;
+ this.rowSeparator = rowSeparator;
+ this.columnSeparator = columnSeparator;
+ this.format = format;
+ // disable grouping to prevent parsing problems
+ this.format.setGroupingUsed(false);
+ }
+
+ /**
+ * Get the set of locales for which real vectors formats are available.
+ *
+ * <p>This is the same set as the {@link NumberFormat} set.
+ *
+ * @return available real vector format locales.
+ */
+ public static Locale[] getAvailableLocales() {
+ return NumberFormat.getAvailableLocales();
+ }
+
+ /**
+ * Get the format prefix.
+ *
+ * @return format prefix.
+ */
+ public String getPrefix() {
+ return prefix;
+ }
+
+ /**
+ * Get the format suffix.
+ *
+ * @return format suffix.
+ */
+ public String getSuffix() {
+ return suffix;
+ }
+
+ /**
+ * Get the format prefix.
+ *
+ * @return format prefix.
+ */
+ public String getRowPrefix() {
+ return rowPrefix;
+ }
+
+ /**
+ * Get the format suffix.
+ *
+ * @return format suffix.
+ */
+ public String getRowSuffix() {
+ return rowSuffix;
+ }
+
+ /**
+ * Get the format separator between rows of the matrix.
+ *
+ * @return format separator for rows.
+ */
+ public String getRowSeparator() {
+ return rowSeparator;
+ }
+
+ /**
+ * Get the format separator between components.
+ *
+ * @return format separator between components.
+ */
+ public String getColumnSeparator() {
+ return columnSeparator;
+ }
+
+ /**
+ * Get the components format.
+ *
+ * @return components format.
+ */
+ public NumberFormat getFormat() {
+ return format;
+ }
+
+ /**
+ * Returns the default real vector format for the current locale.
+ *
+ * @return the default real vector format.
+ */
+ public static RealMatrixFormat getInstance() {
+ return getInstance(Locale.getDefault());
+ }
+
+ /**
+ * Returns the default real vector format for the given locale.
+ *
+ * @param locale the specific locale used by the format.
+ * @return the real vector format specific to the given locale.
+ */
+ public static RealMatrixFormat getInstance(final Locale locale) {
+ return new RealMatrixFormat(CompositeFormat.getDefaultNumberFormat(locale));
+ }
+
+ /**
+ * This method calls {@link #format(RealMatrix,StringBuffer,FieldPosition)}.
+ *
+ * @param m RealMatrix object to format.
+ * @return a formatted matrix.
+ */
+ public String format(RealMatrix m) {
+ return format(m, new StringBuffer(), new FieldPosition(0)).toString();
+ }
+
+ /**
+ * Formats a {@link RealMatrix} object to produce a string.
+ *
+ * @param matrix the object to format.
+ * @param toAppendTo where the text is to be appended
+ * @param pos On input: an alignment field, if desired. On output: the offsets of the alignment
+ * field
+ * @return the value passed in as toAppendTo.
+ */
+ public StringBuffer format(RealMatrix matrix, StringBuffer toAppendTo, FieldPosition pos) {
+
+ pos.setBeginIndex(0);
+ pos.setEndIndex(0);
+
+ // format prefix
+ toAppendTo.append(prefix);
+
+ // format rows
+ final int rows = matrix.getRowDimension();
+ for (int i = 0; i < rows; ++i) {
+ toAppendTo.append(rowPrefix);
+ for (int j = 0; j < matrix.getColumnDimension(); ++j) {
+ if (j > 0) {
+ toAppendTo.append(columnSeparator);
+ }
+ CompositeFormat.formatDouble(matrix.getEntry(i, j), format, toAppendTo, pos);
+ }
+ toAppendTo.append(rowSuffix);
+ if (i < rows - 1) {
+ toAppendTo.append(rowSeparator);
+ }
+ }
+
+ // format suffix
+ toAppendTo.append(suffix);
+
+ return toAppendTo;
+ }
+
+ /**
+ * Parse a string to produce a {@link RealMatrix} object.
+ *
+ * @param source String to parse.
+ * @return the parsed {@link RealMatrix} object.
+ * @throws MathParseException if the beginning of the specified string cannot be parsed.
+ */
+ public RealMatrix parse(String source) {
+ final ParsePosition parsePosition = new ParsePosition(0);
+ final RealMatrix result = parse(source, parsePosition);
+ if (parsePosition.getIndex() == 0) {
+ throw new MathParseException(
+ source, parsePosition.getErrorIndex(), Array2DRowRealMatrix.class);
+ }
+ return result;
+ }
+
+ /**
+ * Parse a string to produce a {@link RealMatrix} object.
+ *
+ * @param source String to parse.
+ * @param pos input/ouput parsing parameter.
+ * @return the parsed {@link RealMatrix} object.
+ */
+ public RealMatrix parse(String source, ParsePosition pos) {
+ int initialIndex = pos.getIndex();
+
+ final String trimmedPrefix = prefix.trim();
+ final String trimmedSuffix = suffix.trim();
+ final String trimmedRowPrefix = rowPrefix.trim();
+ final String trimmedRowSuffix = rowSuffix.trim();
+ final String trimmedColumnSeparator = columnSeparator.trim();
+ final String trimmedRowSeparator = rowSeparator.trim();
+
+ // parse prefix
+ CompositeFormat.parseAndIgnoreWhitespace(source, pos);
+ if (!CompositeFormat.parseFixedstring(source, trimmedPrefix, pos)) {
+ return null;
+ }
+
+ // parse components
+ List<List<Number>> matrix = new ArrayList<List<Number>>();
+ List<Number> rowComponents = new ArrayList<Number>();
+ for (boolean loop = true; loop; ) {
+
+ if (!rowComponents.isEmpty()) {
+ CompositeFormat.parseAndIgnoreWhitespace(source, pos);
+ if (!CompositeFormat.parseFixedstring(source, trimmedColumnSeparator, pos)) {
+ if (trimmedRowSuffix.length() != 0
+ && !CompositeFormat.parseFixedstring(source, trimmedRowSuffix, pos)) {
+ return null;
+ } else {
+ CompositeFormat.parseAndIgnoreWhitespace(source, pos);
+ if (CompositeFormat.parseFixedstring(source, trimmedRowSeparator, pos)) {
+ matrix.add(rowComponents);
+ rowComponents = new ArrayList<Number>();
+ continue;
+ } else {
+ loop = false;
+ }
+ }
+ }
+ } else {
+ CompositeFormat.parseAndIgnoreWhitespace(source, pos);
+ if (trimmedRowPrefix.length() != 0
+ && !CompositeFormat.parseFixedstring(source, trimmedRowPrefix, pos)) {
+ return null;
+ }
+ }
+
+ if (loop) {
+ CompositeFormat.parseAndIgnoreWhitespace(source, pos);
+ Number component = CompositeFormat.parseNumber(source, format, pos);
+ if (component != null) {
+ rowComponents.add(component);
+ } else {
+ if (rowComponents.isEmpty()) {
+ loop = false;
+ } else {
+ // invalid component
+ // set index back to initial, error index should already be set
+ pos.setIndex(initialIndex);
+ return null;
+ }
+ }
+ }
+ }
+
+ if (!rowComponents.isEmpty()) {
+ matrix.add(rowComponents);
+ }
+
+ // parse suffix
+ CompositeFormat.parseAndIgnoreWhitespace(source, pos);
+ if (!CompositeFormat.parseFixedstring(source, trimmedSuffix, pos)) {
+ return null;
+ }
+
+ // do not allow an empty matrix
+ if (matrix.isEmpty()) {
+ pos.setIndex(initialIndex);
+ return null;
+ }
+
+ // build vector
+ double[][] data = new double[matrix.size()][];
+ int row = 0;
+ for (List<Number> rowList : matrix) {
+ data[row] = new double[rowList.size()];
+ for (int i = 0; i < rowList.size(); i++) {
+ data[row][i] = rowList.get(i).doubleValue();
+ }
+ row++;
+ }
+ return MatrixUtils.createRealMatrix(data);
+ }
+}
diff --git a/src/main/java/org/apache/commons/math3/linear/RealMatrixPreservingVisitor.java b/src/main/java/org/apache/commons/math3/linear/RealMatrixPreservingVisitor.java
new file mode 100644
index 0000000..094ce07
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/linear/RealMatrixPreservingVisitor.java
@@ -0,0 +1,58 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math3.linear;
+
+/**
+ * Interface defining a visitor for matrix entries.
+ *
+ * @see DefaultRealMatrixPreservingVisitor
+ * @since 2.0
+ */
+public interface RealMatrixPreservingVisitor {
+ /**
+ * Start visiting a matrix.
+ *
+ * <p>This method is called once before any entry of the matrix is visited.
+ *
+ * @param rows number of rows of the matrix
+ * @param columns number of columns of the matrix
+ * @param startRow Initial row index
+ * @param endRow Final row index (inclusive)
+ * @param startColumn Initial column index
+ * @param endColumn Final column index (inclusive)
+ */
+ void start(int rows, int columns, int startRow, int endRow, int startColumn, int endColumn);
+
+ /**
+ * Visit one matrix entry.
+ *
+ * @param row row index of the entry
+ * @param column column index of the entry
+ * @param value current value of the entry
+ */
+ void visit(int row, int column, double value);
+
+ /**
+ * End visiting a matrix.
+ *
+ * <p>This method is called once after all entries of the matrix have been visited.
+ *
+ * @return the value that the <code>walkInXxxOrder</code> must return
+ */
+ double end();
+}
diff --git a/src/main/java/org/apache/commons/math3/linear/RealVector.java b/src/main/java/org/apache/commons/math3/linear/RealVector.java
new file mode 100644
index 0000000..ca25235
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/linear/RealVector.java
@@ -0,0 +1,1509 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math3.linear;
+
+import org.apache.commons.math3.analysis.FunctionUtils;
+import org.apache.commons.math3.analysis.UnivariateFunction;
+import org.apache.commons.math3.analysis.function.Add;
+import org.apache.commons.math3.analysis.function.Divide;
+import org.apache.commons.math3.analysis.function.Multiply;
+import org.apache.commons.math3.exception.DimensionMismatchException;
+import org.apache.commons.math3.exception.MathArithmeticException;
+import org.apache.commons.math3.exception.MathUnsupportedOperationException;
+import org.apache.commons.math3.exception.NotPositiveException;
+import org.apache.commons.math3.exception.NumberIsTooSmallException;
+import org.apache.commons.math3.exception.OutOfRangeException;
+import org.apache.commons.math3.exception.util.LocalizedFormats;
+import org.apache.commons.math3.util.FastMath;
+
+import java.util.Iterator;
+import java.util.NoSuchElementException;
+
+/**
+ * Class defining a real-valued vector with basic algebraic operations.
+ *
+ * <p>vector element indexing is 0-based -- e.g., {@code getEntry(0)} returns the first element of
+ * the vector.
+ *
+ * <p>The {@code code map} and {@code mapToSelf} methods operate on vectors element-wise, i.e. they
+ * perform the same operation (adding a scalar, applying a function ...) on each element in turn.
+ * The {@code map} versions create a new vector to hold the result and do not change the instance.
+ * The {@code mapToSelf} version uses the instance itself to store the results, so the instance is
+ * changed by this method. In all cases, the result vector is returned by the methods, allowing the
+ * <i>fluent API</i> style, like this:
+ *
+ * <pre>
+ * RealVector result = v.mapAddToSelf(3.4).mapToSelf(new Tan()).mapToSelf(new Power(2.3));
+ * </pre>
+ *
+ * @since 2.1
+ */
+public abstract class RealVector {
+ /**
+ * Returns the size of the vector.
+ *
+ * @return the size of this vector.
+ */
+ public abstract int getDimension();
+
+ /**
+ * Return the entry at the specified index.
+ *
+ * @param index Index location of entry to be fetched.
+ * @return the vector entry at {@code index}.
+ * @throws OutOfRangeException if the index is not valid.
+ * @see #setEntry(int, double)
+ */
+ public abstract double getEntry(int index) throws OutOfRangeException;
+
+ /**
+ * Set a single element.
+ *
+ * @param index element index.
+ * @param value new value for the element.
+ * @throws OutOfRangeException if the index is not valid.
+ * @see #getEntry(int)
+ */
+ public abstract void setEntry(int index, double value) throws OutOfRangeException;
+
+ /**
+ * Change an entry at the specified index.
+ *
+ * @param index Index location of entry to be set.
+ * @param increment Value to add to the vector entry.
+ * @throws OutOfRangeException if the index is not valid.
+ * @since 3.0
+ */
+ public void addToEntry(int index, double increment) throws OutOfRangeException {
+ setEntry(index, getEntry(index) + increment);
+ }
+
+ /**
+ * Construct a new vector by appending a vector to this vector.
+ *
+ * @param v vector to append to this one.
+ * @return a new vector.
+ */
+ public abstract RealVector append(RealVector v);
+
+ /**
+ * Construct a new vector by appending a double to this vector.
+ *
+ * @param d double to append.
+ * @return a new vector.
+ */
+ public abstract RealVector append(double d);
+
+ /**
+ * Get a subvector from consecutive elements.
+ *
+ * @param index index of first element.
+ * @param n number of elements to be retrieved.
+ * @return a vector containing n elements.
+ * @throws OutOfRangeException if the index is not valid.
+ * @throws NotPositiveException if the number of elements is not positive.
+ */
+ public abstract RealVector getSubVector(int index, int n)
+ throws NotPositiveException, OutOfRangeException;
+
+ /**
+ * Set a sequence of consecutive elements.
+ *
+ * @param index index of first element to be set.
+ * @param v vector containing the values to set.
+ * @throws OutOfRangeException if the index is not valid.
+ */
+ public abstract void setSubVector(int index, RealVector v) throws OutOfRangeException;
+
+ /**
+ * Check whether any coordinate of this vector is {@code NaN}.
+ *
+ * @return {@code true} if any coordinate of this vector is {@code NaN}, {@code false}
+ * otherwise.
+ */
+ public abstract boolean isNaN();
+
+ /**
+ * Check whether any coordinate of this vector is infinite and none are {@code NaN}.
+ *
+ * @return {@code true} if any coordinate of this vector is infinite and none are {@code NaN},
+ * {@code false} otherwise.
+ */
+ public abstract boolean isInfinite();
+
+ /**
+ * Check if instance and specified vectors have the same dimension.
+ *
+ * @param v Vector to compare instance with.
+ * @throws DimensionMismatchException if the vectors do not have the same dimension.
+ */
+ protected void checkVectorDimensions(RealVector v) throws DimensionMismatchException {
+ checkVectorDimensions(v.getDimension());
+ }
+
+ /**
+ * Check if instance dimension is equal to some expected value.
+ *
+ * @param n Expected dimension.
+ * @throws DimensionMismatchException if the dimension is inconsistent with the vector size.
+ */
+ protected void checkVectorDimensions(int n) throws DimensionMismatchException {
+ int d = getDimension();
+ if (d != n) {
+ throw new DimensionMismatchException(d, n);
+ }
+ }
+
+ /**
+ * Check if an index is valid.
+ *
+ * @param index Index to check.
+ * @exception OutOfRangeException if {@code index} is not valid.
+ */
+ protected void checkIndex(final int index) throws OutOfRangeException {
+ if (index < 0 || index >= getDimension()) {
+ throw new OutOfRangeException(LocalizedFormats.INDEX, index, 0, getDimension() - 1);
+ }
+ }
+
+ /**
+ * Checks that the indices of a subvector are valid.
+ *
+ * @param start the index of the first entry of the subvector
+ * @param end the index of the last entry of the subvector (inclusive)
+ * @throws OutOfRangeException if {@code start} of {@code end} are not valid
+ * @throws NumberIsTooSmallException if {@code end < start}
+ * @since 3.1
+ */
+ protected void checkIndices(final int start, final int end)
+ throws NumberIsTooSmallException, OutOfRangeException {
+ final int dim = getDimension();
+ if ((start < 0) || (start >= dim)) {
+ throw new OutOfRangeException(LocalizedFormats.INDEX, start, 0, dim - 1);
+ }
+ if ((end < 0) || (end >= dim)) {
+ throw new OutOfRangeException(LocalizedFormats.INDEX, end, 0, dim - 1);
+ }
+ if (end < start) {
+ // TODO Use more specific error message
+ throw new NumberIsTooSmallException(
+ LocalizedFormats.INITIAL_ROW_AFTER_FINAL_ROW, end, start, false);
+ }
+ }
+
+ /**
+ * Compute the sum of this vector and {@code v}. Returns a new vector. Does not change instance
+ * data.
+ *
+ * @param v Vector to be added.
+ * @return {@code this} + {@code v}.
+ * @throws DimensionMismatchException if {@code v} is not the same size as {@code this} vector.
+ */
+ public RealVector add(RealVector v) throws DimensionMismatchException {
+ checkVectorDimensions(v);
+ RealVector result = v.copy();
+ Iterator<Entry> it = iterator();
+ while (it.hasNext()) {
+ final Entry e = it.next();
+ final int index = e.getIndex();
+ result.setEntry(index, e.getValue() + result.getEntry(index));
+ }
+ return result;
+ }
+
+ /**
+ * Subtract {@code v} from this vector. Returns a new vector. Does not change instance data.
+ *
+ * @param v Vector to be subtracted.
+ * @return {@code this} - {@code v}.
+ * @throws DimensionMismatchException if {@code v} is not the same size as {@code this} vector.
+ */
+ public RealVector subtract(RealVector v) throws DimensionMismatchException {
+ checkVectorDimensions(v);
+ RealVector result = v.mapMultiply(-1d);
+ Iterator<Entry> it = iterator();
+ while (it.hasNext()) {
+ final Entry e = it.next();
+ final int index = e.getIndex();
+ result.setEntry(index, e.getValue() + result.getEntry(index));
+ }
+ return result;
+ }
+
+ /**
+ * Add a value to each entry. Returns a new vector. Does not change instance data.
+ *
+ * @param d Value to be added to each entry.
+ * @return {@code this} + {@code d}.
+ */
+ public RealVector mapAdd(double d) {
+ return copy().mapAddToSelf(d);
+ }
+
+ /**
+ * Add a value to each entry. The instance is changed in-place.
+ *
+ * @param d Value to be added to each entry.
+ * @return {@code this}.
+ */
+ public RealVector mapAddToSelf(double d) {
+ if (d != 0) {
+ return mapToSelf(FunctionUtils.fix2ndArgument(new Add(), d));
+ }
+ return this;
+ }
+
+ /**
+ * Returns a (deep) copy of this vector.
+ *
+ * @return a vector copy.
+ */
+ public abstract RealVector copy();
+
+ /**
+ * Compute the dot product of this vector with {@code v}.
+ *
+ * @param v Vector with which dot product should be computed
+ * @return the scalar dot product between this instance and {@code v}.
+ * @throws DimensionMismatchException if {@code v} is not the same size as {@code this} vector.
+ */
+ public double dotProduct(RealVector v) throws DimensionMismatchException {
+ checkVectorDimensions(v);
+ double d = 0;
+ final int n = getDimension();
+ for (int i = 0; i < n; i++) {
+ d += getEntry(i) * v.getEntry(i);
+ }
+ return d;
+ }
+
+ /**
+ * Computes the cosine of the angle between this vector and the argument.
+ *
+ * @param v Vector.
+ * @return the cosine of the angle between this vector and {@code v}.
+ * @throws MathArithmeticException if {@code this} or {@code v} is the null vector
+ * @throws DimensionMismatchException if the dimensions of {@code this} and {@code v} do not
+ * match
+ */
+ public double cosine(RealVector v) throws DimensionMismatchException, MathArithmeticException {
+ final double norm = getNorm();
+ final double vNorm = v.getNorm();
+
+ if (norm == 0 || vNorm == 0) {
+ throw new MathArithmeticException(LocalizedFormats.ZERO_NORM);
+ }
+ return dotProduct(v) / (norm * vNorm);
+ }
+
+ /**
+ * Element-by-element division.
+ *
+ * @param v Vector by which instance elements must be divided.
+ * @return a vector containing this[i] / v[i] for all i.
+ * @throws DimensionMismatchException if {@code v} is not the same size as {@code this} vector.
+ */
+ public abstract RealVector ebeDivide(RealVector v) throws DimensionMismatchException;
+
+ /**
+ * Element-by-element multiplication.
+ *
+ * @param v Vector by which instance elements must be multiplied
+ * @return a vector containing this[i] * v[i] for all i.
+ * @throws DimensionMismatchException if {@code v} is not the same size as {@code this} vector.
+ */
+ public abstract RealVector ebeMultiply(RealVector v) throws DimensionMismatchException;
+
+ /**
+ * Distance between two vectors.
+ *
+ * <p>This method computes the distance consistent with the L<sub>2</sub> norm, i.e. the square
+ * root of the sum of element differences, or Euclidean distance.
+ *
+ * @param v Vector to which distance is requested.
+ * @return the distance between two vectors.
+ * @throws DimensionMismatchException if {@code v} is not the same size as {@code this} vector.
+ * @see #getL1Distance(RealVector)
+ * @see #getLInfDistance(RealVector)
+ * @see #getNorm()
+ */
+ public double getDistance(RealVector v) throws DimensionMismatchException {
+ checkVectorDimensions(v);
+ double d = 0;
+ Iterator<Entry> it = iterator();
+ while (it.hasNext()) {
+ final Entry e = it.next();
+ final double diff = e.getValue() - v.getEntry(e.getIndex());
+ d += diff * diff;
+ }
+ return FastMath.sqrt(d);
+ }
+
+ /**
+ * Returns the L<sub>2</sub> norm of the vector.
+ *
+ * <p>The L<sub>2</sub> norm is the root of the sum of the squared elements.
+ *
+ * @return the norm.
+ * @see #getL1Norm()
+ * @see #getLInfNorm()
+ * @see #getDistance(RealVector)
+ */
+ public double getNorm() {
+ double sum = 0;
+ Iterator<Entry> it = iterator();
+ while (it.hasNext()) {
+ final Entry e = it.next();
+ final double value = e.getValue();
+ sum += value * value;
+ }
+ return FastMath.sqrt(sum);
+ }
+
+ /**
+ * Returns the L<sub>1</sub> norm of the vector.
+ *
+ * <p>The L<sub>1</sub> norm is the sum of the absolute values of the elements.
+ *
+ * @return the norm.
+ * @see #getNorm()
+ * @see #getLInfNorm()
+ * @see #getL1Distance(RealVector)
+ */
+ public double getL1Norm() {
+ double norm = 0;
+ Iterator<Entry> it = iterator();
+ while (it.hasNext()) {
+ final Entry e = it.next();
+ norm += FastMath.abs(e.getValue());
+ }
+ return norm;
+ }
+
+ /**
+ * Returns the L<sub>&infin;</sub> norm of the vector.
+ *
+ * <p>The L<sub>&infin;</sub> norm is the max of the absolute values of the elements.
+ *
+ * @return the norm.
+ * @see #getNorm()
+ * @see #getL1Norm()
+ * @see #getLInfDistance(RealVector)
+ */
+ public double getLInfNorm() {
+ double norm = 0;
+ Iterator<Entry> it = iterator();
+ while (it.hasNext()) {
+ final Entry e = it.next();
+ norm = FastMath.max(norm, FastMath.abs(e.getValue()));
+ }
+ return norm;
+ }
+
+ /**
+ * Distance between two vectors.
+ *
+ * <p>This method computes the distance consistent with L<sub>1</sub> norm, i.e. the sum of the
+ * absolute values of the elements differences.
+ *
+ * @param v Vector to which distance is requested.
+ * @return the distance between two vectors.
+ * @throws DimensionMismatchException if {@code v} is not the same size as {@code this} vector.
+ */
+ public double getL1Distance(RealVector v) throws DimensionMismatchException {
+ checkVectorDimensions(v);
+ double d = 0;
+ Iterator<Entry> it = iterator();
+ while (it.hasNext()) {
+ final Entry e = it.next();
+ d += FastMath.abs(e.getValue() - v.getEntry(e.getIndex()));
+ }
+ return d;
+ }
+
+ /**
+ * Distance between two vectors.
+ *
+ * <p>This method computes the distance consistent with L<sub>&infin;</sub> norm, i.e. the max
+ * of the absolute values of element differences.
+ *
+ * @param v Vector to which distance is requested.
+ * @return the distance between two vectors.
+ * @throws DimensionMismatchException if {@code v} is not the same size as {@code this} vector.
+ * @see #getDistance(RealVector)
+ * @see #getL1Distance(RealVector)
+ * @see #getLInfNorm()
+ */
+ public double getLInfDistance(RealVector v) throws DimensionMismatchException {
+ checkVectorDimensions(v);
+ double d = 0;
+ Iterator<Entry> it = iterator();
+ while (it.hasNext()) {
+ final Entry e = it.next();
+ d = FastMath.max(FastMath.abs(e.getValue() - v.getEntry(e.getIndex())), d);
+ }
+ return d;
+ }
+
+ /**
+ * Get the index of the minimum entry.
+ *
+ * @return the index of the minimum entry or -1 if vector length is 0 or all entries are {@code
+ * NaN}.
+ */
+ public int getMinIndex() {
+ int minIndex = -1;
+ double minValue = Double.POSITIVE_INFINITY;
+ Iterator<Entry> iterator = iterator();
+ while (iterator.hasNext()) {
+ final Entry entry = iterator.next();
+ if (entry.getValue() <= minValue) {
+ minIndex = entry.getIndex();
+ minValue = entry.getValue();
+ }
+ }
+ return minIndex;
+ }
+
+ /**
+ * Get the value of the minimum entry.
+ *
+ * @return the value of the minimum entry or {@code NaN} if all entries are {@code NaN}.
+ */
+ public double getMinValue() {
+ final int minIndex = getMinIndex();
+ return minIndex < 0 ? Double.NaN : getEntry(minIndex);
+ }
+
+ /**
+ * Get the index of the maximum entry.
+ *
+ * @return the index of the maximum entry or -1 if vector length is 0 or all entries are {@code
+ * NaN}
+ */
+ public int getMaxIndex() {
+ int maxIndex = -1;
+ double maxValue = Double.NEGATIVE_INFINITY;
+ Iterator<Entry> iterator = iterator();
+ while (iterator.hasNext()) {
+ final Entry entry = iterator.next();
+ if (entry.getValue() >= maxValue) {
+ maxIndex = entry.getIndex();
+ maxValue = entry.getValue();
+ }
+ }
+ return maxIndex;
+ }
+
+ /**
+ * Get the value of the maximum entry.
+ *
+ * @return the value of the maximum entry or {@code NaN} if all entries are {@code NaN}.
+ */
+ public double getMaxValue() {
+ final int maxIndex = getMaxIndex();
+ return maxIndex < 0 ? Double.NaN : getEntry(maxIndex);
+ }
+
+ /**
+ * Multiply each entry by the argument. Returns a new vector. Does not change instance data.
+ *
+ * @param d Multiplication factor.
+ * @return {@code this} * {@code d}.
+ */
+ public RealVector mapMultiply(double d) {
+ return copy().mapMultiplyToSelf(d);
+ }
+
+ /**
+ * Multiply each entry. The instance is changed in-place.
+ *
+ * @param d Multiplication factor.
+ * @return {@code this}.
+ */
+ public RealVector mapMultiplyToSelf(double d) {
+ return mapToSelf(FunctionUtils.fix2ndArgument(new Multiply(), d));
+ }
+
+ /**
+ * Subtract a value from each entry. Returns a new vector. Does not change instance data.
+ *
+ * @param d Value to be subtracted.
+ * @return {@code this} - {@code d}.
+ */
+ public RealVector mapSubtract(double d) {
+ return copy().mapSubtractToSelf(d);
+ }
+
+ /**
+ * Subtract a value from each entry. The instance is changed in-place.
+ *
+ * @param d Value to be subtracted.
+ * @return {@code this}.
+ */
+ public RealVector mapSubtractToSelf(double d) {
+ return mapAddToSelf(-d);
+ }
+
+ /**
+ * Divide each entry by the argument. Returns a new vector. Does not change instance data.
+ *
+ * @param d Value to divide by.
+ * @return {@code this} / {@code d}.
+ */
+ public RealVector mapDivide(double d) {
+ return copy().mapDivideToSelf(d);
+ }
+
+ /**
+ * Divide each entry by the argument. The instance is changed in-place.
+ *
+ * @param d Value to divide by.
+ * @return {@code this}.
+ */
+ public RealVector mapDivideToSelf(double d) {
+ return mapToSelf(FunctionUtils.fix2ndArgument(new Divide(), d));
+ }
+
+ /**
+ * Compute the outer product.
+ *
+ * @param v Vector with which outer product should be computed.
+ * @return the matrix outer product between this instance and {@code v}.
+ */
+ public RealMatrix outerProduct(RealVector v) {
+ final int m = this.getDimension();
+ final int n = v.getDimension();
+ final RealMatrix product;
+ if (v instanceof SparseRealVector || this instanceof SparseRealVector) {
+ product = new OpenMapRealMatrix(m, n);
+ } else {
+ product = new Array2DRowRealMatrix(m, n);
+ }
+ for (int i = 0; i < m; i++) {
+ for (int j = 0; j < n; j++) {
+ product.setEntry(i, j, this.getEntry(i) * v.getEntry(j));
+ }
+ }
+ return product;
+ }
+
+ /**
+ * Find the orthogonal projection of this vector onto another vector.
+ *
+ * @param v vector onto which instance must be projected.
+ * @return projection of the instance onto {@code v}.
+ * @throws DimensionMismatchException if {@code v} is not the same size as {@code this} vector.
+ * @throws MathArithmeticException if {@code this} or {@code v} is the null vector
+ */
+ public RealVector projection(final RealVector v)
+ throws DimensionMismatchException, MathArithmeticException {
+ final double norm2 = v.dotProduct(v);
+ if (norm2 == 0.0) {
+ throw new MathArithmeticException(LocalizedFormats.ZERO_NORM);
+ }
+ return v.mapMultiply(dotProduct(v) / v.dotProduct(v));
+ }
+
+ /**
+ * Set all elements to a single value.
+ *
+ * @param value Single value to set for all elements.
+ */
+ public void set(double value) {
+ Iterator<Entry> it = iterator();
+ while (it.hasNext()) {
+ final Entry e = it.next();
+ e.setValue(value);
+ }
+ }
+
+ /**
+ * Convert the vector to an array of {@code double}s. The array is independent from this vector
+ * data: the elements are copied.
+ *
+ * @return an array containing a copy of the vector elements.
+ */
+ public double[] toArray() {
+ int dim = getDimension();
+ double[] values = new double[dim];
+ for (int i = 0; i < dim; i++) {
+ values[i] = getEntry(i);
+ }
+ return values;
+ }
+
+ /**
+ * Creates a unit vector pointing in the direction of this vector. The instance is not changed
+ * by this method.
+ *
+ * @return a unit vector pointing in direction of this vector.
+ * @throws MathArithmeticException if the norm is zero.
+ */
+ public RealVector unitVector() throws MathArithmeticException {
+ final double norm = getNorm();
+ if (norm == 0) {
+ throw new MathArithmeticException(LocalizedFormats.ZERO_NORM);
+ }
+ return mapDivide(norm);
+ }
+
+ /**
+ * Converts this vector into a unit vector. The instance itself is changed by this method.
+ *
+ * @throws MathArithmeticException if the norm is zero.
+ */
+ public void unitize() throws MathArithmeticException {
+ final double norm = getNorm();
+ if (norm == 0) {
+ throw new MathArithmeticException(LocalizedFormats.ZERO_NORM);
+ }
+ mapDivideToSelf(getNorm());
+ }
+
+ /**
+ * Create a sparse iterator over the vector, which may omit some entries. The ommitted entries
+ * are either exact zeroes (for dense implementations) or are the entries which are not stored
+ * (for real sparse vectors). No guarantees are made about order of iteration.
+ *
+ * <p>Note: derived classes are required to return an {@link Iterator} that returns non-null
+ * {@link Entry} objects as long as {@link Iterator#hasNext()} returns {@code true}.
+ *
+ * @return a sparse iterator.
+ */
+ public Iterator<Entry> sparseIterator() {
+ return new SparseEntryIterator();
+ }
+
+ /**
+ * Generic dense iterator. Iteration is in increasing order of the vector index.
+ *
+ * <p>Note: derived classes are required to return an {@link Iterator} that returns non-null
+ * {@link Entry} objects as long as {@link Iterator#hasNext()} returns {@code true}.
+ *
+ * @return a dense iterator.
+ */
+ public Iterator<Entry> iterator() {
+ final int dim = getDimension();
+ return new Iterator<Entry>() {
+
+ /** Current index. */
+ private int i = 0;
+
+ /** Current entry. */
+ private Entry e = new Entry();
+
+ /** {@inheritDoc} */
+ public boolean hasNext() {
+ return i < dim;
+ }
+
+ /** {@inheritDoc} */
+ public Entry next() {
+ if (i < dim) {
+ e.setIndex(i++);
+ return e;
+ } else {
+ throw new NoSuchElementException();
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @throws MathUnsupportedOperationException in all circumstances.
+ */
+ public void remove() throws MathUnsupportedOperationException {
+ throw new MathUnsupportedOperationException();
+ }
+ };
+ }
+
+ /**
+ * Acts as if implemented as:
+ *
+ * <pre>
+ * return copy().mapToSelf(function);
+ * </pre>
+ *
+ * Returns a new vector. Does not change instance data.
+ *
+ * @param function Function to apply to each entry.
+ * @return a new vector.
+ */
+ public RealVector map(UnivariateFunction function) {
+ return copy().mapToSelf(function);
+ }
+
+ /**
+ * Acts as if it is implemented as:
+ *
+ * <pre>
+ * Entry e = null;
+ * for(Iterator<Entry> it = iterator(); it.hasNext(); e = it.next()) {
+ * e.setValue(function.value(e.getValue()));
+ * }
+ * </pre>
+ *
+ * Entries of this vector are modified in-place by this method.
+ *
+ * @param function Function to apply to each entry.
+ * @return a reference to this vector.
+ */
+ public RealVector mapToSelf(UnivariateFunction function) {
+ Iterator<Entry> it = iterator();
+ while (it.hasNext()) {
+ final Entry e = it.next();
+ e.setValue(function.value(e.getValue()));
+ }
+ return this;
+ }
+
+ /**
+ * Returns a new vector representing {@code a * this + b * y}, the linear combination of {@code
+ * this} and {@code y}. Returns a new vector. Does not change instance data.
+ *
+ * @param a Coefficient of {@code this}.
+ * @param b Coefficient of {@code y}.
+ * @param y Vector with which {@code this} is linearly combined.
+ * @return a vector containing {@code a * this[i] + b * y[i]} for all {@code i}.
+ * @throws DimensionMismatchException if {@code y} is not the same size as {@code this} vector.
+ */
+ public RealVector combine(double a, double b, RealVector y) throws DimensionMismatchException {
+ return copy().combineToSelf(a, b, y);
+ }
+
+ /**
+ * Updates {@code this} with the linear combination of {@code this} and {@code y}.
+ *
+ * @param a Weight of {@code this}.
+ * @param b Weight of {@code y}.
+ * @param y Vector with which {@code this} is linearly combined.
+ * @return {@code this}, with components equal to {@code a * this[i] + b * y[i]} for all {@code
+ * i}.
+ * @throws DimensionMismatchException if {@code y} is not the same size as {@code this} vector.
+ */
+ public RealVector combineToSelf(double a, double b, RealVector y)
+ throws DimensionMismatchException {
+ checkVectorDimensions(y);
+ for (int i = 0; i < getDimension(); i++) {
+ final double xi = getEntry(i);
+ final double yi = y.getEntry(i);
+ setEntry(i, a * xi + b * yi);
+ }
+ return this;
+ }
+
+ /**
+ * Visits (but does not alter) all entries of this vector in default order (increasing index).
+ *
+ * @param visitor the visitor to be used to process the entries of this vector
+ * @return the value returned by {@link RealVectorPreservingVisitor#end()} at the end of the
+ * walk
+ * @since 3.1
+ */
+ public double walkInDefaultOrder(final RealVectorPreservingVisitor visitor) {
+ final int dim = getDimension();
+ visitor.start(dim, 0, dim - 1);
+ for (int i = 0; i < dim; i++) {
+ visitor.visit(i, getEntry(i));
+ }
+ return visitor.end();
+ }
+
+ /**
+ * Visits (but does not alter) some entries of this vector in default order (increasing index).
+ *
+ * @param visitor visitor to be used to process the entries of this vector
+ * @param start the index of the first entry to be visited
+ * @param end the index of the last entry to be visited (inclusive)
+ * @return the value returned by {@link RealVectorPreservingVisitor#end()} at the end of the
+ * walk
+ * @throws NumberIsTooSmallException if {@code end < start}.
+ * @throws OutOfRangeException if the indices are not valid.
+ * @since 3.1
+ */
+ public double walkInDefaultOrder(
+ final RealVectorPreservingVisitor visitor, final int start, final int end)
+ throws NumberIsTooSmallException, OutOfRangeException {
+ checkIndices(start, end);
+ visitor.start(getDimension(), start, end);
+ for (int i = start; i <= end; i++) {
+ visitor.visit(i, getEntry(i));
+ }
+ return visitor.end();
+ }
+
+ /**
+ * Visits (but does not alter) all entries of this vector in optimized order. The order in which
+ * the entries are visited is selected so as to lead to the most efficient implementation; it
+ * might depend on the concrete implementation of this abstract class.
+ *
+ * @param visitor the visitor to be used to process the entries of this vector
+ * @return the value returned by {@link RealVectorPreservingVisitor#end()} at the end of the
+ * walk
+ * @since 3.1
+ */
+ public double walkInOptimizedOrder(final RealVectorPreservingVisitor visitor) {
+ return walkInDefaultOrder(visitor);
+ }
+
+ /**
+ * Visits (but does not alter) some entries of this vector in optimized order. The order in
+ * which the entries are visited is selected so as to lead to the most efficient implementation;
+ * it might depend on the concrete implementation of this abstract class.
+ *
+ * @param visitor visitor to be used to process the entries of this vector
+ * @param start the index of the first entry to be visited
+ * @param end the index of the last entry to be visited (inclusive)
+ * @return the value returned by {@link RealVectorPreservingVisitor#end()} at the end of the
+ * walk
+ * @throws NumberIsTooSmallException if {@code end < start}.
+ * @throws OutOfRangeException if the indices are not valid.
+ * @since 3.1
+ */
+ public double walkInOptimizedOrder(
+ final RealVectorPreservingVisitor visitor, final int start, final int end)
+ throws NumberIsTooSmallException, OutOfRangeException {
+ return walkInDefaultOrder(visitor, start, end);
+ }
+
+ /**
+ * Visits (and possibly alters) all entries of this vector in default order (increasing index).
+ *
+ * @param visitor the visitor to be used to process and modify the entries of this vector
+ * @return the value returned by {@link RealVectorChangingVisitor#end()} at the end of the walk
+ * @since 3.1
+ */
+ public double walkInDefaultOrder(final RealVectorChangingVisitor visitor) {
+ final int dim = getDimension();
+ visitor.start(dim, 0, dim - 1);
+ for (int i = 0; i < dim; i++) {
+ setEntry(i, visitor.visit(i, getEntry(i)));
+ }
+ return visitor.end();
+ }
+
+ /**
+ * Visits (and possibly alters) some entries of this vector in default order (increasing index).
+ *
+ * @param visitor visitor to be used to process the entries of this vector
+ * @param start the index of the first entry to be visited
+ * @param end the index of the last entry to be visited (inclusive)
+ * @return the value returned by {@link RealVectorChangingVisitor#end()} at the end of the walk
+ * @throws NumberIsTooSmallException if {@code end < start}.
+ * @throws OutOfRangeException if the indices are not valid.
+ * @since 3.1
+ */
+ public double walkInDefaultOrder(
+ final RealVectorChangingVisitor visitor, final int start, final int end)
+ throws NumberIsTooSmallException, OutOfRangeException {
+ checkIndices(start, end);
+ visitor.start(getDimension(), start, end);
+ for (int i = start; i <= end; i++) {
+ setEntry(i, visitor.visit(i, getEntry(i)));
+ }
+ return visitor.end();
+ }
+
+ /**
+ * Visits (and possibly alters) all entries of this vector in optimized order. The order in
+ * which the entries are visited is selected so as to lead to the most efficient implementation;
+ * it might depend on the concrete implementation of this abstract class.
+ *
+ * @param visitor the visitor to be used to process the entries of this vector
+ * @return the value returned by {@link RealVectorChangingVisitor#end()} at the end of the walk
+ * @since 3.1
+ */
+ public double walkInOptimizedOrder(final RealVectorChangingVisitor visitor) {
+ return walkInDefaultOrder(visitor);
+ }
+
+ /**
+ * Visits (and possibly change) some entries of this vector in optimized order. The order in
+ * which the entries are visited is selected so as to lead to the most efficient implementation;
+ * it might depend on the concrete implementation of this abstract class.
+ *
+ * @param visitor visitor to be used to process the entries of this vector
+ * @param start the index of the first entry to be visited
+ * @param end the index of the last entry to be visited (inclusive)
+ * @return the value returned by {@link RealVectorChangingVisitor#end()} at the end of the walk
+ * @throws NumberIsTooSmallException if {@code end < start}.
+ * @throws OutOfRangeException if the indices are not valid.
+ * @since 3.1
+ */
+ public double walkInOptimizedOrder(
+ final RealVectorChangingVisitor visitor, final int start, final int end)
+ throws NumberIsTooSmallException, OutOfRangeException {
+ return walkInDefaultOrder(visitor, start, end);
+ }
+
+ /** An entry in the vector. */
+ protected class Entry {
+ /** Index of this entry. */
+ private int index;
+
+ /** Simple constructor. */
+ public Entry() {
+ setIndex(0);
+ }
+
+ /**
+ * Get the value of the entry.
+ *
+ * @return the value of the entry.
+ */
+ public double getValue() {
+ return getEntry(getIndex());
+ }
+
+ /**
+ * Set the value of the entry.
+ *
+ * @param value New value for the entry.
+ */
+ public void setValue(double value) {
+ setEntry(getIndex(), value);
+ }
+
+ /**
+ * Get the index of the entry.
+ *
+ * @return the index of the entry.
+ */
+ public int getIndex() {
+ return index;
+ }
+
+ /**
+ * Set the index of the entry.
+ *
+ * @param index New index for the entry.
+ */
+ public void setIndex(int index) {
+ this.index = index;
+ }
+ }
+
+ /**
+ * Test for the equality of two real vectors. If all coordinates of two real vectors are exactly
+ * the same, and none are {@code NaN}, the two real vectors are considered to be equal. {@code
+ * NaN} coordinates are considered to affect globally the vector and be equals to each other -
+ * i.e, if either (or all) coordinates of the real vector are equal to {@code NaN}, the real
+ * vector is equal to a vector with all {@code NaN} coordinates.
+ *
+ * <p>This method <em>must</em> be overriden by concrete subclasses of {@link RealVector} (the
+ * current implementation throws an exception).
+ *
+ * @param other Object to test for equality.
+ * @return {@code true} if two vector objects are equal, {@code false} if {@code other} is null,
+ * not an instance of {@code RealVector}, or not equal to this {@code RealVector} instance.
+ * @throws MathUnsupportedOperationException if this method is not overridden.
+ */
+ @Override
+ public boolean equals(Object other) throws MathUnsupportedOperationException {
+ throw new MathUnsupportedOperationException();
+ }
+
+ /**
+ * {@inheritDoc}. This method <em>must</em> be overriden by concrete subclasses of {@link
+ * RealVector} (current implementation throws an exception).
+ *
+ * @throws MathUnsupportedOperationException if this method is not overridden.
+ */
+ @Override
+ public int hashCode() throws MathUnsupportedOperationException {
+ throw new MathUnsupportedOperationException();
+ }
+
+ /**
+ * This class should rarely be used, but is here to provide a default implementation of
+ * sparseIterator(), which is implemented by walking over the entries, skipping those that are
+ * zero.
+ *
+ * <p>Concrete subclasses which are SparseVector implementations should make their own sparse
+ * iterator, rather than using this one.
+ *
+ * <p>This implementation might be useful for ArrayRealVector, when expensive operations which
+ * preserve the default value are to be done on the entries, and the fraction of non-default
+ * values is small (i.e. someone took a SparseVector, and passed it into the copy-constructor of
+ * ArrayRealVector)
+ */
+ protected class SparseEntryIterator implements Iterator<Entry> {
+ /** Dimension of the vector. */
+ private final int dim;
+
+ /** Last entry returned by {@link #next()}. */
+ private Entry current;
+
+ /** Next entry for {@link #next()} to return. */
+ private Entry next;
+
+ /** Simple constructor. */
+ protected SparseEntryIterator() {
+ dim = getDimension();
+ current = new Entry();
+ next = new Entry();
+ if (next.getValue() == 0) {
+ advance(next);
+ }
+ }
+
+ /**
+ * Advance an entry up to the next nonzero one.
+ *
+ * @param e entry to advance.
+ */
+ protected void advance(Entry e) {
+ if (e == null) {
+ return;
+ }
+ do {
+ e.setIndex(e.getIndex() + 1);
+ } while (e.getIndex() < dim && e.getValue() == 0);
+ if (e.getIndex() >= dim) {
+ e.setIndex(-1);
+ }
+ }
+
+ /** {@inheritDoc} */
+ public boolean hasNext() {
+ return next.getIndex() >= 0;
+ }
+
+ /** {@inheritDoc} */
+ public Entry next() {
+ int index = next.getIndex();
+ if (index < 0) {
+ throw new NoSuchElementException();
+ }
+ current.setIndex(index);
+ advance(next);
+ return current;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @throws MathUnsupportedOperationException in all circumstances.
+ */
+ public void remove() throws MathUnsupportedOperationException {
+ throw new MathUnsupportedOperationException();
+ }
+ }
+
+ /**
+ * Returns an unmodifiable view of the specified vector. The returned vector has read-only
+ * access. An attempt to modify it will result in a {@link MathUnsupportedOperationException}.
+ * However, the returned vector is <em>not</em> immutable, since any modification of {@code v}
+ * will also change the returned view. For example, in the following piece of code
+ *
+ * <pre>
+ * RealVector v = new ArrayRealVector(2);
+ * RealVector w = RealVector.unmodifiableRealVector(v);
+ * v.setEntry(0, 1.2);
+ * v.setEntry(1, -3.4);
+ * </pre>
+ *
+ * the changes will be seen in the {@code w} view of {@code v}.
+ *
+ * @param v Vector for which an unmodifiable view is to be returned.
+ * @return an unmodifiable view of {@code v}.
+ */
+ public static RealVector unmodifiableRealVector(final RealVector v) {
+ /**
+ * This anonymous class is an implementation of {@link RealVector} with read-only access. It
+ * wraps any {@link RealVector}, and exposes all methods which do not modify it. Invoking
+ * methods which should normally result in the modification of the calling {@link
+ * RealVector} results in a {@link MathUnsupportedOperationException}. It should be noted
+ * that {@link UnmodifiableVector} is <em>not</em> immutable.
+ */
+ return new RealVector() {
+ /**
+ * {@inheritDoc}
+ *
+ * @throws MathUnsupportedOperationException in all circumstances.
+ */
+ @Override
+ public RealVector mapToSelf(UnivariateFunction function)
+ throws MathUnsupportedOperationException {
+ throw new MathUnsupportedOperationException();
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public RealVector map(UnivariateFunction function) {
+ return v.map(function);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public Iterator<Entry> iterator() {
+ final Iterator<Entry> i = v.iterator();
+ return new Iterator<Entry>() {
+ /** The current entry. */
+ private final UnmodifiableEntry e = new UnmodifiableEntry();
+
+ /** {@inheritDoc} */
+ public boolean hasNext() {
+ return i.hasNext();
+ }
+
+ /** {@inheritDoc} */
+ public Entry next() {
+ e.setIndex(i.next().getIndex());
+ return e;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @throws MathUnsupportedOperationException in all circumstances.
+ */
+ public void remove() throws MathUnsupportedOperationException {
+ throw new MathUnsupportedOperationException();
+ }
+ };
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public Iterator<Entry> sparseIterator() {
+ final Iterator<Entry> i = v.sparseIterator();
+
+ return new Iterator<Entry>() {
+ /** The current entry. */
+ private final UnmodifiableEntry e = new UnmodifiableEntry();
+
+ /** {@inheritDoc} */
+ public boolean hasNext() {
+ return i.hasNext();
+ }
+
+ /** {@inheritDoc} */
+ public Entry next() {
+ e.setIndex(i.next().getIndex());
+ return e;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @throws MathUnsupportedOperationException in all circumstances.
+ */
+ public void remove() throws MathUnsupportedOperationException {
+ throw new MathUnsupportedOperationException();
+ }
+ };
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public RealVector copy() {
+ return v.copy();
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public RealVector add(RealVector w) throws DimensionMismatchException {
+ return v.add(w);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public RealVector subtract(RealVector w) throws DimensionMismatchException {
+ return v.subtract(w);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public RealVector mapAdd(double d) {
+ return v.mapAdd(d);
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @throws MathUnsupportedOperationException in all circumstances.
+ */
+ @Override
+ public RealVector mapAddToSelf(double d) throws MathUnsupportedOperationException {
+ throw new MathUnsupportedOperationException();
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public RealVector mapSubtract(double d) {
+ return v.mapSubtract(d);
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @throws MathUnsupportedOperationException in all circumstances.
+ */
+ @Override
+ public RealVector mapSubtractToSelf(double d) throws MathUnsupportedOperationException {
+ throw new MathUnsupportedOperationException();
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public RealVector mapMultiply(double d) {
+ return v.mapMultiply(d);
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @throws MathUnsupportedOperationException in all circumstances.
+ */
+ @Override
+ public RealVector mapMultiplyToSelf(double d) throws MathUnsupportedOperationException {
+ throw new MathUnsupportedOperationException();
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public RealVector mapDivide(double d) {
+ return v.mapDivide(d);
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @throws MathUnsupportedOperationException in all circumstances.
+ */
+ @Override
+ public RealVector mapDivideToSelf(double d) throws MathUnsupportedOperationException {
+ throw new MathUnsupportedOperationException();
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public RealVector ebeMultiply(RealVector w) throws DimensionMismatchException {
+ return v.ebeMultiply(w);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public RealVector ebeDivide(RealVector w) throws DimensionMismatchException {
+ return v.ebeDivide(w);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public double dotProduct(RealVector w) throws DimensionMismatchException {
+ return v.dotProduct(w);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public double cosine(RealVector w)
+ throws DimensionMismatchException, MathArithmeticException {
+ return v.cosine(w);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public double getNorm() {
+ return v.getNorm();
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public double getL1Norm() {
+ return v.getL1Norm();
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public double getLInfNorm() {
+ return v.getLInfNorm();
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public double getDistance(RealVector w) throws DimensionMismatchException {
+ return v.getDistance(w);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public double getL1Distance(RealVector w) throws DimensionMismatchException {
+ return v.getL1Distance(w);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public double getLInfDistance(RealVector w) throws DimensionMismatchException {
+ return v.getLInfDistance(w);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public RealVector unitVector() throws MathArithmeticException {
+ return v.unitVector();
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @throws MathUnsupportedOperationException in all circumstances.
+ */
+ @Override
+ public void unitize() throws MathUnsupportedOperationException {
+ throw new MathUnsupportedOperationException();
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public RealMatrix outerProduct(RealVector w) {
+ return v.outerProduct(w);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public double getEntry(int index) throws OutOfRangeException {
+ return v.getEntry(index);
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @throws MathUnsupportedOperationException in all circumstances.
+ */
+ @Override
+ public void setEntry(int index, double value) throws MathUnsupportedOperationException {
+ throw new MathUnsupportedOperationException();
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @throws MathUnsupportedOperationException in all circumstances.
+ */
+ @Override
+ public void addToEntry(int index, double value)
+ throws MathUnsupportedOperationException {
+ throw new MathUnsupportedOperationException();
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public int getDimension() {
+ return v.getDimension();
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public RealVector append(RealVector w) {
+ return v.append(w);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public RealVector append(double d) {
+ return v.append(d);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public RealVector getSubVector(int index, int n)
+ throws OutOfRangeException, NotPositiveException {
+ return v.getSubVector(index, n);
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @throws MathUnsupportedOperationException in all circumstances.
+ */
+ @Override
+ public void setSubVector(int index, RealVector w)
+ throws MathUnsupportedOperationException {
+ throw new MathUnsupportedOperationException();
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @throws MathUnsupportedOperationException in all circumstances.
+ */
+ @Override
+ public void set(double value) throws MathUnsupportedOperationException {
+ throw new MathUnsupportedOperationException();
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public double[] toArray() {
+ return v.toArray();
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public boolean isNaN() {
+ return v.isNaN();
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public boolean isInfinite() {
+ return v.isInfinite();
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public RealVector combine(double a, double b, RealVector y)
+ throws DimensionMismatchException {
+ return v.combine(a, b, y);
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @throws MathUnsupportedOperationException in all circumstances.
+ */
+ @Override
+ public RealVector combineToSelf(double a, double b, RealVector y)
+ throws MathUnsupportedOperationException {
+ throw new MathUnsupportedOperationException();
+ }
+
+ /** An entry in the vector. */
+ class UnmodifiableEntry extends Entry {
+ /** {@inheritDoc} */
+ @Override
+ public double getValue() {
+ return v.getEntry(getIndex());
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @throws MathUnsupportedOperationException in all circumstances.
+ */
+ @Override
+ public void setValue(double value) throws MathUnsupportedOperationException {
+ throw new MathUnsupportedOperationException();
+ }
+ }
+ };
+ }
+}
diff --git a/src/main/java/org/apache/commons/math3/linear/RealVectorChangingVisitor.java b/src/main/java/org/apache/commons/math3/linear/RealVectorChangingVisitor.java
new file mode 100644
index 0000000..8e8597a
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/linear/RealVectorChangingVisitor.java
@@ -0,0 +1,56 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math3.linear;
+
+/**
+ * This interface defines a visitor for the entries of a vector. Visitors implementing this
+ * interface may alter the entries of the vector being visited.
+ *
+ * @since 3.1
+ */
+public interface RealVectorChangingVisitor {
+ /**
+ * Start visiting a vector. This method is called once, before any entry of the vector is
+ * visited.
+ *
+ * @param dimension the size of the vector
+ * @param start the index of the first entry to be visited
+ * @param end the index of the last entry to be visited (inclusive)
+ */
+ void start(int dimension, int start, int end);
+
+ /**
+ * Visit one entry of the vector.
+ *
+ * @param index the index of the entry being visited
+ * @param value the value of the entry being visited
+ * @return the new value of the entry being visited
+ */
+ double visit(int index, double value);
+
+ /**
+ * End visiting a vector. This method is called once, after all entries of the vector have been
+ * visited.
+ *
+ * @return the value returned by {@link
+ * RealVector#walkInDefaultOrder(RealVectorChangingVisitor)}, {@link
+ * RealVector#walkInDefaultOrder(RealVectorChangingVisitor, int, int)}, {@link
+ * RealVector#walkInOptimizedOrder(RealVectorChangingVisitor)} or {@link
+ * RealVector#walkInOptimizedOrder(RealVectorChangingVisitor, int, int)}
+ */
+ double end();
+}
diff --git a/src/main/java/org/apache/commons/math3/linear/RealVectorFormat.java b/src/main/java/org/apache/commons/math3/linear/RealVectorFormat.java
new file mode 100644
index 0000000..addf1ca
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/linear/RealVectorFormat.java
@@ -0,0 +1,310 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math3.linear;
+
+import org.apache.commons.math3.exception.MathParseException;
+import org.apache.commons.math3.util.CompositeFormat;
+
+import java.text.FieldPosition;
+import java.text.NumberFormat;
+import java.text.ParsePosition;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Locale;
+
+/**
+ * Formats a vector in components list format "{v0; v1; ...; vk-1}".
+ *
+ * <p>The prefix and suffix "{" and "}" and the separator "; " can be replaced by any user-defined
+ * strings. The number format for components can be configured.
+ *
+ * <p>White space is ignored at parse time, even if it is in the prefix, suffix or separator
+ * specifications. So even if the default separator does include a space character that is used at
+ * format time, both input string "{1;1;1}" and " { 1 ; 1 ; 1 } " will be parsed without error and
+ * the same vector will be returned. In the second case, however, the parse position after parsing
+ * will be just after the closing curly brace, i.e. just before the trailing space.
+ *
+ * @since 2.0
+ */
+public class RealVectorFormat {
+
+ /** The default prefix: "{". */
+ private static final String DEFAULT_PREFIX = "{";
+
+ /** The default suffix: "}". */
+ private static final String DEFAULT_SUFFIX = "}";
+
+ /** The default separator: ", ". */
+ private static final String DEFAULT_SEPARATOR = "; ";
+
+ /** Prefix. */
+ private final String prefix;
+
+ /** Suffix. */
+ private final String suffix;
+
+ /** Separator. */
+ private final String separator;
+
+ /** Trimmed prefix. */
+ private final String trimmedPrefix;
+
+ /** Trimmed suffix. */
+ private final String trimmedSuffix;
+
+ /** Trimmed separator. */
+ private final String trimmedSeparator;
+
+ /** The format used for components. */
+ private final NumberFormat format;
+
+ /**
+ * Create an instance with default settings.
+ *
+ * <p>The instance uses the default prefix, suffix and separator: "{", "}", and "; " and the
+ * default number format for components.
+ */
+ public RealVectorFormat() {
+ this(
+ DEFAULT_PREFIX,
+ DEFAULT_SUFFIX,
+ DEFAULT_SEPARATOR,
+ CompositeFormat.getDefaultNumberFormat());
+ }
+
+ /**
+ * Create an instance with a custom number format for components.
+ *
+ * @param format the custom format for components.
+ */
+ public RealVectorFormat(final NumberFormat format) {
+ this(DEFAULT_PREFIX, DEFAULT_SUFFIX, DEFAULT_SEPARATOR, format);
+ }
+
+ /**
+ * Create an instance with custom prefix, suffix and separator.
+ *
+ * @param prefix prefix to use instead of the default "{"
+ * @param suffix suffix to use instead of the default "}"
+ * @param separator separator to use instead of the default "; "
+ */
+ public RealVectorFormat(final String prefix, final String suffix, final String separator) {
+ this(prefix, suffix, separator, CompositeFormat.getDefaultNumberFormat());
+ }
+
+ /**
+ * Create an instance with custom prefix, suffix, separator and format for components.
+ *
+ * @param prefix prefix to use instead of the default "{"
+ * @param suffix suffix to use instead of the default "}"
+ * @param separator separator to use instead of the default "; "
+ * @param format the custom format for components.
+ */
+ public RealVectorFormat(
+ final String prefix,
+ final String suffix,
+ final String separator,
+ final NumberFormat format) {
+ this.prefix = prefix;
+ this.suffix = suffix;
+ this.separator = separator;
+ trimmedPrefix = prefix.trim();
+ trimmedSuffix = suffix.trim();
+ trimmedSeparator = separator.trim();
+ this.format = format;
+ }
+
+ /**
+ * Get the set of locales for which real vectors formats are available.
+ *
+ * <p>This is the same set as the {@link NumberFormat} set.
+ *
+ * @return available real vector format locales.
+ */
+ public static Locale[] getAvailableLocales() {
+ return NumberFormat.getAvailableLocales();
+ }
+
+ /**
+ * Get the format prefix.
+ *
+ * @return format prefix.
+ */
+ public String getPrefix() {
+ return prefix;
+ }
+
+ /**
+ * Get the format suffix.
+ *
+ * @return format suffix.
+ */
+ public String getSuffix() {
+ return suffix;
+ }
+
+ /**
+ * Get the format separator between components.
+ *
+ * @return format separator.
+ */
+ public String getSeparator() {
+ return separator;
+ }
+
+ /**
+ * Get the components format.
+ *
+ * @return components format.
+ */
+ public NumberFormat getFormat() {
+ return format;
+ }
+
+ /**
+ * Returns the default real vector format for the current locale.
+ *
+ * @return the default real vector format.
+ */
+ public static RealVectorFormat getInstance() {
+ return getInstance(Locale.getDefault());
+ }
+
+ /**
+ * Returns the default real vector format for the given locale.
+ *
+ * @param locale the specific locale used by the format.
+ * @return the real vector format specific to the given locale.
+ */
+ public static RealVectorFormat getInstance(final Locale locale) {
+ return new RealVectorFormat(CompositeFormat.getDefaultNumberFormat(locale));
+ }
+
+ /**
+ * This method calls {@link #format(RealVector,StringBuffer,FieldPosition)}.
+ *
+ * @param v RealVector object to format.
+ * @return a formatted vector.
+ */
+ public String format(RealVector v) {
+ return format(v, new StringBuffer(), new FieldPosition(0)).toString();
+ }
+
+ /**
+ * Formats a {@link RealVector} object to produce a string.
+ *
+ * @param vector the object to format.
+ * @param toAppendTo where the text is to be appended
+ * @param pos On input: an alignment field, if desired. On output: the offsets of the alignment
+ * field
+ * @return the value passed in as toAppendTo.
+ */
+ public StringBuffer format(RealVector vector, StringBuffer toAppendTo, FieldPosition pos) {
+
+ pos.setBeginIndex(0);
+ pos.setEndIndex(0);
+
+ // format prefix
+ toAppendTo.append(prefix);
+
+ // format components
+ for (int i = 0; i < vector.getDimension(); ++i) {
+ if (i > 0) {
+ toAppendTo.append(separator);
+ }
+ CompositeFormat.formatDouble(vector.getEntry(i), format, toAppendTo, pos);
+ }
+
+ // format suffix
+ toAppendTo.append(suffix);
+
+ return toAppendTo;
+ }
+
+ /**
+ * Parse a string to produce a {@link RealVector} object.
+ *
+ * @param source String to parse.
+ * @return the parsed {@link RealVector} object.
+ * @throws MathParseException if the beginning of the specified string cannot be parsed.
+ */
+ public ArrayRealVector parse(String source) {
+ final ParsePosition parsePosition = new ParsePosition(0);
+ final ArrayRealVector result = parse(source, parsePosition);
+ if (parsePosition.getIndex() == 0) {
+ throw new MathParseException(
+ source, parsePosition.getErrorIndex(), ArrayRealVector.class);
+ }
+ return result;
+ }
+
+ /**
+ * Parse a string to produce a {@link RealVector} object.
+ *
+ * @param source String to parse.
+ * @param pos input/ouput parsing parameter.
+ * @return the parsed {@link RealVector} object.
+ */
+ public ArrayRealVector parse(String source, ParsePosition pos) {
+ int initialIndex = pos.getIndex();
+
+ // parse prefix
+ CompositeFormat.parseAndIgnoreWhitespace(source, pos);
+ if (!CompositeFormat.parseFixedstring(source, trimmedPrefix, pos)) {
+ return null;
+ }
+
+ // parse components
+ List<Number> components = new ArrayList<Number>();
+ for (boolean loop = true; loop; ) {
+
+ if (!components.isEmpty()) {
+ CompositeFormat.parseAndIgnoreWhitespace(source, pos);
+ if (!CompositeFormat.parseFixedstring(source, trimmedSeparator, pos)) {
+ loop = false;
+ }
+ }
+
+ if (loop) {
+ CompositeFormat.parseAndIgnoreWhitespace(source, pos);
+ Number component = CompositeFormat.parseNumber(source, format, pos);
+ if (component != null) {
+ components.add(component);
+ } else {
+ // invalid component
+ // set index back to initial, error index should already be set
+ pos.setIndex(initialIndex);
+ return null;
+ }
+ }
+ }
+
+ // parse suffix
+ CompositeFormat.parseAndIgnoreWhitespace(source, pos);
+ if (!CompositeFormat.parseFixedstring(source, trimmedSuffix, pos)) {
+ return null;
+ }
+
+ // build vector
+ double[] data = new double[components.size()];
+ for (int i = 0; i < data.length; ++i) {
+ data[i] = components.get(i).doubleValue();
+ }
+ return new ArrayRealVector(data, false);
+ }
+}
diff --git a/src/main/java/org/apache/commons/math3/linear/RealVectorPreservingVisitor.java b/src/main/java/org/apache/commons/math3/linear/RealVectorPreservingVisitor.java
new file mode 100644
index 0000000..b1118d6
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/linear/RealVectorPreservingVisitor.java
@@ -0,0 +1,55 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math3.linear;
+
+/**
+ * This interface defines a visitor for the entries of a vector. Visitors implementing this
+ * interface do not alter the entries of the vector being visited.
+ *
+ * @since 3.1
+ */
+public interface RealVectorPreservingVisitor {
+ /**
+ * Start visiting a vector. This method is called once, before any entry of the vector is
+ * visited.
+ *
+ * @param dimension the size of the vector
+ * @param start the index of the first entry to be visited
+ * @param end the index of the last entry to be visited (inclusive)
+ */
+ void start(int dimension, int start, int end);
+
+ /**
+ * Visit one entry of the vector.
+ *
+ * @param index the index of the entry being visited
+ * @param value the value of the entry being visited
+ */
+ void visit(int index, double value);
+
+ /**
+ * End visiting a vector. This method is called once, after all entries of the vector have been
+ * visited.
+ *
+ * @return the value returned by {@link
+ * RealVector#walkInDefaultOrder(RealVectorPreservingVisitor)}, {@link
+ * RealVector#walkInDefaultOrder(RealVectorPreservingVisitor, int, int)}, {@link
+ * RealVector#walkInOptimizedOrder(RealVectorPreservingVisitor)} or {@link
+ * RealVector#walkInOptimizedOrder(RealVectorPreservingVisitor, int, int)}
+ */
+ double end();
+}
diff --git a/src/main/java/org/apache/commons/math3/linear/RectangularCholeskyDecomposition.java b/src/main/java/org/apache/commons/math3/linear/RectangularCholeskyDecomposition.java
new file mode 100644
index 0000000..20eff16
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/linear/RectangularCholeskyDecomposition.java
@@ -0,0 +1,191 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math3.linear;
+
+import org.apache.commons.math3.util.FastMath;
+
+/**
+ * Calculates the rectangular Cholesky decomposition of a matrix.
+ *
+ * <p>The rectangular Cholesky decomposition of a real symmetric positive semidefinite matrix A
+ * consists of a rectangular matrix B with the same number of rows such that: A is almost equal to
+ * BB<sup>T</sup>, depending on a user-defined tolerance. In a sense, this is the square root of A.
+ *
+ * <p>The difference with respect to the regular {@link CholeskyDecomposition} is that rows/columns
+ * may be permuted (hence the rectangular shape instead of the traditional triangular shape) and
+ * there is a threshold to ignore small diagonal elements. This is used for example to generate
+ * {@link org.apache.commons.math3.random.CorrelatedRandomVectorGenerator correlated random
+ * n-dimensions vectors} in a p-dimension subspace (p < n). In other words, it allows generating
+ * random vectors from a covariance matrix that is only positive semidefinite, and not positive
+ * definite.
+ *
+ * <p>Rectangular Cholesky decomposition is <em>not</em> suited for solving linear systems, so it
+ * does not provide any {@link DecompositionSolver decomposition solver}.
+ *
+ * @see <a href="http://mathworld.wolfram.com/CholeskyDecomposition.html">MathWorld</a>
+ * @see <a href="http://en.wikipedia.org/wiki/Cholesky_decomposition">Wikipedia</a>
+ * @since 2.0 (changed to concrete class in 3.0)
+ */
+public class RectangularCholeskyDecomposition {
+
+ /** Permutated Cholesky root of the symmetric positive semidefinite matrix. */
+ private final RealMatrix root;
+
+ /** Rank of the symmetric positive semidefinite matrix. */
+ private int rank;
+
+ /**
+ * Decompose a symmetric positive semidefinite matrix.
+ *
+ * <p><b>Note:</b> this constructor follows the linpack method to detect dependent columns by
+ * proceeding with the Cholesky algorithm until a nonpositive diagonal element is encountered.
+ *
+ * @see <a href="http://eprints.ma.man.ac.uk/1193/01/covered/MIMS_ep2008_56.pdf">Analysis of the
+ * Cholesky Decomposition of a Semi-definite Matrix</a>
+ * @param matrix Symmetric positive semidefinite matrix.
+ * @exception NonPositiveDefiniteMatrixException if the matrix is not positive semidefinite.
+ * @since 3.1
+ */
+ public RectangularCholeskyDecomposition(RealMatrix matrix)
+ throws NonPositiveDefiniteMatrixException {
+ this(matrix, 0);
+ }
+
+ /**
+ * Decompose a symmetric positive semidefinite matrix.
+ *
+ * @param matrix Symmetric positive semidefinite matrix.
+ * @param small Diagonal elements threshold under which columns are considered to be dependent
+ * on previous ones and are discarded.
+ * @exception NonPositiveDefiniteMatrixException if the matrix is not positive semidefinite.
+ */
+ public RectangularCholeskyDecomposition(RealMatrix matrix, double small)
+ throws NonPositiveDefiniteMatrixException {
+
+ final int order = matrix.getRowDimension();
+ final double[][] c = matrix.getData();
+ final double[][] b = new double[order][order];
+
+ int[] index = new int[order];
+ for (int i = 0; i < order; ++i) {
+ index[i] = i;
+ }
+
+ int r = 0;
+ for (boolean loop = true; loop; ) {
+
+ // find maximal diagonal element
+ int swapR = r;
+ for (int i = r + 1; i < order; ++i) {
+ int ii = index[i];
+ int isr = index[swapR];
+ if (c[ii][ii] > c[isr][isr]) {
+ swapR = i;
+ }
+ }
+
+ // swap elements
+ if (swapR != r) {
+ final int tmpIndex = index[r];
+ index[r] = index[swapR];
+ index[swapR] = tmpIndex;
+ final double[] tmpRow = b[r];
+ b[r] = b[swapR];
+ b[swapR] = tmpRow;
+ }
+
+ // check diagonal element
+ int ir = index[r];
+ if (c[ir][ir] <= small) {
+
+ if (r == 0) {
+ throw new NonPositiveDefiniteMatrixException(c[ir][ir], ir, small);
+ }
+
+ // check remaining diagonal elements
+ for (int i = r; i < order; ++i) {
+ if (c[index[i]][index[i]] < -small) {
+ // there is at least one sufficiently negative diagonal element,
+ // the symmetric positive semidefinite matrix is wrong
+ throw new NonPositiveDefiniteMatrixException(
+ c[index[i]][index[i]], i, small);
+ }
+ }
+
+ // all remaining diagonal elements are close to zero, we consider we have
+ // found the rank of the symmetric positive semidefinite matrix
+ loop = false;
+
+ } else {
+
+ // transform the matrix
+ final double sqrt = FastMath.sqrt(c[ir][ir]);
+ b[r][r] = sqrt;
+ final double inverse = 1 / sqrt;
+ final double inverse2 = 1 / c[ir][ir];
+ for (int i = r + 1; i < order; ++i) {
+ final int ii = index[i];
+ final double e = inverse * c[ii][ir];
+ b[i][r] = e;
+ c[ii][ii] -= c[ii][ir] * c[ii][ir] * inverse2;
+ for (int j = r + 1; j < i; ++j) {
+ final int ij = index[j];
+ final double f = c[ii][ij] - e * b[j][r];
+ c[ii][ij] = f;
+ c[ij][ii] = f;
+ }
+ }
+
+ // prepare next iteration
+ loop = ++r < order;
+ }
+ }
+
+ // build the root matrix
+ rank = r;
+ root = MatrixUtils.createRealMatrix(order, r);
+ for (int i = 0; i < order; ++i) {
+ for (int j = 0; j < r; ++j) {
+ root.setEntry(index[i], j, b[i][j]);
+ }
+ }
+ }
+
+ /**
+ * Get the root of the covariance matrix. The root is the rectangular matrix <code>B</code> such
+ * that the covariance matrix is equal to <code>B.B<sup>T</sup></code>
+ *
+ * @return root of the square matrix
+ * @see #getRank()
+ */
+ public RealMatrix getRootMatrix() {
+ return root;
+ }
+
+ /**
+ * Get the rank of the symmetric positive semidefinite matrix. The r is the number of
+ * independent rows in the symmetric positive semidefinite matrix, it is also the number of
+ * columns of the rectangular matrix of the decomposition.
+ *
+ * @return r of the square matrix.
+ * @see #getRootMatrix()
+ */
+ public int getRank() {
+ return rank;
+ }
+}
diff --git a/src/main/java/org/apache/commons/math3/linear/SchurTransformer.java b/src/main/java/org/apache/commons/math3/linear/SchurTransformer.java
new file mode 100644
index 0000000..bea333f
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/linear/SchurTransformer.java
@@ -0,0 +1,474 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math3.linear;
+
+import org.apache.commons.math3.exception.MaxCountExceededException;
+import org.apache.commons.math3.exception.util.LocalizedFormats;
+import org.apache.commons.math3.util.FastMath;
+import org.apache.commons.math3.util.Precision;
+
+/**
+ * Class transforming a general real matrix to Schur form.
+ *
+ * <p>A m &times; m matrix A can be written as the product of three matrices: A = P &times; T
+ * &times; P<sup>T</sup> with P an orthogonal matrix and T an quasi-triangular matrix. Both P and T
+ * are m &times; m matrices.
+ *
+ * <p>Transformation to Schur form is often not a goal by itself, but it is an intermediate step in
+ * more general decomposition algorithms like {@link EigenDecomposition eigen decomposition}. This
+ * class is therefore intended for internal use by the library and is not public. As a consequence
+ * of this explicitly limited scope, many methods directly returns references to internal arrays,
+ * not copies.
+ *
+ * <p>This class is based on the method hqr2 in class EigenvalueDecomposition from the <a
+ * href="http://math.nist.gov/javanumerics/jama/">JAMA</a> library.
+ *
+ * @see <a href="http://mathworld.wolfram.com/SchurDecomposition.html">Schur Decomposition -
+ * MathWorld</a>
+ * @see <a href="http://en.wikipedia.org/wiki/Schur_decomposition">Schur Decomposition -
+ * Wikipedia</a>
+ * @see <a href="http://en.wikipedia.org/wiki/Householder_transformation">Householder
+ * Transformations</a>
+ * @since 3.1
+ */
+class SchurTransformer {
+ /** Maximum allowed iterations for convergence of the transformation. */
+ private static final int MAX_ITERATIONS = 100;
+
+ /** P matrix. */
+ private final double matrixP[][];
+
+ /** T matrix. */
+ private final double matrixT[][];
+
+ /** Cached value of P. */
+ private RealMatrix cachedP;
+
+ /** Cached value of T. */
+ private RealMatrix cachedT;
+
+ /** Cached value of PT. */
+ private RealMatrix cachedPt;
+
+ /** Epsilon criteria taken from JAMA code (originally was 2^-52). */
+ private final double epsilon = Precision.EPSILON;
+
+ /**
+ * Build the transformation to Schur form of a general real matrix.
+ *
+ * @param matrix matrix to transform
+ * @throws NonSquareMatrixException if the matrix is not square
+ */
+ SchurTransformer(final RealMatrix matrix) {
+ if (!matrix.isSquare()) {
+ throw new NonSquareMatrixException(
+ matrix.getRowDimension(), matrix.getColumnDimension());
+ }
+
+ HessenbergTransformer transformer = new HessenbergTransformer(matrix);
+ matrixT = transformer.getH().getData();
+ matrixP = transformer.getP().getData();
+ cachedT = null;
+ cachedP = null;
+ cachedPt = null;
+
+ // transform matrix
+ transform();
+ }
+
+ /**
+ * Returns the matrix P of the transform.
+ *
+ * <p>P is an orthogonal matrix, i.e. its inverse is also its transpose.
+ *
+ * @return the P matrix
+ */
+ public RealMatrix getP() {
+ if (cachedP == null) {
+ cachedP = MatrixUtils.createRealMatrix(matrixP);
+ }
+ return cachedP;
+ }
+
+ /**
+ * Returns the transpose of the matrix P of the transform.
+ *
+ * <p>P is an orthogonal matrix, i.e. its inverse is also its transpose.
+ *
+ * @return the transpose of the P matrix
+ */
+ public RealMatrix getPT() {
+ if (cachedPt == null) {
+ cachedPt = getP().transpose();
+ }
+
+ // return the cached matrix
+ return cachedPt;
+ }
+
+ /**
+ * Returns the quasi-triangular Schur matrix T of the transform.
+ *
+ * @return the T matrix
+ */
+ public RealMatrix getT() {
+ if (cachedT == null) {
+ cachedT = MatrixUtils.createRealMatrix(matrixT);
+ }
+
+ // return the cached matrix
+ return cachedT;
+ }
+
+ /**
+ * Transform original matrix to Schur form.
+ *
+ * @throws MaxCountExceededException if the transformation does not converge
+ */
+ private void transform() {
+ final int n = matrixT.length;
+
+ // compute matrix norm
+ final double norm = getNorm();
+
+ // shift information
+ final ShiftInfo shift = new ShiftInfo();
+
+ // Outer loop over eigenvalue index
+ int iteration = 0;
+ int iu = n - 1;
+ while (iu >= 0) {
+
+ // Look for single small sub-diagonal element
+ final int il = findSmallSubDiagonalElement(iu, norm);
+
+ // Check for convergence
+ if (il == iu) {
+ // One root found
+ matrixT[iu][iu] += shift.exShift;
+ iu--;
+ iteration = 0;
+ } else if (il == iu - 1) {
+ // Two roots found
+ double p = (matrixT[iu - 1][iu - 1] - matrixT[iu][iu]) / 2.0;
+ double q = p * p + matrixT[iu][iu - 1] * matrixT[iu - 1][iu];
+ matrixT[iu][iu] += shift.exShift;
+ matrixT[iu - 1][iu - 1] += shift.exShift;
+
+ if (q >= 0) {
+ double z = FastMath.sqrt(FastMath.abs(q));
+ if (p >= 0) {
+ z = p + z;
+ } else {
+ z = p - z;
+ }
+ final double x = matrixT[iu][iu - 1];
+ final double s = FastMath.abs(x) + FastMath.abs(z);
+ p = x / s;
+ q = z / s;
+ final double r = FastMath.sqrt(p * p + q * q);
+ p /= r;
+ q /= r;
+
+ // Row modification
+ for (int j = iu - 1; j < n; j++) {
+ z = matrixT[iu - 1][j];
+ matrixT[iu - 1][j] = q * z + p * matrixT[iu][j];
+ matrixT[iu][j] = q * matrixT[iu][j] - p * z;
+ }
+
+ // Column modification
+ for (int i = 0; i <= iu; i++) {
+ z = matrixT[i][iu - 1];
+ matrixT[i][iu - 1] = q * z + p * matrixT[i][iu];
+ matrixT[i][iu] = q * matrixT[i][iu] - p * z;
+ }
+
+ // Accumulate transformations
+ for (int i = 0; i <= n - 1; i++) {
+ z = matrixP[i][iu - 1];
+ matrixP[i][iu - 1] = q * z + p * matrixP[i][iu];
+ matrixP[i][iu] = q * matrixP[i][iu] - p * z;
+ }
+ }
+ iu -= 2;
+ iteration = 0;
+ } else {
+ // No convergence yet
+ computeShift(il, iu, iteration, shift);
+
+ // stop transformation after too many iterations
+ if (++iteration > MAX_ITERATIONS) {
+ throw new MaxCountExceededException(
+ LocalizedFormats.CONVERGENCE_FAILED, MAX_ITERATIONS);
+ }
+
+ // the initial houseHolder vector for the QR step
+ final double[] hVec = new double[3];
+
+ final int im = initQRStep(il, iu, shift, hVec);
+ performDoubleQRStep(il, im, iu, shift, hVec);
+ }
+ }
+ }
+
+ /**
+ * Computes the L1 norm of the (quasi-)triangular matrix T.
+ *
+ * @return the L1 norm of matrix T
+ */
+ private double getNorm() {
+ double norm = 0.0;
+ for (int i = 0; i < matrixT.length; i++) {
+ // as matrix T is (quasi-)triangular, also take the sub-diagonal element into account
+ for (int j = FastMath.max(i - 1, 0); j < matrixT.length; j++) {
+ norm += FastMath.abs(matrixT[i][j]);
+ }
+ }
+ return norm;
+ }
+
+ /**
+ * Find the first small sub-diagonal element and returns its index.
+ *
+ * @param startIdx the starting index for the search
+ * @param norm the L1 norm of the matrix
+ * @return the index of the first small sub-diagonal element
+ */
+ private int findSmallSubDiagonalElement(final int startIdx, final double norm) {
+ int l = startIdx;
+ while (l > 0) {
+ double s = FastMath.abs(matrixT[l - 1][l - 1]) + FastMath.abs(matrixT[l][l]);
+ if (s == 0.0) {
+ s = norm;
+ }
+ if (FastMath.abs(matrixT[l][l - 1]) < epsilon * s) {
+ break;
+ }
+ l--;
+ }
+ return l;
+ }
+
+ /**
+ * Compute the shift for the current iteration.
+ *
+ * @param l the index of the small sub-diagonal element
+ * @param idx the current eigenvalue index
+ * @param iteration the current iteration
+ * @param shift holder for shift information
+ */
+ private void computeShift(
+ final int l, final int idx, final int iteration, final ShiftInfo shift) {
+ // Form shift
+ shift.x = matrixT[idx][idx];
+ shift.y = shift.w = 0.0;
+ if (l < idx) {
+ shift.y = matrixT[idx - 1][idx - 1];
+ shift.w = matrixT[idx][idx - 1] * matrixT[idx - 1][idx];
+ }
+
+ // Wilkinson's original ad hoc shift
+ if (iteration == 10) {
+ shift.exShift += shift.x;
+ for (int i = 0; i <= idx; i++) {
+ matrixT[i][i] -= shift.x;
+ }
+ final double s =
+ FastMath.abs(matrixT[idx][idx - 1]) + FastMath.abs(matrixT[idx - 1][idx - 2]);
+ shift.x = 0.75 * s;
+ shift.y = 0.75 * s;
+ shift.w = -0.4375 * s * s;
+ }
+
+ // MATLAB's new ad hoc shift
+ if (iteration == 30) {
+ double s = (shift.y - shift.x) / 2.0;
+ s = s * s + shift.w;
+ if (s > 0.0) {
+ s = FastMath.sqrt(s);
+ if (shift.y < shift.x) {
+ s = -s;
+ }
+ s = shift.x - shift.w / ((shift.y - shift.x) / 2.0 + s);
+ for (int i = 0; i <= idx; i++) {
+ matrixT[i][i] -= s;
+ }
+ shift.exShift += s;
+ shift.x = shift.y = shift.w = 0.964;
+ }
+ }
+ }
+
+ /**
+ * Initialize the householder vectors for the QR step.
+ *
+ * @param il the index of the small sub-diagonal element
+ * @param iu the current eigenvalue index
+ * @param shift shift information holder
+ * @param hVec the initial houseHolder vector
+ * @return the start index for the QR step
+ */
+ private int initQRStep(int il, final int iu, final ShiftInfo shift, double[] hVec) {
+ // Look for two consecutive small sub-diagonal elements
+ int im = iu - 2;
+ while (im >= il) {
+ final double z = matrixT[im][im];
+ final double r = shift.x - z;
+ double s = shift.y - z;
+ hVec[0] = (r * s - shift.w) / matrixT[im + 1][im] + matrixT[im][im + 1];
+ hVec[1] = matrixT[im + 1][im + 1] - z - r - s;
+ hVec[2] = matrixT[im + 2][im + 1];
+
+ if (im == il) {
+ break;
+ }
+
+ final double lhs =
+ FastMath.abs(matrixT[im][im - 1])
+ * (FastMath.abs(hVec[1]) + FastMath.abs(hVec[2]));
+ final double rhs =
+ FastMath.abs(hVec[0])
+ * (FastMath.abs(matrixT[im - 1][im - 1])
+ + FastMath.abs(z)
+ + FastMath.abs(matrixT[im + 1][im + 1]));
+
+ if (lhs < epsilon * rhs) {
+ break;
+ }
+ im--;
+ }
+
+ return im;
+ }
+
+ /**
+ * Perform a double QR step involving rows l:idx and columns m:n
+ *
+ * @param il the index of the small sub-diagonal element
+ * @param im the start index for the QR step
+ * @param iu the current eigenvalue index
+ * @param shift shift information holder
+ * @param hVec the initial houseHolder vector
+ */
+ private void performDoubleQRStep(
+ final int il, final int im, final int iu, final ShiftInfo shift, final double[] hVec) {
+
+ final int n = matrixT.length;
+ double p = hVec[0];
+ double q = hVec[1];
+ double r = hVec[2];
+
+ for (int k = im; k <= iu - 1; k++) {
+ boolean notlast = k != (iu - 1);
+ if (k != im) {
+ p = matrixT[k][k - 1];
+ q = matrixT[k + 1][k - 1];
+ r = notlast ? matrixT[k + 2][k - 1] : 0.0;
+ shift.x = FastMath.abs(p) + FastMath.abs(q) + FastMath.abs(r);
+ if (Precision.equals(shift.x, 0.0, epsilon)) {
+ continue;
+ }
+ p /= shift.x;
+ q /= shift.x;
+ r /= shift.x;
+ }
+ double s = FastMath.sqrt(p * p + q * q + r * r);
+ if (p < 0.0) {
+ s = -s;
+ }
+ if (s != 0.0) {
+ if (k != im) {
+ matrixT[k][k - 1] = -s * shift.x;
+ } else if (il != im) {
+ matrixT[k][k - 1] = -matrixT[k][k - 1];
+ }
+ p += s;
+ shift.x = p / s;
+ shift.y = q / s;
+ double z = r / s;
+ q /= p;
+ r /= p;
+
+ // Row modification
+ for (int j = k; j < n; j++) {
+ p = matrixT[k][j] + q * matrixT[k + 1][j];
+ if (notlast) {
+ p += r * matrixT[k + 2][j];
+ matrixT[k + 2][j] -= p * z;
+ }
+ matrixT[k][j] -= p * shift.x;
+ matrixT[k + 1][j] -= p * shift.y;
+ }
+
+ // Column modification
+ for (int i = 0; i <= FastMath.min(iu, k + 3); i++) {
+ p = shift.x * matrixT[i][k] + shift.y * matrixT[i][k + 1];
+ if (notlast) {
+ p += z * matrixT[i][k + 2];
+ matrixT[i][k + 2] -= p * r;
+ }
+ matrixT[i][k] -= p;
+ matrixT[i][k + 1] -= p * q;
+ }
+
+ // Accumulate transformations
+ final int high = matrixT.length - 1;
+ for (int i = 0; i <= high; i++) {
+ p = shift.x * matrixP[i][k] + shift.y * matrixP[i][k + 1];
+ if (notlast) {
+ p += z * matrixP[i][k + 2];
+ matrixP[i][k + 2] -= p * r;
+ }
+ matrixP[i][k] -= p;
+ matrixP[i][k + 1] -= p * q;
+ }
+ } // (s != 0)
+ } // k loop
+
+ // clean up pollution due to round-off errors
+ for (int i = im + 2; i <= iu; i++) {
+ matrixT[i][i - 2] = 0.0;
+ if (i > im + 2) {
+ matrixT[i][i - 3] = 0.0;
+ }
+ }
+ }
+
+ /**
+ * Internal data structure holding the current shift information. Contains variable names as
+ * present in the original JAMA code.
+ */
+ private static class ShiftInfo {
+ // CHECKSTYLE: stop all
+
+ /** x shift info */
+ double x;
+
+ /** y shift info */
+ double y;
+
+ /** w shift info */
+ double w;
+
+ /** Indicates an exceptional shift. */
+ double exShift;
+
+ // CHECKSTYLE: resume all
+ }
+}
diff --git a/src/main/java/org/apache/commons/math3/linear/SingularMatrixException.java b/src/main/java/org/apache/commons/math3/linear/SingularMatrixException.java
new file mode 100644
index 0000000..ea6e189
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/linear/SingularMatrixException.java
@@ -0,0 +1,35 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math3.linear;
+
+import org.apache.commons.math3.exception.MathIllegalArgumentException;
+import org.apache.commons.math3.exception.util.LocalizedFormats;
+
+/**
+ * Exception to be thrown when a non-singular matrix is expected.
+ *
+ * @since 3.0
+ */
+public class SingularMatrixException extends MathIllegalArgumentException {
+ /** Serializable version Id. */
+ private static final long serialVersionUID = -4206514844735401070L;
+
+ /** Construct an exception. */
+ public SingularMatrixException() {
+ super(LocalizedFormats.SINGULAR_MATRIX);
+ }
+}
diff --git a/src/main/java/org/apache/commons/math3/linear/SingularOperatorException.java b/src/main/java/org/apache/commons/math3/linear/SingularOperatorException.java
new file mode 100644
index 0000000..e211997
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/linear/SingularOperatorException.java
@@ -0,0 +1,35 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math3.linear;
+
+import org.apache.commons.math3.exception.MathIllegalArgumentException;
+import org.apache.commons.math3.exception.util.LocalizedFormats;
+
+/**
+ * Exception to be thrown when trying to invert a singular operator.
+ *
+ * @since 3.0
+ */
+public class SingularOperatorException extends MathIllegalArgumentException {
+ /** Serializable version Id. */
+ private static final long serialVersionUID = -476049978595245033L;
+
+ /** Creates a new instance of this class. */
+ public SingularOperatorException() {
+ super(LocalizedFormats.SINGULAR_OPERATOR);
+ }
+}
diff --git a/src/main/java/org/apache/commons/math3/linear/SingularValueDecomposition.java b/src/main/java/org/apache/commons/math3/linear/SingularValueDecomposition.java
new file mode 100644
index 0000000..7d5c6b7
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/linear/SingularValueDecomposition.java
@@ -0,0 +1,778 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math3.linear;
+
+import org.apache.commons.math3.exception.NumberIsTooLargeException;
+import org.apache.commons.math3.exception.util.LocalizedFormats;
+import org.apache.commons.math3.util.FastMath;
+import org.apache.commons.math3.util.Precision;
+
+/**
+ * Calculates the compact Singular Value Decomposition of a matrix.
+ *
+ * <p>The Singular Value Decomposition of matrix A is a set of three matrices: U, &Sigma; and V such
+ * that A = U &times; &Sigma; &times; V<sup>T</sup>. Let A be a m &times; n matrix, then U is a m
+ * &times; p orthogonal matrix, &Sigma; is a p &times; p diagonal matrix with positive or null
+ * elements, V is a p &times; n orthogonal matrix (hence V<sup>T</sup> is also orthogonal) where
+ * p=min(m,n).
+ *
+ * <p>This class is similar to the class with similar name from the <a
+ * href="http://math.nist.gov/javanumerics/jama/">JAMA</a> library, with the following changes:
+ *
+ * <ul>
+ * <li>the {@code norm2} method which has been renamed as {@link #getNorm() getNorm},
+ * <li>the {@code cond} method which has been renamed as {@link #getConditionNumber()
+ * getConditionNumber},
+ * <li>the {@code rank} method which has been renamed as {@link #getRank() getRank},
+ * <li>a {@link #getUT() getUT} method has been added,
+ * <li>a {@link #getVT() getVT} method has been added,
+ * <li>a {@link #getSolver() getSolver} method has been added,
+ * <li>a {@link #getCovariance(double) getCovariance} method has been added.
+ * </ul>
+ *
+ * @see <a href="http://mathworld.wolfram.com/SingularValueDecomposition.html">MathWorld</a>
+ * @see <a href="http://en.wikipedia.org/wiki/Singular_value_decomposition">Wikipedia</a>
+ * @since 2.0 (changed to concrete class in 3.0)
+ */
+public class SingularValueDecomposition {
+ /** Relative threshold for small singular values. */
+ private static final double EPS = 0x1.0p-52;
+
+ /** Absolute threshold for small singular values. */
+ private static final double TINY = 0x1.0p-966;
+
+ /** Computed singular values. */
+ private final double[] singularValues;
+
+ /** max(row dimension, column dimension). */
+ private final int m;
+
+ /** min(row dimension, column dimension). */
+ private final int n;
+
+ /** Indicator for transposed matrix. */
+ private final boolean transposed;
+
+ /** Cached value of U matrix. */
+ private final RealMatrix cachedU;
+
+ /** Cached value of transposed U matrix. */
+ private RealMatrix cachedUt;
+
+ /** Cached value of S (diagonal) matrix. */
+ private RealMatrix cachedS;
+
+ /** Cached value of V matrix. */
+ private final RealMatrix cachedV;
+
+ /** Cached value of transposed V matrix. */
+ private RealMatrix cachedVt;
+
+ /**
+ * Tolerance value for small singular values, calculated once we have populated
+ * "singularValues".
+ */
+ private final double tol;
+
+ /**
+ * Calculates the compact Singular Value Decomposition of the given matrix.
+ *
+ * @param matrix Matrix to decompose.
+ */
+ public SingularValueDecomposition(final RealMatrix matrix) {
+ final double[][] A;
+
+ // "m" is always the largest dimension.
+ if (matrix.getRowDimension() < matrix.getColumnDimension()) {
+ transposed = true;
+ A = matrix.transpose().getData();
+ m = matrix.getColumnDimension();
+ n = matrix.getRowDimension();
+ } else {
+ transposed = false;
+ A = matrix.getData();
+ m = matrix.getRowDimension();
+ n = matrix.getColumnDimension();
+ }
+
+ singularValues = new double[n];
+ final double[][] U = new double[m][n];
+ final double[][] V = new double[n][n];
+ final double[] e = new double[n];
+ final double[] work = new double[m];
+ // Reduce A to bidiagonal form, storing the diagonal elements
+ // in s and the super-diagonal elements in e.
+ final int nct = FastMath.min(m - 1, n);
+ final int nrt = FastMath.max(0, n - 2);
+ for (int k = 0; k < FastMath.max(nct, nrt); k++) {
+ if (k < nct) {
+ // Compute the transformation for the k-th column and
+ // place the k-th diagonal in s[k].
+ // Compute 2-norm of k-th column without under/overflow.
+ singularValues[k] = 0;
+ for (int i = k; i < m; i++) {
+ singularValues[k] = FastMath.hypot(singularValues[k], A[i][k]);
+ }
+ if (singularValues[k] != 0) {
+ if (A[k][k] < 0) {
+ singularValues[k] = -singularValues[k];
+ }
+ for (int i = k; i < m; i++) {
+ A[i][k] /= singularValues[k];
+ }
+ A[k][k] += 1;
+ }
+ singularValues[k] = -singularValues[k];
+ }
+ for (int j = k + 1; j < n; j++) {
+ if (k < nct && singularValues[k] != 0) {
+ // Apply the transformation.
+ double t = 0;
+ for (int i = k; i < m; i++) {
+ t += A[i][k] * A[i][j];
+ }
+ t = -t / A[k][k];
+ for (int i = k; i < m; i++) {
+ A[i][j] += t * A[i][k];
+ }
+ }
+ // Place the k-th row of A into e for the
+ // subsequent calculation of the row transformation.
+ e[j] = A[k][j];
+ }
+ if (k < nct) {
+ // Place the transformation in U for subsequent back
+ // multiplication.
+ for (int i = k; i < m; i++) {
+ U[i][k] = A[i][k];
+ }
+ }
+ if (k < nrt) {
+ // Compute the k-th row transformation and place the
+ // k-th super-diagonal in e[k].
+ // Compute 2-norm without under/overflow.
+ e[k] = 0;
+ for (int i = k + 1; i < n; i++) {
+ e[k] = FastMath.hypot(e[k], e[i]);
+ }
+ if (e[k] != 0) {
+ if (e[k + 1] < 0) {
+ e[k] = -e[k];
+ }
+ for (int i = k + 1; i < n; i++) {
+ e[i] /= e[k];
+ }
+ e[k + 1] += 1;
+ }
+ e[k] = -e[k];
+ if (k + 1 < m && e[k] != 0) {
+ // Apply the transformation.
+ for (int i = k + 1; i < m; i++) {
+ work[i] = 0;
+ }
+ for (int j = k + 1; j < n; j++) {
+ for (int i = k + 1; i < m; i++) {
+ work[i] += e[j] * A[i][j];
+ }
+ }
+ for (int j = k + 1; j < n; j++) {
+ final double t = -e[j] / e[k + 1];
+ for (int i = k + 1; i < m; i++) {
+ A[i][j] += t * work[i];
+ }
+ }
+ }
+
+ // Place the transformation in V for subsequent
+ // back multiplication.
+ for (int i = k + 1; i < n; i++) {
+ V[i][k] = e[i];
+ }
+ }
+ }
+ // Set up the final bidiagonal matrix or order p.
+ int p = n;
+ if (nct < n) {
+ singularValues[nct] = A[nct][nct];
+ }
+ if (m < p) {
+ singularValues[p - 1] = 0;
+ }
+ if (nrt + 1 < p) {
+ e[nrt] = A[nrt][p - 1];
+ }
+ e[p - 1] = 0;
+
+ // Generate U.
+ for (int j = nct; j < n; j++) {
+ for (int i = 0; i < m; i++) {
+ U[i][j] = 0;
+ }
+ U[j][j] = 1;
+ }
+ for (int k = nct - 1; k >= 0; k--) {
+ if (singularValues[k] != 0) {
+ for (int j = k + 1; j < n; j++) {
+ double t = 0;
+ for (int i = k; i < m; i++) {
+ t += U[i][k] * U[i][j];
+ }
+ t = -t / U[k][k];
+ for (int i = k; i < m; i++) {
+ U[i][j] += t * U[i][k];
+ }
+ }
+ for (int i = k; i < m; i++) {
+ U[i][k] = -U[i][k];
+ }
+ U[k][k] = 1 + U[k][k];
+ for (int i = 0; i < k - 1; i++) {
+ U[i][k] = 0;
+ }
+ } else {
+ for (int i = 0; i < m; i++) {
+ U[i][k] = 0;
+ }
+ U[k][k] = 1;
+ }
+ }
+
+ // Generate V.
+ for (int k = n - 1; k >= 0; k--) {
+ if (k < nrt && e[k] != 0) {
+ for (int j = k + 1; j < n; j++) {
+ double t = 0;
+ for (int i = k + 1; i < n; i++) {
+ t += V[i][k] * V[i][j];
+ }
+ t = -t / V[k + 1][k];
+ for (int i = k + 1; i < n; i++) {
+ V[i][j] += t * V[i][k];
+ }
+ }
+ }
+ for (int i = 0; i < n; i++) {
+ V[i][k] = 0;
+ }
+ V[k][k] = 1;
+ }
+
+ // Main iteration loop for the singular values.
+ final int pp = p - 1;
+ while (p > 0) {
+ int k;
+ int kase;
+ // Here is where a test for too many iterations would go.
+ // This section of the program inspects for
+ // negligible elements in the s and e arrays. On
+ // completion the variables kase and k are set as follows.
+ // kase = 1 if s(p) and e[k-1] are negligible and k<p
+ // kase = 2 if s(k) is negligible and k<p
+ // kase = 3 if e[k-1] is negligible, k<p, and
+ // s(k), ..., s(p) are not negligible (qr step).
+ // kase = 4 if e(p-1) is negligible (convergence).
+ for (k = p - 2; k >= 0; k--) {
+ final double threshold =
+ TINY
+ + EPS
+ * (FastMath.abs(singularValues[k])
+ + FastMath.abs(singularValues[k + 1]));
+
+ // the following condition is written this way in order
+ // to break out of the loop when NaN occurs, writing it
+ // as "if (FastMath.abs(e[k]) <= threshold)" would loop
+ // indefinitely in case of NaNs because comparison on NaNs
+ // always return false, regardless of what is checked
+ // see issue MATH-947
+ if (!(FastMath.abs(e[k]) > threshold)) {
+ e[k] = 0;
+ break;
+ }
+ }
+
+ if (k == p - 2) {
+ kase = 4;
+ } else {
+ int ks;
+ for (ks = p - 1; ks >= k; ks--) {
+ if (ks == k) {
+ break;
+ }
+ final double t =
+ (ks != p ? FastMath.abs(e[ks]) : 0)
+ + (ks != k + 1 ? FastMath.abs(e[ks - 1]) : 0);
+ if (FastMath.abs(singularValues[ks]) <= TINY + EPS * t) {
+ singularValues[ks] = 0;
+ break;
+ }
+ }
+ if (ks == k) {
+ kase = 3;
+ } else if (ks == p - 1) {
+ kase = 1;
+ } else {
+ kase = 2;
+ k = ks;
+ }
+ }
+ k++;
+ // Perform the task indicated by kase.
+ switch (kase) {
+ // Deflate negligible s(p).
+ case 1:
+ {
+ double f = e[p - 2];
+ e[p - 2] = 0;
+ for (int j = p - 2; j >= k; j--) {
+ double t = FastMath.hypot(singularValues[j], f);
+ final double cs = singularValues[j] / t;
+ final double sn = f / t;
+ singularValues[j] = t;
+ if (j != k) {
+ f = -sn * e[j - 1];
+ e[j - 1] = cs * e[j - 1];
+ }
+
+ for (int i = 0; i < n; i++) {
+ t = cs * V[i][j] + sn * V[i][p - 1];
+ V[i][p - 1] = -sn * V[i][j] + cs * V[i][p - 1];
+ V[i][j] = t;
+ }
+ }
+ }
+ break;
+ // Split at negligible s(k).
+ case 2:
+ {
+ double f = e[k - 1];
+ e[k - 1] = 0;
+ for (int j = k; j < p; j++) {
+ double t = FastMath.hypot(singularValues[j], f);
+ final double cs = singularValues[j] / t;
+ final double sn = f / t;
+ singularValues[j] = t;
+ f = -sn * e[j];
+ e[j] = cs * e[j];
+
+ for (int i = 0; i < m; i++) {
+ t = cs * U[i][j] + sn * U[i][k - 1];
+ U[i][k - 1] = -sn * U[i][j] + cs * U[i][k - 1];
+ U[i][j] = t;
+ }
+ }
+ }
+ break;
+ // Perform one qr step.
+ case 3:
+ {
+ // Calculate the shift.
+ final double maxPm1Pm2 =
+ FastMath.max(
+ FastMath.abs(singularValues[p - 1]),
+ FastMath.abs(singularValues[p - 2]));
+ final double scale =
+ FastMath.max(
+ FastMath.max(
+ FastMath.max(maxPm1Pm2, FastMath.abs(e[p - 2])),
+ FastMath.abs(singularValues[k])),
+ FastMath.abs(e[k]));
+ final double sp = singularValues[p - 1] / scale;
+ final double spm1 = singularValues[p - 2] / scale;
+ final double epm1 = e[p - 2] / scale;
+ final double sk = singularValues[k] / scale;
+ final double ek = e[k] / scale;
+ final double b = ((spm1 + sp) * (spm1 - sp) + epm1 * epm1) / 2.0;
+ final double c = (sp * epm1) * (sp * epm1);
+ double shift = 0;
+ if (b != 0 || c != 0) {
+ shift = FastMath.sqrt(b * b + c);
+ if (b < 0) {
+ shift = -shift;
+ }
+ shift = c / (b + shift);
+ }
+ double f = (sk + sp) * (sk - sp) + shift;
+ double g = sk * ek;
+ // Chase zeros.
+ for (int j = k; j < p - 1; j++) {
+ double t = FastMath.hypot(f, g);
+ double cs = f / t;
+ double sn = g / t;
+ if (j != k) {
+ e[j - 1] = t;
+ }
+ f = cs * singularValues[j] + sn * e[j];
+ e[j] = cs * e[j] - sn * singularValues[j];
+ g = sn * singularValues[j + 1];
+ singularValues[j + 1] = cs * singularValues[j + 1];
+
+ for (int i = 0; i < n; i++) {
+ t = cs * V[i][j] + sn * V[i][j + 1];
+ V[i][j + 1] = -sn * V[i][j] + cs * V[i][j + 1];
+ V[i][j] = t;
+ }
+ t = FastMath.hypot(f, g);
+ cs = f / t;
+ sn = g / t;
+ singularValues[j] = t;
+ f = cs * e[j] + sn * singularValues[j + 1];
+ singularValues[j + 1] = -sn * e[j] + cs * singularValues[j + 1];
+ g = sn * e[j + 1];
+ e[j + 1] = cs * e[j + 1];
+ if (j < m - 1) {
+ for (int i = 0; i < m; i++) {
+ t = cs * U[i][j] + sn * U[i][j + 1];
+ U[i][j + 1] = -sn * U[i][j] + cs * U[i][j + 1];
+ U[i][j] = t;
+ }
+ }
+ }
+ e[p - 2] = f;
+ }
+ break;
+ // Convergence.
+ default:
+ {
+ // Make the singular values positive.
+ if (singularValues[k] <= 0) {
+ singularValues[k] = singularValues[k] < 0 ? -singularValues[k] : 0;
+
+ for (int i = 0; i <= pp; i++) {
+ V[i][k] = -V[i][k];
+ }
+ }
+ // Order the singular values.
+ while (k < pp) {
+ if (singularValues[k] >= singularValues[k + 1]) {
+ break;
+ }
+ double t = singularValues[k];
+ singularValues[k] = singularValues[k + 1];
+ singularValues[k + 1] = t;
+ if (k < n - 1) {
+ for (int i = 0; i < n; i++) {
+ t = V[i][k + 1];
+ V[i][k + 1] = V[i][k];
+ V[i][k] = t;
+ }
+ }
+ if (k < m - 1) {
+ for (int i = 0; i < m; i++) {
+ t = U[i][k + 1];
+ U[i][k + 1] = U[i][k];
+ U[i][k] = t;
+ }
+ }
+ k++;
+ }
+ p--;
+ }
+ break;
+ }
+ }
+
+ // Set the small value tolerance used to calculate rank and pseudo-inverse
+ tol = FastMath.max(m * singularValues[0] * EPS, FastMath.sqrt(Precision.SAFE_MIN));
+
+ if (!transposed) {
+ cachedU = MatrixUtils.createRealMatrix(U);
+ cachedV = MatrixUtils.createRealMatrix(V);
+ } else {
+ cachedU = MatrixUtils.createRealMatrix(V);
+ cachedV = MatrixUtils.createRealMatrix(U);
+ }
+ }
+
+ /**
+ * Returns the matrix U of the decomposition.
+ *
+ * <p>U is an orthogonal matrix, i.e. its transpose is also its inverse.
+ *
+ * @return the U matrix
+ * @see #getUT()
+ */
+ public RealMatrix getU() {
+ // return the cached matrix
+ return cachedU;
+ }
+
+ /**
+ * Returns the transpose of the matrix U of the decomposition.
+ *
+ * <p>U is an orthogonal matrix, i.e. its transpose is also its inverse.
+ *
+ * @return the U matrix (or null if decomposed matrix is singular)
+ * @see #getU()
+ */
+ public RealMatrix getUT() {
+ if (cachedUt == null) {
+ cachedUt = getU().transpose();
+ }
+ // return the cached matrix
+ return cachedUt;
+ }
+
+ /**
+ * Returns the diagonal matrix &Sigma; of the decomposition.
+ *
+ * <p>&Sigma; is a diagonal matrix. The singular values are provided in non-increasing order,
+ * for compatibility with Jama.
+ *
+ * @return the &Sigma; matrix
+ */
+ public RealMatrix getS() {
+ if (cachedS == null) {
+ // cache the matrix for subsequent calls
+ cachedS = MatrixUtils.createRealDiagonalMatrix(singularValues);
+ }
+ return cachedS;
+ }
+
+ /**
+ * Returns the diagonal elements of the matrix &Sigma; of the decomposition.
+ *
+ * <p>The singular values are provided in non-increasing order, for compatibility with Jama.
+ *
+ * @return the diagonal elements of the &Sigma; matrix
+ */
+ public double[] getSingularValues() {
+ return singularValues.clone();
+ }
+
+ /**
+ * Returns the matrix V of the decomposition.
+ *
+ * <p>V is an orthogonal matrix, i.e. its transpose is also its inverse.
+ *
+ * @return the V matrix (or null if decomposed matrix is singular)
+ * @see #getVT()
+ */
+ public RealMatrix getV() {
+ // return the cached matrix
+ return cachedV;
+ }
+
+ /**
+ * Returns the transpose of the matrix V of the decomposition.
+ *
+ * <p>V is an orthogonal matrix, i.e. its transpose is also its inverse.
+ *
+ * @return the V matrix (or null if decomposed matrix is singular)
+ * @see #getV()
+ */
+ public RealMatrix getVT() {
+ if (cachedVt == null) {
+ cachedVt = getV().transpose();
+ }
+ // return the cached matrix
+ return cachedVt;
+ }
+
+ /**
+ * Returns the n &times; n covariance matrix.
+ *
+ * <p>The covariance matrix is V &times; J &times; V<sup>T</sup> where J is the diagonal matrix
+ * of the inverse of the squares of the singular values.
+ *
+ * @param minSingularValue value below which singular values are ignored (a 0 or negative value
+ * implies all singular value will be used)
+ * @return covariance matrix
+ * @exception IllegalArgumentException if minSingularValue is larger than the largest singular
+ * value, meaning all singular values are ignored
+ */
+ public RealMatrix getCovariance(final double minSingularValue) {
+ // get the number of singular values to consider
+ final int p = singularValues.length;
+ int dimension = 0;
+ while (dimension < p && singularValues[dimension] >= minSingularValue) {
+ ++dimension;
+ }
+
+ if (dimension == 0) {
+ throw new NumberIsTooLargeException(
+ LocalizedFormats.TOO_LARGE_CUTOFF_SINGULAR_VALUE,
+ minSingularValue,
+ singularValues[0],
+ true);
+ }
+
+ final double[][] data = new double[dimension][p];
+ getVT().walkInOptimizedOrder(
+ new DefaultRealMatrixPreservingVisitor() {
+ /** {@inheritDoc} */
+ @Override
+ public void visit(final int row, final int column, final double value) {
+ data[row][column] = value / singularValues[row];
+ }
+ },
+ 0,
+ dimension - 1,
+ 0,
+ p - 1);
+
+ RealMatrix jv = new Array2DRowRealMatrix(data, false);
+ return jv.transpose().multiply(jv);
+ }
+
+ /**
+ * Returns the L<sub>2</sub> norm of the matrix.
+ *
+ * <p>The L<sub>2</sub> norm is max(|A &times; u|<sub>2</sub> / |u|<sub>2</sub>), where
+ * |.|<sub>2</sub> denotes the vectorial 2-norm (i.e. the traditional euclidian norm).
+ *
+ * @return norm
+ */
+ public double getNorm() {
+ return singularValues[0];
+ }
+
+ /**
+ * Return the condition number of the matrix.
+ *
+ * @return condition number of the matrix
+ */
+ public double getConditionNumber() {
+ return singularValues[0] / singularValues[n - 1];
+ }
+
+ /**
+ * Computes the inverse of the condition number. In cases of rank deficiency, the {@link
+ * #getConditionNumber() condition number} will become undefined.
+ *
+ * @return the inverse of the condition number.
+ */
+ public double getInverseConditionNumber() {
+ return singularValues[n - 1] / singularValues[0];
+ }
+
+ /**
+ * Return the effective numerical matrix rank.
+ *
+ * <p>The effective numerical rank is the number of non-negligible singular values. The
+ * threshold used to identify non-negligible terms is max(m,n) &times; ulp(s<sub>1</sub>) where
+ * ulp(s<sub>1</sub>) is the least significant bit of the largest singular value.
+ *
+ * @return effective numerical matrix rank
+ */
+ public int getRank() {
+ int r = 0;
+ for (int i = 0; i < singularValues.length; i++) {
+ if (singularValues[i] > tol) {
+ r++;
+ }
+ }
+ return r;
+ }
+
+ /**
+ * Get a solver for finding the A &times; X = B solution in least square sense.
+ *
+ * @return a solver
+ */
+ public DecompositionSolver getSolver() {
+ return new Solver(singularValues, getUT(), getV(), getRank() == m, tol);
+ }
+
+ /** Specialized solver. */
+ private static class Solver implements DecompositionSolver {
+ /** Pseudo-inverse of the initial matrix. */
+ private final RealMatrix pseudoInverse;
+
+ /** Singularity indicator. */
+ private boolean nonSingular;
+
+ /**
+ * Build a solver from decomposed matrix.
+ *
+ * @param singularValues Singular values.
+ * @param uT U<sup>T</sup> matrix of the decomposition.
+ * @param v V matrix of the decomposition.
+ * @param nonSingular Singularity indicator.
+ * @param tol tolerance for singular values
+ */
+ private Solver(
+ final double[] singularValues,
+ final RealMatrix uT,
+ final RealMatrix v,
+ final boolean nonSingular,
+ final double tol) {
+ final double[][] suT = uT.getData();
+ for (int i = 0; i < singularValues.length; ++i) {
+ final double a;
+ if (singularValues[i] > tol) {
+ a = 1 / singularValues[i];
+ } else {
+ a = 0;
+ }
+ final double[] suTi = suT[i];
+ for (int j = 0; j < suTi.length; ++j) {
+ suTi[j] *= a;
+ }
+ }
+ pseudoInverse = v.multiply(new Array2DRowRealMatrix(suT, false));
+ this.nonSingular = nonSingular;
+ }
+
+ /**
+ * Solve the linear equation A &times; X = B in least square sense.
+ *
+ * <p>The m&times;n matrix A may not be square, the solution X is such that ||A &times; X -
+ * B|| is minimal.
+ *
+ * @param b Right-hand side of the equation A &times; X = B
+ * @return a vector X that minimizes the two norm of A &times; X - B
+ * @throws org.apache.commons.math3.exception.DimensionMismatchException if the matrices
+ * dimensions do not match.
+ */
+ public RealVector solve(final RealVector b) {
+ return pseudoInverse.operate(b);
+ }
+
+ /**
+ * Solve the linear equation A &times; X = B in least square sense.
+ *
+ * <p>The m&times;n matrix A may not be square, the solution X is such that ||A &times; X -
+ * B|| is minimal.
+ *
+ * @param b Right-hand side of the equation A &times; X = B
+ * @return a matrix X that minimizes the two norm of A &times; X - B
+ * @throws org.apache.commons.math3.exception.DimensionMismatchException if the matrices
+ * dimensions do not match.
+ */
+ public RealMatrix solve(final RealMatrix b) {
+ return pseudoInverse.multiply(b);
+ }
+
+ /**
+ * Check if the decomposed matrix is non-singular.
+ *
+ * @return {@code true} if the decomposed matrix is non-singular.
+ */
+ public boolean isNonSingular() {
+ return nonSingular;
+ }
+
+ /**
+ * Get the pseudo-inverse of the decomposed matrix.
+ *
+ * @return the inverse matrix.
+ */
+ public RealMatrix getInverse() {
+ return pseudoInverse;
+ }
+ }
+}
diff --git a/src/main/java/org/apache/commons/math3/linear/SparseFieldMatrix.java b/src/main/java/org/apache/commons/math3/linear/SparseFieldMatrix.java
new file mode 100644
index 0000000..a1df939
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/linear/SparseFieldMatrix.java
@@ -0,0 +1,184 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math3.linear;
+
+import org.apache.commons.math3.Field;
+import org.apache.commons.math3.FieldElement;
+import org.apache.commons.math3.util.OpenIntToFieldHashMap;
+
+/**
+ * Sparse matrix implementation based on an open addressed map.
+ *
+ * <p>Caveat: This implementation assumes that, for any {@code x}, the equality {@code x * 0d == 0d}
+ * holds. But it is is not true for {@code NaN}. Moreover, zero entries will lose their sign. Some
+ * operations (that involve {@code NaN} and/or infinities) may thus give incorrect results.
+ *
+ * @param <T> the type of the field elements
+ * @since 2.0
+ */
+public class SparseFieldMatrix<T extends FieldElement<T>> extends AbstractFieldMatrix<T> {
+
+ /** Storage for (sparse) matrix elements. */
+ private final OpenIntToFieldHashMap<T> entries;
+
+ /** Row dimension. */
+ private final int rows;
+
+ /** Column dimension. */
+ private final int columns;
+
+ /**
+ * Create a matrix with no data.
+ *
+ * @param field Field to which the elements belong.
+ */
+ public SparseFieldMatrix(final Field<T> field) {
+ super(field);
+ rows = 0;
+ columns = 0;
+ entries = new OpenIntToFieldHashMap<T>(field);
+ }
+
+ /**
+ * Create a new SparseFieldMatrix<T> with the supplied row and column dimensions.
+ *
+ * @param field Field to which the elements belong.
+ * @param rowDimension Number of rows in the new matrix.
+ * @param columnDimension Number of columns in the new matrix.
+ * @throws org.apache.commons.math3.exception.NotStrictlyPositiveException if row or column
+ * dimension is not positive.
+ */
+ public SparseFieldMatrix(
+ final Field<T> field, final int rowDimension, final int columnDimension) {
+ super(field, rowDimension, columnDimension);
+ this.rows = rowDimension;
+ this.columns = columnDimension;
+ entries = new OpenIntToFieldHashMap<T>(field);
+ }
+
+ /**
+ * Copy constructor.
+ *
+ * @param other Instance to copy.
+ */
+ public SparseFieldMatrix(SparseFieldMatrix<T> other) {
+ super(other.getField(), other.getRowDimension(), other.getColumnDimension());
+ rows = other.getRowDimension();
+ columns = other.getColumnDimension();
+ entries = new OpenIntToFieldHashMap<T>(other.entries);
+ }
+
+ /**
+ * Generic copy constructor.
+ *
+ * @param other Instance to copy.
+ */
+ public SparseFieldMatrix(FieldMatrix<T> other) {
+ super(other.getField(), other.getRowDimension(), other.getColumnDimension());
+ rows = other.getRowDimension();
+ columns = other.getColumnDimension();
+ entries = new OpenIntToFieldHashMap<T>(getField());
+ for (int i = 0; i < rows; i++) {
+ for (int j = 0; j < columns; j++) {
+ setEntry(i, j, other.getEntry(i, j));
+ }
+ }
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void addToEntry(int row, int column, T increment) {
+ checkRowIndex(row);
+ checkColumnIndex(column);
+ final int key = computeKey(row, column);
+ final T value = entries.get(key).add(increment);
+ if (getField().getZero().equals(value)) {
+ entries.remove(key);
+ } else {
+ entries.put(key, value);
+ }
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public FieldMatrix<T> copy() {
+ return new SparseFieldMatrix<T>(this);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public FieldMatrix<T> createMatrix(int rowDimension, int columnDimension) {
+ return new SparseFieldMatrix<T>(getField(), rowDimension, columnDimension);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public int getColumnDimension() {
+ return columns;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public T getEntry(int row, int column) {
+ checkRowIndex(row);
+ checkColumnIndex(column);
+ return entries.get(computeKey(row, column));
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public int getRowDimension() {
+ return rows;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void multiplyEntry(int row, int column, T factor) {
+ checkRowIndex(row);
+ checkColumnIndex(column);
+ final int key = computeKey(row, column);
+ final T value = entries.get(key).multiply(factor);
+ if (getField().getZero().equals(value)) {
+ entries.remove(key);
+ } else {
+ entries.put(key, value);
+ }
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void setEntry(int row, int column, T value) {
+ checkRowIndex(row);
+ checkColumnIndex(column);
+ if (getField().getZero().equals(value)) {
+ entries.remove(computeKey(row, column));
+ } else {
+ entries.put(computeKey(row, column), value);
+ }
+ }
+
+ /**
+ * Compute the key to access a matrix element.
+ *
+ * @param row Row index of the matrix element.
+ * @param column Column index of the matrix element.
+ * @return the key within the map to access the matrix element.
+ */
+ private int computeKey(int row, int column) {
+ return row * columns + column;
+ }
+}
diff --git a/src/main/java/org/apache/commons/math3/linear/SparseFieldVector.java b/src/main/java/org/apache/commons/math3/linear/SparseFieldVector.java
new file mode 100644
index 0000000..a86ea50
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/linear/SparseFieldVector.java
@@ -0,0 +1,771 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math3.linear;
+
+import org.apache.commons.math3.Field;
+import org.apache.commons.math3.FieldElement;
+import org.apache.commons.math3.exception.DimensionMismatchException;
+import org.apache.commons.math3.exception.MathArithmeticException;
+import org.apache.commons.math3.exception.NotPositiveException;
+import org.apache.commons.math3.exception.NullArgumentException;
+import org.apache.commons.math3.exception.NumberIsTooSmallException;
+import org.apache.commons.math3.exception.OutOfRangeException;
+import org.apache.commons.math3.exception.util.LocalizedFormats;
+import org.apache.commons.math3.util.MathArrays;
+import org.apache.commons.math3.util.MathUtils;
+import org.apache.commons.math3.util.OpenIntToFieldHashMap;
+
+import java.io.Serializable;
+
+/**
+ * This class implements the {@link FieldVector} interface with a {@link OpenIntToFieldHashMap}
+ * backing store.
+ *
+ * <p>Caveat: This implementation assumes that, for any {@code x}, the equality {@code x * 0d == 0d}
+ * holds. But it is is not true for {@code NaN}. Moreover, zero entries will lose their sign. Some
+ * operations (that involve {@code NaN} and/or infinities) may thus give incorrect results.
+ *
+ * @param <T> the type of the field elements
+ * @since 2.0
+ */
+public class SparseFieldVector<T extends FieldElement<T>> implements FieldVector<T>, Serializable {
+ /** Serialization identifier. */
+ private static final long serialVersionUID = 7841233292190413362L;
+
+ /** Field to which the elements belong. */
+ private final Field<T> field;
+
+ /** Entries of the vector. */
+ private final OpenIntToFieldHashMap<T> entries;
+
+ /** Dimension of the vector. */
+ private final int virtualSize;
+
+ /**
+ * Build a 0-length vector. Zero-length vectors may be used to initialize construction of
+ * vectors by data gathering. We start with zero-length and use either the {@link
+ * #SparseFieldVector(SparseFieldVector, int)} constructor or one of the {@code append} method
+ * ({@link #append(FieldVector)} or {@link #append(SparseFieldVector)}) to gather data into this
+ * vector.
+ *
+ * @param field Field to which the elements belong.
+ */
+ public SparseFieldVector(Field<T> field) {
+ this(field, 0);
+ }
+
+ /**
+ * Construct a vector of zeroes.
+ *
+ * @param field Field to which the elements belong.
+ * @param dimension Size of the vector.
+ */
+ public SparseFieldVector(Field<T> field, int dimension) {
+ this.field = field;
+ virtualSize = dimension;
+ entries = new OpenIntToFieldHashMap<T>(field);
+ }
+
+ /**
+ * Build a resized vector, for use with append.
+ *
+ * @param v Original vector
+ * @param resize Amount to add.
+ */
+ protected SparseFieldVector(SparseFieldVector<T> v, int resize) {
+ field = v.field;
+ virtualSize = v.getDimension() + resize;
+ entries = new OpenIntToFieldHashMap<T>(v.entries);
+ }
+
+ /**
+ * Build a vector with known the sparseness (for advanced use only).
+ *
+ * @param field Field to which the elements belong.
+ * @param dimension Size of the vector.
+ * @param expectedSize Expected number of non-zero entries.
+ */
+ public SparseFieldVector(Field<T> field, int dimension, int expectedSize) {
+ this.field = field;
+ virtualSize = dimension;
+ entries = new OpenIntToFieldHashMap<T>(field, expectedSize);
+ }
+
+ /**
+ * Create from a Field array. Only non-zero entries will be stored.
+ *
+ * @param field Field to which the elements belong.
+ * @param values Set of values to create from.
+ * @exception NullArgumentException if values is null
+ */
+ public SparseFieldVector(Field<T> field, T[] values) throws NullArgumentException {
+ MathUtils.checkNotNull(values);
+ this.field = field;
+ virtualSize = values.length;
+ entries = new OpenIntToFieldHashMap<T>(field);
+ for (int key = 0; key < values.length; key++) {
+ T value = values[key];
+ entries.put(key, value);
+ }
+ }
+
+ /**
+ * Copy constructor.
+ *
+ * @param v Instance to copy.
+ */
+ public SparseFieldVector(SparseFieldVector<T> v) {
+ field = v.field;
+ virtualSize = v.getDimension();
+ entries = new OpenIntToFieldHashMap<T>(v.getEntries());
+ }
+
+ /**
+ * Get the entries of this instance.
+ *
+ * @return the entries of this instance
+ */
+ private OpenIntToFieldHashMap<T> getEntries() {
+ return entries;
+ }
+
+ /**
+ * Optimized method to add sparse vectors.
+ *
+ * @param v Vector to add.
+ * @return {@code this + v}.
+ * @throws DimensionMismatchException if {@code v} is not the same size as {@code this}.
+ */
+ public FieldVector<T> add(SparseFieldVector<T> v) throws DimensionMismatchException {
+ checkVectorDimensions(v.getDimension());
+ SparseFieldVector<T> res = (SparseFieldVector<T>) copy();
+ OpenIntToFieldHashMap<T>.Iterator iter = v.getEntries().iterator();
+ while (iter.hasNext()) {
+ iter.advance();
+ int key = iter.key();
+ T value = iter.value();
+ if (entries.containsKey(key)) {
+ res.setEntry(key, entries.get(key).add(value));
+ } else {
+ res.setEntry(key, value);
+ }
+ }
+ return res;
+ }
+
+ /**
+ * Construct a vector by appending a vector to this vector.
+ *
+ * @param v Vector to append to this one.
+ * @return a new vector.
+ */
+ public FieldVector<T> append(SparseFieldVector<T> v) {
+ SparseFieldVector<T> res = new SparseFieldVector<T>(this, v.getDimension());
+ OpenIntToFieldHashMap<T>.Iterator iter = v.entries.iterator();
+ while (iter.hasNext()) {
+ iter.advance();
+ res.setEntry(iter.key() + virtualSize, iter.value());
+ }
+ return res;
+ }
+
+ /** {@inheritDoc} */
+ public FieldVector<T> append(FieldVector<T> v) {
+ if (v instanceof SparseFieldVector<?>) {
+ return append((SparseFieldVector<T>) v);
+ } else {
+ final int n = v.getDimension();
+ FieldVector<T> res = new SparseFieldVector<T>(this, n);
+ for (int i = 0; i < n; i++) {
+ res.setEntry(i + virtualSize, v.getEntry(i));
+ }
+ return res;
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @exception NullArgumentException if d is null
+ */
+ public FieldVector<T> append(T d) throws NullArgumentException {
+ MathUtils.checkNotNull(d);
+ FieldVector<T> res = new SparseFieldVector<T>(this, 1);
+ res.setEntry(virtualSize, d);
+ return res;
+ }
+
+ /** {@inheritDoc} */
+ public FieldVector<T> copy() {
+ return new SparseFieldVector<T>(this);
+ }
+
+ /** {@inheritDoc} */
+ public T dotProduct(FieldVector<T> v) throws DimensionMismatchException {
+ checkVectorDimensions(v.getDimension());
+ T res = field.getZero();
+ OpenIntToFieldHashMap<T>.Iterator iter = entries.iterator();
+ while (iter.hasNext()) {
+ iter.advance();
+ res = res.add(v.getEntry(iter.key()).multiply(iter.value()));
+ }
+ return res;
+ }
+
+ /** {@inheritDoc} */
+ public FieldVector<T> ebeDivide(FieldVector<T> v)
+ throws DimensionMismatchException, MathArithmeticException {
+ checkVectorDimensions(v.getDimension());
+ SparseFieldVector<T> res = new SparseFieldVector<T>(this);
+ OpenIntToFieldHashMap<T>.Iterator iter = res.entries.iterator();
+ while (iter.hasNext()) {
+ iter.advance();
+ res.setEntry(iter.key(), iter.value().divide(v.getEntry(iter.key())));
+ }
+ return res;
+ }
+
+ /** {@inheritDoc} */
+ public FieldVector<T> ebeMultiply(FieldVector<T> v) throws DimensionMismatchException {
+ checkVectorDimensions(v.getDimension());
+ SparseFieldVector<T> res = new SparseFieldVector<T>(this);
+ OpenIntToFieldHashMap<T>.Iterator iter = res.entries.iterator();
+ while (iter.hasNext()) {
+ iter.advance();
+ res.setEntry(iter.key(), iter.value().multiply(v.getEntry(iter.key())));
+ }
+ return res;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @deprecated as of 3.1, to be removed in 4.0. Please use the {@link #toArray()} method
+ * instead.
+ */
+ @Deprecated
+ public T[] getData() {
+ return toArray();
+ }
+
+ /** {@inheritDoc} */
+ public int getDimension() {
+ return virtualSize;
+ }
+
+ /** {@inheritDoc} */
+ public T getEntry(int index) throws OutOfRangeException {
+ checkIndex(index);
+ return entries.get(index);
+ }
+
+ /** {@inheritDoc} */
+ public Field<T> getField() {
+ return field;
+ }
+
+ /** {@inheritDoc} */
+ public FieldVector<T> getSubVector(int index, int n)
+ throws OutOfRangeException, NotPositiveException {
+ if (n < 0) {
+ throw new NotPositiveException(
+ LocalizedFormats.NUMBER_OF_ELEMENTS_SHOULD_BE_POSITIVE, n);
+ }
+ checkIndex(index);
+ checkIndex(index + n - 1);
+ SparseFieldVector<T> res = new SparseFieldVector<T>(field, n);
+ int end = index + n;
+ OpenIntToFieldHashMap<T>.Iterator iter = entries.iterator();
+ while (iter.hasNext()) {
+ iter.advance();
+ int key = iter.key();
+ if (key >= index && key < end) {
+ res.setEntry(key - index, iter.value());
+ }
+ }
+ return res;
+ }
+
+ /** {@inheritDoc} */
+ public FieldVector<T> mapAdd(T d) throws NullArgumentException {
+ return copy().mapAddToSelf(d);
+ }
+
+ /** {@inheritDoc} */
+ public FieldVector<T> mapAddToSelf(T d) throws NullArgumentException {
+ for (int i = 0; i < virtualSize; i++) {
+ setEntry(i, getEntry(i).add(d));
+ }
+ return this;
+ }
+
+ /** {@inheritDoc} */
+ public FieldVector<T> mapDivide(T d) throws NullArgumentException, MathArithmeticException {
+ return copy().mapDivideToSelf(d);
+ }
+
+ /** {@inheritDoc} */
+ public FieldVector<T> mapDivideToSelf(T d)
+ throws NullArgumentException, MathArithmeticException {
+ OpenIntToFieldHashMap<T>.Iterator iter = entries.iterator();
+ while (iter.hasNext()) {
+ iter.advance();
+ entries.put(iter.key(), iter.value().divide(d));
+ }
+ return this;
+ }
+
+ /** {@inheritDoc} */
+ public FieldVector<T> mapInv() throws MathArithmeticException {
+ return copy().mapInvToSelf();
+ }
+
+ /** {@inheritDoc} */
+ public FieldVector<T> mapInvToSelf() throws MathArithmeticException {
+ for (int i = 0; i < virtualSize; i++) {
+ setEntry(i, field.getOne().divide(getEntry(i)));
+ }
+ return this;
+ }
+
+ /** {@inheritDoc} */
+ public FieldVector<T> mapMultiply(T d) throws NullArgumentException {
+ return copy().mapMultiplyToSelf(d);
+ }
+
+ /** {@inheritDoc} */
+ public FieldVector<T> mapMultiplyToSelf(T d) throws NullArgumentException {
+ OpenIntToFieldHashMap<T>.Iterator iter = entries.iterator();
+ while (iter.hasNext()) {
+ iter.advance();
+ entries.put(iter.key(), iter.value().multiply(d));
+ }
+ return this;
+ }
+
+ /** {@inheritDoc} */
+ public FieldVector<T> mapSubtract(T d) throws NullArgumentException {
+ return copy().mapSubtractToSelf(d);
+ }
+
+ /** {@inheritDoc} */
+ public FieldVector<T> mapSubtractToSelf(T d) throws NullArgumentException {
+ return mapAddToSelf(field.getZero().subtract(d));
+ }
+
+ /**
+ * Optimized method to compute outer product when both vectors are sparse.
+ *
+ * @param v vector with which outer product should be computed
+ * @return the matrix outer product between instance and v
+ */
+ public FieldMatrix<T> outerProduct(SparseFieldVector<T> v) {
+ final int n = v.getDimension();
+ SparseFieldMatrix<T> res = new SparseFieldMatrix<T>(field, virtualSize, n);
+ OpenIntToFieldHashMap<T>.Iterator iter = entries.iterator();
+ while (iter.hasNext()) {
+ iter.advance();
+ OpenIntToFieldHashMap<T>.Iterator iter2 = v.entries.iterator();
+ while (iter2.hasNext()) {
+ iter2.advance();
+ res.setEntry(iter.key(), iter2.key(), iter.value().multiply(iter2.value()));
+ }
+ }
+ return res;
+ }
+
+ /** {@inheritDoc} */
+ public FieldMatrix<T> outerProduct(FieldVector<T> v) {
+ if (v instanceof SparseFieldVector<?>) {
+ return outerProduct((SparseFieldVector<T>) v);
+ } else {
+ final int n = v.getDimension();
+ FieldMatrix<T> res = new SparseFieldMatrix<T>(field, virtualSize, n);
+ OpenIntToFieldHashMap<T>.Iterator iter = entries.iterator();
+ while (iter.hasNext()) {
+ iter.advance();
+ int row = iter.key();
+ FieldElement<T> value = iter.value();
+ for (int col = 0; col < n; col++) {
+ res.setEntry(row, col, value.multiply(v.getEntry(col)));
+ }
+ }
+ return res;
+ }
+ }
+
+ /** {@inheritDoc} */
+ public FieldVector<T> projection(FieldVector<T> v)
+ throws DimensionMismatchException, MathArithmeticException {
+ checkVectorDimensions(v.getDimension());
+ return v.mapMultiply(dotProduct(v).divide(v.dotProduct(v)));
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @exception NullArgumentException if value is null
+ */
+ public void set(T value) {
+ MathUtils.checkNotNull(value);
+ for (int i = 0; i < virtualSize; i++) {
+ setEntry(i, value);
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @exception NullArgumentException if value is null
+ */
+ public void setEntry(int index, T value) throws NullArgumentException, OutOfRangeException {
+ MathUtils.checkNotNull(value);
+ checkIndex(index);
+ entries.put(index, value);
+ }
+
+ /** {@inheritDoc} */
+ public void setSubVector(int index, FieldVector<T> v) throws OutOfRangeException {
+ checkIndex(index);
+ checkIndex(index + v.getDimension() - 1);
+ final int n = v.getDimension();
+ for (int i = 0; i < n; i++) {
+ setEntry(i + index, v.getEntry(i));
+ }
+ }
+
+ /**
+ * Optimized method to compute {@code this} minus {@code v}.
+ *
+ * @param v vector to be subtracted
+ * @return {@code this - v}
+ * @throws DimensionMismatchException if {@code v} is not the same size as {@code this}.
+ */
+ public SparseFieldVector<T> subtract(SparseFieldVector<T> v) throws DimensionMismatchException {
+ checkVectorDimensions(v.getDimension());
+ SparseFieldVector<T> res = (SparseFieldVector<T>) copy();
+ OpenIntToFieldHashMap<T>.Iterator iter = v.getEntries().iterator();
+ while (iter.hasNext()) {
+ iter.advance();
+ int key = iter.key();
+ if (entries.containsKey(key)) {
+ res.setEntry(key, entries.get(key).subtract(iter.value()));
+ } else {
+ res.setEntry(key, field.getZero().subtract(iter.value()));
+ }
+ }
+ return res;
+ }
+
+ /** {@inheritDoc} */
+ public FieldVector<T> subtract(FieldVector<T> v) throws DimensionMismatchException {
+ if (v instanceof SparseFieldVector<?>) {
+ return subtract((SparseFieldVector<T>) v);
+ } else {
+ final int n = v.getDimension();
+ checkVectorDimensions(n);
+ SparseFieldVector<T> res = new SparseFieldVector<T>(this);
+ for (int i = 0; i < n; i++) {
+ if (entries.containsKey(i)) {
+ res.setEntry(i, entries.get(i).subtract(v.getEntry(i)));
+ } else {
+ res.setEntry(i, field.getZero().subtract(v.getEntry(i)));
+ }
+ }
+ return res;
+ }
+ }
+
+ /** {@inheritDoc} */
+ public T[] toArray() {
+ T[] res = MathArrays.buildArray(field, virtualSize);
+ OpenIntToFieldHashMap<T>.Iterator iter = entries.iterator();
+ while (iter.hasNext()) {
+ iter.advance();
+ res[iter.key()] = iter.value();
+ }
+ return res;
+ }
+
+ /**
+ * Check whether an index is valid.
+ *
+ * @param index Index to check.
+ * @throws OutOfRangeException if the index is not valid.
+ */
+ private void checkIndex(final int index) throws OutOfRangeException {
+ if (index < 0 || index >= getDimension()) {
+ throw new OutOfRangeException(index, 0, getDimension() - 1);
+ }
+ }
+
+ /**
+ * Checks that the indices of a subvector are valid.
+ *
+ * @param start the index of the first entry of the subvector
+ * @param end the index of the last entry of the subvector (inclusive)
+ * @throws OutOfRangeException if {@code start} of {@code end} are not valid
+ * @throws NumberIsTooSmallException if {@code end < start}
+ * @since 3.3
+ */
+ private void checkIndices(final int start, final int end)
+ throws NumberIsTooSmallException, OutOfRangeException {
+ final int dim = getDimension();
+ if ((start < 0) || (start >= dim)) {
+ throw new OutOfRangeException(LocalizedFormats.INDEX, start, 0, dim - 1);
+ }
+ if ((end < 0) || (end >= dim)) {
+ throw new OutOfRangeException(LocalizedFormats.INDEX, end, 0, dim - 1);
+ }
+ if (end < start) {
+ throw new NumberIsTooSmallException(
+ LocalizedFormats.INITIAL_ROW_AFTER_FINAL_ROW, end, start, false);
+ }
+ }
+
+ /**
+ * Check if instance dimension is equal to some expected value.
+ *
+ * @param n Expected dimension.
+ * @throws DimensionMismatchException if the dimensions do not match.
+ */
+ protected void checkVectorDimensions(int n) throws DimensionMismatchException {
+ if (getDimension() != n) {
+ throw new DimensionMismatchException(getDimension(), n);
+ }
+ }
+
+ /** {@inheritDoc} */
+ public FieldVector<T> add(FieldVector<T> v) throws DimensionMismatchException {
+ if (v instanceof SparseFieldVector<?>) {
+ return add((SparseFieldVector<T>) v);
+ } else {
+ final int n = v.getDimension();
+ checkVectorDimensions(n);
+ SparseFieldVector<T> res = new SparseFieldVector<T>(field, getDimension());
+ for (int i = 0; i < n; i++) {
+ res.setEntry(i, v.getEntry(i).add(getEntry(i)));
+ }
+ return res;
+ }
+ }
+
+ /**
+ * Visits (but does not alter) all entries of this vector in default order (increasing index).
+ *
+ * @param visitor the visitor to be used to process the entries of this vector
+ * @return the value returned by {@link FieldVectorPreservingVisitor#end()} at the end of the
+ * walk
+ * @since 3.3
+ */
+ public T walkInDefaultOrder(final FieldVectorPreservingVisitor<T> visitor) {
+ final int dim = getDimension();
+ visitor.start(dim, 0, dim - 1);
+ for (int i = 0; i < dim; i++) {
+ visitor.visit(i, getEntry(i));
+ }
+ return visitor.end();
+ }
+
+ /**
+ * Visits (but does not alter) some entries of this vector in default order (increasing index).
+ *
+ * @param visitor visitor to be used to process the entries of this vector
+ * @param start the index of the first entry to be visited
+ * @param end the index of the last entry to be visited (inclusive)
+ * @return the value returned by {@link FieldVectorPreservingVisitor#end()} at the end of the
+ * walk
+ * @throws NumberIsTooSmallException if {@code end < start}.
+ * @throws OutOfRangeException if the indices are not valid.
+ * @since 3.3
+ */
+ public T walkInDefaultOrder(
+ final FieldVectorPreservingVisitor<T> visitor, final int start, final int end)
+ throws NumberIsTooSmallException, OutOfRangeException {
+ checkIndices(start, end);
+ visitor.start(getDimension(), start, end);
+ for (int i = start; i <= end; i++) {
+ visitor.visit(i, getEntry(i));
+ }
+ return visitor.end();
+ }
+
+ /**
+ * Visits (but does not alter) all entries of this vector in optimized order. The order in which
+ * the entries are visited is selected so as to lead to the most efficient implementation; it
+ * might depend on the concrete implementation of this abstract class.
+ *
+ * @param visitor the visitor to be used to process the entries of this vector
+ * @return the value returned by {@link FieldVectorPreservingVisitor#end()} at the end of the
+ * walk
+ * @since 3.3
+ */
+ public T walkInOptimizedOrder(final FieldVectorPreservingVisitor<T> visitor) {
+ return walkInDefaultOrder(visitor);
+ }
+
+ /**
+ * Visits (but does not alter) some entries of this vector in optimized order. The order in
+ * which the entries are visited is selected so as to lead to the most efficient implementation;
+ * it might depend on the concrete implementation of this abstract class.
+ *
+ * @param visitor visitor to be used to process the entries of this vector
+ * @param start the index of the first entry to be visited
+ * @param end the index of the last entry to be visited (inclusive)
+ * @return the value returned by {@link FieldVectorPreservingVisitor#end()} at the end of the
+ * walk
+ * @throws NumberIsTooSmallException if {@code end < start}.
+ * @throws OutOfRangeException if the indices are not valid.
+ * @since 3.3
+ */
+ public T walkInOptimizedOrder(
+ final FieldVectorPreservingVisitor<T> visitor, final int start, final int end)
+ throws NumberIsTooSmallException, OutOfRangeException {
+ return walkInDefaultOrder(visitor, start, end);
+ }
+
+ /**
+ * Visits (and possibly alters) all entries of this vector in default order (increasing index).
+ *
+ * @param visitor the visitor to be used to process and modify the entries of this vector
+ * @return the value returned by {@link FieldVectorChangingVisitor#end()} at the end of the walk
+ * @since 3.3
+ */
+ public T walkInDefaultOrder(final FieldVectorChangingVisitor<T> visitor) {
+ final int dim = getDimension();
+ visitor.start(dim, 0, dim - 1);
+ for (int i = 0; i < dim; i++) {
+ setEntry(i, visitor.visit(i, getEntry(i)));
+ }
+ return visitor.end();
+ }
+
+ /**
+ * Visits (and possibly alters) some entries of this vector in default order (increasing index).
+ *
+ * @param visitor visitor to be used to process the entries of this vector
+ * @param start the index of the first entry to be visited
+ * @param end the index of the last entry to be visited (inclusive)
+ * @return the value returned by {@link FieldVectorChangingVisitor#end()} at the end of the walk
+ * @throws NumberIsTooSmallException if {@code end < start}.
+ * @throws OutOfRangeException if the indices are not valid.
+ * @since 3.3
+ */
+ public T walkInDefaultOrder(
+ final FieldVectorChangingVisitor<T> visitor, final int start, final int end)
+ throws NumberIsTooSmallException, OutOfRangeException {
+ checkIndices(start, end);
+ visitor.start(getDimension(), start, end);
+ for (int i = start; i <= end; i++) {
+ setEntry(i, visitor.visit(i, getEntry(i)));
+ }
+ return visitor.end();
+ }
+
+ /**
+ * Visits (and possibly alters) all entries of this vector in optimized order. The order in
+ * which the entries are visited is selected so as to lead to the most efficient implementation;
+ * it might depend on the concrete implementation of this abstract class.
+ *
+ * @param visitor the visitor to be used to process the entries of this vector
+ * @return the value returned by {@link FieldVectorChangingVisitor#end()} at the end of the walk
+ * @since 3.3
+ */
+ public T walkInOptimizedOrder(final FieldVectorChangingVisitor<T> visitor) {
+ return walkInDefaultOrder(visitor);
+ }
+
+ /**
+ * Visits (and possibly change) some entries of this vector in optimized order. The order in
+ * which the entries are visited is selected so as to lead to the most efficient implementation;
+ * it might depend on the concrete implementation of this abstract class.
+ *
+ * @param visitor visitor to be used to process the entries of this vector
+ * @param start the index of the first entry to be visited
+ * @param end the index of the last entry to be visited (inclusive)
+ * @return the value returned by {@link FieldVectorChangingVisitor#end()} at the end of the walk
+ * @throws NumberIsTooSmallException if {@code end < start}.
+ * @throws OutOfRangeException if the indices are not valid.
+ * @since 3.3
+ */
+ public T walkInOptimizedOrder(
+ final FieldVectorChangingVisitor<T> visitor, final int start, final int end)
+ throws NumberIsTooSmallException, OutOfRangeException {
+ return walkInDefaultOrder(visitor, start, end);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public int hashCode() {
+ final int prime = 31;
+ int result = 1;
+ result = prime * result + ((field == null) ? 0 : field.hashCode());
+ result = prime * result + virtualSize;
+ OpenIntToFieldHashMap<T>.Iterator iter = entries.iterator();
+ while (iter.hasNext()) {
+ iter.advance();
+ int temp = iter.value().hashCode();
+ result = prime * result + temp;
+ }
+ return result;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public boolean equals(Object obj) {
+
+ if (this == obj) {
+ return true;
+ }
+
+ if (!(obj instanceof SparseFieldVector<?>)) {
+ return false;
+ }
+
+ @SuppressWarnings("unchecked") // OK, because "else if" check below ensures that
+ // other must be the same type as this
+ SparseFieldVector<T> other = (SparseFieldVector<T>) obj;
+ if (field == null) {
+ if (other.field != null) {
+ return false;
+ }
+ } else if (!field.equals(other.field)) {
+ return false;
+ }
+ if (virtualSize != other.virtualSize) {
+ return false;
+ }
+
+ OpenIntToFieldHashMap<T>.Iterator iter = entries.iterator();
+ while (iter.hasNext()) {
+ iter.advance();
+ T test = other.getEntry(iter.key());
+ if (!test.equals(iter.value())) {
+ return false;
+ }
+ }
+ iter = other.getEntries().iterator();
+ while (iter.hasNext()) {
+ iter.advance();
+ T test = iter.value();
+ if (!test.equals(getEntry(iter.key()))) {
+ return false;
+ }
+ }
+ return true;
+ }
+}
diff --git a/src/main/java/org/apache/commons/math3/linear/SparseRealMatrix.java b/src/main/java/org/apache/commons/math3/linear/SparseRealMatrix.java
new file mode 100644
index 0000000..093cadf
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/linear/SparseRealMatrix.java
@@ -0,0 +1,30 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math3.linear;
+
+/**
+ * Marker interface for {@link RealMatrix} implementations that require sparse backing storage
+ *
+ * <p>Caveat: Implementation are allowed to assume that, for any {@code x}, the equality {@code x *
+ * 0d == 0d} holds. But it is is not true for {@code NaN}. Moreover, zero entries will lose their
+ * sign. Some operations (that involve {@code NaN} and/or infinities) may thus give incorrect
+ * results.
+ *
+ * @since 2.0
+ */
+public interface SparseRealMatrix extends RealMatrix {}
diff --git a/src/main/java/org/apache/commons/math3/linear/SparseRealVector.java b/src/main/java/org/apache/commons/math3/linear/SparseRealVector.java
new file mode 100644
index 0000000..4eb2ce5
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/linear/SparseRealVector.java
@@ -0,0 +1,29 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math3.linear;
+
+/**
+ * Marker class for RealVectors that require sparse backing storage
+ *
+ * <p>Caveat: Implementation are allowed to assume that, for any {@code x}, the equality {@code x *
+ * 0d == 0d} holds. But it is is not true for {@code NaN}. Moreover, zero entries will lose their
+ * sign. Some operations (that involve {@code NaN} and/or infinities) may thus give incorrect
+ * results, like multiplications, divisions or functions mapping.
+ *
+ * @since 2.0
+ */
+public abstract class SparseRealVector extends RealVector {}
diff --git a/src/main/java/org/apache/commons/math3/linear/SymmLQ.java b/src/main/java/org/apache/commons/math3/linear/SymmLQ.java
new file mode 100644
index 0000000..bc7be0b
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/linear/SymmLQ.java
@@ -0,0 +1,1182 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math3.linear;
+
+import org.apache.commons.math3.exception.DimensionMismatchException;
+import org.apache.commons.math3.exception.MaxCountExceededException;
+import org.apache.commons.math3.exception.NullArgumentException;
+import org.apache.commons.math3.exception.util.ExceptionContext;
+import org.apache.commons.math3.util.FastMath;
+import org.apache.commons.math3.util.IterationManager;
+import org.apache.commons.math3.util.MathUtils;
+
+/**
+ * Implementation of the SYMMLQ iterative linear solver proposed by <a href="#PAIG1975">Paige and
+ * Saunders (1975)</a>. This implementation is largely based on the FORTRAN code by Pr. Michael A.
+ * Saunders, available <a href="http://www.stanford.edu/group/SOL/software/symmlq/f77/">here</a>.
+ *
+ * <p>SYMMLQ is designed to solve the system of linear equations A &middot; x = b where A is an n
+ * &times; n self-adjoint linear operator (defined as a {@link RealLinearOperator}), and b is a
+ * given vector. The operator A is not required to be positive definite. If A is known to be
+ * definite, the method of conjugate gradients might be preferred, since it will require about the
+ * same number of iterations as SYMMLQ but slightly less work per iteration.
+ *
+ * <p>SYMMLQ is designed to solve the system (A - shift &middot; I) &middot; x = b, where shift is a
+ * specified scalar value. If shift and b are suitably chosen, the computed vector x may approximate
+ * an (unnormalized) eigenvector of A, as in the methods of inverse iteration and/or
+ * Rayleigh-quotient iteration. Again, the linear operator (A - shift &middot; I) need not be
+ * positive definite (but <em>must</em> be self-adjoint). The work per iteration is very slightly
+ * less if shift = 0.
+ *
+ * <h3>Preconditioning</h3>
+ *
+ * <p>Preconditioning may reduce the number of iterations required. The solver may be provided with
+ * a positive definite preconditioner M = P<sup>T</sup> &middot; P that is known to approximate (A -
+ * shift &middot; I)<sup>-1</sup> in some sense, where matrix-vector products of the form M &middot;
+ * y = x can be computed efficiently. Then SYMMLQ will implicitly solve the system of equations P
+ * &middot; (A - shift &middot; I) &middot; P<sup>T</sup> &middot; x<sub>hat</sub> = P &middot; b,
+ * i.e. A<sub>hat</sub> &middot; x<sub>hat</sub> = b<sub>hat</sub>, where A<sub>hat</sub> = P
+ * &middot; (A - shift &middot; I) &middot; P<sup>T</sup>, b<sub>hat</sub> = P &middot; b, and
+ * return the solution x = P<sup>T</sup> &middot; x<sub>hat</sub>. The associated residual is
+ * r<sub>hat</sub> = b<sub>hat</sub> - A<sub>hat</sub> &middot; x<sub>hat</sub> = P &middot; [b - (A
+ * - shift &middot; I) &middot; x] = P &middot; r.
+ *
+ * <p>In the case of preconditioning, the {@link IterativeLinearSolverEvent}s that this solver fires
+ * are such that {@link IterativeLinearSolverEvent#getNormOfResidual()} returns the norm of the
+ * <em>preconditioned</em>, updated residual, ||P &middot; r||, not the norm of the <em>true</em>
+ * residual ||r||.
+ *
+ * <h3><a id="stopcrit">Default stopping criterion</a></h3>
+ *
+ * <p>A default stopping criterion is implemented. The iterations stop when || rhat || &le; &delta;
+ * || Ahat || || xhat ||, where xhat is the current estimate of the solution of the transformed
+ * system, rhat the current estimate of the corresponding residual, and &delta; a user-specified
+ * tolerance.
+ *
+ * <h3>Iteration count</h3>
+ *
+ * <p>In the present context, an iteration should be understood as one evaluation of the
+ * matrix-vector product A &middot; x. The initialization phase therefore counts as one iteration.
+ * If the user requires checks on the symmetry of A, this entails one further matrix-vector product
+ * in the initial phase. This further product is <em>not</em> accounted for in the iteration count.
+ * In other words, the number of iterations required to reach convergence will be identical, whether
+ * checks have been required or not.
+ *
+ * <p>The present definition of the iteration count differs from that adopted in the original FOTRAN
+ * code, where the initialization phase was <em>not</em> taken into account.
+ *
+ * <h3><a id="initguess">Initial guess of the solution</a></h3>
+ *
+ * <p>The {@code x} parameter in
+ *
+ * <ul>
+ * <li>{@link #solve(RealLinearOperator, RealVector, RealVector)},
+ * <li>{@link #solve(RealLinearOperator, RealLinearOperator, RealVector, RealVector)}},
+ * <li>{@link #solveInPlace(RealLinearOperator, RealVector, RealVector)},
+ * <li>{@link #solveInPlace(RealLinearOperator, RealLinearOperator, RealVector, RealVector)},
+ * <li>{@link #solveInPlace(RealLinearOperator, RealLinearOperator, RealVector, RealVector,
+ * boolean, double)},
+ * </ul>
+ *
+ * should not be considered as an initial guess, as it is set to zero in the initial phase. If
+ * x<sub>0</sub> is known to be a good approximation to x, one should compute r<sub>0</sub> = b - A
+ * &middot; x, solve A &middot; dx = r0, and set x = x<sub>0</sub> + dx.
+ *
+ * <h3><a id="context">Exception context</a></h3>
+ *
+ * <p>Besides standard {@link DimensionMismatchException}, this class might throw {@link
+ * NonSelfAdjointOperatorException} if the linear operator or the preconditioner are not symmetric.
+ * In this case, the {@link ExceptionContext} provides more information
+ *
+ * <ul>
+ * <li>key {@code "operator"} points to the offending linear operator, say L,
+ * <li>key {@code "vector1"} points to the first offending vector, say x,
+ * <li>key {@code "vector2"} points to the second offending vector, say y, such that x<sup>T</sup>
+ * &middot; L &middot; y &ne; y<sup>T</sup> &middot; L &middot; x (within a certain accuracy).
+ * </ul>
+ *
+ * <p>{@link NonPositiveDefiniteOperatorException} might also be thrown in case the preconditioner
+ * is not positive definite. The relevant keys to the {@link ExceptionContext} are
+ *
+ * <ul>
+ * <li>key {@code "operator"}, which points to the offending linear operator, say L,
+ * <li>key {@code "vector"}, which points to the offending vector, say x, such that x<sup>T</sup>
+ * &middot; L &middot; x < 0.
+ * </ul>
+ *
+ * <h3>References</h3>
+ *
+ * <dl>
+ * <dt><a id="PAIG1975">Paige and Saunders (1975)</a>
+ * <dd>C. C. Paige and M. A. Saunders, <a
+ * href="http://www.stanford.edu/group/SOL/software/symmlq/PS75.pdf"><em> Solution of Sparse
+ * Indefinite Systems of Linear Equations</em></a>, SIAM Journal on Numerical Analysis 12(4):
+ * 617-629, 1975
+ * </dl>
+ *
+ * @since 3.0
+ */
+public class SymmLQ extends PreconditionedIterativeLinearSolver {
+
+ /*
+ * IMPLEMENTATION NOTES
+ * --------------------
+ * The implementation follows as closely as possible the notations of Paige
+ * and Saunders (1975). Attention must be paid to the fact that some
+ * quantities which are relevant to iteration k can only be computed in
+ * iteration (k+1). Therefore, minute attention must be paid to the index of
+ * each state variable of this algorithm.
+ *
+ * 1. Preconditioning
+ * ---------------
+ * The Lanczos iterations associated with Ahat and bhat read
+ * beta[1] = ||P * b||
+ * v[1] = P * b / beta[1]
+ * beta[k+1] * v[k+1] = Ahat * v[k] - alpha[k] * v[k] - beta[k] * v[k-1]
+ * = P * (A - shift * I) * P' * v[k] - alpha[k] * v[k]
+ * - beta[k] * v[k-1]
+ * Multiplying both sides by P', we get
+ * beta[k+1] * (P' * v)[k+1] = M * (A - shift * I) * (P' * v)[k]
+ * - alpha[k] * (P' * v)[k]
+ * - beta[k] * (P' * v[k-1]),
+ * and
+ * alpha[k+1] = v[k+1]' * Ahat * v[k+1]
+ * = v[k+1]' * P * (A - shift * I) * P' * v[k+1]
+ * = (P' * v)[k+1]' * (A - shift * I) * (P' * v)[k+1].
+ *
+ * In other words, the Lanczos iterations are unchanged, except for the fact
+ * that we really compute (P' * v) instead of v. It can easily be checked
+ * that all other formulas are unchanged. It must be noted that P is never
+ * explicitly used, only matrix-vector products involving are invoked.
+ *
+ * 2. Accounting for the shift parameter
+ * ----------------------------------
+ * Is trivial: each time A.operate(x) is invoked, one must subtract shift * x
+ * to the result.
+ *
+ * 3. Accounting for the goodb flag
+ * -----------------------------
+ * When goodb is set to true, the component of xL along b is computed
+ * separately. From Paige and Saunders (1975), equation (5.9), we have
+ * wbar[k+1] = s[k] * wbar[k] - c[k] * v[k+1],
+ * wbar[1] = v[1].
+ * Introducing wbar2[k] = wbar[k] - s[1] * ... * s[k-1] * v[1], it can
+ * easily be verified by induction that wbar2 follows the same recursive
+ * relation
+ * wbar2[k+1] = s[k] * wbar2[k] - c[k] * v[k+1],
+ * wbar2[1] = 0,
+ * and we then have
+ * w[k] = c[k] * wbar2[k] + s[k] * v[k+1]
+ * + s[1] * ... * s[k-1] * c[k] * v[1].
+ * Introducing w2[k] = w[k] - s[1] * ... * s[k-1] * c[k] * v[1], we find,
+ * from (5.10)
+ * xL[k] = zeta[1] * w[1] + ... + zeta[k] * w[k]
+ * = zeta[1] * w2[1] + ... + zeta[k] * w2[k]
+ * + (s[1] * c[2] * zeta[2] + ...
+ * + s[1] * ... * s[k-1] * c[k] * zeta[k]) * v[1]
+ * = xL2[k] + bstep[k] * v[1],
+ * where xL2[k] is defined by
+ * xL2[0] = 0,
+ * xL2[k+1] = xL2[k] + zeta[k+1] * w2[k+1],
+ * and bstep is defined by
+ * bstep[1] = 0,
+ * bstep[k] = bstep[k-1] + s[1] * ... * s[k-1] * c[k] * zeta[k].
+ * We also have, from (5.11)
+ * xC[k] = xL[k-1] + zbar[k] * wbar[k]
+ * = xL2[k-1] + zbar[k] * wbar2[k]
+ * + (bstep[k-1] + s[1] * ... * s[k-1] * zbar[k]) * v[1].
+ */
+
+ /**
+ * A simple container holding the non-final variables used in the iterations. Making the current
+ * state of the solver visible from the outside is necessary, because during the iterations,
+ * {@code x} does not <em>exactly</em> hold the current estimate of the solution. Indeed, {@code
+ * x} needs in general to be moved from the LQ point to the CG point. Besides, additional
+ * upudates must be carried out in case {@code goodb} is set to {@code true}.
+ *
+ * <p>In all subsequent comments, the description of the state variables refer to their value
+ * after a call to {@link #update()}. In these comments, k is the current number of evaluations
+ * of matrix-vector products.
+ */
+ private static class State {
+ /** The cubic root of {@link #MACH_PREC}. */
+ static final double CBRT_MACH_PREC;
+
+ /** The machine precision. */
+ static final double MACH_PREC;
+
+ /** Reference to the linear operator. */
+ private final RealLinearOperator a;
+
+ /** Reference to the right-hand side vector. */
+ private final RealVector b;
+
+ /** {@code true} if symmetry of matrix and conditioner must be checked. */
+ private final boolean check;
+
+ /** The value of the custom tolerance &delta; for the default stopping criterion. */
+ private final double delta;
+
+ /** The value of beta[k+1]. */
+ private double beta;
+
+ /** The value of beta[1]. */
+ private double beta1;
+
+ /** The value of bstep[k-1]. */
+ private double bstep;
+
+ /** The estimate of the norm of P * rC[k]. */
+ private double cgnorm;
+
+ /** The value of dbar[k+1] = -beta[k+1] * c[k-1]. */
+ private double dbar;
+
+ /** The value of gamma[k] * zeta[k]. Was called {@code rhs1} in the initial code. */
+ private double gammaZeta;
+
+ /** The value of gbar[k]. */
+ private double gbar;
+
+ /** The value of max(|alpha[1]|, gamma[1], ..., gamma[k-1]). */
+ private double gmax;
+
+ /** The value of min(|alpha[1]|, gamma[1], ..., gamma[k-1]). */
+ private double gmin;
+
+ /** Copy of the {@code goodb} parameter. */
+ private final boolean goodb;
+
+ /** {@code true} if the default convergence criterion is verified. */
+ private boolean hasConverged;
+
+ /** The estimate of the norm of P * rL[k-1]. */
+ private double lqnorm;
+
+ /** Reference to the preconditioner, M. */
+ private final RealLinearOperator m;
+
+ /** The value of (-eps[k+1] * zeta[k-1]). Was called {@code rhs2} in the initial code. */
+ private double minusEpsZeta;
+
+ /** The value of M * b. */
+ private final RealVector mb;
+
+ /** The value of beta[k]. */
+ private double oldb;
+
+ /** The value of beta[k] * M^(-1) * P' * v[k]. */
+ private RealVector r1;
+
+ /** The value of beta[k+1] * M^(-1) * P' * v[k+1]. */
+ private RealVector r2;
+
+ /**
+ * The value of the updated, preconditioned residual P * r. This value is given by {@code
+ * min(}{@link #cgnorm}{@code , }{@link #lqnorm}{@code )}.
+ */
+ private double rnorm;
+
+ /** Copy of the {@code shift} parameter. */
+ private final double shift;
+
+ /** The value of s[1] * ... * s[k-1]. */
+ private double snprod;
+
+ /**
+ * An estimate of the square of the norm of A * V[k], based on Paige and Saunders (1975),
+ * equation (3.3).
+ */
+ private double tnorm;
+
+ /**
+ * The value of P' * wbar[k] or P' * (wbar[k] - s[1] * ... * s[k-1] * v[1]) if {@code goodb}
+ * is {@code true}. Was called {@code w} in the initial code.
+ */
+ private RealVector wbar;
+
+ /**
+ * A reference to the vector to be updated with the solution. Contains the value of xL[k-1]
+ * if {@code goodb} is {@code false}, (xL[k-1] - bstep[k-1] * v[1]) otherwise.
+ */
+ private final RealVector xL;
+
+ /** The value of beta[k+1] * P' * v[k+1]. */
+ private RealVector y;
+
+ /** The value of zeta[1]^2 + ... + zeta[k-1]^2. */
+ private double ynorm2;
+
+ /** The value of {@code b == 0} (exact floating-point equality). */
+ private boolean bIsNull;
+
+ static {
+ MACH_PREC = FastMath.ulp(1.);
+ CBRT_MACH_PREC = FastMath.cbrt(MACH_PREC);
+ }
+
+ /**
+ * Creates and inits to k = 1 a new instance of this class.
+ *
+ * @param a the linear operator A of the system
+ * @param m the preconditioner, M (can be {@code null})
+ * @param b the right-hand side vector
+ * @param goodb usually {@code false}, except if {@code x} is expected to contain a large
+ * multiple of {@code b}
+ * @param shift the amount to be subtracted to all diagonal elements of A
+ * @param delta the &delta; parameter for the default stopping criterion
+ * @param check {@code true} if self-adjointedness of both matrix and preconditioner should
+ * be checked
+ */
+ State(
+ final RealLinearOperator a,
+ final RealLinearOperator m,
+ final RealVector b,
+ final boolean goodb,
+ final double shift,
+ final double delta,
+ final boolean check) {
+ this.a = a;
+ this.m = m;
+ this.b = b;
+ this.xL = new ArrayRealVector(b.getDimension());
+ this.goodb = goodb;
+ this.shift = shift;
+ this.mb = m == null ? b : m.operate(b);
+ this.hasConverged = false;
+ this.check = check;
+ this.delta = delta;
+ }
+
+ /**
+ * Performs a symmetry check on the specified linear operator, and throws an exception in
+ * case this check fails. Given a linear operator L, and a vector x, this method checks that
+ * x' &middot; L &middot; y = y' &middot; L &middot; x (within a given accuracy), where y =
+ * L &middot; x.
+ *
+ * @param l the linear operator L
+ * @param x the candidate vector x
+ * @param y the candidate vector y = L &middot; x
+ * @param z the vector z = L &middot; y
+ * @throws NonSelfAdjointOperatorException when the test fails
+ */
+ private static void checkSymmetry(
+ final RealLinearOperator l,
+ final RealVector x,
+ final RealVector y,
+ final RealVector z)
+ throws NonSelfAdjointOperatorException {
+ final double s = y.dotProduct(y);
+ final double t = x.dotProduct(z);
+ final double epsa = (s + MACH_PREC) * CBRT_MACH_PREC;
+ if (FastMath.abs(s - t) > epsa) {
+ final NonSelfAdjointOperatorException e;
+ e = new NonSelfAdjointOperatorException();
+ final ExceptionContext context = e.getContext();
+ context.setValue(SymmLQ.OPERATOR, l);
+ context.setValue(SymmLQ.VECTOR1, x);
+ context.setValue(SymmLQ.VECTOR2, y);
+ context.setValue(SymmLQ.THRESHOLD, Double.valueOf(epsa));
+ throw e;
+ }
+ }
+
+ /**
+ * Throws a new {@link NonPositiveDefiniteOperatorException} with appropriate context.
+ *
+ * @param l the offending linear operator
+ * @param v the offending vector
+ * @throws NonPositiveDefiniteOperatorException in any circumstances
+ */
+ private static void throwNPDLOException(final RealLinearOperator l, final RealVector v)
+ throws NonPositiveDefiniteOperatorException {
+ final NonPositiveDefiniteOperatorException e;
+ e = new NonPositiveDefiniteOperatorException();
+ final ExceptionContext context = e.getContext();
+ context.setValue(OPERATOR, l);
+ context.setValue(VECTOR, v);
+ throw e;
+ }
+
+ /**
+ * A clone of the BLAS {@code DAXPY} function, which carries out the operation y &larr; a
+ * &middot; x + y. This is for internal use only: no dimension checks are provided.
+ *
+ * @param a the scalar by which {@code x} is to be multiplied
+ * @param x the vector to be added to {@code y}
+ * @param y the vector to be incremented
+ */
+ private static void daxpy(final double a, final RealVector x, final RealVector y) {
+ final int n = x.getDimension();
+ for (int i = 0; i < n; i++) {
+ y.setEntry(i, a * x.getEntry(i) + y.getEntry(i));
+ }
+ }
+
+ /**
+ * A BLAS-like function, for the operation z &larr; a &middot; x + b &middot; y + z. This is
+ * for internal use only: no dimension checks are provided.
+ *
+ * @param a the scalar by which {@code x} is to be multiplied
+ * @param x the first vector to be added to {@code z}
+ * @param b the scalar by which {@code y} is to be multiplied
+ * @param y the second vector to be added to {@code z}
+ * @param z the vector to be incremented
+ */
+ private static void daxpbypz(
+ final double a,
+ final RealVector x,
+ final double b,
+ final RealVector y,
+ final RealVector z) {
+ final int n = z.getDimension();
+ for (int i = 0; i < n; i++) {
+ final double zi;
+ zi = a * x.getEntry(i) + b * y.getEntry(i) + z.getEntry(i);
+ z.setEntry(i, zi);
+ }
+ }
+
+ /**
+ * Move to the CG point if it seems better. In this version of SYMMLQ, the convergence tests
+ * involve only cgnorm, so we're unlikely to stop at an LQ point, except if the iteration
+ * limit interferes.
+ *
+ * <p>Additional upudates are also carried out in case {@code goodb} is set to {@code true}.
+ *
+ * @param x the vector to be updated with the refined value of xL
+ */
+ void refineSolution(final RealVector x) {
+ final int n = this.xL.getDimension();
+ if (lqnorm < cgnorm) {
+ if (!goodb) {
+ x.setSubVector(0, this.xL);
+ } else {
+ final double step = bstep / beta1;
+ for (int i = 0; i < n; i++) {
+ final double bi = mb.getEntry(i);
+ final double xi = this.xL.getEntry(i);
+ x.setEntry(i, xi + step * bi);
+ }
+ }
+ } else {
+ final double anorm = FastMath.sqrt(tnorm);
+ final double diag = gbar == 0. ? anorm * MACH_PREC : gbar;
+ final double zbar = gammaZeta / diag;
+ final double step = (bstep + snprod * zbar) / beta1;
+ // ynorm = FastMath.sqrt(ynorm2 + zbar * zbar);
+ if (!goodb) {
+ for (int i = 0; i < n; i++) {
+ final double xi = this.xL.getEntry(i);
+ final double wi = wbar.getEntry(i);
+ x.setEntry(i, xi + zbar * wi);
+ }
+ } else {
+ for (int i = 0; i < n; i++) {
+ final double xi = this.xL.getEntry(i);
+ final double wi = wbar.getEntry(i);
+ final double bi = mb.getEntry(i);
+ x.setEntry(i, xi + zbar * wi + step * bi);
+ }
+ }
+ }
+ }
+
+ /**
+ * Performs the initial phase of the SYMMLQ algorithm. On return, the value of the state
+ * variables of {@code this} object correspond to k = 1.
+ */
+ void init() {
+ this.xL.set(0.);
+ /*
+ * Set up y for the first Lanczos vector. y and beta1 will be zero
+ * if b = 0.
+ */
+ this.r1 = this.b.copy();
+ this.y = this.m == null ? this.b.copy() : this.m.operate(this.r1);
+ if ((this.m != null) && this.check) {
+ checkSymmetry(this.m, this.r1, this.y, this.m.operate(this.y));
+ }
+
+ this.beta1 = this.r1.dotProduct(this.y);
+ if (this.beta1 < 0.) {
+ throwNPDLOException(this.m, this.y);
+ }
+ if (this.beta1 == 0.) {
+ /* If b = 0 exactly, stop with x = 0. */
+ this.bIsNull = true;
+ return;
+ }
+ this.bIsNull = false;
+ this.beta1 = FastMath.sqrt(this.beta1);
+ /* At this point
+ * r1 = b,
+ * y = M * b,
+ * beta1 = beta[1].
+ */
+ final RealVector v = this.y.mapMultiply(1. / this.beta1);
+ this.y = this.a.operate(v);
+ if (this.check) {
+ checkSymmetry(this.a, v, this.y, this.a.operate(this.y));
+ }
+ /*
+ * Set up y for the second Lanczos vector. y and beta will be zero
+ * or very small if b is an eigenvector.
+ */
+ daxpy(-this.shift, v, this.y);
+ final double alpha = v.dotProduct(this.y);
+ daxpy(-alpha / this.beta1, this.r1, this.y);
+ /*
+ * At this point
+ * alpha = alpha[1]
+ * y = beta[2] * M^(-1) * P' * v[2]
+ */
+ /* Make sure r2 will be orthogonal to the first v. */
+ final double vty = v.dotProduct(this.y);
+ final double vtv = v.dotProduct(v);
+ daxpy(-vty / vtv, v, this.y);
+ this.r2 = this.y.copy();
+ if (this.m != null) {
+ this.y = this.m.operate(this.r2);
+ }
+ this.oldb = this.beta1;
+ this.beta = this.r2.dotProduct(this.y);
+ if (this.beta < 0.) {
+ throwNPDLOException(this.m, this.y);
+ }
+ this.beta = FastMath.sqrt(this.beta);
+ /*
+ * At this point
+ * oldb = beta[1]
+ * beta = beta[2]
+ * y = beta[2] * P' * v[2]
+ * r2 = beta[2] * M^(-1) * P' * v[2]
+ */
+ this.cgnorm = this.beta1;
+ this.gbar = alpha;
+ this.dbar = this.beta;
+ this.gammaZeta = this.beta1;
+ this.minusEpsZeta = 0.;
+ this.bstep = 0.;
+ this.snprod = 1.;
+ this.tnorm = alpha * alpha + this.beta * this.beta;
+ this.ynorm2 = 0.;
+ this.gmax = FastMath.abs(alpha) + MACH_PREC;
+ this.gmin = this.gmax;
+
+ if (this.goodb) {
+ this.wbar = new ArrayRealVector(this.a.getRowDimension());
+ this.wbar.set(0.);
+ } else {
+ this.wbar = v;
+ }
+ updateNorms();
+ }
+
+ /**
+ * Performs the next iteration of the algorithm. The iteration count should be incremented
+ * prior to calling this method. On return, the value of the state variables of {@code this}
+ * object correspond to the current iteration count {@code k}.
+ */
+ void update() {
+ final RealVector v = y.mapMultiply(1. / beta);
+ y = a.operate(v);
+ daxpbypz(-shift, v, -beta / oldb, r1, y);
+ final double alpha = v.dotProduct(y);
+ /*
+ * At this point
+ * v = P' * v[k],
+ * y = (A - shift * I) * P' * v[k] - beta[k] * M^(-1) * P' * v[k-1],
+ * alpha = v'[k] * P * (A - shift * I) * P' * v[k]
+ * - beta[k] * v[k]' * P * M^(-1) * P' * v[k-1]
+ * = v'[k] * P * (A - shift * I) * P' * v[k]
+ * - beta[k] * v[k]' * v[k-1]
+ * = alpha[k].
+ */
+ daxpy(-alpha / beta, r2, y);
+ /*
+ * At this point
+ * y = (A - shift * I) * P' * v[k] - alpha[k] * M^(-1) * P' * v[k]
+ * - beta[k] * M^(-1) * P' * v[k-1]
+ * = M^(-1) * P' * (P * (A - shift * I) * P' * v[k] -alpha[k] * v[k]
+ * - beta[k] * v[k-1])
+ * = beta[k+1] * M^(-1) * P' * v[k+1],
+ * from Paige and Saunders (1975), equation (3.2).
+ *
+ * WATCH-IT: the two following lines work only because y is no longer
+ * updated up to the end of the present iteration, and is
+ * reinitialized at the beginning of the next iteration.
+ */
+ r1 = r2;
+ r2 = y;
+ if (m != null) {
+ y = m.operate(r2);
+ }
+ oldb = beta;
+ beta = r2.dotProduct(y);
+ if (beta < 0.) {
+ throwNPDLOException(m, y);
+ }
+ beta = FastMath.sqrt(beta);
+ /*
+ * At this point
+ * r1 = beta[k] * M^(-1) * P' * v[k],
+ * r2 = beta[k+1] * M^(-1) * P' * v[k+1],
+ * y = beta[k+1] * P' * v[k+1],
+ * oldb = beta[k],
+ * beta = beta[k+1].
+ */
+ tnorm += alpha * alpha + oldb * oldb + beta * beta;
+ /*
+ * Compute the next plane rotation for Q. See Paige and Saunders
+ * (1975), equation (5.6), with
+ * gamma = gamma[k-1],
+ * c = c[k-1],
+ * s = s[k-1].
+ */
+ final double gamma = FastMath.sqrt(gbar * gbar + oldb * oldb);
+ final double c = gbar / gamma;
+ final double s = oldb / gamma;
+ /*
+ * The relations
+ * gbar[k] = s[k-1] * (-c[k-2] * beta[k]) - c[k-1] * alpha[k]
+ * = s[k-1] * dbar[k] - c[k-1] * alpha[k],
+ * delta[k] = c[k-1] * dbar[k] + s[k-1] * alpha[k],
+ * are not stated in Paige and Saunders (1975), but can be retrieved
+ * by expanding the (k, k-1) and (k, k) coefficients of the matrix in
+ * equation (5.5).
+ */
+ final double deltak = c * dbar + s * alpha;
+ gbar = s * dbar - c * alpha;
+ final double eps = s * beta;
+ dbar = -c * beta;
+ final double zeta = gammaZeta / gamma;
+ /*
+ * At this point
+ * gbar = gbar[k]
+ * deltak = delta[k]
+ * eps = eps[k+1]
+ * dbar = dbar[k+1]
+ * zeta = zeta[k-1]
+ */
+ final double zetaC = zeta * c;
+ final double zetaS = zeta * s;
+ final int n = xL.getDimension();
+ for (int i = 0; i < n; i++) {
+ final double xi = xL.getEntry(i);
+ final double vi = v.getEntry(i);
+ final double wi = wbar.getEntry(i);
+ xL.setEntry(i, xi + wi * zetaC + vi * zetaS);
+ wbar.setEntry(i, wi * s - vi * c);
+ }
+ /*
+ * At this point
+ * x = xL[k-1],
+ * ptwbar = P' wbar[k],
+ * see Paige and Saunders (1975), equations (5.9) and (5.10).
+ */
+ bstep += snprod * c * zeta;
+ snprod *= s;
+ gmax = FastMath.max(gmax, gamma);
+ gmin = FastMath.min(gmin, gamma);
+ ynorm2 += zeta * zeta;
+ gammaZeta = minusEpsZeta - deltak * zeta;
+ minusEpsZeta = -eps * zeta;
+ /*
+ * At this point
+ * snprod = s[1] * ... * s[k-1],
+ * gmax = max(|alpha[1]|, gamma[1], ..., gamma[k-1]),
+ * gmin = min(|alpha[1]|, gamma[1], ..., gamma[k-1]),
+ * ynorm2 = zeta[1]^2 + ... + zeta[k-1]^2,
+ * gammaZeta = gamma[k] * zeta[k],
+ * minusEpsZeta = -eps[k+1] * zeta[k-1].
+ * The relation for gammaZeta can be retrieved from Paige and
+ * Saunders (1975), equation (5.4a), last line of the vector
+ * gbar[k] * zbar[k] = -eps[k] * zeta[k-2] - delta[k] * zeta[k-1].
+ */
+ updateNorms();
+ }
+
+ /**
+ * Computes the norms of the residuals, and checks for convergence. Updates {@link #lqnorm}
+ * and {@link #cgnorm}.
+ */
+ private void updateNorms() {
+ final double anorm = FastMath.sqrt(tnorm);
+ final double ynorm = FastMath.sqrt(ynorm2);
+ final double epsa = anorm * MACH_PREC;
+ final double epsx = anorm * ynorm * MACH_PREC;
+ final double epsr = anorm * ynorm * delta;
+ final double diag = gbar == 0. ? epsa : gbar;
+ lqnorm = FastMath.sqrt(gammaZeta * gammaZeta + minusEpsZeta * minusEpsZeta);
+ final double qrnorm = snprod * beta1;
+ cgnorm = qrnorm * beta / FastMath.abs(diag);
+
+ /*
+ * Estimate cond(A). In this version we look at the diagonals of L
+ * in the factorization of the tridiagonal matrix, T = L * Q.
+ * Sometimes, T[k] can be misleadingly ill-conditioned when T[k+1]
+ * is not, so we must be careful not to overestimate acond.
+ */
+ final double acond;
+ if (lqnorm <= cgnorm) {
+ acond = gmax / gmin;
+ } else {
+ acond = gmax / FastMath.min(gmin, FastMath.abs(diag));
+ }
+ if (acond * MACH_PREC >= 0.1) {
+ throw new IllConditionedOperatorException(acond);
+ }
+ if (beta1 <= epsx) {
+ /*
+ * x has converged to an eigenvector of A corresponding to the
+ * eigenvalue shift.
+ */
+ throw new SingularOperatorException();
+ }
+ rnorm = FastMath.min(cgnorm, lqnorm);
+ hasConverged = (cgnorm <= epsx) || (cgnorm <= epsr);
+ }
+
+ /**
+ * Returns {@code true} if the default stopping criterion is fulfilled.
+ *
+ * @return {@code true} if convergence of the iterations has occurred
+ */
+ boolean hasConverged() {
+ return hasConverged;
+ }
+
+ /**
+ * Returns {@code true} if the right-hand side vector is zero exactly.
+ *
+ * @return the boolean value of {@code b == 0}
+ */
+ boolean bEqualsNullVector() {
+ return bIsNull;
+ }
+
+ /**
+ * Returns {@code true} if {@code beta} is essentially zero. This method is used to check
+ * for early stop of the iterations.
+ *
+ * @return {@code true} if {@code beta < }{@link #MACH_PREC}
+ */
+ boolean betaEqualsZero() {
+ return beta < MACH_PREC;
+ }
+
+ /**
+ * Returns the norm of the updated, preconditioned residual.
+ *
+ * @return the norm of the residual, ||P * r||
+ */
+ double getNormOfResidual() {
+ return rnorm;
+ }
+ }
+
+ /** Key for the exception context. */
+ private static final String OPERATOR = "operator";
+
+ /** Key for the exception context. */
+ private static final String THRESHOLD = "threshold";
+
+ /** Key for the exception context. */
+ private static final String VECTOR = "vector";
+
+ /** Key for the exception context. */
+ private static final String VECTOR1 = "vector1";
+
+ /** Key for the exception context. */
+ private static final String VECTOR2 = "vector2";
+
+ /** {@code true} if symmetry of matrix and conditioner must be checked. */
+ private final boolean check;
+
+ /** The value of the custom tolerance &delta; for the default stopping criterion. */
+ private final double delta;
+
+ /**
+ * Creates a new instance of this class, with <a href="#stopcrit">default stopping
+ * criterion</a>. Note that setting {@code check} to {@code true} entails an extra matrix-vector
+ * product in the initial phase.
+ *
+ * @param maxIterations the maximum number of iterations
+ * @param delta the &delta; parameter for the default stopping criterion
+ * @param check {@code true} if self-adjointedness of both matrix and preconditioner should be
+ * checked
+ */
+ public SymmLQ(final int maxIterations, final double delta, final boolean check) {
+ super(maxIterations);
+ this.delta = delta;
+ this.check = check;
+ }
+
+ /**
+ * Creates a new instance of this class, with <a href="#stopcrit">default stopping criterion</a>
+ * and custom iteration manager. Note that setting {@code check} to {@code true} entails an
+ * extra matrix-vector product in the initial phase.
+ *
+ * @param manager the custom iteration manager
+ * @param delta the &delta; parameter for the default stopping criterion
+ * @param check {@code true} if self-adjointedness of both matrix and preconditioner should be
+ * checked
+ */
+ public SymmLQ(final IterationManager manager, final double delta, final boolean check) {
+ super(manager);
+ this.delta = delta;
+ this.check = check;
+ }
+
+ /**
+ * Returns {@code true} if symmetry of the matrix, and symmetry as well as positive definiteness
+ * of the preconditioner should be checked.
+ *
+ * @return {@code true} if the tests are to be performed
+ */
+ public final boolean getCheck() {
+ return check;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @throws NonSelfAdjointOperatorException if {@link #getCheck()} is {@code true}, and {@code a}
+ * or {@code m} is not self-adjoint
+ * @throws NonPositiveDefiniteOperatorException if {@code m} is not positive definite
+ * @throws IllConditionedOperatorException if {@code a} is ill-conditioned
+ */
+ @Override
+ public RealVector solve(
+ final RealLinearOperator a, final RealLinearOperator m, final RealVector b)
+ throws NullArgumentException,
+ NonSquareOperatorException,
+ DimensionMismatchException,
+ MaxCountExceededException,
+ NonSelfAdjointOperatorException,
+ NonPositiveDefiniteOperatorException,
+ IllConditionedOperatorException {
+ MathUtils.checkNotNull(a);
+ final RealVector x = new ArrayRealVector(a.getColumnDimension());
+ return solveInPlace(a, m, b, x, false, 0.);
+ }
+
+ /**
+ * Returns an estimate of the solution to the linear system (A - shift &middot; I) &middot; x =
+ * b.
+ *
+ * <p>If the solution x is expected to contain a large multiple of {@code b} (as in
+ * Rayleigh-quotient iteration), then better precision may be achieved with {@code goodb} set to
+ * {@code true}; this however requires an extra call to the preconditioner.
+ *
+ * <p>{@code shift} should be zero if the system A &middot; x = b is to be solved. Otherwise, it
+ * could be an approximation to an eigenvalue of A, such as the Rayleigh quotient b<sup>T</sup>
+ * &middot; A &middot; b / (b<sup>T</sup> &middot; b) corresponding to the vector b. If b is
+ * sufficiently like an eigenvector corresponding to an eigenvalue near shift, then the computed
+ * x may have very large components. When normalized, x may be closer to an eigenvector than b.
+ *
+ * @param a the linear operator A of the system
+ * @param m the preconditioner, M (can be {@code null})
+ * @param b the right-hand side vector
+ * @param goodb usually {@code false}, except if {@code x} is expected to contain a large
+ * multiple of {@code b}
+ * @param shift the amount to be subtracted to all diagonal elements of A
+ * @return a reference to {@code x} (shallow copy)
+ * @throws NullArgumentException if one of the parameters is {@code null}
+ * @throws NonSquareOperatorException if {@code a} or {@code m} is not square
+ * @throws DimensionMismatchException if {@code m} or {@code b} have dimensions inconsistent
+ * with {@code a}
+ * @throws MaxCountExceededException at exhaustion of the iteration count, unless a custom
+ * {@link org.apache.commons.math3.util.Incrementor.MaxCountExceededCallback callback} has
+ * been set at construction of the {@link IterationManager}
+ * @throws NonSelfAdjointOperatorException if {@link #getCheck()} is {@code true}, and {@code a}
+ * or {@code m} is not self-adjoint
+ * @throws NonPositiveDefiniteOperatorException if {@code m} is not positive definite
+ * @throws IllConditionedOperatorException if {@code a} is ill-conditioned
+ */
+ public RealVector solve(
+ final RealLinearOperator a,
+ final RealLinearOperator m,
+ final RealVector b,
+ final boolean goodb,
+ final double shift)
+ throws NullArgumentException,
+ NonSquareOperatorException,
+ DimensionMismatchException,
+ MaxCountExceededException,
+ NonSelfAdjointOperatorException,
+ NonPositiveDefiniteOperatorException,
+ IllConditionedOperatorException {
+ MathUtils.checkNotNull(a);
+ final RealVector x = new ArrayRealVector(a.getColumnDimension());
+ return solveInPlace(a, m, b, x, goodb, shift);
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @param x not meaningful in this implementation; should not be considered as an initial guess
+ * (<a href="#initguess">more</a>)
+ * @throws NonSelfAdjointOperatorException if {@link #getCheck()} is {@code true}, and {@code a}
+ * or {@code m} is not self-adjoint
+ * @throws NonPositiveDefiniteOperatorException if {@code m} is not positive definite
+ * @throws IllConditionedOperatorException if {@code a} is ill-conditioned
+ */
+ @Override
+ public RealVector solve(
+ final RealLinearOperator a,
+ final RealLinearOperator m,
+ final RealVector b,
+ final RealVector x)
+ throws NullArgumentException,
+ NonSquareOperatorException,
+ DimensionMismatchException,
+ NonSelfAdjointOperatorException,
+ NonPositiveDefiniteOperatorException,
+ IllConditionedOperatorException,
+ MaxCountExceededException {
+ MathUtils.checkNotNull(x);
+ return solveInPlace(a, m, b, x.copy(), false, 0.);
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @throws NonSelfAdjointOperatorException if {@link #getCheck()} is {@code true}, and {@code a}
+ * is not self-adjoint
+ * @throws IllConditionedOperatorException if {@code a} is ill-conditioned
+ */
+ @Override
+ public RealVector solve(final RealLinearOperator a, final RealVector b)
+ throws NullArgumentException,
+ NonSquareOperatorException,
+ DimensionMismatchException,
+ NonSelfAdjointOperatorException,
+ IllConditionedOperatorException,
+ MaxCountExceededException {
+ MathUtils.checkNotNull(a);
+ final RealVector x = new ArrayRealVector(a.getColumnDimension());
+ x.set(0.);
+ return solveInPlace(a, null, b, x, false, 0.);
+ }
+
+ /**
+ * Returns the solution to the system (A - shift &middot; I) &middot; x = b.
+ *
+ * <p>If the solution x is expected to contain a large multiple of {@code b} (as in
+ * Rayleigh-quotient iteration), then better precision may be achieved with {@code goodb} set to
+ * {@code true}.
+ *
+ * <p>{@code shift} should be zero if the system A &middot; x = b is to be solved. Otherwise, it
+ * could be an approximation to an eigenvalue of A, such as the Rayleigh quotient b<sup>T</sup>
+ * &middot; A &middot; b / (b<sup>T</sup> &middot; b) corresponding to the vector b. If b is
+ * sufficiently like an eigenvector corresponding to an eigenvalue near shift, then the computed
+ * x may have very large components. When normalized, x may be closer to an eigenvector than b.
+ *
+ * @param a the linear operator A of the system
+ * @param b the right-hand side vector
+ * @param goodb usually {@code false}, except if {@code x} is expected to contain a large
+ * multiple of {@code b}
+ * @param shift the amount to be subtracted to all diagonal elements of A
+ * @return a reference to {@code x}
+ * @throws NullArgumentException if one of the parameters is {@code null}
+ * @throws NonSquareOperatorException if {@code a} is not square
+ * @throws DimensionMismatchException if {@code b} has dimensions inconsistent with {@code a}
+ * @throws MaxCountExceededException at exhaustion of the iteration count, unless a custom
+ * {@link org.apache.commons.math3.util.Incrementor.MaxCountExceededCallback callback} has
+ * been set at construction of the {@link IterationManager}
+ * @throws NonSelfAdjointOperatorException if {@link #getCheck()} is {@code true}, and {@code a}
+ * is not self-adjoint
+ * @throws IllConditionedOperatorException if {@code a} is ill-conditioned
+ */
+ public RealVector solve(
+ final RealLinearOperator a, final RealVector b, final boolean goodb, final double shift)
+ throws NullArgumentException,
+ NonSquareOperatorException,
+ DimensionMismatchException,
+ NonSelfAdjointOperatorException,
+ IllConditionedOperatorException,
+ MaxCountExceededException {
+ MathUtils.checkNotNull(a);
+ final RealVector x = new ArrayRealVector(a.getColumnDimension());
+ return solveInPlace(a, null, b, x, goodb, shift);
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @param x not meaningful in this implementation; should not be considered as an initial guess
+ * (<a href="#initguess">more</a>)
+ * @throws NonSelfAdjointOperatorException if {@link #getCheck()} is {@code true}, and {@code a}
+ * is not self-adjoint
+ * @throws IllConditionedOperatorException if {@code a} is ill-conditioned
+ */
+ @Override
+ public RealVector solve(final RealLinearOperator a, final RealVector b, final RealVector x)
+ throws NullArgumentException,
+ NonSquareOperatorException,
+ DimensionMismatchException,
+ NonSelfAdjointOperatorException,
+ IllConditionedOperatorException,
+ MaxCountExceededException {
+ MathUtils.checkNotNull(x);
+ return solveInPlace(a, null, b, x.copy(), false, 0.);
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @param x the vector to be updated with the solution; {@code x} should not be considered as an
+ * initial guess (<a href="#initguess">more</a>)
+ * @throws NonSelfAdjointOperatorException if {@link #getCheck()} is {@code true}, and {@code a}
+ * or {@code m} is not self-adjoint
+ * @throws NonPositiveDefiniteOperatorException if {@code m} is not positive definite
+ * @throws IllConditionedOperatorException if {@code a} is ill-conditioned
+ */
+ @Override
+ public RealVector solveInPlace(
+ final RealLinearOperator a,
+ final RealLinearOperator m,
+ final RealVector b,
+ final RealVector x)
+ throws NullArgumentException,
+ NonSquareOperatorException,
+ DimensionMismatchException,
+ NonSelfAdjointOperatorException,
+ NonPositiveDefiniteOperatorException,
+ IllConditionedOperatorException,
+ MaxCountExceededException {
+ return solveInPlace(a, m, b, x, false, 0.);
+ }
+
+ /**
+ * Returns an estimate of the solution to the linear system (A - shift &middot; I) &middot; x =
+ * b. The solution is computed in-place.
+ *
+ * <p>If the solution x is expected to contain a large multiple of {@code b} (as in
+ * Rayleigh-quotient iteration), then better precision may be achieved with {@code goodb} set to
+ * {@code true}; this however requires an extra call to the preconditioner.
+ *
+ * <p>{@code shift} should be zero if the system A &middot; x = b is to be solved. Otherwise, it
+ * could be an approximation to an eigenvalue of A, such as the Rayleigh quotient b<sup>T</sup>
+ * &middot; A &middot; b / (b<sup>T</sup> &middot; b) corresponding to the vector b. If b is
+ * sufficiently like an eigenvector corresponding to an eigenvalue near shift, then the computed
+ * x may have very large components. When normalized, x may be closer to an eigenvector than b.
+ *
+ * @param a the linear operator A of the system
+ * @param m the preconditioner, M (can be {@code null})
+ * @param b the right-hand side vector
+ * @param x the vector to be updated with the solution; {@code x} should not be considered as an
+ * initial guess (<a href="#initguess">more</a>)
+ * @param goodb usually {@code false}, except if {@code x} is expected to contain a large
+ * multiple of {@code b}
+ * @param shift the amount to be subtracted to all diagonal elements of A
+ * @return a reference to {@code x} (shallow copy).
+ * @throws NullArgumentException if one of the parameters is {@code null}
+ * @throws NonSquareOperatorException if {@code a} or {@code m} is not square
+ * @throws DimensionMismatchException if {@code m}, {@code b} or {@code x} have dimensions
+ * inconsistent with {@code a}.
+ * @throws MaxCountExceededException at exhaustion of the iteration count, unless a custom
+ * {@link org.apache.commons.math3.util.Incrementor.MaxCountExceededCallback callback} has
+ * been set at construction of the {@link IterationManager}
+ * @throws NonSelfAdjointOperatorException if {@link #getCheck()} is {@code true}, and {@code a}
+ * or {@code m} is not self-adjoint
+ * @throws NonPositiveDefiniteOperatorException if {@code m} is not positive definite
+ * @throws IllConditionedOperatorException if {@code a} is ill-conditioned
+ */
+ public RealVector solveInPlace(
+ final RealLinearOperator a,
+ final RealLinearOperator m,
+ final RealVector b,
+ final RealVector x,
+ final boolean goodb,
+ final double shift)
+ throws NullArgumentException,
+ NonSquareOperatorException,
+ DimensionMismatchException,
+ NonSelfAdjointOperatorException,
+ NonPositiveDefiniteOperatorException,
+ IllConditionedOperatorException,
+ MaxCountExceededException {
+ checkParameters(a, m, b, x);
+
+ final IterationManager manager = getIterationManager();
+ /* Initialization counts as an iteration. */
+ manager.resetIterationCount();
+ manager.incrementIterationCount();
+
+ final State state;
+ state = new State(a, m, b, goodb, shift, delta, check);
+ state.init();
+ state.refineSolution(x);
+ IterativeLinearSolverEvent event;
+ event =
+ new DefaultIterativeLinearSolverEvent(
+ this, manager.getIterations(), x, b, state.getNormOfResidual());
+ if (state.bEqualsNullVector()) {
+ /* If b = 0 exactly, stop with x = 0. */
+ manager.fireTerminationEvent(event);
+ return x;
+ }
+ /* Cause termination if beta is essentially zero. */
+ final boolean earlyStop;
+ earlyStop = state.betaEqualsZero() || state.hasConverged();
+ manager.fireInitializationEvent(event);
+ if (!earlyStop) {
+ do {
+ manager.incrementIterationCount();
+ event =
+ new DefaultIterativeLinearSolverEvent(
+ this, manager.getIterations(), x, b, state.getNormOfResidual());
+ manager.fireIterationStartedEvent(event);
+ state.update();
+ state.refineSolution(x);
+ event =
+ new DefaultIterativeLinearSolverEvent(
+ this, manager.getIterations(), x, b, state.getNormOfResidual());
+ manager.fireIterationPerformedEvent(event);
+ } while (!state.hasConverged());
+ }
+ event =
+ new DefaultIterativeLinearSolverEvent(
+ this, manager.getIterations(), x, b, state.getNormOfResidual());
+ manager.fireTerminationEvent(event);
+ return x;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @param x the vector to be updated with the solution; {@code x} should not be considered as an
+ * initial guess (<a href="#initguess">more</a>)
+ * @throws NonSelfAdjointOperatorException if {@link #getCheck()} is {@code true}, and {@code a}
+ * is not self-adjoint
+ * @throws IllConditionedOperatorException if {@code a} is ill-conditioned
+ */
+ @Override
+ public RealVector solveInPlace(
+ final RealLinearOperator a, final RealVector b, final RealVector x)
+ throws NullArgumentException,
+ NonSquareOperatorException,
+ DimensionMismatchException,
+ NonSelfAdjointOperatorException,
+ IllConditionedOperatorException,
+ MaxCountExceededException {
+ return solveInPlace(a, null, b, x, false, 0.);
+ }
+}
diff --git a/src/main/java/org/apache/commons/math3/linear/TriDiagonalTransformer.java b/src/main/java/org/apache/commons/math3/linear/TriDiagonalTransformer.java
new file mode 100644
index 0000000..b2f8d33
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/linear/TriDiagonalTransformer.java
@@ -0,0 +1,274 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math3.linear;
+
+import org.apache.commons.math3.util.FastMath;
+
+import java.util.Arrays;
+
+/**
+ * Class transforming a symmetrical matrix to tridiagonal shape.
+ *
+ * <p>A symmetrical m &times; m matrix A can be written as the product of three matrices: A = Q
+ * &times; T &times; Q<sup>T</sup> with Q an orthogonal matrix and T a symmetrical tridiagonal
+ * matrix. Both Q and T are m &times; m matrices.
+ *
+ * <p>This implementation only uses the upper part of the matrix, the part below the diagonal is not
+ * accessed at all.
+ *
+ * <p>Transformation to tridiagonal shape is often not a goal by itself, but it is an intermediate
+ * step in more general decomposition algorithms like {@link EigenDecomposition eigen
+ * decomposition}. This class is therefore intended for internal use by the library and is not
+ * public. As a consequence of this explicitly limited scope, many methods directly returns
+ * references to internal arrays, not copies.
+ *
+ * @since 2.0
+ */
+class TriDiagonalTransformer {
+ /** Householder vectors. */
+ private final double householderVectors[][];
+
+ /** Main diagonal. */
+ private final double[] main;
+
+ /** Secondary diagonal. */
+ private final double[] secondary;
+
+ /** Cached value of Q. */
+ private RealMatrix cachedQ;
+
+ /** Cached value of Qt. */
+ private RealMatrix cachedQt;
+
+ /** Cached value of T. */
+ private RealMatrix cachedT;
+
+ /**
+ * Build the transformation to tridiagonal shape of a symmetrical matrix.
+ *
+ * <p>The specified matrix is assumed to be symmetrical without any check. Only the upper
+ * triangular part of the matrix is used.
+ *
+ * @param matrix Symmetrical matrix to transform.
+ * @throws NonSquareMatrixException if the matrix is not square.
+ */
+ TriDiagonalTransformer(RealMatrix matrix) {
+ if (!matrix.isSquare()) {
+ throw new NonSquareMatrixException(
+ matrix.getRowDimension(), matrix.getColumnDimension());
+ }
+
+ final int m = matrix.getRowDimension();
+ householderVectors = matrix.getData();
+ main = new double[m];
+ secondary = new double[m - 1];
+ cachedQ = null;
+ cachedQt = null;
+ cachedT = null;
+
+ // transform matrix
+ transform();
+ }
+
+ /**
+ * Returns the matrix Q of the transform.
+ *
+ * <p>Q is an orthogonal matrix, i.e. its transpose is also its inverse.
+ *
+ * @return the Q matrix
+ */
+ public RealMatrix getQ() {
+ if (cachedQ == null) {
+ cachedQ = getQT().transpose();
+ }
+ return cachedQ;
+ }
+
+ /**
+ * Returns the transpose of the matrix Q of the transform.
+ *
+ * <p>Q is an orthogonal matrix, i.e. its transpose is also its inverse.
+ *
+ * @return the Q matrix
+ */
+ public RealMatrix getQT() {
+ if (cachedQt == null) {
+ final int m = householderVectors.length;
+ double[][] qta = new double[m][m];
+
+ // build up first part of the matrix by applying Householder transforms
+ for (int k = m - 1; k >= 1; --k) {
+ final double[] hK = householderVectors[k - 1];
+ qta[k][k] = 1;
+ if (hK[k] != 0.0) {
+ final double inv = 1.0 / (secondary[k - 1] * hK[k]);
+ double beta = 1.0 / secondary[k - 1];
+ qta[k][k] = 1 + beta * hK[k];
+ for (int i = k + 1; i < m; ++i) {
+ qta[k][i] = beta * hK[i];
+ }
+ for (int j = k + 1; j < m; ++j) {
+ beta = 0;
+ for (int i = k + 1; i < m; ++i) {
+ beta += qta[j][i] * hK[i];
+ }
+ beta *= inv;
+ qta[j][k] = beta * hK[k];
+ for (int i = k + 1; i < m; ++i) {
+ qta[j][i] += beta * hK[i];
+ }
+ }
+ }
+ }
+ qta[0][0] = 1;
+ cachedQt = MatrixUtils.createRealMatrix(qta);
+ }
+
+ // return the cached matrix
+ return cachedQt;
+ }
+
+ /**
+ * Returns the tridiagonal matrix T of the transform.
+ *
+ * @return the T matrix
+ */
+ public RealMatrix getT() {
+ if (cachedT == null) {
+ final int m = main.length;
+ double[][] ta = new double[m][m];
+ for (int i = 0; i < m; ++i) {
+ ta[i][i] = main[i];
+ if (i > 0) {
+ ta[i][i - 1] = secondary[i - 1];
+ }
+ if (i < main.length - 1) {
+ ta[i][i + 1] = secondary[i];
+ }
+ }
+ cachedT = MatrixUtils.createRealMatrix(ta);
+ }
+
+ // return the cached matrix
+ return cachedT;
+ }
+
+ /**
+ * Get the Householder vectors of the transform.
+ *
+ * <p>Note that since this class is only intended for internal use, it returns directly a
+ * reference to its internal arrays, not a copy.
+ *
+ * @return the main diagonal elements of the B matrix
+ */
+ double[][] getHouseholderVectorsRef() {
+ return householderVectors;
+ }
+
+ /**
+ * Get the main diagonal elements of the matrix T of the transform.
+ *
+ * <p>Note that since this class is only intended for internal use, it returns directly a
+ * reference to its internal arrays, not a copy.
+ *
+ * @return the main diagonal elements of the T matrix
+ */
+ double[] getMainDiagonalRef() {
+ return main;
+ }
+
+ /**
+ * Get the secondary diagonal elements of the matrix T of the transform.
+ *
+ * <p>Note that since this class is only intended for internal use, it returns directly a
+ * reference to its internal arrays, not a copy.
+ *
+ * @return the secondary diagonal elements of the T matrix
+ */
+ double[] getSecondaryDiagonalRef() {
+ return secondary;
+ }
+
+ /**
+ * Transform original matrix to tridiagonal form.
+ *
+ * <p>Transformation is done using Householder transforms.
+ */
+ private void transform() {
+ final int m = householderVectors.length;
+ final double[] z = new double[m];
+ for (int k = 0; k < m - 1; k++) {
+
+ // zero-out a row and a column simultaneously
+ final double[] hK = householderVectors[k];
+ main[k] = hK[k];
+ double xNormSqr = 0;
+ for (int j = k + 1; j < m; ++j) {
+ final double c = hK[j];
+ xNormSqr += c * c;
+ }
+ final double a = (hK[k + 1] > 0) ? -FastMath.sqrt(xNormSqr) : FastMath.sqrt(xNormSqr);
+ secondary[k] = a;
+ if (a != 0.0) {
+ // apply Householder transform from left and right simultaneously
+
+ hK[k + 1] -= a;
+ final double beta = -1 / (a * hK[k + 1]);
+
+ // compute a = beta A v, where v is the Householder vector
+ // this loop is written in such a way
+ // 1) only the upper triangular part of the matrix is accessed
+ // 2) access is cache-friendly for a matrix stored in rows
+ Arrays.fill(z, k + 1, m, 0);
+ for (int i = k + 1; i < m; ++i) {
+ final double[] hI = householderVectors[i];
+ final double hKI = hK[i];
+ double zI = hI[i] * hKI;
+ for (int j = i + 1; j < m; ++j) {
+ final double hIJ = hI[j];
+ zI += hIJ * hK[j];
+ z[j] += hIJ * hKI;
+ }
+ z[i] = beta * (z[i] + zI);
+ }
+
+ // compute gamma = beta vT z / 2
+ double gamma = 0;
+ for (int i = k + 1; i < m; ++i) {
+ gamma += z[i] * hK[i];
+ }
+ gamma *= beta / 2;
+
+ // compute z = z - gamma v
+ for (int i = k + 1; i < m; ++i) {
+ z[i] -= gamma * hK[i];
+ }
+
+ // update matrix: A = A - v zT - z vT
+ // only the upper triangular part of the matrix is updated
+ for (int i = k + 1; i < m; ++i) {
+ final double[] hI = householderVectors[i];
+ for (int j = i; j < m; ++j) {
+ hI[j] -= hK[i] * z[j] + z[i] * hK[j];
+ }
+ }
+ }
+ }
+ main[m - 1] = householderVectors[m - 1][m - 1];
+ }
+}
diff --git a/src/main/java/org/apache/commons/math3/linear/package-info.java b/src/main/java/org/apache/commons/math3/linear/package-info.java
new file mode 100644
index 0000000..4b0c86b
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/linear/package-info.java
@@ -0,0 +1,18 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/** Linear algebra support. */
+package org.apache.commons.math3.linear;
diff --git a/src/main/java/org/apache/commons/math3/ml/clustering/CentroidCluster.java b/src/main/java/org/apache/commons/math3/ml/clustering/CentroidCluster.java
new file mode 100644
index 0000000..5cfc7bc
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/ml/clustering/CentroidCluster.java
@@ -0,0 +1,53 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math3.ml.clustering;
+
+/**
+ * A Cluster used by centroid-based clustering algorithms.
+ * <p>
+ * Defines additionally a cluster center which may not necessarily be a member
+ * of the original data set.
+ *
+ * @param <T> the type of points that can be clustered
+ * @since 3.2
+ */
+public class CentroidCluster<T extends Clusterable> extends Cluster<T> {
+
+ /** Serializable version identifier. */
+ private static final long serialVersionUID = -3075288519071812288L;
+
+ /** Center of the cluster. */
+ private final Clusterable center;
+
+ /**
+ * Build a cluster centered at a specified point.
+ * @param center the point which is to be the center of this cluster
+ */
+ public CentroidCluster(final Clusterable center) {
+ super();
+ this.center = center;
+ }
+
+ /**
+ * Get the point chosen to be the center of this cluster.
+ * @return chosen cluster center
+ */
+ public Clusterable getCenter() {
+ return center;
+ }
+
+}
diff --git a/src/main/java/org/apache/commons/math3/ml/clustering/Cluster.java b/src/main/java/org/apache/commons/math3/ml/clustering/Cluster.java
new file mode 100644
index 0000000..fa6df94
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/ml/clustering/Cluster.java
@@ -0,0 +1,60 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math3.ml.clustering;
+
+import java.io.Serializable;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Cluster holding a set of {@link Clusterable} points.
+ * @param <T> the type of points that can be clustered
+ * @since 3.2
+ */
+public class Cluster<T extends Clusterable> implements Serializable {
+
+ /** Serializable version identifier. */
+ private static final long serialVersionUID = -3442297081515880464L;
+
+ /** The points contained in this cluster. */
+ private final List<T> points;
+
+ /**
+ * Build a cluster centered at a specified point.
+ */
+ public Cluster() {
+ points = new ArrayList<T>();
+ }
+
+ /**
+ * Add a point to this cluster.
+ * @param point point to add
+ */
+ public void addPoint(final T point) {
+ points.add(point);
+ }
+
+ /**
+ * Get the points contained in the cluster.
+ * @return points contained in the cluster
+ */
+ public List<T> getPoints() {
+ return points;
+ }
+
+}
diff --git a/src/main/java/org/apache/commons/math3/ml/clustering/Clusterable.java b/src/main/java/org/apache/commons/math3/ml/clustering/Clusterable.java
new file mode 100644
index 0000000..e712eb7
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/ml/clustering/Clusterable.java
@@ -0,0 +1,32 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math3.ml.clustering;
+
+/**
+ * Interface for n-dimensional points that can be clustered together.
+ * @since 3.2
+ */
+public interface Clusterable {
+
+ /**
+ * Gets the n-dimensional point.
+ *
+ * @return the point array
+ */
+ double[] getPoint();
+}
diff --git a/src/main/java/org/apache/commons/math3/ml/clustering/Clusterer.java b/src/main/java/org/apache/commons/math3/ml/clustering/Clusterer.java
new file mode 100644
index 0000000..30e38c6
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/ml/clustering/Clusterer.java
@@ -0,0 +1,80 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math3.ml.clustering;
+
+import java.util.Collection;
+import java.util.List;
+
+import org.apache.commons.math3.exception.ConvergenceException;
+import org.apache.commons.math3.exception.MathIllegalArgumentException;
+import org.apache.commons.math3.ml.distance.DistanceMeasure;
+
+/**
+ * Base class for clustering algorithms.
+ *
+ * @param <T> the type of points that can be clustered
+ * @since 3.2
+ */
+public abstract class Clusterer<T extends Clusterable> {
+
+ /** The distance measure to use. */
+ private DistanceMeasure measure;
+
+ /**
+ * Build a new clusterer with the given {@link DistanceMeasure}.
+ *
+ * @param measure the distance measure to use
+ */
+ protected Clusterer(final DistanceMeasure measure) {
+ this.measure = measure;
+ }
+
+ /**
+ * Perform a cluster analysis on the given set of {@link Clusterable} instances.
+ *
+ * @param points the set of {@link Clusterable} instances
+ * @return a {@link List} of clusters
+ * @throws MathIllegalArgumentException if points are null or the number of
+ * data points is not compatible with this clusterer
+ * @throws ConvergenceException if the algorithm has not yet converged after
+ * the maximum number of iterations has been exceeded
+ */
+ public abstract List<? extends Cluster<T>> cluster(Collection<T> points)
+ throws MathIllegalArgumentException, ConvergenceException;
+
+ /**
+ * Returns the {@link DistanceMeasure} instance used by this clusterer.
+ *
+ * @return the distance measure
+ */
+ public DistanceMeasure getDistanceMeasure() {
+ return measure;
+ }
+
+ /**
+ * Calculates the distance between two {@link Clusterable} instances
+ * with the configured {@link DistanceMeasure}.
+ *
+ * @param p1 the first clusterable
+ * @param p2 the second clusterable
+ * @return the distance between the two clusterables
+ */
+ protected double distance(final Clusterable p1, final Clusterable p2) {
+ return measure.compute(p1.getPoint(), p2.getPoint());
+ }
+
+}
diff --git a/src/main/java/org/apache/commons/math3/ml/clustering/DBSCANClusterer.java b/src/main/java/org/apache/commons/math3/ml/clustering/DBSCANClusterer.java
new file mode 100644
index 0000000..ce3d5cd
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/ml/clustering/DBSCANClusterer.java
@@ -0,0 +1,233 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math3.ml.clustering;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import org.apache.commons.math3.exception.NotPositiveException;
+import org.apache.commons.math3.exception.NullArgumentException;
+import org.apache.commons.math3.ml.distance.DistanceMeasure;
+import org.apache.commons.math3.ml.distance.EuclideanDistance;
+import org.apache.commons.math3.util.MathUtils;
+
+/**
+ * DBSCAN (density-based spatial clustering of applications with noise) algorithm.
+ * <p>
+ * The DBSCAN algorithm forms clusters based on the idea of density connectivity, i.e.
+ * a point p is density connected to another point q, if there exists a chain of
+ * points p<sub>i</sub>, with i = 1 .. n and p<sub>1</sub> = p and p<sub>n</sub> = q,
+ * such that each pair &lt;p<sub>i</sub>, p<sub>i+1</sub>&gt; is directly density-reachable.
+ * A point q is directly density-reachable from point p if it is in the &epsilon;-neighborhood
+ * of this point.
+ * <p>
+ * Any point that is not density-reachable from a formed cluster is treated as noise, and
+ * will thus not be present in the result.
+ * <p>
+ * The algorithm requires two parameters:
+ * <ul>
+ * <li>eps: the distance that defines the &epsilon;-neighborhood of a point
+ * <li>minPoints: the minimum number of density-connected points required to form a cluster
+ * </ul>
+ *
+ * @param <T> type of the points to cluster
+ * @see <a href="http://en.wikipedia.org/wiki/DBSCAN">DBSCAN (wikipedia)</a>
+ * @see <a href="http://www.dbs.ifi.lmu.de/Publikationen/Papers/KDD-96.final.frame.pdf">
+ * A Density-Based Algorithm for Discovering Clusters in Large Spatial Databases with Noise</a>
+ * @since 3.2
+ */
+public class DBSCANClusterer<T extends Clusterable> extends Clusterer<T> {
+
+ /** Maximum radius of the neighborhood to be considered. */
+ private final double eps;
+
+ /** Minimum number of points needed for a cluster. */
+ private final int minPts;
+
+ /** Status of a point during the clustering process. */
+ private enum PointStatus {
+ /** The point has is considered to be noise. */
+ NOISE,
+ /** The point is already part of a cluster. */
+ PART_OF_CLUSTER
+ }
+
+ /**
+ * Creates a new instance of a DBSCANClusterer.
+ * <p>
+ * The euclidean distance will be used as default distance measure.
+ *
+ * @param eps maximum radius of the neighborhood to be considered
+ * @param minPts minimum number of points needed for a cluster
+ * @throws NotPositiveException if {@code eps < 0.0} or {@code minPts < 0}
+ */
+ public DBSCANClusterer(final double eps, final int minPts)
+ throws NotPositiveException {
+ this(eps, minPts, new EuclideanDistance());
+ }
+
+ /**
+ * Creates a new instance of a DBSCANClusterer.
+ *
+ * @param eps maximum radius of the neighborhood to be considered
+ * @param minPts minimum number of points needed for a cluster
+ * @param measure the distance measure to use
+ * @throws NotPositiveException if {@code eps < 0.0} or {@code minPts < 0}
+ */
+ public DBSCANClusterer(final double eps, final int minPts, final DistanceMeasure measure)
+ throws NotPositiveException {
+ super(measure);
+
+ if (eps < 0.0d) {
+ throw new NotPositiveException(eps);
+ }
+ if (minPts < 0) {
+ throw new NotPositiveException(minPts);
+ }
+ this.eps = eps;
+ this.minPts = minPts;
+ }
+
+ /**
+ * Returns the maximum radius of the neighborhood to be considered.
+ * @return maximum radius of the neighborhood
+ */
+ public double getEps() {
+ return eps;
+ }
+
+ /**
+ * Returns the minimum number of points needed for a cluster.
+ * @return minimum number of points needed for a cluster
+ */
+ public int getMinPts() {
+ return minPts;
+ }
+
+ /**
+ * Performs DBSCAN cluster analysis.
+ *
+ * @param points the points to cluster
+ * @return the list of clusters
+ * @throws NullArgumentException if the data points are null
+ */
+ @Override
+ public List<Cluster<T>> cluster(final Collection<T> points) throws NullArgumentException {
+
+ // sanity checks
+ MathUtils.checkNotNull(points);
+
+ final List<Cluster<T>> clusters = new ArrayList<Cluster<T>>();
+ final Map<Clusterable, PointStatus> visited = new HashMap<Clusterable, PointStatus>();
+
+ for (final T point : points) {
+ if (visited.get(point) != null) {
+ continue;
+ }
+ final List<T> neighbors = getNeighbors(point, points);
+ if (neighbors.size() >= minPts) {
+ // DBSCAN does not care about center points
+ final Cluster<T> cluster = new Cluster<T>();
+ clusters.add(expandCluster(cluster, point, neighbors, points, visited));
+ } else {
+ visited.put(point, PointStatus.NOISE);
+ }
+ }
+
+ return clusters;
+ }
+
+ /**
+ * Expands the cluster to include density-reachable items.
+ *
+ * @param cluster Cluster to expand
+ * @param point Point to add to cluster
+ * @param neighbors List of neighbors
+ * @param points the data set
+ * @param visited the set of already visited points
+ * @return the expanded cluster
+ */
+ private Cluster<T> expandCluster(final Cluster<T> cluster,
+ final T point,
+ final List<T> neighbors,
+ final Collection<T> points,
+ final Map<Clusterable, PointStatus> visited) {
+ cluster.addPoint(point);
+ visited.put(point, PointStatus.PART_OF_CLUSTER);
+
+ List<T> seeds = new ArrayList<T>(neighbors);
+ int index = 0;
+ while (index < seeds.size()) {
+ final T current = seeds.get(index);
+ PointStatus pStatus = visited.get(current);
+ // only check non-visited points
+ if (pStatus == null) {
+ final List<T> currentNeighbors = getNeighbors(current, points);
+ if (currentNeighbors.size() >= minPts) {
+ seeds = merge(seeds, currentNeighbors);
+ }
+ }
+
+ if (pStatus != PointStatus.PART_OF_CLUSTER) {
+ visited.put(current, PointStatus.PART_OF_CLUSTER);
+ cluster.addPoint(current);
+ }
+
+ index++;
+ }
+ return cluster;
+ }
+
+ /**
+ * Returns a list of density-reachable neighbors of a {@code point}.
+ *
+ * @param point the point to look for
+ * @param points possible neighbors
+ * @return the List of neighbors
+ */
+ private List<T> getNeighbors(final T point, final Collection<T> points) {
+ final List<T> neighbors = new ArrayList<T>();
+ for (final T neighbor : points) {
+ if (point != neighbor && distance(neighbor, point) <= eps) {
+ neighbors.add(neighbor);
+ }
+ }
+ return neighbors;
+ }
+
+ /**
+ * Merges two lists together.
+ *
+ * @param one first list
+ * @param two second list
+ * @return merged lists
+ */
+ private List<T> merge(final List<T> one, final List<T> two) {
+ final Set<T> oneSet = new HashSet<T>(one);
+ for (T item : two) {
+ if (!oneSet.contains(item)) {
+ one.add(item);
+ }
+ }
+ return one;
+ }
+}
diff --git a/src/main/java/org/apache/commons/math3/ml/clustering/DoublePoint.java b/src/main/java/org/apache/commons/math3/ml/clustering/DoublePoint.java
new file mode 100644
index 0000000..4fb31f7
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/ml/clustering/DoublePoint.java
@@ -0,0 +1,86 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math3.ml.clustering;
+
+import java.io.Serializable;
+import java.util.Arrays;
+
+/**
+ * A simple implementation of {@link Clusterable} for points with double coordinates.
+ * @since 3.2
+ */
+public class DoublePoint implements Clusterable, Serializable {
+
+ /** Serializable version identifier. */
+ private static final long serialVersionUID = 3946024775784901369L;
+
+ /** Point coordinates. */
+ private final double[] point;
+
+ /**
+ * Build an instance wrapping an double array.
+ * <p>
+ * The wrapped array is referenced, it is <em>not</em> copied.
+ *
+ * @param point the n-dimensional point in double space
+ */
+ public DoublePoint(final double[] point) {
+ this.point = point;
+ }
+
+ /**
+ * Build an instance wrapping an integer array.
+ * <p>
+ * The wrapped array is copied to an internal double array.
+ *
+ * @param point the n-dimensional point in integer space
+ */
+ public DoublePoint(final int[] point) {
+ this.point = new double[point.length];
+ for ( int i = 0; i < point.length; i++) {
+ this.point[i] = point[i];
+ }
+ }
+
+ /** {@inheritDoc} */
+ public double[] getPoint() {
+ return point;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public boolean equals(final Object other) {
+ if (!(other instanceof DoublePoint)) {
+ return false;
+ }
+ return Arrays.equals(point, ((DoublePoint) other).point);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public int hashCode() {
+ return Arrays.hashCode(point);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public String toString() {
+ return Arrays.toString(point);
+ }
+
+}
diff --git a/src/main/java/org/apache/commons/math3/ml/clustering/FuzzyKMeansClusterer.java b/src/main/java/org/apache/commons/math3/ml/clustering/FuzzyKMeansClusterer.java
new file mode 100644
index 0000000..5f89934
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/ml/clustering/FuzzyKMeansClusterer.java
@@ -0,0 +1,426 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math3.ml.clustering;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+
+import org.apache.commons.math3.exception.MathIllegalArgumentException;
+import org.apache.commons.math3.exception.MathIllegalStateException;
+import org.apache.commons.math3.exception.NumberIsTooSmallException;
+import org.apache.commons.math3.linear.MatrixUtils;
+import org.apache.commons.math3.linear.RealMatrix;
+import org.apache.commons.math3.ml.distance.DistanceMeasure;
+import org.apache.commons.math3.ml.distance.EuclideanDistance;
+import org.apache.commons.math3.random.JDKRandomGenerator;
+import org.apache.commons.math3.random.RandomGenerator;
+import org.apache.commons.math3.util.FastMath;
+import org.apache.commons.math3.util.MathArrays;
+import org.apache.commons.math3.util.MathUtils;
+
+/**
+ * Fuzzy K-Means clustering algorithm.
+ * <p>
+ * The Fuzzy K-Means algorithm is a variation of the classical K-Means algorithm, with the
+ * major difference that a single data point is not uniquely assigned to a single cluster.
+ * Instead, each point i has a set of weights u<sub>ij</sub> which indicate the degree of membership
+ * to the cluster j.
+ * <p>
+ * The algorithm then tries to minimize the objective function:
+ * <pre>
+ * J = &#8721;<sub>i=1..C</sub>&#8721;<sub>k=1..N</sub> u<sub>ik</sub><sup>m</sup>d<sub>ik</sub><sup>2</sup>
+ * </pre>
+ * with d<sub>ik</sub> being the distance between data point i and the cluster center k.
+ * <p>
+ * The algorithm requires two parameters:
+ * <ul>
+ * <li>k: the number of clusters
+ * <li>fuzziness: determines the level of cluster fuzziness, larger values lead to fuzzier clusters
+ * </ul>
+ * Additional, optional parameters:
+ * <ul>
+ * <li>maxIterations: the maximum number of iterations
+ * <li>epsilon: the convergence criteria, default is 1e-3
+ * </ul>
+ * <p>
+ * The fuzzy variant of the K-Means algorithm is more robust with regard to the selection
+ * of the initial cluster centers.
+ *
+ * @param <T> type of the points to cluster
+ * @since 3.3
+ */
+public class FuzzyKMeansClusterer<T extends Clusterable> extends Clusterer<T> {
+
+ /** The default value for the convergence criteria. */
+ private static final double DEFAULT_EPSILON = 1e-3;
+
+ /** The number of clusters. */
+ private final int k;
+
+ /** The maximum number of iterations. */
+ private final int maxIterations;
+
+ /** The fuzziness factor. */
+ private final double fuzziness;
+
+ /** The convergence criteria. */
+ private final double epsilon;
+
+ /** Random generator for choosing initial centers. */
+ private final RandomGenerator random;
+
+ /** The membership matrix. */
+ private double[][] membershipMatrix;
+
+ /** The list of points used in the last call to {@link #cluster(Collection)}. */
+ private List<T> points;
+
+ /** The list of clusters resulting from the last call to {@link #cluster(Collection)}. */
+ private List<CentroidCluster<T>> clusters;
+
+ /**
+ * Creates a new instance of a FuzzyKMeansClusterer.
+ * <p>
+ * The euclidean distance will be used as default distance measure.
+ *
+ * @param k the number of clusters to split the data into
+ * @param fuzziness the fuzziness factor, must be &gt; 1.0
+ * @throws NumberIsTooSmallException if {@code fuzziness <= 1.0}
+ */
+ public FuzzyKMeansClusterer(final int k, final double fuzziness) throws NumberIsTooSmallException {
+ this(k, fuzziness, -1, new EuclideanDistance());
+ }
+
+ /**
+ * Creates a new instance of a FuzzyKMeansClusterer.
+ *
+ * @param k the number of clusters to split the data into
+ * @param fuzziness the fuzziness factor, must be &gt; 1.0
+ * @param maxIterations the maximum number of iterations to run the algorithm for.
+ * If negative, no maximum will be used.
+ * @param measure the distance measure to use
+ * @throws NumberIsTooSmallException if {@code fuzziness <= 1.0}
+ */
+ public FuzzyKMeansClusterer(final int k, final double fuzziness,
+ final int maxIterations, final DistanceMeasure measure)
+ throws NumberIsTooSmallException {
+ this(k, fuzziness, maxIterations, measure, DEFAULT_EPSILON, new JDKRandomGenerator());
+ }
+
+ /**
+ * Creates a new instance of a FuzzyKMeansClusterer.
+ *
+ * @param k the number of clusters to split the data into
+ * @param fuzziness the fuzziness factor, must be &gt; 1.0
+ * @param maxIterations the maximum number of iterations to run the algorithm for.
+ * If negative, no maximum will be used.
+ * @param measure the distance measure to use
+ * @param epsilon the convergence criteria (default is 1e-3)
+ * @param random random generator to use for choosing initial centers
+ * @throws NumberIsTooSmallException if {@code fuzziness <= 1.0}
+ */
+ public FuzzyKMeansClusterer(final int k, final double fuzziness,
+ final int maxIterations, final DistanceMeasure measure,
+ final double epsilon, final RandomGenerator random)
+ throws NumberIsTooSmallException {
+
+ super(measure);
+
+ if (fuzziness <= 1.0d) {
+ throw new NumberIsTooSmallException(fuzziness, 1.0, false);
+ }
+ this.k = k;
+ this.fuzziness = fuzziness;
+ this.maxIterations = maxIterations;
+ this.epsilon = epsilon;
+ this.random = random;
+
+ this.membershipMatrix = null;
+ this.points = null;
+ this.clusters = null;
+ }
+
+ /**
+ * Return the number of clusters this instance will use.
+ * @return the number of clusters
+ */
+ public int getK() {
+ return k;
+ }
+
+ /**
+ * Returns the fuzziness factor used by this instance.
+ * @return the fuzziness factor
+ */
+ public double getFuzziness() {
+ return fuzziness;
+ }
+
+ /**
+ * Returns the maximum number of iterations this instance will use.
+ * @return the maximum number of iterations, or -1 if no maximum is set
+ */
+ public int getMaxIterations() {
+ return maxIterations;
+ }
+
+ /**
+ * Returns the convergence criteria used by this instance.
+ * @return the convergence criteria
+ */
+ public double getEpsilon() {
+ return epsilon;
+ }
+
+ /**
+ * Returns the random generator this instance will use.
+ * @return the random generator
+ */
+ public RandomGenerator getRandomGenerator() {
+ return random;
+ }
+
+ /**
+ * Returns the {@code nxk} membership matrix, where {@code n} is the number
+ * of data points and {@code k} the number of clusters.
+ * <p>
+ * The element U<sub>i,j</sub> represents the membership value for data point {@code i}
+ * to cluster {@code j}.
+ *
+ * @return the membership matrix
+ * @throws MathIllegalStateException if {@link #cluster(Collection)} has not been called before
+ */
+ public RealMatrix getMembershipMatrix() {
+ if (membershipMatrix == null) {
+ throw new MathIllegalStateException();
+ }
+ return MatrixUtils.createRealMatrix(membershipMatrix);
+ }
+
+ /**
+ * Returns an unmodifiable list of the data points used in the last
+ * call to {@link #cluster(Collection)}.
+ * @return the list of data points, or {@code null} if {@link #cluster(Collection)} has
+ * not been called before.
+ */
+ public List<T> getDataPoints() {
+ return points;
+ }
+
+ /**
+ * Returns the list of clusters resulting from the last call to {@link #cluster(Collection)}.
+ * @return the list of clusters, or {@code null} if {@link #cluster(Collection)} has
+ * not been called before.
+ */
+ public List<CentroidCluster<T>> getClusters() {
+ return clusters;
+ }
+
+ /**
+ * Get the value of the objective function.
+ * @return the objective function evaluation as double value
+ * @throws MathIllegalStateException if {@link #cluster(Collection)} has not been called before
+ */
+ public double getObjectiveFunctionValue() {
+ if (points == null || clusters == null) {
+ throw new MathIllegalStateException();
+ }
+
+ int i = 0;
+ double objFunction = 0.0;
+ for (final T point : points) {
+ int j = 0;
+ for (final CentroidCluster<T> cluster : clusters) {
+ final double dist = distance(point, cluster.getCenter());
+ objFunction += (dist * dist) * FastMath.pow(membershipMatrix[i][j], fuzziness);
+ j++;
+ }
+ i++;
+ }
+ return objFunction;
+ }
+
+ /**
+ * Performs Fuzzy K-Means cluster analysis.
+ *
+ * @param dataPoints the points to cluster
+ * @return the list of clusters
+ * @throws MathIllegalArgumentException if the data points are null or the number
+ * of clusters is larger than the number of data points
+ */
+ @Override
+ public List<CentroidCluster<T>> cluster(final Collection<T> dataPoints)
+ throws MathIllegalArgumentException {
+
+ // sanity checks
+ MathUtils.checkNotNull(dataPoints);
+
+ final int size = dataPoints.size();
+
+ // number of clusters has to be smaller or equal the number of data points
+ if (size < k) {
+ throw new NumberIsTooSmallException(size, k, false);
+ }
+
+ // copy the input collection to an unmodifiable list with indexed access
+ points = Collections.unmodifiableList(new ArrayList<T>(dataPoints));
+ clusters = new ArrayList<CentroidCluster<T>>();
+ membershipMatrix = new double[size][k];
+ final double[][] oldMatrix = new double[size][k];
+
+ // if no points are provided, return an empty list of clusters
+ if (size == 0) {
+ return clusters;
+ }
+
+ initializeMembershipMatrix();
+
+ // there is at least one point
+ final int pointDimension = points.get(0).getPoint().length;
+ for (int i = 0; i < k; i++) {
+ clusters.add(new CentroidCluster<T>(new DoublePoint(new double[pointDimension])));
+ }
+
+ int iteration = 0;
+ final int max = (maxIterations < 0) ? Integer.MAX_VALUE : maxIterations;
+ double difference = 0.0;
+
+ do {
+ saveMembershipMatrix(oldMatrix);
+ updateClusterCenters();
+ updateMembershipMatrix();
+ difference = calculateMaxMembershipChange(oldMatrix);
+ } while (difference > epsilon && ++iteration < max);
+
+ return clusters;
+ }
+
+ /**
+ * Update the cluster centers.
+ */
+ private void updateClusterCenters() {
+ int j = 0;
+ final List<CentroidCluster<T>> newClusters = new ArrayList<CentroidCluster<T>>(k);
+ for (final CentroidCluster<T> cluster : clusters) {
+ final Clusterable center = cluster.getCenter();
+ int i = 0;
+ double[] arr = new double[center.getPoint().length];
+ double sum = 0.0;
+ for (final T point : points) {
+ final double u = FastMath.pow(membershipMatrix[i][j], fuzziness);
+ final double[] pointArr = point.getPoint();
+ for (int idx = 0; idx < arr.length; idx++) {
+ arr[idx] += u * pointArr[idx];
+ }
+ sum += u;
+ i++;
+ }
+ MathArrays.scaleInPlace(1.0 / sum, arr);
+ newClusters.add(new CentroidCluster<T>(new DoublePoint(arr)));
+ j++;
+ }
+ clusters.clear();
+ clusters = newClusters;
+ }
+
+ /**
+ * Updates the membership matrix and assigns the points to the cluster with
+ * the highest membership.
+ */
+ private void updateMembershipMatrix() {
+ for (int i = 0; i < points.size(); i++) {
+ final T point = points.get(i);
+ double maxMembership = Double.MIN_VALUE;
+ int newCluster = -1;
+ for (int j = 0; j < clusters.size(); j++) {
+ double sum = 0.0;
+ final double distA = FastMath.abs(distance(point, clusters.get(j).getCenter()));
+
+ if (distA != 0.0) {
+ for (final CentroidCluster<T> c : clusters) {
+ final double distB = FastMath.abs(distance(point, c.getCenter()));
+ if (distB == 0.0) {
+ sum = Double.POSITIVE_INFINITY;
+ break;
+ }
+ sum += FastMath.pow(distA / distB, 2.0 / (fuzziness - 1.0));
+ }
+ }
+
+ double membership;
+ if (sum == 0.0) {
+ membership = 1.0;
+ } else if (sum == Double.POSITIVE_INFINITY) {
+ membership = 0.0;
+ } else {
+ membership = 1.0 / sum;
+ }
+ membershipMatrix[i][j] = membership;
+
+ if (membershipMatrix[i][j] > maxMembership) {
+ maxMembership = membershipMatrix[i][j];
+ newCluster = j;
+ }
+ }
+ clusters.get(newCluster).addPoint(point);
+ }
+ }
+
+ /**
+ * Initialize the membership matrix with random values.
+ */
+ private void initializeMembershipMatrix() {
+ for (int i = 0; i < points.size(); i++) {
+ for (int j = 0; j < k; j++) {
+ membershipMatrix[i][j] = random.nextDouble();
+ }
+ membershipMatrix[i] = MathArrays.normalizeArray(membershipMatrix[i], 1.0);
+ }
+ }
+
+ /**
+ * Calculate the maximum element-by-element change of the membership matrix
+ * for the current iteration.
+ *
+ * @param matrix the membership matrix of the previous iteration
+ * @return the maximum membership matrix change
+ */
+ private double calculateMaxMembershipChange(final double[][] matrix) {
+ double maxMembership = 0.0;
+ for (int i = 0; i < points.size(); i++) {
+ for (int j = 0; j < clusters.size(); j++) {
+ double v = FastMath.abs(membershipMatrix[i][j] - matrix[i][j]);
+ maxMembership = FastMath.max(v, maxMembership);
+ }
+ }
+ return maxMembership;
+ }
+
+ /**
+ * Copy the membership matrix into the provided matrix.
+ *
+ * @param matrix the place to store the membership matrix
+ */
+ private void saveMembershipMatrix(final double[][] matrix) {
+ for (int i = 0; i < points.size(); i++) {
+ System.arraycopy(membershipMatrix[i], 0, matrix[i], 0, clusters.size());
+ }
+ }
+
+}
diff --git a/src/main/java/org/apache/commons/math3/ml/clustering/KMeansPlusPlusClusterer.java b/src/main/java/org/apache/commons/math3/ml/clustering/KMeansPlusPlusClusterer.java
new file mode 100644
index 0000000..2e57fac
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/ml/clustering/KMeansPlusPlusClusterer.java
@@ -0,0 +1,565 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math3.ml.clustering;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+
+import org.apache.commons.math3.exception.ConvergenceException;
+import org.apache.commons.math3.exception.MathIllegalArgumentException;
+import org.apache.commons.math3.exception.NumberIsTooSmallException;
+import org.apache.commons.math3.exception.util.LocalizedFormats;
+import org.apache.commons.math3.ml.distance.DistanceMeasure;
+import org.apache.commons.math3.ml.distance.EuclideanDistance;
+import org.apache.commons.math3.random.JDKRandomGenerator;
+import org.apache.commons.math3.random.RandomGenerator;
+import org.apache.commons.math3.stat.descriptive.moment.Variance;
+import org.apache.commons.math3.util.MathUtils;
+
+/**
+ * Clustering algorithm based on David Arthur and Sergei Vassilvitski k-means++ algorithm.
+ * @param <T> type of the points to cluster
+ * @see <a href="http://en.wikipedia.org/wiki/K-means%2B%2B">K-means++ (wikipedia)</a>
+ * @since 3.2
+ */
+public class KMeansPlusPlusClusterer<T extends Clusterable> extends Clusterer<T> {
+
+ /** Strategies to use for replacing an empty cluster. */
+ public enum EmptyClusterStrategy {
+
+ /** Split the cluster with largest distance variance. */
+ LARGEST_VARIANCE,
+
+ /** Split the cluster with largest number of points. */
+ LARGEST_POINTS_NUMBER,
+
+ /** Create a cluster around the point farthest from its centroid. */
+ FARTHEST_POINT,
+
+ /** Generate an error. */
+ ERROR
+
+ }
+
+ /** The number of clusters. */
+ private final int k;
+
+ /** The maximum number of iterations. */
+ private final int maxIterations;
+
+ /** Random generator for choosing initial centers. */
+ private final RandomGenerator random;
+
+ /** Selected strategy for empty clusters. */
+ private final EmptyClusterStrategy emptyStrategy;
+
+ /** Build a clusterer.
+ * <p>
+ * The default strategy for handling empty clusters that may appear during
+ * algorithm iterations is to split the cluster with largest distance variance.
+ * <p>
+ * The euclidean distance will be used as default distance measure.
+ *
+ * @param k the number of clusters to split the data into
+ */
+ public KMeansPlusPlusClusterer(final int k) {
+ this(k, -1);
+ }
+
+ /** Build a clusterer.
+ * <p>
+ * The default strategy for handling empty clusters that may appear during
+ * algorithm iterations is to split the cluster with largest distance variance.
+ * <p>
+ * The euclidean distance will be used as default distance measure.
+ *
+ * @param k the number of clusters to split the data into
+ * @param maxIterations the maximum number of iterations to run the algorithm for.
+ * If negative, no maximum will be used.
+ */
+ public KMeansPlusPlusClusterer(final int k, final int maxIterations) {
+ this(k, maxIterations, new EuclideanDistance());
+ }
+
+ /** Build a clusterer.
+ * <p>
+ * The default strategy for handling empty clusters that may appear during
+ * algorithm iterations is to split the cluster with largest distance variance.
+ *
+ * @param k the number of clusters to split the data into
+ * @param maxIterations the maximum number of iterations to run the algorithm for.
+ * If negative, no maximum will be used.
+ * @param measure the distance measure to use
+ */
+ public KMeansPlusPlusClusterer(final int k, final int maxIterations, final DistanceMeasure measure) {
+ this(k, maxIterations, measure, new JDKRandomGenerator());
+ }
+
+ /** Build a clusterer.
+ * <p>
+ * The default strategy for handling empty clusters that may appear during
+ * algorithm iterations is to split the cluster with largest distance variance.
+ *
+ * @param k the number of clusters to split the data into
+ * @param maxIterations the maximum number of iterations to run the algorithm for.
+ * If negative, no maximum will be used.
+ * @param measure the distance measure to use
+ * @param random random generator to use for choosing initial centers
+ */
+ public KMeansPlusPlusClusterer(final int k, final int maxIterations,
+ final DistanceMeasure measure,
+ final RandomGenerator random) {
+ this(k, maxIterations, measure, random, EmptyClusterStrategy.LARGEST_VARIANCE);
+ }
+
+ /** Build a clusterer.
+ *
+ * @param k the number of clusters to split the data into
+ * @param maxIterations the maximum number of iterations to run the algorithm for.
+ * If negative, no maximum will be used.
+ * @param measure the distance measure to use
+ * @param random random generator to use for choosing initial centers
+ * @param emptyStrategy strategy to use for handling empty clusters that
+ * may appear during algorithm iterations
+ */
+ public KMeansPlusPlusClusterer(final int k, final int maxIterations,
+ final DistanceMeasure measure,
+ final RandomGenerator random,
+ final EmptyClusterStrategy emptyStrategy) {
+ super(measure);
+ this.k = k;
+ this.maxIterations = maxIterations;
+ this.random = random;
+ this.emptyStrategy = emptyStrategy;
+ }
+
+ /**
+ * Return the number of clusters this instance will use.
+ * @return the number of clusters
+ */
+ public int getK() {
+ return k;
+ }
+
+ /**
+ * Returns the maximum number of iterations this instance will use.
+ * @return the maximum number of iterations, or -1 if no maximum is set
+ */
+ public int getMaxIterations() {
+ return maxIterations;
+ }
+
+ /**
+ * Returns the random generator this instance will use.
+ * @return the random generator
+ */
+ public RandomGenerator getRandomGenerator() {
+ return random;
+ }
+
+ /**
+ * Returns the {@link EmptyClusterStrategy} used by this instance.
+ * @return the {@link EmptyClusterStrategy}
+ */
+ public EmptyClusterStrategy getEmptyClusterStrategy() {
+ return emptyStrategy;
+ }
+
+ /**
+ * Runs the K-means++ clustering algorithm.
+ *
+ * @param points the points to cluster
+ * @return a list of clusters containing the points
+ * @throws MathIllegalArgumentException if the data points are null or the number
+ * of clusters is larger than the number of data points
+ * @throws ConvergenceException if an empty cluster is encountered and the
+ * {@link #emptyStrategy} is set to {@code ERROR}
+ */
+ @Override
+ public List<CentroidCluster<T>> cluster(final Collection<T> points)
+ throws MathIllegalArgumentException, ConvergenceException {
+
+ // sanity checks
+ MathUtils.checkNotNull(points);
+
+ // number of clusters has to be smaller or equal the number of data points
+ if (points.size() < k) {
+ throw new NumberIsTooSmallException(points.size(), k, false);
+ }
+
+ // create the initial clusters
+ List<CentroidCluster<T>> clusters = chooseInitialCenters(points);
+
+ // create an array containing the latest assignment of a point to a cluster
+ // no need to initialize the array, as it will be filled with the first assignment
+ int[] assignments = new int[points.size()];
+ assignPointsToClusters(clusters, points, assignments);
+
+ // iterate through updating the centers until we're done
+ final int max = (maxIterations < 0) ? Integer.MAX_VALUE : maxIterations;
+ for (int count = 0; count < max; count++) {
+ boolean emptyCluster = false;
+ List<CentroidCluster<T>> newClusters = new ArrayList<CentroidCluster<T>>();
+ for (final CentroidCluster<T> cluster : clusters) {
+ final Clusterable newCenter;
+ if (cluster.getPoints().isEmpty()) {
+ switch (emptyStrategy) {
+ case LARGEST_VARIANCE :
+ newCenter = getPointFromLargestVarianceCluster(clusters);
+ break;
+ case LARGEST_POINTS_NUMBER :
+ newCenter = getPointFromLargestNumberCluster(clusters);
+ break;
+ case FARTHEST_POINT :
+ newCenter = getFarthestPoint(clusters);
+ break;
+ default :
+ throw new ConvergenceException(LocalizedFormats.EMPTY_CLUSTER_IN_K_MEANS);
+ }
+ emptyCluster = true;
+ } else {
+ newCenter = centroidOf(cluster.getPoints(), cluster.getCenter().getPoint().length);
+ }
+ newClusters.add(new CentroidCluster<T>(newCenter));
+ }
+ int changes = assignPointsToClusters(newClusters, points, assignments);
+ clusters = newClusters;
+
+ // if there were no more changes in the point-to-cluster assignment
+ // and there are no empty clusters left, return the current clusters
+ if (changes == 0 && !emptyCluster) {
+ return clusters;
+ }
+ }
+ return clusters;
+ }
+
+ /**
+ * Adds the given points to the closest {@link Cluster}.
+ *
+ * @param clusters the {@link Cluster}s to add the points to
+ * @param points the points to add to the given {@link Cluster}s
+ * @param assignments points assignments to clusters
+ * @return the number of points assigned to different clusters as the iteration before
+ */
+ private int assignPointsToClusters(final List<CentroidCluster<T>> clusters,
+ final Collection<T> points,
+ final int[] assignments) {
+ int assignedDifferently = 0;
+ int pointIndex = 0;
+ for (final T p : points) {
+ int clusterIndex = getNearestCluster(clusters, p);
+ if (clusterIndex != assignments[pointIndex]) {
+ assignedDifferently++;
+ }
+
+ CentroidCluster<T> cluster = clusters.get(clusterIndex);
+ cluster.addPoint(p);
+ assignments[pointIndex++] = clusterIndex;
+ }
+
+ return assignedDifferently;
+ }
+
+ /**
+ * Use K-means++ to choose the initial centers.
+ *
+ * @param points the points to choose the initial centers from
+ * @return the initial centers
+ */
+ private List<CentroidCluster<T>> chooseInitialCenters(final Collection<T> points) {
+
+ // Convert to list for indexed access. Make it unmodifiable, since removal of items
+ // would screw up the logic of this method.
+ final List<T> pointList = Collections.unmodifiableList(new ArrayList<T> (points));
+
+ // The number of points in the list.
+ final int numPoints = pointList.size();
+
+ // Set the corresponding element in this array to indicate when
+ // elements of pointList are no longer available.
+ final boolean[] taken = new boolean[numPoints];
+
+ // The resulting list of initial centers.
+ final List<CentroidCluster<T>> resultSet = new ArrayList<CentroidCluster<T>>();
+
+ // Choose one center uniformly at random from among the data points.
+ final int firstPointIndex = random.nextInt(numPoints);
+
+ final T firstPoint = pointList.get(firstPointIndex);
+
+ resultSet.add(new CentroidCluster<T>(firstPoint));
+
+ // Must mark it as taken
+ taken[firstPointIndex] = true;
+
+ // To keep track of the minimum distance squared of elements of
+ // pointList to elements of resultSet.
+ final double[] minDistSquared = new double[numPoints];
+
+ // Initialize the elements. Since the only point in resultSet is firstPoint,
+ // this is very easy.
+ for (int i = 0; i < numPoints; i++) {
+ if (i != firstPointIndex) { // That point isn't considered
+ double d = distance(firstPoint, pointList.get(i));
+ minDistSquared[i] = d*d;
+ }
+ }
+
+ while (resultSet.size() < k) {
+
+ // Sum up the squared distances for the points in pointList not
+ // already taken.
+ double distSqSum = 0.0;
+
+ for (int i = 0; i < numPoints; i++) {
+ if (!taken[i]) {
+ distSqSum += minDistSquared[i];
+ }
+ }
+
+ // Add one new data point as a center. Each point x is chosen with
+ // probability proportional to D(x)2
+ final double r = random.nextDouble() * distSqSum;
+
+ // The index of the next point to be added to the resultSet.
+ int nextPointIndex = -1;
+
+ // Sum through the squared min distances again, stopping when
+ // sum >= r.
+ double sum = 0.0;
+ for (int i = 0; i < numPoints; i++) {
+ if (!taken[i]) {
+ sum += minDistSquared[i];
+ if (sum >= r) {
+ nextPointIndex = i;
+ break;
+ }
+ }
+ }
+
+ // If it's not set to >= 0, the point wasn't found in the previous
+ // for loop, probably because distances are extremely small. Just pick
+ // the last available point.
+ if (nextPointIndex == -1) {
+ for (int i = numPoints - 1; i >= 0; i--) {
+ if (!taken[i]) {
+ nextPointIndex = i;
+ break;
+ }
+ }
+ }
+
+ // We found one.
+ if (nextPointIndex >= 0) {
+
+ final T p = pointList.get(nextPointIndex);
+
+ resultSet.add(new CentroidCluster<T> (p));
+
+ // Mark it as taken.
+ taken[nextPointIndex] = true;
+
+ if (resultSet.size() < k) {
+ // Now update elements of minDistSquared. We only have to compute
+ // the distance to the new center to do this.
+ for (int j = 0; j < numPoints; j++) {
+ // Only have to worry about the points still not taken.
+ if (!taken[j]) {
+ double d = distance(p, pointList.get(j));
+ double d2 = d * d;
+ if (d2 < minDistSquared[j]) {
+ minDistSquared[j] = d2;
+ }
+ }
+ }
+ }
+
+ } else {
+ // None found --
+ // Break from the while loop to prevent
+ // an infinite loop.
+ break;
+ }
+ }
+
+ return resultSet;
+ }
+
+ /**
+ * Get a random point from the {@link Cluster} with the largest distance variance.
+ *
+ * @param clusters the {@link Cluster}s to search
+ * @return a random point from the selected cluster
+ * @throws ConvergenceException if clusters are all empty
+ */
+ private T getPointFromLargestVarianceCluster(final Collection<CentroidCluster<T>> clusters)
+ throws ConvergenceException {
+
+ double maxVariance = Double.NEGATIVE_INFINITY;
+ Cluster<T> selected = null;
+ for (final CentroidCluster<T> cluster : clusters) {
+ if (!cluster.getPoints().isEmpty()) {
+
+ // compute the distance variance of the current cluster
+ final Clusterable center = cluster.getCenter();
+ final Variance stat = new Variance();
+ for (final T point : cluster.getPoints()) {
+ stat.increment(distance(point, center));
+ }
+ final double variance = stat.getResult();
+
+ // select the cluster with the largest variance
+ if (variance > maxVariance) {
+ maxVariance = variance;
+ selected = cluster;
+ }
+
+ }
+ }
+
+ // did we find at least one non-empty cluster ?
+ if (selected == null) {
+ throw new ConvergenceException(LocalizedFormats.EMPTY_CLUSTER_IN_K_MEANS);
+ }
+
+ // extract a random point from the cluster
+ final List<T> selectedPoints = selected.getPoints();
+ return selectedPoints.remove(random.nextInt(selectedPoints.size()));
+
+ }
+
+ /**
+ * Get a random point from the {@link Cluster} with the largest number of points
+ *
+ * @param clusters the {@link Cluster}s to search
+ * @return a random point from the selected cluster
+ * @throws ConvergenceException if clusters are all empty
+ */
+ private T getPointFromLargestNumberCluster(final Collection<? extends Cluster<T>> clusters)
+ throws ConvergenceException {
+
+ int maxNumber = 0;
+ Cluster<T> selected = null;
+ for (final Cluster<T> cluster : clusters) {
+
+ // get the number of points of the current cluster
+ final int number = cluster.getPoints().size();
+
+ // select the cluster with the largest number of points
+ if (number > maxNumber) {
+ maxNumber = number;
+ selected = cluster;
+ }
+
+ }
+
+ // did we find at least one non-empty cluster ?
+ if (selected == null) {
+ throw new ConvergenceException(LocalizedFormats.EMPTY_CLUSTER_IN_K_MEANS);
+ }
+
+ // extract a random point from the cluster
+ final List<T> selectedPoints = selected.getPoints();
+ return selectedPoints.remove(random.nextInt(selectedPoints.size()));
+
+ }
+
+ /**
+ * Get the point farthest to its cluster center
+ *
+ * @param clusters the {@link Cluster}s to search
+ * @return point farthest to its cluster center
+ * @throws ConvergenceException if clusters are all empty
+ */
+ private T getFarthestPoint(final Collection<CentroidCluster<T>> clusters) throws ConvergenceException {
+
+ double maxDistance = Double.NEGATIVE_INFINITY;
+ Cluster<T> selectedCluster = null;
+ int selectedPoint = -1;
+ for (final CentroidCluster<T> cluster : clusters) {
+
+ // get the farthest point
+ final Clusterable center = cluster.getCenter();
+ final List<T> points = cluster.getPoints();
+ for (int i = 0; i < points.size(); ++i) {
+ final double distance = distance(points.get(i), center);
+ if (distance > maxDistance) {
+ maxDistance = distance;
+ selectedCluster = cluster;
+ selectedPoint = i;
+ }
+ }
+
+ }
+
+ // did we find at least one non-empty cluster ?
+ if (selectedCluster == null) {
+ throw new ConvergenceException(LocalizedFormats.EMPTY_CLUSTER_IN_K_MEANS);
+ }
+
+ return selectedCluster.getPoints().remove(selectedPoint);
+
+ }
+
+ /**
+ * Returns the nearest {@link Cluster} to the given point
+ *
+ * @param clusters the {@link Cluster}s to search
+ * @param point the point to find the nearest {@link Cluster} for
+ * @return the index of the nearest {@link Cluster} to the given point
+ */
+ private int getNearestCluster(final Collection<CentroidCluster<T>> clusters, final T point) {
+ double minDistance = Double.MAX_VALUE;
+ int clusterIndex = 0;
+ int minCluster = 0;
+ for (final CentroidCluster<T> c : clusters) {
+ final double distance = distance(point, c.getCenter());
+ if (distance < minDistance) {
+ minDistance = distance;
+ minCluster = clusterIndex;
+ }
+ clusterIndex++;
+ }
+ return minCluster;
+ }
+
+ /**
+ * Computes the centroid for a set of points.
+ *
+ * @param points the set of points
+ * @param dimension the point dimension
+ * @return the computed centroid for the set of points
+ */
+ private Clusterable centroidOf(final Collection<T> points, final int dimension) {
+ final double[] centroid = new double[dimension];
+ for (final T p : points) {
+ final double[] point = p.getPoint();
+ for (int i = 0; i < centroid.length; i++) {
+ centroid[i] += point[i];
+ }
+ }
+ for (int i = 0; i < centroid.length; i++) {
+ centroid[i] /= points.size();
+ }
+ return new DoublePoint(centroid);
+ }
+
+}
diff --git a/src/main/java/org/apache/commons/math3/ml/clustering/MultiKMeansPlusPlusClusterer.java b/src/main/java/org/apache/commons/math3/ml/clustering/MultiKMeansPlusPlusClusterer.java
new file mode 100644
index 0000000..796fc7a
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/ml/clustering/MultiKMeansPlusPlusClusterer.java
@@ -0,0 +1,135 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math3.ml.clustering;
+
+import java.util.Collection;
+import java.util.List;
+
+import org.apache.commons.math3.exception.ConvergenceException;
+import org.apache.commons.math3.exception.MathIllegalArgumentException;
+import org.apache.commons.math3.ml.clustering.evaluation.ClusterEvaluator;
+import org.apache.commons.math3.ml.clustering.evaluation.SumOfClusterVariances;
+
+/**
+ * A wrapper around a k-means++ clustering algorithm which performs multiple trials
+ * and returns the best solution.
+ * @param <T> type of the points to cluster
+ * @since 3.2
+ */
+public class MultiKMeansPlusPlusClusterer<T extends Clusterable> extends Clusterer<T> {
+
+ /** The underlying k-means clusterer. */
+ private final KMeansPlusPlusClusterer<T> clusterer;
+
+ /** The number of trial runs. */
+ private final int numTrials;
+
+ /** The cluster evaluator to use. */
+ private final ClusterEvaluator<T> evaluator;
+
+ /** Build a clusterer.
+ * @param clusterer the k-means clusterer to use
+ * @param numTrials number of trial runs
+ */
+ public MultiKMeansPlusPlusClusterer(final KMeansPlusPlusClusterer<T> clusterer,
+ final int numTrials) {
+ this(clusterer, numTrials, new SumOfClusterVariances<T>(clusterer.getDistanceMeasure()));
+ }
+
+ /** Build a clusterer.
+ * @param clusterer the k-means clusterer to use
+ * @param numTrials number of trial runs
+ * @param evaluator the cluster evaluator to use
+ * @since 3.3
+ */
+ public MultiKMeansPlusPlusClusterer(final KMeansPlusPlusClusterer<T> clusterer,
+ final int numTrials,
+ final ClusterEvaluator<T> evaluator) {
+ super(clusterer.getDistanceMeasure());
+ this.clusterer = clusterer;
+ this.numTrials = numTrials;
+ this.evaluator = evaluator;
+ }
+
+ /**
+ * Returns the embedded k-means clusterer used by this instance.
+ * @return the embedded clusterer
+ */
+ public KMeansPlusPlusClusterer<T> getClusterer() {
+ return clusterer;
+ }
+
+ /**
+ * Returns the number of trials this instance will do.
+ * @return the number of trials
+ */
+ public int getNumTrials() {
+ return numTrials;
+ }
+
+ /**
+ * Returns the {@link ClusterEvaluator} used to determine the "best" clustering.
+ * @return the used {@link ClusterEvaluator}
+ * @since 3.3
+ */
+ public ClusterEvaluator<T> getClusterEvaluator() {
+ return evaluator;
+ }
+
+ /**
+ * Runs the K-means++ clustering algorithm.
+ *
+ * @param points the points to cluster
+ * @return a list of clusters containing the points
+ * @throws MathIllegalArgumentException if the data points are null or the number
+ * of clusters is larger than the number of data points
+ * @throws ConvergenceException if an empty cluster is encountered and the
+ * underlying {@link KMeansPlusPlusClusterer} has its
+ * {@link KMeansPlusPlusClusterer.EmptyClusterStrategy} is set to {@code ERROR}.
+ */
+ @Override
+ public List<CentroidCluster<T>> cluster(final Collection<T> points)
+ throws MathIllegalArgumentException, ConvergenceException {
+
+ // at first, we have not found any clusters list yet
+ List<CentroidCluster<T>> best = null;
+ double bestVarianceSum = Double.POSITIVE_INFINITY;
+
+ // do several clustering trials
+ for (int i = 0; i < numTrials; ++i) {
+
+ // compute a clusters list
+ List<CentroidCluster<T>> clusters = clusterer.cluster(points);
+
+ // compute the variance of the current list
+ final double varianceSum = evaluator.score(clusters);
+
+ if (evaluator.isBetterScore(varianceSum, bestVarianceSum)) {
+ // this one is the best we have found so far, remember it
+ best = clusters;
+ bestVarianceSum = varianceSum;
+ }
+
+ }
+
+ // return the best clusters list found
+ return best;
+
+ }
+
+}
diff --git a/src/main/java/org/apache/commons/math3/ml/clustering/evaluation/ClusterEvaluator.java b/src/main/java/org/apache/commons/math3/ml/clustering/evaluation/ClusterEvaluator.java
new file mode 100644
index 0000000..2bb8ba3
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/ml/clustering/evaluation/ClusterEvaluator.java
@@ -0,0 +1,122 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math3.ml.clustering.evaluation;
+
+import java.util.List;
+
+import org.apache.commons.math3.ml.clustering.CentroidCluster;
+import org.apache.commons.math3.ml.clustering.Cluster;
+import org.apache.commons.math3.ml.clustering.Clusterable;
+import org.apache.commons.math3.ml.clustering.DoublePoint;
+import org.apache.commons.math3.ml.distance.DistanceMeasure;
+import org.apache.commons.math3.ml.distance.EuclideanDistance;
+
+/**
+ * Base class for cluster evaluation methods.
+ *
+ * @param <T> type of the clustered points
+ * @since 3.3
+ */
+public abstract class ClusterEvaluator<T extends Clusterable> {
+
+ /** The distance measure to use when evaluating the cluster. */
+ private final DistanceMeasure measure;
+
+ /**
+ * Creates a new cluster evaluator with an {@link EuclideanDistance}
+ * as distance measure.
+ */
+ public ClusterEvaluator() {
+ this(new EuclideanDistance());
+ }
+
+ /**
+ * Creates a new cluster evaluator with the given distance measure.
+ * @param measure the distance measure to use
+ */
+ public ClusterEvaluator(final DistanceMeasure measure) {
+ this.measure = measure;
+ }
+
+ /**
+ * Computes the evaluation score for the given list of clusters.
+ * @param clusters the clusters to evaluate
+ * @return the computed score
+ */
+ public abstract double score(List<? extends Cluster<T>> clusters);
+
+ /**
+ * Returns whether the first evaluation score is considered to be better
+ * than the second one by this evaluator.
+ * <p>
+ * Specific implementations shall override this method if the returned scores
+ * do not follow the same ordering, i.e. smaller score is better.
+ *
+ * @param score1 the first score
+ * @param score2 the second score
+ * @return {@code true} if the first score is considered to be better, {@code false} otherwise
+ */
+ public boolean isBetterScore(double score1, double score2) {
+ return score1 < score2;
+ }
+
+ /**
+ * Calculates the distance between two {@link Clusterable} instances
+ * with the configured {@link DistanceMeasure}.
+ *
+ * @param p1 the first clusterable
+ * @param p2 the second clusterable
+ * @return the distance between the two clusterables
+ */
+ protected double distance(final Clusterable p1, final Clusterable p2) {
+ return measure.compute(p1.getPoint(), p2.getPoint());
+ }
+
+ /**
+ * Computes the centroid for a cluster.
+ *
+ * @param cluster the cluster
+ * @return the computed centroid for the cluster,
+ * or {@code null} if the cluster does not contain any points
+ */
+ protected Clusterable centroidOf(final Cluster<T> cluster) {
+ final List<T> points = cluster.getPoints();
+ if (points.isEmpty()) {
+ return null;
+ }
+
+ // in case the cluster is of type CentroidCluster, no need to compute the centroid
+ if (cluster instanceof CentroidCluster) {
+ return ((CentroidCluster<T>) cluster).getCenter();
+ }
+
+ final int dimension = points.get(0).getPoint().length;
+ final double[] centroid = new double[dimension];
+ for (final T p : points) {
+ final double[] point = p.getPoint();
+ for (int i = 0; i < centroid.length; i++) {
+ centroid[i] += point[i];
+ }
+ }
+ for (int i = 0; i < centroid.length; i++) {
+ centroid[i] /= points.size();
+ }
+ return new DoublePoint(centroid);
+ }
+
+}
diff --git a/src/main/java/org/apache/commons/math3/ml/clustering/evaluation/SumOfClusterVariances.java b/src/main/java/org/apache/commons/math3/ml/clustering/evaluation/SumOfClusterVariances.java
new file mode 100644
index 0000000..b5b249c
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/ml/clustering/evaluation/SumOfClusterVariances.java
@@ -0,0 +1,69 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math3.ml.clustering.evaluation;
+
+import java.util.List;
+
+import org.apache.commons.math3.ml.clustering.Cluster;
+import org.apache.commons.math3.ml.clustering.Clusterable;
+import org.apache.commons.math3.ml.distance.DistanceMeasure;
+import org.apache.commons.math3.stat.descriptive.moment.Variance;
+
+/**
+ * Computes the sum of intra-cluster distance variances according to the formula:
+ * <pre>
+ * \( score = \sum\limits_{i=1}^n \sigma_i^2 \)
+ * </pre>
+ * where n is the number of clusters and \( \sigma_i^2 \) is the variance of
+ * intra-cluster distances of cluster \( c_i \).
+ *
+ * @param <T> the type of the clustered points
+ * @since 3.3
+ */
+public class SumOfClusterVariances<T extends Clusterable> extends ClusterEvaluator<T> {
+
+ /**
+ *
+ * @param measure the distance measure to use
+ */
+ public SumOfClusterVariances(final DistanceMeasure measure) {
+ super(measure);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public double score(final List<? extends Cluster<T>> clusters) {
+ double varianceSum = 0.0;
+ for (final Cluster<T> cluster : clusters) {
+ if (!cluster.getPoints().isEmpty()) {
+
+ final Clusterable center = centroidOf(cluster);
+
+ // compute the distance variance of the current cluster
+ final Variance stat = new Variance();
+ for (final T point : cluster.getPoints()) {
+ stat.increment(distance(point, center));
+ }
+ varianceSum += stat.getResult();
+
+ }
+ }
+ return varianceSum;
+ }
+
+}
diff --git a/src/main/java/org/apache/commons/math3/ml/clustering/evaluation/package-info.java b/src/main/java/org/apache/commons/math3/ml/clustering/evaluation/package-info.java
new file mode 100644
index 0000000..700f566
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/ml/clustering/evaluation/package-info.java
@@ -0,0 +1,20 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/**
+ * Cluster evaluation methods.
+ */
+package org.apache.commons.math3.ml.clustering.evaluation;
diff --git a/src/main/java/org/apache/commons/math3/ml/clustering/package-info.java b/src/main/java/org/apache/commons/math3/ml/clustering/package-info.java
new file mode 100644
index 0000000..02f1d20
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/ml/clustering/package-info.java
@@ -0,0 +1,20 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/**
+ * Clustering algorithms.
+ */
+package org.apache.commons.math3.ml.clustering;
diff --git a/src/main/java/org/apache/commons/math3/ml/distance/CanberraDistance.java b/src/main/java/org/apache/commons/math3/ml/distance/CanberraDistance.java
new file mode 100644
index 0000000..d467c3b
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/ml/distance/CanberraDistance.java
@@ -0,0 +1,46 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math3.ml.distance;
+
+import org.apache.commons.math3.exception.DimensionMismatchException;
+import org.apache.commons.math3.util.FastMath;
+import org.apache.commons.math3.util.MathArrays;
+
+/**
+ * Calculates the Canberra distance between two points.
+ *
+ * @since 3.2
+ */
+public class CanberraDistance implements DistanceMeasure {
+
+ /** Serializable version identifier. */
+ private static final long serialVersionUID = -6972277381587032228L;
+
+ /** {@inheritDoc} */
+ public double compute(double[] a, double[] b)
+ throws DimensionMismatchException {
+ MathArrays.checkEqualLength(a, b);
+ double sum = 0;
+ for (int i = 0; i < a.length; i++) {
+ final double num = FastMath.abs(a[i] - b[i]);
+ final double denom = FastMath.abs(a[i]) + FastMath.abs(b[i]);
+ sum += num == 0.0 && denom == 0.0 ? 0.0 : num / denom;
+ }
+ return sum;
+ }
+
+}
diff --git a/src/main/java/org/apache/commons/math3/ml/distance/ChebyshevDistance.java b/src/main/java/org/apache/commons/math3/ml/distance/ChebyshevDistance.java
new file mode 100644
index 0000000..05dccb5
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/ml/distance/ChebyshevDistance.java
@@ -0,0 +1,38 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math3.ml.distance;
+
+import org.apache.commons.math3.exception.DimensionMismatchException;
+import org.apache.commons.math3.util.MathArrays;
+
+/**
+ * Calculates the L<sub>&infin;</sub> (max of abs) distance between two points.
+ *
+ * @since 3.2
+ */
+public class ChebyshevDistance implements DistanceMeasure {
+
+ /** Serializable version identifier. */
+ private static final long serialVersionUID = -4694868171115238296L;
+
+ /** {@inheritDoc} */
+ public double compute(double[] a, double[] b)
+ throws DimensionMismatchException {
+ return MathArrays.distanceInf(a, b);
+ }
+
+}
diff --git a/src/main/java/org/apache/commons/math3/ml/distance/DistanceMeasure.java b/src/main/java/org/apache/commons/math3/ml/distance/DistanceMeasure.java
new file mode 100644
index 0000000..ff9c27f
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/ml/distance/DistanceMeasure.java
@@ -0,0 +1,41 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math3.ml.distance;
+
+import java.io.Serializable;
+
+import org.apache.commons.math3.exception.DimensionMismatchException;
+
+/**
+ * Interface for distance measures of n-dimensional vectors.
+ *
+ * @since 3.2
+ */
+public interface DistanceMeasure extends Serializable {
+
+ /**
+ * Compute the distance between two n-dimensional vectors.
+ * <p>
+ * The two vectors are required to have the same dimension.
+ *
+ * @param a the first vector
+ * @param b the second vector
+ * @return the distance between the two vectors
+ * @throws DimensionMismatchException if the array lengths differ.
+ */
+ double compute(double[] a, double[] b) throws DimensionMismatchException;
+}
diff --git a/src/main/java/org/apache/commons/math3/ml/distance/EarthMoversDistance.java b/src/main/java/org/apache/commons/math3/ml/distance/EarthMoversDistance.java
new file mode 100644
index 0000000..2518624
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/ml/distance/EarthMoversDistance.java
@@ -0,0 +1,48 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math3.ml.distance;
+
+import org.apache.commons.math3.exception.DimensionMismatchException;
+import org.apache.commons.math3.util.FastMath;
+import org.apache.commons.math3.util.MathArrays;
+
+/**
+ * Calculates the Earh Mover's distance (also known as Wasserstein metric) between two distributions.
+ *
+ * @see <a href="http://en.wikipedia.org/wiki/Earth_mover's_distance">Earth Mover's distance (Wikipedia)</a>
+ *
+ * @since 3.3
+ */
+public class EarthMoversDistance implements DistanceMeasure {
+
+ /** Serializable version identifier. */
+ private static final long serialVersionUID = -5406732779747414922L;
+
+ /** {@inheritDoc} */
+ public double compute(double[] a, double[] b)
+ throws DimensionMismatchException {
+ MathArrays.checkEqualLength(a, b);
+ double lastDistance = 0;
+ double totalDistance = 0;
+ for (int i = 0; i < a.length; i++) {
+ final double currentDistance = (a[i] + lastDistance) - b[i];
+ totalDistance += FastMath.abs(currentDistance);
+ lastDistance = currentDistance;
+ }
+ return totalDistance;
+ }
+}
diff --git a/src/main/java/org/apache/commons/math3/ml/distance/EuclideanDistance.java b/src/main/java/org/apache/commons/math3/ml/distance/EuclideanDistance.java
new file mode 100644
index 0000000..187badc
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/ml/distance/EuclideanDistance.java
@@ -0,0 +1,38 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math3.ml.distance;
+
+import org.apache.commons.math3.exception.DimensionMismatchException;
+import org.apache.commons.math3.util.MathArrays;
+
+/**
+ * Calculates the L<sub>2</sub> (Euclidean) distance between two points.
+ *
+ * @since 3.2
+ */
+public class EuclideanDistance implements DistanceMeasure {
+
+ /** Serializable version identifier. */
+ private static final long serialVersionUID = 1717556319784040040L;
+
+ /** {@inheritDoc} */
+ public double compute(double[] a, double[] b)
+ throws DimensionMismatchException {
+ return MathArrays.distance(a, b);
+ }
+
+}
diff --git a/src/main/java/org/apache/commons/math3/ml/distance/ManhattanDistance.java b/src/main/java/org/apache/commons/math3/ml/distance/ManhattanDistance.java
new file mode 100644
index 0000000..2eebe1b
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/ml/distance/ManhattanDistance.java
@@ -0,0 +1,38 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math3.ml.distance;
+
+import org.apache.commons.math3.exception.DimensionMismatchException;
+import org.apache.commons.math3.util.MathArrays;
+
+/**
+ * Calculates the L<sub>1</sub> (sum of abs) distance between two points.
+ *
+ * @since 3.2
+ */
+public class ManhattanDistance implements DistanceMeasure {
+
+ /** Serializable version identifier. */
+ private static final long serialVersionUID = -9108154600539125566L;
+
+ /** {@inheritDoc} */
+ public double compute(double[] a, double[] b)
+ throws DimensionMismatchException {
+ return MathArrays.distance1(a, b);
+ }
+
+}
diff --git a/src/main/java/org/apache/commons/math3/ml/distance/package-info.java b/src/main/java/org/apache/commons/math3/ml/distance/package-info.java
new file mode 100644
index 0000000..f6d124a
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/ml/distance/package-info.java
@@ -0,0 +1,20 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/**
+ * Common distance measures.
+ */
+package org.apache.commons.math3.ml.distance;
diff --git a/src/main/java/org/apache/commons/math3/ml/neuralnet/FeatureInitializer.java b/src/main/java/org/apache/commons/math3/ml/neuralnet/FeatureInitializer.java
new file mode 100644
index 0000000..1f48d45
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/ml/neuralnet/FeatureInitializer.java
@@ -0,0 +1,32 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math3.ml.neuralnet;
+
+/**
+ * Defines how to assign the first value of a neuron's feature.
+ *
+ * @since 3.3
+ */
+public interface FeatureInitializer {
+ /**
+ * Selects the initial value.
+ *
+ * @return the initial value.
+ */
+ double value();
+}
diff --git a/src/main/java/org/apache/commons/math3/ml/neuralnet/FeatureInitializerFactory.java b/src/main/java/org/apache/commons/math3/ml/neuralnet/FeatureInitializerFactory.java
new file mode 100644
index 0000000..f5569b1
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/ml/neuralnet/FeatureInitializerFactory.java
@@ -0,0 +1,114 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math3.ml.neuralnet;
+
+import org.apache.commons.math3.distribution.RealDistribution;
+import org.apache.commons.math3.distribution.UniformRealDistribution;
+import org.apache.commons.math3.analysis.UnivariateFunction;
+import org.apache.commons.math3.analysis.function.Constant;
+import org.apache.commons.math3.random.RandomGenerator;
+
+/**
+ * Creates functions that will select the initial values of a neuron's
+ * features.
+ *
+ * @since 3.3
+ */
+public class FeatureInitializerFactory {
+ /** Class contains only static methods. */
+ private FeatureInitializerFactory() {}
+
+ /**
+ * Uniform sampling of the given range.
+ *
+ * @param min Lower bound of the range.
+ * @param max Upper bound of the range.
+ * @param rng Random number generator used to draw samples from a
+ * uniform distribution.
+ * @return an initializer such that the features will be initialized with
+ * values within the given range.
+ * @throws org.apache.commons.math3.exception.NumberIsTooLargeException
+ * if {@code min >= max}.
+ */
+ public static FeatureInitializer uniform(final RandomGenerator rng,
+ final double min,
+ final double max) {
+ return randomize(new UniformRealDistribution(rng, min, max),
+ function(new Constant(0), 0, 0));
+ }
+
+ /**
+ * Uniform sampling of the given range.
+ *
+ * @param min Lower bound of the range.
+ * @param max Upper bound of the range.
+ * @return an initializer such that the features will be initialized with
+ * values within the given range.
+ * @throws org.apache.commons.math3.exception.NumberIsTooLargeException
+ * if {@code min >= max}.
+ */
+ public static FeatureInitializer uniform(final double min,
+ final double max) {
+ return randomize(new UniformRealDistribution(min, max),
+ function(new Constant(0), 0, 0));
+ }
+
+ /**
+ * Creates an initializer from a univariate function {@code f(x)}.
+ * The argument {@code x} is set to {@code init} at the first call
+ * and will be incremented at each call.
+ *
+ * @param f Function.
+ * @param init Initial value.
+ * @param inc Increment
+ * @return the initializer.
+ */
+ public static FeatureInitializer function(final UnivariateFunction f,
+ final double init,
+ final double inc) {
+ return new FeatureInitializer() {
+ /** Argument. */
+ private double arg = init;
+
+ /** {@inheritDoc} */
+ public double value() {
+ final double result = f.value(arg);
+ arg += inc;
+ return result;
+ }
+ };
+ }
+
+ /**
+ * Adds some amount of random data to the given initializer.
+ *
+ * @param random Random variable distribution.
+ * @param orig Original initializer.
+ * @return an initializer whose {@link FeatureInitializer#value() value}
+ * method will return {@code orig.value() + random.sample()}.
+ */
+ public static FeatureInitializer randomize(final RealDistribution random,
+ final FeatureInitializer orig) {
+ return new FeatureInitializer() {
+ /** {@inheritDoc} */
+ public double value() {
+ return orig.value() + random.sample();
+ }
+ };
+ }
+}
diff --git a/src/main/java/org/apache/commons/math3/ml/neuralnet/MapUtils.java b/src/main/java/org/apache/commons/math3/ml/neuralnet/MapUtils.java
new file mode 100644
index 0000000..0b7a675
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/ml/neuralnet/MapUtils.java
@@ -0,0 +1,326 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math3.ml.neuralnet;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Comparator;
+
+import org.apache.commons.math3.exception.NoDataException;
+import org.apache.commons.math3.ml.distance.DistanceMeasure;
+import org.apache.commons.math3.ml.neuralnet.twod.NeuronSquareMesh2D;
+import org.apache.commons.math3.util.Pair;
+
+/**
+ * Utilities for network maps.
+ *
+ * @since 3.3
+ */
+public class MapUtils {
+ /**
+ * Class contains only static methods.
+ */
+ private MapUtils() {}
+
+ /**
+ * Finds the neuron that best matches the given features.
+ *
+ * @param features Data.
+ * @param neurons List of neurons to scan. If the list is empty
+ * {@code null} will be returned.
+ * @param distance Distance function. The neuron's features are
+ * passed as the first argument to {@link DistanceMeasure#compute(double[],double[])}.
+ * @return the neuron whose features are closest to the given data.
+ * @throws org.apache.commons.math3.exception.DimensionMismatchException
+ * if the size of the input is not compatible with the neurons features
+ * size.
+ */
+ public static Neuron findBest(double[] features,
+ Iterable<Neuron> neurons,
+ DistanceMeasure distance) {
+ Neuron best = null;
+ double min = Double.POSITIVE_INFINITY;
+ for (final Neuron n : neurons) {
+ final double d = distance.compute(n.getFeatures(), features);
+ if (d < min) {
+ min = d;
+ best = n;
+ }
+ }
+
+ return best;
+ }
+
+ /**
+ * Finds the two neurons that best match the given features.
+ *
+ * @param features Data.
+ * @param neurons List of neurons to scan. If the list is empty
+ * {@code null} will be returned.
+ * @param distance Distance function. The neuron's features are
+ * passed as the first argument to {@link DistanceMeasure#compute(double[],double[])}.
+ * @return the two neurons whose features are closest to the given data.
+ * @throws org.apache.commons.math3.exception.DimensionMismatchException
+ * if the size of the input is not compatible with the neurons features
+ * size.
+ */
+ public static Pair<Neuron, Neuron> findBestAndSecondBest(double[] features,
+ Iterable<Neuron> neurons,
+ DistanceMeasure distance) {
+ Neuron[] best = { null, null };
+ double[] min = { Double.POSITIVE_INFINITY,
+ Double.POSITIVE_INFINITY };
+ for (final Neuron n : neurons) {
+ final double d = distance.compute(n.getFeatures(), features);
+ if (d < min[0]) {
+ // Replace second best with old best.
+ min[1] = min[0];
+ best[1] = best[0];
+
+ // Store current as new best.
+ min[0] = d;
+ best[0] = n;
+ } else if (d < min[1]) {
+ // Replace old second best with current.
+ min[1] = d;
+ best[1] = n;
+ }
+ }
+
+ return new Pair<Neuron, Neuron>(best[0], best[1]);
+ }
+
+ /**
+ * Creates a list of neurons sorted in increased order of the distance
+ * to the given {@code features}.
+ *
+ * @param features Data.
+ * @param neurons List of neurons to scan. If it is empty, an empty array
+ * will be returned.
+ * @param distance Distance function.
+ * @return the neurons, sorted in increasing order of distance in data
+ * space.
+ * @throws org.apache.commons.math3.exception.DimensionMismatchException
+ * if the size of the input is not compatible with the neurons features
+ * size.
+ *
+ * @see #findBest(double[],Iterable,DistanceMeasure)
+ * @see #findBestAndSecondBest(double[],Iterable,DistanceMeasure)
+ *
+ * @since 3.6
+ */
+ public static Neuron[] sort(double[] features,
+ Iterable<Neuron> neurons,
+ DistanceMeasure distance) {
+ final List<PairNeuronDouble> list = new ArrayList<PairNeuronDouble>();
+
+ for (final Neuron n : neurons) {
+ final double d = distance.compute(n.getFeatures(), features);
+ list.add(new PairNeuronDouble(n, d));
+ }
+
+ Collections.sort(list, PairNeuronDouble.COMPARATOR);
+
+ final int len = list.size();
+ final Neuron[] sorted = new Neuron[len];
+
+ for (int i = 0; i < len; i++) {
+ sorted[i] = list.get(i).getNeuron();
+ }
+ return sorted;
+ }
+
+ /**
+ * Computes the <a href="http://en.wikipedia.org/wiki/U-Matrix">
+ * U-matrix</a> of a two-dimensional map.
+ *
+ * @param map Network.
+ * @param distance Function to use for computing the average
+ * distance from a neuron to its neighbours.
+ * @return the matrix of average distances.
+ */
+ public static double[][] computeU(NeuronSquareMesh2D map,
+ DistanceMeasure distance) {
+ final int numRows = map.getNumberOfRows();
+ final int numCols = map.getNumberOfColumns();
+ final double[][] uMatrix = new double[numRows][numCols];
+
+ final Network net = map.getNetwork();
+
+ for (int i = 0; i < numRows; i++) {
+ for (int j = 0; j < numCols; j++) {
+ final Neuron neuron = map.getNeuron(i, j);
+ final Collection<Neuron> neighbours = net.getNeighbours(neuron);
+ final double[] features = neuron.getFeatures();
+
+ double d = 0;
+ int count = 0;
+ for (Neuron n : neighbours) {
+ ++count;
+ d += distance.compute(features, n.getFeatures());
+ }
+
+ uMatrix[i][j] = d / count;
+ }
+ }
+
+ return uMatrix;
+ }
+
+ /**
+ * Computes the "hit" histogram of a two-dimensional map.
+ *
+ * @param data Feature vectors.
+ * @param map Network.
+ * @param distance Function to use for determining the best matching unit.
+ * @return the number of hits for each neuron in the map.
+ */
+ public static int[][] computeHitHistogram(Iterable<double[]> data,
+ NeuronSquareMesh2D map,
+ DistanceMeasure distance) {
+ final HashMap<Neuron, Integer> hit = new HashMap<Neuron, Integer>();
+ final Network net = map.getNetwork();
+
+ for (double[] f : data) {
+ final Neuron best = findBest(f, net, distance);
+ final Integer count = hit.get(best);
+ if (count == null) {
+ hit.put(best, 1);
+ } else {
+ hit.put(best, count + 1);
+ }
+ }
+
+ // Copy the histogram data into a 2D map.
+ final int numRows = map.getNumberOfRows();
+ final int numCols = map.getNumberOfColumns();
+ final int[][] histo = new int[numRows][numCols];
+
+ for (int i = 0; i < numRows; i++) {
+ for (int j = 0; j < numCols; j++) {
+ final Neuron neuron = map.getNeuron(i, j);
+ final Integer count = hit.get(neuron);
+ if (count == null) {
+ histo[i][j] = 0;
+ } else {
+ histo[i][j] = count;
+ }
+ }
+ }
+
+ return histo;
+ }
+
+ /**
+ * Computes the quantization error.
+ * The quantization error is the average distance between a feature vector
+ * and its "best matching unit" (closest neuron).
+ *
+ * @param data Feature vectors.
+ * @param neurons List of neurons to scan.
+ * @param distance Distance function.
+ * @return the error.
+ * @throws NoDataException if {@code data} is empty.
+ */
+ public static double computeQuantizationError(Iterable<double[]> data,
+ Iterable<Neuron> neurons,
+ DistanceMeasure distance) {
+ double d = 0;
+ int count = 0;
+ for (double[] f : data) {
+ ++count;
+ d += distance.compute(f, findBest(f, neurons, distance).getFeatures());
+ }
+
+ if (count == 0) {
+ throw new NoDataException();
+ }
+
+ return d / count;
+ }
+
+ /**
+ * Computes the topographic error.
+ * The topographic error is the proportion of data for which first and
+ * second best matching units are not adjacent in the map.
+ *
+ * @param data Feature vectors.
+ * @param net Network.
+ * @param distance Distance function.
+ * @return the error.
+ * @throws NoDataException if {@code data} is empty.
+ */
+ public static double computeTopographicError(Iterable<double[]> data,
+ Network net,
+ DistanceMeasure distance) {
+ int notAdjacentCount = 0;
+ int count = 0;
+ for (double[] f : data) {
+ ++count;
+ final Pair<Neuron, Neuron> p = findBestAndSecondBest(f, net, distance);
+ if (!net.getNeighbours(p.getFirst()).contains(p.getSecond())) {
+ // Increment count if first and second best matching units
+ // are not neighbours.
+ ++notAdjacentCount;
+ }
+ }
+
+ if (count == 0) {
+ throw new NoDataException();
+ }
+
+ return ((double) notAdjacentCount) / count;
+ }
+
+ /**
+ * Helper data structure holding a (Neuron, double) pair.
+ */
+ private static class PairNeuronDouble {
+ /** Comparator. */
+ static final Comparator<PairNeuronDouble> COMPARATOR
+ = new Comparator<PairNeuronDouble>() {
+ /** {@inheritDoc} */
+ public int compare(PairNeuronDouble o1,
+ PairNeuronDouble o2) {
+ return Double.compare(o1.value, o2.value);
+ }
+ };
+ /** Key. */
+ private final Neuron neuron;
+ /** Value. */
+ private final double value;
+
+ /**
+ * @param neuron Neuron.
+ * @param value Value.
+ */
+ PairNeuronDouble(Neuron neuron, double value) {
+ this.neuron = neuron;
+ this.value = value;
+ }
+
+ /** @return the neuron. */
+ public Neuron getNeuron() {
+ return neuron;
+ }
+
+ }
+}
diff --git a/src/main/java/org/apache/commons/math3/ml/neuralnet/Network.java b/src/main/java/org/apache/commons/math3/ml/neuralnet/Network.java
new file mode 100644
index 0000000..4b208a3
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/ml/neuralnet/Network.java
@@ -0,0 +1,499 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math3.ml.neuralnet;
+
+import java.io.Serializable;
+import java.io.ObjectInputStream;
+import java.util.NoSuchElementException;
+import java.util.List;
+import java.util.ArrayList;
+import java.util.Set;
+import java.util.HashSet;
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.Comparator;
+import java.util.Collections;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.atomic.AtomicLong;
+import org.apache.commons.math3.exception.DimensionMismatchException;
+import org.apache.commons.math3.exception.MathIllegalStateException;
+
+/**
+ * Neural network, composed of {@link Neuron} instances and the links
+ * between them.
+ *
+ * Although updating a neuron's state is thread-safe, modifying the
+ * network's topology (adding or removing links) is not.
+ *
+ * @since 3.3
+ */
+public class Network
+ implements Iterable<Neuron>,
+ Serializable {
+ /** Serializable. */
+ private static final long serialVersionUID = 20130207L;
+ /** Neurons. */
+ private final ConcurrentHashMap<Long, Neuron> neuronMap
+ = new ConcurrentHashMap<Long, Neuron>();
+ /** Next available neuron identifier. */
+ private final AtomicLong nextId;
+ /** Neuron's features set size. */
+ private final int featureSize;
+ /** Links. */
+ private final ConcurrentHashMap<Long, Set<Long>> linkMap
+ = new ConcurrentHashMap<Long, Set<Long>>();
+
+ /**
+ * Comparator that prescribes an order of the neurons according
+ * to the increasing order of their identifier.
+ */
+ public static class NeuronIdentifierComparator
+ implements Comparator<Neuron>,
+ Serializable {
+ /** Version identifier. */
+ private static final long serialVersionUID = 20130207L;
+
+ /** {@inheritDoc} */
+ public int compare(Neuron a,
+ Neuron b) {
+ final long aId = a.getIdentifier();
+ final long bId = b.getIdentifier();
+ return aId < bId ? -1 :
+ aId > bId ? 1 : 0;
+ }
+ }
+
+ /**
+ * Constructor with restricted access, solely used for deserialization.
+ *
+ * @param nextId Next available identifier.
+ * @param featureSize Number of features.
+ * @param neuronList Neurons.
+ * @param neighbourIdList Links associated to each of the neurons in
+ * {@code neuronList}.
+ * @throws MathIllegalStateException if an inconsistency is detected
+ * (which probably means that the serialized form has been corrupted).
+ */
+ Network(long nextId,
+ int featureSize,
+ Neuron[] neuronList,
+ long[][] neighbourIdList) {
+ final int numNeurons = neuronList.length;
+ if (numNeurons != neighbourIdList.length) {
+ throw new MathIllegalStateException();
+ }
+
+ for (int i = 0; i < numNeurons; i++) {
+ final Neuron n = neuronList[i];
+ final long id = n.getIdentifier();
+ if (id >= nextId) {
+ throw new MathIllegalStateException();
+ }
+ neuronMap.put(id, n);
+ linkMap.put(id, new HashSet<Long>());
+ }
+
+ for (int i = 0; i < numNeurons; i++) {
+ final long aId = neuronList[i].getIdentifier();
+ final Set<Long> aLinks = linkMap.get(aId);
+ for (Long bId : neighbourIdList[i]) {
+ if (neuronMap.get(bId) == null) {
+ throw new MathIllegalStateException();
+ }
+ addLinkToLinkSet(aLinks, bId);
+ }
+ }
+
+ this.nextId = new AtomicLong(nextId);
+ this.featureSize = featureSize;
+ }
+
+ /**
+ * @param initialIdentifier Identifier for the first neuron that
+ * will be added to this network.
+ * @param featureSize Size of the neuron's features.
+ */
+ public Network(long initialIdentifier,
+ int featureSize) {
+ nextId = new AtomicLong(initialIdentifier);
+ this.featureSize = featureSize;
+ }
+
+ /**
+ * Performs a deep copy of this instance.
+ * Upon return, the copied and original instances will be independent:
+ * Updating one will not affect the other.
+ *
+ * @return a new instance with the same state as this instance.
+ * @since 3.6
+ */
+ public synchronized Network copy() {
+ final Network copy = new Network(nextId.get(),
+ featureSize);
+
+
+ for (Map.Entry<Long, Neuron> e : neuronMap.entrySet()) {
+ copy.neuronMap.put(e.getKey(), e.getValue().copy());
+ }
+
+ for (Map.Entry<Long, Set<Long>> e : linkMap.entrySet()) {
+ copy.linkMap.put(e.getKey(), new HashSet<Long>(e.getValue()));
+ }
+
+ return copy;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public Iterator<Neuron> iterator() {
+ return neuronMap.values().iterator();
+ }
+
+ /**
+ * Creates a list of the neurons, sorted in a custom order.
+ *
+ * @param comparator {@link Comparator} used for sorting the neurons.
+ * @return a list of neurons, sorted in the order prescribed by the
+ * given {@code comparator}.
+ * @see NeuronIdentifierComparator
+ */
+ public Collection<Neuron> getNeurons(Comparator<Neuron> comparator) {
+ final List<Neuron> neurons = new ArrayList<Neuron>();
+ neurons.addAll(neuronMap.values());
+
+ Collections.sort(neurons, comparator);
+
+ return neurons;
+ }
+
+ /**
+ * Creates a neuron and assigns it a unique identifier.
+ *
+ * @param features Initial values for the neuron's features.
+ * @return the neuron's identifier.
+ * @throws DimensionMismatchException if the length of {@code features}
+ * is different from the expected size (as set by the
+ * {@link #Network(long,int) constructor}).
+ */
+ public long createNeuron(double[] features) {
+ if (features.length != featureSize) {
+ throw new DimensionMismatchException(features.length, featureSize);
+ }
+
+ final long id = createNextId();
+ neuronMap.put(id, new Neuron(id, features));
+ linkMap.put(id, new HashSet<Long>());
+ return id;
+ }
+
+ /**
+ * Deletes a neuron.
+ * Links from all neighbours to the removed neuron will also be
+ * {@link #deleteLink(Neuron,Neuron) deleted}.
+ *
+ * @param neuron Neuron to be removed from this network.
+ * @throws NoSuchElementException if {@code n} does not belong to
+ * this network.
+ */
+ public void deleteNeuron(Neuron neuron) {
+ final Collection<Neuron> neighbours = getNeighbours(neuron);
+
+ // Delete links to from neighbours.
+ for (Neuron n : neighbours) {
+ deleteLink(n, neuron);
+ }
+
+ // Remove neuron.
+ neuronMap.remove(neuron.getIdentifier());
+ }
+
+ /**
+ * Gets the size of the neurons' features set.
+ *
+ * @return the size of the features set.
+ */
+ public int getFeaturesSize() {
+ return featureSize;
+ }
+
+ /**
+ * Adds a link from neuron {@code a} to neuron {@code b}.
+ * Note: the link is not bi-directional; if a bi-directional link is
+ * required, an additional call must be made with {@code a} and
+ * {@code b} exchanged in the argument list.
+ *
+ * @param a Neuron.
+ * @param b Neuron.
+ * @throws NoSuchElementException if the neurons do not exist in the
+ * network.
+ */
+ public void addLink(Neuron a,
+ Neuron b) {
+ final long aId = a.getIdentifier();
+ final long bId = b.getIdentifier();
+
+ // Check that the neurons belong to this network.
+ if (a != getNeuron(aId)) {
+ throw new NoSuchElementException(Long.toString(aId));
+ }
+ if (b != getNeuron(bId)) {
+ throw new NoSuchElementException(Long.toString(bId));
+ }
+
+ // Add link from "a" to "b".
+ addLinkToLinkSet(linkMap.get(aId), bId);
+ }
+
+ /**
+ * Adds a link to neuron {@code id} in given {@code linkSet}.
+ * Note: no check verifies that the identifier indeed belongs
+ * to this network.
+ *
+ * @param linkSet Neuron identifier.
+ * @param id Neuron identifier.
+ */
+ private void addLinkToLinkSet(Set<Long> linkSet,
+ long id) {
+ linkSet.add(id);
+ }
+
+ /**
+ * Deletes the link between neurons {@code a} and {@code b}.
+ *
+ * @param a Neuron.
+ * @param b Neuron.
+ * @throws NoSuchElementException if the neurons do not exist in the
+ * network.
+ */
+ public void deleteLink(Neuron a,
+ Neuron b) {
+ final long aId = a.getIdentifier();
+ final long bId = b.getIdentifier();
+
+ // Check that the neurons belong to this network.
+ if (a != getNeuron(aId)) {
+ throw new NoSuchElementException(Long.toString(aId));
+ }
+ if (b != getNeuron(bId)) {
+ throw new NoSuchElementException(Long.toString(bId));
+ }
+
+ // Delete link from "a" to "b".
+ deleteLinkFromLinkSet(linkMap.get(aId), bId);
+ }
+
+ /**
+ * Deletes a link to neuron {@code id} in given {@code linkSet}.
+ * Note: no check verifies that the identifier indeed belongs
+ * to this network.
+ *
+ * @param linkSet Neuron identifier.
+ * @param id Neuron identifier.
+ */
+ private void deleteLinkFromLinkSet(Set<Long> linkSet,
+ long id) {
+ linkSet.remove(id);
+ }
+
+ /**
+ * Retrieves the neuron with the given (unique) {@code id}.
+ *
+ * @param id Identifier.
+ * @return the neuron associated with the given {@code id}.
+ * @throws NoSuchElementException if the neuron does not exist in the
+ * network.
+ */
+ public Neuron getNeuron(long id) {
+ final Neuron n = neuronMap.get(id);
+ if (n == null) {
+ throw new NoSuchElementException(Long.toString(id));
+ }
+ return n;
+ }
+
+ /**
+ * Retrieves the neurons in the neighbourhood of any neuron in the
+ * {@code neurons} list.
+ * @param neurons Neurons for which to retrieve the neighbours.
+ * @return the list of neighbours.
+ * @see #getNeighbours(Iterable,Iterable)
+ */
+ public Collection<Neuron> getNeighbours(Iterable<Neuron> neurons) {
+ return getNeighbours(neurons, null);
+ }
+
+ /**
+ * Retrieves the neurons in the neighbourhood of any neuron in the
+ * {@code neurons} list.
+ * The {@code exclude} list allows to retrieve the "concentric"
+ * neighbourhoods by removing the neurons that belong to the inner
+ * "circles".
+ *
+ * @param neurons Neurons for which to retrieve the neighbours.
+ * @param exclude Neurons to exclude from the returned list.
+ * Can be {@code null}.
+ * @return the list of neighbours.
+ */
+ public Collection<Neuron> getNeighbours(Iterable<Neuron> neurons,
+ Iterable<Neuron> exclude) {
+ final Set<Long> idList = new HashSet<Long>();
+
+ for (Neuron n : neurons) {
+ idList.addAll(linkMap.get(n.getIdentifier()));
+ }
+ if (exclude != null) {
+ for (Neuron n : exclude) {
+ idList.remove(n.getIdentifier());
+ }
+ }
+
+ final List<Neuron> neuronList = new ArrayList<Neuron>();
+ for (Long id : idList) {
+ neuronList.add(getNeuron(id));
+ }
+
+ return neuronList;
+ }
+
+ /**
+ * Retrieves the neighbours of the given neuron.
+ *
+ * @param neuron Neuron for which to retrieve the neighbours.
+ * @return the list of neighbours.
+ * @see #getNeighbours(Neuron,Iterable)
+ */
+ public Collection<Neuron> getNeighbours(Neuron neuron) {
+ return getNeighbours(neuron, null);
+ }
+
+ /**
+ * Retrieves the neighbours of the given neuron.
+ *
+ * @param neuron Neuron for which to retrieve the neighbours.
+ * @param exclude Neurons to exclude from the returned list.
+ * Can be {@code null}.
+ * @return the list of neighbours.
+ */
+ public Collection<Neuron> getNeighbours(Neuron neuron,
+ Iterable<Neuron> exclude) {
+ final Set<Long> idList = linkMap.get(neuron.getIdentifier());
+ if (exclude != null) {
+ for (Neuron n : exclude) {
+ idList.remove(n.getIdentifier());
+ }
+ }
+
+ final List<Neuron> neuronList = new ArrayList<Neuron>();
+ for (Long id : idList) {
+ neuronList.add(getNeuron(id));
+ }
+
+ return neuronList;
+ }
+
+ /**
+ * Creates a neuron identifier.
+ *
+ * @return a value that will serve as a unique identifier.
+ */
+ private Long createNextId() {
+ return nextId.getAndIncrement();
+ }
+
+ /**
+ * Prevents proxy bypass.
+ *
+ * @param in Input stream.
+ */
+ private void readObject(ObjectInputStream in) {
+ throw new IllegalStateException();
+ }
+
+ /**
+ * Custom serialization.
+ *
+ * @return the proxy instance that will be actually serialized.
+ */
+ private Object writeReplace() {
+ final Neuron[] neuronList = neuronMap.values().toArray(new Neuron[0]);
+ final long[][] neighbourIdList = new long[neuronList.length][];
+
+ for (int i = 0; i < neuronList.length; i++) {
+ final Collection<Neuron> neighbours = getNeighbours(neuronList[i]);
+ final long[] neighboursId = new long[neighbours.size()];
+ int count = 0;
+ for (Neuron n : neighbours) {
+ neighboursId[count] = n.getIdentifier();
+ ++count;
+ }
+ neighbourIdList[i] = neighboursId;
+ }
+
+ return new SerializationProxy(nextId.get(),
+ featureSize,
+ neuronList,
+ neighbourIdList);
+ }
+
+ /**
+ * Serialization.
+ */
+ private static class SerializationProxy implements Serializable {
+ /** Serializable. */
+ private static final long serialVersionUID = 20130207L;
+ /** Next identifier. */
+ private final long nextId;
+ /** Number of features. */
+ private final int featureSize;
+ /** Neurons. */
+ private final Neuron[] neuronList;
+ /** Links. */
+ private final long[][] neighbourIdList;
+
+ /**
+ * @param nextId Next available identifier.
+ * @param featureSize Number of features.
+ * @param neuronList Neurons.
+ * @param neighbourIdList Links associated to each of the neurons in
+ * {@code neuronList}.
+ */
+ SerializationProxy(long nextId,
+ int featureSize,
+ Neuron[] neuronList,
+ long[][] neighbourIdList) {
+ this.nextId = nextId;
+ this.featureSize = featureSize;
+ this.neuronList = neuronList;
+ this.neighbourIdList = neighbourIdList;
+ }
+
+ /**
+ * Custom serialization.
+ *
+ * @return the {@link Network} for which this instance is the proxy.
+ */
+ private Object readResolve() {
+ return new Network(nextId,
+ featureSize,
+ neuronList,
+ neighbourIdList);
+ }
+ }
+}
diff --git a/src/main/java/org/apache/commons/math3/ml/neuralnet/Neuron.java b/src/main/java/org/apache/commons/math3/ml/neuralnet/Neuron.java
new file mode 100644
index 0000000..8cae3ea
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/ml/neuralnet/Neuron.java
@@ -0,0 +1,272 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math3.ml.neuralnet;
+
+import java.io.Serializable;
+import java.io.ObjectInputStream;
+import java.util.concurrent.atomic.AtomicReference;
+import java.util.concurrent.atomic.AtomicLong;
+
+import org.apache.commons.math3.exception.DimensionMismatchException;
+import org.apache.commons.math3.util.Precision;
+
+
+/**
+ * Describes a neuron element of a neural network.
+ *
+ * This class aims to be thread-safe.
+ *
+ * @since 3.3
+ */
+public class Neuron implements Serializable {
+ /** Serializable. */
+ private static final long serialVersionUID = 20130207L;
+ /** Identifier. */
+ private final long identifier;
+ /** Length of the feature set. */
+ private final int size;
+ /** Neuron data. */
+ private final AtomicReference<double[]> features;
+ /** Number of attempts to update a neuron. */
+ private final AtomicLong numberOfAttemptedUpdates = new AtomicLong(0);
+ /** Number of successful updates of a neuron. */
+ private final AtomicLong numberOfSuccessfulUpdates = new AtomicLong(0);
+
+ /**
+ * Creates a neuron.
+ * The size of the feature set is fixed to the length of the given
+ * argument.
+ * <br/>
+ * Constructor is package-private: Neurons must be
+ * {@link Network#createNeuron(double[]) created} by the network
+ * instance to which they will belong.
+ *
+ * @param identifier Identifier (assigned by the {@link Network}).
+ * @param features Initial values of the feature set.
+ */
+ Neuron(long identifier,
+ double[] features) {
+ this.identifier = identifier;
+ this.size = features.length;
+ this.features = new AtomicReference<double[]>(features.clone());
+ }
+
+ /**
+ * Performs a deep copy of this instance.
+ * Upon return, the copied and original instances will be independent:
+ * Updating one will not affect the other.
+ *
+ * @return a new instance with the same state as this instance.
+ * @since 3.6
+ */
+ public synchronized Neuron copy() {
+ final Neuron copy = new Neuron(getIdentifier(),
+ getFeatures());
+ copy.numberOfAttemptedUpdates.set(numberOfAttemptedUpdates.get());
+ copy.numberOfSuccessfulUpdates.set(numberOfSuccessfulUpdates.get());
+
+ return copy;
+ }
+
+ /**
+ * Gets the neuron's identifier.
+ *
+ * @return the identifier.
+ */
+ public long getIdentifier() {
+ return identifier;
+ }
+
+ /**
+ * Gets the length of the feature set.
+ *
+ * @return the number of features.
+ */
+ public int getSize() {
+ return size;
+ }
+
+ /**
+ * Gets the neuron's features.
+ *
+ * @return a copy of the neuron's features.
+ */
+ public double[] getFeatures() {
+ return features.get().clone();
+ }
+
+ /**
+ * Tries to atomically update the neuron's features.
+ * Update will be performed only if the expected values match the
+ * current values.<br/>
+ * In effect, when concurrent threads call this method, the state
+ * could be modified by one, so that it does not correspond to the
+ * the state assumed by another.
+ * Typically, a caller {@link #getFeatures() retrieves the current state},
+ * and uses it to compute the new state.
+ * During this computation, another thread might have done the same
+ * thing, and updated the state: If the current thread were to proceed
+ * with its own update, it would overwrite the new state (which might
+ * already have been used by yet other threads).
+ * To prevent this, the method does not perform the update when a
+ * concurrent modification has been detected, and returns {@code false}.
+ * When this happens, the caller should fetch the new current state,
+ * redo its computation, and call this method again.
+ *
+ * @param expect Current values of the features, as assumed by the caller.
+ * Update will never succeed if the contents of this array does not match
+ * the values returned by {@link #getFeatures()}.
+ * @param update Features's new values.
+ * @return {@code true} if the update was successful, {@code false}
+ * otherwise.
+ * @throws DimensionMismatchException if the length of {@code update} is
+ * not the same as specified in the {@link #Neuron(long,double[])
+ * constructor}.
+ */
+ public boolean compareAndSetFeatures(double[] expect,
+ double[] update) {
+ if (update.length != size) {
+ throw new DimensionMismatchException(update.length, size);
+ }
+
+ // Get the internal reference. Note that this must not be a copy;
+ // otherwise the "compareAndSet" below will always fail.
+ final double[] current = features.get();
+ if (!containSameValues(current, expect)) {
+ // Some other thread already modified the state.
+ return false;
+ }
+
+ // Increment attempt counter.
+ numberOfAttemptedUpdates.incrementAndGet();
+
+ if (features.compareAndSet(current, update.clone())) {
+ // The current thread could atomically update the state (attempt succeeded).
+ numberOfSuccessfulUpdates.incrementAndGet();
+ return true;
+ } else {
+ // Some other thread came first (attempt failed).
+ return false;
+ }
+ }
+
+ /**
+ * Retrieves the number of calls to the
+ * {@link #compareAndSetFeatures(double[],double[]) compareAndSetFeatures}
+ * method.
+ * Note that if the caller wants to use this method in combination with
+ * {@link #getNumberOfSuccessfulUpdates()}, additional synchronization
+ * may be required to ensure consistency.
+ *
+ * @return the number of update attempts.
+ * @since 3.6
+ */
+ public long getNumberOfAttemptedUpdates() {
+ return numberOfAttemptedUpdates.get();
+ }
+
+ /**
+ * Retrieves the number of successful calls to the
+ * {@link #compareAndSetFeatures(double[],double[]) compareAndSetFeatures}
+ * method.
+ * Note that if the caller wants to use this method in combination with
+ * {@link #getNumberOfAttemptedUpdates()}, additional synchronization
+ * may be required to ensure consistency.
+ *
+ * @return the number of successful updates.
+ * @since 3.6
+ */
+ public long getNumberOfSuccessfulUpdates() {
+ return numberOfSuccessfulUpdates.get();
+ }
+
+ /**
+ * Checks whether the contents of both arrays is the same.
+ *
+ * @param current Current values.
+ * @param expect Expected values.
+ * @throws DimensionMismatchException if the length of {@code expected}
+ * is not the same as specified in the {@link #Neuron(long,double[])
+ * constructor}.
+ * @return {@code true} if the arrays contain the same values.
+ */
+ private boolean containSameValues(double[] current,
+ double[] expect) {
+ if (expect.length != size) {
+ throw new DimensionMismatchException(expect.length, size);
+ }
+
+ for (int i = 0; i < size; i++) {
+ if (!Precision.equals(current[i], expect[i])) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ /**
+ * Prevents proxy bypass.
+ *
+ * @param in Input stream.
+ */
+ private void readObject(ObjectInputStream in) {
+ throw new IllegalStateException();
+ }
+
+ /**
+ * Custom serialization.
+ *
+ * @return the proxy instance that will be actually serialized.
+ */
+ private Object writeReplace() {
+ return new SerializationProxy(identifier,
+ features.get());
+ }
+
+ /**
+ * Serialization.
+ */
+ private static class SerializationProxy implements Serializable {
+ /** Serializable. */
+ private static final long serialVersionUID = 20130207L;
+ /** Features. */
+ private final double[] features;
+ /** Identifier. */
+ private final long identifier;
+
+ /**
+ * @param identifier Identifier.
+ * @param features Features.
+ */
+ SerializationProxy(long identifier,
+ double[] features) {
+ this.identifier = identifier;
+ this.features = features;
+ }
+
+ /**
+ * Custom serialization.
+ *
+ * @return the {@link Neuron} for which this instance is the proxy.
+ */
+ private Object readResolve() {
+ return new Neuron(identifier,
+ features);
+ }
+ }
+}
diff --git a/src/main/java/org/apache/commons/math3/ml/neuralnet/SquareNeighbourhood.java b/src/main/java/org/apache/commons/math3/ml/neuralnet/SquareNeighbourhood.java
new file mode 100644
index 0000000..a3c0d95
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/ml/neuralnet/SquareNeighbourhood.java
@@ -0,0 +1,38 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math3.ml.neuralnet;
+
+/**
+ * Defines neighbourhood types.
+ *
+ * @since 3.3
+ */
+public enum SquareNeighbourhood {
+ /**
+ * <a href="http://en.wikipedia.org/wiki/Von_Neumann_neighborhood"
+ * Von Neumann neighbourhood</a>: in two dimensions, each (internal)
+ * neuron has four neighbours.
+ */
+ VON_NEUMANN,
+ /**
+ * <a href="http://en.wikipedia.org/wiki/Moore_neighborhood"
+ * Moore neighbourhood</a>: in two dimensions, each (internal)
+ * neuron has eight neighbours.
+ */
+ MOORE,
+}
diff --git a/src/main/java/org/apache/commons/math3/ml/neuralnet/UpdateAction.java b/src/main/java/org/apache/commons/math3/ml/neuralnet/UpdateAction.java
new file mode 100644
index 0000000..041d3d6
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/ml/neuralnet/UpdateAction.java
@@ -0,0 +1,34 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math3.ml.neuralnet;
+
+/**
+ * Describes how to update the network in response to a training
+ * sample.
+ *
+ * @since 3.3
+ */
+public interface UpdateAction {
+ /**
+ * Updates the network in response to the sample {@code features}.
+ *
+ * @param net Network.
+ * @param features Training data.
+ */
+ void update(Network net, double[] features);
+}
diff --git a/src/main/java/org/apache/commons/math3/ml/neuralnet/oned/NeuronString.java b/src/main/java/org/apache/commons/math3/ml/neuralnet/oned/NeuronString.java
new file mode 100644
index 0000000..fad6042
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/ml/neuralnet/oned/NeuronString.java
@@ -0,0 +1,238 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math3.ml.neuralnet.oned;
+
+import java.io.Serializable;
+import java.io.ObjectInputStream;
+import org.apache.commons.math3.ml.neuralnet.Network;
+import org.apache.commons.math3.ml.neuralnet.FeatureInitializer;
+import org.apache.commons.math3.exception.NumberIsTooSmallException;
+import org.apache.commons.math3.exception.OutOfRangeException;
+
+/**
+ * Neural network with the topology of a one-dimensional line.
+ * Each neuron defines one point on the line.
+ *
+ * @since 3.3
+ */
+public class NeuronString implements Serializable {
+ /** Serial version ID */
+ private static final long serialVersionUID = 1L;
+ /** Underlying network. */
+ private final Network network;
+ /** Number of neurons. */
+ private final int size;
+ /** Wrap. */
+ private final boolean wrap;
+
+ /**
+ * Mapping of the 1D coordinate to the neuron identifiers
+ * (attributed by the {@link #network} instance).
+ */
+ private final long[] identifiers;
+
+ /**
+ * Constructor with restricted access, solely used for deserialization.
+ *
+ * @param wrap Whether to wrap the dimension (i.e the first and last
+ * neurons will be linked together).
+ * @param featuresList Arrays that will initialize the features sets of
+ * the network's neurons.
+ * @throws NumberIsTooSmallException if {@code num < 2}.
+ */
+ NeuronString(boolean wrap,
+ double[][] featuresList) {
+ size = featuresList.length;
+
+ if (size < 2) {
+ throw new NumberIsTooSmallException(size, 2, true);
+ }
+
+ this.wrap = wrap;
+
+ final int fLen = featuresList[0].length;
+ network = new Network(0, fLen);
+ identifiers = new long[size];
+
+ // Add neurons.
+ for (int i = 0; i < size; i++) {
+ identifiers[i] = network.createNeuron(featuresList[i]);
+ }
+
+ // Add links.
+ createLinks();
+ }
+
+ /**
+ * Creates a one-dimensional network:
+ * Each neuron not located on the border of the mesh has two
+ * neurons linked to it.
+ * <br/>
+ * The links are bi-directional.
+ * Neurons created successively are neighbours (i.e. there are
+ * links between them).
+ * <br/>
+ * The topology of the network can also be a circle (if the
+ * dimension is wrapped).
+ *
+ * @param num Number of neurons.
+ * @param wrap Whether to wrap the dimension (i.e the first and last
+ * neurons will be linked together).
+ * @param featureInit Arrays that will initialize the features sets of
+ * the network's neurons.
+ * @throws NumberIsTooSmallException if {@code num < 2}.
+ */
+ public NeuronString(int num,
+ boolean wrap,
+ FeatureInitializer[] featureInit) {
+ if (num < 2) {
+ throw new NumberIsTooSmallException(num, 2, true);
+ }
+
+ size = num;
+ this.wrap = wrap;
+ identifiers = new long[num];
+
+ final int fLen = featureInit.length;
+ network = new Network(0, fLen);
+
+ // Add neurons.
+ for (int i = 0; i < num; i++) {
+ final double[] features = new double[fLen];
+ for (int fIndex = 0; fIndex < fLen; fIndex++) {
+ features[fIndex] = featureInit[fIndex].value();
+ }
+ identifiers[i] = network.createNeuron(features);
+ }
+
+ // Add links.
+ createLinks();
+ }
+
+ /**
+ * Retrieves the underlying network.
+ * A reference is returned (enabling, for example, the network to be
+ * trained).
+ * This also implies that calling methods that modify the {@link Network}
+ * topology may cause this class to become inconsistent.
+ *
+ * @return the network.
+ */
+ public Network getNetwork() {
+ return network;
+ }
+
+ /**
+ * Gets the number of neurons.
+ *
+ * @return the number of neurons.
+ */
+ public int getSize() {
+ return size;
+ }
+
+ /**
+ * Retrieves the features set from the neuron at location
+ * {@code i} in the map.
+ *
+ * @param i Neuron index.
+ * @return the features of the neuron at index {@code i}.
+ * @throws OutOfRangeException if {@code i} is out of range.
+ */
+ public double[] getFeatures(int i) {
+ if (i < 0 ||
+ i >= size) {
+ throw new OutOfRangeException(i, 0, size - 1);
+ }
+
+ return network.getNeuron(identifiers[i]).getFeatures();
+ }
+
+ /**
+ * Creates the neighbour relationships between neurons.
+ */
+ private void createLinks() {
+ for (int i = 0; i < size - 1; i++) {
+ network.addLink(network.getNeuron(i), network.getNeuron(i + 1));
+ }
+ for (int i = size - 1; i > 0; i--) {
+ network.addLink(network.getNeuron(i), network.getNeuron(i - 1));
+ }
+ if (wrap) {
+ network.addLink(network.getNeuron(0), network.getNeuron(size - 1));
+ network.addLink(network.getNeuron(size - 1), network.getNeuron(0));
+ }
+ }
+
+ /**
+ * Prevents proxy bypass.
+ *
+ * @param in Input stream.
+ */
+ private void readObject(ObjectInputStream in) {
+ throw new IllegalStateException();
+ }
+
+ /**
+ * Custom serialization.
+ *
+ * @return the proxy instance that will be actually serialized.
+ */
+ private Object writeReplace() {
+ final double[][] featuresList = new double[size][];
+ for (int i = 0; i < size; i++) {
+ featuresList[i] = getFeatures(i);
+ }
+
+ return new SerializationProxy(wrap,
+ featuresList);
+ }
+
+ /**
+ * Serialization.
+ */
+ private static class SerializationProxy implements Serializable {
+ /** Serializable. */
+ private static final long serialVersionUID = 20130226L;
+ /** Wrap. */
+ private final boolean wrap;
+ /** Neurons' features. */
+ private final double[][] featuresList;
+
+ /**
+ * @param wrap Whether the dimension is wrapped.
+ * @param featuresList List of neurons features.
+ * {@code neuronList}.
+ */
+ SerializationProxy(boolean wrap,
+ double[][] featuresList) {
+ this.wrap = wrap;
+ this.featuresList = featuresList;
+ }
+
+ /**
+ * Custom serialization.
+ *
+ * @return the {@link Neuron} for which this instance is the proxy.
+ */
+ private Object readResolve() {
+ return new NeuronString(wrap,
+ featuresList);
+ }
+ }
+}
diff --git a/src/main/java/org/apache/commons/math3/ml/neuralnet/oned/package-info.java b/src/main/java/org/apache/commons/math3/ml/neuralnet/oned/package-info.java
new file mode 100644
index 0000000..0b47fae
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/ml/neuralnet/oned/package-info.java
@@ -0,0 +1,22 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * One-dimensional neural networks.
+ */
+
+package org.apache.commons.math3.ml.neuralnet.oned;
diff --git a/src/main/java/org/apache/commons/math3/ml/neuralnet/package-info.java b/src/main/java/org/apache/commons/math3/ml/neuralnet/package-info.java
new file mode 100644
index 0000000..d8e907e
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/ml/neuralnet/package-info.java
@@ -0,0 +1,22 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * Neural networks.
+ */
+
+package org.apache.commons.math3.ml.neuralnet;
diff --git a/src/main/java/org/apache/commons/math3/ml/neuralnet/sofm/KohonenTrainingTask.java b/src/main/java/org/apache/commons/math3/ml/neuralnet/sofm/KohonenTrainingTask.java
new file mode 100644
index 0000000..9aa497d
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/ml/neuralnet/sofm/KohonenTrainingTask.java
@@ -0,0 +1,59 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math3.ml.neuralnet.sofm;
+
+import java.util.Iterator;
+import org.apache.commons.math3.ml.neuralnet.Network;
+
+/**
+ * Trainer for Kohonen's Self-Organizing Map.
+ *
+ * @since 3.3
+ */
+public class KohonenTrainingTask implements Runnable {
+ /** SOFM to be trained. */
+ private final Network net;
+ /** Training data. */
+ private final Iterator<double[]> featuresIterator;
+ /** Update procedure. */
+ private final KohonenUpdateAction updateAction;
+
+ /**
+ * Creates a (sequential) trainer for the given network.
+ *
+ * @param net Network to be trained with the SOFM algorithm.
+ * @param featuresIterator Training data iterator.
+ * @param updateAction SOFM update procedure.
+ */
+ public KohonenTrainingTask(Network net,
+ Iterator<double[]> featuresIterator,
+ KohonenUpdateAction updateAction) {
+ this.net = net;
+ this.featuresIterator = featuresIterator;
+ this.updateAction = updateAction;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public void run() {
+ while (featuresIterator.hasNext()) {
+ updateAction.update(net, featuresIterator.next());
+ }
+ }
+}
diff --git a/src/main/java/org/apache/commons/math3/ml/neuralnet/sofm/KohonenUpdateAction.java b/src/main/java/org/apache/commons/math3/ml/neuralnet/sofm/KohonenUpdateAction.java
new file mode 100644
index 0000000..0618aeb
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/ml/neuralnet/sofm/KohonenUpdateAction.java
@@ -0,0 +1,225 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math3.ml.neuralnet.sofm;
+
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.concurrent.atomic.AtomicLong;
+
+import org.apache.commons.math3.analysis.function.Gaussian;
+import org.apache.commons.math3.linear.ArrayRealVector;
+import org.apache.commons.math3.ml.distance.DistanceMeasure;
+import org.apache.commons.math3.ml.neuralnet.MapUtils;
+import org.apache.commons.math3.ml.neuralnet.Network;
+import org.apache.commons.math3.ml.neuralnet.Neuron;
+import org.apache.commons.math3.ml.neuralnet.UpdateAction;
+
+/**
+ * Update formula for <a href="http://en.wikipedia.org/wiki/Kohonen">
+ * Kohonen's Self-Organizing Map</a>.
+ * <br/>
+ * The {@link #update(Network,double[]) update} method modifies the
+ * features {@code w} of the "winning" neuron and its neighbours
+ * according to the following rule:
+ * <code>
+ * w<sub>new</sub> = w<sub>old</sub> + &alpha; e<sup>(-d / &sigma;)</sup> * (sample - w<sub>old</sub>)
+ * </code>
+ * where
+ * <ul>
+ * <li>&alpha; is the current <em>learning rate</em>, </li>
+ * <li>&sigma; is the current <em>neighbourhood size</em>, and</li>
+ * <li>{@code d} is the number of links to traverse in order to reach
+ * the neuron from the winning neuron.</li>
+ * </ul>
+ * <br/>
+ * This class is thread-safe as long as the arguments passed to the
+ * {@link #KohonenUpdateAction(DistanceMeasure,LearningFactorFunction,
+ * NeighbourhoodSizeFunction) constructor} are instances of thread-safe
+ * classes.
+ * <br/>
+ * Each call to the {@link #update(Network,double[]) update} method
+ * will increment the internal counter used to compute the current
+ * values for
+ * <ul>
+ * <li>the <em>learning rate</em>, and</li>
+ * <li>the <em>neighbourhood size</em>.</li>
+ * </ul>
+ * Consequently, the function instances that compute those values (passed
+ * to the constructor of this class) must take into account whether this
+ * class's instance will be shared by multiple threads, as this will impact
+ * the training process.
+ *
+ * @since 3.3
+ */
+public class KohonenUpdateAction implements UpdateAction {
+ /** Distance function. */
+ private final DistanceMeasure distance;
+ /** Learning factor update function. */
+ private final LearningFactorFunction learningFactor;
+ /** Neighbourhood size update function. */
+ private final NeighbourhoodSizeFunction neighbourhoodSize;
+ /** Number of calls to {@link #update(Network,double[])}. */
+ private final AtomicLong numberOfCalls = new AtomicLong(0);
+
+ /**
+ * @param distance Distance function.
+ * @param learningFactor Learning factor update function.
+ * @param neighbourhoodSize Neighbourhood size update function.
+ */
+ public KohonenUpdateAction(DistanceMeasure distance,
+ LearningFactorFunction learningFactor,
+ NeighbourhoodSizeFunction neighbourhoodSize) {
+ this.distance = distance;
+ this.learningFactor = learningFactor;
+ this.neighbourhoodSize = neighbourhoodSize;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public void update(Network net,
+ double[] features) {
+ final long numCalls = numberOfCalls.incrementAndGet() - 1;
+ final double currentLearning = learningFactor.value(numCalls);
+ final Neuron best = findAndUpdateBestNeuron(net,
+ features,
+ currentLearning);
+
+ final int currentNeighbourhood = neighbourhoodSize.value(numCalls);
+ // The farther away the neighbour is from the winning neuron, the
+ // smaller the learning rate will become.
+ final Gaussian neighbourhoodDecay
+ = new Gaussian(currentLearning,
+ 0,
+ currentNeighbourhood);
+
+ if (currentNeighbourhood > 0) {
+ // Initial set of neurons only contains the winning neuron.
+ Collection<Neuron> neighbours = new HashSet<Neuron>();
+ neighbours.add(best);
+ // Winning neuron must be excluded from the neighbours.
+ final HashSet<Neuron> exclude = new HashSet<Neuron>();
+ exclude.add(best);
+
+ int radius = 1;
+ do {
+ // Retrieve immediate neighbours of the current set of neurons.
+ neighbours = net.getNeighbours(neighbours, exclude);
+
+ // Update all the neighbours.
+ for (Neuron n : neighbours) {
+ updateNeighbouringNeuron(n, features, neighbourhoodDecay.value(radius));
+ }
+
+ // Add the neighbours to the exclude list so that they will
+ // not be update more than once per training step.
+ exclude.addAll(neighbours);
+ ++radius;
+ } while (radius <= currentNeighbourhood);
+ }
+ }
+
+ /**
+ * Retrieves the number of calls to the {@link #update(Network,double[]) update}
+ * method.
+ *
+ * @return the current number of calls.
+ */
+ public long getNumberOfCalls() {
+ return numberOfCalls.get();
+ }
+
+ /**
+ * Tries to update a neuron.
+ *
+ * @param n Neuron to be updated.
+ * @param features Training data.
+ * @param learningRate Learning factor.
+ * @return {@code true} if the update succeeded, {@code true} if a
+ * concurrent update has been detected.
+ */
+ private boolean attemptNeuronUpdate(Neuron n,
+ double[] features,
+ double learningRate) {
+ final double[] expect = n.getFeatures();
+ final double[] update = computeFeatures(expect,
+ features,
+ learningRate);
+
+ return n.compareAndSetFeatures(expect, update);
+ }
+
+ /**
+ * Atomically updates the given neuron.
+ *
+ * @param n Neuron to be updated.
+ * @param features Training data.
+ * @param learningRate Learning factor.
+ */
+ private void updateNeighbouringNeuron(Neuron n,
+ double[] features,
+ double learningRate) {
+ while (true) {
+ if (attemptNeuronUpdate(n, features, learningRate)) {
+ break;
+ }
+ }
+ }
+
+ /**
+ * Searches for the neuron whose features are closest to the given
+ * sample, and atomically updates its features.
+ *
+ * @param net Network.
+ * @param features Sample data.
+ * @param learningRate Current learning factor.
+ * @return the winning neuron.
+ */
+ private Neuron findAndUpdateBestNeuron(Network net,
+ double[] features,
+ double learningRate) {
+ while (true) {
+ final Neuron best = MapUtils.findBest(features, net, distance);
+
+ if (attemptNeuronUpdate(best, features, learningRate)) {
+ return best;
+ }
+
+ // If another thread modified the state of the winning neuron,
+ // it may not be the best match anymore for the given training
+ // sample: Hence, the winner search is performed again.
+ }
+ }
+
+ /**
+ * Computes the new value of the features set.
+ *
+ * @param current Current values of the features.
+ * @param sample Training data.
+ * @param learningRate Learning factor.
+ * @return the new values for the features.
+ */
+ private double[] computeFeatures(double[] current,
+ double[] sample,
+ double learningRate) {
+ final ArrayRealVector c = new ArrayRealVector(current, false);
+ final ArrayRealVector s = new ArrayRealVector(sample, false);
+ // c + learningRate * (s - c)
+ return s.subtract(c).mapMultiplyToSelf(learningRate).add(c).toArray();
+ }
+}
diff --git a/src/main/java/org/apache/commons/math3/ml/neuralnet/sofm/LearningFactorFunction.java b/src/main/java/org/apache/commons/math3/ml/neuralnet/sofm/LearningFactorFunction.java
new file mode 100644
index 0000000..ba9d152
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/ml/neuralnet/sofm/LearningFactorFunction.java
@@ -0,0 +1,34 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math3.ml.neuralnet.sofm;
+
+/**
+ * Provides the learning rate as a function of the number of calls
+ * already performed during the learning task.
+ *
+ * @since 3.3
+ */
+public interface LearningFactorFunction {
+ /**
+ * Computes the learning rate at the current call.
+ *
+ * @param numCall Current step of the training task.
+ * @return the value of the function at {@code numCall}.
+ */
+ double value(long numCall);
+}
diff --git a/src/main/java/org/apache/commons/math3/ml/neuralnet/sofm/LearningFactorFunctionFactory.java b/src/main/java/org/apache/commons/math3/ml/neuralnet/sofm/LearningFactorFunctionFactory.java
new file mode 100644
index 0000000..9165e82
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/ml/neuralnet/sofm/LearningFactorFunctionFactory.java
@@ -0,0 +1,117 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math3.ml.neuralnet.sofm;
+
+import org.apache.commons.math3.ml.neuralnet.sofm.util.ExponentialDecayFunction;
+import org.apache.commons.math3.ml.neuralnet.sofm.util.QuasiSigmoidDecayFunction;
+import org.apache.commons.math3.exception.OutOfRangeException;
+
+/**
+ * Factory for creating instances of {@link LearningFactorFunction}.
+ *
+ * @since 3.3
+ */
+public class LearningFactorFunctionFactory {
+ /** Class contains only static methods. */
+ private LearningFactorFunctionFactory() {}
+
+ /**
+ * Creates an exponential decay {@link LearningFactorFunction function}.
+ * It will compute <code>a e<sup>-x / b</sup></code>,
+ * where {@code x} is the (integer) independent variable and
+ * <ul>
+ * <li><code>a = initValue</code>
+ * <li><code>b = -numCall / ln(valueAtNumCall / initValue)</code>
+ * </ul>
+ *
+ * @param initValue Initial value, i.e.
+ * {@link LearningFactorFunction#value(long) value(0)}.
+ * @param valueAtNumCall Value of the function at {@code numCall}.
+ * @param numCall Argument for which the function returns
+ * {@code valueAtNumCall}.
+ * @return the learning factor function.
+ * @throws org.apache.commons.math3.exception.OutOfRangeException
+ * if {@code initValue <= 0} or {@code initValue > 1}.
+ * @throws org.apache.commons.math3.exception.NotStrictlyPositiveException
+ * if {@code valueAtNumCall <= 0}.
+ * @throws org.apache.commons.math3.exception.NumberIsTooLargeException
+ * if {@code valueAtNumCall >= initValue}.
+ * @throws org.apache.commons.math3.exception.NotStrictlyPositiveException
+ * if {@code numCall <= 0}.
+ */
+ public static LearningFactorFunction exponentialDecay(final double initValue,
+ final double valueAtNumCall,
+ final long numCall) {
+ if (initValue <= 0 ||
+ initValue > 1) {
+ throw new OutOfRangeException(initValue, 0, 1);
+ }
+
+ return new LearningFactorFunction() {
+ /** DecayFunction. */
+ private final ExponentialDecayFunction decay
+ = new ExponentialDecayFunction(initValue, valueAtNumCall, numCall);
+
+ /** {@inheritDoc} */
+ public double value(long n) {
+ return decay.value(n);
+ }
+ };
+ }
+
+ /**
+ * Creates an sigmoid-like {@code LearningFactorFunction function}.
+ * The function {@code f} will have the following properties:
+ * <ul>
+ * <li>{@code f(0) = initValue}</li>
+ * <li>{@code numCall} is the inflexion point</li>
+ * <li>{@code slope = f'(numCall)}</li>
+ * </ul>
+ *
+ * @param initValue Initial value, i.e.
+ * {@link LearningFactorFunction#value(long) value(0)}.
+ * @param slope Value of the function derivative at {@code numCall}.
+ * @param numCall Inflexion point.
+ * @return the learning factor function.
+ * @throws org.apache.commons.math3.exception.OutOfRangeException
+ * if {@code initValue <= 0} or {@code initValue > 1}.
+ * @throws org.apache.commons.math3.exception.NumberIsTooLargeException
+ * if {@code slope >= 0}.
+ * @throws org.apache.commons.math3.exception.NotStrictlyPositiveException
+ * if {@code numCall <= 0}.
+ */
+ public static LearningFactorFunction quasiSigmoidDecay(final double initValue,
+ final double slope,
+ final long numCall) {
+ if (initValue <= 0 ||
+ initValue > 1) {
+ throw new OutOfRangeException(initValue, 0, 1);
+ }
+
+ return new LearningFactorFunction() {
+ /** DecayFunction. */
+ private final QuasiSigmoidDecayFunction decay
+ = new QuasiSigmoidDecayFunction(initValue, slope, numCall);
+
+ /** {@inheritDoc} */
+ public double value(long n) {
+ return decay.value(n);
+ }
+ };
+ }
+}
diff --git a/src/main/java/org/apache/commons/math3/ml/neuralnet/sofm/NeighbourhoodSizeFunction.java b/src/main/java/org/apache/commons/math3/ml/neuralnet/sofm/NeighbourhoodSizeFunction.java
new file mode 100644
index 0000000..68149f7
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/ml/neuralnet/sofm/NeighbourhoodSizeFunction.java
@@ -0,0 +1,37 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math3.ml.neuralnet.sofm;
+
+/**
+ * Provides the network neighbourhood's size as a function of the
+ * number of calls already performed during the learning task.
+ * The "neighbourhood" is the set of neurons that can be reached
+ * by traversing at most the number of links returned by this
+ * function.
+ *
+ * @since 3.3
+ */
+public interface NeighbourhoodSizeFunction {
+ /**
+ * Computes the neighbourhood size at the current call.
+ *
+ * @param numCall Current step of the training task.
+ * @return the value of the function at {@code numCall}.
+ */
+ int value(long numCall);
+}
diff --git a/src/main/java/org/apache/commons/math3/ml/neuralnet/sofm/NeighbourhoodSizeFunctionFactory.java b/src/main/java/org/apache/commons/math3/ml/neuralnet/sofm/NeighbourhoodSizeFunctionFactory.java
new file mode 100644
index 0000000..bdbfa2f
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/ml/neuralnet/sofm/NeighbourhoodSizeFunctionFactory.java
@@ -0,0 +1,107 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math3.ml.neuralnet.sofm;
+
+import org.apache.commons.math3.ml.neuralnet.sofm.util.ExponentialDecayFunction;
+import org.apache.commons.math3.ml.neuralnet.sofm.util.QuasiSigmoidDecayFunction;
+import org.apache.commons.math3.util.FastMath;
+
+/**
+ * Factory for creating instances of {@link NeighbourhoodSizeFunction}.
+ *
+ * @since 3.3
+ */
+public class NeighbourhoodSizeFunctionFactory {
+ /** Class contains only static methods. */
+ private NeighbourhoodSizeFunctionFactory() {}
+
+ /**
+ * Creates an exponential decay {@link NeighbourhoodSizeFunction function}.
+ * It will compute <code>a e<sup>-x / b</sup></code>,
+ * where {@code x} is the (integer) independent variable and
+ * <ul>
+ * <li><code>a = initValue</code>
+ * <li><code>b = -numCall / ln(valueAtNumCall / initValue)</code>
+ * </ul>
+ *
+ * @param initValue Initial value, i.e.
+ * {@link NeighbourhoodSizeFunction#value(long) value(0)}.
+ * @param valueAtNumCall Value of the function at {@code numCall}.
+ * @param numCall Argument for which the function returns
+ * {@code valueAtNumCall}.
+ * @return the neighbourhood size function.
+ * @throws org.apache.commons.math3.exception.NotStrictlyPositiveException
+ * if {@code initValue <= 0}.
+ * @throws org.apache.commons.math3.exception.NotStrictlyPositiveException
+ * if {@code valueAtNumCall <= 0}.
+ * @throws org.apache.commons.math3.exception.NumberIsTooLargeException
+ * if {@code valueAtNumCall >= initValue}.
+ * @throws org.apache.commons.math3.exception.NotStrictlyPositiveException
+ * if {@code numCall <= 0}.
+ */
+ public static NeighbourhoodSizeFunction exponentialDecay(final double initValue,
+ final double valueAtNumCall,
+ final long numCall) {
+ return new NeighbourhoodSizeFunction() {
+ /** DecayFunction. */
+ private final ExponentialDecayFunction decay
+ = new ExponentialDecayFunction(initValue, valueAtNumCall, numCall);
+
+ /** {@inheritDoc} */
+ public int value(long n) {
+ return (int) FastMath.rint(decay.value(n));
+ }
+ };
+ }
+
+ /**
+ * Creates an sigmoid-like {@code NeighbourhoodSizeFunction function}.
+ * The function {@code f} will have the following properties:
+ * <ul>
+ * <li>{@code f(0) = initValue}</li>
+ * <li>{@code numCall} is the inflexion point</li>
+ * <li>{@code slope = f'(numCall)}</li>
+ * </ul>
+ *
+ * @param initValue Initial value, i.e.
+ * {@link NeighbourhoodSizeFunction#value(long) value(0)}.
+ * @param slope Value of the function derivative at {@code numCall}.
+ * @param numCall Inflexion point.
+ * @return the neighbourhood size function.
+ * @throws org.apache.commons.math3.exception.NotStrictlyPositiveException
+ * if {@code initValue <= 0}.
+ * @throws org.apache.commons.math3.exception.NumberIsTooLargeException
+ * if {@code slope >= 0}.
+ * @throws org.apache.commons.math3.exception.NotStrictlyPositiveException
+ * if {@code numCall <= 0}.
+ */
+ public static NeighbourhoodSizeFunction quasiSigmoidDecay(final double initValue,
+ final double slope,
+ final long numCall) {
+ return new NeighbourhoodSizeFunction() {
+ /** DecayFunction. */
+ private final QuasiSigmoidDecayFunction decay
+ = new QuasiSigmoidDecayFunction(initValue, slope, numCall);
+
+ /** {@inheritDoc} */
+ public int value(long n) {
+ return (int) FastMath.rint(decay.value(n));
+ }
+ };
+ }
+}
diff --git a/src/main/java/org/apache/commons/math3/ml/neuralnet/sofm/package-info.java b/src/main/java/org/apache/commons/math3/ml/neuralnet/sofm/package-info.java
new file mode 100644
index 0000000..60c3c61
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/ml/neuralnet/sofm/package-info.java
@@ -0,0 +1,22 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * Self Organizing Feature Map.
+ */
+
+package org.apache.commons.math3.ml.neuralnet.sofm;
diff --git a/src/main/java/org/apache/commons/math3/ml/neuralnet/sofm/util/ExponentialDecayFunction.java b/src/main/java/org/apache/commons/math3/ml/neuralnet/sofm/util/ExponentialDecayFunction.java
new file mode 100644
index 0000000..19e7380
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/ml/neuralnet/sofm/util/ExponentialDecayFunction.java
@@ -0,0 +1,83 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math3.ml.neuralnet.sofm.util;
+
+import org.apache.commons.math3.exception.NotStrictlyPositiveException;
+import org.apache.commons.math3.exception.NumberIsTooLargeException;
+import org.apache.commons.math3.util.FastMath;
+
+/**
+ * Exponential decay function: <code>a e<sup>-x / b</sup></code>,
+ * where {@code x} is the (integer) independent variable.
+ * <br/>
+ * Class is immutable.
+ *
+ * @since 3.3
+ */
+public class ExponentialDecayFunction {
+ /** Factor {@code a}. */
+ private final double a;
+ /** Factor {@code 1 / b}. */
+ private final double oneOverB;
+
+ /**
+ * Creates an instance. It will be such that
+ * <ul>
+ * <li>{@code a = initValue}</li>
+ * <li>{@code b = -numCall / ln(valueAtNumCall / initValue)}</li>
+ * </ul>
+ *
+ * @param initValue Initial value, i.e. {@link #value(long) value(0)}.
+ * @param valueAtNumCall Value of the function at {@code numCall}.
+ * @param numCall Argument for which the function returns
+ * {@code valueAtNumCall}.
+ * @throws NotStrictlyPositiveException if {@code initValue <= 0}.
+ * @throws NotStrictlyPositiveException if {@code valueAtNumCall <= 0}.
+ * @throws NumberIsTooLargeException if {@code valueAtNumCall >= initValue}.
+ * @throws NotStrictlyPositiveException if {@code numCall <= 0}.
+ */
+ public ExponentialDecayFunction(double initValue,
+ double valueAtNumCall,
+ long numCall) {
+ if (initValue <= 0) {
+ throw new NotStrictlyPositiveException(initValue);
+ }
+ if (valueAtNumCall <= 0) {
+ throw new NotStrictlyPositiveException(valueAtNumCall);
+ }
+ if (valueAtNumCall >= initValue) {
+ throw new NumberIsTooLargeException(valueAtNumCall, initValue, false);
+ }
+ if (numCall <= 0) {
+ throw new NotStrictlyPositiveException(numCall);
+ }
+
+ a = initValue;
+ oneOverB = -FastMath.log(valueAtNumCall / initValue) / numCall;
+ }
+
+ /**
+ * Computes <code>a e<sup>-numCall / b</sup></code>.
+ *
+ * @param numCall Current step of the training task.
+ * @return the value of the function at {@code numCall}.
+ */
+ public double value(long numCall) {
+ return a * FastMath.exp(-numCall * oneOverB);
+ }
+}
diff --git a/src/main/java/org/apache/commons/math3/ml/neuralnet/sofm/util/QuasiSigmoidDecayFunction.java b/src/main/java/org/apache/commons/math3/ml/neuralnet/sofm/util/QuasiSigmoidDecayFunction.java
new file mode 100644
index 0000000..3d35c17
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/ml/neuralnet/sofm/util/QuasiSigmoidDecayFunction.java
@@ -0,0 +1,87 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math3.ml.neuralnet.sofm.util;
+
+import org.apache.commons.math3.exception.NotStrictlyPositiveException;
+import org.apache.commons.math3.exception.NumberIsTooLargeException;
+import org.apache.commons.math3.analysis.function.Logistic;
+
+/**
+ * Decay function whose shape is similar to a sigmoid.
+ * <br/>
+ * Class is immutable.
+ *
+ * @since 3.3
+ */
+public class QuasiSigmoidDecayFunction {
+ /** Sigmoid. */
+ private final Logistic sigmoid;
+ /** See {@link #value(long)}. */
+ private final double scale;
+
+ /**
+ * Creates an instance.
+ * The function {@code f} will have the following properties:
+ * <ul>
+ * <li>{@code f(0) = initValue}</li>
+ * <li>{@code numCall} is the inflexion point</li>
+ * <li>{@code slope = f'(numCall)}</li>
+ * </ul>
+ *
+ * @param initValue Initial value, i.e. {@link #value(long) value(0)}.
+ * @param slope Value of the function derivative at {@code numCall}.
+ * @param numCall Inflexion point.
+ * @throws NotStrictlyPositiveException if {@code initValue <= 0}.
+ * @throws NumberIsTooLargeException if {@code slope >= 0}.
+ * @throws NotStrictlyPositiveException if {@code numCall <= 0}.
+ */
+ public QuasiSigmoidDecayFunction(double initValue,
+ double slope,
+ long numCall) {
+ if (initValue <= 0) {
+ throw new NotStrictlyPositiveException(initValue);
+ }
+ if (slope >= 0) {
+ throw new NumberIsTooLargeException(slope, 0, false);
+ }
+ if (numCall <= 1) {
+ throw new NotStrictlyPositiveException(numCall);
+ }
+
+ final double k = initValue;
+ final double m = numCall;
+ final double b = 4 * slope / initValue;
+ final double q = 1;
+ final double a = 0;
+ final double n = 1;
+ sigmoid = new Logistic(k, m, b, q, a, n);
+
+ final double y0 = sigmoid.value(0);
+ scale = k / y0;
+ }
+
+ /**
+ * Computes the value of the learning factor.
+ *
+ * @param numCall Current step of the training task.
+ * @return the value of the function at {@code numCall}.
+ */
+ public double value(long numCall) {
+ return scale * sigmoid.value(numCall);
+ }
+}
diff --git a/src/main/java/org/apache/commons/math3/ml/neuralnet/sofm/util/package-info.java b/src/main/java/org/apache/commons/math3/ml/neuralnet/sofm/util/package-info.java
new file mode 100644
index 0000000..5078ed2
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/ml/neuralnet/sofm/util/package-info.java
@@ -0,0 +1,22 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * Miscellaneous utilities.
+ */
+
+package org.apache.commons.math3.ml.neuralnet.sofm.util;
diff --git a/src/main/java/org/apache/commons/math3/ml/neuralnet/twod/NeuronSquareMesh2D.java b/src/main/java/org/apache/commons/math3/ml/neuralnet/twod/NeuronSquareMesh2D.java
new file mode 100644
index 0000000..5277bc5
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/ml/neuralnet/twod/NeuronSquareMesh2D.java
@@ -0,0 +1,628 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math3.ml.neuralnet.twod;
+
+import java.util.List;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.io.Serializable;
+import java.io.ObjectInputStream;
+import org.apache.commons.math3.ml.neuralnet.Neuron;
+import org.apache.commons.math3.ml.neuralnet.Network;
+import org.apache.commons.math3.ml.neuralnet.FeatureInitializer;
+import org.apache.commons.math3.ml.neuralnet.SquareNeighbourhood;
+import org.apache.commons.math3.exception.NumberIsTooSmallException;
+import org.apache.commons.math3.exception.OutOfRangeException;
+import org.apache.commons.math3.exception.MathInternalError;
+
+/**
+ * Neural network with the topology of a two-dimensional surface.
+ * Each neuron defines one surface element.
+ * <br/>
+ * This network is primarily intended to represent a
+ * <a href="http://en.wikipedia.org/wiki/Kohonen">
+ * Self Organizing Feature Map</a>.
+ *
+ * @see org.apache.commons.math3.ml.neuralnet.sofm
+ * @since 3.3
+ */
+public class NeuronSquareMesh2D
+ implements Iterable<Neuron>,
+ Serializable {
+ /** Serial version ID */
+ private static final long serialVersionUID = 1L;
+ /** Underlying network. */
+ private final Network network;
+ /** Number of rows. */
+ private final int numberOfRows;
+ /** Number of columns. */
+ private final int numberOfColumns;
+ /** Wrap. */
+ private final boolean wrapRows;
+ /** Wrap. */
+ private final boolean wrapColumns;
+ /** Neighbourhood type. */
+ private final SquareNeighbourhood neighbourhood;
+ /**
+ * Mapping of the 2D coordinates (in the rectangular mesh) to
+ * the neuron identifiers (attributed by the {@link #network}
+ * instance).
+ */
+ private final long[][] identifiers;
+
+ /**
+ * Horizontal (along row) direction.
+ * @since 3.6
+ */
+ public enum HorizontalDirection {
+ /** Column at the right of the current column. */
+ RIGHT,
+ /** Current column. */
+ CENTER,
+ /** Column at the left of the current column. */
+ LEFT,
+ }
+ /**
+ * Vertical (along column) direction.
+ * @since 3.6
+ */
+ public enum VerticalDirection {
+ /** Row above the current row. */
+ UP,
+ /** Current row. */
+ CENTER,
+ /** Row below the current row. */
+ DOWN,
+ }
+
+ /**
+ * Constructor with restricted access, solely used for deserialization.
+ *
+ * @param wrapRowDim Whether to wrap the first dimension (i.e the first
+ * and last neurons will be linked together).
+ * @param wrapColDim Whether to wrap the second dimension (i.e the first
+ * and last neurons will be linked together).
+ * @param neighbourhoodType Neighbourhood type.
+ * @param featuresList Arrays that will initialize the features sets of
+ * the network's neurons.
+ * @throws NumberIsTooSmallException if {@code numRows < 2} or
+ * {@code numCols < 2}.
+ */
+ NeuronSquareMesh2D(boolean wrapRowDim,
+ boolean wrapColDim,
+ SquareNeighbourhood neighbourhoodType,
+ double[][][] featuresList) {
+ numberOfRows = featuresList.length;
+ numberOfColumns = featuresList[0].length;
+
+ if (numberOfRows < 2) {
+ throw new NumberIsTooSmallException(numberOfRows, 2, true);
+ }
+ if (numberOfColumns < 2) {
+ throw new NumberIsTooSmallException(numberOfColumns, 2, true);
+ }
+
+ wrapRows = wrapRowDim;
+ wrapColumns = wrapColDim;
+ neighbourhood = neighbourhoodType;
+
+ final int fLen = featuresList[0][0].length;
+ network = new Network(0, fLen);
+ identifiers = new long[numberOfRows][numberOfColumns];
+
+ // Add neurons.
+ for (int i = 0; i < numberOfRows; i++) {
+ for (int j = 0; j < numberOfColumns; j++) {
+ identifiers[i][j] = network.createNeuron(featuresList[i][j]);
+ }
+ }
+
+ // Add links.
+ createLinks();
+ }
+
+ /**
+ * Creates a two-dimensional network composed of square cells:
+ * Each neuron not located on the border of the mesh has four
+ * neurons linked to it.
+ * <br/>
+ * The links are bi-directional.
+ * <br/>
+ * The topology of the network can also be a cylinder (if one
+ * of the dimensions is wrapped) or a torus (if both dimensions
+ * are wrapped).
+ *
+ * @param numRows Number of neurons in the first dimension.
+ * @param wrapRowDim Whether to wrap the first dimension (i.e the first
+ * and last neurons will be linked together).
+ * @param numCols Number of neurons in the second dimension.
+ * @param wrapColDim Whether to wrap the second dimension (i.e the first
+ * and last neurons will be linked together).
+ * @param neighbourhoodType Neighbourhood type.
+ * @param featureInit Array of functions that will initialize the
+ * corresponding element of the features set of each newly created
+ * neuron. In particular, the size of this array defines the size of
+ * feature set.
+ * @throws NumberIsTooSmallException if {@code numRows < 2} or
+ * {@code numCols < 2}.
+ */
+ public NeuronSquareMesh2D(int numRows,
+ boolean wrapRowDim,
+ int numCols,
+ boolean wrapColDim,
+ SquareNeighbourhood neighbourhoodType,
+ FeatureInitializer[] featureInit) {
+ if (numRows < 2) {
+ throw new NumberIsTooSmallException(numRows, 2, true);
+ }
+ if (numCols < 2) {
+ throw new NumberIsTooSmallException(numCols, 2, true);
+ }
+
+ numberOfRows = numRows;
+ wrapRows = wrapRowDim;
+ numberOfColumns = numCols;
+ wrapColumns = wrapColDim;
+ neighbourhood = neighbourhoodType;
+ identifiers = new long[numberOfRows][numberOfColumns];
+
+ final int fLen = featureInit.length;
+ network = new Network(0, fLen);
+
+ // Add neurons.
+ for (int i = 0; i < numRows; i++) {
+ for (int j = 0; j < numCols; j++) {
+ final double[] features = new double[fLen];
+ for (int fIndex = 0; fIndex < fLen; fIndex++) {
+ features[fIndex] = featureInit[fIndex].value();
+ }
+ identifiers[i][j] = network.createNeuron(features);
+ }
+ }
+
+ // Add links.
+ createLinks();
+ }
+
+ /**
+ * Constructor with restricted access, solely used for making a
+ * {@link #copy() deep copy}.
+ *
+ * @param wrapRowDim Whether to wrap the first dimension (i.e the first
+ * and last neurons will be linked together).
+ * @param wrapColDim Whether to wrap the second dimension (i.e the first
+ * and last neurons will be linked together).
+ * @param neighbourhoodType Neighbourhood type.
+ * @param net Underlying network.
+ * @param idGrid Neuron identifiers.
+ */
+ private NeuronSquareMesh2D(boolean wrapRowDim,
+ boolean wrapColDim,
+ SquareNeighbourhood neighbourhoodType,
+ Network net,
+ long[][] idGrid) {
+ numberOfRows = idGrid.length;
+ numberOfColumns = idGrid[0].length;
+ wrapRows = wrapRowDim;
+ wrapColumns = wrapColDim;
+ neighbourhood = neighbourhoodType;
+ network = net;
+ identifiers = idGrid;
+ }
+
+ /**
+ * Performs a deep copy of this instance.
+ * Upon return, the copied and original instances will be independent:
+ * Updating one will not affect the other.
+ *
+ * @return a new instance with the same state as this instance.
+ * @since 3.6
+ */
+ public synchronized NeuronSquareMesh2D copy() {
+ final long[][] idGrid = new long[numberOfRows][numberOfColumns];
+ for (int r = 0; r < numberOfRows; r++) {
+ for (int c = 0; c < numberOfColumns; c++) {
+ idGrid[r][c] = identifiers[r][c];
+ }
+ }
+
+ return new NeuronSquareMesh2D(wrapRows,
+ wrapColumns,
+ neighbourhood,
+ network.copy(),
+ idGrid);
+ }
+
+ /**
+ * {@inheritDoc}
+ * @since 3.6
+ */
+ public Iterator<Neuron> iterator() {
+ return network.iterator();
+ }
+
+ /**
+ * Retrieves the underlying network.
+ * A reference is returned (enabling, for example, the network to be
+ * trained).
+ * This also implies that calling methods that modify the {@link Network}
+ * topology may cause this class to become inconsistent.
+ *
+ * @return the network.
+ */
+ public Network getNetwork() {
+ return network;
+ }
+
+ /**
+ * Gets the number of neurons in each row of this map.
+ *
+ * @return the number of rows.
+ */
+ public int getNumberOfRows() {
+ return numberOfRows;
+ }
+
+ /**
+ * Gets the number of neurons in each column of this map.
+ *
+ * @return the number of column.
+ */
+ public int getNumberOfColumns() {
+ return numberOfColumns;
+ }
+
+ /**
+ * Retrieves the neuron at location {@code (i, j)} in the map.
+ * The neuron at position {@code (0, 0)} is located at the upper-left
+ * corner of the map.
+ *
+ * @param i Row index.
+ * @param j Column index.
+ * @return the neuron at {@code (i, j)}.
+ * @throws OutOfRangeException if {@code i} or {@code j} is
+ * out of range.
+ *
+ * @see #getNeuron(int,int,HorizontalDirection,VerticalDirection)
+ */
+ public Neuron getNeuron(int i,
+ int j) {
+ if (i < 0 ||
+ i >= numberOfRows) {
+ throw new OutOfRangeException(i, 0, numberOfRows - 1);
+ }
+ if (j < 0 ||
+ j >= numberOfColumns) {
+ throw new OutOfRangeException(j, 0, numberOfColumns - 1);
+ }
+
+ return network.getNeuron(identifiers[i][j]);
+ }
+
+ /**
+ * Retrieves the neuron at {@code (location[0], location[1])} in the map.
+ * The neuron at position {@code (0, 0)} is located at the upper-left
+ * corner of the map.
+ *
+ * @param row Row index.
+ * @param col Column index.
+ * @param alongRowDir Direction along the given {@code row} (i.e. an
+ * offset will be added to the given <em>column</em> index.
+ * @param alongColDir Direction along the given {@code col} (i.e. an
+ * offset will be added to the given <em>row</em> index.
+ * @return the neuron at the requested location, or {@code null} if
+ * the location is not on the map.
+ *
+ * @see #getNeuron(int,int)
+ */
+ public Neuron getNeuron(int row,
+ int col,
+ HorizontalDirection alongRowDir,
+ VerticalDirection alongColDir) {
+ final int[] location = getLocation(row, col, alongRowDir, alongColDir);
+
+ return location == null ? null : getNeuron(location[0], location[1]);
+ }
+
+ /**
+ * Computes the location of a neighbouring neuron.
+ * It will return {@code null} if the resulting location is not part
+ * of the map.
+ * Position {@code (0, 0)} is at the upper-left corner of the map.
+ *
+ * @param row Row index.
+ * @param col Column index.
+ * @param alongRowDir Direction along the given {@code row} (i.e. an
+ * offset will be added to the given <em>column</em> index.
+ * @param alongColDir Direction along the given {@code col} (i.e. an
+ * offset will be added to the given <em>row</em> index.
+ * @return an array of length 2 containing the indices of the requested
+ * location, or {@code null} if that location is not part of the map.
+ *
+ * @see #getNeuron(int,int)
+ */
+ private int[] getLocation(int row,
+ int col,
+ HorizontalDirection alongRowDir,
+ VerticalDirection alongColDir) {
+ final int colOffset;
+ switch (alongRowDir) {
+ case LEFT:
+ colOffset = -1;
+ break;
+ case RIGHT:
+ colOffset = 1;
+ break;
+ case CENTER:
+ colOffset = 0;
+ break;
+ default:
+ // Should never happen.
+ throw new MathInternalError();
+ }
+ int colIndex = col + colOffset;
+ if (wrapColumns) {
+ if (colIndex < 0) {
+ colIndex += numberOfColumns;
+ } else {
+ colIndex %= numberOfColumns;
+ }
+ }
+
+ final int rowOffset;
+ switch (alongColDir) {
+ case UP:
+ rowOffset = -1;
+ break;
+ case DOWN:
+ rowOffset = 1;
+ break;
+ case CENTER:
+ rowOffset = 0;
+ break;
+ default:
+ // Should never happen.
+ throw new MathInternalError();
+ }
+ int rowIndex = row + rowOffset;
+ if (wrapRows) {
+ if (rowIndex < 0) {
+ rowIndex += numberOfRows;
+ } else {
+ rowIndex %= numberOfRows;
+ }
+ }
+
+ if (rowIndex < 0 ||
+ rowIndex >= numberOfRows ||
+ colIndex < 0 ||
+ colIndex >= numberOfColumns) {
+ return null;
+ } else {
+ return new int[] { rowIndex, colIndex };
+ }
+ }
+
+ /**
+ * Creates the neighbour relationships between neurons.
+ */
+ private void createLinks() {
+ // "linkEnd" will store the identifiers of the "neighbours".
+ final List<Long> linkEnd = new ArrayList<Long>();
+ final int iLast = numberOfRows - 1;
+ final int jLast = numberOfColumns - 1;
+ for (int i = 0; i < numberOfRows; i++) {
+ for (int j = 0; j < numberOfColumns; j++) {
+ linkEnd.clear();
+
+ switch (neighbourhood) {
+
+ case MOORE:
+ // Add links to "diagonal" neighbours.
+ if (i > 0) {
+ if (j > 0) {
+ linkEnd.add(identifiers[i - 1][j - 1]);
+ }
+ if (j < jLast) {
+ linkEnd.add(identifiers[i - 1][j + 1]);
+ }
+ }
+ if (i < iLast) {
+ if (j > 0) {
+ linkEnd.add(identifiers[i + 1][j - 1]);
+ }
+ if (j < jLast) {
+ linkEnd.add(identifiers[i + 1][j + 1]);
+ }
+ }
+ if (wrapRows) {
+ if (i == 0) {
+ if (j > 0) {
+ linkEnd.add(identifiers[iLast][j - 1]);
+ }
+ if (j < jLast) {
+ linkEnd.add(identifiers[iLast][j + 1]);
+ }
+ } else if (i == iLast) {
+ if (j > 0) {
+ linkEnd.add(identifiers[0][j - 1]);
+ }
+ if (j < jLast) {
+ linkEnd.add(identifiers[0][j + 1]);
+ }
+ }
+ }
+ if (wrapColumns) {
+ if (j == 0) {
+ if (i > 0) {
+ linkEnd.add(identifiers[i - 1][jLast]);
+ }
+ if (i < iLast) {
+ linkEnd.add(identifiers[i + 1][jLast]);
+ }
+ } else if (j == jLast) {
+ if (i > 0) {
+ linkEnd.add(identifiers[i - 1][0]);
+ }
+ if (i < iLast) {
+ linkEnd.add(identifiers[i + 1][0]);
+ }
+ }
+ }
+ if (wrapRows &&
+ wrapColumns) {
+ if (i == 0 &&
+ j == 0) {
+ linkEnd.add(identifiers[iLast][jLast]);
+ } else if (i == 0 &&
+ j == jLast) {
+ linkEnd.add(identifiers[iLast][0]);
+ } else if (i == iLast &&
+ j == 0) {
+ linkEnd.add(identifiers[0][jLast]);
+ } else if (i == iLast &&
+ j == jLast) {
+ linkEnd.add(identifiers[0][0]);
+ }
+ }
+
+ // Case falls through since the "Moore" neighbourhood
+ // also contains the neurons that belong to the "Von
+ // Neumann" neighbourhood.
+
+ // fallthru (CheckStyle)
+ case VON_NEUMANN:
+ // Links to preceding and following "row".
+ if (i > 0) {
+ linkEnd.add(identifiers[i - 1][j]);
+ }
+ if (i < iLast) {
+ linkEnd.add(identifiers[i + 1][j]);
+ }
+ if (wrapRows) {
+ if (i == 0) {
+ linkEnd.add(identifiers[iLast][j]);
+ } else if (i == iLast) {
+ linkEnd.add(identifiers[0][j]);
+ }
+ }
+
+ // Links to preceding and following "column".
+ if (j > 0) {
+ linkEnd.add(identifiers[i][j - 1]);
+ }
+ if (j < jLast) {
+ linkEnd.add(identifiers[i][j + 1]);
+ }
+ if (wrapColumns) {
+ if (j == 0) {
+ linkEnd.add(identifiers[i][jLast]);
+ } else if (j == jLast) {
+ linkEnd.add(identifiers[i][0]);
+ }
+ }
+ break;
+
+ default:
+ throw new MathInternalError(); // Cannot happen.
+ }
+
+ final Neuron aNeuron = network.getNeuron(identifiers[i][j]);
+ for (long b : linkEnd) {
+ final Neuron bNeuron = network.getNeuron(b);
+ // Link to all neighbours.
+ // The reverse links will be added as the loop proceeds.
+ network.addLink(aNeuron, bNeuron);
+ }
+ }
+ }
+ }
+
+ /**
+ * Prevents proxy bypass.
+ *
+ * @param in Input stream.
+ */
+ private void readObject(ObjectInputStream in) {
+ throw new IllegalStateException();
+ }
+
+ /**
+ * Custom serialization.
+ *
+ * @return the proxy instance that will be actually serialized.
+ */
+ private Object writeReplace() {
+ final double[][][] featuresList = new double[numberOfRows][numberOfColumns][];
+ for (int i = 0; i < numberOfRows; i++) {
+ for (int j = 0; j < numberOfColumns; j++) {
+ featuresList[i][j] = getNeuron(i, j).getFeatures();
+ }
+ }
+
+ return new SerializationProxy(wrapRows,
+ wrapColumns,
+ neighbourhood,
+ featuresList);
+ }
+
+ /**
+ * Serialization.
+ */
+ private static class SerializationProxy implements Serializable {
+ /** Serializable. */
+ private static final long serialVersionUID = 20130226L;
+ /** Wrap. */
+ private final boolean wrapRows;
+ /** Wrap. */
+ private final boolean wrapColumns;
+ /** Neighbourhood type. */
+ private final SquareNeighbourhood neighbourhood;
+ /** Neurons' features. */
+ private final double[][][] featuresList;
+
+ /**
+ * @param wrapRows Whether the row dimension is wrapped.
+ * @param wrapColumns Whether the column dimension is wrapped.
+ * @param neighbourhood Neighbourhood type.
+ * @param featuresList List of neurons features.
+ * {@code neuronList}.
+ */
+ SerializationProxy(boolean wrapRows,
+ boolean wrapColumns,
+ SquareNeighbourhood neighbourhood,
+ double[][][] featuresList) {
+ this.wrapRows = wrapRows;
+ this.wrapColumns = wrapColumns;
+ this.neighbourhood = neighbourhood;
+ this.featuresList = featuresList;
+ }
+
+ /**
+ * Custom serialization.
+ *
+ * @return the {@link Neuron} for which this instance is the proxy.
+ */
+ private Object readResolve() {
+ return new NeuronSquareMesh2D(wrapRows,
+ wrapColumns,
+ neighbourhood,
+ featuresList);
+ }
+ }
+}
diff --git a/src/main/java/org/apache/commons/math3/ml/neuralnet/twod/package-info.java b/src/main/java/org/apache/commons/math3/ml/neuralnet/twod/package-info.java
new file mode 100644
index 0000000..41535e8
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/ml/neuralnet/twod/package-info.java
@@ -0,0 +1,22 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * Two-dimensional neural networks.
+ */
+
+package org.apache.commons.math3.ml.neuralnet.twod;
diff --git a/src/main/java/org/apache/commons/math3/ml/neuralnet/twod/util/HitHistogram.java b/src/main/java/org/apache/commons/math3/ml/neuralnet/twod/util/HitHistogram.java
new file mode 100644
index 0000000..06cee98
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/ml/neuralnet/twod/util/HitHistogram.java
@@ -0,0 +1,83 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math3.ml.neuralnet.twod.util;
+
+import org.apache.commons.math3.ml.neuralnet.MapUtils;
+import org.apache.commons.math3.ml.neuralnet.Neuron;
+import org.apache.commons.math3.ml.neuralnet.twod.NeuronSquareMesh2D;
+import org.apache.commons.math3.ml.distance.DistanceMeasure;
+
+/**
+ * Computes the hit histogram.
+ * Each bin will contain the number of data for which the corresponding
+ * neuron is the best matching unit.
+ * @since 3.6
+ */
+public class HitHistogram implements MapDataVisualization {
+ /** Distance. */
+ private final DistanceMeasure distance;
+ /** Whether to compute relative bin counts. */
+ private final boolean normalizeCount;
+
+ /**
+ * @param normalizeCount Whether to compute relative bin counts.
+ * If {@code true}, the data count in each bin will be divided by the total
+ * number of samples.
+ * @param distance Distance.
+ */
+ public HitHistogram(boolean normalizeCount,
+ DistanceMeasure distance) {
+ this.normalizeCount = normalizeCount;
+ this.distance = distance;
+ }
+
+ /** {@inheritDoc} */
+ public double[][] computeImage(NeuronSquareMesh2D map,
+ Iterable<double[]> data) {
+ final int nR = map.getNumberOfRows();
+ final int nC = map.getNumberOfColumns();
+
+ final LocationFinder finder = new LocationFinder(map);
+
+ // Total number of samples.
+ int numSamples = 0;
+ // Hit bins.
+ final double[][] hit = new double[nR][nC];
+
+ for (double[] sample : data) {
+ final Neuron best = MapUtils.findBest(sample, map, distance);
+
+ final LocationFinder.Location loc = finder.getLocation(best);
+ final int row = loc.getRow();
+ final int col = loc.getColumn();
+ hit[row][col] += 1;
+
+ ++numSamples;
+ }
+
+ if (normalizeCount) {
+ for (int r = 0; r < nR; r++) {
+ for (int c = 0; c < nC; c++) {
+ hit[r][c] /= numSamples;
+ }
+ }
+ }
+
+ return hit;
+ }
+}
diff --git a/src/main/java/org/apache/commons/math3/ml/neuralnet/twod/util/LocationFinder.java b/src/main/java/org/apache/commons/math3/ml/neuralnet/twod/util/LocationFinder.java
new file mode 100644
index 0000000..e4ece61
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/ml/neuralnet/twod/util/LocationFinder.java
@@ -0,0 +1,105 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math3.ml.neuralnet.twod.util;
+
+import java.util.Map;
+import java.util.HashMap;
+import org.apache.commons.math3.ml.neuralnet.Neuron;
+import org.apache.commons.math3.ml.neuralnet.twod.NeuronSquareMesh2D;
+import org.apache.commons.math3.exception.MathIllegalStateException;
+
+/**
+ * Helper class to find the grid coordinates of a neuron.
+ * @since 3.6
+ */
+public class LocationFinder {
+ /** Identifier to location mapping. */
+ private final Map<Long, Location> locations = new HashMap<Long, Location>();
+
+ /**
+ * Container holding a (row, column) pair.
+ */
+ public static class Location {
+ /** Row index. */
+ private final int row;
+ /** Column index. */
+ private final int column;
+
+ /**
+ * @param row Row index.
+ * @param column Column index.
+ */
+ public Location(int row,
+ int column) {
+ this.row = row;
+ this.column = column;
+ }
+
+ /**
+ * @return the row index.
+ */
+ public int getRow() {
+ return row;
+ }
+
+ /**
+ * @return the column index.
+ */
+ public int getColumn() {
+ return column;
+ }
+ }
+
+ /**
+ * Builds a finder to retrieve the locations of neurons that
+ * belong to the given {@code map}.
+ *
+ * @param map Map.
+ *
+ * @throws MathIllegalStateException if the network contains non-unique
+ * identifiers. This indicates an inconsistent state due to a bug in
+ * the construction code of the underlying
+ * {@link org.apache.commons.math3.ml.neuralnet.Network network}.
+ */
+ public LocationFinder(NeuronSquareMesh2D map) {
+ final int nR = map.getNumberOfRows();
+ final int nC = map.getNumberOfColumns();
+
+ for (int r = 0; r < nR; r++) {
+ for (int c = 0; c < nC; c++) {
+ final Long id = map.getNeuron(r, c).getIdentifier();
+ if (locations.get(id) != null) {
+ throw new MathIllegalStateException();
+ }
+ locations.put(id, new Location(r, c));
+ }
+ }
+ }
+
+ /**
+ * Retrieves a neuron's grid coordinates.
+ *
+ * @param n Neuron.
+ * @return the (row, column) coordinates of {@code n}, or {@code null}
+ * if no such neuron belongs to the {@link #LocationFinder(NeuronSquareMesh2D)
+ * map used to build this instance}.
+ */
+ public Location getLocation(Neuron n) {
+ return locations.get(n.getIdentifier());
+ }
+}
diff --git a/src/main/java/org/apache/commons/math3/ml/neuralnet/twod/util/MapDataVisualization.java b/src/main/java/org/apache/commons/math3/ml/neuralnet/twod/util/MapDataVisualization.java
new file mode 100644
index 0000000..71fab43
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/ml/neuralnet/twod/util/MapDataVisualization.java
@@ -0,0 +1,38 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math3.ml.neuralnet.twod.util;
+
+import org.apache.commons.math3.ml.neuralnet.twod.NeuronSquareMesh2D;
+
+/**
+ * Interface for algorithms that compute some metrics of the projection of
+ * data on a 2D-map.
+ * @since 3.6
+ */
+public interface MapDataVisualization {
+ /**
+ * Creates an image of the {@code data} metrics when represented by the
+ * {@code map}.
+ *
+ * @param map Map.
+ * @param data Data.
+ * @return a 2D-array (in row major order) representing the metrics.
+ */
+ double[][] computeImage(NeuronSquareMesh2D map,
+ Iterable<double[]> data);
+}
diff --git a/src/main/java/org/apache/commons/math3/ml/neuralnet/twod/util/MapVisualization.java b/src/main/java/org/apache/commons/math3/ml/neuralnet/twod/util/MapVisualization.java
new file mode 100644
index 0000000..9304d76
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/ml/neuralnet/twod/util/MapVisualization.java
@@ -0,0 +1,34 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math3.ml.neuralnet.twod.util;
+
+import org.apache.commons.math3.ml.neuralnet.twod.NeuronSquareMesh2D;
+
+/**
+ * Interface for algorithms that compute some property of a 2D-map.
+ * @since 3.6
+ */
+public interface MapVisualization {
+ /**
+ * Creates an image of the {@code map}.
+ *
+ * @param map Map.
+ * @return a 2D-array (in row major order) representing the property.
+ */
+ double[][] computeImage(NeuronSquareMesh2D map);
+}
diff --git a/src/main/java/org/apache/commons/math3/ml/neuralnet/twod/util/QuantizationError.java b/src/main/java/org/apache/commons/math3/ml/neuralnet/twod/util/QuantizationError.java
new file mode 100644
index 0000000..8ec1da3
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/ml/neuralnet/twod/util/QuantizationError.java
@@ -0,0 +1,76 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math3.ml.neuralnet.twod.util;
+
+import org.apache.commons.math3.ml.neuralnet.MapUtils;
+import org.apache.commons.math3.ml.neuralnet.Neuron;
+import org.apache.commons.math3.ml.neuralnet.twod.NeuronSquareMesh2D;
+import org.apache.commons.math3.ml.distance.DistanceMeasure;
+
+/**
+ * Computes the quantization error histogram.
+ * Each bin will contain the average of the distances between samples
+ * mapped to the corresponding unit and the weight vector of that unit.
+ * @since 3.6
+ */
+public class QuantizationError implements MapDataVisualization {
+ /** Distance. */
+ private final DistanceMeasure distance;
+
+ /**
+ * @param distance Distance.
+ */
+ public QuantizationError(DistanceMeasure distance) {
+ this.distance = distance;
+ }
+
+ /** {@inheritDoc} */
+ public double[][] computeImage(NeuronSquareMesh2D map,
+ Iterable<double[]> data) {
+ final int nR = map.getNumberOfRows();
+ final int nC = map.getNumberOfColumns();
+
+ final LocationFinder finder = new LocationFinder(map);
+
+ // Hit bins.
+ final int[][] hit = new int[nR][nC];
+ // Error bins.
+ final double[][] error = new double[nR][nC];
+
+ for (double[] sample : data) {
+ final Neuron best = MapUtils.findBest(sample, map, distance);
+
+ final LocationFinder.Location loc = finder.getLocation(best);
+ final int row = loc.getRow();
+ final int col = loc.getColumn();
+ hit[row][col] += 1;
+ error[row][col] += distance.compute(sample, best.getFeatures());
+ }
+
+ for (int r = 0; r < nR; r++) {
+ for (int c = 0; c < nC; c++) {
+ final int count = hit[r][c];
+ if (count != 0) {
+ error[r][c] /= count;
+ }
+ }
+ }
+
+ return error;
+ }
+}
diff --git a/src/main/java/org/apache/commons/math3/ml/neuralnet/twod/util/SmoothedDataHistogram.java b/src/main/java/org/apache/commons/math3/ml/neuralnet/twod/util/SmoothedDataHistogram.java
new file mode 100644
index 0000000..b8e552c
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/ml/neuralnet/twod/util/SmoothedDataHistogram.java
@@ -0,0 +1,97 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math3.ml.neuralnet.twod.util;
+
+import org.apache.commons.math3.ml.neuralnet.MapUtils;
+import org.apache.commons.math3.ml.neuralnet.Neuron;
+import org.apache.commons.math3.ml.neuralnet.twod.NeuronSquareMesh2D;
+import org.apache.commons.math3.ml.distance.DistanceMeasure;
+import org.apache.commons.math3.exception.NumberIsTooSmallException;
+
+/**
+ * Visualization of high-dimensional data projection on a 2D-map.
+ * The method is described in
+ * <quote>
+ * <em>Using Smoothed Data Histograms for Cluster Visualization in Self-Organizing Maps</em>
+ * <br>
+ * by Elias Pampalk, Andreas Rauber and Dieter Merkl.
+ * </quote>
+ * @since 3.6
+ */
+public class SmoothedDataHistogram implements MapDataVisualization {
+ /** Smoothing parameter. */
+ private final int smoothingBins;
+ /** Distance. */
+ private final DistanceMeasure distance;
+ /** Normalization factor. */
+ private final double membershipNormalization;
+
+ /**
+ * @param smoothingBins Number of bins.
+ * @param distance Distance.
+ */
+ public SmoothedDataHistogram(int smoothingBins,
+ DistanceMeasure distance) {
+ this.smoothingBins = smoothingBins;
+ this.distance = distance;
+
+ double sum = 0;
+ for (int i = 0; i < smoothingBins; i++) {
+ sum += smoothingBins - i;
+ }
+
+ this.membershipNormalization = 1d / sum;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @throws NumberIsTooSmallException if the size of the {@code map}
+ * is smaller than the number of {@link #SmoothedDataHistogram(int,DistanceMeasure)
+ * smoothing bins}.
+ */
+ public double[][] computeImage(NeuronSquareMesh2D map,
+ Iterable<double[]> data) {
+ final int nR = map.getNumberOfRows();
+ final int nC = map.getNumberOfColumns();
+
+ final int mapSize = nR * nC;
+ if (mapSize < smoothingBins) {
+ throw new NumberIsTooSmallException(mapSize, smoothingBins, true);
+ }
+
+ final LocationFinder finder = new LocationFinder(map);
+
+ // Histogram bins.
+ final double[][] histo = new double[nR][nC];
+
+ for (double[] sample : data) {
+ final Neuron[] sorted = MapUtils.sort(sample,
+ map.getNetwork(),
+ distance);
+ for (int i = 0; i < smoothingBins; i++) {
+ final LocationFinder.Location loc = finder.getLocation(sorted[i]);
+ final int row = loc.getRow();
+ final int col = loc.getColumn();
+ histo[row][col] += (smoothingBins - i) * membershipNormalization;
+ }
+ }
+
+ return histo;
+ }
+}
diff --git a/src/main/java/org/apache/commons/math3/ml/neuralnet/twod/util/TopographicErrorHistogram.java b/src/main/java/org/apache/commons/math3/ml/neuralnet/twod/util/TopographicErrorHistogram.java
new file mode 100644
index 0000000..b831de8
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/ml/neuralnet/twod/util/TopographicErrorHistogram.java
@@ -0,0 +1,91 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math3.ml.neuralnet.twod.util;
+
+import org.apache.commons.math3.ml.neuralnet.MapUtils;
+import org.apache.commons.math3.ml.neuralnet.Neuron;
+import org.apache.commons.math3.ml.neuralnet.Network;
+import org.apache.commons.math3.ml.neuralnet.twod.NeuronSquareMesh2D;
+import org.apache.commons.math3.ml.distance.DistanceMeasure;
+import org.apache.commons.math3.util.Pair;
+
+/**
+ * Computes the topographic error histogram.
+ * Each bin will contain the number of data for which the first and
+ * second best matching units are not adjacent in the map.
+ * @since 3.6
+ */
+public class TopographicErrorHistogram implements MapDataVisualization {
+ /** Distance. */
+ private final DistanceMeasure distance;
+ /** Whether to compute relative bin counts. */
+ private final boolean relativeCount;
+
+ /**
+ * @param relativeCount Whether to compute relative bin counts.
+ * If {@code true}, the data count in each bin will be divided by the total
+ * number of samples mapped to the neuron represented by that bin.
+ * @param distance Distance.
+ */
+ public TopographicErrorHistogram(boolean relativeCount,
+ DistanceMeasure distance) {
+ this.relativeCount = relativeCount;
+ this.distance = distance;
+ }
+
+ /** {@inheritDoc} */
+ public double[][] computeImage(NeuronSquareMesh2D map,
+ Iterable<double[]> data) {
+ final int nR = map.getNumberOfRows();
+ final int nC = map.getNumberOfColumns();
+
+ final Network net = map.getNetwork();
+ final LocationFinder finder = new LocationFinder(map);
+
+ // Hit bins.
+ final int[][] hit = new int[nR][nC];
+ // Error bins.
+ final double[][] error = new double[nR][nC];
+
+ for (double[] sample : data) {
+ final Pair<Neuron, Neuron> p = MapUtils.findBestAndSecondBest(sample, map, distance);
+ final Neuron best = p.getFirst();
+
+ final LocationFinder.Location loc = finder.getLocation(best);
+ final int row = loc.getRow();
+ final int col = loc.getColumn();
+ hit[row][col] += 1;
+
+ if (!net.getNeighbours(best).contains(p.getSecond())) {
+ // Increment count if first and second best matching units
+ // are not neighbours.
+ error[row][col] += 1;
+ }
+ }
+
+ if (relativeCount) {
+ for (int r = 0; r < nR; r++) {
+ for (int c = 0; c < nC; c++) {
+ error[r][c] /= hit[r][c];
+ }
+ }
+ }
+
+ return error;
+ }
+}
diff --git a/src/main/java/org/apache/commons/math3/ml/neuralnet/twod/util/UnifiedDistanceMatrix.java b/src/main/java/org/apache/commons/math3/ml/neuralnet/twod/util/UnifiedDistanceMatrix.java
new file mode 100644
index 0000000..aee982a
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/ml/neuralnet/twod/util/UnifiedDistanceMatrix.java
@@ -0,0 +1,209 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math3.ml.neuralnet.twod.util;
+
+import java.util.Collection;
+import org.apache.commons.math3.ml.neuralnet.Neuron;
+import org.apache.commons.math3.ml.neuralnet.Network;
+import org.apache.commons.math3.ml.neuralnet.twod.NeuronSquareMesh2D;
+import org.apache.commons.math3.ml.distance.DistanceMeasure;
+
+/**
+ * <a href="http://en.wikipedia.org/wiki/U-Matrix">U-Matrix</a>
+ * visualization of high-dimensional data projection.
+ * @since 3.6
+ */
+public class UnifiedDistanceMatrix implements MapVisualization {
+ /** Whether to show distance between each pair of neighbouring units. */
+ private final boolean individualDistances;
+ /** Distance. */
+ private final DistanceMeasure distance;
+
+ /**
+ * Simple constructor.
+ *
+ * @param individualDistances If {@code true}, the 8 individual
+ * inter-units distances will be {@link #computeImage(NeuronSquareMesh2D)
+ * computed}. They will be stored in additional pixels around each of
+ * the original units of the 2D-map. The additional pixels that lie
+ * along a "diagonal" are shared by <em>two</em> pairs of units: their
+ * value will be set to the average distance between the units belonging
+ * to each of the pairs. The value zero will be stored in the pixel
+ * corresponding to the location of a unit of the 2D-map.
+ * <br>
+ * If {@code false}, only the average distance between a unit and all its
+ * neighbours will be computed (and stored in the pixel corresponding to
+ * that unit of the 2D-map). In that case, the number of neighbours taken
+ * into account depends on the network's
+ * {@link org.apache.commons.math3.ml.neuralnet.SquareNeighbourhood
+ * neighbourhood type}.
+ * @param distance Distance.
+ */
+ public UnifiedDistanceMatrix(boolean individualDistances,
+ DistanceMeasure distance) {
+ this.individualDistances = individualDistances;
+ this.distance = distance;
+ }
+
+ /** {@inheritDoc} */
+ public double[][] computeImage(NeuronSquareMesh2D map) {
+ if (individualDistances) {
+ return individualDistances(map);
+ } else {
+ return averageDistances(map);
+ }
+ }
+
+ /**
+ * Computes the distances between a unit of the map and its
+ * neighbours.
+ * The image will contain more pixels than the number of neurons
+ * in the given {@code map} because each neuron has 8 neighbours.
+ * The value zero will be stored in the pixels corresponding to
+ * the location of a map unit.
+ *
+ * @param map Map.
+ * @return an image representing the individual distances.
+ */
+ private double[][] individualDistances(NeuronSquareMesh2D map) {
+ final int numRows = map.getNumberOfRows();
+ final int numCols = map.getNumberOfColumns();
+
+ final double[][] uMatrix = new double[numRows * 2 + 1][numCols * 2 + 1];
+
+ // 1.
+ // Fill right and bottom slots of each unit's location with the
+ // distance between the current unit and each of the two neighbours,
+ // respectively.
+ for (int i = 0; i < numRows; i++) {
+ // Current unit's row index in result image.
+ final int iR = 2 * i + 1;
+
+ for (int j = 0; j < numCols; j++) {
+ // Current unit's column index in result image.
+ final int jR = 2 * j + 1;
+
+ final double[] current = map.getNeuron(i, j).getFeatures();
+ Neuron neighbour;
+
+ // Right neighbour.
+ neighbour = map.getNeuron(i, j,
+ NeuronSquareMesh2D.HorizontalDirection.RIGHT,
+ NeuronSquareMesh2D.VerticalDirection.CENTER);
+ if (neighbour != null) {
+ uMatrix[iR][jR + 1] = distance.compute(current,
+ neighbour.getFeatures());
+ }
+
+ // Bottom-center neighbour.
+ neighbour = map.getNeuron(i, j,
+ NeuronSquareMesh2D.HorizontalDirection.CENTER,
+ NeuronSquareMesh2D.VerticalDirection.DOWN);
+ if (neighbour != null) {
+ uMatrix[iR + 1][jR] = distance.compute(current,
+ neighbour.getFeatures());
+ }
+ }
+ }
+
+ // 2.
+ // Fill the bottom-rigth slot of each unit's location with the average
+ // of the distances between
+ // * the current unit and its bottom-right neighbour, and
+ // * the bottom-center neighbour and the right neighbour.
+ for (int i = 0; i < numRows; i++) {
+ // Current unit's row index in result image.
+ final int iR = 2 * i + 1;
+
+ for (int j = 0; j < numCols; j++) {
+ // Current unit's column index in result image.
+ final int jR = 2 * j + 1;
+
+ final Neuron current = map.getNeuron(i, j);
+ final Neuron right = map.getNeuron(i, j,
+ NeuronSquareMesh2D.HorizontalDirection.RIGHT,
+ NeuronSquareMesh2D.VerticalDirection.CENTER);
+ final Neuron bottom = map.getNeuron(i, j,
+ NeuronSquareMesh2D.HorizontalDirection.CENTER,
+ NeuronSquareMesh2D.VerticalDirection.DOWN);
+ final Neuron bottomRight = map.getNeuron(i, j,
+ NeuronSquareMesh2D.HorizontalDirection.RIGHT,
+ NeuronSquareMesh2D.VerticalDirection.DOWN);
+
+ final double current2BottomRight = bottomRight == null ?
+ 0 :
+ distance.compute(current.getFeatures(),
+ bottomRight.getFeatures());
+ final double right2Bottom = (right == null ||
+ bottom == null) ?
+ 0 :
+ distance.compute(right.getFeatures(),
+ bottom.getFeatures());
+
+ // Bottom-right slot.
+ uMatrix[iR + 1][jR + 1] = 0.5 * (current2BottomRight + right2Bottom);
+ }
+ }
+
+ // 3. Copy last row into first row.
+ final int lastRow = uMatrix.length - 1;
+ uMatrix[0] = uMatrix[lastRow];
+
+ // 4.
+ // Copy last column into first column.
+ final int lastCol = uMatrix[0].length - 1;
+ for (int r = 0; r < lastRow; r++) {
+ uMatrix[r][0] = uMatrix[r][lastCol];
+ }
+
+ return uMatrix;
+ }
+
+ /**
+ * Computes the distances between a unit of the map and its neighbours.
+ *
+ * @param map Map.
+ * @return an image representing the average distances.
+ */
+ private double[][] averageDistances(NeuronSquareMesh2D map) {
+ final int numRows = map.getNumberOfRows();
+ final int numCols = map.getNumberOfColumns();
+ final double[][] uMatrix = new double[numRows][numCols];
+
+ final Network net = map.getNetwork();
+
+ for (int i = 0; i < numRows; i++) {
+ for (int j = 0; j < numCols; j++) {
+ final Neuron neuron = map.getNeuron(i, j);
+ final Collection<Neuron> neighbours = net.getNeighbours(neuron);
+ final double[] features = neuron.getFeatures();
+
+ double d = 0;
+ int count = 0;
+ for (Neuron n : neighbours) {
+ ++count;
+ d += distance.compute(features, n.getFeatures());
+ }
+
+ uMatrix[i][j] = d / count;
+ }
+ }
+
+ return uMatrix;
+ }
+}
diff --git a/src/main/java/org/apache/commons/math3/ml/neuralnet/twod/util/package-info.java b/src/main/java/org/apache/commons/math3/ml/neuralnet/twod/util/package-info.java
new file mode 100644
index 0000000..cd4aab0
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/ml/neuralnet/twod/util/package-info.java
@@ -0,0 +1,22 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * Utilities to visualize two-dimensional neural networks.
+ */
+
+package org.apache.commons.math3.ml.neuralnet.twod.util;
diff --git a/src/main/java/org/apache/commons/math3/ml/package-info.java b/src/main/java/org/apache/commons/math3/ml/package-info.java
new file mode 100644
index 0000000..394aad2
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/ml/package-info.java
@@ -0,0 +1,18 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/** Base package for machine learning algorithms. */
+package org.apache.commons.math3.ml;
diff --git a/src/main/java/org/apache/commons/math3/ode/AbstractFieldIntegrator.java b/src/main/java/org/apache/commons/math3/ode/AbstractFieldIntegrator.java
new file mode 100644
index 0000000..efcd08a
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/ode/AbstractFieldIntegrator.java
@@ -0,0 +1,510 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math3.ode;
+
+import org.apache.commons.math3.Field;
+import org.apache.commons.math3.RealFieldElement;
+import org.apache.commons.math3.analysis.solvers.BracketedRealFieldUnivariateSolver;
+import org.apache.commons.math3.analysis.solvers.FieldBracketingNthOrderBrentSolver;
+import org.apache.commons.math3.exception.DimensionMismatchException;
+import org.apache.commons.math3.exception.MaxCountExceededException;
+import org.apache.commons.math3.exception.NoBracketingException;
+import org.apache.commons.math3.exception.NumberIsTooSmallException;
+import org.apache.commons.math3.exception.util.LocalizedFormats;
+import org.apache.commons.math3.ode.events.FieldEventHandler;
+import org.apache.commons.math3.ode.events.FieldEventState;
+import org.apache.commons.math3.ode.sampling.AbstractFieldStepInterpolator;
+import org.apache.commons.math3.ode.sampling.FieldStepHandler;
+import org.apache.commons.math3.util.FastMath;
+import org.apache.commons.math3.util.IntegerSequence;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.Iterator;
+import java.util.List;
+import java.util.SortedSet;
+import java.util.TreeSet;
+
+/**
+ * Base class managing common boilerplate for all integrators.
+ *
+ * @param <T> the type of the field elements
+ * @since 3.6
+ */
+public abstract class AbstractFieldIntegrator<T extends RealFieldElement<T>>
+ implements FirstOrderFieldIntegrator<T> {
+
+ /** Default relative accuracy. */
+ private static final double DEFAULT_RELATIVE_ACCURACY = 1e-14;
+
+ /** Default function value accuracy. */
+ private static final double DEFAULT_FUNCTION_VALUE_ACCURACY = 1e-15;
+
+ /** Step handler. */
+ private Collection<FieldStepHandler<T>> stepHandlers;
+
+ /** Current step start. */
+ private FieldODEStateAndDerivative<T> stepStart;
+
+ /** Current stepsize. */
+ private T stepSize;
+
+ /** Indicator for last step. */
+ private boolean isLastStep;
+
+ /** Indicator that a state or derivative reset was triggered by some event. */
+ private boolean resetOccurred;
+
+ /** Field to which the time and state vector elements belong. */
+ private final Field<T> field;
+
+ /** Events states. */
+ private Collection<FieldEventState<T>> eventsStates;
+
+ /** Initialization indicator of events states. */
+ private boolean statesInitialized;
+
+ /** Name of the method. */
+ private final String name;
+
+ /** Counter for number of evaluations. */
+ private IntegerSequence.Incrementor evaluations;
+
+ /** Differential equations to integrate. */
+ private transient FieldExpandableODE<T> equations;
+
+ /**
+ * Build an instance.
+ *
+ * @param field field to which the time and state vector elements belong
+ * @param name name of the method
+ */
+ protected AbstractFieldIntegrator(final Field<T> field, final String name) {
+ this.field = field;
+ this.name = name;
+ stepHandlers = new ArrayList<FieldStepHandler<T>>();
+ stepStart = null;
+ stepSize = null;
+ eventsStates = new ArrayList<FieldEventState<T>>();
+ statesInitialized = false;
+ evaluations = IntegerSequence.Incrementor.create().withMaximalCount(Integer.MAX_VALUE);
+ }
+
+ /**
+ * Get the field to which state vector elements belong.
+ *
+ * @return field to which state vector elements belong
+ */
+ public Field<T> getField() {
+ return field;
+ }
+
+ /** {@inheritDoc} */
+ public String getName() {
+ return name;
+ }
+
+ /** {@inheritDoc} */
+ public void addStepHandler(final FieldStepHandler<T> handler) {
+ stepHandlers.add(handler);
+ }
+
+ /** {@inheritDoc} */
+ public Collection<FieldStepHandler<T>> getStepHandlers() {
+ return Collections.unmodifiableCollection(stepHandlers);
+ }
+
+ /** {@inheritDoc} */
+ public void clearStepHandlers() {
+ stepHandlers.clear();
+ }
+
+ /** {@inheritDoc} */
+ public void addEventHandler(
+ final FieldEventHandler<T> handler,
+ final double maxCheckInterval,
+ final double convergence,
+ final int maxIterationCount) {
+ addEventHandler(
+ handler,
+ maxCheckInterval,
+ convergence,
+ maxIterationCount,
+ new FieldBracketingNthOrderBrentSolver<T>(
+ field.getZero().add(DEFAULT_RELATIVE_ACCURACY),
+ field.getZero().add(convergence),
+ field.getZero().add(DEFAULT_FUNCTION_VALUE_ACCURACY),
+ 5));
+ }
+
+ /** {@inheritDoc} */
+ public void addEventHandler(
+ final FieldEventHandler<T> handler,
+ final double maxCheckInterval,
+ final double convergence,
+ final int maxIterationCount,
+ final BracketedRealFieldUnivariateSolver<T> solver) {
+ eventsStates.add(
+ new FieldEventState<T>(
+ handler,
+ maxCheckInterval,
+ field.getZero().add(convergence),
+ maxIterationCount,
+ solver));
+ }
+
+ /** {@inheritDoc} */
+ public Collection<FieldEventHandler<T>> getEventHandlers() {
+ final List<FieldEventHandler<T>> list =
+ new ArrayList<FieldEventHandler<T>>(eventsStates.size());
+ for (FieldEventState<T> state : eventsStates) {
+ list.add(state.getEventHandler());
+ }
+ return Collections.unmodifiableCollection(list);
+ }
+
+ /** {@inheritDoc} */
+ public void clearEventHandlers() {
+ eventsStates.clear();
+ }
+
+ /** {@inheritDoc} */
+ public FieldODEStateAndDerivative<T> getCurrentStepStart() {
+ return stepStart;
+ }
+
+ /** {@inheritDoc} */
+ public T getCurrentSignedStepsize() {
+ return stepSize;
+ }
+
+ /** {@inheritDoc} */
+ public void setMaxEvaluations(int maxEvaluations) {
+ evaluations =
+ evaluations.withMaximalCount(
+ (maxEvaluations < 0) ? Integer.MAX_VALUE : maxEvaluations);
+ }
+
+ /** {@inheritDoc} */
+ public int getMaxEvaluations() {
+ return evaluations.getMaximalCount();
+ }
+
+ /** {@inheritDoc} */
+ public int getEvaluations() {
+ return evaluations.getCount();
+ }
+
+ /**
+ * Prepare the start of an integration.
+ *
+ * @param eqn equations to integrate
+ * @param t0 start value of the independent <i>time</i> variable
+ * @param y0 array containing the start value of the state vector
+ * @param t target time for the integration
+ * @return initial state with derivatives added
+ */
+ protected FieldODEStateAndDerivative<T> initIntegration(
+ final FieldExpandableODE<T> eqn, final T t0, final T[] y0, final T t) {
+
+ this.equations = eqn;
+ evaluations = evaluations.withStart(0);
+
+ // initialize ODE
+ eqn.init(t0, y0, t);
+
+ // set up derivatives of initial state
+ final T[] y0Dot = computeDerivatives(t0, y0);
+ final FieldODEStateAndDerivative<T> state0 =
+ new FieldODEStateAndDerivative<T>(t0, y0, y0Dot);
+
+ // initialize event handlers
+ for (final FieldEventState<T> state : eventsStates) {
+ state.getEventHandler().init(state0, t);
+ }
+
+ // initialize step handlers
+ for (FieldStepHandler<T> handler : stepHandlers) {
+ handler.init(state0, t);
+ }
+
+ setStateInitialized(false);
+
+ return state0;
+ }
+
+ /**
+ * Get the differential equations to integrate.
+ *
+ * @return differential equations to integrate
+ */
+ protected FieldExpandableODE<T> getEquations() {
+ return equations;
+ }
+
+ /**
+ * Get the evaluations counter.
+ *
+ * @return evaluations counter
+ */
+ protected IntegerSequence.Incrementor getEvaluationsCounter() {
+ return evaluations;
+ }
+
+ /**
+ * Compute the derivatives and check the number of evaluations.
+ *
+ * @param t current value of the independent <I>time</I> variable
+ * @param y array containing the current value of the state vector
+ * @return state completed with derivatives
+ * @exception DimensionMismatchException if arrays dimensions do not match equations settings
+ * @exception MaxCountExceededException if the number of functions evaluations is exceeded
+ * @exception NullPointerException if the ODE equations have not been set (i.e. if this method
+ * is called outside of a call to {@link #integrate(FieldExpandableODE, FieldODEState,
+ * RealFieldElement) integrate}
+ */
+ public T[] computeDerivatives(final T t, final T[] y)
+ throws DimensionMismatchException, MaxCountExceededException, NullPointerException {
+ evaluations.increment();
+ return equations.computeDerivatives(t, y);
+ }
+
+ /**
+ * Set the stateInitialized flag.
+ *
+ * <p>This method must be called by integrators with the value {@code false} before they start
+ * integration, so a proper lazy initialization is done automatically on the first step.
+ *
+ * @param stateInitialized new value for the flag
+ */
+ protected void setStateInitialized(final boolean stateInitialized) {
+ this.statesInitialized = stateInitialized;
+ }
+
+ /**
+ * Accept a step, triggering events and step handlers.
+ *
+ * @param interpolator step interpolator
+ * @param tEnd final integration time
+ * @return state at end of step
+ * @exception MaxCountExceededException if the interpolator throws one because the number of
+ * functions evaluations is exceeded
+ * @exception NoBracketingException if the location of an event cannot be bracketed
+ * @exception DimensionMismatchException if arrays dimensions do not match equations settings
+ */
+ protected FieldODEStateAndDerivative<T> acceptStep(
+ final AbstractFieldStepInterpolator<T> interpolator, final T tEnd)
+ throws MaxCountExceededException, DimensionMismatchException, NoBracketingException {
+
+ FieldODEStateAndDerivative<T> previousState = interpolator.getGlobalPreviousState();
+ final FieldODEStateAndDerivative<T> currentState = interpolator.getGlobalCurrentState();
+
+ // initialize the events states if needed
+ if (!statesInitialized) {
+ for (FieldEventState<T> state : eventsStates) {
+ state.reinitializeBegin(interpolator);
+ }
+ statesInitialized = true;
+ }
+
+ // search for next events that may occur during the step
+ final int orderingSign = interpolator.isForward() ? +1 : -1;
+ SortedSet<FieldEventState<T>> occurringEvents =
+ new TreeSet<FieldEventState<T>>(
+ new Comparator<FieldEventState<T>>() {
+
+ /** {@inheritDoc} */
+ public int compare(FieldEventState<T> es0, FieldEventState<T> es1) {
+ return orderingSign
+ * Double.compare(
+ es0.getEventTime().getReal(),
+ es1.getEventTime().getReal());
+ }
+ });
+
+ for (final FieldEventState<T> state : eventsStates) {
+ if (state.evaluateStep(interpolator)) {
+ // the event occurs during the current step
+ occurringEvents.add(state);
+ }
+ }
+
+ AbstractFieldStepInterpolator<T> restricted = interpolator;
+ while (!occurringEvents.isEmpty()) {
+
+ // handle the chronologically first event
+ final Iterator<FieldEventState<T>> iterator = occurringEvents.iterator();
+ final FieldEventState<T> currentEvent = iterator.next();
+ iterator.remove();
+
+ // get state at event time
+ final FieldODEStateAndDerivative<T> eventState =
+ restricted.getInterpolatedState(currentEvent.getEventTime());
+
+ // restrict the interpolator to the first part of the step, up to the event
+ restricted = restricted.restrictStep(previousState, eventState);
+
+ // advance all event states to current time
+ for (final FieldEventState<T> state : eventsStates) {
+ state.stepAccepted(eventState);
+ isLastStep = isLastStep || state.stop();
+ }
+
+ // handle the first part of the step, up to the event
+ for (final FieldStepHandler<T> handler : stepHandlers) {
+ handler.handleStep(restricted, isLastStep);
+ }
+
+ if (isLastStep) {
+ // the event asked to stop integration
+ return eventState;
+ }
+
+ FieldODEState<T> newState = null;
+ resetOccurred = false;
+ for (final FieldEventState<T> state : eventsStates) {
+ newState = state.reset(eventState);
+ if (newState != null) {
+ // some event handler has triggered changes that
+ // invalidate the derivatives, we need to recompute them
+ final T[] y = equations.getMapper().mapState(newState);
+ final T[] yDot = computeDerivatives(newState.getTime(), y);
+ resetOccurred = true;
+ return equations.getMapper().mapStateAndDerivative(newState.getTime(), y, yDot);
+ }
+ }
+
+ // prepare handling of the remaining part of the step
+ previousState = eventState;
+ restricted = restricted.restrictStep(eventState, currentState);
+
+ // check if the same event occurs again in the remaining part of the step
+ if (currentEvent.evaluateStep(restricted)) {
+ // the event occurs during the current step
+ occurringEvents.add(currentEvent);
+ }
+ }
+
+ // last part of the step, after the last event
+ for (final FieldEventState<T> state : eventsStates) {
+ state.stepAccepted(currentState);
+ isLastStep = isLastStep || state.stop();
+ }
+ isLastStep =
+ isLastStep
+ || currentState.getTime().subtract(tEnd).abs().getReal()
+ <= FastMath.ulp(tEnd.getReal());
+
+ // handle the remaining part of the step, after all events if any
+ for (FieldStepHandler<T> handler : stepHandlers) {
+ handler.handleStep(restricted, isLastStep);
+ }
+
+ return currentState;
+ }
+
+ /**
+ * Check the integration span.
+ *
+ * @param eqn set of differential equations
+ * @param t target time for the integration
+ * @exception NumberIsTooSmallException if integration span is too small
+ * @exception DimensionMismatchException if adaptive step size integrators tolerance arrays
+ * dimensions are not compatible with equations settings
+ */
+ protected void sanityChecks(final FieldODEState<T> eqn, final T t)
+ throws NumberIsTooSmallException, DimensionMismatchException {
+
+ final double threshold =
+ 1000
+ * FastMath.ulp(
+ FastMath.max(
+ FastMath.abs(eqn.getTime().getReal()),
+ FastMath.abs(t.getReal())));
+ final double dt = eqn.getTime().subtract(t).abs().getReal();
+ if (dt <= threshold) {
+ throw new NumberIsTooSmallException(
+ LocalizedFormats.TOO_SMALL_INTEGRATION_INTERVAL, dt, threshold, false);
+ }
+ }
+
+ /**
+ * Check if a reset occurred while last step was accepted.
+ *
+ * @return true if a reset occurred while last step was accepted
+ */
+ protected boolean resetOccurred() {
+ return resetOccurred;
+ }
+
+ /**
+ * Set the current step size.
+ *
+ * @param stepSize step size to set
+ */
+ protected void setStepSize(final T stepSize) {
+ this.stepSize = stepSize;
+ }
+
+ /**
+ * Get the current step size.
+ *
+ * @return current step size
+ */
+ protected T getStepSize() {
+ return stepSize;
+ }
+
+ /**
+ * Set current step start.
+ *
+ * @param stepStart step start
+ */
+ protected void setStepStart(final FieldODEStateAndDerivative<T> stepStart) {
+ this.stepStart = stepStart;
+ }
+
+ /**
+ * Getcurrent step start.
+ *
+ * @return current step start
+ */
+ protected FieldODEStateAndDerivative<T> getStepStart() {
+ return stepStart;
+ }
+
+ /**
+ * Set the last state flag.
+ *
+ * @param isLastStep if true, this step is the last one
+ */
+ protected void setIsLastStep(final boolean isLastStep) {
+ this.isLastStep = isLastStep;
+ }
+
+ /**
+ * Check if this step is the last one.
+ *
+ * @return true if this step is the last one
+ */
+ protected boolean isLastStep() {
+ return isLastStep;
+ }
+}
diff --git a/src/main/java/org/apache/commons/math3/ode/AbstractIntegrator.java b/src/main/java/org/apache/commons/math3/ode/AbstractIntegrator.java
new file mode 100644
index 0000000..f3fed1f
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/ode/AbstractIntegrator.java
@@ -0,0 +1,509 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math3.ode;
+
+import org.apache.commons.math3.analysis.solvers.BracketingNthOrderBrentSolver;
+import org.apache.commons.math3.analysis.solvers.UnivariateSolver;
+import org.apache.commons.math3.exception.DimensionMismatchException;
+import org.apache.commons.math3.exception.MaxCountExceededException;
+import org.apache.commons.math3.exception.NoBracketingException;
+import org.apache.commons.math3.exception.NumberIsTooSmallException;
+import org.apache.commons.math3.exception.util.LocalizedFormats;
+import org.apache.commons.math3.ode.events.EventHandler;
+import org.apache.commons.math3.ode.events.EventState;
+import org.apache.commons.math3.ode.sampling.AbstractStepInterpolator;
+import org.apache.commons.math3.ode.sampling.StepHandler;
+import org.apache.commons.math3.util.FastMath;
+import org.apache.commons.math3.util.IntegerSequence;
+import org.apache.commons.math3.util.Precision;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.Iterator;
+import java.util.List;
+import java.util.SortedSet;
+import java.util.TreeSet;
+
+/**
+ * Base class managing common boilerplate for all integrators.
+ *
+ * @since 2.0
+ */
+public abstract class AbstractIntegrator implements FirstOrderIntegrator {
+
+ /** Step handler. */
+ protected Collection<StepHandler> stepHandlers;
+
+ /** Current step start time. */
+ protected double stepStart;
+
+ /** Current stepsize. */
+ protected double stepSize;
+
+ /** Indicator for last step. */
+ protected boolean isLastStep;
+
+ /** Indicator that a state or derivative reset was triggered by some event. */
+ protected boolean resetOccurred;
+
+ /** Events states. */
+ private Collection<EventState> eventsStates;
+
+ /** Initialization indicator of events states. */
+ private boolean statesInitialized;
+
+ /** Name of the method. */
+ private final String name;
+
+ /** Counter for number of evaluations. */
+ private IntegerSequence.Incrementor evaluations;
+
+ /** Differential equations to integrate. */
+ private transient ExpandableStatefulODE expandable;
+
+ /**
+ * Build an instance.
+ *
+ * @param name name of the method
+ */
+ public AbstractIntegrator(final String name) {
+ this.name = name;
+ stepHandlers = new ArrayList<StepHandler>();
+ stepStart = Double.NaN;
+ stepSize = Double.NaN;
+ eventsStates = new ArrayList<EventState>();
+ statesInitialized = false;
+ evaluations = IntegerSequence.Incrementor.create().withMaximalCount(Integer.MAX_VALUE);
+ }
+
+ /** Build an instance with a null name. */
+ protected AbstractIntegrator() {
+ this(null);
+ }
+
+ /** {@inheritDoc} */
+ public String getName() {
+ return name;
+ }
+
+ /** {@inheritDoc} */
+ public void addStepHandler(final StepHandler handler) {
+ stepHandlers.add(handler);
+ }
+
+ /** {@inheritDoc} */
+ public Collection<StepHandler> getStepHandlers() {
+ return Collections.unmodifiableCollection(stepHandlers);
+ }
+
+ /** {@inheritDoc} */
+ public void clearStepHandlers() {
+ stepHandlers.clear();
+ }
+
+ /** {@inheritDoc} */
+ public void addEventHandler(
+ final EventHandler handler,
+ final double maxCheckInterval,
+ final double convergence,
+ final int maxIterationCount) {
+ addEventHandler(
+ handler,
+ maxCheckInterval,
+ convergence,
+ maxIterationCount,
+ new BracketingNthOrderBrentSolver(convergence, 5));
+ }
+
+ /** {@inheritDoc} */
+ public void addEventHandler(
+ final EventHandler handler,
+ final double maxCheckInterval,
+ final double convergence,
+ final int maxIterationCount,
+ final UnivariateSolver solver) {
+ eventsStates.add(
+ new EventState(handler, maxCheckInterval, convergence, maxIterationCount, solver));
+ }
+
+ /** {@inheritDoc} */
+ public Collection<EventHandler> getEventHandlers() {
+ final List<EventHandler> list = new ArrayList<EventHandler>(eventsStates.size());
+ for (EventState state : eventsStates) {
+ list.add(state.getEventHandler());
+ }
+ return Collections.unmodifiableCollection(list);
+ }
+
+ /** {@inheritDoc} */
+ public void clearEventHandlers() {
+ eventsStates.clear();
+ }
+
+ /** {@inheritDoc} */
+ public double getCurrentStepStart() {
+ return stepStart;
+ }
+
+ /** {@inheritDoc} */
+ public double getCurrentSignedStepsize() {
+ return stepSize;
+ }
+
+ /** {@inheritDoc} */
+ public void setMaxEvaluations(int maxEvaluations) {
+ evaluations =
+ evaluations.withMaximalCount(
+ (maxEvaluations < 0) ? Integer.MAX_VALUE : maxEvaluations);
+ }
+
+ /** {@inheritDoc} */
+ public int getMaxEvaluations() {
+ return evaluations.getMaximalCount();
+ }
+
+ /** {@inheritDoc} */
+ public int getEvaluations() {
+ return evaluations.getCount();
+ }
+
+ /**
+ * Prepare the start of an integration.
+ *
+ * @param t0 start value of the independent <i>time</i> variable
+ * @param y0 array containing the start value of the state vector
+ * @param t target time for the integration
+ */
+ protected void initIntegration(final double t0, final double[] y0, final double t) {
+
+ evaluations = evaluations.withStart(0);
+
+ for (final EventState state : eventsStates) {
+ state.setExpandable(expandable);
+ state.getEventHandler().init(t0, y0, t);
+ }
+
+ for (StepHandler handler : stepHandlers) {
+ handler.init(t0, y0, t);
+ }
+
+ setStateInitialized(false);
+ }
+
+ /**
+ * Set the equations.
+ *
+ * @param equations equations to set
+ */
+ protected void setEquations(final ExpandableStatefulODE equations) {
+ this.expandable = equations;
+ }
+
+ /**
+ * Get the differential equations to integrate.
+ *
+ * @return differential equations to integrate
+ * @since 3.2
+ */
+ protected ExpandableStatefulODE getExpandable() {
+ return expandable;
+ }
+
+ /**
+ * Get the evaluations counter.
+ *
+ * @return evaluations counter
+ * @since 3.2
+ * @deprecated as of 3.6 replaced with {@link #getCounter()}
+ */
+ @Deprecated
+ protected org.apache.commons.math3.util.Incrementor getEvaluationsCounter() {
+ return org.apache.commons.math3.util.Incrementor.wrap(evaluations);
+ }
+
+ /**
+ * Get the evaluations counter.
+ *
+ * @return evaluations counter
+ * @since 3.6
+ */
+ protected IntegerSequence.Incrementor getCounter() {
+ return evaluations;
+ }
+
+ /** {@inheritDoc} */
+ public double integrate(
+ final FirstOrderDifferentialEquations equations,
+ final double t0,
+ final double[] y0,
+ final double t,
+ final double[] y)
+ throws DimensionMismatchException,
+ NumberIsTooSmallException,
+ MaxCountExceededException,
+ NoBracketingException {
+
+ if (y0.length != equations.getDimension()) {
+ throw new DimensionMismatchException(y0.length, equations.getDimension());
+ }
+ if (y.length != equations.getDimension()) {
+ throw new DimensionMismatchException(y.length, equations.getDimension());
+ }
+
+ // prepare expandable stateful equations
+ final ExpandableStatefulODE expandableODE = new ExpandableStatefulODE(equations);
+ expandableODE.setTime(t0);
+ expandableODE.setPrimaryState(y0);
+
+ // perform integration
+ integrate(expandableODE, t);
+
+ // extract results back from the stateful equations
+ System.arraycopy(expandableODE.getPrimaryState(), 0, y, 0, y.length);
+ return expandableODE.getTime();
+ }
+
+ /**
+ * Integrate a set of differential equations up to the given time.
+ *
+ * <p>This method solves an Initial Value Problem (IVP).
+ *
+ * <p>The set of differential equations is composed of a main set, which can be extended by some
+ * sets of secondary equations. The set of equations must be already set up with initial time
+ * and partial states. At integration completion, the final time and partial states will be
+ * available in the same object.
+ *
+ * <p>Since this method stores some internal state variables made available in its public
+ * interface during integration ({@link #getCurrentSignedStepsize()}), it is <em>not</em>
+ * thread-safe.
+ *
+ * @param equations complete set of differential equations to integrate
+ * @param t target time for the integration (can be set to a value smaller than <code>t0</code>
+ * for backward integration)
+ * @exception NumberIsTooSmallException if integration step is too small
+ * @throws DimensionMismatchException if the dimension of the complete state does not match the
+ * complete equations sets dimension
+ * @exception MaxCountExceededException if the number of functions evaluations is exceeded
+ * @exception NoBracketingException if the location of an event cannot be bracketed
+ */
+ public abstract void integrate(ExpandableStatefulODE equations, double t)
+ throws NumberIsTooSmallException,
+ DimensionMismatchException,
+ MaxCountExceededException,
+ NoBracketingException;
+
+ /**
+ * Compute the derivatives and check the number of evaluations.
+ *
+ * @param t current value of the independent <I>time</I> variable
+ * @param y array containing the current value of the state vector
+ * @param yDot placeholder array where to put the time derivative of the state vector
+ * @exception MaxCountExceededException if the number of functions evaluations is exceeded
+ * @exception DimensionMismatchException if arrays dimensions do not match equations settings
+ * @exception NullPointerException if the ODE equations have not been set (i.e. if this method
+ * is called outside of a call to {@link #integrate(ExpandableStatefulODE, double)} or
+ * {@link #integrate(FirstOrderDifferentialEquations, double, double[], double, double[])})
+ */
+ public void computeDerivatives(final double t, final double[] y, final double[] yDot)
+ throws MaxCountExceededException, DimensionMismatchException, NullPointerException {
+ evaluations.increment();
+ expandable.computeDerivatives(t, y, yDot);
+ }
+
+ /**
+ * Set the stateInitialized flag.
+ *
+ * <p>This method must be called by integrators with the value {@code false} before they start
+ * integration, so a proper lazy initialization is done automatically on the first step.
+ *
+ * @param stateInitialized new value for the flag
+ * @since 2.2
+ */
+ protected void setStateInitialized(final boolean stateInitialized) {
+ this.statesInitialized = stateInitialized;
+ }
+
+ /**
+ * Accept a step, triggering events and step handlers.
+ *
+ * @param interpolator step interpolator
+ * @param y state vector at step end time, must be reset if an event asks for resetting or if an
+ * events stops integration during the step
+ * @param yDot placeholder array where to put the time derivative of the state vector
+ * @param tEnd final integration time
+ * @return time at end of step
+ * @exception MaxCountExceededException if the interpolator throws one because the number of
+ * functions evaluations is exceeded
+ * @exception NoBracketingException if the location of an event cannot be bracketed
+ * @exception DimensionMismatchException if arrays dimensions do not match equations settings
+ * @since 2.2
+ */
+ protected double acceptStep(
+ final AbstractStepInterpolator interpolator,
+ final double[] y,
+ final double[] yDot,
+ final double tEnd)
+ throws MaxCountExceededException, DimensionMismatchException, NoBracketingException {
+
+ double previousT = interpolator.getGlobalPreviousTime();
+ final double currentT = interpolator.getGlobalCurrentTime();
+
+ // initialize the events states if needed
+ if (!statesInitialized) {
+ for (EventState state : eventsStates) {
+ state.reinitializeBegin(interpolator);
+ }
+ statesInitialized = true;
+ }
+
+ // search for next events that may occur during the step
+ final int orderingSign = interpolator.isForward() ? +1 : -1;
+ SortedSet<EventState> occurringEvents =
+ new TreeSet<EventState>(
+ new Comparator<EventState>() {
+
+ /** {@inheritDoc} */
+ public int compare(EventState es0, EventState es1) {
+ return orderingSign
+ * Double.compare(es0.getEventTime(), es1.getEventTime());
+ }
+ });
+
+ for (final EventState state : eventsStates) {
+ if (state.evaluateStep(interpolator)) {
+ // the event occurs during the current step
+ occurringEvents.add(state);
+ }
+ }
+
+ while (!occurringEvents.isEmpty()) {
+
+ // handle the chronologically first event
+ final Iterator<EventState> iterator = occurringEvents.iterator();
+ final EventState currentEvent = iterator.next();
+ iterator.remove();
+
+ // restrict the interpolator to the first part of the step, up to the event
+ final double eventT = currentEvent.getEventTime();
+ interpolator.setSoftPreviousTime(previousT);
+ interpolator.setSoftCurrentTime(eventT);
+
+ // get state at event time
+ interpolator.setInterpolatedTime(eventT);
+ final double[] eventYComplete = new double[y.length];
+ expandable
+ .getPrimaryMapper()
+ .insertEquationData(interpolator.getInterpolatedState(), eventYComplete);
+ int index = 0;
+ for (EquationsMapper secondary : expandable.getSecondaryMappers()) {
+ secondary.insertEquationData(
+ interpolator.getInterpolatedSecondaryState(index++), eventYComplete);
+ }
+
+ // advance all event states to current time
+ for (final EventState state : eventsStates) {
+ state.stepAccepted(eventT, eventYComplete);
+ isLastStep = isLastStep || state.stop();
+ }
+
+ // handle the first part of the step, up to the event
+ for (final StepHandler handler : stepHandlers) {
+ handler.handleStep(interpolator, isLastStep);
+ }
+
+ if (isLastStep) {
+ // the event asked to stop integration
+ System.arraycopy(eventYComplete, 0, y, 0, y.length);
+ return eventT;
+ }
+
+ boolean needReset = false;
+ resetOccurred = false;
+ needReset = currentEvent.reset(eventT, eventYComplete);
+ if (needReset) {
+ // some event handler has triggered changes that
+ // invalidate the derivatives, we need to recompute them
+ interpolator.setInterpolatedTime(eventT);
+ System.arraycopy(eventYComplete, 0, y, 0, y.length);
+ computeDerivatives(eventT, y, yDot);
+ resetOccurred = true;
+ return eventT;
+ }
+
+ // prepare handling of the remaining part of the step
+ previousT = eventT;
+ interpolator.setSoftPreviousTime(eventT);
+ interpolator.setSoftCurrentTime(currentT);
+
+ // check if the same event occurs again in the remaining part of the step
+ if (currentEvent.evaluateStep(interpolator)) {
+ // the event occurs during the current step
+ occurringEvents.add(currentEvent);
+ }
+ }
+
+ // last part of the step, after the last event
+ interpolator.setInterpolatedTime(currentT);
+ final double[] currentY = new double[y.length];
+ expandable
+ .getPrimaryMapper()
+ .insertEquationData(interpolator.getInterpolatedState(), currentY);
+ int index = 0;
+ for (EquationsMapper secondary : expandable.getSecondaryMappers()) {
+ secondary.insertEquationData(
+ interpolator.getInterpolatedSecondaryState(index++), currentY);
+ }
+ for (final EventState state : eventsStates) {
+ state.stepAccepted(currentT, currentY);
+ isLastStep = isLastStep || state.stop();
+ }
+ isLastStep = isLastStep || Precision.equals(currentT, tEnd, 1);
+
+ // handle the remaining part of the step, after all events if any
+ for (StepHandler handler : stepHandlers) {
+ handler.handleStep(interpolator, isLastStep);
+ }
+
+ return currentT;
+ }
+
+ /**
+ * Check the integration span.
+ *
+ * @param equations set of differential equations
+ * @param t target time for the integration
+ * @exception NumberIsTooSmallException if integration span is too small
+ * @exception DimensionMismatchException if adaptive step size integrators tolerance arrays
+ * dimensions are not compatible with equations settings
+ */
+ protected void sanityChecks(final ExpandableStatefulODE equations, final double t)
+ throws NumberIsTooSmallException, DimensionMismatchException {
+
+ final double threshold =
+ 1000
+ * FastMath.ulp(
+ FastMath.max(FastMath.abs(equations.getTime()), FastMath.abs(t)));
+ final double dt = FastMath.abs(equations.getTime() - t);
+ if (dt <= threshold) {
+ throw new NumberIsTooSmallException(
+ LocalizedFormats.TOO_SMALL_INTEGRATION_INTERVAL, dt, threshold, false);
+ }
+ }
+}
diff --git a/src/main/java/org/apache/commons/math3/ode/AbstractParameterizable.java b/src/main/java/org/apache/commons/math3/ode/AbstractParameterizable.java
new file mode 100644
index 0000000..8b0a27e
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/ode/AbstractParameterizable.java
@@ -0,0 +1,81 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math3.ode;
+
+import java.util.ArrayList;
+import java.util.Collection;
+
+/**
+ * This abstract class provides boilerplate parameters list.
+ *
+ * @since 3.0
+ */
+public abstract class AbstractParameterizable implements Parameterizable {
+
+ /** List of the parameters names. */
+ private final Collection<String> parametersNames;
+
+ /**
+ * Simple constructor.
+ *
+ * @param names names of the supported parameters
+ */
+ protected AbstractParameterizable(final String... names) {
+ parametersNames = new ArrayList<String>();
+ for (final String name : names) {
+ parametersNames.add(name);
+ }
+ }
+
+ /**
+ * Simple constructor.
+ *
+ * @param names names of the supported parameters
+ */
+ protected AbstractParameterizable(final Collection<String> names) {
+ parametersNames = new ArrayList<String>();
+ parametersNames.addAll(names);
+ }
+
+ /** {@inheritDoc} */
+ public Collection<String> getParametersNames() {
+ return parametersNames;
+ }
+
+ /** {@inheritDoc} */
+ public boolean isSupported(final String name) {
+ for (final String supportedName : parametersNames) {
+ if (supportedName.equals(name)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Check if a parameter is supported and throw an IllegalArgumentException if not.
+ *
+ * @param name name of the parameter to check
+ * @exception UnknownParameterException if the parameter is not supported
+ * @see #isSupported(String)
+ */
+ public void complainIfNotSupported(final String name) throws UnknownParameterException {
+ if (!isSupported(name)) {
+ throw new UnknownParameterException(name);
+ }
+ }
+}
diff --git a/src/main/java/org/apache/commons/math3/ode/ContinuousOutputFieldModel.java b/src/main/java/org/apache/commons/math3/ode/ContinuousOutputFieldModel.java
new file mode 100644
index 0000000..e6950d2
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/ode/ContinuousOutputFieldModel.java
@@ -0,0 +1,360 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math3.ode;
+
+import org.apache.commons.math3.RealFieldElement;
+import org.apache.commons.math3.exception.DimensionMismatchException;
+import org.apache.commons.math3.exception.MathIllegalArgumentException;
+import org.apache.commons.math3.exception.MaxCountExceededException;
+import org.apache.commons.math3.exception.util.LocalizedFormats;
+import org.apache.commons.math3.ode.sampling.FieldStepHandler;
+import org.apache.commons.math3.ode.sampling.FieldStepInterpolator;
+import org.apache.commons.math3.util.FastMath;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * This class stores all information provided by an ODE integrator during the integration process
+ * and build a continuous model of the solution from this.
+ *
+ * <p>This class act as a step handler from the integrator point of view. It is called iteratively
+ * during the integration process and stores a copy of all steps information in a sorted collection
+ * for later use. Once the integration process is over, the user can use the {@link
+ * #getInterpolatedState(RealFieldElement) getInterpolatedState} method to retrieve this information
+ * at any time. It is important to wait for the integration to be over before attempting to call
+ * {@link #getInterpolatedState(RealFieldElement)} because some internal variables are set only once
+ * the last step has been handled.
+ *
+ * <p>This is useful for example if the main loop of the user application should remain independent
+ * from the integration process or if one needs to mimic the behaviour of an analytical model
+ * despite a numerical model is used (i.e. one needs the ability to get the model value at any time
+ * or to navigate through the data).
+ *
+ * <p>If problem modeling is done with several separate integration phases for contiguous intervals,
+ * the same ContinuousOutputModel can be used as step handler for all integration phases as long as
+ * they are performed in order and in the same direction. As an example, one can extrapolate the
+ * trajectory of a satellite with one model (i.e. one set of differential equations) up to the
+ * beginning of a maneuver, use another more complex model including thrusters modeling and accurate
+ * attitude control during the maneuver, and revert to the first model after the end of the
+ * maneuver. If the same continuous output model handles the steps of all integration phases, the
+ * user do not need to bother when the maneuver begins or ends, he has all the data available in a
+ * transparent manner.
+ *
+ * <p>One should be aware that the amount of data stored in a ContinuousOutputFieldModel instance
+ * can be important if the state vector is large, if the integration interval is long or if the
+ * steps are small (which can result from small tolerance settings in {@link
+ * org.apache.commons.math3.ode.nonstiff.AdaptiveStepsizeFieldIntegrator adaptive step size
+ * integrators}).
+ *
+ * @see FieldStepHandler
+ * @see FieldStepInterpolator
+ * @param <T> the type of the field elements
+ * @since 3.6
+ */
+public class ContinuousOutputFieldModel<T extends RealFieldElement<T>>
+ implements FieldStepHandler<T> {
+
+ /** Initial integration time. */
+ private T initialTime;
+
+ /** Final integration time. */
+ private T finalTime;
+
+ /** Integration direction indicator. */
+ private boolean forward;
+
+ /** Current interpolator index. */
+ private int index;
+
+ /** Steps table. */
+ private List<FieldStepInterpolator<T>> steps;
+
+ /** Simple constructor. Build an empty continuous output model. */
+ public ContinuousOutputFieldModel() {
+ steps = new ArrayList<FieldStepInterpolator<T>>();
+ initialTime = null;
+ finalTime = null;
+ forward = true;
+ index = 0;
+ }
+
+ /**
+ * Append another model at the end of the instance.
+ *
+ * @param model model to add at the end of the instance
+ * @exception MathIllegalArgumentException if the model to append is not compatible with the
+ * instance (dimension of the state vector, propagation direction, hole between the dates)
+ * @exception DimensionMismatchException if the dimensions of the states or the number of
+ * secondary states do not match
+ * @exception MaxCountExceededException if the number of functions evaluations is exceeded
+ * during step finalization
+ */
+ public void append(final ContinuousOutputFieldModel<T> model)
+ throws MathIllegalArgumentException, MaxCountExceededException {
+
+ if (model.steps.size() == 0) {
+ return;
+ }
+
+ if (steps.size() == 0) {
+ initialTime = model.initialTime;
+ forward = model.forward;
+ } else {
+
+ // safety checks
+ final FieldODEStateAndDerivative<T> s1 = steps.get(0).getPreviousState();
+ final FieldODEStateAndDerivative<T> s2 = model.steps.get(0).getPreviousState();
+ checkDimensionsEquality(s1.getStateDimension(), s2.getStateDimension());
+ checkDimensionsEquality(
+ s1.getNumberOfSecondaryStates(), s2.getNumberOfSecondaryStates());
+ for (int i = 0; i < s1.getNumberOfSecondaryStates(); ++i) {
+ checkDimensionsEquality(
+ s1.getSecondaryStateDimension(i), s2.getSecondaryStateDimension(i));
+ }
+
+ if (forward ^ model.forward) {
+ throw new MathIllegalArgumentException(
+ LocalizedFormats.PROPAGATION_DIRECTION_MISMATCH);
+ }
+
+ final FieldStepInterpolator<T> lastInterpolator = steps.get(index);
+ final T current = lastInterpolator.getCurrentState().getTime();
+ final T previous = lastInterpolator.getPreviousState().getTime();
+ final T step = current.subtract(previous);
+ final T gap = model.getInitialTime().subtract(current);
+ if (gap.abs().subtract(step.abs().multiply(1.0e-3)).getReal() > 0) {
+ throw new MathIllegalArgumentException(
+ LocalizedFormats.HOLE_BETWEEN_MODELS_TIME_RANGES, gap.abs().getReal());
+ }
+ }
+
+ for (FieldStepInterpolator<T> interpolator : model.steps) {
+ steps.add(interpolator);
+ }
+
+ index = steps.size() - 1;
+ finalTime = (steps.get(index)).getCurrentState().getTime();
+ }
+
+ /**
+ * Check dimensions equality.
+ *
+ * @param d1 first dimension
+ * @param d2 second dimansion
+ * @exception DimensionMismatchException if dimensions do not match
+ */
+ private void checkDimensionsEquality(final int d1, final int d2)
+ throws DimensionMismatchException {
+ if (d1 != d2) {
+ throw new DimensionMismatchException(d2, d1);
+ }
+ }
+
+ /** {@inheritDoc} */
+ public void init(final FieldODEStateAndDerivative<T> initialState, final T t) {
+ initialTime = initialState.getTime();
+ finalTime = t;
+ forward = true;
+ index = 0;
+ steps.clear();
+ }
+
+ /**
+ * Handle the last accepted step. A copy of the information provided by the last step is stored
+ * in the instance for later use.
+ *
+ * @param interpolator interpolator for the last accepted step.
+ * @param isLast true if the step is the last one
+ * @exception MaxCountExceededException if the number of functions evaluations is exceeded
+ * during step finalization
+ */
+ public void handleStep(final FieldStepInterpolator<T> interpolator, final boolean isLast)
+ throws MaxCountExceededException {
+
+ if (steps.size() == 0) {
+ initialTime = interpolator.getPreviousState().getTime();
+ forward = interpolator.isForward();
+ }
+
+ steps.add(interpolator);
+
+ if (isLast) {
+ finalTime = interpolator.getCurrentState().getTime();
+ index = steps.size() - 1;
+ }
+ }
+
+ /**
+ * Get the initial integration time.
+ *
+ * @return initial integration time
+ */
+ public T getInitialTime() {
+ return initialTime;
+ }
+
+ /**
+ * Get the final integration time.
+ *
+ * @return final integration time
+ */
+ public T getFinalTime() {
+ return finalTime;
+ }
+
+ /**
+ * Get the state at interpolated time.
+ *
+ * @param time time of the interpolated point
+ * @return state at interpolated time
+ */
+ public FieldODEStateAndDerivative<T> getInterpolatedState(final T time) {
+
+ // initialize the search with the complete steps table
+ int iMin = 0;
+ final FieldStepInterpolator<T> sMin = steps.get(iMin);
+ T tMin =
+ sMin.getPreviousState()
+ .getTime()
+ .add(sMin.getCurrentState().getTime())
+ .multiply(0.5);
+
+ int iMax = steps.size() - 1;
+ final FieldStepInterpolator<T> sMax = steps.get(iMax);
+ T tMax =
+ sMax.getPreviousState()
+ .getTime()
+ .add(sMax.getCurrentState().getTime())
+ .multiply(0.5);
+
+ // handle points outside of the integration interval
+ // or in the first and last step
+ if (locatePoint(time, sMin) <= 0) {
+ index = iMin;
+ return sMin.getInterpolatedState(time);
+ }
+ if (locatePoint(time, sMax) >= 0) {
+ index = iMax;
+ return sMax.getInterpolatedState(time);
+ }
+
+ // reduction of the table slice size
+ while (iMax - iMin > 5) {
+
+ // use the last estimated index as the splitting index
+ final FieldStepInterpolator<T> si = steps.get(index);
+ final int location = locatePoint(time, si);
+ if (location < 0) {
+ iMax = index;
+ tMax =
+ si.getPreviousState()
+ .getTime()
+ .add(si.getCurrentState().getTime())
+ .multiply(0.5);
+ } else if (location > 0) {
+ iMin = index;
+ tMin =
+ si.getPreviousState()
+ .getTime()
+ .add(si.getCurrentState().getTime())
+ .multiply(0.5);
+ } else {
+ // we have found the target step, no need to continue searching
+ return si.getInterpolatedState(time);
+ }
+
+ // compute a new estimate of the index in the reduced table slice
+ final int iMed = (iMin + iMax) / 2;
+ final FieldStepInterpolator<T> sMed = steps.get(iMed);
+ final T tMed =
+ sMed.getPreviousState()
+ .getTime()
+ .add(sMed.getCurrentState().getTime())
+ .multiply(0.5);
+
+ if (tMed.subtract(tMin).abs().subtract(1.0e-6).getReal() < 0
+ || tMax.subtract(tMed).abs().subtract(1.0e-6).getReal() < 0) {
+ // too close to the bounds, we estimate using a simple dichotomy
+ index = iMed;
+ } else {
+ // estimate the index using a reverse quadratic polynomial
+ // (reverse means we have i = P(t), thus allowing to simply
+ // compute index = P(time) rather than solving a quadratic equation)
+ final T d12 = tMax.subtract(tMed);
+ final T d23 = tMed.subtract(tMin);
+ final T d13 = tMax.subtract(tMin);
+ final T dt1 = time.subtract(tMax);
+ final T dt2 = time.subtract(tMed);
+ final T dt3 = time.subtract(tMin);
+ final T iLagrange =
+ dt2.multiply(dt3)
+ .multiply(d23)
+ .multiply(iMax)
+ .subtract(dt1.multiply(dt3).multiply(d13).multiply(iMed))
+ .add(dt1.multiply(dt2).multiply(d12).multiply(iMin))
+ .divide(d12.multiply(d23).multiply(d13));
+ index = (int) FastMath.rint(iLagrange.getReal());
+ }
+
+ // force the next size reduction to be at least one tenth
+ final int low = FastMath.max(iMin + 1, (9 * iMin + iMax) / 10);
+ final int high = FastMath.min(iMax - 1, (iMin + 9 * iMax) / 10);
+ if (index < low) {
+ index = low;
+ } else if (index > high) {
+ index = high;
+ }
+ }
+
+ // now the table slice is very small, we perform an iterative search
+ index = iMin;
+ while (index <= iMax && locatePoint(time, steps.get(index)) > 0) {
+ ++index;
+ }
+
+ return steps.get(index).getInterpolatedState(time);
+ }
+
+ /**
+ * Compare a step interval and a double.
+ *
+ * @param time point to locate
+ * @param interval step interval
+ * @return -1 if the double is before the interval, 0 if it is in the interval, and +1 if it is
+ * after the interval, according to the interval direction
+ */
+ private int locatePoint(final T time, final FieldStepInterpolator<T> interval) {
+ if (forward) {
+ if (time.subtract(interval.getPreviousState().getTime()).getReal() < 0) {
+ return -1;
+ } else if (time.subtract(interval.getCurrentState().getTime()).getReal() > 0) {
+ return +1;
+ } else {
+ return 0;
+ }
+ }
+ if (time.subtract(interval.getPreviousState().getTime()).getReal() > 0) {
+ return -1;
+ } else if (time.subtract(interval.getCurrentState().getTime()).getReal() < 0) {
+ return +1;
+ } else {
+ return 0;
+ }
+ }
+}
diff --git a/src/main/java/org/apache/commons/math3/ode/ContinuousOutputModel.java b/src/main/java/org/apache/commons/math3/ode/ContinuousOutputModel.java
new file mode 100644
index 0000000..6bb637d
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/ode/ContinuousOutputModel.java
@@ -0,0 +1,435 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math3.ode;
+
+import org.apache.commons.math3.exception.DimensionMismatchException;
+import org.apache.commons.math3.exception.MathIllegalArgumentException;
+import org.apache.commons.math3.exception.MaxCountExceededException;
+import org.apache.commons.math3.exception.util.LocalizedFormats;
+import org.apache.commons.math3.ode.sampling.StepHandler;
+import org.apache.commons.math3.ode.sampling.StepInterpolator;
+import org.apache.commons.math3.util.FastMath;
+
+import java.io.Serializable;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * This class stores all information provided by an ODE integrator during the integration process
+ * and build a continuous model of the solution from this.
+ *
+ * <p>This class act as a step handler from the integrator point of view. It is called iteratively
+ * during the integration process and stores a copy of all steps information in a sorted collection
+ * for later use. Once the integration process is over, the user can use the {@link
+ * #setInterpolatedTime setInterpolatedTime} and {@link #getInterpolatedState getInterpolatedState}
+ * to retrieve this information at any time. It is important to wait for the integration to be over
+ * before attempting to call {@link #setInterpolatedTime setInterpolatedTime} because some internal
+ * variables are set only once the last step has been handled.
+ *
+ * <p>This is useful for example if the main loop of the user application should remain independent
+ * from the integration process or if one needs to mimic the behaviour of an analytical model
+ * despite a numerical model is used (i.e. one needs the ability to get the model value at any time
+ * or to navigate through the data).
+ *
+ * <p>If problem modeling is done with several separate integration phases for contiguous intervals,
+ * the same ContinuousOutputModel can be used as step handler for all integration phases as long as
+ * they are performed in order and in the same direction. As an example, one can extrapolate the
+ * trajectory of a satellite with one model (i.e. one set of differential equations) up to the
+ * beginning of a maneuver, use another more complex model including thrusters modeling and accurate
+ * attitude control during the maneuver, and revert to the first model after the end of the
+ * maneuver. If the same continuous output model handles the steps of all integration phases, the
+ * user do not need to bother when the maneuver begins or ends, he has all the data available in a
+ * transparent manner.
+ *
+ * <p>An important feature of this class is that it implements the <code>Serializable</code>
+ * interface. This means that the result of an integration can be serialized and reused later (if
+ * stored into a persistent medium like a filesystem or a database) or elsewhere (if sent to another
+ * application). Only the result of the integration is stored, there is no reference to the
+ * integrated problem by itself.
+ *
+ * <p>One should be aware that the amount of data stored in a ContinuousOutputModel instance can be
+ * important if the state vector is large, if the integration interval is long or if the steps are
+ * small (which can result from small tolerance settings in {@link
+ * org.apache.commons.math3.ode.nonstiff.AdaptiveStepsizeIntegrator adaptive step size
+ * integrators}).
+ *
+ * @see StepHandler
+ * @see StepInterpolator
+ * @since 1.2
+ */
+public class ContinuousOutputModel implements StepHandler, Serializable {
+
+ /** Serializable version identifier */
+ private static final long serialVersionUID = -1417964919405031606L;
+
+ /** Initial integration time. */
+ private double initialTime;
+
+ /** Final integration time. */
+ private double finalTime;
+
+ /** Integration direction indicator. */
+ private boolean forward;
+
+ /** Current interpolator index. */
+ private int index;
+
+ /** Steps table. */
+ private List<StepInterpolator> steps;
+
+ /** Simple constructor. Build an empty continuous output model. */
+ public ContinuousOutputModel() {
+ steps = new ArrayList<StepInterpolator>();
+ initialTime = Double.NaN;
+ finalTime = Double.NaN;
+ forward = true;
+ index = 0;
+ }
+
+ /**
+ * Append another model at the end of the instance.
+ *
+ * @param model model to add at the end of the instance
+ * @exception MathIllegalArgumentException if the model to append is not compatible with the
+ * instance (dimension of the state vector, propagation direction, hole between the dates)
+ * @exception MaxCountExceededException if the number of functions evaluations is exceeded
+ * during step finalization
+ */
+ public void append(final ContinuousOutputModel model)
+ throws MathIllegalArgumentException, MaxCountExceededException {
+
+ if (model.steps.size() == 0) {
+ return;
+ }
+
+ if (steps.size() == 0) {
+ initialTime = model.initialTime;
+ forward = model.forward;
+ } else {
+
+ if (getInterpolatedState().length != model.getInterpolatedState().length) {
+ throw new DimensionMismatchException(
+ model.getInterpolatedState().length, getInterpolatedState().length);
+ }
+
+ if (forward ^ model.forward) {
+ throw new MathIllegalArgumentException(
+ LocalizedFormats.PROPAGATION_DIRECTION_MISMATCH);
+ }
+
+ final StepInterpolator lastInterpolator = steps.get(index);
+ final double current = lastInterpolator.getCurrentTime();
+ final double previous = lastInterpolator.getPreviousTime();
+ final double step = current - previous;
+ final double gap = model.getInitialTime() - current;
+ if (FastMath.abs(gap) > 1.0e-3 * FastMath.abs(step)) {
+ throw new MathIllegalArgumentException(
+ LocalizedFormats.HOLE_BETWEEN_MODELS_TIME_RANGES, FastMath.abs(gap));
+ }
+ }
+
+ for (StepInterpolator interpolator : model.steps) {
+ steps.add(interpolator.copy());
+ }
+
+ index = steps.size() - 1;
+ finalTime = (steps.get(index)).getCurrentTime();
+ }
+
+ /** {@inheritDoc} */
+ public void init(double t0, double[] y0, double t) {
+ initialTime = Double.NaN;
+ finalTime = Double.NaN;
+ forward = true;
+ index = 0;
+ steps.clear();
+ }
+
+ /**
+ * Handle the last accepted step. A copy of the information provided by the last step is stored
+ * in the instance for later use.
+ *
+ * @param interpolator interpolator for the last accepted step.
+ * @param isLast true if the step is the last one
+ * @exception MaxCountExceededException if the number of functions evaluations is exceeded
+ * during step finalization
+ */
+ public void handleStep(final StepInterpolator interpolator, final boolean isLast)
+ throws MaxCountExceededException {
+
+ if (steps.size() == 0) {
+ initialTime = interpolator.getPreviousTime();
+ forward = interpolator.isForward();
+ }
+
+ steps.add(interpolator.copy());
+
+ if (isLast) {
+ finalTime = interpolator.getCurrentTime();
+ index = steps.size() - 1;
+ }
+ }
+
+ /**
+ * Get the initial integration time.
+ *
+ * @return initial integration time
+ */
+ public double getInitialTime() {
+ return initialTime;
+ }
+
+ /**
+ * Get the final integration time.
+ *
+ * @return final integration time
+ */
+ public double getFinalTime() {
+ return finalTime;
+ }
+
+ /**
+ * Get the time of the interpolated point. If {@link #setInterpolatedTime} has not been called,
+ * it returns the final integration time.
+ *
+ * @return interpolation point time
+ */
+ public double getInterpolatedTime() {
+ return steps.get(index).getInterpolatedTime();
+ }
+
+ /**
+ * Set the time of the interpolated point.
+ *
+ * <p>This method should <strong>not</strong> be called before the integration is over because
+ * some internal variables are set only once the last step has been handled.
+ *
+ * <p>Setting the time outside of the integration interval is now allowed, but should be used
+ * with care since the accuracy of the interpolator will probably be very poor far from this
+ * interval. This allowance has been added to simplify implementation of search algorithms near
+ * the interval endpoints.
+ *
+ * <p>Note that each time this method is called, the internal arrays returned in {@link
+ * #getInterpolatedState()}, {@link #getInterpolatedDerivatives()} and {@link
+ * #getInterpolatedSecondaryState(int)} <em>will</em> be overwritten. So if their content must
+ * be preserved across several calls, user must copy them.
+ *
+ * @param time time of the interpolated point
+ * @see #getInterpolatedState()
+ * @see #getInterpolatedDerivatives()
+ * @see #getInterpolatedSecondaryState(int)
+ */
+ public void setInterpolatedTime(final double time) {
+
+ // initialize the search with the complete steps table
+ int iMin = 0;
+ final StepInterpolator sMin = steps.get(iMin);
+ double tMin = 0.5 * (sMin.getPreviousTime() + sMin.getCurrentTime());
+
+ int iMax = steps.size() - 1;
+ final StepInterpolator sMax = steps.get(iMax);
+ double tMax = 0.5 * (sMax.getPreviousTime() + sMax.getCurrentTime());
+
+ // handle points outside of the integration interval
+ // or in the first and last step
+ if (locatePoint(time, sMin) <= 0) {
+ index = iMin;
+ sMin.setInterpolatedTime(time);
+ return;
+ }
+ if (locatePoint(time, sMax) >= 0) {
+ index = iMax;
+ sMax.setInterpolatedTime(time);
+ return;
+ }
+
+ // reduction of the table slice size
+ while (iMax - iMin > 5) {
+
+ // use the last estimated index as the splitting index
+ final StepInterpolator si = steps.get(index);
+ final int location = locatePoint(time, si);
+ if (location < 0) {
+ iMax = index;
+ tMax = 0.5 * (si.getPreviousTime() + si.getCurrentTime());
+ } else if (location > 0) {
+ iMin = index;
+ tMin = 0.5 * (si.getPreviousTime() + si.getCurrentTime());
+ } else {
+ // we have found the target step, no need to continue searching
+ si.setInterpolatedTime(time);
+ return;
+ }
+
+ // compute a new estimate of the index in the reduced table slice
+ final int iMed = (iMin + iMax) / 2;
+ final StepInterpolator sMed = steps.get(iMed);
+ final double tMed = 0.5 * (sMed.getPreviousTime() + sMed.getCurrentTime());
+
+ if ((FastMath.abs(tMed - tMin) < 1e-6) || (FastMath.abs(tMax - tMed) < 1e-6)) {
+ // too close to the bounds, we estimate using a simple dichotomy
+ index = iMed;
+ } else {
+ // estimate the index using a reverse quadratic polynom
+ // (reverse means we have i = P(t), thus allowing to simply
+ // compute index = P(time) rather than solving a quadratic equation)
+ final double d12 = tMax - tMed;
+ final double d23 = tMed - tMin;
+ final double d13 = tMax - tMin;
+ final double dt1 = time - tMax;
+ final double dt2 = time - tMed;
+ final double dt3 = time - tMin;
+ final double iLagrange =
+ ((dt2 * dt3 * d23) * iMax
+ - (dt1 * dt3 * d13) * iMed
+ + (dt1 * dt2 * d12) * iMin)
+ / (d12 * d23 * d13);
+ index = (int) FastMath.rint(iLagrange);
+ }
+
+ // force the next size reduction to be at least one tenth
+ final int low = FastMath.max(iMin + 1, (9 * iMin + iMax) / 10);
+ final int high = FastMath.min(iMax - 1, (iMin + 9 * iMax) / 10);
+ if (index < low) {
+ index = low;
+ } else if (index > high) {
+ index = high;
+ }
+ }
+
+ // now the table slice is very small, we perform an iterative search
+ index = iMin;
+ while ((index <= iMax) && (locatePoint(time, steps.get(index)) > 0)) {
+ ++index;
+ }
+
+ steps.get(index).setInterpolatedTime(time);
+ }
+
+ /**
+ * Get the state vector of the interpolated point.
+ *
+ * <p>The returned vector is a reference to a reused array, so it should not be modified and it
+ * should be copied if it needs to be preserved across several calls to the associated {@link
+ * #setInterpolatedTime(double)} method.
+ *
+ * @return state vector at time {@link #getInterpolatedTime}
+ * @exception MaxCountExceededException if the number of functions evaluations is exceeded
+ * @see #setInterpolatedTime(double)
+ * @see #getInterpolatedDerivatives()
+ * @see #getInterpolatedSecondaryState(int)
+ * @see #getInterpolatedSecondaryDerivatives(int)
+ */
+ public double[] getInterpolatedState() throws MaxCountExceededException {
+ return steps.get(index).getInterpolatedState();
+ }
+
+ /**
+ * Get the derivatives of the state vector of the interpolated point.
+ *
+ * <p>The returned vector is a reference to a reused array, so it should not be modified and it
+ * should be copied if it needs to be preserved across several calls to the associated {@link
+ * #setInterpolatedTime(double)} method.
+ *
+ * @return derivatives of the state vector at time {@link #getInterpolatedTime}
+ * @exception MaxCountExceededException if the number of functions evaluations is exceeded
+ * @see #setInterpolatedTime(double)
+ * @see #getInterpolatedState()
+ * @see #getInterpolatedSecondaryState(int)
+ * @see #getInterpolatedSecondaryDerivatives(int)
+ * @since 3.4
+ */
+ public double[] getInterpolatedDerivatives() throws MaxCountExceededException {
+ return steps.get(index).getInterpolatedDerivatives();
+ }
+
+ /**
+ * Get the interpolated secondary state corresponding to the secondary equations.
+ *
+ * <p>The returned vector is a reference to a reused array, so it should not be modified and it
+ * should be copied if it needs to be preserved across several calls to the associated {@link
+ * #setInterpolatedTime(double)} method.
+ *
+ * @param secondaryStateIndex index of the secondary set, as returned by {@link
+ * org.apache.commons.math3.ode.ExpandableStatefulODE#addSecondaryEquations(
+ * org.apache.commons.math3.ode.SecondaryEquations)
+ * ExpandableStatefulODE.addSecondaryEquations(SecondaryEquations)}
+ * @return interpolated secondary state at the current interpolation date
+ * @see #setInterpolatedTime(double)
+ * @see #getInterpolatedState()
+ * @see #getInterpolatedDerivatives()
+ * @see #getInterpolatedSecondaryDerivatives(int)
+ * @since 3.2
+ * @exception MaxCountExceededException if the number of functions evaluations is exceeded
+ */
+ public double[] getInterpolatedSecondaryState(final int secondaryStateIndex)
+ throws MaxCountExceededException {
+ return steps.get(index).getInterpolatedSecondaryState(secondaryStateIndex);
+ }
+
+ /**
+ * Get the interpolated secondary derivatives corresponding to the secondary equations.
+ *
+ * <p>The returned vector is a reference to a reused array, so it should not be modified and it
+ * should be copied if it needs to be preserved across several calls to the associated {@link
+ * #setInterpolatedTime(double)} method.
+ *
+ * @param secondaryStateIndex index of the secondary set, as returned by {@link
+ * org.apache.commons.math3.ode.ExpandableStatefulODE#addSecondaryEquations(
+ * org.apache.commons.math3.ode.SecondaryEquations)
+ * ExpandableStatefulODE.addSecondaryEquations(SecondaryEquations)}
+ * @return interpolated secondary derivatives at the current interpolation date
+ * @see #setInterpolatedTime(double)
+ * @see #getInterpolatedState()
+ * @see #getInterpolatedDerivatives()
+ * @see #getInterpolatedSecondaryState(int)
+ * @since 3.4
+ * @exception MaxCountExceededException if the number of functions evaluations is exceeded
+ */
+ public double[] getInterpolatedSecondaryDerivatives(final int secondaryStateIndex)
+ throws MaxCountExceededException {
+ return steps.get(index).getInterpolatedSecondaryDerivatives(secondaryStateIndex);
+ }
+
+ /**
+ * Compare a step interval and a double.
+ *
+ * @param time point to locate
+ * @param interval step interval
+ * @return -1 if the double is before the interval, 0 if it is in the interval, and +1 if it is
+ * after the interval, according to the interval direction
+ */
+ private int locatePoint(final double time, final StepInterpolator interval) {
+ if (forward) {
+ if (time < interval.getPreviousTime()) {
+ return -1;
+ } else if (time > interval.getCurrentTime()) {
+ return +1;
+ } else {
+ return 0;
+ }
+ }
+ if (time > interval.getPreviousTime()) {
+ return -1;
+ } else if (time < interval.getCurrentTime()) {
+ return +1;
+ } else {
+ return 0;
+ }
+ }
+}
diff --git a/src/main/java/org/apache/commons/math3/ode/EquationsMapper.java b/src/main/java/org/apache/commons/math3/ode/EquationsMapper.java
new file mode 100644
index 0000000..c123cec
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/ode/EquationsMapper.java
@@ -0,0 +1,106 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math3.ode;
+
+import org.apache.commons.math3.exception.DimensionMismatchException;
+
+import java.io.Serializable;
+
+/**
+ * Class mapping the part of a complete state or derivative that pertains to a specific differential
+ * equation.
+ *
+ * <p>Instances of this class are guaranteed to be immutable.
+ *
+ * @see SecondaryEquations
+ * @since 3.0
+ */
+public class EquationsMapper implements Serializable {
+
+ /** Serializable UID. */
+ private static final long serialVersionUID = 20110925L;
+
+ /** Index of the first equation element in complete state arrays. */
+ private final int firstIndex;
+
+ /** Dimension of the secondary state parameters. */
+ private final int dimension;
+
+ /**
+ * simple constructor.
+ *
+ * @param firstIndex index of the first equation element in complete state arrays
+ * @param dimension dimension of the secondary state parameters
+ */
+ public EquationsMapper(final int firstIndex, final int dimension) {
+ this.firstIndex = firstIndex;
+ this.dimension = dimension;
+ }
+
+ /**
+ * Get the index of the first equation element in complete state arrays.
+ *
+ * @return index of the first equation element in complete state arrays
+ */
+ public int getFirstIndex() {
+ return firstIndex;
+ }
+
+ /**
+ * Get the dimension of the secondary state parameters.
+ *
+ * @return dimension of the secondary state parameters
+ */
+ public int getDimension() {
+ return dimension;
+ }
+
+ /**
+ * Extract equation data from a complete state or derivative array.
+ *
+ * @param complete complete state or derivative array from which equation data should be
+ * retrieved
+ * @param equationData placeholder where to put equation data
+ * @throws DimensionMismatchException if the dimension of the equation data does not match the
+ * mapper dimension
+ */
+ public void extractEquationData(double[] complete, double[] equationData)
+ throws DimensionMismatchException {
+ if (equationData.length != dimension) {
+ throw new DimensionMismatchException(equationData.length, dimension);
+ }
+ System.arraycopy(complete, firstIndex, equationData, 0, dimension);
+ }
+
+ /**
+ * Insert equation data into a complete state or derivative array.
+ *
+ * @param equationData equation data to be inserted into the complete array
+ * @param complete placeholder where to put equation data (only the part corresponding to the
+ * equation will be overwritten)
+ * @throws DimensionMismatchException if the dimension of the equation data does not match the
+ * mapper dimension
+ */
+ public void insertEquationData(double[] equationData, double[] complete)
+ throws DimensionMismatchException {
+ if (equationData.length != dimension) {
+ throw new DimensionMismatchException(equationData.length, dimension);
+ }
+ System.arraycopy(equationData, 0, complete, firstIndex, dimension);
+ }
+}
diff --git a/src/main/java/org/apache/commons/math3/ode/ExpandableStatefulODE.java b/src/main/java/org/apache/commons/math3/ode/ExpandableStatefulODE.java
new file mode 100644
index 0000000..e4b7ef5
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/ode/ExpandableStatefulODE.java
@@ -0,0 +1,349 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math3.ode;
+
+import org.apache.commons.math3.exception.DimensionMismatchException;
+import org.apache.commons.math3.exception.MaxCountExceededException;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * This class represents a combined set of first order differential equations, with at least a
+ * primary set of equations expandable by some sets of secondary equations.
+ *
+ * <p>One typical use case is the computation of the Jacobian matrix for some ODE. In this case, the
+ * primary set of equations corresponds to the raw ODE, and we add to this set another bunch of
+ * secondary equations which represent the Jacobian matrix of the primary set.
+ *
+ * <p>We want the integrator to use <em>only</em> the primary set to estimate the errors and hence
+ * the step sizes. It should <em>not</em> use the secondary equations in this computation. The
+ * {@link AbstractIntegrator integrator} will be able to know where the primary set ends and so
+ * where the secondary sets begin.
+ *
+ * @see FirstOrderDifferentialEquations
+ * @see JacobianMatrices
+ * @since 3.0
+ */
+public class ExpandableStatefulODE {
+
+ /** Primary differential equation. */
+ private final FirstOrderDifferentialEquations primary;
+
+ /** Mapper for primary equation. */
+ private final EquationsMapper primaryMapper;
+
+ /** Time. */
+ private double time;
+
+ /** State. */
+ private final double[] primaryState;
+
+ /** State derivative. */
+ private final double[] primaryStateDot;
+
+ /** Components of the expandable ODE. */
+ private List<SecondaryComponent> components;
+
+ /**
+ * Build an expandable set from its primary ODE set.
+ *
+ * @param primary the primary set of differential equations to be integrated.
+ */
+ public ExpandableStatefulODE(final FirstOrderDifferentialEquations primary) {
+ final int n = primary.getDimension();
+ this.primary = primary;
+ this.primaryMapper = new EquationsMapper(0, n);
+ this.time = Double.NaN;
+ this.primaryState = new double[n];
+ this.primaryStateDot = new double[n];
+ this.components = new ArrayList<ExpandableStatefulODE.SecondaryComponent>();
+ }
+
+ /**
+ * Get the primary set of differential equations.
+ *
+ * @return primary set of differential equations
+ */
+ public FirstOrderDifferentialEquations getPrimary() {
+ return primary;
+ }
+
+ /**
+ * Return the dimension of the complete set of equations.
+ *
+ * <p>The complete set of equations correspond to the primary set plus all secondary sets.
+ *
+ * @return dimension of the complete set of equations
+ */
+ public int getTotalDimension() {
+ if (components.isEmpty()) {
+ // there are no secondary equations, the complete set is limited to the primary set
+ return primaryMapper.getDimension();
+ } else {
+ // there are secondary equations, the complete set ends after the last set
+ final EquationsMapper lastMapper = components.get(components.size() - 1).mapper;
+ return lastMapper.getFirstIndex() + lastMapper.getDimension();
+ }
+ }
+
+ /**
+ * Get the current time derivative of the complete state vector.
+ *
+ * @param t current value of the independent <I>time</I> variable
+ * @param y array containing the current value of the complete state vector
+ * @param yDot placeholder array where to put the time derivative of the complete state vector
+ * @exception MaxCountExceededException if the number of functions evaluations is exceeded
+ * @exception DimensionMismatchException if arrays dimensions do not match equations settings
+ */
+ public void computeDerivatives(final double t, final double[] y, final double[] yDot)
+ throws MaxCountExceededException, DimensionMismatchException {
+
+ // compute derivatives of the primary equations
+ primaryMapper.extractEquationData(y, primaryState);
+ primary.computeDerivatives(t, primaryState, primaryStateDot);
+
+ // Add contribution for secondary equations
+ for (final SecondaryComponent component : components) {
+ component.mapper.extractEquationData(y, component.state);
+ component.equation.computeDerivatives(
+ t, primaryState, primaryStateDot, component.state, component.stateDot);
+ component.mapper.insertEquationData(component.stateDot, yDot);
+ }
+
+ primaryMapper.insertEquationData(primaryStateDot, yDot);
+ }
+
+ /**
+ * Add a set of secondary equations to be integrated along with the primary set.
+ *
+ * @param secondary secondary equations set
+ * @return index of the secondary equation in the expanded state
+ */
+ public int addSecondaryEquations(final SecondaryEquations secondary) {
+
+ final int firstIndex;
+ if (components.isEmpty()) {
+ // lazy creation of the components list
+ components = new ArrayList<ExpandableStatefulODE.SecondaryComponent>();
+ firstIndex = primary.getDimension();
+ } else {
+ final SecondaryComponent last = components.get(components.size() - 1);
+ firstIndex = last.mapper.getFirstIndex() + last.mapper.getDimension();
+ }
+
+ components.add(new SecondaryComponent(secondary, firstIndex));
+
+ return components.size() - 1;
+ }
+
+ /**
+ * Get an equations mapper for the primary equations set.
+ *
+ * @return mapper for the primary set
+ * @see #getSecondaryMappers()
+ */
+ public EquationsMapper getPrimaryMapper() {
+ return primaryMapper;
+ }
+
+ /**
+ * Get the equations mappers for the secondary equations sets.
+ *
+ * @return equations mappers for the secondary equations sets
+ * @see #getPrimaryMapper()
+ */
+ public EquationsMapper[] getSecondaryMappers() {
+ final EquationsMapper[] mappers = new EquationsMapper[components.size()];
+ for (int i = 0; i < mappers.length; ++i) {
+ mappers[i] = components.get(i).mapper;
+ }
+ return mappers;
+ }
+
+ /**
+ * Set current time.
+ *
+ * @param time current time
+ */
+ public void setTime(final double time) {
+ this.time = time;
+ }
+
+ /**
+ * Get current time.
+ *
+ * @return current time
+ */
+ public double getTime() {
+ return time;
+ }
+
+ /**
+ * Set primary part of the current state.
+ *
+ * @param primaryState primary part of the current state
+ * @throws DimensionMismatchException if the dimension of the array does not match the primary
+ * set
+ */
+ public void setPrimaryState(final double[] primaryState) throws DimensionMismatchException {
+
+ // safety checks
+ if (primaryState.length != this.primaryState.length) {
+ throw new DimensionMismatchException(primaryState.length, this.primaryState.length);
+ }
+
+ // set the data
+ System.arraycopy(primaryState, 0, this.primaryState, 0, primaryState.length);
+ }
+
+ /**
+ * Get primary part of the current state.
+ *
+ * @return primary part of the current state
+ */
+ public double[] getPrimaryState() {
+ return primaryState.clone();
+ }
+
+ /**
+ * Get primary part of the current state derivative.
+ *
+ * @return primary part of the current state derivative
+ */
+ public double[] getPrimaryStateDot() {
+ return primaryStateDot.clone();
+ }
+
+ /**
+ * Set secondary part of the current state.
+ *
+ * @param index index of the part to set as returned by {@link
+ * #addSecondaryEquations(SecondaryEquations)}
+ * @param secondaryState secondary part of the current state
+ * @throws DimensionMismatchException if the dimension of the partial state does not match the
+ * selected equations set dimension
+ */
+ public void setSecondaryState(final int index, final double[] secondaryState)
+ throws DimensionMismatchException {
+
+ // get either the secondary state
+ double[] localArray = components.get(index).state;
+
+ // safety checks
+ if (secondaryState.length != localArray.length) {
+ throw new DimensionMismatchException(secondaryState.length, localArray.length);
+ }
+
+ // set the data
+ System.arraycopy(secondaryState, 0, localArray, 0, secondaryState.length);
+ }
+
+ /**
+ * Get secondary part of the current state.
+ *
+ * @param index index of the part to set as returned by {@link
+ * #addSecondaryEquations(SecondaryEquations)}
+ * @return secondary part of the current state
+ */
+ public double[] getSecondaryState(final int index) {
+ return components.get(index).state.clone();
+ }
+
+ /**
+ * Get secondary part of the current state derivative.
+ *
+ * @param index index of the part to set as returned by {@link
+ * #addSecondaryEquations(SecondaryEquations)}
+ * @return secondary part of the current state derivative
+ */
+ public double[] getSecondaryStateDot(final int index) {
+ return components.get(index).stateDot.clone();
+ }
+
+ /**
+ * Set the complete current state.
+ *
+ * @param completeState complete current state to copy data from
+ * @throws DimensionMismatchException if the dimension of the complete state does not match the
+ * complete equations sets dimension
+ */
+ public void setCompleteState(final double[] completeState) throws DimensionMismatchException {
+
+ // safety checks
+ if (completeState.length != getTotalDimension()) {
+ throw new DimensionMismatchException(completeState.length, getTotalDimension());
+ }
+
+ // set the data
+ primaryMapper.extractEquationData(completeState, primaryState);
+ for (final SecondaryComponent component : components) {
+ component.mapper.extractEquationData(completeState, component.state);
+ }
+ }
+
+ /**
+ * Get the complete current state.
+ *
+ * @return complete current state
+ * @throws DimensionMismatchException if the dimension of the complete state does not match the
+ * complete equations sets dimension
+ */
+ public double[] getCompleteState() throws DimensionMismatchException {
+
+ // allocate complete array
+ double[] completeState = new double[getTotalDimension()];
+
+ // set the data
+ primaryMapper.insertEquationData(primaryState, completeState);
+ for (final SecondaryComponent component : components) {
+ component.mapper.insertEquationData(component.state, completeState);
+ }
+
+ return completeState;
+ }
+
+ /** Components of the compound stateful ODE. */
+ private static class SecondaryComponent {
+
+ /** Secondary differential equation. */
+ private final SecondaryEquations equation;
+
+ /** Mapper between local and complete arrays. */
+ private final EquationsMapper mapper;
+
+ /** State. */
+ private final double[] state;
+
+ /** State derivative. */
+ private final double[] stateDot;
+
+ /**
+ * Simple constructor.
+ *
+ * @param equation secondary differential equation
+ * @param firstIndex index to use for the first element in the complete arrays
+ */
+ SecondaryComponent(final SecondaryEquations equation, final int firstIndex) {
+ final int n = equation.getDimension();
+ this.equation = equation;
+ mapper = new EquationsMapper(firstIndex, n);
+ state = new double[n];
+ stateDot = new double[n];
+ }
+ }
+}
diff --git a/src/main/java/org/apache/commons/math3/ode/FieldEquationsMapper.java b/src/main/java/org/apache/commons/math3/ode/FieldEquationsMapper.java
new file mode 100644
index 0000000..e28f5be
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/ode/FieldEquationsMapper.java
@@ -0,0 +1,220 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math3.ode;
+
+import org.apache.commons.math3.RealFieldElement;
+import org.apache.commons.math3.exception.DimensionMismatchException;
+import org.apache.commons.math3.exception.MathIllegalArgumentException;
+import org.apache.commons.math3.exception.util.LocalizedFormats;
+import org.apache.commons.math3.util.MathArrays;
+
+import java.io.Serializable;
+
+/**
+ * Class mapping the part of a complete state or derivative that pertains to a set of differential
+ * equations.
+ *
+ * <p>Instances of this class are guaranteed to be immutable.
+ *
+ * @see FieldExpandableODE
+ * @param <T> the type of the field elements
+ * @since 3.6
+ */
+public class FieldEquationsMapper<T extends RealFieldElement<T>> implements Serializable {
+
+ /** Serializable UID. */
+ private static final long serialVersionUID = 20151114L;
+
+ /** Start indices of the components. */
+ private final int[] start;
+
+ /**
+ * Create a mapper by adding a new equation to another mapper.
+ *
+ * <p>The new equation will have index {@code mapper.}{@link #getNumberOfEquations()}, or 0 if
+ * {@code mapper} is null.
+ *
+ * @param mapper former mapper, with one equation less (null for first equation)
+ * @param dimension dimension of the equation state vector
+ */
+ FieldEquationsMapper(final FieldEquationsMapper<T> mapper, final int dimension) {
+ final int index = (mapper == null) ? 0 : mapper.getNumberOfEquations();
+ this.start = new int[index + 2];
+ if (mapper == null) {
+ start[0] = 0;
+ } else {
+ System.arraycopy(mapper.start, 0, start, 0, index + 1);
+ }
+ start[index + 1] = start[index] + dimension;
+ }
+
+ /**
+ * Get the number of equations mapped.
+ *
+ * @return number of equations mapped
+ */
+ public int getNumberOfEquations() {
+ return start.length - 1;
+ }
+
+ /**
+ * Return the dimension of the complete set of equations.
+ *
+ * <p>The complete set of equations correspond to the primary set plus all secondary sets.
+ *
+ * @return dimension of the complete set of equations
+ */
+ public int getTotalDimension() {
+ return start[start.length - 1];
+ }
+
+ /**
+ * Map a state to a complete flat array.
+ *
+ * @param state state to map
+ * @return flat array containing the mapped state, including primary and secondary components
+ */
+ public T[] mapState(final FieldODEState<T> state) {
+ final T[] y = MathArrays.buildArray(state.getTime().getField(), getTotalDimension());
+ int index = 0;
+ insertEquationData(index, state.getState(), y);
+ while (++index < getNumberOfEquations()) {
+ insertEquationData(index, state.getSecondaryState(index), y);
+ }
+ return y;
+ }
+
+ /**
+ * Map a state derivative to a complete flat array.
+ *
+ * @param state state to map
+ * @return flat array containing the mapped state derivative, including primary and secondary
+ * components
+ */
+ public T[] mapDerivative(final FieldODEStateAndDerivative<T> state) {
+ final T[] yDot = MathArrays.buildArray(state.getTime().getField(), getTotalDimension());
+ int index = 0;
+ insertEquationData(index, state.getDerivative(), yDot);
+ while (++index < getNumberOfEquations()) {
+ insertEquationData(index, state.getSecondaryDerivative(index), yDot);
+ }
+ return yDot;
+ }
+
+ /**
+ * Map flat arrays to a state and derivative.
+ *
+ * @param t time
+ * @param y state array to map, including primary and secondary components
+ * @param yDot state derivative array to map, including primary and secondary components
+ * @return mapped state
+ * @exception DimensionMismatchException if an array does not match total dimension
+ */
+ public FieldODEStateAndDerivative<T> mapStateAndDerivative(
+ final T t, final T[] y, final T[] yDot) throws DimensionMismatchException {
+
+ if (y.length != getTotalDimension()) {
+ throw new DimensionMismatchException(y.length, getTotalDimension());
+ }
+
+ if (yDot.length != getTotalDimension()) {
+ throw new DimensionMismatchException(yDot.length, getTotalDimension());
+ }
+
+ final int n = getNumberOfEquations();
+ int index = 0;
+ final T[] state = extractEquationData(index, y);
+ final T[] derivative = extractEquationData(index, yDot);
+ if (n < 2) {
+ return new FieldODEStateAndDerivative<T>(t, state, derivative);
+ } else {
+ final T[][] secondaryState = MathArrays.buildArray(t.getField(), n - 1, -1);
+ final T[][] secondaryDerivative = MathArrays.buildArray(t.getField(), n - 1, -1);
+ while (++index < getNumberOfEquations()) {
+ secondaryState[index - 1] = extractEquationData(index, y);
+ secondaryDerivative[index - 1] = extractEquationData(index, yDot);
+ }
+ return new FieldODEStateAndDerivative<T>(
+ t, state, derivative, secondaryState, secondaryDerivative);
+ }
+ }
+
+ /**
+ * Extract equation data from a complete state or derivative array.
+ *
+ * @param index index of the equation, must be between 0 included and {@link
+ * #getNumberOfEquations()} (excluded)
+ * @param complete complete state or derivative array from which equation data should be
+ * retrieved
+ * @return equation data
+ * @exception MathIllegalArgumentException if index is out of range
+ * @exception DimensionMismatchException if complete state has not enough elements
+ */
+ public T[] extractEquationData(final int index, final T[] complete)
+ throws MathIllegalArgumentException, DimensionMismatchException {
+ checkIndex(index);
+ final int begin = start[index];
+ final int end = start[index + 1];
+ if (complete.length < end) {
+ throw new DimensionMismatchException(complete.length, end);
+ }
+ final int dimension = end - begin;
+ final T[] equationData = MathArrays.buildArray(complete[0].getField(), dimension);
+ System.arraycopy(complete, begin, equationData, 0, dimension);
+ return equationData;
+ }
+
+ /**
+ * Insert equation data into a complete state or derivative array.
+ *
+ * @param index index of the equation, must be between 0 included and {@link
+ * #getNumberOfEquations()} (excluded)
+ * @param equationData equation data to be inserted into the complete array
+ * @param complete placeholder where to put equation data (only the part corresponding to the
+ * equation will be overwritten)
+ * @exception DimensionMismatchException if either array has not enough elements
+ */
+ public void insertEquationData(final int index, T[] equationData, T[] complete)
+ throws DimensionMismatchException {
+ checkIndex(index);
+ final int begin = start[index];
+ final int end = start[index + 1];
+ final int dimension = end - begin;
+ if (complete.length < end) {
+ throw new DimensionMismatchException(complete.length, end);
+ }
+ if (equationData.length != dimension) {
+ throw new DimensionMismatchException(equationData.length, dimension);
+ }
+ System.arraycopy(equationData, 0, complete, begin, dimension);
+ }
+
+ /**
+ * Check equation index.
+ *
+ * @param index index of the equation, must be between 0 included and {@link
+ * #getNumberOfEquations()} (excluded)
+ * @exception MathIllegalArgumentException if index is out of range
+ */
+ private void checkIndex(final int index) throws MathIllegalArgumentException {
+ if (index < 0 || index > start.length - 2) {
+ throw new MathIllegalArgumentException(
+ LocalizedFormats.ARGUMENT_OUTSIDE_DOMAIN, index, 0, start.length - 2);
+ }
+ }
+}
diff --git a/src/main/java/org/apache/commons/math3/ode/FieldExpandableODE.java b/src/main/java/org/apache/commons/math3/ode/FieldExpandableODE.java
new file mode 100644
index 0000000..a9f079e
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/ode/FieldExpandableODE.java
@@ -0,0 +1,148 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math3.ode;
+
+import org.apache.commons.math3.RealFieldElement;
+import org.apache.commons.math3.exception.DimensionMismatchException;
+import org.apache.commons.math3.exception.MaxCountExceededException;
+import org.apache.commons.math3.util.MathArrays;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * This class represents a combined set of first order differential equations, with at least a
+ * primary set of equations expandable by some sets of secondary equations.
+ *
+ * <p>One typical use case is the computation of the Jacobian matrix for some ODE. In this case, the
+ * primary set of equations corresponds to the raw ODE, and we add to this set another bunch of
+ * secondary equations which represent the Jacobian matrix of the primary set.
+ *
+ * <p>We want the integrator to use <em>only</em> the primary set to estimate the errors and hence
+ * the step sizes. It should <em>not</em> use the secondary equations in this computation. The
+ * {@link FirstOrderFieldIntegrator integrator} will be able to know where the primary set ends and
+ * so where the secondary sets begin.
+ *
+ * @see FirstOrderFieldDifferentialEquations
+ * @see FieldSecondaryEquations
+ * @param <T> the type of the field elements
+ * @since 3.6
+ */
+public class FieldExpandableODE<T extends RealFieldElement<T>> {
+
+ /** Primary differential equation. */
+ private final FirstOrderFieldDifferentialEquations<T> primary;
+
+ /** Components of the expandable ODE. */
+ private List<FieldSecondaryEquations<T>> components;
+
+ /** Mapper for all equations. */
+ private FieldEquationsMapper<T> mapper;
+
+ /**
+ * Build an expandable set from its primary ODE set.
+ *
+ * @param primary the primary set of differential equations to be integrated.
+ */
+ public FieldExpandableODE(final FirstOrderFieldDifferentialEquations<T> primary) {
+ this.primary = primary;
+ this.components = new ArrayList<FieldSecondaryEquations<T>>();
+ this.mapper = new FieldEquationsMapper<T>(null, primary.getDimension());
+ }
+
+ /**
+ * Get the mapper for the set of equations.
+ *
+ * @return mapper for the set of equations
+ */
+ public FieldEquationsMapper<T> getMapper() {
+ return mapper;
+ }
+
+ /**
+ * Add a set of secondary equations to be integrated along with the primary set.
+ *
+ * @param secondary secondary equations set
+ * @return index of the secondary equation in the expanded state, to be used as the parameter to
+ * {@link FieldODEState#getSecondaryState(int)} and {@link
+ * FieldODEStateAndDerivative#getSecondaryDerivative(int)} (beware index 0 corresponds to
+ * main state, additional states start at 1)
+ */
+ public int addSecondaryEquations(final FieldSecondaryEquations<T> secondary) {
+
+ components.add(secondary);
+ mapper = new FieldEquationsMapper<T>(mapper, secondary.getDimension());
+
+ return components.size();
+ }
+
+ /**
+ * Initialize equations at the start of an ODE integration.
+ *
+ * @param t0 value of the independent <I>time</I> variable at integration start
+ * @param y0 array containing the value of the state vector at integration start
+ * @param finalTime target time for the integration
+ * @exception MaxCountExceededException if the number of functions evaluations is exceeded
+ * @exception DimensionMismatchException if arrays dimensions do not match equations settings
+ */
+ public void init(final T t0, final T[] y0, final T finalTime) {
+
+ // initialize primary equations
+ int index = 0;
+ final T[] primary0 = mapper.extractEquationData(index, y0);
+ primary.init(t0, primary0, finalTime);
+
+ // initialize secondary equations
+ while (++index < mapper.getNumberOfEquations()) {
+ final T[] secondary0 = mapper.extractEquationData(index, y0);
+ components.get(index - 1).init(t0, primary0, secondary0, finalTime);
+ }
+ }
+
+ /**
+ * Get the current time derivative of the complete state vector.
+ *
+ * @param t current value of the independent <I>time</I> variable
+ * @param y array containing the current value of the complete state vector
+ * @return time derivative of the complete state vector
+ * @exception MaxCountExceededException if the number of functions evaluations is exceeded
+ * @exception DimensionMismatchException if arrays dimensions do not match equations settings
+ */
+ public T[] computeDerivatives(final T t, final T[] y)
+ throws MaxCountExceededException, DimensionMismatchException {
+
+ final T[] yDot = MathArrays.buildArray(t.getField(), mapper.getTotalDimension());
+
+ // compute derivatives of the primary equations
+ int index = 0;
+ final T[] primaryState = mapper.extractEquationData(index, y);
+ final T[] primaryStateDot = primary.computeDerivatives(t, primaryState);
+ mapper.insertEquationData(index, primaryStateDot, yDot);
+
+ // Add contribution for secondary equations
+ while (++index < mapper.getNumberOfEquations()) {
+ final T[] componentState = mapper.extractEquationData(index, y);
+ final T[] componentStateDot =
+ components
+ .get(index - 1)
+ .computeDerivatives(t, primaryState, primaryStateDot, componentState);
+ mapper.insertEquationData(index, componentStateDot, yDot);
+ }
+
+ return yDot;
+ }
+}
diff --git a/src/main/java/org/apache/commons/math3/ode/FieldODEState.java b/src/main/java/org/apache/commons/math3/ode/FieldODEState.java
new file mode 100644
index 0000000..baa7c96
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/ode/FieldODEState.java
@@ -0,0 +1,155 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math3.ode;
+
+import org.apache.commons.math3.Field;
+import org.apache.commons.math3.RealFieldElement;
+import org.apache.commons.math3.util.MathArrays;
+
+/**
+ * Container for time, main and secondary state vectors.
+ *
+ * @see FirstOrderFieldDifferentialEquations
+ * @see FieldSecondaryEquations
+ * @see FirstOrderFieldIntegrator
+ * @see FieldODEStateAndDerivative
+ * @param <T> the type of the field elements
+ * @since 3.6
+ */
+public class FieldODEState<T extends RealFieldElement<T>> {
+
+ /** Time. */
+ private final T time;
+
+ /** Main state at time. */
+ private final T[] state;
+
+ /** Secondary state at time. */
+ private final T[][] secondaryState;
+
+ /**
+ * Simple constructor.
+ *
+ * <p>Calling this constructor is equivalent to call {@link #FieldODEState(RealFieldElement,
+ * RealFieldElement[], RealFieldElement[][]) FieldODEState(time, state, null)}.
+ *
+ * @param time time
+ * @param state state at time
+ */
+ public FieldODEState(T time, T[] state) {
+ this(time, state, null);
+ }
+
+ /**
+ * Simple constructor.
+ *
+ * @param time time
+ * @param state state at time
+ * @param secondaryState state at time (may be null)
+ */
+ public FieldODEState(T time, T[] state, T[][] secondaryState) {
+ this.time = time;
+ this.state = state.clone();
+ this.secondaryState = copy(time.getField(), secondaryState);
+ }
+
+ /**
+ * Copy a two-dimensions array.
+ *
+ * @param field field to which elements belong
+ * @param original original array (may be null)
+ * @return copied array or null if original array was null
+ */
+ protected T[][] copy(final Field<T> field, final T[][] original) {
+
+ // special handling of null arrays
+ if (original == null) {
+ return null;
+ }
+
+ // allocate the array
+ final T[][] copied = MathArrays.buildArray(field, original.length, -1);
+
+ // copy content
+ for (int i = 0; i < original.length; ++i) {
+ copied[i] = original[i].clone();
+ }
+
+ return copied;
+ }
+
+ /**
+ * Get time.
+ *
+ * @return time
+ */
+ public T getTime() {
+ return time;
+ }
+
+ /**
+ * Get main state dimension.
+ *
+ * @return main state dimension
+ */
+ public int getStateDimension() {
+ return state.length;
+ }
+
+ /**
+ * Get main state at time.
+ *
+ * @return main state at time
+ */
+ public T[] getState() {
+ return state.clone();
+ }
+
+ /**
+ * Get the number of secondary states.
+ *
+ * @return number of secondary states.
+ */
+ public int getNumberOfSecondaryStates() {
+ return secondaryState == null ? 0 : secondaryState.length;
+ }
+
+ /**
+ * Get secondary state dimension.
+ *
+ * @param index index of the secondary set as returned by {@link
+ * FieldExpandableODE#addSecondaryEquations(FieldSecondaryEquations)} (beware index 0
+ * corresponds to main state, additional states start at 1)
+ * @return secondary state dimension
+ */
+ public int getSecondaryStateDimension(final int index) {
+ return index == 0 ? state.length : secondaryState[index - 1].length;
+ }
+
+ /**
+ * Get secondary state at time.
+ *
+ * @param index index of the secondary set as returned by {@link
+ * FieldExpandableODE#addSecondaryEquations(FieldSecondaryEquations)} (beware index 0
+ * corresponds to main state, additional states start at 1)
+ * @return secondary state at time
+ */
+ public T[] getSecondaryState(final int index) {
+ return index == 0 ? state.clone() : secondaryState[index - 1].clone();
+ }
+}
diff --git a/src/main/java/org/apache/commons/math3/ode/FieldODEStateAndDerivative.java b/src/main/java/org/apache/commons/math3/ode/FieldODEStateAndDerivative.java
new file mode 100644
index 0000000..8759a77
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/ode/FieldODEStateAndDerivative.java
@@ -0,0 +1,91 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math3.ode;
+
+import org.apache.commons.math3.RealFieldElement;
+
+/**
+ * Container for time, main and secondary state vectors as well as their derivatives.
+ *
+ * @see FirstOrderFieldDifferentialEquations
+ * @see FieldSecondaryEquations
+ * @see FirstOrderFieldIntegrator
+ * @param <T> the type of the field elements
+ * @since 3.6
+ */
+public class FieldODEStateAndDerivative<T extends RealFieldElement<T>> extends FieldODEState<T> {
+
+ /** Derivative of the main state at time. */
+ private final T[] derivative;
+
+ /** Derivative of the secondary state at time. */
+ private final T[][] secondaryDerivative;
+
+ /**
+ * Simple constructor.
+ *
+ * <p>Calling this constructor is equivalent to call {@link
+ * #FieldODEStateAndDerivative(RealFieldElement, RealFieldElement[], RealFieldElement[],
+ * RealFieldElement[][], RealFieldElement[][]) FieldODEStateAndDerivative(time, state,
+ * derivative, null, null)}.
+ *
+ * @param time time
+ * @param state state at time
+ * @param derivative derivative of the state at time
+ */
+ public FieldODEStateAndDerivative(T time, T[] state, T[] derivative) {
+ this(time, state, derivative, null, null);
+ }
+
+ /**
+ * Simple constructor.
+ *
+ * @param time time
+ * @param state state at time
+ * @param derivative derivative of the state at time
+ * @param secondaryState state at time (may be null)
+ * @param secondaryDerivative derivative of the state at time (may be null)
+ */
+ public FieldODEStateAndDerivative(
+ T time, T[] state, T[] derivative, T[][] secondaryState, T[][] secondaryDerivative) {
+ super(time, state, secondaryState);
+ this.derivative = derivative.clone();
+ this.secondaryDerivative = copy(time.getField(), secondaryDerivative);
+ }
+
+ /**
+ * Get derivative of the main state at time.
+ *
+ * @return derivative of the main state at time
+ */
+ public T[] getDerivative() {
+ return derivative.clone();
+ }
+
+ /**
+ * Get derivative of the secondary state at time.
+ *
+ * @param index index of the secondary set as returned by {@link
+ * FieldExpandableODE#addSecondaryEquations(FieldSecondaryEquations)} (beware index 0
+ * corresponds to main state, additional states start at 1)
+ * @return derivative of the secondary state at time
+ */
+ public T[] getSecondaryDerivative(final int index) {
+ return index == 0 ? derivative.clone() : secondaryDerivative[index - 1].clone();
+ }
+}
diff --git a/src/main/java/org/apache/commons/math3/ode/FieldSecondaryEquations.java b/src/main/java/org/apache/commons/math3/ode/FieldSecondaryEquations.java
new file mode 100644
index 0000000..9c5384f
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/ode/FieldSecondaryEquations.java
@@ -0,0 +1,77 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math3.ode;
+
+import org.apache.commons.math3.RealFieldElement;
+import org.apache.commons.math3.exception.DimensionMismatchException;
+import org.apache.commons.math3.exception.MaxCountExceededException;
+
+/**
+ * This interface allows users to add secondary differential equations to a primary set of
+ * differential equations.
+ *
+ * <p>In some cases users may need to integrate some problem-specific equations along with a primary
+ * set of differential equations. One example is optimal control where adjoined parameters linked to
+ * the minimized Hamiltonian must be integrated.
+ *
+ * <p>This interface allows users to add such equations to a primary set of {@link
+ * FirstOrderFieldDifferentialEquations first order differential equations} thanks to the {@link
+ * FieldExpandableODE#addSecondaryEquations(FieldSecondaryEquations)} method.
+ *
+ * @see FirstOrderFieldDifferentialEquations
+ * @see FieldExpandableODE
+ * @param <T> the type of the field elements
+ * @since 3.6
+ */
+public interface FieldSecondaryEquations<T extends RealFieldElement<T>> {
+
+ /**
+ * Get the dimension of the secondary state parameters.
+ *
+ * @return dimension of the secondary state parameters
+ */
+ int getDimension();
+
+ /**
+ * Initialize equations at the start of an ODE integration.
+ *
+ * <p>This method is called once at the start of the integration. It may be used by the
+ * equations to initialize some internal data if needed.
+ *
+ * @param t0 value of the independent <I>time</I> variable at integration start
+ * @param primary0 array containing the value of the primary state vector at integration start
+ * @param secondary0 array containing the value of the secondary state vector at integration
+ * start
+ * @param finalTime target time for the integration
+ */
+ void init(T t0, T[] primary0, T[] secondary0, T finalTime);
+
+ /**
+ * Compute the derivatives related to the secondary state parameters.
+ *
+ * @param t current value of the independent <I>time</I> variable
+ * @param primary array containing the current value of the primary state vector
+ * @param primaryDot array containing the derivative of the primary state vector
+ * @param secondary array containing the current value of the secondary state vector
+ * @return derivative of the secondary state vector
+ * @exception MaxCountExceededException if the number of functions evaluations is exceeded
+ * @exception DimensionMismatchException if arrays dimensions do not match equations settings
+ */
+ T[] computeDerivatives(T t, T[] primary, T[] primaryDot, T[] secondary)
+ throws MaxCountExceededException, DimensionMismatchException;
+}
diff --git a/src/main/java/org/apache/commons/math3/ode/FirstOrderConverter.java b/src/main/java/org/apache/commons/math3/ode/FirstOrderConverter.java
new file mode 100644
index 0000000..cfa0343
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/ode/FirstOrderConverter.java
@@ -0,0 +1,109 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math3.ode;
+
+/**
+ * This class converts second order differential equations to first order ones.
+ *
+ * <p>This class is a wrapper around a {@link SecondOrderDifferentialEquations} which allow to use a
+ * {@link FirstOrderIntegrator} to integrate it.
+ *
+ * <p>The transformation is done by changing the n dimension state vector to a 2n dimension vector,
+ * where the first n components are the initial state variables and the n last components are their
+ * first time derivative. The first time derivative of this state vector then really contains both
+ * the first and second time derivative of the initial state vector, which can be handled by the
+ * underlying second order equations set.
+ *
+ * <p>One should be aware that the data is duplicated during the transformation process and that for
+ * each call to {@link #computeDerivatives computeDerivatives}, this wrapper does copy 4n scalars :
+ * 2n before the call to {@link SecondOrderDifferentialEquations#computeSecondDerivatives
+ * computeSecondDerivatives} in order to dispatch the y state vector into z and zDot, and 2n after
+ * the call to gather zDot and zDDot into yDot. Since the underlying problem by itself perhaps also
+ * needs to copy data and dispatch the arrays into domain objects, this has an impact on both memory
+ * and CPU usage. The only way to avoid this duplication is to perform the transformation at the
+ * problem level, i.e. to implement the problem as a first order one and then avoid using this
+ * class.
+ *
+ * @see FirstOrderIntegrator
+ * @see FirstOrderDifferentialEquations
+ * @see SecondOrderDifferentialEquations
+ * @since 1.2
+ */
+public class FirstOrderConverter implements FirstOrderDifferentialEquations {
+
+ /** Underlying second order equations set. */
+ private final SecondOrderDifferentialEquations equations;
+
+ /** second order problem dimension. */
+ private final int dimension;
+
+ /** state vector. */
+ private final double[] z;
+
+ /** first time derivative of the state vector. */
+ private final double[] zDot;
+
+ /** second time derivative of the state vector. */
+ private final double[] zDDot;
+
+ /**
+ * Simple constructor. Build a converter around a second order equations set.
+ *
+ * @param equations second order equations set to convert
+ */
+ public FirstOrderConverter(final SecondOrderDifferentialEquations equations) {
+ this.equations = equations;
+ dimension = equations.getDimension();
+ z = new double[dimension];
+ zDot = new double[dimension];
+ zDDot = new double[dimension];
+ }
+
+ /**
+ * Get the dimension of the problem.
+ *
+ * <p>The dimension of the first order problem is twice the dimension of the underlying second
+ * order problem.
+ *
+ * @return dimension of the problem
+ */
+ public int getDimension() {
+ return 2 * dimension;
+ }
+
+ /**
+ * Get the current time derivative of the state vector.
+ *
+ * @param t current value of the independent <I>time</I> variable
+ * @param y array containing the current value of the state vector
+ * @param yDot placeholder array where to put the time derivative of the state vector
+ */
+ public void computeDerivatives(final double t, final double[] y, final double[] yDot) {
+
+ // split the state vector in two
+ System.arraycopy(y, 0, z, 0, dimension);
+ System.arraycopy(y, dimension, zDot, 0, dimension);
+
+ // apply the underlying equations set
+ equations.computeSecondDerivatives(t, z, zDot, zDDot);
+
+ // build the result state derivative
+ System.arraycopy(zDot, 0, yDot, 0, dimension);
+ System.arraycopy(zDDot, 0, yDot, dimension, dimension);
+ }
+}
diff --git a/src/main/java/org/apache/commons/math3/ode/FirstOrderDifferentialEquations.java b/src/main/java/org/apache/commons/math3/ode/FirstOrderDifferentialEquations.java
new file mode 100644
index 0000000..28bf169
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/ode/FirstOrderDifferentialEquations.java
@@ -0,0 +1,63 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math3.ode;
+
+import org.apache.commons.math3.exception.DimensionMismatchException;
+import org.apache.commons.math3.exception.MaxCountExceededException;
+
+/**
+ * This interface represents a first order differential equations set.
+ *
+ * <p>This interface should be implemented by all real first order differential equation problems
+ * before they can be handled by the integrators {@link FirstOrderIntegrator#integrate} method.
+ *
+ * <p>A first order differential equations problem, as seen by an integrator is the time derivative
+ * <code>dY/dt</code> of a state vector <code>Y</code>, both being one dimensional arrays. From the
+ * integrator point of view, this derivative depends only on the current time <code>t</code> and on
+ * the state vector <code>Y</code>.
+ *
+ * <p>For real problems, the derivative depends also on parameters that do not belong to the state
+ * vector (dynamical model constants for example). These constants are completely outside of the
+ * scope of this interface, the classes that implement it are allowed to handle them as they want.
+ *
+ * @see FirstOrderIntegrator
+ * @see FirstOrderConverter
+ * @see SecondOrderDifferentialEquations
+ * @since 1.2
+ */
+public interface FirstOrderDifferentialEquations {
+
+ /**
+ * Get the dimension of the problem.
+ *
+ * @return dimension of the problem
+ */
+ int getDimension();
+
+ /**
+ * Get the current time derivative of the state vector.
+ *
+ * @param t current value of the independent <I>time</I> variable
+ * @param y array containing the current value of the state vector
+ * @param yDot placeholder array where to put the time derivative of the state vector
+ * @exception MaxCountExceededException if the number of functions evaluations is exceeded
+ * @exception DimensionMismatchException if arrays dimensions do not match equations settings
+ */
+ void computeDerivatives(double t, double[] y, double[] yDot)
+ throws MaxCountExceededException, DimensionMismatchException;
+}
diff --git a/src/main/java/org/apache/commons/math3/ode/FirstOrderFieldDifferentialEquations.java b/src/main/java/org/apache/commons/math3/ode/FirstOrderFieldDifferentialEquations.java
new file mode 100644
index 0000000..a430067
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/ode/FirstOrderFieldDifferentialEquations.java
@@ -0,0 +1,70 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math3.ode;
+
+import org.apache.commons.math3.RealFieldElement;
+
+/**
+ * This interface represents a first order differential equations set.
+ *
+ * <p>This interface should be implemented by all real first order differential equation problems
+ * before they can be handled by the integrators {@link FirstOrderIntegrator#integrate} method.
+ *
+ * <p>A first order differential equations problem, as seen by an integrator is the time derivative
+ * <code>dY/dt</code> of a state vector <code>Y</code>, both being one dimensional arrays. From the
+ * integrator point of view, this derivative depends only on the current time <code>t</code> and on
+ * the state vector <code>Y</code>.
+ *
+ * <p>For real problems, the derivative depends also on parameters that do not belong to the state
+ * vector (dynamical model constants for example). These constants are completely outside of the
+ * scope of this interface, the classes that implement it are allowed to handle them as they want.
+ *
+ * @see FirstOrderFieldIntegrator
+ * @param <T> the type of the field elements
+ * @since 3.6
+ */
+public interface FirstOrderFieldDifferentialEquations<T extends RealFieldElement<T>> {
+
+ /**
+ * Get the dimension of the problem.
+ *
+ * @return dimension of the problem
+ */
+ int getDimension();
+
+ /**
+ * Initialize equations at the start of an ODE integration.
+ *
+ * <p>This method is called once at the start of the integration. It may be used by the
+ * equations to initialize some internal data if needed.
+ *
+ * @param t0 value of the independent <I>time</I> variable at integration start
+ * @param y0 array containing the value of the state vector at integration start
+ * @param finalTime target time for the integration
+ */
+ void init(T t0, T[] y0, T finalTime);
+
+ /**
+ * Get the current time derivative of the state vector.
+ *
+ * @param t current value of the independent <I>time</I> variable
+ * @param y array containing the current value of the state vector
+ * @return time derivative of the state vector
+ */
+ T[] computeDerivatives(T t, T[] y);
+}
diff --git a/src/main/java/org/apache/commons/math3/ode/FirstOrderFieldIntegrator.java b/src/main/java/org/apache/commons/math3/ode/FirstOrderFieldIntegrator.java
new file mode 100644
index 0000000..8d26495
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/ode/FirstOrderFieldIntegrator.java
@@ -0,0 +1,217 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math3.ode;
+
+import org.apache.commons.math3.RealFieldElement;
+import org.apache.commons.math3.analysis.solvers.BracketedRealFieldUnivariateSolver;
+import org.apache.commons.math3.exception.MaxCountExceededException;
+import org.apache.commons.math3.exception.NoBracketingException;
+import org.apache.commons.math3.exception.NumberIsTooSmallException;
+import org.apache.commons.math3.ode.events.FieldEventHandler;
+import org.apache.commons.math3.ode.sampling.FieldStepHandler;
+
+import java.util.Collection;
+
+/**
+ * This interface represents a first order integrator for differential equations.
+ *
+ * <p>The classes which are devoted to solve first order differential equations should implement
+ * this interface. The problems which can be handled should implement the {@link
+ * FirstOrderDifferentialEquations} interface.
+ *
+ * @see FirstOrderFieldDifferentialEquations
+ * @param <T> the type of the field elements
+ * @since 3.6
+ */
+public interface FirstOrderFieldIntegrator<T extends RealFieldElement<T>> {
+
+ /**
+ * Get the name of the method.
+ *
+ * @return name of the method
+ */
+ String getName();
+
+ /**
+ * Add a step handler to this integrator.
+ *
+ * <p>The handler will be called by the integrator for each accepted step.
+ *
+ * @param handler handler for the accepted steps
+ * @see #getStepHandlers()
+ * @see #clearStepHandlers()
+ */
+ void addStepHandler(FieldStepHandler<T> handler);
+
+ /**
+ * Get all the step handlers that have been added to the integrator.
+ *
+ * @return an unmodifiable collection of the added events handlers
+ * @see #addStepHandler(FieldStepHandler)
+ * @see #clearStepHandlers()
+ */
+ Collection<FieldStepHandler<T>> getStepHandlers();
+
+ /**
+ * Remove all the step handlers that have been added to the integrator.
+ *
+ * @see #addStepHandler(FieldStepHandler)
+ * @see #getStepHandlers()
+ */
+ void clearStepHandlers();
+
+ /**
+ * Add an event handler to the integrator.
+ *
+ * <p>The default solver is a 5<sup>th</sup> order {@link
+ * org.apache.commons.math3.analysis.solvers.FieldBracketingNthOrderBrentSolver}.
+ *
+ * @param handler event handler
+ * @param maxCheckInterval maximal time interval between switching function checks (this
+ * interval prevents missing sign changes in case the integration steps becomes very large)
+ * @param convergence convergence threshold in the event time search
+ * @param maxIterationCount upper limit of the iteration count in the event time search events.
+ * @see #addEventHandler(FieldEventHandler, double, double, int,
+ * org.apache.commons.math3.analysis.solvers.BracketedRealFieldUnivariateSolver)
+ * @see #getEventHandlers()
+ * @see #clearEventHandlers()
+ */
+ void addEventHandler(
+ FieldEventHandler<T> handler,
+ double maxCheckInterval,
+ double convergence,
+ int maxIterationCount);
+
+ /**
+ * Add an event handler to the integrator.
+ *
+ * @param handler event handler
+ * @param maxCheckInterval maximal time interval between switching function checks (this
+ * interval prevents missing sign changes in case the integration steps becomes very large)
+ * @param convergence convergence threshold in the event time search
+ * @param maxIterationCount upper limit of the iteration count in the event time search events.
+ * @param solver solver to use to locate the event
+ * @see #addEventHandler(FieldEventHandler, double, double, int)
+ * @see #getEventHandlers()
+ * @see #clearEventHandlers()
+ */
+ void addEventHandler(
+ FieldEventHandler<T> handler,
+ double maxCheckInterval,
+ double convergence,
+ int maxIterationCount,
+ BracketedRealFieldUnivariateSolver<T> solver);
+
+ /**
+ * Get all the event handlers that have been added to the integrator.
+ *
+ * @return an unmodifiable collection of the added events handlers
+ * @see #addEventHandler(FieldEventHandler, double, double, int)
+ * @see #clearEventHandlers()
+ */
+ Collection<FieldEventHandler<T>> getEventHandlers();
+
+ /**
+ * Remove all the event handlers that have been added to the integrator.
+ *
+ * @see #addEventHandler(FieldEventHandler, double, double, int)
+ * @see #getEventHandlers()
+ */
+ void clearEventHandlers();
+
+ /**
+ * Get the current value of the step start time t<sub>i</sub>.
+ *
+ * <p>This method can be called during integration (typically by the object implementing the
+ * {@link FirstOrderDifferentialEquations differential equations} problem) if the value of the
+ * current step that is attempted is needed.
+ *
+ * <p>The result is undefined if the method is called outside of calls to <code>integrate</code>
+ * .
+ *
+ * @return current value of the state at step start time t<sub>i</sub>
+ */
+ FieldODEStateAndDerivative<T> getCurrentStepStart();
+
+ /**
+ * Get the current signed value of the integration stepsize.
+ *
+ * <p>This method can be called during integration (typically by the object implementing the
+ * {@link FirstOrderDifferentialEquations differential equations} problem) if the signed value
+ * of the current stepsize that is tried is needed.
+ *
+ * <p>The result is undefined if the method is called outside of calls to <code>integrate</code>
+ * .
+ *
+ * @return current signed value of the stepsize
+ */
+ T getCurrentSignedStepsize();
+
+ /**
+ * Set the maximal number of differential equations function evaluations.
+ *
+ * <p>The purpose of this method is to avoid infinite loops which can occur for example when
+ * stringent error constraints are set or when lots of discrete events are triggered, thus
+ * leading to many rejected steps.
+ *
+ * @param maxEvaluations maximal number of function evaluations (negative values are silently
+ * converted to maximal integer value, thus representing almost unlimited evaluations)
+ */
+ void setMaxEvaluations(int maxEvaluations);
+
+ /**
+ * Get the maximal number of functions evaluations.
+ *
+ * @return maximal number of functions evaluations
+ */
+ int getMaxEvaluations();
+
+ /**
+ * Get the number of evaluations of the differential equations function.
+ *
+ * <p>The number of evaluations corresponds to the last call to the <code>integrate</code>
+ * method. It is 0 if the method has not been called yet.
+ *
+ * @return number of evaluations of the differential equations function
+ */
+ int getEvaluations();
+
+ /**
+ * Integrate the differential equations up to the given time.
+ *
+ * <p>This method solves an Initial Value Problem (IVP).
+ *
+ * <p>Since this method stores some internal state variables made available in its public
+ * interface during integration ({@link #getCurrentSignedStepsize()}), it is <em>not</em>
+ * thread-safe.
+ *
+ * @param equations differential equations to integrate
+ * @param initialState initial state (time, primary and secondary state vectors)
+ * @param finalTime target time for the integration (can be set to a value smaller than {@code
+ * t0} for backward integration)
+ * @return final state, its time will be the same as {@code finalTime} if integration reached
+ * its target, but may be different if some {@link
+ * org.apache.commons.math3.ode.events.FieldEventHandler} stops it at some point.
+ * @exception NumberIsTooSmallException if integration step is too small
+ * @exception MaxCountExceededException if the number of functions evaluations is exceeded
+ * @exception NoBracketingException if the location of an event cannot be bracketed
+ */
+ FieldODEStateAndDerivative<T> integrate(
+ FieldExpandableODE<T> equations, FieldODEState<T> initialState, T finalTime)
+ throws NumberIsTooSmallException, MaxCountExceededException, NoBracketingException;
+}
diff --git a/src/main/java/org/apache/commons/math3/ode/FirstOrderIntegrator.java b/src/main/java/org/apache/commons/math3/ode/FirstOrderIntegrator.java
new file mode 100644
index 0000000..786a41b
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/ode/FirstOrderIntegrator.java
@@ -0,0 +1,69 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math3.ode;
+
+import org.apache.commons.math3.exception.DimensionMismatchException;
+import org.apache.commons.math3.exception.MaxCountExceededException;
+import org.apache.commons.math3.exception.NoBracketingException;
+import org.apache.commons.math3.exception.NumberIsTooSmallException;
+
+/**
+ * This interface represents a first order integrator for differential equations.
+ *
+ * <p>The classes which are devoted to solve first order differential equations should implement
+ * this interface. The problems which can be handled should implement the {@link
+ * FirstOrderDifferentialEquations} interface.
+ *
+ * @see FirstOrderDifferentialEquations
+ * @see org.apache.commons.math3.ode.sampling.StepHandler
+ * @see org.apache.commons.math3.ode.events.EventHandler
+ * @since 1.2
+ */
+public interface FirstOrderIntegrator extends ODEIntegrator {
+
+ /**
+ * Integrate the differential equations up to the given time.
+ *
+ * <p>This method solves an Initial Value Problem (IVP).
+ *
+ * <p>Since this method stores some internal state variables made available in its public
+ * interface during integration ({@link #getCurrentSignedStepsize()}), it is <em>not</em>
+ * thread-safe.
+ *
+ * @param equations differential equations to integrate
+ * @param t0 initial time
+ * @param y0 initial value of the state vector at t0
+ * @param t target time for the integration (can be set to a value smaller than <code>t0</code>
+ * for backward integration)
+ * @param y placeholder where to put the state vector at each successful step (and hence at the
+ * end of integration), can be the same object as y0
+ * @return stop time, will be the same as target time if integration reached its target, but may
+ * be different if some {@link org.apache.commons.math3.ode.events.EventHandler} stops it at
+ * some point.
+ * @exception DimensionMismatchException if arrays dimension do not match equations settings
+ * @exception NumberIsTooSmallException if integration step is too small
+ * @exception MaxCountExceededException if the number of functions evaluations is exceeded
+ * @exception NoBracketingException if the location of an event cannot be bracketed
+ */
+ double integrate(
+ FirstOrderDifferentialEquations equations, double t0, double[] y0, double t, double[] y)
+ throws DimensionMismatchException,
+ NumberIsTooSmallException,
+ MaxCountExceededException,
+ NoBracketingException;
+}
diff --git a/src/main/java/org/apache/commons/math3/ode/JacobianMatrices.java b/src/main/java/org/apache/commons/math3/ode/JacobianMatrices.java
new file mode 100644
index 0000000..eecdce7
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/ode/JacobianMatrices.java
@@ -0,0 +1,510 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math3.ode;
+
+import org.apache.commons.math3.exception.DimensionMismatchException;
+import org.apache.commons.math3.exception.MathIllegalArgumentException;
+import org.apache.commons.math3.exception.MaxCountExceededException;
+import org.apache.commons.math3.exception.util.LocalizedFormats;
+
+import java.lang.reflect.Array;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+/**
+ * This class defines a set of {@link SecondaryEquations secondary equations} to compute the
+ * Jacobian matrices with respect to the initial state vector and, if any, to some parameters of the
+ * primary ODE set.
+ *
+ * <p>It is intended to be packed into an {@link ExpandableStatefulODE} in conjunction with a
+ * primary set of ODE, which may be:
+ *
+ * <ul>
+ * <li>a {@link FirstOrderDifferentialEquations}
+ * <li>a {@link MainStateJacobianProvider}
+ * </ul>
+ *
+ * In order to compute Jacobian matrices with respect to some parameters of the primary ODE set, the
+ * following parameter Jacobian providers may be set:
+ *
+ * <ul>
+ * <li>a {@link ParameterJacobianProvider}
+ * <li>a {@link ParameterizedODE}
+ * </ul>
+ *
+ * @see ExpandableStatefulODE
+ * @see FirstOrderDifferentialEquations
+ * @see MainStateJacobianProvider
+ * @see ParameterJacobianProvider
+ * @see ParameterizedODE
+ * @since 3.0
+ */
+public class JacobianMatrices {
+
+ /** Expandable first order differential equation. */
+ private ExpandableStatefulODE efode;
+
+ /** Index of the instance in the expandable set. */
+ private int index;
+
+ /** FODE with exact primary Jacobian computation skill. */
+ private MainStateJacobianProvider jode;
+
+ /** FODE without exact parameter Jacobian computation skill. */
+ private ParameterizedODE pode;
+
+ /** Main state vector dimension. */
+ private int stateDim;
+
+ /** Selected parameters for parameter Jacobian computation. */
+ private ParameterConfiguration[] selectedParameters;
+
+ /** FODE with exact parameter Jacobian computation skill. */
+ private List<ParameterJacobianProvider> jacobianProviders;
+
+ /** Parameters dimension. */
+ private int paramDim;
+
+ /** Boolean for selected parameters consistency. */
+ private boolean dirtyParameter;
+
+ /** State and parameters Jacobian matrices in a row. */
+ private double[] matricesData;
+
+ /**
+ * Simple constructor for a secondary equations set computing Jacobian matrices.
+ *
+ * <p>Parameters must belong to the supported ones given by {@link
+ * Parameterizable#getParametersNames()}, so the primary set of differential equations must be
+ * {@link Parameterizable}.
+ *
+ * <p>Note that each selection clears the previous selected parameters.
+ *
+ * @param fode the primary first order differential equations set to extend
+ * @param hY step used for finite difference computation with respect to state vector
+ * @param parameters parameters to consider for Jacobian matrices processing (may be null if
+ * parameters Jacobians is not desired)
+ * @exception DimensionMismatchException if there is a dimension mismatch between the steps
+ * array {@code hY} and the equation dimension
+ */
+ public JacobianMatrices(
+ final FirstOrderDifferentialEquations fode,
+ final double[] hY,
+ final String... parameters)
+ throws DimensionMismatchException {
+ this(new MainStateJacobianWrapper(fode, hY), parameters);
+ }
+
+ /**
+ * Simple constructor for a secondary equations set computing Jacobian matrices.
+ *
+ * <p>Parameters must belong to the supported ones given by {@link
+ * Parameterizable#getParametersNames()}, so the primary set of differential equations must be
+ * {@link Parameterizable}.
+ *
+ * <p>Note that each selection clears the previous selected parameters.
+ *
+ * @param jode the primary first order differential equations set to extend
+ * @param parameters parameters to consider for Jacobian matrices processing (may be null if
+ * parameters Jacobians is not desired)
+ */
+ public JacobianMatrices(final MainStateJacobianProvider jode, final String... parameters) {
+
+ this.efode = null;
+ this.index = -1;
+
+ this.jode = jode;
+ this.pode = null;
+
+ this.stateDim = jode.getDimension();
+
+ if (parameters == null) {
+ selectedParameters = null;
+ paramDim = 0;
+ } else {
+ this.selectedParameters = new ParameterConfiguration[parameters.length];
+ for (int i = 0; i < parameters.length; ++i) {
+ selectedParameters[i] = new ParameterConfiguration(parameters[i], Double.NaN);
+ }
+ paramDim = parameters.length;
+ }
+ this.dirtyParameter = false;
+
+ this.jacobianProviders = new ArrayList<ParameterJacobianProvider>();
+
+ // set the default initial state Jacobian to the identity
+ // and the default initial parameters Jacobian to the null matrix
+ matricesData = new double[(stateDim + paramDim) * stateDim];
+ for (int i = 0; i < stateDim; ++i) {
+ matricesData[i * (stateDim + 1)] = 1.0;
+ }
+ }
+
+ /**
+ * Register the variational equations for the Jacobians matrices to the expandable set.
+ *
+ * @param expandable expandable set into which variational equations should be registered
+ * @throws DimensionMismatchException if the dimension of the partial state does not match the
+ * selected equations set dimension
+ * @exception MismatchedEquations if the primary set of the expandable set does not match the
+ * one used to build the instance
+ * @see ExpandableStatefulODE#addSecondaryEquations(SecondaryEquations)
+ */
+ public void registerVariationalEquations(final ExpandableStatefulODE expandable)
+ throws DimensionMismatchException, MismatchedEquations {
+
+ // safety checks
+ final FirstOrderDifferentialEquations ode =
+ (jode instanceof MainStateJacobianWrapper)
+ ? ((MainStateJacobianWrapper) jode).ode
+ : jode;
+ if (expandable.getPrimary() != ode) {
+ throw new MismatchedEquations();
+ }
+
+ efode = expandable;
+ index = efode.addSecondaryEquations(new JacobiansSecondaryEquations());
+ efode.setSecondaryState(index, matricesData);
+ }
+
+ /**
+ * Add a parameter Jacobian provider.
+ *
+ * @param provider the parameter Jacobian provider to compute exactly the parameter Jacobian
+ * matrix
+ */
+ public void addParameterJacobianProvider(final ParameterJacobianProvider provider) {
+ jacobianProviders.add(provider);
+ }
+
+ /**
+ * Set a parameter Jacobian provider.
+ *
+ * @param parameterizedOde the parameterized ODE to compute the parameter Jacobian matrix using
+ * finite differences
+ */
+ public void setParameterizedODE(final ParameterizedODE parameterizedOde) {
+ this.pode = parameterizedOde;
+ dirtyParameter = true;
+ }
+
+ /**
+ * Set the step associated to a parameter in order to compute by finite difference the Jacobian
+ * matrix.
+ *
+ * <p>Needed if and only if the primary ODE set is a {@link ParameterizedODE}.
+ *
+ * <p>Given a non zero parameter value pval for the parameter, a reasonable value for such a
+ * step is {@code pval * FastMath.sqrt(Precision.EPSILON)}.
+ *
+ * <p>A zero value for such a step doesn't enable to compute the parameter Jacobian matrix.
+ *
+ * @param parameter parameter to consider for Jacobian processing
+ * @param hP step for Jacobian finite difference computation w.r.t. the specified parameter
+ * @see ParameterizedODE
+ * @exception UnknownParameterException if the parameter is not supported
+ */
+ public void setParameterStep(final String parameter, final double hP)
+ throws UnknownParameterException {
+
+ for (ParameterConfiguration param : selectedParameters) {
+ if (parameter.equals(param.getParameterName())) {
+ param.setHP(hP);
+ dirtyParameter = true;
+ return;
+ }
+ }
+
+ throw new UnknownParameterException(parameter);
+ }
+
+ /**
+ * Set the initial value of the Jacobian matrix with respect to state.
+ *
+ * <p>If this method is not called, the initial value of the Jacobian matrix with respect to
+ * state is set to identity.
+ *
+ * @param dYdY0 initial Jacobian matrix w.r.t. state
+ * @exception DimensionMismatchException if matrix dimensions are incorrect
+ */
+ public void setInitialMainStateJacobian(final double[][] dYdY0)
+ throws DimensionMismatchException {
+
+ // Check dimensions
+ checkDimension(stateDim, dYdY0);
+ checkDimension(stateDim, dYdY0[0]);
+
+ // store the matrix in row major order as a single dimension array
+ int i = 0;
+ for (final double[] row : dYdY0) {
+ System.arraycopy(row, 0, matricesData, i, stateDim);
+ i += stateDim;
+ }
+
+ if (efode != null) {
+ efode.setSecondaryState(index, matricesData);
+ }
+ }
+
+ /**
+ * Set the initial value of a column of the Jacobian matrix with respect to one parameter.
+ *
+ * <p>If this method is not called for some parameter, the initial value of the column of the
+ * Jacobian matrix with respect to this parameter is set to zero.
+ *
+ * @param pName parameter name
+ * @param dYdP initial Jacobian column vector with respect to the parameter
+ * @exception UnknownParameterException if a parameter is not supported
+ * @throws DimensionMismatchException if the column vector does not match state dimension
+ */
+ public void setInitialParameterJacobian(final String pName, final double[] dYdP)
+ throws UnknownParameterException, DimensionMismatchException {
+
+ // Check dimensions
+ checkDimension(stateDim, dYdP);
+
+ // store the column in a global single dimension array
+ int i = stateDim * stateDim;
+ for (ParameterConfiguration param : selectedParameters) {
+ if (pName.equals(param.getParameterName())) {
+ System.arraycopy(dYdP, 0, matricesData, i, stateDim);
+ if (efode != null) {
+ efode.setSecondaryState(index, matricesData);
+ }
+ return;
+ }
+ i += stateDim;
+ }
+
+ throw new UnknownParameterException(pName);
+ }
+
+ /**
+ * Get the current value of the Jacobian matrix with respect to state.
+ *
+ * @param dYdY0 current Jacobian matrix with respect to state.
+ */
+ public void getCurrentMainSetJacobian(final double[][] dYdY0) {
+
+ // get current state for this set of equations from the expandable fode
+ double[] p = efode.getSecondaryState(index);
+
+ int j = 0;
+ for (int i = 0; i < stateDim; i++) {
+ System.arraycopy(p, j, dYdY0[i], 0, stateDim);
+ j += stateDim;
+ }
+ }
+
+ /**
+ * Get the current value of the Jacobian matrix with respect to one parameter.
+ *
+ * @param pName name of the parameter for the computed Jacobian matrix
+ * @param dYdP current Jacobian matrix with respect to the named parameter
+ */
+ public void getCurrentParameterJacobian(String pName, final double[] dYdP) {
+
+ // get current state for this set of equations from the expandable fode
+ double[] p = efode.getSecondaryState(index);
+
+ int i = stateDim * stateDim;
+ for (ParameterConfiguration param : selectedParameters) {
+ if (param.getParameterName().equals(pName)) {
+ System.arraycopy(p, i, dYdP, 0, stateDim);
+ return;
+ }
+ i += stateDim;
+ }
+ }
+
+ /**
+ * Check array dimensions.
+ *
+ * @param expected expected dimension
+ * @param array (may be null if expected is 0)
+ * @throws DimensionMismatchException if the array dimension does not match the expected one
+ */
+ private void checkDimension(final int expected, final Object array)
+ throws DimensionMismatchException {
+ int arrayDimension = (array == null) ? 0 : Array.getLength(array);
+ if (arrayDimension != expected) {
+ throw new DimensionMismatchException(arrayDimension, expected);
+ }
+ }
+
+ /**
+ * Local implementation of secondary equations.
+ *
+ * <p>This class is an inner class to ensure proper scheduling of calls by forcing the use of
+ * {@link JacobianMatrices#registerVariationalEquations(ExpandableStatefulODE)}.
+ */
+ private class JacobiansSecondaryEquations implements SecondaryEquations {
+
+ /** {@inheritDoc} */
+ public int getDimension() {
+ return stateDim * (stateDim + paramDim);
+ }
+
+ /** {@inheritDoc} */
+ public void computeDerivatives(
+ final double t,
+ final double[] y,
+ final double[] yDot,
+ final double[] z,
+ final double[] zDot)
+ throws MaxCountExceededException, DimensionMismatchException {
+
+ // Lazy initialization
+ if (dirtyParameter && (paramDim != 0)) {
+ jacobianProviders.add(new ParameterJacobianWrapper(jode, pode, selectedParameters));
+ dirtyParameter = false;
+ }
+
+ // variational equations:
+ // from d[dy/dt]/dy0 and d[dy/dt]/dp to d[dy/dy0]/dt and d[dy/dp]/dt
+
+ // compute Jacobian matrix with respect to primary state
+ double[][] dFdY = new double[stateDim][stateDim];
+ jode.computeMainStateJacobian(t, y, yDot, dFdY);
+
+ // Dispatch Jacobian matrix in the compound secondary state vector
+ for (int i = 0; i < stateDim; ++i) {
+ final double[] dFdYi = dFdY[i];
+ for (int j = 0; j < stateDim; ++j) {
+ double s = 0;
+ final int startIndex = j;
+ int zIndex = startIndex;
+ for (int l = 0; l < stateDim; ++l) {
+ s += dFdYi[l] * z[zIndex];
+ zIndex += stateDim;
+ }
+ zDot[startIndex + i * stateDim] = s;
+ }
+ }
+
+ if (paramDim != 0) {
+ // compute Jacobian matrices with respect to parameters
+ double[] dFdP = new double[stateDim];
+ int startIndex = stateDim * stateDim;
+ for (ParameterConfiguration param : selectedParameters) {
+ boolean found = false;
+ for (int k = 0; (!found) && (k < jacobianProviders.size()); ++k) {
+ final ParameterJacobianProvider provider = jacobianProviders.get(k);
+ if (provider.isSupported(param.getParameterName())) {
+ provider.computeParameterJacobian(
+ t, y, yDot, param.getParameterName(), dFdP);
+ for (int i = 0; i < stateDim; ++i) {
+ final double[] dFdYi = dFdY[i];
+ int zIndex = startIndex;
+ double s = dFdP[i];
+ for (int l = 0; l < stateDim; ++l) {
+ s += dFdYi[l] * z[zIndex];
+ zIndex++;
+ }
+ zDot[startIndex + i] = s;
+ }
+ found = true;
+ }
+ }
+ if (!found) {
+ Arrays.fill(zDot, startIndex, startIndex + stateDim, 0.0);
+ }
+ startIndex += stateDim;
+ }
+ }
+ }
+ }
+
+ /**
+ * Wrapper class to compute jacobian matrices by finite differences for ODE which do not compute
+ * them by themselves.
+ */
+ private static class MainStateJacobianWrapper implements MainStateJacobianProvider {
+
+ /**
+ * Raw ODE without jacobians computation skill to be wrapped into a
+ * MainStateJacobianProvider.
+ */
+ private final FirstOrderDifferentialEquations ode;
+
+ /** Steps for finite difference computation of the jacobian df/dy w.r.t. state. */
+ private final double[] hY;
+
+ /**
+ * Wrap a {@link FirstOrderDifferentialEquations} into a {@link MainStateJacobianProvider}.
+ *
+ * @param ode original ODE problem, without jacobians computation skill
+ * @param hY step sizes to compute the jacobian df/dy
+ * @exception DimensionMismatchException if there is a dimension mismatch between the steps
+ * array {@code hY} and the equation dimension
+ */
+ MainStateJacobianWrapper(final FirstOrderDifferentialEquations ode, final double[] hY)
+ throws DimensionMismatchException {
+ this.ode = ode;
+ this.hY = hY.clone();
+ if (hY.length != ode.getDimension()) {
+ throw new DimensionMismatchException(ode.getDimension(), hY.length);
+ }
+ }
+
+ /** {@inheritDoc} */
+ public int getDimension() {
+ return ode.getDimension();
+ }
+
+ /** {@inheritDoc} */
+ public void computeDerivatives(double t, double[] y, double[] yDot)
+ throws MaxCountExceededException, DimensionMismatchException {
+ ode.computeDerivatives(t, y, yDot);
+ }
+
+ /** {@inheritDoc} */
+ public void computeMainStateJacobian(double t, double[] y, double[] yDot, double[][] dFdY)
+ throws MaxCountExceededException, DimensionMismatchException {
+
+ final int n = ode.getDimension();
+ final double[] tmpDot = new double[n];
+
+ for (int j = 0; j < n; ++j) {
+ final double savedYj = y[j];
+ y[j] += hY[j];
+ ode.computeDerivatives(t, y, tmpDot);
+ for (int i = 0; i < n; ++i) {
+ dFdY[i][j] = (tmpDot[i] - yDot[i]) / hY[j];
+ }
+ y[j] = savedYj;
+ }
+ }
+ }
+
+ /**
+ * Special exception for equations mismatch.
+ *
+ * @since 3.1
+ */
+ public static class MismatchedEquations extends MathIllegalArgumentException {
+
+ /** Serializable UID. */
+ private static final long serialVersionUID = 20120902L;
+
+ /** Simple constructor. */
+ public MismatchedEquations() {
+ super(LocalizedFormats.UNMATCHED_ODE_IN_EXPANDED_SET);
+ }
+ }
+}
diff --git a/src/main/java/org/apache/commons/math3/ode/MainStateJacobianProvider.java b/src/main/java/org/apache/commons/math3/ode/MainStateJacobianProvider.java
new file mode 100644
index 0000000..b1ac0ee
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/ode/MainStateJacobianProvider.java
@@ -0,0 +1,45 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math3.ode;
+
+import org.apache.commons.math3.exception.DimensionMismatchException;
+import org.apache.commons.math3.exception.MaxCountExceededException;
+
+/**
+ * Interface expanding {@link FirstOrderDifferentialEquations first order differential equations} in
+ * order to compute exactly the main state jacobian matrix for {@link JacobianMatrices partial
+ * derivatives equations}.
+ *
+ * @since 3.0
+ */
+public interface MainStateJacobianProvider extends FirstOrderDifferentialEquations {
+
+ /**
+ * Compute the jacobian matrix of ODE with respect to main state.
+ *
+ * @param t current value of the independent <I>time</I> variable
+ * @param y array containing the current value of the main state vector
+ * @param yDot array containing the current value of the time derivative of the main state
+ * vector
+ * @param dFdY placeholder array where to put the jacobian matrix of the ODE w.r.t. the main
+ * state vector
+ * @exception MaxCountExceededException if the number of functions evaluations is exceeded
+ * @exception DimensionMismatchException if arrays dimensions do not match equations settings
+ */
+ void computeMainStateJacobian(double t, double[] y, double[] yDot, double[][] dFdY)
+ throws MaxCountExceededException, DimensionMismatchException;
+}
diff --git a/src/main/java/org/apache/commons/math3/ode/MultistepFieldIntegrator.java b/src/main/java/org/apache/commons/math3/ode/MultistepFieldIntegrator.java
new file mode 100644
index 0000000..44a8b81
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/ode/MultistepFieldIntegrator.java
@@ -0,0 +1,482 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math3.ode;
+
+import org.apache.commons.math3.Field;
+import org.apache.commons.math3.RealFieldElement;
+import org.apache.commons.math3.exception.DimensionMismatchException;
+import org.apache.commons.math3.exception.MathIllegalStateException;
+import org.apache.commons.math3.exception.MaxCountExceededException;
+import org.apache.commons.math3.exception.NoBracketingException;
+import org.apache.commons.math3.exception.NumberIsTooSmallException;
+import org.apache.commons.math3.exception.util.LocalizedFormats;
+import org.apache.commons.math3.linear.Array2DRowFieldMatrix;
+import org.apache.commons.math3.ode.nonstiff.AdaptiveStepsizeFieldIntegrator;
+import org.apache.commons.math3.ode.nonstiff.DormandPrince853FieldIntegrator;
+import org.apache.commons.math3.ode.sampling.FieldStepHandler;
+import org.apache.commons.math3.ode.sampling.FieldStepInterpolator;
+import org.apache.commons.math3.util.FastMath;
+import org.apache.commons.math3.util.MathArrays;
+import org.apache.commons.math3.util.MathUtils;
+
+/**
+ * This class is the base class for multistep integrators for Ordinary Differential Equations.
+ *
+ * <p>We define scaled derivatives s<sub>i</sub>(n) at step n as:
+ *
+ * <pre>
+ * s<sub>1</sub>(n) = h y'<sub>n</sub> for first derivative
+ * s<sub>2</sub>(n) = h<sup>2</sup>/2 y''<sub>n</sub> for second derivative
+ * s<sub>3</sub>(n) = h<sup>3</sup>/6 y'''<sub>n</sub> for third derivative
+ * ...
+ * s<sub>k</sub>(n) = h<sup>k</sup>/k! y<sup>(k)</sup><sub>n</sub> for k<sup>th</sup> derivative
+ * </pre>
+ *
+ * <p>Rather than storing several previous steps separately, this implementation uses the Nordsieck
+ * vector with higher degrees scaled derivatives all taken at the same step (y<sub>n</sub>,
+ * s<sub>1</sub>(n) and r<sub>n</sub>) where r<sub>n</sub> is defined as:
+ *
+ * <pre>
+ * r<sub>n</sub> = [ s<sub>2</sub>(n), s<sub>3</sub>(n) ... s<sub>k</sub>(n) ]<sup>T</sup>
+ * </pre>
+ *
+ * (we omit the k index in the notation for clarity)
+ *
+ * <p>Multistep integrators with Nordsieck representation are highly sensitive to large step changes
+ * because when the step is multiplied by factor a, the k<sup>th</sup> component of the Nordsieck
+ * vector is multiplied by a<sup>k</sup> and the last components are the least accurate ones. The
+ * default max growth factor is therefore set to a quite low value: 2<sup>1/order</sup>.
+ *
+ * @see org.apache.commons.math3.ode.nonstiff.AdamsBashforthFieldIntegrator
+ * @see org.apache.commons.math3.ode.nonstiff.AdamsMoultonFieldIntegrator
+ * @param <T> the type of the field elements
+ * @since 3.6
+ */
+public abstract class MultistepFieldIntegrator<T extends RealFieldElement<T>>
+ extends AdaptiveStepsizeFieldIntegrator<T> {
+
+ /** First scaled derivative (h y'). */
+ protected T[] scaled;
+
+ /**
+ * Nordsieck matrix of the higher scaled derivatives.
+ *
+ * <p>(h<sup>2</sup>/2 y'', h<sup>3</sup>/6 y''' ..., h<sup>k</sup>/k! y<sup>(k)</sup>)
+ */
+ protected Array2DRowFieldMatrix<T> nordsieck;
+
+ /** Starter integrator. */
+ private FirstOrderFieldIntegrator<T> starter;
+
+ /** Number of steps of the multistep method (excluding the one being computed). */
+ private final int nSteps;
+
+ /** Stepsize control exponent. */
+ private double exp;
+
+ /** Safety factor for stepsize control. */
+ private double safety;
+
+ /** Minimal reduction factor for stepsize control. */
+ private double minReduction;
+
+ /** Maximal growth factor for stepsize control. */
+ private double maxGrowth;
+
+ /**
+ * Build a multistep integrator with the given stepsize bounds.
+ *
+ * <p>The default starter integrator is set to the {@link DormandPrince853FieldIntegrator
+ * Dormand-Prince 8(5,3)} integrator with some defaults settings.
+ *
+ * <p>The default max growth factor is set to a quite low value: 2<sup>1/order</sup>.
+ *
+ * @param field field to which the time and state vector elements belong
+ * @param name name of the method
+ * @param nSteps number of steps of the multistep method (excluding the one being computed)
+ * @param order order of the method
+ * @param minStep minimal step (must be positive even for backward integration), the last step
+ * can be smaller than this
+ * @param maxStep maximal step (must be positive even for backward integration)
+ * @param scalAbsoluteTolerance allowed absolute error
+ * @param scalRelativeTolerance allowed relative error
+ * @exception NumberIsTooSmallException if number of steps is smaller than 2
+ */
+ protected MultistepFieldIntegrator(
+ final Field<T> field,
+ final String name,
+ final int nSteps,
+ final int order,
+ final double minStep,
+ final double maxStep,
+ final double scalAbsoluteTolerance,
+ final double scalRelativeTolerance)
+ throws NumberIsTooSmallException {
+
+ super(field, name, minStep, maxStep, scalAbsoluteTolerance, scalRelativeTolerance);
+
+ if (nSteps < 2) {
+ throw new NumberIsTooSmallException(
+ LocalizedFormats.INTEGRATION_METHOD_NEEDS_AT_LEAST_TWO_PREVIOUS_POINTS,
+ nSteps,
+ 2,
+ true);
+ }
+
+ starter =
+ new DormandPrince853FieldIntegrator<T>(
+ field, minStep, maxStep, scalAbsoluteTolerance, scalRelativeTolerance);
+ this.nSteps = nSteps;
+
+ exp = -1.0 / order;
+
+ // set the default values of the algorithm control parameters
+ setSafety(0.9);
+ setMinReduction(0.2);
+ setMaxGrowth(FastMath.pow(2.0, -exp));
+ }
+
+ /**
+ * Build a multistep integrator with the given stepsize bounds.
+ *
+ * <p>The default starter integrator is set to the {@link DormandPrince853FieldIntegrator
+ * Dormand-Prince 8(5,3)} integrator with some defaults settings.
+ *
+ * <p>The default max growth factor is set to a quite low value: 2<sup>1/order</sup>.
+ *
+ * @param field field to which the time and state vector elements belong
+ * @param name name of the method
+ * @param nSteps number of steps of the multistep method (excluding the one being computed)
+ * @param order order of the method
+ * @param minStep minimal step (must be positive even for backward integration), the last step
+ * can be smaller than this
+ * @param maxStep maximal step (must be positive even for backward integration)
+ * @param vecAbsoluteTolerance allowed absolute error
+ * @param vecRelativeTolerance allowed relative error
+ */
+ protected MultistepFieldIntegrator(
+ final Field<T> field,
+ final String name,
+ final int nSteps,
+ final int order,
+ final double minStep,
+ final double maxStep,
+ final double[] vecAbsoluteTolerance,
+ final double[] vecRelativeTolerance) {
+ super(field, name, minStep, maxStep, vecAbsoluteTolerance, vecRelativeTolerance);
+ starter =
+ new DormandPrince853FieldIntegrator<T>(
+ field, minStep, maxStep, vecAbsoluteTolerance, vecRelativeTolerance);
+ this.nSteps = nSteps;
+
+ exp = -1.0 / order;
+
+ // set the default values of the algorithm control parameters
+ setSafety(0.9);
+ setMinReduction(0.2);
+ setMaxGrowth(FastMath.pow(2.0, -exp));
+ }
+
+ /**
+ * Get the starter integrator.
+ *
+ * @return starter integrator
+ */
+ public FirstOrderFieldIntegrator<T> getStarterIntegrator() {
+ return starter;
+ }
+
+ /**
+ * Set the starter integrator.
+ *
+ * <p>The various step and event handlers for this starter integrator will be managed
+ * automatically by the multi-step integrator. Any user configuration for these elements will be
+ * cleared before use.
+ *
+ * @param starterIntegrator starter integrator
+ */
+ public void setStarterIntegrator(FirstOrderFieldIntegrator<T> starterIntegrator) {
+ this.starter = starterIntegrator;
+ }
+
+ /**
+ * Start the integration.
+ *
+ * <p>This method computes one step using the underlying starter integrator, and initializes the
+ * Nordsieck vector at step start. The starter integrator purpose is only to establish initial
+ * conditions, it does not really change time by itself. The top level multistep integrator
+ * remains in charge of handling time propagation and events handling as it will starts its own
+ * computation right from the beginning. In a sense, the starter integrator can be seen as a
+ * dummy one and so it will never trigger any user event nor call any user step handler.
+ *
+ * @param equations complete set of differential equations to integrate
+ * @param initialState initial state (time, primary and secondary state vectors)
+ * @param t target time for the integration (can be set to a value smaller than <code>t0</code>
+ * for backward integration)
+ * @exception DimensionMismatchException if arrays dimension do not match equations settings
+ * @exception NumberIsTooSmallException if integration step is too small
+ * @exception MaxCountExceededException if the number of functions evaluations is exceeded
+ * @exception NoBracketingException if the location of an event cannot be bracketed
+ */
+ protected void start(
+ final FieldExpandableODE<T> equations, final FieldODEState<T> initialState, final T t)
+ throws DimensionMismatchException,
+ NumberIsTooSmallException,
+ MaxCountExceededException,
+ NoBracketingException {
+
+ // make sure NO user event nor user step handler is triggered,
+ // this is the task of the top level integrator, not the task
+ // of the starter integrator
+ starter.clearEventHandlers();
+ starter.clearStepHandlers();
+
+ // set up one specific step handler to extract initial Nordsieck vector
+ starter.addStepHandler(
+ new FieldNordsieckInitializer(equations.getMapper(), (nSteps + 3) / 2));
+
+ // start integration, expecting a InitializationCompletedMarkerException
+ try {
+
+ starter.integrate(equations, initialState, t);
+
+ // we should not reach this step
+ throw new MathIllegalStateException(LocalizedFormats.MULTISTEP_STARTER_STOPPED_EARLY);
+
+ } catch (InitializationCompletedMarkerException icme) { // NOPMD
+ // this is the expected nominal interruption of the start integrator
+
+ // count the evaluations used by the starter
+ getEvaluationsCounter().increment(starter.getEvaluations());
+ }
+
+ // remove the specific step handler
+ starter.clearStepHandlers();
+ }
+
+ /**
+ * Initialize the high order scaled derivatives at step start.
+ *
+ * @param h step size to use for scaling
+ * @param t first steps times
+ * @param y first steps states
+ * @param yDot first steps derivatives
+ * @return Nordieck vector at first step (h<sup>2</sup>/2 y''<sub>n</sub>, h<sup>3</sup>/6
+ * y'''<sub>n</sub> ... h<sup>k</sup>/k! y<sup>(k)</sup><sub>n</sub>)
+ */
+ protected abstract Array2DRowFieldMatrix<T> initializeHighOrderDerivatives(
+ final T h, final T[] t, final T[][] y, final T[][] yDot);
+
+ /**
+ * Get the minimal reduction factor for stepsize control.
+ *
+ * @return minimal reduction factor
+ */
+ public double getMinReduction() {
+ return minReduction;
+ }
+
+ /**
+ * Set the minimal reduction factor for stepsize control.
+ *
+ * @param minReduction minimal reduction factor
+ */
+ public void setMinReduction(final double minReduction) {
+ this.minReduction = minReduction;
+ }
+
+ /**
+ * Get the maximal growth factor for stepsize control.
+ *
+ * @return maximal growth factor
+ */
+ public double getMaxGrowth() {
+ return maxGrowth;
+ }
+
+ /**
+ * Set the maximal growth factor for stepsize control.
+ *
+ * @param maxGrowth maximal growth factor
+ */
+ public void setMaxGrowth(final double maxGrowth) {
+ this.maxGrowth = maxGrowth;
+ }
+
+ /**
+ * Get the safety factor for stepsize control.
+ *
+ * @return safety factor
+ */
+ public double getSafety() {
+ return safety;
+ }
+
+ /**
+ * Set the safety factor for stepsize control.
+ *
+ * @param safety safety factor
+ */
+ public void setSafety(final double safety) {
+ this.safety = safety;
+ }
+
+ /**
+ * Get the number of steps of the multistep method (excluding the one being computed).
+ *
+ * @return number of steps of the multistep method (excluding the one being computed)
+ */
+ public int getNSteps() {
+ return nSteps;
+ }
+
+ /**
+ * Rescale the instance.
+ *
+ * <p>Since the scaled and Nordsieck arrays are shared with the caller, this method has the side
+ * effect of rescaling this arrays in the caller too.
+ *
+ * @param newStepSize new step size to use in the scaled and Nordsieck arrays
+ */
+ protected void rescale(final T newStepSize) {
+
+ final T ratio = newStepSize.divide(getStepSize());
+ for (int i = 0; i < scaled.length; ++i) {
+ scaled[i] = scaled[i].multiply(ratio);
+ }
+
+ final T[][] nData = nordsieck.getDataRef();
+ T power = ratio;
+ for (int i = 0; i < nData.length; ++i) {
+ power = power.multiply(ratio);
+ final T[] nDataI = nData[i];
+ for (int j = 0; j < nDataI.length; ++j) {
+ nDataI[j] = nDataI[j].multiply(power);
+ }
+ }
+
+ setStepSize(newStepSize);
+ }
+
+ /**
+ * Compute step grow/shrink factor according to normalized error.
+ *
+ * @param error normalized error of the current step
+ * @return grow/shrink factor for next step
+ */
+ protected T computeStepGrowShrinkFactor(final T error) {
+ return MathUtils.min(
+ error.getField().getZero().add(maxGrowth),
+ MathUtils.max(
+ error.getField().getZero().add(minReduction),
+ error.pow(exp).multiply(safety)));
+ }
+
+ /** Specialized step handler storing the first step. */
+ private class FieldNordsieckInitializer implements FieldStepHandler<T> {
+
+ /** Equation mapper. */
+ private final FieldEquationsMapper<T> mapper;
+
+ /** Steps counter. */
+ private int count;
+
+ /** Saved start. */
+ private FieldODEStateAndDerivative<T> savedStart;
+
+ /** First steps times. */
+ private final T[] t;
+
+ /** First steps states. */
+ private final T[][] y;
+
+ /** First steps derivatives. */
+ private final T[][] yDot;
+
+ /**
+ * Simple constructor.
+ *
+ * @param mapper equation mapper
+ * @param nbStartPoints number of start points (including the initial point)
+ */
+ FieldNordsieckInitializer(final FieldEquationsMapper<T> mapper, final int nbStartPoints) {
+ this.mapper = mapper;
+ this.count = 0;
+ this.t = MathArrays.buildArray(getField(), nbStartPoints);
+ this.y = MathArrays.buildArray(getField(), nbStartPoints, -1);
+ this.yDot = MathArrays.buildArray(getField(), nbStartPoints, -1);
+ }
+
+ /** {@inheritDoc} */
+ public void handleStep(FieldStepInterpolator<T> interpolator, boolean isLast)
+ throws MaxCountExceededException {
+
+ if (count == 0) {
+ // first step, we need to store also the point at the beginning of the step
+ final FieldODEStateAndDerivative<T> prev = interpolator.getPreviousState();
+ savedStart = prev;
+ t[count] = prev.getTime();
+ y[count] = mapper.mapState(prev);
+ yDot[count] = mapper.mapDerivative(prev);
+ }
+
+ // store the point at the end of the step
+ ++count;
+ final FieldODEStateAndDerivative<T> curr = interpolator.getCurrentState();
+ t[count] = curr.getTime();
+ y[count] = mapper.mapState(curr);
+ yDot[count] = mapper.mapDerivative(curr);
+
+ if (count == t.length - 1) {
+
+ // this was the last point we needed, we can compute the derivatives
+ setStepSize(t[t.length - 1].subtract(t[0]).divide(t.length - 1));
+
+ // first scaled derivative
+ scaled = MathArrays.buildArray(getField(), yDot[0].length);
+ for (int j = 0; j < scaled.length; ++j) {
+ scaled[j] = yDot[0][j].multiply(getStepSize());
+ }
+
+ // higher order derivatives
+ nordsieck = initializeHighOrderDerivatives(getStepSize(), t, y, yDot);
+
+ // stop the integrator now that all needed steps have been handled
+ setStepStart(savedStart);
+ throw new InitializationCompletedMarkerException();
+ }
+ }
+
+ /** {@inheritDoc} */
+ public void init(final FieldODEStateAndDerivative<T> initialState, T finalTime) {
+ // nothing to do
+ }
+ }
+
+ /** Marker exception used ONLY to stop the starter integrator after first step. */
+ private static class InitializationCompletedMarkerException extends RuntimeException {
+
+ /** Serializable version identifier. */
+ private static final long serialVersionUID = -1914085471038046418L;
+
+ /** Simple constructor. */
+ InitializationCompletedMarkerException() {
+ super((Throwable) null);
+ }
+ }
+}
diff --git a/src/main/java/org/apache/commons/math3/ode/MultistepIntegrator.java b/src/main/java/org/apache/commons/math3/ode/MultistepIntegrator.java
new file mode 100644
index 0000000..fd76124
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/ode/MultistepIntegrator.java
@@ -0,0 +1,496 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math3.ode;
+
+import org.apache.commons.math3.exception.DimensionMismatchException;
+import org.apache.commons.math3.exception.MathIllegalStateException;
+import org.apache.commons.math3.exception.MaxCountExceededException;
+import org.apache.commons.math3.exception.NoBracketingException;
+import org.apache.commons.math3.exception.NumberIsTooSmallException;
+import org.apache.commons.math3.exception.util.LocalizedFormats;
+import org.apache.commons.math3.linear.Array2DRowRealMatrix;
+import org.apache.commons.math3.ode.nonstiff.AdaptiveStepsizeIntegrator;
+import org.apache.commons.math3.ode.nonstiff.DormandPrince853Integrator;
+import org.apache.commons.math3.ode.sampling.StepHandler;
+import org.apache.commons.math3.ode.sampling.StepInterpolator;
+import org.apache.commons.math3.util.FastMath;
+
+/**
+ * This class is the base class for multistep integrators for Ordinary Differential Equations.
+ *
+ * <p>We define scaled derivatives s<sub>i</sub>(n) at step n as:
+ *
+ * <pre>
+ * s<sub>1</sub>(n) = h y'<sub>n</sub> for first derivative
+ * s<sub>2</sub>(n) = h<sup>2</sup>/2 y''<sub>n</sub> for second derivative
+ * s<sub>3</sub>(n) = h<sup>3</sup>/6 y'''<sub>n</sub> for third derivative
+ * ...
+ * s<sub>k</sub>(n) = h<sup>k</sup>/k! y<sup>(k)</sup><sub>n</sub> for k<sup>th</sup> derivative
+ * </pre>
+ *
+ * <p>Rather than storing several previous steps separately, this implementation uses the Nordsieck
+ * vector with higher degrees scaled derivatives all taken at the same step (y<sub>n</sub>,
+ * s<sub>1</sub>(n) and r<sub>n</sub>) where r<sub>n</sub> is defined as:
+ *
+ * <pre>
+ * r<sub>n</sub> = [ s<sub>2</sub>(n), s<sub>3</sub>(n) ... s<sub>k</sub>(n) ]<sup>T</sup>
+ * </pre>
+ *
+ * (we omit the k index in the notation for clarity)
+ *
+ * <p>Multistep integrators with Nordsieck representation are highly sensitive to large step changes
+ * because when the step is multiplied by factor a, the k<sup>th</sup> component of the Nordsieck
+ * vector is multiplied by a<sup>k</sup> and the last components are the least accurate ones. The
+ * default max growth factor is therefore set to a quite low value: 2<sup>1/order</sup>.
+ *
+ * @see org.apache.commons.math3.ode.nonstiff.AdamsBashforthIntegrator
+ * @see org.apache.commons.math3.ode.nonstiff.AdamsMoultonIntegrator
+ * @since 2.0
+ */
+public abstract class MultistepIntegrator extends AdaptiveStepsizeIntegrator {
+
+ /** First scaled derivative (h y'). */
+ protected double[] scaled;
+
+ /**
+ * Nordsieck matrix of the higher scaled derivatives.
+ *
+ * <p>(h<sup>2</sup>/2 y'', h<sup>3</sup>/6 y''' ..., h<sup>k</sup>/k! y<sup>(k)</sup>)
+ */
+ protected Array2DRowRealMatrix nordsieck;
+
+ /** Starter integrator. */
+ private FirstOrderIntegrator starter;
+
+ /** Number of steps of the multistep method (excluding the one being computed). */
+ private final int nSteps;
+
+ /** Stepsize control exponent. */
+ private double exp;
+
+ /** Safety factor for stepsize control. */
+ private double safety;
+
+ /** Minimal reduction factor for stepsize control. */
+ private double minReduction;
+
+ /** Maximal growth factor for stepsize control. */
+ private double maxGrowth;
+
+ /**
+ * Build a multistep integrator with the given stepsize bounds.
+ *
+ * <p>The default starter integrator is set to the {@link DormandPrince853Integrator
+ * Dormand-Prince 8(5,3)} integrator with some defaults settings.
+ *
+ * <p>The default max growth factor is set to a quite low value: 2<sup>1/order</sup>.
+ *
+ * @param name name of the method
+ * @param nSteps number of steps of the multistep method (excluding the one being computed)
+ * @param order order of the method
+ * @param minStep minimal step (must be positive even for backward integration), the last step
+ * can be smaller than this
+ * @param maxStep maximal step (must be positive even for backward integration)
+ * @param scalAbsoluteTolerance allowed absolute error
+ * @param scalRelativeTolerance allowed relative error
+ * @exception NumberIsTooSmallException if number of steps is smaller than 2
+ */
+ protected MultistepIntegrator(
+ final String name,
+ final int nSteps,
+ final int order,
+ final double minStep,
+ final double maxStep,
+ final double scalAbsoluteTolerance,
+ final double scalRelativeTolerance)
+ throws NumberIsTooSmallException {
+
+ super(name, minStep, maxStep, scalAbsoluteTolerance, scalRelativeTolerance);
+
+ if (nSteps < 2) {
+ throw new NumberIsTooSmallException(
+ LocalizedFormats.INTEGRATION_METHOD_NEEDS_AT_LEAST_TWO_PREVIOUS_POINTS,
+ nSteps,
+ 2,
+ true);
+ }
+
+ starter =
+ new DormandPrince853Integrator(
+ minStep, maxStep, scalAbsoluteTolerance, scalRelativeTolerance);
+ this.nSteps = nSteps;
+
+ exp = -1.0 / order;
+
+ // set the default values of the algorithm control parameters
+ setSafety(0.9);
+ setMinReduction(0.2);
+ setMaxGrowth(FastMath.pow(2.0, -exp));
+ }
+
+ /**
+ * Build a multistep integrator with the given stepsize bounds.
+ *
+ * <p>The default starter integrator is set to the {@link DormandPrince853Integrator
+ * Dormand-Prince 8(5,3)} integrator with some defaults settings.
+ *
+ * <p>The default max growth factor is set to a quite low value: 2<sup>1/order</sup>.
+ *
+ * @param name name of the method
+ * @param nSteps number of steps of the multistep method (excluding the one being computed)
+ * @param order order of the method
+ * @param minStep minimal step (must be positive even for backward integration), the last step
+ * can be smaller than this
+ * @param maxStep maximal step (must be positive even for backward integration)
+ * @param vecAbsoluteTolerance allowed absolute error
+ * @param vecRelativeTolerance allowed relative error
+ */
+ protected MultistepIntegrator(
+ final String name,
+ final int nSteps,
+ final int order,
+ final double minStep,
+ final double maxStep,
+ final double[] vecAbsoluteTolerance,
+ final double[] vecRelativeTolerance) {
+ super(name, minStep, maxStep, vecAbsoluteTolerance, vecRelativeTolerance);
+ starter =
+ new DormandPrince853Integrator(
+ minStep, maxStep, vecAbsoluteTolerance, vecRelativeTolerance);
+ this.nSteps = nSteps;
+
+ exp = -1.0 / order;
+
+ // set the default values of the algorithm control parameters
+ setSafety(0.9);
+ setMinReduction(0.2);
+ setMaxGrowth(FastMath.pow(2.0, -exp));
+ }
+
+ /**
+ * Get the starter integrator.
+ *
+ * @return starter integrator
+ */
+ public ODEIntegrator getStarterIntegrator() {
+ return starter;
+ }
+
+ /**
+ * Set the starter integrator.
+ *
+ * <p>The various step and event handlers for this starter integrator will be managed
+ * automatically by the multi-step integrator. Any user configuration for these elements will be
+ * cleared before use.
+ *
+ * @param starterIntegrator starter integrator
+ */
+ public void setStarterIntegrator(FirstOrderIntegrator starterIntegrator) {
+ this.starter = starterIntegrator;
+ }
+
+ /**
+ * Start the integration.
+ *
+ * <p>This method computes one step using the underlying starter integrator, and initializes the
+ * Nordsieck vector at step start. The starter integrator purpose is only to establish initial
+ * conditions, it does not really change time by itself. The top level multistep integrator
+ * remains in charge of handling time propagation and events handling as it will starts its own
+ * computation right from the beginning. In a sense, the starter integrator can be seen as a
+ * dummy one and so it will never trigger any user event nor call any user step handler.
+ *
+ * @param t0 initial time
+ * @param y0 initial value of the state vector at t0
+ * @param t target time for the integration (can be set to a value smaller than <code>t0</code>
+ * for backward integration)
+ * @exception DimensionMismatchException if arrays dimension do not match equations settings
+ * @exception NumberIsTooSmallException if integration step is too small
+ * @exception MaxCountExceededException if the number of functions evaluations is exceeded
+ * @exception NoBracketingException if the location of an event cannot be bracketed
+ */
+ protected void start(final double t0, final double[] y0, final double t)
+ throws DimensionMismatchException,
+ NumberIsTooSmallException,
+ MaxCountExceededException,
+ NoBracketingException {
+
+ // make sure NO user event nor user step handler is triggered,
+ // this is the task of the top level integrator, not the task
+ // of the starter integrator
+ starter.clearEventHandlers();
+ starter.clearStepHandlers();
+
+ // set up one specific step handler to extract initial Nordsieck vector
+ starter.addStepHandler(new NordsieckInitializer((nSteps + 3) / 2, y0.length));
+
+ // start integration, expecting a InitializationCompletedMarkerException
+ try {
+
+ if (starter instanceof AbstractIntegrator) {
+ ((AbstractIntegrator) starter).integrate(getExpandable(), t);
+ } else {
+ starter.integrate(
+ new FirstOrderDifferentialEquations() {
+
+ /** {@inheritDoc} */
+ public int getDimension() {
+ return getExpandable().getTotalDimension();
+ }
+
+ /** {@inheritDoc} */
+ public void computeDerivatives(double t, double[] y, double[] yDot) {
+ getExpandable().computeDerivatives(t, y, yDot);
+ }
+ },
+ t0,
+ y0,
+ t,
+ new double[y0.length]);
+ }
+
+ // we should not reach this step
+ throw new MathIllegalStateException(LocalizedFormats.MULTISTEP_STARTER_STOPPED_EARLY);
+
+ } catch (InitializationCompletedMarkerException icme) { // NOPMD
+ // this is the expected nominal interruption of the start integrator
+
+ // count the evaluations used by the starter
+ getCounter().increment(starter.getEvaluations());
+ }
+
+ // remove the specific step handler
+ starter.clearStepHandlers();
+ }
+
+ /**
+ * Initialize the high order scaled derivatives at step start.
+ *
+ * @param h step size to use for scaling
+ * @param t first steps times
+ * @param y first steps states
+ * @param yDot first steps derivatives
+ * @return Nordieck vector at first step (h<sup>2</sup>/2 y''<sub>n</sub>, h<sup>3</sup>/6
+ * y'''<sub>n</sub> ... h<sup>k</sup>/k! y<sup>(k)</sup><sub>n</sub>)
+ */
+ protected abstract Array2DRowRealMatrix initializeHighOrderDerivatives(
+ final double h, final double[] t, final double[][] y, final double[][] yDot);
+
+ /**
+ * Get the minimal reduction factor for stepsize control.
+ *
+ * @return minimal reduction factor
+ */
+ public double getMinReduction() {
+ return minReduction;
+ }
+
+ /**
+ * Set the minimal reduction factor for stepsize control.
+ *
+ * @param minReduction minimal reduction factor
+ */
+ public void setMinReduction(final double minReduction) {
+ this.minReduction = minReduction;
+ }
+
+ /**
+ * Get the maximal growth factor for stepsize control.
+ *
+ * @return maximal growth factor
+ */
+ public double getMaxGrowth() {
+ return maxGrowth;
+ }
+
+ /**
+ * Set the maximal growth factor for stepsize control.
+ *
+ * @param maxGrowth maximal growth factor
+ */
+ public void setMaxGrowth(final double maxGrowth) {
+ this.maxGrowth = maxGrowth;
+ }
+
+ /**
+ * Get the safety factor for stepsize control.
+ *
+ * @return safety factor
+ */
+ public double getSafety() {
+ return safety;
+ }
+
+ /**
+ * Set the safety factor for stepsize control.
+ *
+ * @param safety safety factor
+ */
+ public void setSafety(final double safety) {
+ this.safety = safety;
+ }
+
+ /**
+ * Get the number of steps of the multistep method (excluding the one being computed).
+ *
+ * @return number of steps of the multistep method (excluding the one being computed)
+ */
+ public int getNSteps() {
+ return nSteps;
+ }
+
+ /**
+ * Compute step grow/shrink factor according to normalized error.
+ *
+ * @param error normalized error of the current step
+ * @return grow/shrink factor for next step
+ */
+ protected double computeStepGrowShrinkFactor(final double error) {
+ return FastMath.min(
+ maxGrowth, FastMath.max(minReduction, safety * FastMath.pow(error, exp)));
+ }
+
+ /**
+ * Transformer used to convert the first step to Nordsieck representation.
+ *
+ * @deprecated as of 3.6 this unused interface is deprecated
+ */
+ @Deprecated
+ public interface NordsieckTransformer {
+ /**
+ * Initialize the high order scaled derivatives at step start.
+ *
+ * @param h step size to use for scaling
+ * @param t first steps times
+ * @param y first steps states
+ * @param yDot first steps derivatives
+ * @return Nordieck vector at first step (h<sup>2</sup>/2 y''<sub>n</sub>, h<sup>3</sup>/6
+ * y'''<sub>n</sub> ... h<sup>k</sup>/k! y<sup>(k)</sup><sub>n</sub>)
+ */
+ Array2DRowRealMatrix initializeHighOrderDerivatives(
+ final double h, final double[] t, final double[][] y, final double[][] yDot);
+ }
+
+ /** Specialized step handler storing the first step. */
+ private class NordsieckInitializer implements StepHandler {
+
+ /** Steps counter. */
+ private int count;
+
+ /** First steps times. */
+ private final double[] t;
+
+ /** First steps states. */
+ private final double[][] y;
+
+ /** First steps derivatives. */
+ private final double[][] yDot;
+
+ /**
+ * Simple constructor.
+ *
+ * @param nbStartPoints number of start points (including the initial point)
+ * @param n problem dimension
+ */
+ NordsieckInitializer(final int nbStartPoints, final int n) {
+ this.count = 0;
+ this.t = new double[nbStartPoints];
+ this.y = new double[nbStartPoints][n];
+ this.yDot = new double[nbStartPoints][n];
+ }
+
+ /** {@inheritDoc} */
+ public void handleStep(StepInterpolator interpolator, boolean isLast)
+ throws MaxCountExceededException {
+
+ final double prev = interpolator.getPreviousTime();
+ final double curr = interpolator.getCurrentTime();
+
+ if (count == 0) {
+ // first step, we need to store also the point at the beginning of the step
+ interpolator.setInterpolatedTime(prev);
+ t[0] = prev;
+ final ExpandableStatefulODE expandable = getExpandable();
+ final EquationsMapper primary = expandable.getPrimaryMapper();
+ primary.insertEquationData(interpolator.getInterpolatedState(), y[count]);
+ primary.insertEquationData(interpolator.getInterpolatedDerivatives(), yDot[count]);
+ int index = 0;
+ for (final EquationsMapper secondary : expandable.getSecondaryMappers()) {
+ secondary.insertEquationData(
+ interpolator.getInterpolatedSecondaryState(index), y[count]);
+ secondary.insertEquationData(
+ interpolator.getInterpolatedSecondaryDerivatives(index), yDot[count]);
+ ++index;
+ }
+ }
+
+ // store the point at the end of the step
+ ++count;
+ interpolator.setInterpolatedTime(curr);
+ t[count] = curr;
+
+ final ExpandableStatefulODE expandable = getExpandable();
+ final EquationsMapper primary = expandable.getPrimaryMapper();
+ primary.insertEquationData(interpolator.getInterpolatedState(), y[count]);
+ primary.insertEquationData(interpolator.getInterpolatedDerivatives(), yDot[count]);
+ int index = 0;
+ for (final EquationsMapper secondary : expandable.getSecondaryMappers()) {
+ secondary.insertEquationData(
+ interpolator.getInterpolatedSecondaryState(index), y[count]);
+ secondary.insertEquationData(
+ interpolator.getInterpolatedSecondaryDerivatives(index), yDot[count]);
+ ++index;
+ }
+
+ if (count == t.length - 1) {
+
+ // this was the last point we needed, we can compute the derivatives
+ stepStart = t[0];
+ stepSize = (t[t.length - 1] - t[0]) / (t.length - 1);
+
+ // first scaled derivative
+ scaled = yDot[0].clone();
+ for (int j = 0; j < scaled.length; ++j) {
+ scaled[j] *= stepSize;
+ }
+
+ // higher order derivatives
+ nordsieck = initializeHighOrderDerivatives(stepSize, t, y, yDot);
+
+ // stop the integrator now that all needed steps have been handled
+ throw new InitializationCompletedMarkerException();
+ }
+ }
+
+ /** {@inheritDoc} */
+ public void init(double t0, double[] y0, double time) {
+ // nothing to do
+ }
+ }
+
+ /** Marker exception used ONLY to stop the starter integrator after first step. */
+ private static class InitializationCompletedMarkerException extends RuntimeException {
+
+ /** Serializable version identifier. */
+ private static final long serialVersionUID = -1914085471038046418L;
+
+ /** Simple constructor. */
+ InitializationCompletedMarkerException() {
+ super((Throwable) null);
+ }
+ }
+}
diff --git a/src/main/java/org/apache/commons/math3/ode/ODEIntegrator.java b/src/main/java/org/apache/commons/math3/ode/ODEIntegrator.java
new file mode 100644
index 0000000..f661960
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/ode/ODEIntegrator.java
@@ -0,0 +1,185 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math3.ode;
+
+import org.apache.commons.math3.analysis.solvers.UnivariateSolver;
+import org.apache.commons.math3.ode.events.EventHandler;
+import org.apache.commons.math3.ode.sampling.StepHandler;
+
+import java.util.Collection;
+
+/**
+ * This interface defines the common parts shared by integrators for first and second order
+ * differential equations.
+ *
+ * @see FirstOrderIntegrator
+ * @see SecondOrderIntegrator
+ * @since 2.0
+ */
+public interface ODEIntegrator {
+
+ /**
+ * Get the name of the method.
+ *
+ * @return name of the method
+ */
+ String getName();
+
+ /**
+ * Add a step handler to this integrator.
+ *
+ * <p>The handler will be called by the integrator for each accepted step.
+ *
+ * @param handler handler for the accepted steps
+ * @see #getStepHandlers()
+ * @see #clearStepHandlers()
+ * @since 2.0
+ */
+ void addStepHandler(StepHandler handler);
+
+ /**
+ * Get all the step handlers that have been added to the integrator.
+ *
+ * @return an unmodifiable collection of the added events handlers
+ * @see #addStepHandler(StepHandler)
+ * @see #clearStepHandlers()
+ * @since 2.0
+ */
+ Collection<StepHandler> getStepHandlers();
+
+ /**
+ * Remove all the step handlers that have been added to the integrator.
+ *
+ * @see #addStepHandler(StepHandler)
+ * @see #getStepHandlers()
+ * @since 2.0
+ */
+ void clearStepHandlers();
+
+ /**
+ * Add an event handler to the integrator. Uses a default {@link UnivariateSolver} with an
+ * absolute accuracy equal to the given convergence threshold, as root-finding algorithm to
+ * detect the state events.
+ *
+ * @param handler event handler
+ * @param maxCheckInterval maximal time interval between switching function checks (this
+ * interval prevents missing sign changes in case the integration steps becomes very large)
+ * @param convergence convergence threshold in the event time search
+ * @param maxIterationCount upper limit of the iteration count in the event time search
+ * @see #getEventHandlers()
+ * @see #clearEventHandlers()
+ */
+ void addEventHandler(
+ EventHandler handler,
+ double maxCheckInterval,
+ double convergence,
+ int maxIterationCount);
+
+ /**
+ * Add an event handler to the integrator.
+ *
+ * @param handler event handler
+ * @param maxCheckInterval maximal time interval between switching function checks (this
+ * interval prevents missing sign changes in case the integration steps becomes very large)
+ * @param convergence convergence threshold in the event time search
+ * @param maxIterationCount upper limit of the iteration count in the event time search
+ * @param solver The root-finding algorithm to use to detect the state events.
+ * @see #getEventHandlers()
+ * @see #clearEventHandlers()
+ */
+ void addEventHandler(
+ EventHandler handler,
+ double maxCheckInterval,
+ double convergence,
+ int maxIterationCount,
+ UnivariateSolver solver);
+
+ /**
+ * Get all the event handlers that have been added to the integrator.
+ *
+ * @return an unmodifiable collection of the added events handlers
+ * @see #addEventHandler(EventHandler, double, double, int)
+ * @see #clearEventHandlers()
+ */
+ Collection<EventHandler> getEventHandlers();
+
+ /**
+ * Remove all the event handlers that have been added to the integrator.
+ *
+ * @see #addEventHandler(EventHandler, double, double, int)
+ * @see #getEventHandlers()
+ */
+ void clearEventHandlers();
+
+ /**
+ * Get the current value of the step start time t<sub>i</sub>.
+ *
+ * <p>This method can be called during integration (typically by the object implementing the
+ * {@link FirstOrderDifferentialEquations differential equations} problem) if the value of the
+ * current step that is attempted is needed.
+ *
+ * <p>The result is undefined if the method is called outside of calls to <code>integrate</code>
+ * .
+ *
+ * @return current value of the step start time t<sub>i</sub>
+ */
+ double getCurrentStepStart();
+
+ /**
+ * Get the current signed value of the integration stepsize.
+ *
+ * <p>This method can be called during integration (typically by the object implementing the
+ * {@link FirstOrderDifferentialEquations differential equations} problem) if the signed value
+ * of the current stepsize that is tried is needed.
+ *
+ * <p>The result is undefined if the method is called outside of calls to <code>integrate</code>
+ * .
+ *
+ * @return current signed value of the stepsize
+ */
+ double getCurrentSignedStepsize();
+
+ /**
+ * Set the maximal number of differential equations function evaluations.
+ *
+ * <p>The purpose of this method is to avoid infinite loops which can occur for example when
+ * stringent error constraints are set or when lots of discrete events are triggered, thus
+ * leading to many rejected steps.
+ *
+ * @param maxEvaluations maximal number of function evaluations (negative values are silently
+ * converted to maximal integer value, thus representing almost unlimited evaluations)
+ */
+ void setMaxEvaluations(int maxEvaluations);
+
+ /**
+ * Get the maximal number of functions evaluations.
+ *
+ * @return maximal number of functions evaluations
+ */
+ int getMaxEvaluations();
+
+ /**
+ * Get the number of evaluations of the differential equations function.
+ *
+ * <p>The number of evaluations corresponds to the last call to the <code>integrate</code>
+ * method. It is 0 if the method has not been called yet.
+ *
+ * @return number of evaluations of the differential equations function
+ */
+ int getEvaluations();
+}
diff --git a/src/main/java/org/apache/commons/math3/ode/ParameterConfiguration.java b/src/main/java/org/apache/commons/math3/ode/ParameterConfiguration.java
new file mode 100644
index 0000000..fe475a5
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/ode/ParameterConfiguration.java
@@ -0,0 +1,75 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math3.ode;
+
+import java.io.Serializable;
+
+/**
+ * Simple container pairing a parameter name with a step in order to compute the associated Jacobian
+ * matrix by finite difference.
+ *
+ * @since 3.0
+ */
+class ParameterConfiguration implements Serializable {
+
+ /** Serializable UID. */
+ private static final long serialVersionUID = 2247518849090889379L;
+
+ /** Parameter name. */
+ private String parameterName;
+
+ /** Parameter step for finite difference computation. */
+ private double hP;
+
+ /**
+ * Parameter name and step pair constructor.
+ *
+ * @param parameterName parameter name
+ * @param hP parameter step
+ */
+ ParameterConfiguration(final String parameterName, final double hP) {
+ this.parameterName = parameterName;
+ this.hP = hP;
+ }
+
+ /**
+ * Get parameter name.
+ *
+ * @return parameterName parameter name
+ */
+ public String getParameterName() {
+ return parameterName;
+ }
+
+ /**
+ * Get parameter step.
+ *
+ * @return hP parameter step
+ */
+ public double getHP() {
+ return hP;
+ }
+
+ /**
+ * Set parameter step.
+ *
+ * @param hParam parameter step
+ */
+ public void setHP(final double hParam) {
+ this.hP = hParam;
+ }
+}
diff --git a/src/main/java/org/apache/commons/math3/ode/ParameterJacobianProvider.java b/src/main/java/org/apache/commons/math3/ode/ParameterJacobianProvider.java
new file mode 100644
index 0000000..eac6689
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/ode/ParameterJacobianProvider.java
@@ -0,0 +1,50 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math3.ode;
+
+import org.apache.commons.math3.exception.DimensionMismatchException;
+import org.apache.commons.math3.exception.MaxCountExceededException;
+
+/**
+ * Interface to compute exactly Jacobian matrix for some parameter when computing {@link
+ * JacobianMatrices partial derivatives equations}.
+ *
+ * @since 3.0
+ */
+public interface ParameterJacobianProvider extends Parameterizable {
+
+ /**
+ * Compute the Jacobian matrix of ODE with respect to one parameter.
+ *
+ * <p>If the parameter does not belong to the collection returned by {@link
+ * #getParametersNames()}, the Jacobian will be set to 0, but no errors will be triggered.
+ *
+ * @param t current value of the independent <I>time</I> variable
+ * @param y array containing the current value of the main state vector
+ * @param yDot array containing the current value of the time derivative of the main state
+ * vector
+ * @param paramName name of the parameter to consider
+ * @param dFdP placeholder array where to put the Jacobian matrix of the ODE with respect to the
+ * parameter
+ * @exception MaxCountExceededException if the number of functions evaluations is exceeded
+ * @exception DimensionMismatchException if arrays dimensions do not match equations settings
+ * @exception UnknownParameterException if the parameter is not supported
+ */
+ void computeParameterJacobian(
+ double t, double[] y, double[] yDot, String paramName, double[] dFdP)
+ throws DimensionMismatchException, MaxCountExceededException, UnknownParameterException;
+}
diff --git a/src/main/java/org/apache/commons/math3/ode/ParameterJacobianWrapper.java b/src/main/java/org/apache/commons/math3/ode/ParameterJacobianWrapper.java
new file mode 100644
index 0000000..351d829
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/ode/ParameterJacobianWrapper.java
@@ -0,0 +1,103 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math3.ode;
+
+import org.apache.commons.math3.exception.DimensionMismatchException;
+import org.apache.commons.math3.exception.MaxCountExceededException;
+
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * Wrapper class to compute Jacobian matrices by finite differences for ODE which do not compute
+ * them by themselves.
+ *
+ * @since 3.0
+ */
+class ParameterJacobianWrapper implements ParameterJacobianProvider {
+
+ /** Main ODE set. */
+ private final FirstOrderDifferentialEquations fode;
+
+ /**
+ * Raw ODE without Jacobian computation skill to be wrapped into a ParameterJacobianProvider.
+ */
+ private final ParameterizedODE pode;
+
+ /** Steps for finite difference computation of the Jacobian df/dp w.r.t. parameters. */
+ private final Map<String, Double> hParam;
+
+ /**
+ * Wrap a {@link ParameterizedODE} into a {@link ParameterJacobianProvider}.
+ *
+ * @param fode main first order differential equations set
+ * @param pode secondary problem, without parameter Jacobian computation skill
+ * @param paramsAndSteps parameters and steps to compute the Jacobians df/dp
+ * @see JacobianMatrices#setParameterStep(String, double)
+ */
+ ParameterJacobianWrapper(
+ final FirstOrderDifferentialEquations fode,
+ final ParameterizedODE pode,
+ final ParameterConfiguration[] paramsAndSteps) {
+ this.fode = fode;
+ this.pode = pode;
+ this.hParam = new HashMap<String, Double>();
+
+ // set up parameters for jacobian computation
+ for (final ParameterConfiguration param : paramsAndSteps) {
+ final String name = param.getParameterName();
+ if (pode.isSupported(name)) {
+ hParam.put(name, param.getHP());
+ }
+ }
+ }
+
+ /** {@inheritDoc} */
+ public Collection<String> getParametersNames() {
+ return pode.getParametersNames();
+ }
+
+ /** {@inheritDoc} */
+ public boolean isSupported(String name) {
+ return pode.isSupported(name);
+ }
+
+ /** {@inheritDoc} */
+ public void computeParameterJacobian(
+ double t, double[] y, double[] yDot, String paramName, double[] dFdP)
+ throws DimensionMismatchException, MaxCountExceededException {
+
+ final int n = fode.getDimension();
+ if (pode.isSupported(paramName)) {
+ final double[] tmpDot = new double[n];
+
+ // compute the jacobian df/dp w.r.t. parameter
+ final double p = pode.getParameter(paramName);
+ final double hP = hParam.get(paramName);
+ pode.setParameter(paramName, p + hP);
+ fode.computeDerivatives(t, y, tmpDot);
+ for (int i = 0; i < n; ++i) {
+ dFdP[i] = (tmpDot[i] - yDot[i]) / hP;
+ }
+ pode.setParameter(paramName, p);
+ } else {
+ Arrays.fill(dFdP, 0, n, 0.0);
+ }
+ }
+}
diff --git a/src/main/java/org/apache/commons/math3/ode/Parameterizable.java b/src/main/java/org/apache/commons/math3/ode/Parameterizable.java
new file mode 100644
index 0000000..40371cb
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/ode/Parameterizable.java
@@ -0,0 +1,46 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math3.ode;
+
+import java.util.Collection;
+
+/**
+ * This interface enables to process any parameterizable object.
+ *
+ * @since 3.0
+ */
+public interface Parameterizable {
+
+ /**
+ * Get the names of the supported parameters.
+ *
+ * @return parameters names
+ * @see #isSupported(String)
+ */
+ Collection<String> getParametersNames();
+
+ /**
+ * Check if a parameter is supported.
+ *
+ * <p>Supported parameters are those listed by {@link #getParametersNames()}.
+ *
+ * @param name parameter name to check
+ * @return true if the parameter is supported
+ * @see #getParametersNames()
+ */
+ boolean isSupported(String name);
+}
diff --git a/src/main/java/org/apache/commons/math3/ode/ParameterizedODE.java b/src/main/java/org/apache/commons/math3/ode/ParameterizedODE.java
new file mode 100644
index 0000000..ab597c6
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/ode/ParameterizedODE.java
@@ -0,0 +1,44 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math3.ode;
+
+/**
+ * Interface to compute by finite difference Jacobian matrix for some parameter when computing
+ * {@link JacobianMatrices partial derivatives equations}.
+ *
+ * @since 3.0
+ */
+public interface ParameterizedODE extends Parameterizable {
+
+ /**
+ * Get parameter value from its name.
+ *
+ * @param name parameter name
+ * @return parameter value
+ * @exception UnknownParameterException if parameter is not supported
+ */
+ double getParameter(String name) throws UnknownParameterException;
+
+ /**
+ * Set the value for a given parameter.
+ *
+ * @param name parameter name
+ * @param value parameter value
+ * @exception UnknownParameterException if parameter is not supported
+ */
+ void setParameter(String name, double value) throws UnknownParameterException;
+}
diff --git a/src/main/java/org/apache/commons/math3/ode/ParameterizedWrapper.java b/src/main/java/org/apache/commons/math3/ode/ParameterizedWrapper.java
new file mode 100644
index 0000000..601ce5f
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/ode/ParameterizedWrapper.java
@@ -0,0 +1,88 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math3.ode;
+
+import org.apache.commons.math3.exception.DimensionMismatchException;
+import org.apache.commons.math3.exception.MaxCountExceededException;
+
+import java.util.ArrayList;
+import java.util.Collection;
+
+/**
+ * Wrapper class enabling {@link FirstOrderDifferentialEquations basic simple} ODE instances to be
+ * used when processing {@link JacobianMatrices}.
+ *
+ * @since 3.0
+ */
+class ParameterizedWrapper implements ParameterizedODE {
+
+ /** Basic FODE without parameter. */
+ private final FirstOrderDifferentialEquations fode;
+
+ /**
+ * Simple constructor.
+ *
+ * @param ode original first order differential equations
+ */
+ ParameterizedWrapper(final FirstOrderDifferentialEquations ode) {
+ this.fode = ode;
+ }
+
+ /**
+ * Get the dimension of the underlying FODE.
+ *
+ * @return dimension of the underlying FODE
+ */
+ public int getDimension() {
+ return fode.getDimension();
+ }
+
+ /**
+ * Get the current time derivative of the state vector of the underlying FODE.
+ *
+ * @param t current value of the independent <I>time</I> variable
+ * @param y array containing the current value of the state vector
+ * @param yDot placeholder array where to put the time derivative of the state vector
+ * @exception MaxCountExceededException if the number of functions evaluations is exceeded
+ * @exception DimensionMismatchException if arrays dimensions do not match equations settings
+ */
+ public void computeDerivatives(double t, double[] y, double[] yDot)
+ throws MaxCountExceededException, DimensionMismatchException {
+ fode.computeDerivatives(t, y, yDot);
+ }
+
+ /** {@inheritDoc} */
+ public Collection<String> getParametersNames() {
+ return new ArrayList<String>();
+ }
+
+ /** {@inheritDoc} */
+ public boolean isSupported(String name) {
+ return false;
+ }
+
+ /** {@inheritDoc} */
+ public double getParameter(String name) throws UnknownParameterException {
+ if (!isSupported(name)) {
+ throw new UnknownParameterException(name);
+ }
+ return Double.NaN;
+ }
+
+ /** {@inheritDoc} */
+ public void setParameter(String name, double value) {}
+}
diff --git a/src/main/java/org/apache/commons/math3/ode/SecondOrderDifferentialEquations.java b/src/main/java/org/apache/commons/math3/ode/SecondOrderDifferentialEquations.java
new file mode 100644
index 0000000..4f7f6bc
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/ode/SecondOrderDifferentialEquations.java
@@ -0,0 +1,59 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math3.ode;
+
+/**
+ * This interface represents a second order differential equations set.
+ *
+ * <p>This interface should be implemented by all real second order differential equation problems
+ * before they can be handled by the integrators {@link SecondOrderIntegrator#integrate} method.
+ *
+ * <p>A second order differential equations problem, as seen by an integrator is the second time
+ * derivative <code>d2Y/dt^2</code> of a state vector <code>Y</code>, both being one dimensional
+ * arrays. From the integrator point of view, this derivative depends only on the current time
+ * <code>t</code>, on the state vector <code>Y</code> and on the first time derivative of the state
+ * vector.
+ *
+ * <p>For real problems, the derivative depends also on parameters that do not belong to the state
+ * vector (dynamical model constants for example). These constants are completely outside of the
+ * scope of this interface, the classes that implement it are allowed to handle them as they want.
+ *
+ * @see SecondOrderIntegrator
+ * @see FirstOrderConverter
+ * @see FirstOrderDifferentialEquations
+ * @since 1.2
+ */
+public interface SecondOrderDifferentialEquations {
+
+ /**
+ * Get the dimension of the problem.
+ *
+ * @return dimension of the problem
+ */
+ int getDimension();
+
+ /**
+ * Get the current time derivative of the state vector.
+ *
+ * @param t current value of the independent <I>time</I> variable
+ * @param y array containing the current value of the state vector
+ * @param yDot array containing the current value of the first derivative of the state vector
+ * @param yDDot placeholder array where to put the second time derivative of the state vector
+ */
+ void computeSecondDerivatives(double t, double[] y, double[] yDot, double[] yDDot);
+}
diff --git a/src/main/java/org/apache/commons/math3/ode/SecondOrderIntegrator.java b/src/main/java/org/apache/commons/math3/ode/SecondOrderIntegrator.java
new file mode 100644
index 0000000..4df6c65
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/ode/SecondOrderIntegrator.java
@@ -0,0 +1,61 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math3.ode;
+
+import org.apache.commons.math3.exception.MathIllegalArgumentException;
+import org.apache.commons.math3.exception.MathIllegalStateException;
+
+/**
+ * This interface represents a second order integrator for differential equations.
+ *
+ * <p>The classes which are devoted to solve second order differential equations should implement
+ * this interface. The problems which can be handled should implement the {@link
+ * SecondOrderDifferentialEquations} interface.
+ *
+ * @see SecondOrderDifferentialEquations
+ * @since 1.2
+ */
+public interface SecondOrderIntegrator extends ODEIntegrator {
+
+ /**
+ * Integrate the differential equations up to the given time
+ *
+ * @param equations differential equations to integrate
+ * @param t0 initial time
+ * @param y0 initial value of the state vector at t0
+ * @param yDot0 initial value of the first derivative of the state vector at t0
+ * @param t target time for the integration (can be set to a value smaller thant <code>t0</code>
+ * for backward integration)
+ * @param y placeholder where to put the state vector at each successful step (and hence at the
+ * end of integration), can be the same object as y0
+ * @param yDot placeholder where to put the first derivative of the state vector at time t, can
+ * be the same object as yDot0
+ * @throws MathIllegalStateException if the integrator cannot perform integration
+ * @throws MathIllegalArgumentException if integration parameters are wrong (typically too small
+ * integration span)
+ */
+ void integrate(
+ SecondOrderDifferentialEquations equations,
+ double t0,
+ double[] y0,
+ double[] yDot0,
+ double t,
+ double[] y,
+ double[] yDot)
+ throws MathIllegalStateException, MathIllegalArgumentException;
+}
diff --git a/src/main/java/org/apache/commons/math3/ode/SecondaryEquations.java b/src/main/java/org/apache/commons/math3/ode/SecondaryEquations.java
new file mode 100644
index 0000000..95fed2c
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/ode/SecondaryEquations.java
@@ -0,0 +1,66 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math3.ode;
+
+import org.apache.commons.math3.exception.DimensionMismatchException;
+import org.apache.commons.math3.exception.MaxCountExceededException;
+
+/**
+ * This interface allows users to add secondary differential equations to a primary set of
+ * differential equations.
+ *
+ * <p>In some cases users may need to integrate some problem-specific equations along with a primary
+ * set of differential equations. One example is optimal control where adjoined parameters linked to
+ * the minimized hamiltonian must be integrated.
+ *
+ * <p>This interface allows users to add such equations to a primary set of {@link
+ * FirstOrderDifferentialEquations first order differential equations} thanks to the {@link
+ * ExpandableStatefulODE#addSecondaryEquations(SecondaryEquations)} method.
+ *
+ * @see ExpandableStatefulODE
+ * @since 3.0
+ */
+public interface SecondaryEquations {
+
+ /**
+ * Get the dimension of the secondary state parameters.
+ *
+ * @return dimension of the secondary state parameters
+ */
+ int getDimension();
+
+ /**
+ * Compute the derivatives related to the secondary state parameters.
+ *
+ * @param t current value of the independent <I>time</I> variable
+ * @param primary array containing the current value of the primary state vector
+ * @param primaryDot array containing the derivative of the primary state vector
+ * @param secondary array containing the current value of the secondary state vector
+ * @param secondaryDot placeholder array where to put the derivative of the secondary state
+ * vector
+ * @exception MaxCountExceededException if the number of functions evaluations is exceeded
+ * @exception DimensionMismatchException if arrays dimensions do not match equations settings
+ */
+ void computeDerivatives(
+ double t,
+ double[] primary,
+ double[] primaryDot,
+ double[] secondary,
+ double[] secondaryDot)
+ throws MaxCountExceededException, DimensionMismatchException;
+}
diff --git a/src/main/java/org/apache/commons/math3/ode/UnknownParameterException.java b/src/main/java/org/apache/commons/math3/ode/UnknownParameterException.java
new file mode 100644
index 0000000..57d31bf
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/ode/UnknownParameterException.java
@@ -0,0 +1,51 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math3.ode;
+
+import org.apache.commons.math3.exception.MathIllegalArgumentException;
+import org.apache.commons.math3.exception.util.LocalizedFormats;
+
+/**
+ * Exception to be thrown when a parameter is unknown.
+ *
+ * @since 3.1
+ */
+public class UnknownParameterException extends MathIllegalArgumentException {
+
+ /** Serializable version Id. */
+ private static final long serialVersionUID = 20120902L;
+
+ /** Parameter name. */
+ private final String name;
+
+ /**
+ * Construct an exception from the unknown parameter.
+ *
+ * @param name parameter name.
+ */
+ public UnknownParameterException(final String name) {
+ super(LocalizedFormats.UNKNOWN_PARAMETER, name);
+ this.name = name;
+ }
+
+ /**
+ * @return the name of the unknown parameter.
+ */
+ public String getName() {
+ return name;
+ }
+}
diff --git a/src/main/java/org/apache/commons/math3/ode/events/Action.java b/src/main/java/org/apache/commons/math3/ode/events/Action.java
new file mode 100644
index 0000000..ea7fa51
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/ode/events/Action.java
@@ -0,0 +1,56 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math3.ode.events;
+
+/** Enumerate for actions to be performed when an event occurs during ODE integration.
+ * @since 3.6
+ */
+public enum Action {
+
+ /** Stop indicator.
+ * <p>This value should be used as the return value of the {@code
+ * eventOccurred} method when the integration should be
+ * stopped after the event ending the current step.</p>
+ */
+ STOP,
+
+ /** Reset state indicator.
+ * <p>This value should be used as the return value of the {@code
+ * eventOccurred}} method when the integration should
+ * go on after the event ending the current step, with a new state
+ * vector (which will be retrieved thanks to the {@code resetState}
+ * method).</p>
+ */
+ RESET_STATE,
+
+ /** Reset derivatives indicator.
+ * <p>This value should be used as the return value of the {@code
+ * eventOccurred} method when the integration should
+ * go on after the event ending the current step, with a new derivatives
+ * vector.</p>
+ */
+ RESET_DERIVATIVES,
+
+ /** Continue indicator.
+ * <p>This value should be used as the return value of the {@code
+ * eventOccurred} method when the integration should go
+ * on after the event ending the current step.</p>
+ */
+ CONTINUE;
+
+}
diff --git a/src/main/java/org/apache/commons/math3/ode/events/EventFilter.java b/src/main/java/org/apache/commons/math3/ode/events/EventFilter.java
new file mode 100644
index 0000000..ffc2715
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/ode/events/EventFilter.java
@@ -0,0 +1,204 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math3.ode.events;
+
+import java.util.Arrays;
+
+/** Wrapper used to detect only increasing or decreasing events.
+ *
+ * <p>General {@link EventHandler events} are defined implicitly
+ * by a {@link EventHandler#g(double, double[]) g function} crossing
+ * zero. This function needs to be continuous in the event neighborhood,
+ * and its sign must remain consistent between events. This implies that
+ * during an ODE integration, events triggered are alternately events
+ * for which the function increases from negative to positive values,
+ * and events for which the function decreases from positive to
+ * negative values.
+ * </p>
+ *
+ * <p>Sometimes, users are only interested in one type of event (say
+ * increasing events for example) and not in the other type. In these
+ * cases, looking precisely for all events location and triggering
+ * events that will later be ignored is a waste of computing time.</p>
+ *
+ * <p>Users can wrap a regular {@link EventHandler event handler} in
+ * an instance of this class and provide this wrapping instance to
+ * the {@link org.apache.commons.math3.ode.FirstOrderIntegrator ODE solver}
+ * in order to avoid wasting time looking for uninteresting events.
+ * The wrapper will intercept the calls to the {@link
+ * EventHandler#g(double, double[]) g function} and to the {@link
+ * EventHandler#eventOccurred(double, double[], boolean)
+ * eventOccurred} method in order to ignore uninteresting events. The
+ * wrapped regular {@link EventHandler event handler} will the see only
+ * the interesting events, i.e. either only {@code increasing} events or
+ * {@code decreasing} events. the number of calls to the {@link
+ * EventHandler#g(double, double[]) g function} will also be reduced.</p>
+ *
+ * @since 3.2
+ */
+
+public class EventFilter implements EventHandler {
+
+ /** Number of past transformers updates stored. */
+ private static final int HISTORY_SIZE = 100;
+
+ /** Wrapped event handler. */
+ private final EventHandler rawHandler;
+
+ /** Filter to use. */
+ private final FilterType filter;
+
+ /** Transformers of the g function. */
+ private final Transformer[] transformers;
+
+ /** Update time of the transformers. */
+ private final double[] updates;
+
+ /** Indicator for forward integration. */
+ private boolean forward;
+
+ /** Extreme time encountered so far. */
+ private double extremeT;
+
+ /** Wrap an {@link EventHandler event handler}.
+ * @param rawHandler event handler to wrap
+ * @param filter filter to use
+ */
+ public EventFilter(final EventHandler rawHandler, final FilterType filter) {
+ this.rawHandler = rawHandler;
+ this.filter = filter;
+ this.transformers = new Transformer[HISTORY_SIZE];
+ this.updates = new double[HISTORY_SIZE];
+ }
+
+ /** {@inheritDoc} */
+ public void init(double t0, double[] y0, double t) {
+
+ // delegate to raw handler
+ rawHandler.init(t0, y0, t);
+
+ // initialize events triggering logic
+ forward = t >= t0;
+ extremeT = forward ? Double.NEGATIVE_INFINITY : Double.POSITIVE_INFINITY;
+ Arrays.fill(transformers, Transformer.UNINITIALIZED);
+ Arrays.fill(updates, extremeT);
+
+ }
+
+ /** {@inheritDoc} */
+ public double g(double t, double[] y) {
+
+ final double rawG = rawHandler.g(t, y);
+
+ // search which transformer should be applied to g
+ if (forward) {
+ final int last = transformers.length - 1;
+ if (extremeT < t) {
+ // we are at the forward end of the history
+
+ // check if a new rough root has been crossed
+ final Transformer previous = transformers[last];
+ final Transformer next = filter.selectTransformer(previous, rawG, forward);
+ if (next != previous) {
+ // there is a root somewhere between extremeT and t.
+ // the new transformer is valid for t (this is how we have just computed
+ // it above), but it is in fact valid on both sides of the root, so
+ // it was already valid before t and even up to previous time. We store
+ // the switch at extremeT for safety, to ensure the previous transformer
+ // is not applied too close of the root
+ System.arraycopy(updates, 1, updates, 0, last);
+ System.arraycopy(transformers, 1, transformers, 0, last);
+ updates[last] = extremeT;
+ transformers[last] = next;
+ }
+
+ extremeT = t;
+
+ // apply the transform
+ return next.transformed(rawG);
+
+ } else {
+ // we are in the middle of the history
+
+ // select the transformer
+ for (int i = last; i > 0; --i) {
+ if (updates[i] <= t) {
+ // apply the transform
+ return transformers[i].transformed(rawG);
+ }
+ }
+
+ return transformers[0].transformed(rawG);
+
+ }
+ } else {
+ if (t < extremeT) {
+ // we are at the backward end of the history
+
+ // check if a new rough root has been crossed
+ final Transformer previous = transformers[0];
+ final Transformer next = filter.selectTransformer(previous, rawG, forward);
+ if (next != previous) {
+ // there is a root somewhere between extremeT and t.
+ // the new transformer is valid for t (this is how we have just computed
+ // it above), but it is in fact valid on both sides of the root, so
+ // it was already valid before t and even up to previous time. We store
+ // the switch at extremeT for safety, to ensure the previous transformer
+ // is not applied too close of the root
+ System.arraycopy(updates, 0, updates, 1, updates.length - 1);
+ System.arraycopy(transformers, 0, transformers, 1, transformers.length - 1);
+ updates[0] = extremeT;
+ transformers[0] = next;
+ }
+
+ extremeT = t;
+
+ // apply the transform
+ return next.transformed(rawG);
+
+ } else {
+ // we are in the middle of the history
+
+ // select the transformer
+ for (int i = 0; i < updates.length - 1; ++i) {
+ if (t <= updates[i]) {
+ // apply the transform
+ return transformers[i].transformed(rawG);
+ }
+ }
+
+ return transformers[updates.length - 1].transformed(rawG);
+
+ }
+ }
+
+ }
+
+ /** {@inheritDoc} */
+ public Action eventOccurred(double t, double[] y, boolean increasing) {
+ // delegate to raw handler, fixing increasing status on the fly
+ return rawHandler.eventOccurred(t, y, filter.getTriggeredIncreasing());
+ }
+
+ /** {@inheritDoc} */
+ public void resetState(double t, double[] y) {
+ // delegate to raw handler
+ rawHandler.resetState(t, y);
+ }
+
+}
diff --git a/src/main/java/org/apache/commons/math3/ode/events/EventHandler.java b/src/main/java/org/apache/commons/math3/ode/events/EventHandler.java
new file mode 100644
index 0000000..58533bc
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/ode/events/EventHandler.java
@@ -0,0 +1,219 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math3.ode.events;
+
+
+/** This interface represents a handler for discrete events triggered
+ * during ODE integration.
+ *
+ * <p>Some events can be triggered at discrete times as an ODE problem
+ * is solved. This occurs for example when the integration process
+ * should be stopped as some state is reached (G-stop facility) when the
+ * precise date is unknown a priori, or when the derivatives have
+ * discontinuities, or simply when the user wants to monitor some
+ * states boundaries crossings.
+ * </p>
+ *
+ * <p>These events are defined as occurring when a <code>g</code>
+ * switching function sign changes.</p>
+ *
+ * <p>Since events are only problem-dependent and are triggered by the
+ * independent <i>time</i> variable and the state vector, they can
+ * occur at virtually any time, unknown in advance. The integrators will
+ * take care to avoid sign changes inside the steps, they will reduce
+ * the step size when such an event is detected in order to put this
+ * event exactly at the end of the current step. This guarantees that
+ * step interpolation (which always has a one step scope) is relevant
+ * even in presence of discontinuities. This is independent from the
+ * stepsize control provided by integrators that monitor the local
+ * error (this event handling feature is available for all integrators,
+ * including fixed step ones).</p>
+ *
+ * @since 1.2
+ */
+
+public interface EventHandler {
+
+ /** Enumerate for actions to be performed when an event occurs. */
+ enum Action {
+
+ /** Stop indicator.
+ * <p>This value should be used as the return value of the {@link
+ * #eventOccurred eventOccurred} method when the integration should be
+ * stopped after the event ending the current step.</p>
+ */
+ STOP,
+
+ /** Reset state indicator.
+ * <p>This value should be used as the return value of the {@link
+ * #eventOccurred eventOccurred} method when the integration should
+ * go on after the event ending the current step, with a new state
+ * vector (which will be retrieved thanks to the {@link #resetState
+ * resetState} method).</p>
+ */
+ RESET_STATE,
+
+ /** Reset derivatives indicator.
+ * <p>This value should be used as the return value of the {@link
+ * #eventOccurred eventOccurred} method when the integration should
+ * go on after the event ending the current step, with a new derivatives
+ * vector (which will be retrieved thanks to the {@link
+ * org.apache.commons.math3.ode.FirstOrderDifferentialEquations#computeDerivatives}
+ * method).</p>
+ */
+ RESET_DERIVATIVES,
+
+ /** Continue indicator.
+ * <p>This value should be used as the return value of the {@link
+ * #eventOccurred eventOccurred} method when the integration should go
+ * on after the event ending the current step.</p>
+ */
+ CONTINUE;
+
+ }
+
+ /** Initialize event handler at the start of an ODE integration.
+ * <p>
+ * This method is called once at the start of the integration. It
+ * may be used by the event handler to initialize some internal data
+ * if needed.
+ * </p>
+ * @param t0 start value of the independent <i>time</i> variable
+ * @param y0 array containing the start value of the state vector
+ * @param t target time for the integration
+ */
+ void init(double t0, double[] y0, double t);
+
+ /** Compute the value of the switching function.
+
+ * <p>The discrete events are generated when the sign of this
+ * switching function changes. The integrator will take care to change
+ * the stepsize in such a way these events occur exactly at step boundaries.
+ * The switching function must be continuous in its roots neighborhood
+ * (but not necessarily smooth), as the integrator will need to find its
+ * roots to locate precisely the events.</p>
+ * <p>Also note that the integrator expect that once an event has occurred,
+ * the sign of the switching function at the start of the next step (i.e.
+ * just after the event) is the opposite of the sign just before the event.
+ * This consistency between the steps <string>must</strong> be preserved,
+ * otherwise {@link org.apache.commons.math3.exception.NoBracketingException
+ * exceptions} related to root not being bracketed will occur.</p>
+ * <p>This need for consistency is sometimes tricky to achieve. A typical
+ * example is using an event to model a ball bouncing on the floor. The first
+ * idea to represent this would be to have {@code g(t) = h(t)} where h is the
+ * height above the floor at time {@code t}. When {@code g(t)} reaches 0, the
+ * ball is on the floor, so it should bounce and the typical way to do this is
+ * to reverse its vertical velocity. However, this would mean that before the
+ * event {@code g(t)} was decreasing from positive values to 0, and after the
+ * event {@code g(t)} would be increasing from 0 to positive values again.
+ * Consistency is broken here! The solution here is to have {@code g(t) = sign
+ * * h(t)}, where sign is a variable with initial value set to {@code +1}. Each
+ * time {@link #eventOccurred(double, double[], boolean) eventOccurred} is called,
+ * {@code sign} is reset to {@code -sign}. This allows the {@code g(t)}
+ * function to remain continuous (and even smooth) even across events, despite
+ * {@code h(t)} is not. Basically, the event is used to <em>fold</em> {@code h(t)}
+ * at bounce points, and {@code sign} is used to <em>unfold</em> it back, so the
+ * solvers sees a {@code g(t)} function which behaves smoothly even across events.</p>
+
+ * @param t current value of the independent <i>time</i> variable
+ * @param y array containing the current value of the state vector
+ * @return value of the g switching function
+ */
+ double g(double t, double[] y);
+
+ /** Handle an event and choose what to do next.
+
+ * <p>This method is called when the integrator has accepted a step
+ * ending exactly on a sign change of the function, just <em>before</em>
+ * the step handler itself is called (see below for scheduling). It
+ * allows the user to update his internal data to acknowledge the fact
+ * the event has been handled (for example setting a flag in the {@link
+ * org.apache.commons.math3.ode.FirstOrderDifferentialEquations
+ * differential equations} to switch the derivatives computation in
+ * case of discontinuity), or to direct the integrator to either stop
+ * or continue integration, possibly with a reset state or derivatives.</p>
+
+ * <ul>
+ * <li>if {@link Action#STOP} is returned, the step handler will be called
+ * with the <code>isLast</code> flag of the {@link
+ * org.apache.commons.math3.ode.sampling.StepHandler#handleStep handleStep}
+ * method set to true and the integration will be stopped,</li>
+ * <li>if {@link Action#RESET_STATE} is returned, the {@link #resetState
+ * resetState} method will be called once the step handler has
+ * finished its task, and the integrator will also recompute the
+ * derivatives,</li>
+ * <li>if {@link Action#RESET_DERIVATIVES} is returned, the integrator
+ * will recompute the derivatives,
+ * <li>if {@link Action#CONTINUE} is returned, no specific action will
+ * be taken (apart from having called this method) and integration
+ * will continue.</li>
+ * </ul>
+
+ * <p>The scheduling between this method and the {@link
+ * org.apache.commons.math3.ode.sampling.StepHandler StepHandler} method {@link
+ * org.apache.commons.math3.ode.sampling.StepHandler#handleStep(
+ * org.apache.commons.math3.ode.sampling.StepInterpolator, boolean)
+ * handleStep(interpolator, isLast)} is to call this method first and
+ * <code>handleStep</code> afterwards. This scheduling allows the integrator to
+ * pass <code>true</code> as the <code>isLast</code> parameter to the step
+ * handler to make it aware the step will be the last one if this method
+ * returns {@link Action#STOP}. As the interpolator may be used to navigate back
+ * throughout the last step (as {@link
+ * org.apache.commons.math3.ode.sampling.StepNormalizer StepNormalizer}
+ * does for example), user code called by this method and user
+ * code called by step handlers may experience apparently out of order values
+ * of the independent time variable. As an example, if the same user object
+ * implements both this {@link EventHandler EventHandler} interface and the
+ * {@link org.apache.commons.math3.ode.sampling.FixedStepHandler FixedStepHandler}
+ * interface, a <em>forward</em> integration may call its
+ * <code>eventOccurred</code> method with t = 10 first and call its
+ * <code>handleStep</code> method with t = 9 afterwards. Such out of order
+ * calls are limited to the size of the integration step for {@link
+ * org.apache.commons.math3.ode.sampling.StepHandler variable step handlers} and
+ * to the size of the fixed step for {@link
+ * org.apache.commons.math3.ode.sampling.FixedStepHandler fixed step handlers}.</p>
+
+ * @param t current value of the independent <i>time</i> variable
+ * @param y array containing the current value of the state vector
+ * @param increasing if true, the value of the switching function increases
+ * when times increases around event (note that increase is measured with respect
+ * to physical time, not with respect to integration which may go backward in time)
+ * @return indication of what the integrator should do next, this
+ * value must be one of {@link Action#STOP}, {@link Action#RESET_STATE},
+ * {@link Action#RESET_DERIVATIVES} or {@link Action#CONTINUE}
+ */
+ Action eventOccurred(double t, double[] y, boolean increasing);
+
+ /** Reset the state prior to continue the integration.
+
+ * <p>This method is called after the step handler has returned and
+ * before the next step is started, but only when {@link
+ * #eventOccurred} has itself returned the {@link Action#RESET_STATE}
+ * indicator. It allows the user to reset the state vector for the
+ * next step, without perturbing the step handler of the finishing
+ * step. If the {@link #eventOccurred} never returns the {@link
+ * Action#RESET_STATE} indicator, this function will never be called, and it is
+ * safe to leave its body empty.</p>
+
+ * @param t current value of the independent <i>time</i> variable
+ * @param y array containing the current value of the state vector
+ * the new state should be put in the same array
+ */
+ void resetState(double t, double[] y);
+
+}
diff --git a/src/main/java/org/apache/commons/math3/ode/events/EventState.java b/src/main/java/org/apache/commons/math3/ode/events/EventState.java
new file mode 100644
index 0000000..9e9da8d
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/ode/events/EventState.java
@@ -0,0 +1,431 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math3.ode.events;
+
+import org.apache.commons.math3.analysis.UnivariateFunction;
+import org.apache.commons.math3.analysis.solvers.AllowedSolution;
+import org.apache.commons.math3.analysis.solvers.BracketedUnivariateSolver;
+import org.apache.commons.math3.analysis.solvers.PegasusSolver;
+import org.apache.commons.math3.analysis.solvers.UnivariateSolver;
+import org.apache.commons.math3.analysis.solvers.UnivariateSolverUtils;
+import org.apache.commons.math3.exception.MaxCountExceededException;
+import org.apache.commons.math3.exception.NoBracketingException;
+import org.apache.commons.math3.ode.EquationsMapper;
+import org.apache.commons.math3.ode.ExpandableStatefulODE;
+import org.apache.commons.math3.ode.sampling.StepInterpolator;
+import org.apache.commons.math3.util.FastMath;
+
+/** This class handles the state for one {@link EventHandler
+ * event handler} during integration steps.
+ *
+ * <p>Each time the integrator proposes a step, the event handler
+ * switching function should be checked. This class handles the state
+ * of one handler during one integration step, with references to the
+ * state at the end of the preceding step. This information is used to
+ * decide if the handler should trigger an event or not during the
+ * proposed step.</p>
+ *
+ * @since 1.2
+ */
+public class EventState {
+
+ /** Event handler. */
+ private final EventHandler handler;
+
+ /** Maximal time interval between events handler checks. */
+ private final double maxCheckInterval;
+
+ /** Convergence threshold for event localization. */
+ private final double convergence;
+
+ /** Upper limit in the iteration count for event localization. */
+ private final int maxIterationCount;
+
+ /** Equation being integrated. */
+ private ExpandableStatefulODE expandable;
+
+ /** Time at the beginning of the step. */
+ private double t0;
+
+ /** Value of the events handler at the beginning of the step. */
+ private double g0;
+
+ /** Simulated sign of g0 (we cheat when crossing events). */
+ private boolean g0Positive;
+
+ /** Indicator of event expected during the step. */
+ private boolean pendingEvent;
+
+ /** Occurrence time of the pending event. */
+ private double pendingEventTime;
+
+ /** Occurrence time of the previous event. */
+ private double previousEventTime;
+
+ /** Integration direction. */
+ private boolean forward;
+
+ /** Variation direction around pending event.
+ * (this is considered with respect to the integration direction)
+ */
+ private boolean increasing;
+
+ /** Next action indicator. */
+ private EventHandler.Action nextAction;
+
+ /** Root-finding algorithm to use to detect state events. */
+ private final UnivariateSolver solver;
+
+ /** Simple constructor.
+ * @param handler event handler
+ * @param maxCheckInterval maximal time interval between switching
+ * function checks (this interval prevents missing sign changes in
+ * case the integration steps becomes very large)
+ * @param convergence convergence threshold in the event time search
+ * @param maxIterationCount upper limit of the iteration count in
+ * the event time search
+ * @param solver Root-finding algorithm to use to detect state events
+ */
+ public EventState(final EventHandler handler, final double maxCheckInterval,
+ final double convergence, final int maxIterationCount,
+ final UnivariateSolver solver) {
+ this.handler = handler;
+ this.maxCheckInterval = maxCheckInterval;
+ this.convergence = FastMath.abs(convergence);
+ this.maxIterationCount = maxIterationCount;
+ this.solver = solver;
+
+ // some dummy values ...
+ expandable = null;
+ t0 = Double.NaN;
+ g0 = Double.NaN;
+ g0Positive = true;
+ pendingEvent = false;
+ pendingEventTime = Double.NaN;
+ previousEventTime = Double.NaN;
+ increasing = true;
+ nextAction = EventHandler.Action.CONTINUE;
+
+ }
+
+ /** Get the underlying event handler.
+ * @return underlying event handler
+ */
+ public EventHandler getEventHandler() {
+ return handler;
+ }
+
+ /** Set the equation.
+ * @param expandable equation being integrated
+ */
+ public void setExpandable(final ExpandableStatefulODE expandable) {
+ this.expandable = expandable;
+ }
+
+ /** Get the maximal time interval between events handler checks.
+ * @return maximal time interval between events handler checks
+ */
+ public double getMaxCheckInterval() {
+ return maxCheckInterval;
+ }
+
+ /** Get the convergence threshold for event localization.
+ * @return convergence threshold for event localization
+ */
+ public double getConvergence() {
+ return convergence;
+ }
+
+ /** Get the upper limit in the iteration count for event localization.
+ * @return upper limit in the iteration count for event localization
+ */
+ public int getMaxIterationCount() {
+ return maxIterationCount;
+ }
+
+ /** Reinitialize the beginning of the step.
+ * @param interpolator valid for the current step
+ * @exception MaxCountExceededException if the interpolator throws one because
+ * the number of functions evaluations is exceeded
+ */
+ public void reinitializeBegin(final StepInterpolator interpolator)
+ throws MaxCountExceededException {
+
+ t0 = interpolator.getPreviousTime();
+ interpolator.setInterpolatedTime(t0);
+ g0 = handler.g(t0, getCompleteState(interpolator));
+ if (g0 == 0) {
+ // excerpt from MATH-421 issue:
+ // If an ODE solver is setup with an EventHandler that return STOP
+ // when the even is triggered, the integrator stops (which is exactly
+ // the expected behavior). If however the user wants to restart the
+ // solver from the final state reached at the event with the same
+ // configuration (expecting the event to be triggered again at a
+ // later time), then the integrator may fail to start. It can get stuck
+ // at the previous event. The use case for the bug MATH-421 is fairly
+ // general, so events occurring exactly at start in the first step should
+ // be ignored.
+
+ // extremely rare case: there is a zero EXACTLY at interval start
+ // we will use the sign slightly after step beginning to force ignoring this zero
+ final double epsilon = FastMath.max(solver.getAbsoluteAccuracy(),
+ FastMath.abs(solver.getRelativeAccuracy() * t0));
+ final double tStart = t0 + 0.5 * epsilon;
+ interpolator.setInterpolatedTime(tStart);
+ g0 = handler.g(tStart, getCompleteState(interpolator));
+ }
+ g0Positive = g0 >= 0;
+
+ }
+
+ /** Get the complete state (primary and secondary).
+ * @param interpolator interpolator to use
+ * @return complete state
+ */
+ private double[] getCompleteState(final StepInterpolator interpolator) {
+
+ final double[] complete = new double[expandable.getTotalDimension()];
+
+ expandable.getPrimaryMapper().insertEquationData(interpolator.getInterpolatedState(),
+ complete);
+ int index = 0;
+ for (EquationsMapper secondary : expandable.getSecondaryMappers()) {
+ secondary.insertEquationData(interpolator.getInterpolatedSecondaryState(index++),
+ complete);
+ }
+
+ return complete;
+
+ }
+
+ /** Evaluate the impact of the proposed step on the event handler.
+ * @param interpolator step interpolator for the proposed step
+ * @return true if the event handler triggers an event before
+ * the end of the proposed step
+ * @exception MaxCountExceededException if the interpolator throws one because
+ * the number of functions evaluations is exceeded
+ * @exception NoBracketingException if the event cannot be bracketed
+ */
+ public boolean evaluateStep(final StepInterpolator interpolator)
+ throws MaxCountExceededException, NoBracketingException {
+
+ try {
+ forward = interpolator.isForward();
+ final double t1 = interpolator.getCurrentTime();
+ final double dt = t1 - t0;
+ if (FastMath.abs(dt) < convergence) {
+ // we cannot do anything on such a small step, don't trigger any events
+ return false;
+ }
+ final int n = FastMath.max(1, (int) FastMath.ceil(FastMath.abs(dt) / maxCheckInterval));
+ final double h = dt / n;
+
+ final UnivariateFunction f = new UnivariateFunction() {
+ /** {@inheritDoc} */
+ public double value(final double t) throws LocalMaxCountExceededException {
+ try {
+ interpolator.setInterpolatedTime(t);
+ return handler.g(t, getCompleteState(interpolator));
+ } catch (MaxCountExceededException mcee) {
+ throw new LocalMaxCountExceededException(mcee);
+ }
+ }
+ };
+
+ double ta = t0;
+ double ga = g0;
+ for (int i = 0; i < n; ++i) {
+
+ // evaluate handler value at the end of the substep
+ final double tb = (i == n - 1) ? t1 : t0 + (i + 1) * h;
+ interpolator.setInterpolatedTime(tb);
+ final double gb = handler.g(tb, getCompleteState(interpolator));
+
+ // check events occurrence
+ if (g0Positive ^ (gb >= 0)) {
+ // there is a sign change: an event is expected during this step
+
+ // variation direction, with respect to the integration direction
+ increasing = gb >= ga;
+
+ // find the event time making sure we select a solution just at or past the exact root
+ final double root;
+ if (solver instanceof BracketedUnivariateSolver<?>) {
+ @SuppressWarnings("unchecked")
+ BracketedUnivariateSolver<UnivariateFunction> bracketing =
+ (BracketedUnivariateSolver<UnivariateFunction>) solver;
+ root = forward ?
+ bracketing.solve(maxIterationCount, f, ta, tb, AllowedSolution.RIGHT_SIDE) :
+ bracketing.solve(maxIterationCount, f, tb, ta, AllowedSolution.LEFT_SIDE);
+ } else {
+ final double baseRoot = forward ?
+ solver.solve(maxIterationCount, f, ta, tb) :
+ solver.solve(maxIterationCount, f, tb, ta);
+ final int remainingEval = maxIterationCount - solver.getEvaluations();
+ BracketedUnivariateSolver<UnivariateFunction> bracketing =
+ new PegasusSolver(solver.getRelativeAccuracy(), solver.getAbsoluteAccuracy());
+ root = forward ?
+ UnivariateSolverUtils.forceSide(remainingEval, f, bracketing,
+ baseRoot, ta, tb, AllowedSolution.RIGHT_SIDE) :
+ UnivariateSolverUtils.forceSide(remainingEval, f, bracketing,
+ baseRoot, tb, ta, AllowedSolution.LEFT_SIDE);
+ }
+
+ if ((!Double.isNaN(previousEventTime)) &&
+ (FastMath.abs(root - ta) <= convergence) &&
+ (FastMath.abs(root - previousEventTime) <= convergence)) {
+ // we have either found nothing or found (again ?) a past event,
+ // retry the substep excluding this value, and taking care to have the
+ // required sign in case the g function is noisy around its zero and
+ // crosses the axis several times
+ do {
+ ta = forward ? ta + convergence : ta - convergence;
+ ga = f.value(ta);
+ } while ((g0Positive ^ (ga >= 0)) && (forward ^ (ta >= tb)));
+
+ if (forward ^ (ta >= tb)) {
+ // we were able to skip this spurious root
+ --i;
+ } else {
+ // we can't avoid this root before the end of the step,
+ // we have to handle it despite it is close to the former one
+ // maybe we have two very close roots
+ pendingEventTime = root;
+ pendingEvent = true;
+ return true;
+ }
+ } else if (Double.isNaN(previousEventTime) ||
+ (FastMath.abs(previousEventTime - root) > convergence)) {
+ pendingEventTime = root;
+ pendingEvent = true;
+ return true;
+ } else {
+ // no sign change: there is no event for now
+ ta = tb;
+ ga = gb;
+ }
+
+ } else {
+ // no sign change: there is no event for now
+ ta = tb;
+ ga = gb;
+ }
+
+ }
+
+ // no event during the whole step
+ pendingEvent = false;
+ pendingEventTime = Double.NaN;
+ return false;
+
+ } catch (LocalMaxCountExceededException lmcee) {
+ throw lmcee.getException();
+ }
+
+ }
+
+ /** Get the occurrence time of the event triggered in the current step.
+ * @return occurrence time of the event triggered in the current
+ * step or infinity if no events are triggered
+ */
+ public double getEventTime() {
+ return pendingEvent ?
+ pendingEventTime :
+ (forward ? Double.POSITIVE_INFINITY : Double.NEGATIVE_INFINITY);
+ }
+
+ /** Acknowledge the fact the step has been accepted by the integrator.
+ * @param t value of the independent <i>time</i> variable at the
+ * end of the step
+ * @param y array containing the current value of the state vector
+ * at the end of the step
+ */
+ public void stepAccepted(final double t, final double[] y) {
+
+ t0 = t;
+ g0 = handler.g(t, y);
+
+ if (pendingEvent && (FastMath.abs(pendingEventTime - t) <= convergence)) {
+ // force the sign to its value "just after the event"
+ previousEventTime = t;
+ g0Positive = increasing;
+ nextAction = handler.eventOccurred(t, y, !(increasing ^ forward));
+ } else {
+ g0Positive = g0 >= 0;
+ nextAction = EventHandler.Action.CONTINUE;
+ }
+ }
+
+ /** Check if the integration should be stopped at the end of the
+ * current step.
+ * @return true if the integration should be stopped
+ */
+ public boolean stop() {
+ return nextAction == EventHandler.Action.STOP;
+ }
+
+ /** Let the event handler reset the state if it wants.
+ * @param t value of the independent <i>time</i> variable at the
+ * beginning of the next step
+ * @param y array were to put the desired state vector at the beginning
+ * of the next step
+ * @return true if the integrator should reset the derivatives too
+ */
+ public boolean reset(final double t, final double[] y) {
+
+ if (!(pendingEvent && (FastMath.abs(pendingEventTime - t) <= convergence))) {
+ return false;
+ }
+
+ if (nextAction == EventHandler.Action.RESET_STATE) {
+ handler.resetState(t, y);
+ }
+ pendingEvent = false;
+ pendingEventTime = Double.NaN;
+
+ return (nextAction == EventHandler.Action.RESET_STATE) ||
+ (nextAction == EventHandler.Action.RESET_DERIVATIVES);
+
+ }
+
+ /** Local wrapper to propagate exceptions. */
+ private static class LocalMaxCountExceededException extends RuntimeException {
+
+ /** Serializable UID. */
+ private static final long serialVersionUID = 20120901L;
+
+ /** Wrapped exception. */
+ private final MaxCountExceededException wrapped;
+
+ /** Simple constructor.
+ * @param exception exception to wrap
+ */
+ LocalMaxCountExceededException(final MaxCountExceededException exception) {
+ wrapped = exception;
+ }
+
+ /** Get the wrapped exception.
+ * @return wrapped exception
+ */
+ public MaxCountExceededException getException() {
+ return wrapped;
+ }
+
+ }
+
+}
diff --git a/src/main/java/org/apache/commons/math3/ode/events/FieldEventHandler.java b/src/main/java/org/apache/commons/math3/ode/events/FieldEventHandler.java
new file mode 100644
index 0000000..058f113
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/ode/events/FieldEventHandler.java
@@ -0,0 +1,180 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math3.ode.events;
+
+import org.apache.commons.math3.RealFieldElement;
+import org.apache.commons.math3.ode.FieldODEState;
+import org.apache.commons.math3.ode.FieldODEStateAndDerivative;
+
+/** This interface represents a handler for discrete events triggered
+ * during ODE integration.
+ *
+ * <p>Some events can be triggered at discrete times as an ODE problem
+ * is solved. This occurs for example when the integration process
+ * should be stopped as some state is reached (G-stop facility) when the
+ * precise date is unknown a priori, or when the derivatives have
+ * discontinuities, or simply when the user wants to monitor some
+ * states boundaries crossings.
+ * </p>
+ *
+ * <p>These events are defined as occurring when a <code>g</code>
+ * switching function sign changes.</p>
+ *
+ * <p>Since events are only problem-dependent and are triggered by the
+ * independent <i>time</i> variable and the state vector, they can
+ * occur at virtually any time, unknown in advance. The integrators will
+ * take care to avoid sign changes inside the steps, they will reduce
+ * the step size when such an event is detected in order to put this
+ * event exactly at the end of the current step. This guarantees that
+ * step interpolation (which always has a one step scope) is relevant
+ * even in presence of discontinuities. This is independent from the
+ * stepsize control provided by integrators that monitor the local
+ * error (this event handling feature is available for all integrators,
+ * including fixed step ones).</p>
+ *
+ * @param <T> the type of the field elements
+ * @since 3.6
+ */
+public interface FieldEventHandler<T extends RealFieldElement<T>> {
+
+ /** Initialize event handler at the start of an ODE integration.
+ * <p>
+ * This method is called once at the start of the integration. It
+ * may be used by the event handler to initialize some internal data
+ * if needed.
+ * </p>
+ * @param initialState initial time, state vector and derivative
+ * @param finalTime target time for the integration
+ */
+ void init(FieldODEStateAndDerivative<T> initialState, T finalTime);
+
+ /** Compute the value of the switching function.
+
+ * <p>The discrete events are generated when the sign of this
+ * switching function changes. The integrator will take care to change
+ * the stepsize in such a way these events occur exactly at step boundaries.
+ * The switching function must be continuous in its roots neighborhood
+ * (but not necessarily smooth), as the integrator will need to find its
+ * roots to locate precisely the events.</p>
+ * <p>Also note that the integrator expect that once an event has occurred,
+ * the sign of the switching function at the start of the next step (i.e.
+ * just after the event) is the opposite of the sign just before the event.
+ * This consistency between the steps <string>must</strong> be preserved,
+ * otherwise {@link org.apache.commons.math3.exception.NoBracketingException
+ * exceptions} related to root not being bracketed will occur.</p>
+ * <p>This need for consistency is sometimes tricky to achieve. A typical
+ * example is using an event to model a ball bouncing on the floor. The first
+ * idea to represent this would be to have {@code g(t) = h(t)} where h is the
+ * height above the floor at time {@code t}. When {@code g(t)} reaches 0, the
+ * ball is on the floor, so it should bounce and the typical way to do this is
+ * to reverse its vertical velocity. However, this would mean that before the
+ * event {@code g(t)} was decreasing from positive values to 0, and after the
+ * event {@code g(t)} would be increasing from 0 to positive values again.
+ * Consistency is broken here! The solution here is to have {@code g(t) = sign
+ * * h(t)}, where sign is a variable with initial value set to {@code +1}. Each
+ * time {@link #eventOccurred(FieldODEStateAndDerivative, boolean) eventOccurred}
+ * method is called, {@code sign} is reset to {@code -sign}. This allows the
+ * {@code g(t)} function to remain continuous (and even smooth) even across events,
+ * despite {@code h(t)} is not. Basically, the event is used to <em>fold</em>
+ * {@code h(t)} at bounce points, and {@code sign} is used to <em>unfold</em> it
+ * back, so the solvers sees a {@code g(t)} function which behaves smoothly even
+ * across events.</p>
+
+ * @param state current value of the independent <i>time</i> variable, state vector
+ * and derivative
+ * @return value of the g switching function
+ */
+ T g(FieldODEStateAndDerivative<T> state);
+
+ /** Handle an event and choose what to do next.
+
+ * <p>This method is called when the integrator has accepted a step
+ * ending exactly on a sign change of the function, just <em>before</em>
+ * the step handler itself is called (see below for scheduling). It
+ * allows the user to update his internal data to acknowledge the fact
+ * the event has been handled (for example setting a flag in the {@link
+ * org.apache.commons.math3.ode.FirstOrderDifferentialEquations
+ * differential equations} to switch the derivatives computation in
+ * case of discontinuity), or to direct the integrator to either stop
+ * or continue integration, possibly with a reset state or derivatives.</p>
+
+ * <ul>
+ * <li>if {@link Action#STOP} is returned, the step handler will be called
+ * with the <code>isLast</code> flag of the {@link
+ * org.apache.commons.math3.ode.sampling.StepHandler#handleStep handleStep}
+ * method set to true and the integration will be stopped,</li>
+ * <li>if {@link Action#RESET_STATE} is returned, the {@link #resetState
+ * resetState} method will be called once the step handler has
+ * finished its task, and the integrator will also recompute the
+ * derivatives,</li>
+ * <li>if {@link Action#RESET_DERIVATIVES} is returned, the integrator
+ * will recompute the derivatives,
+ * <li>if {@link Action#CONTINUE} is returned, no specific action will
+ * be taken (apart from having called this method) and integration
+ * will continue.</li>
+ * </ul>
+
+ * <p>The scheduling between this method and the {@link
+ * org.apache.commons.math3.ode.sampling.FieldStepHandler FieldStepHandler} method {@link
+ * org.apache.commons.math3.ode.sampling.FieldStepHandler#handleStep(
+ * org.apache.commons.math3.ode.sampling.FieldStepInterpolator, boolean)
+ * handleStep(interpolator, isLast)} is to call this method first and
+ * <code>handleStep</code> afterwards. This scheduling allows the integrator to
+ * pass <code>true</code> as the <code>isLast</code> parameter to the step
+ * handler to make it aware the step will be the last one if this method
+ * returns {@link Action#STOP}. As the interpolator may be used to navigate back
+ * throughout the last step, user code called by this method and user
+ * code called by step handlers may experience apparently out of order values
+ * of the independent time variable. As an example, if the same user object
+ * implements both this {@link FieldEventHandler FieldEventHandler} interface and the
+ * {@link org.apache.commons.math3.ode.sampling.FieldStepHandler FieldStepHandler}
+ * interface, a <em>forward</em> integration may call its
+ * {code eventOccurred} method with t = 10 first and call its
+ * {code handleStep} method with t = 9 afterwards. Such out of order
+ * calls are limited to the size of the integration step for {@link
+ * org.apache.commons.math3.ode.sampling.FieldStepHandler variable step handlers}.</p>
+
+ * @param state current value of the independent <i>time</i> variable, state vector
+ * and derivative
+ * @param increasing if true, the value of the switching function increases
+ * when times increases around event (note that increase is measured with respect
+ * to physical time, not with respect to integration which may go backward in time)
+ * @return indication of what the integrator should do next, this
+ * value must be one of {@link Action#STOP}, {@link Action#RESET_STATE},
+ * {@link Action#RESET_DERIVATIVES} or {@link Action#CONTINUE}
+ */
+ Action eventOccurred(FieldODEStateAndDerivative<T> state, boolean increasing);
+
+ /** Reset the state prior to continue the integration.
+
+ * <p>This method is called after the step handler has returned and
+ * before the next step is started, but only when {@link
+ * #eventOccurred(FieldODEStateAndDerivative, boolean) eventOccurred} has itself
+ * returned the {@link Action#RESET_STATE} indicator. It allows the user to reset
+ * the state vector for the next step, without perturbing the step handler of the
+ * finishing step. If the {@link #eventOccurred(FieldODEStateAndDerivative, boolean)
+ * eventOccurred} never returns the {@link Action#RESET_STATE} indicator, this
+ * function will never be called, and it is safe to leave its body empty.</p>
+ * @param state current value of the independent <i>time</i> variable, state vector
+ * and derivative
+ * @return reset state (note that it does not include the derivatives, they will
+ * be added automatically by the integrator afterwards)
+ */
+ FieldODEState<T> resetState(FieldODEStateAndDerivative<T> state);
+
+}
diff --git a/src/main/java/org/apache/commons/math3/ode/events/FieldEventState.java b/src/main/java/org/apache/commons/math3/ode/events/FieldEventState.java
new file mode 100644
index 0000000..5c22357
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/ode/events/FieldEventState.java
@@ -0,0 +1,344 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math3.ode.events;
+
+import org.apache.commons.math3.RealFieldElement;
+import org.apache.commons.math3.analysis.RealFieldUnivariateFunction;
+import org.apache.commons.math3.analysis.solvers.AllowedSolution;
+import org.apache.commons.math3.analysis.solvers.BracketedRealFieldUnivariateSolver;
+import org.apache.commons.math3.exception.MaxCountExceededException;
+import org.apache.commons.math3.exception.NoBracketingException;
+import org.apache.commons.math3.ode.FieldODEState;
+import org.apache.commons.math3.ode.FieldODEStateAndDerivative;
+import org.apache.commons.math3.ode.sampling.FieldStepInterpolator;
+import org.apache.commons.math3.util.FastMath;
+
+/** This class handles the state for one {@link EventHandler
+ * event handler} during integration steps.
+ *
+ * <p>Each time the integrator proposes a step, the event handler
+ * switching function should be checked. This class handles the state
+ * of one handler during one integration step, with references to the
+ * state at the end of the preceding step. This information is used to
+ * decide if the handler should trigger an event or not during the
+ * proposed step.</p>
+ *
+ * @param <T> the type of the field elements
+ * @since 3.6
+ */
+public class FieldEventState<T extends RealFieldElement<T>> {
+
+ /** Event handler. */
+ private final FieldEventHandler<T> handler;
+
+ /** Maximal time interval between events handler checks. */
+ private final double maxCheckInterval;
+
+ /** Convergence threshold for event localization. */
+ private final T convergence;
+
+ /** Upper limit in the iteration count for event localization. */
+ private final int maxIterationCount;
+
+ /** Time at the beginning of the step. */
+ private T t0;
+
+ /** Value of the events handler at the beginning of the step. */
+ private T g0;
+
+ /** Simulated sign of g0 (we cheat when crossing events). */
+ private boolean g0Positive;
+
+ /** Indicator of event expected during the step. */
+ private boolean pendingEvent;
+
+ /** Occurrence time of the pending event. */
+ private T pendingEventTime;
+
+ /** Occurrence time of the previous event. */
+ private T previousEventTime;
+
+ /** Integration direction. */
+ private boolean forward;
+
+ /** Variation direction around pending event.
+ * (this is considered with respect to the integration direction)
+ */
+ private boolean increasing;
+
+ /** Next action indicator. */
+ private Action nextAction;
+
+ /** Root-finding algorithm to use to detect state events. */
+ private final BracketedRealFieldUnivariateSolver<T> solver;
+
+ /** Simple constructor.
+ * @param handler event handler
+ * @param maxCheckInterval maximal time interval between switching
+ * function checks (this interval prevents missing sign changes in
+ * case the integration steps becomes very large)
+ * @param convergence convergence threshold in the event time search
+ * @param maxIterationCount upper limit of the iteration count in
+ * the event time search
+ * @param solver Root-finding algorithm to use to detect state events
+ */
+ public FieldEventState(final FieldEventHandler<T> handler, final double maxCheckInterval,
+ final T convergence, final int maxIterationCount,
+ final BracketedRealFieldUnivariateSolver<T> solver) {
+ this.handler = handler;
+ this.maxCheckInterval = maxCheckInterval;
+ this.convergence = convergence.abs();
+ this.maxIterationCount = maxIterationCount;
+ this.solver = solver;
+
+ // some dummy values ...
+ t0 = null;
+ g0 = null;
+ g0Positive = true;
+ pendingEvent = false;
+ pendingEventTime = null;
+ previousEventTime = null;
+ increasing = true;
+ nextAction = Action.CONTINUE;
+
+ }
+
+ /** Get the underlying event handler.
+ * @return underlying event handler
+ */
+ public FieldEventHandler<T> getEventHandler() {
+ return handler;
+ }
+
+ /** Get the maximal time interval between events handler checks.
+ * @return maximal time interval between events handler checks
+ */
+ public double getMaxCheckInterval() {
+ return maxCheckInterval;
+ }
+
+ /** Get the convergence threshold for event localization.
+ * @return convergence threshold for event localization
+ */
+ public T getConvergence() {
+ return convergence;
+ }
+
+ /** Get the upper limit in the iteration count for event localization.
+ * @return upper limit in the iteration count for event localization
+ */
+ public int getMaxIterationCount() {
+ return maxIterationCount;
+ }
+
+ /** Reinitialize the beginning of the step.
+ * @param interpolator valid for the current step
+ * @exception MaxCountExceededException if the interpolator throws one because
+ * the number of functions evaluations is exceeded
+ */
+ public void reinitializeBegin(final FieldStepInterpolator<T> interpolator)
+ throws MaxCountExceededException {
+
+ final FieldODEStateAndDerivative<T> s0 = interpolator.getPreviousState();
+ t0 = s0.getTime();
+ g0 = handler.g(s0);
+ if (g0.getReal() == 0) {
+ // excerpt from MATH-421 issue:
+ // If an ODE solver is setup with an EventHandler that return STOP
+ // when the even is triggered, the integrator stops (which is exactly
+ // the expected behavior). If however the user wants to restart the
+ // solver from the final state reached at the event with the same
+ // configuration (expecting the event to be triggered again at a
+ // later time), then the integrator may fail to start. It can get stuck
+ // at the previous event. The use case for the bug MATH-421 is fairly
+ // general, so events occurring exactly at start in the first step should
+ // be ignored.
+
+ // extremely rare case: there is a zero EXACTLY at interval start
+ // we will use the sign slightly after step beginning to force ignoring this zero
+ final double epsilon = FastMath.max(solver.getAbsoluteAccuracy().getReal(),
+ FastMath.abs(solver.getRelativeAccuracy().multiply(t0).getReal()));
+ final T tStart = t0.add(0.5 * epsilon);
+ g0 = handler.g(interpolator.getInterpolatedState(tStart));
+ }
+ g0Positive = g0.getReal() >= 0;
+
+ }
+
+ /** Evaluate the impact of the proposed step on the event handler.
+ * @param interpolator step interpolator for the proposed step
+ * @return true if the event handler triggers an event before
+ * the end of the proposed step
+ * @exception MaxCountExceededException if the interpolator throws one because
+ * the number of functions evaluations is exceeded
+ * @exception NoBracketingException if the event cannot be bracketed
+ */
+ public boolean evaluateStep(final FieldStepInterpolator<T> interpolator)
+ throws MaxCountExceededException, NoBracketingException {
+
+ forward = interpolator.isForward();
+ final FieldODEStateAndDerivative<T> s1 = interpolator.getCurrentState();
+ final T t1 = s1.getTime();
+ final T dt = t1.subtract(t0);
+ if (dt.abs().subtract(convergence).getReal() < 0) {
+ // we cannot do anything on such a small step, don't trigger any events
+ return false;
+ }
+ final int n = FastMath.max(1, (int) FastMath.ceil(FastMath.abs(dt.getReal()) / maxCheckInterval));
+ final T h = dt.divide(n);
+
+ final RealFieldUnivariateFunction<T> f = new RealFieldUnivariateFunction<T>() {
+ /** {@inheritDoc} */
+ public T value(final T t) {
+ return handler.g(interpolator.getInterpolatedState(t));
+ }
+ };
+
+ T ta = t0;
+ T ga = g0;
+ for (int i = 0; i < n; ++i) {
+
+ // evaluate handler value at the end of the substep
+ final T tb = (i == n - 1) ? t1 : t0.add(h.multiply(i + 1));
+ final T gb = handler.g(interpolator.getInterpolatedState(tb));
+
+ // check events occurrence
+ if (g0Positive ^ (gb.getReal() >= 0)) {
+ // there is a sign change: an event is expected during this step
+
+ // variation direction, with respect to the integration direction
+ increasing = gb.subtract(ga).getReal() >= 0;
+
+ // find the event time making sure we select a solution just at or past the exact root
+ final T root = forward ?
+ solver.solve(maxIterationCount, f, ta, tb, AllowedSolution.RIGHT_SIDE) :
+ solver.solve(maxIterationCount, f, tb, ta, AllowedSolution.LEFT_SIDE);
+
+ if (previousEventTime != null &&
+ root.subtract(ta).abs().subtract(convergence).getReal() <= 0 &&
+ root.subtract(previousEventTime).abs().subtract(convergence).getReal() <= 0) {
+ // we have either found nothing or found (again ?) a past event,
+ // retry the substep excluding this value, and taking care to have the
+ // required sign in case the g function is noisy around its zero and
+ // crosses the axis several times
+ do {
+ ta = forward ? ta.add(convergence) : ta.subtract(convergence);
+ ga = f.value(ta);
+ } while ((g0Positive ^ (ga.getReal() >= 0)) && (forward ^ (ta.subtract(tb).getReal() >= 0)));
+
+ if (forward ^ (ta.subtract(tb).getReal() >= 0)) {
+ // we were able to skip this spurious root
+ --i;
+ } else {
+ // we can't avoid this root before the end of the step,
+ // we have to handle it despite it is close to the former one
+ // maybe we have two very close roots
+ pendingEventTime = root;
+ pendingEvent = true;
+ return true;
+ }
+ } else if (previousEventTime == null ||
+ previousEventTime.subtract(root).abs().subtract(convergence).getReal() > 0) {
+ pendingEventTime = root;
+ pendingEvent = true;
+ return true;
+ } else {
+ // no sign change: there is no event for now
+ ta = tb;
+ ga = gb;
+ }
+
+ } else {
+ // no sign change: there is no event for now
+ ta = tb;
+ ga = gb;
+ }
+
+ }
+
+ // no event during the whole step
+ pendingEvent = false;
+ pendingEventTime = null;
+ return false;
+
+ }
+
+ /** Get the occurrence time of the event triggered in the current step.
+ * @return occurrence time of the event triggered in the current
+ * step or infinity if no events are triggered
+ */
+ public T getEventTime() {
+ return pendingEvent ?
+ pendingEventTime :
+ t0.getField().getZero().add(forward ? Double.POSITIVE_INFINITY : Double.NEGATIVE_INFINITY);
+ }
+
+ /** Acknowledge the fact the step has been accepted by the integrator.
+ * @param state state at the end of the step
+ */
+ public void stepAccepted(final FieldODEStateAndDerivative<T> state) {
+
+ t0 = state.getTime();
+ g0 = handler.g(state);
+
+ if (pendingEvent && pendingEventTime.subtract(state.getTime()).abs().subtract(convergence).getReal() <= 0) {
+ // force the sign to its value "just after the event"
+ previousEventTime = state.getTime();
+ g0Positive = increasing;
+ nextAction = handler.eventOccurred(state, !(increasing ^ forward));
+ } else {
+ g0Positive = g0.getReal() >= 0;
+ nextAction = Action.CONTINUE;
+ }
+ }
+
+ /** Check if the integration should be stopped at the end of the
+ * current step.
+ * @return true if the integration should be stopped
+ */
+ public boolean stop() {
+ return nextAction == Action.STOP;
+ }
+
+ /** Let the event handler reset the state if it wants.
+ * @param state state at the beginning of the next step
+ * @return reset state (may by the same as initial state if only
+ * derivatives should be reset), or null if nothing is reset
+ */
+ public FieldODEState<T> reset(final FieldODEStateAndDerivative<T> state) {
+
+ if (!(pendingEvent && pendingEventTime.subtract(state.getTime()).abs().subtract(convergence).getReal() <= 0)) {
+ return null;
+ }
+
+ final FieldODEState<T> newState;
+ if (nextAction == Action.RESET_STATE) {
+ newState = handler.resetState(state);
+ } else if (nextAction == Action.RESET_DERIVATIVES) {
+ newState = state;
+ } else {
+ newState = null;
+ }
+ pendingEvent = false;
+ pendingEventTime = null;
+
+ return newState;
+
+ }
+
+}
diff --git a/src/main/java/org/apache/commons/math3/ode/events/FilterType.java b/src/main/java/org/apache/commons/math3/ode/events/FilterType.java
new file mode 100644
index 0000000..2ac0df8
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/ode/events/FilterType.java
@@ -0,0 +1,400 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math3.ode.events;
+
+import org.apache.commons.math3.exception.MathInternalError;
+
+/** Enumerate for {@link EventFilter filtering events}.
+ *
+ * @since 3.2
+ */
+
+public enum FilterType {
+
+ /** Constant for triggering only decreasing events.
+ * <p>When this filter is used, the wrapped {@link EventHandler
+ * event handler} {@link EventHandler#eventOccurred(double, double[],
+ * boolean) eventOccurred} method will be called <em>only</em> with
+ * its {@code increasing} argument set to false.</p>
+ */
+ TRIGGER_ONLY_DECREASING_EVENTS {
+
+ /** {@inheritDoc} */
+ @Override
+ protected boolean getTriggeredIncreasing() {
+ return false;
+ }
+
+ /** {@inheritDoc}
+ * <p>
+ * states scheduling for computing h(t,y) as an altered version of g(t, y)
+ * <ul>
+ * <li>0 are triggered events for which a zero is produced (here decreasing events)</li>
+ * <li>X are ignored events for which zero is masked (here increasing events)</li>
+ * </ul>
+ * </p>
+ * <pre>
+ * g(t)
+ * ___ ___ ___
+ * / \ / \ / \
+ * / \ / \ / \
+ * / g>0 \ / g>0 \ / g>0 \
+ * / \ / \ / \
+ * ----- X --------- 0 --------- X --------- 0 --------- X --------- 0 ---
+ * / \ / \ / \
+ * / \ g<0 / \ g<0 / \ g<0
+ * / \ / \ / \ /
+ * ___/ \___/ \___/ \___/
+ * </pre>
+ * <pre>
+ * h(t,y)) as an alteration of g(t,y)
+ * ___ ___ ___
+ * \ / \ / \ / \
+ * \ / \ h=+g / \ / \
+ * \ / \ h=min(-s,-g,+g) / \ / \
+ * \_/ \ / \_/ \
+ * ------ ---------- 0 ----------_---------- 0 --------------------- 0 ---
+ * \ / \ / \
+ * h=max(+s,-g,+g) \ / \ / h=max(+s,-g,+g) \
+ * \ / \ / h=-g \ /
+ * \___/ \___/ \___/
+ * </pre>
+ * <p>
+ * As shown by the figure above, several expressions are used to compute h,
+ * depending on the current state:
+ * <ul>
+ * <li>h = max(+s,-g,+g)</li>
+ * <li>h = +g</li>
+ * <li>h = min(-s,-g,+g)</li>
+ * <li>h = -g</li>
+ * </ul>
+ * where s is a tiny positive value: {@link org.apache.commons.math3.util.Precision#SAFE_MIN}.
+ * </p>
+ */
+ @Override
+ protected Transformer selectTransformer(final Transformer previous,
+ final double g, final boolean forward) {
+ if (forward) {
+ switch (previous) {
+ case UNINITIALIZED :
+ // we are initializing the first point
+ if (g > 0) {
+ // initialize as if previous root (i.e. backward one) was an ignored increasing event
+ return Transformer.MAX;
+ } else if (g < 0) {
+ // initialize as if previous root (i.e. backward one) was a triggered decreasing event
+ return Transformer.PLUS;
+ } else {
+ // we are exactly at a root, we don't know if it is an increasing
+ // or a decreasing event, we remain in uninitialized state
+ return Transformer.UNINITIALIZED;
+ }
+ case PLUS :
+ if (g >= 0) {
+ // we have crossed the zero line on an ignored increasing event,
+ // we must change the transformer
+ return Transformer.MIN;
+ } else {
+ // we are still in the same status
+ return previous;
+ }
+ case MINUS :
+ if (g >= 0) {
+ // we have crossed the zero line on an ignored increasing event,
+ // we must change the transformer
+ return Transformer.MAX;
+ } else {
+ // we are still in the same status
+ return previous;
+ }
+ case MIN :
+ if (g <= 0) {
+ // we have crossed the zero line on a triggered decreasing event,
+ // we must change the transformer
+ return Transformer.MINUS;
+ } else {
+ // we are still in the same status
+ return previous;
+ }
+ case MAX :
+ if (g <= 0) {
+ // we have crossed the zero line on a triggered decreasing event,
+ // we must change the transformer
+ return Transformer.PLUS;
+ } else {
+ // we are still in the same status
+ return previous;
+ }
+ default :
+ // this should never happen
+ throw new MathInternalError();
+ }
+ } else {
+ switch (previous) {
+ case UNINITIALIZED :
+ // we are initializing the first point
+ if (g > 0) {
+ // initialize as if previous root (i.e. forward one) was a triggered decreasing event
+ return Transformer.MINUS;
+ } else if (g < 0) {
+ // initialize as if previous root (i.e. forward one) was an ignored increasing event
+ return Transformer.MIN;
+ } else {
+ // we are exactly at a root, we don't know if it is an increasing
+ // or a decreasing event, we remain in uninitialized state
+ return Transformer.UNINITIALIZED;
+ }
+ case PLUS :
+ if (g <= 0) {
+ // we have crossed the zero line on an ignored increasing event,
+ // we must change the transformer
+ return Transformer.MAX;
+ } else {
+ // we are still in the same status
+ return previous;
+ }
+ case MINUS :
+ if (g <= 0) {
+ // we have crossed the zero line on an ignored increasing event,
+ // we must change the transformer
+ return Transformer.MIN;
+ } else {
+ // we are still in the same status
+ return previous;
+ }
+ case MIN :
+ if (g >= 0) {
+ // we have crossed the zero line on a triggered decreasing event,
+ // we must change the transformer
+ return Transformer.PLUS;
+ } else {
+ // we are still in the same status
+ return previous;
+ }
+ case MAX :
+ if (g >= 0) {
+ // we have crossed the zero line on a triggered decreasing event,
+ // we must change the transformer
+ return Transformer.MINUS;
+ } else {
+ // we are still in the same status
+ return previous;
+ }
+ default :
+ // this should never happen
+ throw new MathInternalError();
+ }
+ }
+ }
+
+ },
+
+ /** Constant for triggering only increasing events.
+ * <p>When this filter is used, the wrapped {@link EventHandler
+ * event handler} {@link EventHandler#eventOccurred(double, double[],
+ * boolean) eventOccurred} method will be called <em>only</em> with
+ * its {@code increasing} argument set to true.</p>
+ */
+ TRIGGER_ONLY_INCREASING_EVENTS {
+
+ /** {@inheritDoc} */
+ @Override
+ protected boolean getTriggeredIncreasing() {
+ return true;
+ }
+
+ /** {@inheritDoc}
+ * <p>
+ * states scheduling for computing h(t,y) as an altered version of g(t, y)
+ * <ul>
+ * <li>0 are triggered events for which a zero is produced (here increasing events)</li>
+ * <li>X are ignored events for which zero is masked (here decreasing events)</li>
+ * </ul>
+ * </p>
+ * <pre>
+ * g(t)
+ * ___ ___ ___
+ * / \ / \ / \
+ * / \ / \ / \
+ * / g>0 \ / g>0 \ / g>0 \
+ * / \ / \ / \
+ * ----- 0 --------- X --------- 0 --------- X --------- 0 --------- X ---
+ * / \ / \ / \
+ * / \ g<0 / \ g<0 / \ g<0
+ * / \ / \ / \ /
+ * ___/ \___/ \___/ \___/
+ * </pre>
+ * <pre>
+ * h(t,y)) as an alteration of g(t,y)
+ * ___ ___
+ * \ / \ / \
+ * \ h=-g / \ / \ h=-g
+ * \ h=min(-s,-g,+g) / \ / \ h=min(-s,-g,+g)
+ * \ / \_/ \
+ * ------0 ----------_---------- 0 --------------------- 0 --------- _ ---
+ * \ / \ / \ / \
+ * \ / \ / h=max(+s,-g,+g) \ / \
+ * \ / \ / h=+g \ / \ /
+ * \___/ \___/ \___/ \___/
+ * </pre>
+ * <p>
+ * As shown by the figure above, several expressions are used to compute h,
+ * depending on the current state:
+ * <ul>
+ * <li>h = max(+s,-g,+g)</li>
+ * <li>h = +g</li>
+ * <li>h = min(-s,-g,+g)</li>
+ * <li>h = -g</li>
+ * </ul>
+ * where s is a tiny positive value: {@link org.apache.commons.math3.util.Precision#SAFE_MIN}.
+ * </p>
+ */
+ @Override
+ protected Transformer selectTransformer(final Transformer previous,
+ final double g, final boolean forward) {
+ if (forward) {
+ switch (previous) {
+ case UNINITIALIZED :
+ // we are initializing the first point
+ if (g > 0) {
+ // initialize as if previous root (i.e. backward one) was a triggered increasing event
+ return Transformer.PLUS;
+ } else if (g < 0) {
+ // initialize as if previous root (i.e. backward one) was an ignored decreasing event
+ return Transformer.MIN;
+ } else {
+ // we are exactly at a root, we don't know if it is an increasing
+ // or a decreasing event, we remain in uninitialized state
+ return Transformer.UNINITIALIZED;
+ }
+ case PLUS :
+ if (g <= 0) {
+ // we have crossed the zero line on an ignored decreasing event,
+ // we must change the transformer
+ return Transformer.MAX;
+ } else {
+ // we are still in the same status
+ return previous;
+ }
+ case MINUS :
+ if (g <= 0) {
+ // we have crossed the zero line on an ignored decreasing event,
+ // we must change the transformer
+ return Transformer.MIN;
+ } else {
+ // we are still in the same status
+ return previous;
+ }
+ case MIN :
+ if (g >= 0) {
+ // we have crossed the zero line on a triggered increasing event,
+ // we must change the transformer
+ return Transformer.PLUS;
+ } else {
+ // we are still in the same status
+ return previous;
+ }
+ case MAX :
+ if (g >= 0) {
+ // we have crossed the zero line on a triggered increasing event,
+ // we must change the transformer
+ return Transformer.MINUS;
+ } else {
+ // we are still in the same status
+ return previous;
+ }
+ default :
+ // this should never happen
+ throw new MathInternalError();
+ }
+ } else {
+ switch (previous) {
+ case UNINITIALIZED :
+ // we are initializing the first point
+ if (g > 0) {
+ // initialize as if previous root (i.e. forward one) was an ignored decreasing event
+ return Transformer.MAX;
+ } else if (g < 0) {
+ // initialize as if previous root (i.e. forward one) was a triggered increasing event
+ return Transformer.MINUS;
+ } else {
+ // we are exactly at a root, we don't know if it is an increasing
+ // or a decreasing event, we remain in uninitialized state
+ return Transformer.UNINITIALIZED;
+ }
+ case PLUS :
+ if (g >= 0) {
+ // we have crossed the zero line on an ignored decreasing event,
+ // we must change the transformer
+ return Transformer.MIN;
+ } else {
+ // we are still in the same status
+ return previous;
+ }
+ case MINUS :
+ if (g >= 0) {
+ // we have crossed the zero line on an ignored decreasing event,
+ // we must change the transformer
+ return Transformer.MAX;
+ } else {
+ // we are still in the same status
+ return previous;
+ }
+ case MIN :
+ if (g <= 0) {
+ // we have crossed the zero line on a triggered increasing event,
+ // we must change the transformer
+ return Transformer.MINUS;
+ } else {
+ // we are still in the same status
+ return previous;
+ }
+ case MAX :
+ if (g <= 0) {
+ // we have crossed the zero line on a triggered increasing event,
+ // we must change the transformer
+ return Transformer.PLUS;
+ } else {
+ // we are still in the same status
+ return previous;
+ }
+ default :
+ // this should never happen
+ throw new MathInternalError();
+ }
+ }
+ }
+
+ };
+
+ /** Get the increasing status of triggered events.
+ * @return true if triggered events are increasing events
+ */
+ protected abstract boolean getTriggeredIncreasing();
+
+ /** Get next function transformer in the specified direction.
+ * @param previous transformer active on the previous point with respect
+ * to integration direction (may be null if no previous point is known)
+ * @param g current value of the g function
+ * @param forward true if integration goes forward
+ * @return next transformer transformer
+ */
+ protected abstract Transformer selectTransformer(Transformer previous,
+ double g, boolean forward);
+
+}
diff --git a/src/main/java/org/apache/commons/math3/ode/events/Transformer.java b/src/main/java/org/apache/commons/math3/ode/events/Transformer.java
new file mode 100644
index 0000000..5c1f97c
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/ode/events/Transformer.java
@@ -0,0 +1,107 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math3.ode.events;
+
+import org.apache.commons.math3.util.FastMath;
+import org.apache.commons.math3.util.Precision;
+
+
+/** Transformer for {@link EventHandler#g(double, double[]) g functions}.
+ * @see EventFilter
+ * @see FilterType
+ * @since 3.2
+ */
+enum Transformer {
+
+ /** Transformer computing transformed = 0.
+ * <p>
+ * This transformer is used when we initialize the filter, until we get at
+ * least one non-zero value to select the proper transformer.
+ * </p>
+ */
+ UNINITIALIZED {
+ /** {@inheritDoc} */
+ @Override
+ protected double transformed(final double g) {
+ return 0;
+ }
+ },
+
+ /** Transformer computing transformed = g.
+ * <p>
+ * When this transformer is applied, the roots of the original function
+ * are preserved, with the same {@code increasing/decreasing} status.
+ * </p>
+ */
+ PLUS {
+ /** {@inheritDoc} */
+ @Override
+ protected double transformed(final double g) {
+ return g;
+ }
+ },
+
+ /** Transformer computing transformed = -g.
+ * <p>
+ * When this transformer is applied, the roots of the original function
+ * are preserved, with reversed {@code increasing/decreasing} status.
+ * </p>
+ */
+ MINUS {
+ /** {@inheritDoc} */
+ @Override
+ protected double transformed(final double g) {
+ return -g;
+ }
+ },
+
+ /** Transformer computing transformed = min(-{@link Precision#SAFE_MIN}, -g, +g).
+ * <p>
+ * When this transformer is applied, the transformed function is
+ * guaranteed to be always strictly negative (i.e. there are no roots).
+ * </p>
+ */
+ MIN {
+ /** {@inheritDoc} */
+ @Override
+ protected double transformed(final double g) {
+ return FastMath.min(-Precision.SAFE_MIN, FastMath.min(-g, +g));
+ }
+ },
+
+ /** Transformer computing transformed = max(+{@link Precision#SAFE_MIN}, -g, +g).
+ * <p>
+ * When this transformer is applied, the transformed function is
+ * guaranteed to be always strictly positive (i.e. there are no roots).
+ * </p>
+ */
+ MAX {
+ /** {@inheritDoc} */
+ @Override
+ protected double transformed(final double g) {
+ return FastMath.max(+Precision.SAFE_MIN, FastMath.max(-g, +g));
+ }
+ };
+
+ /** Transform value of function g.
+ * @param g raw value of function g
+ * @return transformed value of function g
+ */
+ protected abstract double transformed(double g);
+
+}
diff --git a/src/main/java/org/apache/commons/math3/ode/events/package-info.java b/src/main/java/org/apache/commons/math3/ode/events/package-info.java
new file mode 100644
index 0000000..9d3b917
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/ode/events/package-info.java
@@ -0,0 +1,96 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/**
+ *
+ * <p>
+ * This package provides classes to handle discrete events occurring during
+ * Ordinary Differential Equations integration.
+ * </p>
+ *
+ * <p>
+ * Discrete events detection is based on switching functions. The user provides
+ * a simple {@link org.apache.commons.math3.ode.events.EventHandler#g g(t, y)}
+ * function depending on the current time and state. The integrator will monitor
+ * the value of the function throughout integration range and will trigger the
+ * event when its sign changes. The magnitude of the value is almost irrelevant,
+ * it should however be continuous (but not necessarily smooth) for the sake of
+ * root finding. The steps are shortened as needed to ensure the events occur
+ * at step boundaries (even if the integrator is a fixed-step integrator).
+ * </p>
+ *
+ * <p>
+ * When an event is triggered, several different options are available:
+ * </p>
+ * <ul>
+ * <li>integration can be stopped (this is called a G-stop facility),</li>
+ * <li>the state vector or the derivatives can be changed,</li>
+ * <li>or integration can simply go on.</li>
+ * </ul>
+ *
+ * <p>
+ * The first case, G-stop, is the most common one. A typical use case is when an
+ * ODE must be solved up to some target state is reached, with a known value of
+ * the state but an unknown occurrence time. As an example, if we want to monitor
+ * a chemical reaction up to some predefined concentration for the first substance,
+ * we can use the following switching function setting:
+ * <pre>
+ * public double g(double t, double[] y) {
+ * return y[0] - targetConcentration;
+ * }
+ *
+ * public int eventOccurred(double t, double[] y) {
+ * return STOP;
+ * }
+ * </pre>
+ * </p>
+ *
+ * <p>
+ * The second case, change state vector or derivatives is encountered when dealing
+ * with discontinuous dynamical models. A typical case would be the motion of a
+ * spacecraft when thrusters are fired for orbital maneuvers. The acceleration is
+ * smooth as long as no maneuver are performed, depending only on gravity, drag,
+ * third body attraction, radiation pressure. Firing a thruster introduces a
+ * discontinuity that must be handled appropriately by the integrator. In such a case,
+ * we would use a switching function setting similar to this:
+ * <pre>
+ * public double g(double t, double[] y) {
+ * return (t - tManeuverStart) &lowast; (t - tManeuverStop);
+ * }
+ *
+ * public int eventOccurred(double t, double[] y) {
+ * return RESET_DERIVATIVES;
+ * }
+ * </pre>
+ * </p>
+ *
+ * <p>
+ * The third case is useful mainly for monitoring purposes, a simple example is:
+ * <pre>
+ * public double g(double t, double[] y) {
+ * return y[0] - y[1];
+ * }
+ *
+ * public int eventOccurred(double t, double[] y) {
+ * logger.log("y0(t) and y1(t) curves cross at t = " + t);
+ * return CONTINUE;
+ * }
+ * </pre>
+ * </p>
+ *
+ *
+ */
+package org.apache.commons.math3.ode.events;
diff --git a/src/main/java/org/apache/commons/math3/ode/nonstiff/AdamsBashforthFieldIntegrator.java b/src/main/java/org/apache/commons/math3/ode/nonstiff/AdamsBashforthFieldIntegrator.java
new file mode 100644
index 0000000..bec3343
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/ode/nonstiff/AdamsBashforthFieldIntegrator.java
@@ -0,0 +1,354 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math3.ode.nonstiff;
+
+import org.apache.commons.math3.Field;
+import org.apache.commons.math3.RealFieldElement;
+import org.apache.commons.math3.exception.DimensionMismatchException;
+import org.apache.commons.math3.exception.MaxCountExceededException;
+import org.apache.commons.math3.exception.NoBracketingException;
+import org.apache.commons.math3.exception.NumberIsTooSmallException;
+import org.apache.commons.math3.linear.Array2DRowFieldMatrix;
+import org.apache.commons.math3.linear.FieldMatrix;
+import org.apache.commons.math3.ode.FieldExpandableODE;
+import org.apache.commons.math3.ode.FieldODEState;
+import org.apache.commons.math3.ode.FieldODEStateAndDerivative;
+import org.apache.commons.math3.util.MathArrays;
+
+
+/**
+ * This class implements explicit Adams-Bashforth integrators for Ordinary
+ * Differential Equations.
+ *
+ * <p>Adams-Bashforth methods (in fact due to Adams alone) are explicit
+ * multistep ODE solvers. This implementation is a variation of the classical
+ * one: it uses adaptive stepsize to implement error control, whereas
+ * classical implementations are fixed step size. The value of state vector
+ * at step n+1 is a simple combination of the value at step n and of the
+ * derivatives at steps n, n-1, n-2 ... Depending on the number k of previous
+ * steps one wants to use for computing the next value, different formulas
+ * are available:</p>
+ * <ul>
+ * <li>k = 1: y<sub>n+1</sub> = y<sub>n</sub> + h y'<sub>n</sub></li>
+ * <li>k = 2: y<sub>n+1</sub> = y<sub>n</sub> + h (3y'<sub>n</sub>-y'<sub>n-1</sub>)/2</li>
+ * <li>k = 3: y<sub>n+1</sub> = y<sub>n</sub> + h (23y'<sub>n</sub>-16y'<sub>n-1</sub>+5y'<sub>n-2</sub>)/12</li>
+ * <li>k = 4: y<sub>n+1</sub> = y<sub>n</sub> + h (55y'<sub>n</sub>-59y'<sub>n-1</sub>+37y'<sub>n-2</sub>-9y'<sub>n-3</sub>)/24</li>
+ * <li>...</li>
+ * </ul>
+ *
+ * <p>A k-steps Adams-Bashforth method is of order k.</p>
+ *
+ * <h3>Implementation details</h3>
+ *
+ * <p>We define scaled derivatives s<sub>i</sub>(n) at step n as:
+ * <pre>
+ * s<sub>1</sub>(n) = h y'<sub>n</sub> for first derivative
+ * s<sub>2</sub>(n) = h<sup>2</sup>/2 y''<sub>n</sub> for second derivative
+ * s<sub>3</sub>(n) = h<sup>3</sup>/6 y'''<sub>n</sub> for third derivative
+ * ...
+ * s<sub>k</sub>(n) = h<sup>k</sup>/k! y<sup>(k)</sup><sub>n</sub> for k<sup>th</sup> derivative
+ * </pre></p>
+ *
+ * <p>The definitions above use the classical representation with several previous first
+ * derivatives. Lets define
+ * <pre>
+ * q<sub>n</sub> = [ s<sub>1</sub>(n-1) s<sub>1</sub>(n-2) ... s<sub>1</sub>(n-(k-1)) ]<sup>T</sup>
+ * </pre>
+ * (we omit the k index in the notation for clarity). With these definitions,
+ * Adams-Bashforth methods can be written:
+ * <ul>
+ * <li>k = 1: y<sub>n+1</sub> = y<sub>n</sub> + s<sub>1</sub>(n)</li>
+ * <li>k = 2: y<sub>n+1</sub> = y<sub>n</sub> + 3/2 s<sub>1</sub>(n) + [ -1/2 ] q<sub>n</sub></li>
+ * <li>k = 3: y<sub>n+1</sub> = y<sub>n</sub> + 23/12 s<sub>1</sub>(n) + [ -16/12 5/12 ] q<sub>n</sub></li>
+ * <li>k = 4: y<sub>n+1</sub> = y<sub>n</sub> + 55/24 s<sub>1</sub>(n) + [ -59/24 37/24 -9/24 ] q<sub>n</sub></li>
+ * <li>...</li>
+ * </ul></p>
+ *
+ * <p>Instead of using the classical representation with first derivatives only (y<sub>n</sub>,
+ * s<sub>1</sub>(n) and q<sub>n</sub>), our implementation uses the Nordsieck vector with
+ * higher degrees scaled derivatives all taken at the same step (y<sub>n</sub>, s<sub>1</sub>(n)
+ * and r<sub>n</sub>) where r<sub>n</sub> is defined as:
+ * <pre>
+ * r<sub>n</sub> = [ s<sub>2</sub>(n), s<sub>3</sub>(n) ... s<sub>k</sub>(n) ]<sup>T</sup>
+ * </pre>
+ * (here again we omit the k index in the notation for clarity)
+ * </p>
+ *
+ * <p>Taylor series formulas show that for any index offset i, s<sub>1</sub>(n-i) can be
+ * computed from s<sub>1</sub>(n), s<sub>2</sub>(n) ... s<sub>k</sub>(n), the formula being exact
+ * for degree k polynomials.
+ * <pre>
+ * s<sub>1</sub>(n-i) = s<sub>1</sub>(n) + &sum;<sub>j&gt;0</sub> (j+1) (-i)<sup>j</sup> s<sub>j+1</sub>(n)
+ * </pre>
+ * The previous formula can be used with several values for i to compute the transform between
+ * classical representation and Nordsieck vector. The transform between r<sub>n</sub>
+ * and q<sub>n</sub> resulting from the Taylor series formulas above is:
+ * <pre>
+ * q<sub>n</sub> = s<sub>1</sub>(n) u + P r<sub>n</sub>
+ * </pre>
+ * where u is the [ 1 1 ... 1 ]<sup>T</sup> vector and P is the (k-1)&times;(k-1) matrix built
+ * with the (j+1) (-i)<sup>j</sup> terms with i being the row number starting from 1 and j being
+ * the column number starting from 1:
+ * <pre>
+ * [ -2 3 -4 5 ... ]
+ * [ -4 12 -32 80 ... ]
+ * P = [ -6 27 -108 405 ... ]
+ * [ -8 48 -256 1280 ... ]
+ * [ ... ]
+ * </pre></p>
+ *
+ * <p>Using the Nordsieck vector has several advantages:
+ * <ul>
+ * <li>it greatly simplifies step interpolation as the interpolator mainly applies
+ * Taylor series formulas,</li>
+ * <li>it simplifies step changes that occur when discrete events that truncate
+ * the step are triggered,</li>
+ * <li>it allows to extend the methods in order to support adaptive stepsize.</li>
+ * </ul></p>
+ *
+ * <p>The Nordsieck vector at step n+1 is computed from the Nordsieck vector at step n as follows:
+ * <ul>
+ * <li>y<sub>n+1</sub> = y<sub>n</sub> + s<sub>1</sub>(n) + u<sup>T</sup> r<sub>n</sub></li>
+ * <li>s<sub>1</sub>(n+1) = h f(t<sub>n+1</sub>, y<sub>n+1</sub>)</li>
+ * <li>r<sub>n+1</sub> = (s<sub>1</sub>(n) - s<sub>1</sub>(n+1)) P<sup>-1</sup> u + P<sup>-1</sup> A P r<sub>n</sub></li>
+ * </ul>
+ * where A is a rows shifting matrix (the lower left part is an identity matrix):
+ * <pre>
+ * [ 0 0 ... 0 0 | 0 ]
+ * [ ---------------+---]
+ * [ 1 0 ... 0 0 | 0 ]
+ * A = [ 0 1 ... 0 0 | 0 ]
+ * [ ... | 0 ]
+ * [ 0 0 ... 1 0 | 0 ]
+ * [ 0 0 ... 0 1 | 0 ]
+ * </pre></p>
+ *
+ * <p>The P<sup>-1</sup>u vector and the P<sup>-1</sup> A P matrix do not depend on the state,
+ * they only depend on k and therefore are precomputed once for all.</p>
+ *
+ * @param <T> the type of the field elements
+ * @since 3.6
+ */
+public class AdamsBashforthFieldIntegrator<T extends RealFieldElement<T>> extends AdamsFieldIntegrator<T> {
+
+ /** Integrator method name. */
+ private static final String METHOD_NAME = "Adams-Bashforth";
+
+ /**
+ * Build an Adams-Bashforth integrator with the given order and step control parameters.
+ * @param field field to which the time and state vector elements belong
+ * @param nSteps number of steps of the method excluding the one being computed
+ * @param minStep minimal step (sign is irrelevant, regardless of
+ * integration direction, forward or backward), the last step can
+ * be smaller than this
+ * @param maxStep maximal step (sign is irrelevant, regardless of
+ * integration direction, forward or backward), the last step can
+ * be smaller than this
+ * @param scalAbsoluteTolerance allowed absolute error
+ * @param scalRelativeTolerance allowed relative error
+ * @exception NumberIsTooSmallException if order is 1 or less
+ */
+ public AdamsBashforthFieldIntegrator(final Field<T> field, final int nSteps,
+ final double minStep, final double maxStep,
+ final double scalAbsoluteTolerance,
+ final double scalRelativeTolerance)
+ throws NumberIsTooSmallException {
+ super(field, METHOD_NAME, nSteps, nSteps, minStep, maxStep,
+ scalAbsoluteTolerance, scalRelativeTolerance);
+ }
+
+ /**
+ * Build an Adams-Bashforth integrator with the given order and step control parameters.
+ * @param field field to which the time and state vector elements belong
+ * @param nSteps number of steps of the method excluding the one being computed
+ * @param minStep minimal step (sign is irrelevant, regardless of
+ * integration direction, forward or backward), the last step can
+ * be smaller than this
+ * @param maxStep maximal step (sign is irrelevant, regardless of
+ * integration direction, forward or backward), the last step can
+ * be smaller than this
+ * @param vecAbsoluteTolerance allowed absolute error
+ * @param vecRelativeTolerance allowed relative error
+ * @exception IllegalArgumentException if order is 1 or less
+ */
+ public AdamsBashforthFieldIntegrator(final Field<T> field, final int nSteps,
+ final double minStep, final double maxStep,
+ final double[] vecAbsoluteTolerance,
+ final double[] vecRelativeTolerance)
+ throws IllegalArgumentException {
+ super(field, METHOD_NAME, nSteps, nSteps, minStep, maxStep,
+ vecAbsoluteTolerance, vecRelativeTolerance);
+ }
+
+ /** Estimate error.
+ * <p>
+ * Error is estimated by interpolating back to previous state using
+ * the state Taylor expansion and comparing to real previous state.
+ * </p>
+ * @param previousState state vector at step start
+ * @param predictedState predicted state vector at step end
+ * @param predictedScaled predicted value of the scaled derivatives at step end
+ * @param predictedNordsieck predicted value of the Nordsieck vector at step end
+ * @return estimated normalized local discretization error
+ */
+ private T errorEstimation(final T[] previousState,
+ final T[] predictedState,
+ final T[] predictedScaled,
+ final FieldMatrix<T> predictedNordsieck) {
+
+ T error = getField().getZero();
+ for (int i = 0; i < mainSetDimension; ++i) {
+ final T yScale = predictedState[i].abs();
+ final T tol = (vecAbsoluteTolerance == null) ?
+ yScale.multiply(scalRelativeTolerance).add(scalAbsoluteTolerance) :
+ yScale.multiply(vecRelativeTolerance[i]).add(vecAbsoluteTolerance[i]);
+
+ // apply Taylor formula from high order to low order,
+ // for the sake of numerical accuracy
+ T variation = getField().getZero();
+ int sign = predictedNordsieck.getRowDimension() % 2 == 0 ? -1 : 1;
+ for (int k = predictedNordsieck.getRowDimension() - 1; k >= 0; --k) {
+ variation = variation.add(predictedNordsieck.getEntry(k, i).multiply(sign));
+ sign = -sign;
+ }
+ variation = variation.subtract(predictedScaled[i]);
+
+ final T ratio = predictedState[i].subtract(previousState[i]).add(variation).divide(tol);
+ error = error.add(ratio.multiply(ratio));
+
+ }
+
+ return error.divide(mainSetDimension).sqrt();
+
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public FieldODEStateAndDerivative<T> integrate(final FieldExpandableODE<T> equations,
+ final FieldODEState<T> initialState,
+ final T finalTime)
+ throws NumberIsTooSmallException, DimensionMismatchException,
+ MaxCountExceededException, NoBracketingException {
+
+ sanityChecks(initialState, finalTime);
+ final T t0 = initialState.getTime();
+ final T[] y = equations.getMapper().mapState(initialState);
+ setStepStart(initIntegration(equations, t0, y, finalTime));
+ final boolean forward = finalTime.subtract(initialState.getTime()).getReal() > 0;
+
+ // compute the initial Nordsieck vector using the configured starter integrator
+ start(equations, getStepStart(), finalTime);
+
+ // reuse the step that was chosen by the starter integrator
+ FieldODEStateAndDerivative<T> stepStart = getStepStart();
+ FieldODEStateAndDerivative<T> stepEnd =
+ AdamsFieldStepInterpolator.taylor(stepStart,
+ stepStart.getTime().add(getStepSize()),
+ getStepSize(), scaled, nordsieck);
+
+ // main integration loop
+ setIsLastStep(false);
+ do {
+
+ T[] predictedY = null;
+ final T[] predictedScaled = MathArrays.buildArray(getField(), y.length);
+ Array2DRowFieldMatrix<T> predictedNordsieck = null;
+ T error = getField().getZero().add(10);
+ while (error.subtract(1.0).getReal() >= 0.0) {
+
+ // predict a first estimate of the state at step end
+ predictedY = stepEnd.getState();
+
+ // evaluate the derivative
+ final T[] yDot = computeDerivatives(stepEnd.getTime(), predictedY);
+
+ // predict Nordsieck vector at step end
+ for (int j = 0; j < predictedScaled.length; ++j) {
+ predictedScaled[j] = getStepSize().multiply(yDot[j]);
+ }
+ predictedNordsieck = updateHighOrderDerivativesPhase1(nordsieck);
+ updateHighOrderDerivativesPhase2(scaled, predictedScaled, predictedNordsieck);
+
+ // evaluate error
+ error = errorEstimation(y, predictedY, predictedScaled, predictedNordsieck);
+
+ if (error.subtract(1.0).getReal() >= 0.0) {
+ // reject the step and attempt to reduce error by stepsize control
+ final T factor = computeStepGrowShrinkFactor(error);
+ rescale(filterStep(getStepSize().multiply(factor), forward, false));
+ stepEnd = AdamsFieldStepInterpolator.taylor(getStepStart(),
+ getStepStart().getTime().add(getStepSize()),
+ getStepSize(),
+ scaled,
+ nordsieck);
+
+ }
+ }
+
+ // discrete events handling
+ setStepStart(acceptStep(new AdamsFieldStepInterpolator<T>(getStepSize(), stepEnd,
+ predictedScaled, predictedNordsieck, forward,
+ getStepStart(), stepEnd,
+ equations.getMapper()),
+ finalTime));
+ scaled = predictedScaled;
+ nordsieck = predictedNordsieck;
+
+ if (!isLastStep()) {
+
+ System.arraycopy(predictedY, 0, y, 0, y.length);
+
+ if (resetOccurred()) {
+ // some events handler has triggered changes that
+ // invalidate the derivatives, we need to restart from scratch
+ start(equations, getStepStart(), finalTime);
+ }
+
+ // stepsize control for next step
+ final T factor = computeStepGrowShrinkFactor(error);
+ final T scaledH = getStepSize().multiply(factor);
+ final T nextT = getStepStart().getTime().add(scaledH);
+ final boolean nextIsLast = forward ?
+ nextT.subtract(finalTime).getReal() >= 0 :
+ nextT.subtract(finalTime).getReal() <= 0;
+ T hNew = filterStep(scaledH, forward, nextIsLast);
+
+ final T filteredNextT = getStepStart().getTime().add(hNew);
+ final boolean filteredNextIsLast = forward ?
+ filteredNextT.subtract(finalTime).getReal() >= 0 :
+ filteredNextT.subtract(finalTime).getReal() <= 0;
+ if (filteredNextIsLast) {
+ hNew = finalTime.subtract(getStepStart().getTime());
+ }
+
+ rescale(hNew);
+ stepEnd = AdamsFieldStepInterpolator.taylor(getStepStart(), getStepStart().getTime().add(getStepSize()),
+ getStepSize(), scaled, nordsieck);
+
+ }
+
+ } while (!isLastStep());
+
+ final FieldODEStateAndDerivative<T> finalState = getStepStart();
+ setStepStart(null);
+ setStepSize(null);
+ return finalState;
+
+ }
+
+}
diff --git a/src/main/java/org/apache/commons/math3/ode/nonstiff/AdamsBashforthIntegrator.java b/src/main/java/org/apache/commons/math3/ode/nonstiff/AdamsBashforthIntegrator.java
new file mode 100644
index 0000000..af4a435
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/ode/nonstiff/AdamsBashforthIntegrator.java
@@ -0,0 +1,362 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math3.ode.nonstiff;
+
+import org.apache.commons.math3.exception.DimensionMismatchException;
+import org.apache.commons.math3.exception.MaxCountExceededException;
+import org.apache.commons.math3.exception.NoBracketingException;
+import org.apache.commons.math3.exception.NumberIsTooSmallException;
+import org.apache.commons.math3.linear.Array2DRowRealMatrix;
+import org.apache.commons.math3.linear.RealMatrix;
+import org.apache.commons.math3.ode.EquationsMapper;
+import org.apache.commons.math3.ode.ExpandableStatefulODE;
+import org.apache.commons.math3.ode.sampling.NordsieckStepInterpolator;
+import org.apache.commons.math3.util.FastMath;
+
+
+/**
+ * This class implements explicit Adams-Bashforth integrators for Ordinary
+ * Differential Equations.
+ *
+ * <p>Adams-Bashforth methods (in fact due to Adams alone) are explicit
+ * multistep ODE solvers. This implementation is a variation of the classical
+ * one: it uses adaptive stepsize to implement error control, whereas
+ * classical implementations are fixed step size. The value of state vector
+ * at step n+1 is a simple combination of the value at step n and of the
+ * derivatives at steps n, n-1, n-2 ... Depending on the number k of previous
+ * steps one wants to use for computing the next value, different formulas
+ * are available:</p>
+ * <ul>
+ * <li>k = 1: y<sub>n+1</sub> = y<sub>n</sub> + h y'<sub>n</sub></li>
+ * <li>k = 2: y<sub>n+1</sub> = y<sub>n</sub> + h (3y'<sub>n</sub>-y'<sub>n-1</sub>)/2</li>
+ * <li>k = 3: y<sub>n+1</sub> = y<sub>n</sub> + h (23y'<sub>n</sub>-16y'<sub>n-1</sub>+5y'<sub>n-2</sub>)/12</li>
+ * <li>k = 4: y<sub>n+1</sub> = y<sub>n</sub> + h (55y'<sub>n</sub>-59y'<sub>n-1</sub>+37y'<sub>n-2</sub>-9y'<sub>n-3</sub>)/24</li>
+ * <li>...</li>
+ * </ul>
+ *
+ * <p>A k-steps Adams-Bashforth method is of order k.</p>
+ *
+ * <h3>Implementation details</h3>
+ *
+ * <p>We define scaled derivatives s<sub>i</sub>(n) at step n as:
+ * <pre>
+ * s<sub>1</sub>(n) = h y'<sub>n</sub> for first derivative
+ * s<sub>2</sub>(n) = h<sup>2</sup>/2 y''<sub>n</sub> for second derivative
+ * s<sub>3</sub>(n) = h<sup>3</sup>/6 y'''<sub>n</sub> for third derivative
+ * ...
+ * s<sub>k</sub>(n) = h<sup>k</sup>/k! y<sup>(k)</sup><sub>n</sub> for k<sup>th</sup> derivative
+ * </pre></p>
+ *
+ * <p>The definitions above use the classical representation with several previous first
+ * derivatives. Lets define
+ * <pre>
+ * q<sub>n</sub> = [ s<sub>1</sub>(n-1) s<sub>1</sub>(n-2) ... s<sub>1</sub>(n-(k-1)) ]<sup>T</sup>
+ * </pre>
+ * (we omit the k index in the notation for clarity). With these definitions,
+ * Adams-Bashforth methods can be written:
+ * <ul>
+ * <li>k = 1: y<sub>n+1</sub> = y<sub>n</sub> + s<sub>1</sub>(n)</li>
+ * <li>k = 2: y<sub>n+1</sub> = y<sub>n</sub> + 3/2 s<sub>1</sub>(n) + [ -1/2 ] q<sub>n</sub></li>
+ * <li>k = 3: y<sub>n+1</sub> = y<sub>n</sub> + 23/12 s<sub>1</sub>(n) + [ -16/12 5/12 ] q<sub>n</sub></li>
+ * <li>k = 4: y<sub>n+1</sub> = y<sub>n</sub> + 55/24 s<sub>1</sub>(n) + [ -59/24 37/24 -9/24 ] q<sub>n</sub></li>
+ * <li>...</li>
+ * </ul></p>
+ *
+ * <p>Instead of using the classical representation with first derivatives only (y<sub>n</sub>,
+ * s<sub>1</sub>(n) and q<sub>n</sub>), our implementation uses the Nordsieck vector with
+ * higher degrees scaled derivatives all taken at the same step (y<sub>n</sub>, s<sub>1</sub>(n)
+ * and r<sub>n</sub>) where r<sub>n</sub> is defined as:
+ * <pre>
+ * r<sub>n</sub> = [ s<sub>2</sub>(n), s<sub>3</sub>(n) ... s<sub>k</sub>(n) ]<sup>T</sup>
+ * </pre>
+ * (here again we omit the k index in the notation for clarity)
+ * </p>
+ *
+ * <p>Taylor series formulas show that for any index offset i, s<sub>1</sub>(n-i) can be
+ * computed from s<sub>1</sub>(n), s<sub>2</sub>(n) ... s<sub>k</sub>(n), the formula being exact
+ * for degree k polynomials.
+ * <pre>
+ * s<sub>1</sub>(n-i) = s<sub>1</sub>(n) + &sum;<sub>j&gt;0</sub> (j+1) (-i)<sup>j</sup> s<sub>j+1</sub>(n)
+ * </pre>
+ * The previous formula can be used with several values for i to compute the transform between
+ * classical representation and Nordsieck vector. The transform between r<sub>n</sub>
+ * and q<sub>n</sub> resulting from the Taylor series formulas above is:
+ * <pre>
+ * q<sub>n</sub> = s<sub>1</sub>(n) u + P r<sub>n</sub>
+ * </pre>
+ * where u is the [ 1 1 ... 1 ]<sup>T</sup> vector and P is the (k-1)&times;(k-1) matrix built
+ * with the (j+1) (-i)<sup>j</sup> terms with i being the row number starting from 1 and j being
+ * the column number starting from 1:
+ * <pre>
+ * [ -2 3 -4 5 ... ]
+ * [ -4 12 -32 80 ... ]
+ * P = [ -6 27 -108 405 ... ]
+ * [ -8 48 -256 1280 ... ]
+ * [ ... ]
+ * </pre></p>
+ *
+ * <p>Using the Nordsieck vector has several advantages:
+ * <ul>
+ * <li>it greatly simplifies step interpolation as the interpolator mainly applies
+ * Taylor series formulas,</li>
+ * <li>it simplifies step changes that occur when discrete events that truncate
+ * the step are triggered,</li>
+ * <li>it allows to extend the methods in order to support adaptive stepsize.</li>
+ * </ul></p>
+ *
+ * <p>The Nordsieck vector at step n+1 is computed from the Nordsieck vector at step n as follows:
+ * <ul>
+ * <li>y<sub>n+1</sub> = y<sub>n</sub> + s<sub>1</sub>(n) + u<sup>T</sup> r<sub>n</sub></li>
+ * <li>s<sub>1</sub>(n+1) = h f(t<sub>n+1</sub>, y<sub>n+1</sub>)</li>
+ * <li>r<sub>n+1</sub> = (s<sub>1</sub>(n) - s<sub>1</sub>(n+1)) P<sup>-1</sup> u + P<sup>-1</sup> A P r<sub>n</sub></li>
+ * </ul>
+ * where A is a rows shifting matrix (the lower left part is an identity matrix):
+ * <pre>
+ * [ 0 0 ... 0 0 | 0 ]
+ * [ ---------------+---]
+ * [ 1 0 ... 0 0 | 0 ]
+ * A = [ 0 1 ... 0 0 | 0 ]
+ * [ ... | 0 ]
+ * [ 0 0 ... 1 0 | 0 ]
+ * [ 0 0 ... 0 1 | 0 ]
+ * </pre></p>
+ *
+ * <p>The P<sup>-1</sup>u vector and the P<sup>-1</sup> A P matrix do not depend on the state,
+ * they only depend on k and therefore are precomputed once for all.</p>
+ *
+ * @since 2.0
+ */
+public class AdamsBashforthIntegrator extends AdamsIntegrator {
+
+ /** Integrator method name. */
+ private static final String METHOD_NAME = "Adams-Bashforth";
+
+ /**
+ * Build an Adams-Bashforth integrator with the given order and step control parameters.
+ * @param nSteps number of steps of the method excluding the one being computed
+ * @param minStep minimal step (sign is irrelevant, regardless of
+ * integration direction, forward or backward), the last step can
+ * be smaller than this
+ * @param maxStep maximal step (sign is irrelevant, regardless of
+ * integration direction, forward or backward), the last step can
+ * be smaller than this
+ * @param scalAbsoluteTolerance allowed absolute error
+ * @param scalRelativeTolerance allowed relative error
+ * @exception NumberIsTooSmallException if order is 1 or less
+ */
+ public AdamsBashforthIntegrator(final int nSteps,
+ final double minStep, final double maxStep,
+ final double scalAbsoluteTolerance,
+ final double scalRelativeTolerance)
+ throws NumberIsTooSmallException {
+ super(METHOD_NAME, nSteps, nSteps, minStep, maxStep,
+ scalAbsoluteTolerance, scalRelativeTolerance);
+ }
+
+ /**
+ * Build an Adams-Bashforth integrator with the given order and step control parameters.
+ * @param nSteps number of steps of the method excluding the one being computed
+ * @param minStep minimal step (sign is irrelevant, regardless of
+ * integration direction, forward or backward), the last step can
+ * be smaller than this
+ * @param maxStep maximal step (sign is irrelevant, regardless of
+ * integration direction, forward or backward), the last step can
+ * be smaller than this
+ * @param vecAbsoluteTolerance allowed absolute error
+ * @param vecRelativeTolerance allowed relative error
+ * @exception IllegalArgumentException if order is 1 or less
+ */
+ public AdamsBashforthIntegrator(final int nSteps,
+ final double minStep, final double maxStep,
+ final double[] vecAbsoluteTolerance,
+ final double[] vecRelativeTolerance)
+ throws IllegalArgumentException {
+ super(METHOD_NAME, nSteps, nSteps, minStep, maxStep,
+ vecAbsoluteTolerance, vecRelativeTolerance);
+ }
+
+ /** Estimate error.
+ * <p>
+ * Error is estimated by interpolating back to previous state using
+ * the state Taylor expansion and comparing to real previous state.
+ * </p>
+ * @param previousState state vector at step start
+ * @param predictedState predicted state vector at step end
+ * @param predictedScaled predicted value of the scaled derivatives at step end
+ * @param predictedNordsieck predicted value of the Nordsieck vector at step end
+ * @return estimated normalized local discretization error
+ */
+ private double errorEstimation(final double[] previousState,
+ final double[] predictedState,
+ final double[] predictedScaled,
+ final RealMatrix predictedNordsieck) {
+
+ double error = 0;
+ for (int i = 0; i < mainSetDimension; ++i) {
+ final double yScale = FastMath.abs(predictedState[i]);
+ final double tol = (vecAbsoluteTolerance == null) ?
+ (scalAbsoluteTolerance + scalRelativeTolerance * yScale) :
+ (vecAbsoluteTolerance[i] + vecRelativeTolerance[i] * yScale);
+
+ // apply Taylor formula from high order to low order,
+ // for the sake of numerical accuracy
+ double variation = 0;
+ int sign = predictedNordsieck.getRowDimension() % 2 == 0 ? -1 : 1;
+ for (int k = predictedNordsieck.getRowDimension() - 1; k >= 0; --k) {
+ variation += sign * predictedNordsieck.getEntry(k, i);
+ sign = -sign;
+ }
+ variation -= predictedScaled[i];
+
+ final double ratio = (predictedState[i] - previousState[i] + variation) / tol;
+ error += ratio * ratio;
+
+ }
+
+ return FastMath.sqrt(error / mainSetDimension);
+
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void integrate(final ExpandableStatefulODE equations, final double t)
+ throws NumberIsTooSmallException, DimensionMismatchException,
+ MaxCountExceededException, NoBracketingException {
+
+ sanityChecks(equations, t);
+ setEquations(equations);
+ final boolean forward = t > equations.getTime();
+
+ // initialize working arrays
+ final double[] y = equations.getCompleteState();
+ final double[] yDot = new double[y.length];
+
+ // set up an interpolator sharing the integrator arrays
+ final NordsieckStepInterpolator interpolator = new NordsieckStepInterpolator();
+ interpolator.reinitialize(y, forward,
+ equations.getPrimaryMapper(), equations.getSecondaryMappers());
+
+ // set up integration control objects
+ initIntegration(equations.getTime(), y, t);
+
+ // compute the initial Nordsieck vector using the configured starter integrator
+ start(equations.getTime(), y, t);
+ interpolator.reinitialize(stepStart, stepSize, scaled, nordsieck);
+ interpolator.storeTime(stepStart);
+
+ // reuse the step that was chosen by the starter integrator
+ double hNew = stepSize;
+ interpolator.rescale(hNew);
+
+ // main integration loop
+ isLastStep = false;
+ do {
+
+ interpolator.shift();
+ final double[] predictedY = new double[y.length];
+ final double[] predictedScaled = new double[y.length];
+ Array2DRowRealMatrix predictedNordsieck = null;
+ double error = 10;
+ while (error >= 1.0) {
+
+ // predict a first estimate of the state at step end
+ final double stepEnd = stepStart + hNew;
+ interpolator.storeTime(stepEnd);
+ final ExpandableStatefulODE expandable = getExpandable();
+ final EquationsMapper primary = expandable.getPrimaryMapper();
+ primary.insertEquationData(interpolator.getInterpolatedState(), predictedY);
+ int index = 0;
+ for (final EquationsMapper secondary : expandable.getSecondaryMappers()) {
+ secondary.insertEquationData(interpolator.getInterpolatedSecondaryState(index), predictedY);
+ ++index;
+ }
+
+ // evaluate the derivative
+ computeDerivatives(stepEnd, predictedY, yDot);
+
+ // predict Nordsieck vector at step end
+ for (int j = 0; j < predictedScaled.length; ++j) {
+ predictedScaled[j] = hNew * yDot[j];
+ }
+ predictedNordsieck = updateHighOrderDerivativesPhase1(nordsieck);
+ updateHighOrderDerivativesPhase2(scaled, predictedScaled, predictedNordsieck);
+
+ // evaluate error
+ error = errorEstimation(y, predictedY, predictedScaled, predictedNordsieck);
+
+ if (error >= 1.0) {
+ // reject the step and attempt to reduce error by stepsize control
+ final double factor = computeStepGrowShrinkFactor(error);
+ hNew = filterStep(hNew * factor, forward, false);
+ interpolator.rescale(hNew);
+
+ }
+ }
+
+ stepSize = hNew;
+ final double stepEnd = stepStart + stepSize;
+ interpolator.reinitialize(stepEnd, stepSize, predictedScaled, predictedNordsieck);
+
+ // discrete events handling
+ interpolator.storeTime(stepEnd);
+ System.arraycopy(predictedY, 0, y, 0, y.length);
+ stepStart = acceptStep(interpolator, y, yDot, t);
+ scaled = predictedScaled;
+ nordsieck = predictedNordsieck;
+ interpolator.reinitialize(stepEnd, stepSize, scaled, nordsieck);
+
+ if (!isLastStep) {
+
+ // prepare next step
+ interpolator.storeTime(stepStart);
+
+ if (resetOccurred) {
+ // some events handler has triggered changes that
+ // invalidate the derivatives, we need to restart from scratch
+ start(stepStart, y, t);
+ interpolator.reinitialize(stepStart, stepSize, scaled, nordsieck);
+ }
+
+ // stepsize control for next step
+ final double factor = computeStepGrowShrinkFactor(error);
+ final double scaledH = stepSize * factor;
+ final double nextT = stepStart + scaledH;
+ final boolean nextIsLast = forward ? (nextT >= t) : (nextT <= t);
+ hNew = filterStep(scaledH, forward, nextIsLast);
+
+ final double filteredNextT = stepStart + hNew;
+ final boolean filteredNextIsLast = forward ? (filteredNextT >= t) : (filteredNextT <= t);
+ if (filteredNextIsLast) {
+ hNew = t - stepStart;
+ }
+
+ interpolator.rescale(hNew);
+
+ }
+
+ } while (!isLastStep);
+
+ // dispatch results
+ equations.setTime(stepStart);
+ equations.setCompleteState(y);
+
+ resetInternalState();
+
+ }
+
+}
diff --git a/src/main/java/org/apache/commons/math3/ode/nonstiff/AdamsFieldIntegrator.java b/src/main/java/org/apache/commons/math3/ode/nonstiff/AdamsFieldIntegrator.java
new file mode 100644
index 0000000..fcd9397
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/ode/nonstiff/AdamsFieldIntegrator.java
@@ -0,0 +1,145 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math3.ode.nonstiff;
+
+import org.apache.commons.math3.Field;
+import org.apache.commons.math3.RealFieldElement;
+import org.apache.commons.math3.exception.DimensionMismatchException;
+import org.apache.commons.math3.exception.MaxCountExceededException;
+import org.apache.commons.math3.exception.NoBracketingException;
+import org.apache.commons.math3.exception.NumberIsTooSmallException;
+import org.apache.commons.math3.linear.Array2DRowFieldMatrix;
+import org.apache.commons.math3.ode.FieldExpandableODE;
+import org.apache.commons.math3.ode.FieldODEState;
+import org.apache.commons.math3.ode.FieldODEStateAndDerivative;
+import org.apache.commons.math3.ode.MultistepFieldIntegrator;
+
+
+/** Base class for {@link AdamsBashforthFieldIntegrator Adams-Bashforth} and
+ * {@link AdamsMoultonFieldIntegrator Adams-Moulton} integrators.
+ * @param <T> the type of the field elements
+ * @since 3.6
+ */
+public abstract class AdamsFieldIntegrator<T extends RealFieldElement<T>> extends MultistepFieldIntegrator<T> {
+
+ /** Transformer. */
+ private final AdamsNordsieckFieldTransformer<T> transformer;
+
+ /**
+ * Build an Adams integrator with the given order and step control parameters.
+ * @param field field to which the time and state vector elements belong
+ * @param name name of the method
+ * @param nSteps number of steps of the method excluding the one being computed
+ * @param order order of the method
+ * @param minStep minimal step (sign is irrelevant, regardless of
+ * integration direction, forward or backward), the last step can
+ * be smaller than this
+ * @param maxStep maximal step (sign is irrelevant, regardless of
+ * integration direction, forward or backward), the last step can
+ * be smaller than this
+ * @param scalAbsoluteTolerance allowed absolute error
+ * @param scalRelativeTolerance allowed relative error
+ * @exception NumberIsTooSmallException if order is 1 or less
+ */
+ public AdamsFieldIntegrator(final Field<T> field, final String name,
+ final int nSteps, final int order,
+ final double minStep, final double maxStep,
+ final double scalAbsoluteTolerance,
+ final double scalRelativeTolerance)
+ throws NumberIsTooSmallException {
+ super(field, name, nSteps, order, minStep, maxStep,
+ scalAbsoluteTolerance, scalRelativeTolerance);
+ transformer = AdamsNordsieckFieldTransformer.getInstance(field, nSteps);
+ }
+
+ /**
+ * Build an Adams integrator with the given order and step control parameters.
+ * @param field field to which the time and state vector elements belong
+ * @param name name of the method
+ * @param nSteps number of steps of the method excluding the one being computed
+ * @param order order of the method
+ * @param minStep minimal step (sign is irrelevant, regardless of
+ * integration direction, forward or backward), the last step can
+ * be smaller than this
+ * @param maxStep maximal step (sign is irrelevant, regardless of
+ * integration direction, forward or backward), the last step can
+ * be smaller than this
+ * @param vecAbsoluteTolerance allowed absolute error
+ * @param vecRelativeTolerance allowed relative error
+ * @exception IllegalArgumentException if order is 1 or less
+ */
+ public AdamsFieldIntegrator(final Field<T> field, final String name,
+ final int nSteps, final int order,
+ final double minStep, final double maxStep,
+ final double[] vecAbsoluteTolerance,
+ final double[] vecRelativeTolerance)
+ throws IllegalArgumentException {
+ super(field, name, nSteps, order, minStep, maxStep,
+ vecAbsoluteTolerance, vecRelativeTolerance);
+ transformer = AdamsNordsieckFieldTransformer.getInstance(field, nSteps);
+ }
+
+ /** {@inheritDoc} */
+ public abstract FieldODEStateAndDerivative<T> integrate(final FieldExpandableODE<T> equations,
+ final FieldODEState<T> initialState,
+ final T finalTime)
+ throws NumberIsTooSmallException, DimensionMismatchException,
+ MaxCountExceededException, NoBracketingException;
+
+ /** {@inheritDoc} */
+ @Override
+ protected Array2DRowFieldMatrix<T> initializeHighOrderDerivatives(final T h, final T[] t,
+ final T[][] y,
+ final T[][] yDot) {
+ return transformer.initializeHighOrderDerivatives(h, t, y, yDot);
+ }
+
+ /** Update the high order scaled derivatives for Adams integrators (phase 1).
+ * <p>The complete update of high order derivatives has a form similar to:
+ * <pre>
+ * r<sub>n+1</sub> = (s<sub>1</sub>(n) - s<sub>1</sub>(n+1)) P<sup>-1</sup> u + P<sup>-1</sup> A P r<sub>n</sub>
+ * </pre>
+ * this method computes the P<sup>-1</sup> A P r<sub>n</sub> part.</p>
+ * @param highOrder high order scaled derivatives
+ * (h<sup>2</sup>/2 y'', ... h<sup>k</sup>/k! y(k))
+ * @return updated high order derivatives
+ * @see #updateHighOrderDerivativesPhase2(RealFieldElement[], RealFieldElement[], Array2DRowFieldMatrix)
+ */
+ public Array2DRowFieldMatrix<T> updateHighOrderDerivativesPhase1(final Array2DRowFieldMatrix<T> highOrder) {
+ return transformer.updateHighOrderDerivativesPhase1(highOrder);
+ }
+
+ /** Update the high order scaled derivatives Adams integrators (phase 2).
+ * <p>The complete update of high order derivatives has a form similar to:
+ * <pre>
+ * r<sub>n+1</sub> = (s<sub>1</sub>(n) - s<sub>1</sub>(n+1)) P<sup>-1</sup> u + P<sup>-1</sup> A P r<sub>n</sub>
+ * </pre>
+ * this method computes the (s<sub>1</sub>(n) - s<sub>1</sub>(n+1)) P<sup>-1</sup> u part.</p>
+ * <p>Phase 1 of the update must already have been performed.</p>
+ * @param start first order scaled derivatives at step start
+ * @param end first order scaled derivatives at step end
+ * @param highOrder high order scaled derivatives, will be modified
+ * (h<sup>2</sup>/2 y'', ... h<sup>k</sup>/k! y(k))
+ * @see #updateHighOrderDerivativesPhase1(Array2DRowFieldMatrix)
+ */
+ public void updateHighOrderDerivativesPhase2(final T[] start, final T[] end,
+ final Array2DRowFieldMatrix<T> highOrder) {
+ transformer.updateHighOrderDerivativesPhase2(start, end, highOrder);
+ }
+
+}
diff --git a/src/main/java/org/apache/commons/math3/ode/nonstiff/AdamsFieldStepInterpolator.java b/src/main/java/org/apache/commons/math3/ode/nonstiff/AdamsFieldStepInterpolator.java
new file mode 100644
index 0000000..5de61cc
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/ode/nonstiff/AdamsFieldStepInterpolator.java
@@ -0,0 +1,189 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math3.ode.nonstiff;
+
+import java.util.Arrays;
+
+import org.apache.commons.math3.RealFieldElement;
+import org.apache.commons.math3.linear.Array2DRowFieldMatrix;
+import org.apache.commons.math3.ode.FieldEquationsMapper;
+import org.apache.commons.math3.ode.FieldODEStateAndDerivative;
+import org.apache.commons.math3.ode.sampling.AbstractFieldStepInterpolator;
+import org.apache.commons.math3.util.MathArrays;
+
+/**
+ * This class implements an interpolator for Adams integrators using Nordsieck representation.
+ *
+ * <p>This interpolator computes dense output around the current point.
+ * The interpolation equation is based on Taylor series formulas.
+ *
+ * @see AdamsBashforthFieldIntegrator
+ * @see AdamsMoultonFieldIntegrator
+ * @param <T> the type of the field elements
+ * @since 3.6
+ */
+
+class AdamsFieldStepInterpolator<T extends RealFieldElement<T>> extends AbstractFieldStepInterpolator<T> {
+
+ /** Step size used in the first scaled derivative and Nordsieck vector. */
+ private T scalingH;
+
+ /** Reference state.
+ * <p>Sometimes, the reference state is the same as globalPreviousState,
+ * sometimes it is the same as globalCurrentState, so we use a separate
+ * field to avoid any confusion.
+ * </p>
+ */
+ private final FieldODEStateAndDerivative<T> reference;
+
+ /** First scaled derivative. */
+ private final T[] scaled;
+
+ /** Nordsieck vector. */
+ private final Array2DRowFieldMatrix<T> nordsieck;
+
+ /** Simple constructor.
+ * @param stepSize step size used in the scaled and Nordsieck arrays
+ * @param reference reference state from which Taylor expansion are estimated
+ * @param scaled first scaled derivative
+ * @param nordsieck Nordsieck vector
+ * @param isForward integration direction indicator
+ * @param globalPreviousState start of the global step
+ * @param globalCurrentState end of the global step
+ * @param equationsMapper mapper for ODE equations primary and secondary components
+ */
+ AdamsFieldStepInterpolator(final T stepSize, final FieldODEStateAndDerivative<T> reference,
+ final T[] scaled, final Array2DRowFieldMatrix<T> nordsieck,
+ final boolean isForward,
+ final FieldODEStateAndDerivative<T> globalPreviousState,
+ final FieldODEStateAndDerivative<T> globalCurrentState,
+ final FieldEquationsMapper<T> equationsMapper) {
+ this(stepSize, reference, scaled, nordsieck,
+ isForward, globalPreviousState, globalCurrentState,
+ globalPreviousState, globalCurrentState, equationsMapper);
+ }
+
+ /** Simple constructor.
+ * @param stepSize step size used in the scaled and Nordsieck arrays
+ * @param reference reference state from which Taylor expansion are estimated
+ * @param scaled first scaled derivative
+ * @param nordsieck Nordsieck vector
+ * @param isForward integration direction indicator
+ * @param globalPreviousState start of the global step
+ * @param globalCurrentState end of the global step
+ * @param softPreviousState start of the restricted step
+ * @param softCurrentState end of the restricted step
+ * @param equationsMapper mapper for ODE equations primary and secondary components
+ */
+ private AdamsFieldStepInterpolator(final T stepSize, final FieldODEStateAndDerivative<T> reference,
+ final T[] scaled, final Array2DRowFieldMatrix<T> nordsieck,
+ final boolean isForward,
+ final FieldODEStateAndDerivative<T> globalPreviousState,
+ final FieldODEStateAndDerivative<T> globalCurrentState,
+ final FieldODEStateAndDerivative<T> softPreviousState,
+ final FieldODEStateAndDerivative<T> softCurrentState,
+ final FieldEquationsMapper<T> equationsMapper) {
+ super(isForward, globalPreviousState, globalCurrentState,
+ softPreviousState, softCurrentState, equationsMapper);
+ this.scalingH = stepSize;
+ this.reference = reference;
+ this.scaled = scaled.clone();
+ this.nordsieck = new Array2DRowFieldMatrix<T>(nordsieck.getData(), false);
+ }
+
+ /** Create a new instance.
+ * @param newForward integration direction indicator
+ * @param newGlobalPreviousState start of the global step
+ * @param newGlobalCurrentState end of the global step
+ * @param newSoftPreviousState start of the restricted step
+ * @param newSoftCurrentState end of the restricted step
+ * @param newMapper equations mapper for the all equations
+ * @return a new instance
+ */
+ @Override
+ protected AdamsFieldStepInterpolator<T> create(boolean newForward,
+ FieldODEStateAndDerivative<T> newGlobalPreviousState,
+ FieldODEStateAndDerivative<T> newGlobalCurrentState,
+ FieldODEStateAndDerivative<T> newSoftPreviousState,
+ FieldODEStateAndDerivative<T> newSoftCurrentState,
+ FieldEquationsMapper<T> newMapper) {
+ return new AdamsFieldStepInterpolator<T>(scalingH, reference, scaled, nordsieck,
+ newForward,
+ newGlobalPreviousState, newGlobalCurrentState,
+ newSoftPreviousState, newSoftCurrentState,
+ newMapper);
+
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ protected FieldODEStateAndDerivative<T> computeInterpolatedStateAndDerivatives(final FieldEquationsMapper<T> equationsMapper,
+ final T time, final T theta,
+ final T thetaH, final T oneMinusThetaH) {
+ return taylor(reference, time, scalingH, scaled, nordsieck);
+ }
+
+ /** Estimate state by applying Taylor formula.
+ * @param reference reference state
+ * @param time time at which state must be estimated
+ * @param stepSize step size used in the scaled and Nordsieck arrays
+ * @param scaled first scaled derivative
+ * @param nordsieck Nordsieck vector
+ * @return estimated state
+ * @param <S> the type of the field elements
+ */
+ public static <S extends RealFieldElement<S>> FieldODEStateAndDerivative<S> taylor(final FieldODEStateAndDerivative<S> reference,
+ final S time, final S stepSize,
+ final S[] scaled,
+ final Array2DRowFieldMatrix<S> nordsieck) {
+
+ final S x = time.subtract(reference.getTime());
+ final S normalizedAbscissa = x.divide(stepSize);
+
+ S[] stateVariation = MathArrays.buildArray(time.getField(), scaled.length);
+ Arrays.fill(stateVariation, time.getField().getZero());
+ S[] estimatedDerivatives = MathArrays.buildArray(time.getField(), scaled.length);
+ Arrays.fill(estimatedDerivatives, time.getField().getZero());
+
+ // apply Taylor formula from high order to low order,
+ // for the sake of numerical accuracy
+ final S[][] nData = nordsieck.getDataRef();
+ for (int i = nData.length - 1; i >= 0; --i) {
+ final int order = i + 2;
+ final S[] nDataI = nData[i];
+ final S power = normalizedAbscissa.pow(order);
+ for (int j = 0; j < nDataI.length; ++j) {
+ final S d = nDataI[j].multiply(power);
+ stateVariation[j] = stateVariation[j].add(d);
+ estimatedDerivatives[j] = estimatedDerivatives[j].add(d.multiply(order));
+ }
+ }
+
+ S[] estimatedState = reference.getState();
+ for (int j = 0; j < stateVariation.length; ++j) {
+ stateVariation[j] = stateVariation[j].add(scaled[j].multiply(normalizedAbscissa));
+ estimatedState[j] = estimatedState[j].add(stateVariation[j]);
+ estimatedDerivatives[j] =
+ estimatedDerivatives[j].add(scaled[j].multiply(normalizedAbscissa)).divide(x);
+ }
+
+ return new FieldODEStateAndDerivative<S>(time, estimatedState, estimatedDerivatives);
+
+ }
+
+}
diff --git a/src/main/java/org/apache/commons/math3/ode/nonstiff/AdamsIntegrator.java b/src/main/java/org/apache/commons/math3/ode/nonstiff/AdamsIntegrator.java
new file mode 100644
index 0000000..dd6f2a1
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/ode/nonstiff/AdamsIntegrator.java
@@ -0,0 +1,136 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math3.ode.nonstiff;
+
+import org.apache.commons.math3.exception.DimensionMismatchException;
+import org.apache.commons.math3.exception.MaxCountExceededException;
+import org.apache.commons.math3.exception.NoBracketingException;
+import org.apache.commons.math3.exception.NumberIsTooSmallException;
+import org.apache.commons.math3.linear.Array2DRowRealMatrix;
+import org.apache.commons.math3.ode.ExpandableStatefulODE;
+import org.apache.commons.math3.ode.MultistepIntegrator;
+
+
+/** Base class for {@link AdamsBashforthIntegrator Adams-Bashforth} and
+ * {@link AdamsMoultonIntegrator Adams-Moulton} integrators.
+ * @since 2.0
+ */
+public abstract class AdamsIntegrator extends MultistepIntegrator {
+
+ /** Transformer. */
+ private final AdamsNordsieckTransformer transformer;
+
+ /**
+ * Build an Adams integrator with the given order and step control parameters.
+ * @param name name of the method
+ * @param nSteps number of steps of the method excluding the one being computed
+ * @param order order of the method
+ * @param minStep minimal step (sign is irrelevant, regardless of
+ * integration direction, forward or backward), the last step can
+ * be smaller than this
+ * @param maxStep maximal step (sign is irrelevant, regardless of
+ * integration direction, forward or backward), the last step can
+ * be smaller than this
+ * @param scalAbsoluteTolerance allowed absolute error
+ * @param scalRelativeTolerance allowed relative error
+ * @exception NumberIsTooSmallException if order is 1 or less
+ */
+ public AdamsIntegrator(final String name, final int nSteps, final int order,
+ final double minStep, final double maxStep,
+ final double scalAbsoluteTolerance,
+ final double scalRelativeTolerance)
+ throws NumberIsTooSmallException {
+ super(name, nSteps, order, minStep, maxStep,
+ scalAbsoluteTolerance, scalRelativeTolerance);
+ transformer = AdamsNordsieckTransformer.getInstance(nSteps);
+ }
+
+ /**
+ * Build an Adams integrator with the given order and step control parameters.
+ * @param name name of the method
+ * @param nSteps number of steps of the method excluding the one being computed
+ * @param order order of the method
+ * @param minStep minimal step (sign is irrelevant, regardless of
+ * integration direction, forward or backward), the last step can
+ * be smaller than this
+ * @param maxStep maximal step (sign is irrelevant, regardless of
+ * integration direction, forward or backward), the last step can
+ * be smaller than this
+ * @param vecAbsoluteTolerance allowed absolute error
+ * @param vecRelativeTolerance allowed relative error
+ * @exception IllegalArgumentException if order is 1 or less
+ */
+ public AdamsIntegrator(final String name, final int nSteps, final int order,
+ final double minStep, final double maxStep,
+ final double[] vecAbsoluteTolerance,
+ final double[] vecRelativeTolerance)
+ throws IllegalArgumentException {
+ super(name, nSteps, order, minStep, maxStep,
+ vecAbsoluteTolerance, vecRelativeTolerance);
+ transformer = AdamsNordsieckTransformer.getInstance(nSteps);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public abstract void integrate(final ExpandableStatefulODE equations, final double t)
+ throws NumberIsTooSmallException, DimensionMismatchException,
+ MaxCountExceededException, NoBracketingException;
+
+ /** {@inheritDoc} */
+ @Override
+ protected Array2DRowRealMatrix initializeHighOrderDerivatives(final double h, final double[] t,
+ final double[][] y,
+ final double[][] yDot) {
+ return transformer.initializeHighOrderDerivatives(h, t, y, yDot);
+ }
+
+ /** Update the high order scaled derivatives for Adams integrators (phase 1).
+ * <p>The complete update of high order derivatives has a form similar to:
+ * <pre>
+ * r<sub>n+1</sub> = (s<sub>1</sub>(n) - s<sub>1</sub>(n+1)) P<sup>-1</sup> u + P<sup>-1</sup> A P r<sub>n</sub>
+ * </pre>
+ * this method computes the P<sup>-1</sup> A P r<sub>n</sub> part.</p>
+ * @param highOrder high order scaled derivatives
+ * (h<sup>2</sup>/2 y'', ... h<sup>k</sup>/k! y(k))
+ * @return updated high order derivatives
+ * @see #updateHighOrderDerivativesPhase2(double[], double[], Array2DRowRealMatrix)
+ */
+ public Array2DRowRealMatrix updateHighOrderDerivativesPhase1(final Array2DRowRealMatrix highOrder) {
+ return transformer.updateHighOrderDerivativesPhase1(highOrder);
+ }
+
+ /** Update the high order scaled derivatives Adams integrators (phase 2).
+ * <p>The complete update of high order derivatives has a form similar to:
+ * <pre>
+ * r<sub>n+1</sub> = (s<sub>1</sub>(n) - s<sub>1</sub>(n+1)) P<sup>-1</sup> u + P<sup>-1</sup> A P r<sub>n</sub>
+ * </pre>
+ * this method computes the (s<sub>1</sub>(n) - s<sub>1</sub>(n+1)) P<sup>-1</sup> u part.</p>
+ * <p>Phase 1 of the update must already have been performed.</p>
+ * @param start first order scaled derivatives at step start
+ * @param end first order scaled derivatives at step end
+ * @param highOrder high order scaled derivatives, will be modified
+ * (h<sup>2</sup>/2 y'', ... h<sup>k</sup>/k! y(k))
+ * @see #updateHighOrderDerivativesPhase1(Array2DRowRealMatrix)
+ */
+ public void updateHighOrderDerivativesPhase2(final double[] start,
+ final double[] end,
+ final Array2DRowRealMatrix highOrder) {
+ transformer.updateHighOrderDerivativesPhase2(start, end, highOrder);
+ }
+
+}
diff --git a/src/main/java/org/apache/commons/math3/ode/nonstiff/AdamsMoultonFieldIntegrator.java b/src/main/java/org/apache/commons/math3/ode/nonstiff/AdamsMoultonFieldIntegrator.java
new file mode 100644
index 0000000..2594321
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/ode/nonstiff/AdamsMoultonFieldIntegrator.java
@@ -0,0 +1,416 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math3.ode.nonstiff;
+
+import java.util.Arrays;
+
+import org.apache.commons.math3.Field;
+import org.apache.commons.math3.RealFieldElement;
+import org.apache.commons.math3.exception.DimensionMismatchException;
+import org.apache.commons.math3.exception.MaxCountExceededException;
+import org.apache.commons.math3.exception.NoBracketingException;
+import org.apache.commons.math3.exception.NumberIsTooSmallException;
+import org.apache.commons.math3.linear.Array2DRowFieldMatrix;
+import org.apache.commons.math3.linear.FieldMatrixPreservingVisitor;
+import org.apache.commons.math3.ode.FieldExpandableODE;
+import org.apache.commons.math3.ode.FieldODEState;
+import org.apache.commons.math3.ode.FieldODEStateAndDerivative;
+import org.apache.commons.math3.util.MathArrays;
+import org.apache.commons.math3.util.MathUtils;
+
+
+/**
+ * This class implements implicit Adams-Moulton integrators for Ordinary
+ * Differential Equations.
+ *
+ * <p>Adams-Moulton methods (in fact due to Adams alone) are implicit
+ * multistep ODE solvers. This implementation is a variation of the classical
+ * one: it uses adaptive stepsize to implement error control, whereas
+ * classical implementations are fixed step size. The value of state vector
+ * at step n+1 is a simple combination of the value at step n and of the
+ * derivatives at steps n+1, n, n-1 ... Since y'<sub>n+1</sub> is needed to
+ * compute y<sub>n+1</sub>, another method must be used to compute a first
+ * estimate of y<sub>n+1</sub>, then compute y'<sub>n+1</sub>, then compute
+ * a final estimate of y<sub>n+1</sub> using the following formulas. Depending
+ * on the number k of previous steps one wants to use for computing the next
+ * value, different formulas are available for the final estimate:</p>
+ * <ul>
+ * <li>k = 1: y<sub>n+1</sub> = y<sub>n</sub> + h y'<sub>n+1</sub></li>
+ * <li>k = 2: y<sub>n+1</sub> = y<sub>n</sub> + h (y'<sub>n+1</sub>+y'<sub>n</sub>)/2</li>
+ * <li>k = 3: y<sub>n+1</sub> = y<sub>n</sub> + h (5y'<sub>n+1</sub>+8y'<sub>n</sub>-y'<sub>n-1</sub>)/12</li>
+ * <li>k = 4: y<sub>n+1</sub> = y<sub>n</sub> + h (9y'<sub>n+1</sub>+19y'<sub>n</sub>-5y'<sub>n-1</sub>+y'<sub>n-2</sub>)/24</li>
+ * <li>...</li>
+ * </ul>
+ *
+ * <p>A k-steps Adams-Moulton method is of order k+1.</p>
+ *
+ * <h3>Implementation details</h3>
+ *
+ * <p>We define scaled derivatives s<sub>i</sub>(n) at step n as:
+ * <pre>
+ * s<sub>1</sub>(n) = h y'<sub>n</sub> for first derivative
+ * s<sub>2</sub>(n) = h<sup>2</sup>/2 y''<sub>n</sub> for second derivative
+ * s<sub>3</sub>(n) = h<sup>3</sup>/6 y'''<sub>n</sub> for third derivative
+ * ...
+ * s<sub>k</sub>(n) = h<sup>k</sup>/k! y<sup>(k)</sup><sub>n</sub> for k<sup>th</sup> derivative
+ * </pre></p>
+ *
+ * <p>The definitions above use the classical representation with several previous first
+ * derivatives. Lets define
+ * <pre>
+ * q<sub>n</sub> = [ s<sub>1</sub>(n-1) s<sub>1</sub>(n-2) ... s<sub>1</sub>(n-(k-1)) ]<sup>T</sup>
+ * </pre>
+ * (we omit the k index in the notation for clarity). With these definitions,
+ * Adams-Moulton methods can be written:
+ * <ul>
+ * <li>k = 1: y<sub>n+1</sub> = y<sub>n</sub> + s<sub>1</sub>(n+1)</li>
+ * <li>k = 2: y<sub>n+1</sub> = y<sub>n</sub> + 1/2 s<sub>1</sub>(n+1) + [ 1/2 ] q<sub>n+1</sub></li>
+ * <li>k = 3: y<sub>n+1</sub> = y<sub>n</sub> + 5/12 s<sub>1</sub>(n+1) + [ 8/12 -1/12 ] q<sub>n+1</sub></li>
+ * <li>k = 4: y<sub>n+1</sub> = y<sub>n</sub> + 9/24 s<sub>1</sub>(n+1) + [ 19/24 -5/24 1/24 ] q<sub>n+1</sub></li>
+ * <li>...</li>
+ * </ul></p>
+ *
+ * <p>Instead of using the classical representation with first derivatives only (y<sub>n</sub>,
+ * s<sub>1</sub>(n+1) and q<sub>n+1</sub>), our implementation uses the Nordsieck vector with
+ * higher degrees scaled derivatives all taken at the same step (y<sub>n</sub>, s<sub>1</sub>(n)
+ * and r<sub>n</sub>) where r<sub>n</sub> is defined as:
+ * <pre>
+ * r<sub>n</sub> = [ s<sub>2</sub>(n), s<sub>3</sub>(n) ... s<sub>k</sub>(n) ]<sup>T</sup>
+ * </pre>
+ * (here again we omit the k index in the notation for clarity)
+ * </p>
+ *
+ * <p>Taylor series formulas show that for any index offset i, s<sub>1</sub>(n-i) can be
+ * computed from s<sub>1</sub>(n), s<sub>2</sub>(n) ... s<sub>k</sub>(n), the formula being exact
+ * for degree k polynomials.
+ * <pre>
+ * s<sub>1</sub>(n-i) = s<sub>1</sub>(n) + &sum;<sub>j&gt;0</sub> (j+1) (-i)<sup>j</sup> s<sub>j+1</sub>(n)
+ * </pre>
+ * The previous formula can be used with several values for i to compute the transform between
+ * classical representation and Nordsieck vector. The transform between r<sub>n</sub>
+ * and q<sub>n</sub> resulting from the Taylor series formulas above is:
+ * <pre>
+ * q<sub>n</sub> = s<sub>1</sub>(n) u + P r<sub>n</sub>
+ * </pre>
+ * where u is the [ 1 1 ... 1 ]<sup>T</sup> vector and P is the (k-1)&times;(k-1) matrix built
+ * with the (j+1) (-i)<sup>j</sup> terms with i being the row number starting from 1 and j being
+ * the column number starting from 1:
+ * <pre>
+ * [ -2 3 -4 5 ... ]
+ * [ -4 12 -32 80 ... ]
+ * P = [ -6 27 -108 405 ... ]
+ * [ -8 48 -256 1280 ... ]
+ * [ ... ]
+ * </pre></p>
+ *
+ * <p>Using the Nordsieck vector has several advantages:
+ * <ul>
+ * <li>it greatly simplifies step interpolation as the interpolator mainly applies
+ * Taylor series formulas,</li>
+ * <li>it simplifies step changes that occur when discrete events that truncate
+ * the step are triggered,</li>
+ * <li>it allows to extend the methods in order to support adaptive stepsize.</li>
+ * </ul></p>
+ *
+ * <p>The predicted Nordsieck vector at step n+1 is computed from the Nordsieck vector at step
+ * n as follows:
+ * <ul>
+ * <li>Y<sub>n+1</sub> = y<sub>n</sub> + s<sub>1</sub>(n) + u<sup>T</sup> r<sub>n</sub></li>
+ * <li>S<sub>1</sub>(n+1) = h f(t<sub>n+1</sub>, Y<sub>n+1</sub>)</li>
+ * <li>R<sub>n+1</sub> = (s<sub>1</sub>(n) - S<sub>1</sub>(n+1)) P<sup>-1</sup> u + P<sup>-1</sup> A P r<sub>n</sub></li>
+ * </ul>
+ * where A is a rows shifting matrix (the lower left part is an identity matrix):
+ * <pre>
+ * [ 0 0 ... 0 0 | 0 ]
+ * [ ---------------+---]
+ * [ 1 0 ... 0 0 | 0 ]
+ * A = [ 0 1 ... 0 0 | 0 ]
+ * [ ... | 0 ]
+ * [ 0 0 ... 1 0 | 0 ]
+ * [ 0 0 ... 0 1 | 0 ]
+ * </pre>
+ * From this predicted vector, the corrected vector is computed as follows:
+ * <ul>
+ * <li>y<sub>n+1</sub> = y<sub>n</sub> + S<sub>1</sub>(n+1) + [ -1 +1 -1 +1 ... &plusmn;1 ] r<sub>n+1</sub></li>
+ * <li>s<sub>1</sub>(n+1) = h f(t<sub>n+1</sub>, y<sub>n+1</sub>)</li>
+ * <li>r<sub>n+1</sub> = R<sub>n+1</sub> + (s<sub>1</sub>(n+1) - S<sub>1</sub>(n+1)) P<sup>-1</sup> u</li>
+ * </ul>
+ * where the upper case Y<sub>n+1</sub>, S<sub>1</sub>(n+1) and R<sub>n+1</sub> represent the
+ * predicted states whereas the lower case y<sub>n+1</sub>, s<sub>n+1</sub> and r<sub>n+1</sub>
+ * represent the corrected states.</p>
+ *
+ * <p>The P<sup>-1</sup>u vector and the P<sup>-1</sup> A P matrix do not depend on the state,
+ * they only depend on k and therefore are precomputed once for all.</p>
+ *
+ * @param <T> the type of the field elements
+ * @since 3.6
+ */
+public class AdamsMoultonFieldIntegrator<T extends RealFieldElement<T>> extends AdamsFieldIntegrator<T> {
+
+ /** Integrator method name. */
+ private static final String METHOD_NAME = "Adams-Moulton";
+
+ /**
+ * Build an Adams-Moulton integrator with the given order and error control parameters.
+ * @param field field to which the time and state vector elements belong
+ * @param nSteps number of steps of the method excluding the one being computed
+ * @param minStep minimal step (sign is irrelevant, regardless of
+ * integration direction, forward or backward), the last step can
+ * be smaller than this
+ * @param maxStep maximal step (sign is irrelevant, regardless of
+ * integration direction, forward or backward), the last step can
+ * be smaller than this
+ * @param scalAbsoluteTolerance allowed absolute error
+ * @param scalRelativeTolerance allowed relative error
+ * @exception NumberIsTooSmallException if order is 1 or less
+ */
+ public AdamsMoultonFieldIntegrator(final Field<T> field, final int nSteps,
+ final double minStep, final double maxStep,
+ final double scalAbsoluteTolerance,
+ final double scalRelativeTolerance)
+ throws NumberIsTooSmallException {
+ super(field, METHOD_NAME, nSteps, nSteps + 1, minStep, maxStep,
+ scalAbsoluteTolerance, scalRelativeTolerance);
+ }
+
+ /**
+ * Build an Adams-Moulton integrator with the given order and error control parameters.
+ * @param field field to which the time and state vector elements belong
+ * @param nSteps number of steps of the method excluding the one being computed
+ * @param minStep minimal step (sign is irrelevant, regardless of
+ * integration direction, forward or backward), the last step can
+ * be smaller than this
+ * @param maxStep maximal step (sign is irrelevant, regardless of
+ * integration direction, forward or backward), the last step can
+ * be smaller than this
+ * @param vecAbsoluteTolerance allowed absolute error
+ * @param vecRelativeTolerance allowed relative error
+ * @exception IllegalArgumentException if order is 1 or less
+ */
+ public AdamsMoultonFieldIntegrator(final Field<T> field, final int nSteps,
+ final double minStep, final double maxStep,
+ final double[] vecAbsoluteTolerance,
+ final double[] vecRelativeTolerance)
+ throws IllegalArgumentException {
+ super(field, METHOD_NAME, nSteps, nSteps + 1, minStep, maxStep,
+ vecAbsoluteTolerance, vecRelativeTolerance);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public FieldODEStateAndDerivative<T> integrate(final FieldExpandableODE<T> equations,
+ final FieldODEState<T> initialState,
+ final T finalTime)
+ throws NumberIsTooSmallException, DimensionMismatchException,
+ MaxCountExceededException, NoBracketingException {
+
+ sanityChecks(initialState, finalTime);
+ final T t0 = initialState.getTime();
+ final T[] y = equations.getMapper().mapState(initialState);
+ setStepStart(initIntegration(equations, t0, y, finalTime));
+ final boolean forward = finalTime.subtract(initialState.getTime()).getReal() > 0;
+
+ // compute the initial Nordsieck vector using the configured starter integrator
+ start(equations, getStepStart(), finalTime);
+
+ // reuse the step that was chosen by the starter integrator
+ FieldODEStateAndDerivative<T> stepStart = getStepStart();
+ FieldODEStateAndDerivative<T> stepEnd =
+ AdamsFieldStepInterpolator.taylor(stepStart,
+ stepStart.getTime().add(getStepSize()),
+ getStepSize(), scaled, nordsieck);
+
+ // main integration loop
+ setIsLastStep(false);
+ do {
+
+ T[] predictedY = null;
+ final T[] predictedScaled = MathArrays.buildArray(getField(), y.length);
+ Array2DRowFieldMatrix<T> predictedNordsieck = null;
+ T error = getField().getZero().add(10);
+ while (error.subtract(1.0).getReal() >= 0.0) {
+
+ // predict a first estimate of the state at step end (P in the PECE sequence)
+ predictedY = stepEnd.getState();
+
+ // evaluate a first estimate of the derivative (first E in the PECE sequence)
+ final T[] yDot = computeDerivatives(stepEnd.getTime(), predictedY);
+
+ // update Nordsieck vector
+ for (int j = 0; j < predictedScaled.length; ++j) {
+ predictedScaled[j] = getStepSize().multiply(yDot[j]);
+ }
+ predictedNordsieck = updateHighOrderDerivativesPhase1(nordsieck);
+ updateHighOrderDerivativesPhase2(scaled, predictedScaled, predictedNordsieck);
+
+ // apply correction (C in the PECE sequence)
+ error = predictedNordsieck.walkInOptimizedOrder(new Corrector(y, predictedScaled, predictedY));
+
+ if (error.subtract(1.0).getReal() >= 0.0) {
+ // reject the step and attempt to reduce error by stepsize control
+ final T factor = computeStepGrowShrinkFactor(error);
+ rescale(filterStep(getStepSize().multiply(factor), forward, false));
+ stepEnd = AdamsFieldStepInterpolator.taylor(getStepStart(),
+ getStepStart().getTime().add(getStepSize()),
+ getStepSize(),
+ scaled,
+ nordsieck);
+ }
+ }
+
+ // evaluate a final estimate of the derivative (second E in the PECE sequence)
+ final T[] correctedYDot = computeDerivatives(stepEnd.getTime(), predictedY);
+
+ // update Nordsieck vector
+ final T[] correctedScaled = MathArrays.buildArray(getField(), y.length);
+ for (int j = 0; j < correctedScaled.length; ++j) {
+ correctedScaled[j] = getStepSize().multiply(correctedYDot[j]);
+ }
+ updateHighOrderDerivativesPhase2(predictedScaled, correctedScaled, predictedNordsieck);
+
+ // discrete events handling
+ stepEnd = new FieldODEStateAndDerivative<T>(stepEnd.getTime(), predictedY, correctedYDot);
+ setStepStart(acceptStep(new AdamsFieldStepInterpolator<T>(getStepSize(), stepEnd,
+ correctedScaled, predictedNordsieck, forward,
+ getStepStart(), stepEnd,
+ equations.getMapper()),
+ finalTime));
+ scaled = correctedScaled;
+ nordsieck = predictedNordsieck;
+
+ if (!isLastStep()) {
+
+ System.arraycopy(predictedY, 0, y, 0, y.length);
+
+ if (resetOccurred()) {
+ // some events handler has triggered changes that
+ // invalidate the derivatives, we need to restart from scratch
+ start(equations, getStepStart(), finalTime);
+ }
+
+ // stepsize control for next step
+ final T factor = computeStepGrowShrinkFactor(error);
+ final T scaledH = getStepSize().multiply(factor);
+ final T nextT = getStepStart().getTime().add(scaledH);
+ final boolean nextIsLast = forward ?
+ nextT.subtract(finalTime).getReal() >= 0 :
+ nextT.subtract(finalTime).getReal() <= 0;
+ T hNew = filterStep(scaledH, forward, nextIsLast);
+
+ final T filteredNextT = getStepStart().getTime().add(hNew);
+ final boolean filteredNextIsLast = forward ?
+ filteredNextT.subtract(finalTime).getReal() >= 0 :
+ filteredNextT.subtract(finalTime).getReal() <= 0;
+ if (filteredNextIsLast) {
+ hNew = finalTime.subtract(getStepStart().getTime());
+ }
+
+ rescale(hNew);
+ stepEnd = AdamsFieldStepInterpolator.taylor(getStepStart(), getStepStart().getTime().add(getStepSize()),
+ getStepSize(), scaled, nordsieck);
+
+ }
+
+ } while (!isLastStep());
+
+ final FieldODEStateAndDerivative<T> finalState = getStepStart();
+ setStepStart(null);
+ setStepSize(null);
+ return finalState;
+
+ }
+
+ /** Corrector for current state in Adams-Moulton method.
+ * <p>
+ * This visitor implements the Taylor series formula:
+ * <pre>
+ * Y<sub>n+1</sub> = y<sub>n</sub> + s<sub>1</sub>(n+1) + [ -1 +1 -1 +1 ... &plusmn;1 ] r<sub>n+1</sub>
+ * </pre>
+ * </p>
+ */
+ private class Corrector implements FieldMatrixPreservingVisitor<T> {
+
+ /** Previous state. */
+ private final T[] previous;
+
+ /** Current scaled first derivative. */
+ private final T[] scaled;
+
+ /** Current state before correction. */
+ private final T[] before;
+
+ /** Current state after correction. */
+ private final T[] after;
+
+ /** Simple constructor.
+ * @param previous previous state
+ * @param scaled current scaled first derivative
+ * @param state state to correct (will be overwritten after visit)
+ */
+ Corrector(final T[] previous, final T[] scaled, final T[] state) {
+ this.previous = previous;
+ this.scaled = scaled;
+ this.after = state;
+ this.before = state.clone();
+ }
+
+ /** {@inheritDoc} */
+ public void start(int rows, int columns,
+ int startRow, int endRow, int startColumn, int endColumn) {
+ Arrays.fill(after, getField().getZero());
+ }
+
+ /** {@inheritDoc} */
+ public void visit(int row, int column, T value) {
+ if ((row & 0x1) == 0) {
+ after[column] = after[column].subtract(value);
+ } else {
+ after[column] = after[column].add(value);
+ }
+ }
+
+ /**
+ * End visiting the Nordsieck vector.
+ * <p>The correction is used to control stepsize. So its amplitude is
+ * considered to be an error, which must be normalized according to
+ * error control settings. If the normalized value is greater than 1,
+ * the correction was too large and the step must be rejected.</p>
+ * @return the normalized correction, if greater than 1, the step
+ * must be rejected
+ */
+ public T end() {
+
+ T error = getField().getZero();
+ for (int i = 0; i < after.length; ++i) {
+ after[i] = after[i].add(previous[i].add(scaled[i]));
+ if (i < mainSetDimension) {
+ final T yScale = MathUtils.max(previous[i].abs(), after[i].abs());
+ final T tol = (vecAbsoluteTolerance == null) ?
+ yScale.multiply(scalRelativeTolerance).add(scalAbsoluteTolerance) :
+ yScale.multiply(vecRelativeTolerance[i]).add(vecAbsoluteTolerance[i]);
+ final T ratio = after[i].subtract(before[i]).divide(tol); // (corrected-predicted)/tol
+ error = error.add(ratio.multiply(ratio));
+ }
+ }
+
+ return error.divide(mainSetDimension).sqrt();
+
+ }
+ }
+
+}
diff --git a/src/main/java/org/apache/commons/math3/ode/nonstiff/AdamsMoultonIntegrator.java b/src/main/java/org/apache/commons/math3/ode/nonstiff/AdamsMoultonIntegrator.java
new file mode 100644
index 0000000..69d2469
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/ode/nonstiff/AdamsMoultonIntegrator.java
@@ -0,0 +1,421 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math3.ode.nonstiff;
+
+import java.util.Arrays;
+
+import org.apache.commons.math3.exception.DimensionMismatchException;
+import org.apache.commons.math3.exception.MaxCountExceededException;
+import org.apache.commons.math3.exception.NoBracketingException;
+import org.apache.commons.math3.exception.NumberIsTooSmallException;
+import org.apache.commons.math3.linear.Array2DRowRealMatrix;
+import org.apache.commons.math3.linear.RealMatrixPreservingVisitor;
+import org.apache.commons.math3.ode.EquationsMapper;
+import org.apache.commons.math3.ode.ExpandableStatefulODE;
+import org.apache.commons.math3.ode.sampling.NordsieckStepInterpolator;
+import org.apache.commons.math3.util.FastMath;
+
+
+/**
+ * This class implements implicit Adams-Moulton integrators for Ordinary
+ * Differential Equations.
+ *
+ * <p>Adams-Moulton methods (in fact due to Adams alone) are implicit
+ * multistep ODE solvers. This implementation is a variation of the classical
+ * one: it uses adaptive stepsize to implement error control, whereas
+ * classical implementations are fixed step size. The value of state vector
+ * at step n+1 is a simple combination of the value at step n and of the
+ * derivatives at steps n+1, n, n-1 ... Since y'<sub>n+1</sub> is needed to
+ * compute y<sub>n+1</sub>, another method must be used to compute a first
+ * estimate of y<sub>n+1</sub>, then compute y'<sub>n+1</sub>, then compute
+ * a final estimate of y<sub>n+1</sub> using the following formulas. Depending
+ * on the number k of previous steps one wants to use for computing the next
+ * value, different formulas are available for the final estimate:</p>
+ * <ul>
+ * <li>k = 1: y<sub>n+1</sub> = y<sub>n</sub> + h y'<sub>n+1</sub></li>
+ * <li>k = 2: y<sub>n+1</sub> = y<sub>n</sub> + h (y'<sub>n+1</sub>+y'<sub>n</sub>)/2</li>
+ * <li>k = 3: y<sub>n+1</sub> = y<sub>n</sub> + h (5y'<sub>n+1</sub>+8y'<sub>n</sub>-y'<sub>n-1</sub>)/12</li>
+ * <li>k = 4: y<sub>n+1</sub> = y<sub>n</sub> + h (9y'<sub>n+1</sub>+19y'<sub>n</sub>-5y'<sub>n-1</sub>+y'<sub>n-2</sub>)/24</li>
+ * <li>...</li>
+ * </ul>
+ *
+ * <p>A k-steps Adams-Moulton method is of order k+1.</p>
+ *
+ * <h3>Implementation details</h3>
+ *
+ * <p>We define scaled derivatives s<sub>i</sub>(n) at step n as:
+ * <pre>
+ * s<sub>1</sub>(n) = h y'<sub>n</sub> for first derivative
+ * s<sub>2</sub>(n) = h<sup>2</sup>/2 y''<sub>n</sub> for second derivative
+ * s<sub>3</sub>(n) = h<sup>3</sup>/6 y'''<sub>n</sub> for third derivative
+ * ...
+ * s<sub>k</sub>(n) = h<sup>k</sup>/k! y<sup>(k)</sup><sub>n</sub> for k<sup>th</sup> derivative
+ * </pre></p>
+ *
+ * <p>The definitions above use the classical representation with several previous first
+ * derivatives. Lets define
+ * <pre>
+ * q<sub>n</sub> = [ s<sub>1</sub>(n-1) s<sub>1</sub>(n-2) ... s<sub>1</sub>(n-(k-1)) ]<sup>T</sup>
+ * </pre>
+ * (we omit the k index in the notation for clarity). With these definitions,
+ * Adams-Moulton methods can be written:
+ * <ul>
+ * <li>k = 1: y<sub>n+1</sub> = y<sub>n</sub> + s<sub>1</sub>(n+1)</li>
+ * <li>k = 2: y<sub>n+1</sub> = y<sub>n</sub> + 1/2 s<sub>1</sub>(n+1) + [ 1/2 ] q<sub>n+1</sub></li>
+ * <li>k = 3: y<sub>n+1</sub> = y<sub>n</sub> + 5/12 s<sub>1</sub>(n+1) + [ 8/12 -1/12 ] q<sub>n+1</sub></li>
+ * <li>k = 4: y<sub>n+1</sub> = y<sub>n</sub> + 9/24 s<sub>1</sub>(n+1) + [ 19/24 -5/24 1/24 ] q<sub>n+1</sub></li>
+ * <li>...</li>
+ * </ul></p>
+ *
+ * <p>Instead of using the classical representation with first derivatives only (y<sub>n</sub>,
+ * s<sub>1</sub>(n+1) and q<sub>n+1</sub>), our implementation uses the Nordsieck vector with
+ * higher degrees scaled derivatives all taken at the same step (y<sub>n</sub>, s<sub>1</sub>(n)
+ * and r<sub>n</sub>) where r<sub>n</sub> is defined as:
+ * <pre>
+ * r<sub>n</sub> = [ s<sub>2</sub>(n), s<sub>3</sub>(n) ... s<sub>k</sub>(n) ]<sup>T</sup>
+ * </pre>
+ * (here again we omit the k index in the notation for clarity)
+ * </p>
+ *
+ * <p>Taylor series formulas show that for any index offset i, s<sub>1</sub>(n-i) can be
+ * computed from s<sub>1</sub>(n), s<sub>2</sub>(n) ... s<sub>k</sub>(n), the formula being exact
+ * for degree k polynomials.
+ * <pre>
+ * s<sub>1</sub>(n-i) = s<sub>1</sub>(n) + &sum;<sub>j&gt;0</sub> (j+1) (-i)<sup>j</sup> s<sub>j+1</sub>(n)
+ * </pre>
+ * The previous formula can be used with several values for i to compute the transform between
+ * classical representation and Nordsieck vector. The transform between r<sub>n</sub>
+ * and q<sub>n</sub> resulting from the Taylor series formulas above is:
+ * <pre>
+ * q<sub>n</sub> = s<sub>1</sub>(n) u + P r<sub>n</sub>
+ * </pre>
+ * where u is the [ 1 1 ... 1 ]<sup>T</sup> vector and P is the (k-1)&times;(k-1) matrix built
+ * with the (j+1) (-i)<sup>j</sup> terms with i being the row number starting from 1 and j being
+ * the column number starting from 1:
+ * <pre>
+ * [ -2 3 -4 5 ... ]
+ * [ -4 12 -32 80 ... ]
+ * P = [ -6 27 -108 405 ... ]
+ * [ -8 48 -256 1280 ... ]
+ * [ ... ]
+ * </pre></p>
+ *
+ * <p>Using the Nordsieck vector has several advantages:
+ * <ul>
+ * <li>it greatly simplifies step interpolation as the interpolator mainly applies
+ * Taylor series formulas,</li>
+ * <li>it simplifies step changes that occur when discrete events that truncate
+ * the step are triggered,</li>
+ * <li>it allows to extend the methods in order to support adaptive stepsize.</li>
+ * </ul></p>
+ *
+ * <p>The predicted Nordsieck vector at step n+1 is computed from the Nordsieck vector at step
+ * n as follows:
+ * <ul>
+ * <li>Y<sub>n+1</sub> = y<sub>n</sub> + s<sub>1</sub>(n) + u<sup>T</sup> r<sub>n</sub></li>
+ * <li>S<sub>1</sub>(n+1) = h f(t<sub>n+1</sub>, Y<sub>n+1</sub>)</li>
+ * <li>R<sub>n+1</sub> = (s<sub>1</sub>(n) - S<sub>1</sub>(n+1)) P<sup>-1</sup> u + P<sup>-1</sup> A P r<sub>n</sub></li>
+ * </ul>
+ * where A is a rows shifting matrix (the lower left part is an identity matrix):
+ * <pre>
+ * [ 0 0 ... 0 0 | 0 ]
+ * [ ---------------+---]
+ * [ 1 0 ... 0 0 | 0 ]
+ * A = [ 0 1 ... 0 0 | 0 ]
+ * [ ... | 0 ]
+ * [ 0 0 ... 1 0 | 0 ]
+ * [ 0 0 ... 0 1 | 0 ]
+ * </pre>
+ * From this predicted vector, the corrected vector is computed as follows:
+ * <ul>
+ * <li>y<sub>n+1</sub> = y<sub>n</sub> + S<sub>1</sub>(n+1) + [ -1 +1 -1 +1 ... &plusmn;1 ] r<sub>n+1</sub></li>
+ * <li>s<sub>1</sub>(n+1) = h f(t<sub>n+1</sub>, y<sub>n+1</sub>)</li>
+ * <li>r<sub>n+1</sub> = R<sub>n+1</sub> + (s<sub>1</sub>(n+1) - S<sub>1</sub>(n+1)) P<sup>-1</sup> u</li>
+ * </ul>
+ * where the upper case Y<sub>n+1</sub>, S<sub>1</sub>(n+1) and R<sub>n+1</sub> represent the
+ * predicted states whereas the lower case y<sub>n+1</sub>, s<sub>n+1</sub> and r<sub>n+1</sub>
+ * represent the corrected states.</p>
+ *
+ * <p>The P<sup>-1</sup>u vector and the P<sup>-1</sup> A P matrix do not depend on the state,
+ * they only depend on k and therefore are precomputed once for all.</p>
+ *
+ * @since 2.0
+ */
+public class AdamsMoultonIntegrator extends AdamsIntegrator {
+
+ /** Integrator method name. */
+ private static final String METHOD_NAME = "Adams-Moulton";
+
+ /**
+ * Build an Adams-Moulton integrator with the given order and error control parameters.
+ * @param nSteps number of steps of the method excluding the one being computed
+ * @param minStep minimal step (sign is irrelevant, regardless of
+ * integration direction, forward or backward), the last step can
+ * be smaller than this
+ * @param maxStep maximal step (sign is irrelevant, regardless of
+ * integration direction, forward or backward), the last step can
+ * be smaller than this
+ * @param scalAbsoluteTolerance allowed absolute error
+ * @param scalRelativeTolerance allowed relative error
+ * @exception NumberIsTooSmallException if order is 1 or less
+ */
+ public AdamsMoultonIntegrator(final int nSteps,
+ final double minStep, final double maxStep,
+ final double scalAbsoluteTolerance,
+ final double scalRelativeTolerance)
+ throws NumberIsTooSmallException {
+ super(METHOD_NAME, nSteps, nSteps + 1, minStep, maxStep,
+ scalAbsoluteTolerance, scalRelativeTolerance);
+ }
+
+ /**
+ * Build an Adams-Moulton integrator with the given order and error control parameters.
+ * @param nSteps number of steps of the method excluding the one being computed
+ * @param minStep minimal step (sign is irrelevant, regardless of
+ * integration direction, forward or backward), the last step can
+ * be smaller than this
+ * @param maxStep maximal step (sign is irrelevant, regardless of
+ * integration direction, forward or backward), the last step can
+ * be smaller than this
+ * @param vecAbsoluteTolerance allowed absolute error
+ * @param vecRelativeTolerance allowed relative error
+ * @exception IllegalArgumentException if order is 1 or less
+ */
+ public AdamsMoultonIntegrator(final int nSteps,
+ final double minStep, final double maxStep,
+ final double[] vecAbsoluteTolerance,
+ final double[] vecRelativeTolerance)
+ throws IllegalArgumentException {
+ super(METHOD_NAME, nSteps, nSteps + 1, minStep, maxStep,
+ vecAbsoluteTolerance, vecRelativeTolerance);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void integrate(final ExpandableStatefulODE equations,final double t)
+ throws NumberIsTooSmallException, DimensionMismatchException,
+ MaxCountExceededException, NoBracketingException {
+
+ sanityChecks(equations, t);
+ setEquations(equations);
+ final boolean forward = t > equations.getTime();
+
+ // initialize working arrays
+ final double[] y0 = equations.getCompleteState();
+ final double[] y = y0.clone();
+ final double[] yDot = new double[y.length];
+ final double[] yTmp = new double[y.length];
+ final double[] predictedScaled = new double[y.length];
+ Array2DRowRealMatrix nordsieckTmp = null;
+
+ // set up two interpolators sharing the integrator arrays
+ final NordsieckStepInterpolator interpolator = new NordsieckStepInterpolator();
+ interpolator.reinitialize(y, forward,
+ equations.getPrimaryMapper(), equations.getSecondaryMappers());
+
+ // set up integration control objects
+ initIntegration(equations.getTime(), y0, t);
+
+ // compute the initial Nordsieck vector using the configured starter integrator
+ start(equations.getTime(), y, t);
+ interpolator.reinitialize(stepStart, stepSize, scaled, nordsieck);
+ interpolator.storeTime(stepStart);
+
+ double hNew = stepSize;
+ interpolator.rescale(hNew);
+
+ isLastStep = false;
+ do {
+
+ double error = 10;
+ while (error >= 1.0) {
+
+ stepSize = hNew;
+
+ // predict a first estimate of the state at step end (P in the PECE sequence)
+ final double stepEnd = stepStart + stepSize;
+ interpolator.setInterpolatedTime(stepEnd);
+ final ExpandableStatefulODE expandable = getExpandable();
+ final EquationsMapper primary = expandable.getPrimaryMapper();
+ primary.insertEquationData(interpolator.getInterpolatedState(), yTmp);
+ int index = 0;
+ for (final EquationsMapper secondary : expandable.getSecondaryMappers()) {
+ secondary.insertEquationData(interpolator.getInterpolatedSecondaryState(index), yTmp);
+ ++index;
+ }
+
+ // evaluate a first estimate of the derivative (first E in the PECE sequence)
+ computeDerivatives(stepEnd, yTmp, yDot);
+
+ // update Nordsieck vector
+ for (int j = 0; j < y0.length; ++j) {
+ predictedScaled[j] = stepSize * yDot[j];
+ }
+ nordsieckTmp = updateHighOrderDerivativesPhase1(nordsieck);
+ updateHighOrderDerivativesPhase2(scaled, predictedScaled, nordsieckTmp);
+
+ // apply correction (C in the PECE sequence)
+ error = nordsieckTmp.walkInOptimizedOrder(new Corrector(y, predictedScaled, yTmp));
+
+ if (error >= 1.0) {
+ // reject the step and attempt to reduce error by stepsize control
+ final double factor = computeStepGrowShrinkFactor(error);
+ hNew = filterStep(stepSize * factor, forward, false);
+ interpolator.rescale(hNew);
+ }
+ }
+
+ // evaluate a final estimate of the derivative (second E in the PECE sequence)
+ final double stepEnd = stepStart + stepSize;
+ computeDerivatives(stepEnd, yTmp, yDot);
+
+ // update Nordsieck vector
+ final double[] correctedScaled = new double[y0.length];
+ for (int j = 0; j < y0.length; ++j) {
+ correctedScaled[j] = stepSize * yDot[j];
+ }
+ updateHighOrderDerivativesPhase2(predictedScaled, correctedScaled, nordsieckTmp);
+
+ // discrete events handling
+ System.arraycopy(yTmp, 0, y, 0, y.length);
+ interpolator.reinitialize(stepEnd, stepSize, correctedScaled, nordsieckTmp);
+ interpolator.storeTime(stepStart);
+ interpolator.shift();
+ interpolator.storeTime(stepEnd);
+ stepStart = acceptStep(interpolator, y, yDot, t);
+ scaled = correctedScaled;
+ nordsieck = nordsieckTmp;
+
+ if (!isLastStep) {
+
+ // prepare next step
+ interpolator.storeTime(stepStart);
+
+ if (resetOccurred) {
+ // some events handler has triggered changes that
+ // invalidate the derivatives, we need to restart from scratch
+ start(stepStart, y, t);
+ interpolator.reinitialize(stepStart, stepSize, scaled, nordsieck);
+
+ }
+
+ // stepsize control for next step
+ final double factor = computeStepGrowShrinkFactor(error);
+ final double scaledH = stepSize * factor;
+ final double nextT = stepStart + scaledH;
+ final boolean nextIsLast = forward ? (nextT >= t) : (nextT <= t);
+ hNew = filterStep(scaledH, forward, nextIsLast);
+
+ final double filteredNextT = stepStart + hNew;
+ final boolean filteredNextIsLast = forward ? (filteredNextT >= t) : (filteredNextT <= t);
+ if (filteredNextIsLast) {
+ hNew = t - stepStart;
+ }
+
+ interpolator.rescale(hNew);
+ }
+
+ } while (!isLastStep);
+
+ // dispatch results
+ equations.setTime(stepStart);
+ equations.setCompleteState(y);
+
+ resetInternalState();
+
+ }
+
+ /** Corrector for current state in Adams-Moulton method.
+ * <p>
+ * This visitor implements the Taylor series formula:
+ * <pre>
+ * Y<sub>n+1</sub> = y<sub>n</sub> + s<sub>1</sub>(n+1) + [ -1 +1 -1 +1 ... &plusmn;1 ] r<sub>n+1</sub>
+ * </pre>
+ * </p>
+ */
+ private class Corrector implements RealMatrixPreservingVisitor {
+
+ /** Previous state. */
+ private final double[] previous;
+
+ /** Current scaled first derivative. */
+ private final double[] scaled;
+
+ /** Current state before correction. */
+ private final double[] before;
+
+ /** Current state after correction. */
+ private final double[] after;
+
+ /** Simple constructor.
+ * @param previous previous state
+ * @param scaled current scaled first derivative
+ * @param state state to correct (will be overwritten after visit)
+ */
+ Corrector(final double[] previous, final double[] scaled, final double[] state) {
+ this.previous = previous;
+ this.scaled = scaled;
+ this.after = state;
+ this.before = state.clone();
+ }
+
+ /** {@inheritDoc} */
+ public void start(int rows, int columns,
+ int startRow, int endRow, int startColumn, int endColumn) {
+ Arrays.fill(after, 0.0);
+ }
+
+ /** {@inheritDoc} */
+ public void visit(int row, int column, double value) {
+ if ((row & 0x1) == 0) {
+ after[column] -= value;
+ } else {
+ after[column] += value;
+ }
+ }
+
+ /**
+ * End visiting the Nordsieck vector.
+ * <p>The correction is used to control stepsize. So its amplitude is
+ * considered to be an error, which must be normalized according to
+ * error control settings. If the normalized value is greater than 1,
+ * the correction was too large and the step must be rejected.</p>
+ * @return the normalized correction, if greater than 1, the step
+ * must be rejected
+ */
+ public double end() {
+
+ double error = 0;
+ for (int i = 0; i < after.length; ++i) {
+ after[i] += previous[i] + scaled[i];
+ if (i < mainSetDimension) {
+ final double yScale = FastMath.max(FastMath.abs(previous[i]), FastMath.abs(after[i]));
+ final double tol = (vecAbsoluteTolerance == null) ?
+ (scalAbsoluteTolerance + scalRelativeTolerance * yScale) :
+ (vecAbsoluteTolerance[i] + vecRelativeTolerance[i] * yScale);
+ final double ratio = (after[i] - before[i]) / tol; // (corrected-predicted)/tol
+ error += ratio * ratio;
+ }
+ }
+
+ return FastMath.sqrt(error / mainSetDimension);
+
+ }
+ }
+
+}
diff --git a/src/main/java/org/apache/commons/math3/ode/nonstiff/AdamsNordsieckFieldTransformer.java b/src/main/java/org/apache/commons/math3/ode/nonstiff/AdamsNordsieckFieldTransformer.java
new file mode 100644
index 0000000..b8f872b
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/ode/nonstiff/AdamsNordsieckFieldTransformer.java
@@ -0,0 +1,363 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math3.ode.nonstiff;
+
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.Map;
+
+import org.apache.commons.math3.Field;
+import org.apache.commons.math3.RealFieldElement;
+import org.apache.commons.math3.linear.Array2DRowFieldMatrix;
+import org.apache.commons.math3.linear.ArrayFieldVector;
+import org.apache.commons.math3.linear.FieldDecompositionSolver;
+import org.apache.commons.math3.linear.FieldLUDecomposition;
+import org.apache.commons.math3.linear.FieldMatrix;
+import org.apache.commons.math3.util.MathArrays;
+
+/** Transformer to Nordsieck vectors for Adams integrators.
+ * <p>This class is used by {@link AdamsBashforthIntegrator Adams-Bashforth} and
+ * {@link AdamsMoultonIntegrator Adams-Moulton} integrators to convert between
+ * classical representation with several previous first derivatives and Nordsieck
+ * representation with higher order scaled derivatives.</p>
+ *
+ * <p>We define scaled derivatives s<sub>i</sub>(n) at step n as:
+ * <pre>
+ * s<sub>1</sub>(n) = h y'<sub>n</sub> for first derivative
+ * s<sub>2</sub>(n) = h<sup>2</sup>/2 y''<sub>n</sub> for second derivative
+ * s<sub>3</sub>(n) = h<sup>3</sup>/6 y'''<sub>n</sub> for third derivative
+ * ...
+ * s<sub>k</sub>(n) = h<sup>k</sup>/k! y<sup>(k)</sup><sub>n</sub> for k<sup>th</sup> derivative
+ * </pre></p>
+ *
+ * <p>With the previous definition, the classical representation of multistep methods
+ * uses first derivatives only, i.e. it handles y<sub>n</sub>, s<sub>1</sub>(n) and
+ * q<sub>n</sub> where q<sub>n</sub> is defined as:
+ * <pre>
+ * q<sub>n</sub> = [ s<sub>1</sub>(n-1) s<sub>1</sub>(n-2) ... s<sub>1</sub>(n-(k-1)) ]<sup>T</sup>
+ * </pre>
+ * (we omit the k index in the notation for clarity).</p>
+ *
+ * <p>Another possible representation uses the Nordsieck vector with
+ * higher degrees scaled derivatives all taken at the same step, i.e it handles y<sub>n</sub>,
+ * s<sub>1</sub>(n) and r<sub>n</sub>) where r<sub>n</sub> is defined as:
+ * <pre>
+ * r<sub>n</sub> = [ s<sub>2</sub>(n), s<sub>3</sub>(n) ... s<sub>k</sub>(n) ]<sup>T</sup>
+ * </pre>
+ * (here again we omit the k index in the notation for clarity)
+ * </p>
+ *
+ * <p>Taylor series formulas show that for any index offset i, s<sub>1</sub>(n-i) can be
+ * computed from s<sub>1</sub>(n), s<sub>2</sub>(n) ... s<sub>k</sub>(n), the formula being exact
+ * for degree k polynomials.
+ * <pre>
+ * s<sub>1</sub>(n-i) = s<sub>1</sub>(n) + &sum;<sub>j&gt;0</sub> (j+1) (-i)<sup>j</sup> s<sub>j+1</sub>(n)
+ * </pre>
+ * The previous formula can be used with several values for i to compute the transform between
+ * classical representation and Nordsieck vector at step end. The transform between r<sub>n</sub>
+ * and q<sub>n</sub> resulting from the Taylor series formulas above is:
+ * <pre>
+ * q<sub>n</sub> = s<sub>1</sub>(n) u + P r<sub>n</sub>
+ * </pre>
+ * where u is the [ 1 1 ... 1 ]<sup>T</sup> vector and P is the (k-1)&times;(k-1) matrix built
+ * with the (j+1) (-i)<sup>j</sup> terms with i being the row number starting from 1 and j being
+ * the column number starting from 1:
+ * <pre>
+ * [ -2 3 -4 5 ... ]
+ * [ -4 12 -32 80 ... ]
+ * P = [ -6 27 -108 405 ... ]
+ * [ -8 48 -256 1280 ... ]
+ * [ ... ]
+ * </pre></p>
+ *
+ * <p>Changing -i into +i in the formula above can be used to compute a similar transform between
+ * classical representation and Nordsieck vector at step start. The resulting matrix is simply
+ * the absolute value of matrix P.</p>
+ *
+ * <p>For {@link AdamsBashforthIntegrator Adams-Bashforth} method, the Nordsieck vector
+ * at step n+1 is computed from the Nordsieck vector at step n as follows:
+ * <ul>
+ * <li>y<sub>n+1</sub> = y<sub>n</sub> + s<sub>1</sub>(n) + u<sup>T</sup> r<sub>n</sub></li>
+ * <li>s<sub>1</sub>(n+1) = h f(t<sub>n+1</sub>, y<sub>n+1</sub>)</li>
+ * <li>r<sub>n+1</sub> = (s<sub>1</sub>(n) - s<sub>1</sub>(n+1)) P<sup>-1</sup> u + P<sup>-1</sup> A P r<sub>n</sub></li>
+ * </ul>
+ * where A is a rows shifting matrix (the lower left part is an identity matrix):
+ * <pre>
+ * [ 0 0 ... 0 0 | 0 ]
+ * [ ---------------+---]
+ * [ 1 0 ... 0 0 | 0 ]
+ * A = [ 0 1 ... 0 0 | 0 ]
+ * [ ... | 0 ]
+ * [ 0 0 ... 1 0 | 0 ]
+ * [ 0 0 ... 0 1 | 0 ]
+ * </pre></p>
+ *
+ * <p>For {@link AdamsMoultonIntegrator Adams-Moulton} method, the predicted Nordsieck vector
+ * at step n+1 is computed from the Nordsieck vector at step n as follows:
+ * <ul>
+ * <li>Y<sub>n+1</sub> = y<sub>n</sub> + s<sub>1</sub>(n) + u<sup>T</sup> r<sub>n</sub></li>
+ * <li>S<sub>1</sub>(n+1) = h f(t<sub>n+1</sub>, Y<sub>n+1</sub>)</li>
+ * <li>R<sub>n+1</sub> = (s<sub>1</sub>(n) - s<sub>1</sub>(n+1)) P<sup>-1</sup> u + P<sup>-1</sup> A P r<sub>n</sub></li>
+ * </ul>
+ * From this predicted vector, the corrected vector is computed as follows:
+ * <ul>
+ * <li>y<sub>n+1</sub> = y<sub>n</sub> + S<sub>1</sub>(n+1) + [ -1 +1 -1 +1 ... &plusmn;1 ] r<sub>n+1</sub></li>
+ * <li>s<sub>1</sub>(n+1) = h f(t<sub>n+1</sub>, y<sub>n+1</sub>)</li>
+ * <li>r<sub>n+1</sub> = R<sub>n+1</sub> + (s<sub>1</sub>(n+1) - S<sub>1</sub>(n+1)) P<sup>-1</sup> u</li>
+ * </ul>
+ * where the upper case Y<sub>n+1</sub>, S<sub>1</sub>(n+1) and R<sub>n+1</sub> represent the
+ * predicted states whereas the lower case y<sub>n+1</sub>, s<sub>n+1</sub> and r<sub>n+1</sub>
+ * represent the corrected states.</p>
+ *
+ * <p>We observe that both methods use similar update formulas. In both cases a P<sup>-1</sup>u
+ * vector and a P<sup>-1</sup> A P matrix are used that do not depend on the state,
+ * they only depend on k. This class handles these transformations.</p>
+ *
+ * @param <T> the type of the field elements
+ * @since 3.6
+ */
+public class AdamsNordsieckFieldTransformer<T extends RealFieldElement<T>> {
+
+ /** Cache for already computed coefficients. */
+ private static final Map<Integer,
+ Map<Field<? extends RealFieldElement<?>>,
+ AdamsNordsieckFieldTransformer<? extends RealFieldElement<?>>>> CACHE =
+ new HashMap<Integer, Map<Field<? extends RealFieldElement<?>>,
+ AdamsNordsieckFieldTransformer<? extends RealFieldElement<?>>>>();
+
+ /** Field to which the time and state vector elements belong. */
+ private final Field<T> field;
+
+ /** Update matrix for the higher order derivatives h<sup>2</sup>/2 y'', h<sup>3</sup>/6 y''' ... */
+ private final Array2DRowFieldMatrix<T> update;
+
+ /** Update coefficients of the higher order derivatives wrt y'. */
+ private final T[] c1;
+
+ /** Simple constructor.
+ * @param field field to which the time and state vector elements belong
+ * @param n number of steps of the multistep method
+ * (excluding the one being computed)
+ */
+ private AdamsNordsieckFieldTransformer(final Field<T> field, final int n) {
+
+ this.field = field;
+ final int rows = n - 1;
+
+ // compute coefficients
+ FieldMatrix<T> bigP = buildP(rows);
+ FieldDecompositionSolver<T> pSolver =
+ new FieldLUDecomposition<T>(bigP).getSolver();
+
+ T[] u = MathArrays.buildArray(field, rows);
+ Arrays.fill(u, field.getOne());
+ c1 = pSolver.solve(new ArrayFieldVector<T>(u, false)).toArray();
+
+ // update coefficients are computed by combining transform from
+ // Nordsieck to multistep, then shifting rows to represent step advance
+ // then applying inverse transform
+ T[][] shiftedP = bigP.getData();
+ for (int i = shiftedP.length - 1; i > 0; --i) {
+ // shift rows
+ shiftedP[i] = shiftedP[i - 1];
+ }
+ shiftedP[0] = MathArrays.buildArray(field, rows);
+ Arrays.fill(shiftedP[0], field.getZero());
+ update = new Array2DRowFieldMatrix<T>(pSolver.solve(new Array2DRowFieldMatrix<T>(shiftedP, false)).getData());
+
+ }
+
+ /** Get the Nordsieck transformer for a given field and number of steps.
+ * @param field field to which the time and state vector elements belong
+ * @param nSteps number of steps of the multistep method
+ * (excluding the one being computed)
+ * @return Nordsieck transformer for the specified field and number of steps
+ * @param <T> the type of the field elements
+ */
+ @SuppressWarnings("unchecked")
+ public static <T extends RealFieldElement<T>> AdamsNordsieckFieldTransformer<T>
+ getInstance(final Field<T> field, final int nSteps) {
+ synchronized(CACHE) {
+ Map<Field<? extends RealFieldElement<?>>,
+ AdamsNordsieckFieldTransformer<? extends RealFieldElement<?>>> map = CACHE.get(nSteps);
+ if (map == null) {
+ map = new HashMap<Field<? extends RealFieldElement<?>>,
+ AdamsNordsieckFieldTransformer<? extends RealFieldElement<?>>>();
+ CACHE.put(nSteps, map);
+ }
+ @SuppressWarnings("rawtypes") // use rawtype to avoid compilation problems with java 1.5
+ AdamsNordsieckFieldTransformer t = map.get(field);
+ if (t == null) {
+ t = new AdamsNordsieckFieldTransformer<T>(field, nSteps);
+ map.put(field, (AdamsNordsieckFieldTransformer<T>) t);
+ }
+ return (AdamsNordsieckFieldTransformer<T>) t;
+
+ }
+ }
+
+ /** Build the P matrix.
+ * <p>The P matrix general terms are shifted (j+1) (-i)<sup>j</sup> terms
+ * with i being the row number starting from 1 and j being the column
+ * number starting from 1:
+ * <pre>
+ * [ -2 3 -4 5 ... ]
+ * [ -4 12 -32 80 ... ]
+ * P = [ -6 27 -108 405 ... ]
+ * [ -8 48 -256 1280 ... ]
+ * [ ... ]
+ * </pre></p>
+ * @param rows number of rows of the matrix
+ * @return P matrix
+ */
+ private FieldMatrix<T> buildP(final int rows) {
+
+ final T[][] pData = MathArrays.buildArray(field, rows, rows);
+
+ for (int i = 1; i <= pData.length; ++i) {
+ // build the P matrix elements from Taylor series formulas
+ final T[] pI = pData[i - 1];
+ final int factor = -i;
+ T aj = field.getZero().add(factor);
+ for (int j = 1; j <= pI.length; ++j) {
+ pI[j - 1] = aj.multiply(j + 1);
+ aj = aj.multiply(factor);
+ }
+ }
+
+ return new Array2DRowFieldMatrix<T>(pData, false);
+
+ }
+
+ /** Initialize the high order scaled derivatives at step start.
+ * @param h step size to use for scaling
+ * @param t first steps times
+ * @param y first steps states
+ * @param yDot first steps derivatives
+ * @return Nordieck vector at start of first step (h<sup>2</sup>/2 y''<sub>n</sub>,
+ * h<sup>3</sup>/6 y'''<sub>n</sub> ... h<sup>k</sup>/k! y<sup>(k)</sup><sub>n</sub>)
+ */
+
+ public Array2DRowFieldMatrix<T> initializeHighOrderDerivatives(final T h, final T[] t,
+ final T[][] y,
+ final T[][] yDot) {
+
+ // using Taylor series with di = ti - t0, we get:
+ // y(ti) - y(t0) - di y'(t0) = di^2 / h^2 s2 + ... + di^k / h^k sk + O(h^k)
+ // y'(ti) - y'(t0) = 2 di / h^2 s2 + ... + k di^(k-1) / h^k sk + O(h^(k-1))
+ // we write these relations for i = 1 to i= 1+n/2 as a set of n + 2 linear
+ // equations depending on the Nordsieck vector [s2 ... sk rk], so s2 to sk correspond
+ // to the appropriately truncated Taylor expansion, and rk is the Taylor remainder.
+ // The goal is to have s2 to sk as accurate as possible considering the fact the sum is
+ // truncated and we don't want the error terms to be included in s2 ... sk, so we need
+ // to solve also for the remainder
+ final T[][] a = MathArrays.buildArray(field, c1.length + 1, c1.length + 1);
+ final T[][] b = MathArrays.buildArray(field, c1.length + 1, y[0].length);
+ final T[] y0 = y[0];
+ final T[] yDot0 = yDot[0];
+ for (int i = 1; i < y.length; ++i) {
+
+ final T di = t[i].subtract(t[0]);
+ final T ratio = di.divide(h);
+ T dikM1Ohk = h.reciprocal();
+
+ // linear coefficients of equations
+ // y(ti) - y(t0) - di y'(t0) and y'(ti) - y'(t0)
+ final T[] aI = a[2 * i - 2];
+ final T[] aDotI = (2 * i - 1) < a.length ? a[2 * i - 1] : null;
+ for (int j = 0; j < aI.length; ++j) {
+ dikM1Ohk = dikM1Ohk.multiply(ratio);
+ aI[j] = di.multiply(dikM1Ohk);
+ if (aDotI != null) {
+ aDotI[j] = dikM1Ohk.multiply(j + 2);
+ }
+ }
+
+ // expected value of the previous equations
+ final T[] yI = y[i];
+ final T[] yDotI = yDot[i];
+ final T[] bI = b[2 * i - 2];
+ final T[] bDotI = (2 * i - 1) < b.length ? b[2 * i - 1] : null;
+ for (int j = 0; j < yI.length; ++j) {
+ bI[j] = yI[j].subtract(y0[j]).subtract(di.multiply(yDot0[j]));
+ if (bDotI != null) {
+ bDotI[j] = yDotI[j].subtract(yDot0[j]);
+ }
+ }
+
+ }
+
+ // solve the linear system to get the best estimate of the Nordsieck vector [s2 ... sk],
+ // with the additional terms s(k+1) and c grabbing the parts after the truncated Taylor expansion
+ final FieldLUDecomposition<T> decomposition = new FieldLUDecomposition<T>(new Array2DRowFieldMatrix<T>(a, false));
+ final FieldMatrix<T> x = decomposition.getSolver().solve(new Array2DRowFieldMatrix<T>(b, false));
+
+ // extract just the Nordsieck vector [s2 ... sk]
+ final Array2DRowFieldMatrix<T> truncatedX =
+ new Array2DRowFieldMatrix<T>(field, x.getRowDimension() - 1, x.getColumnDimension());
+ for (int i = 0; i < truncatedX.getRowDimension(); ++i) {
+ for (int j = 0; j < truncatedX.getColumnDimension(); ++j) {
+ truncatedX.setEntry(i, j, x.getEntry(i, j));
+ }
+ }
+ return truncatedX;
+
+ }
+
+ /** Update the high order scaled derivatives for Adams integrators (phase 1).
+ * <p>The complete update of high order derivatives has a form similar to:
+ * <pre>
+ * r<sub>n+1</sub> = (s<sub>1</sub>(n) - s<sub>1</sub>(n+1)) P<sup>-1</sup> u + P<sup>-1</sup> A P r<sub>n</sub>
+ * </pre>
+ * this method computes the P<sup>-1</sup> A P r<sub>n</sub> part.</p>
+ * @param highOrder high order scaled derivatives
+ * (h<sup>2</sup>/2 y'', ... h<sup>k</sup>/k! y(k))
+ * @return updated high order derivatives
+ * @see #updateHighOrderDerivativesPhase2(RealFieldElement[], RealFieldElement[], Array2DRowFieldMatrix)
+ */
+ public Array2DRowFieldMatrix<T> updateHighOrderDerivativesPhase1(final Array2DRowFieldMatrix<T> highOrder) {
+ return update.multiply(highOrder);
+ }
+
+ /** Update the high order scaled derivatives Adams integrators (phase 2).
+ * <p>The complete update of high order derivatives has a form similar to:
+ * <pre>
+ * r<sub>n+1</sub> = (s<sub>1</sub>(n) - s<sub>1</sub>(n+1)) P<sup>-1</sup> u + P<sup>-1</sup> A P r<sub>n</sub>
+ * </pre>
+ * this method computes the (s<sub>1</sub>(n) - s<sub>1</sub>(n+1)) P<sup>-1</sup> u part.</p>
+ * <p>Phase 1 of the update must already have been performed.</p>
+ * @param start first order scaled derivatives at step start
+ * @param end first order scaled derivatives at step end
+ * @param highOrder high order scaled derivatives, will be modified
+ * (h<sup>2</sup>/2 y'', ... h<sup>k</sup>/k! y(k))
+ * @see #updateHighOrderDerivativesPhase1(Array2DRowFieldMatrix)
+ */
+ public void updateHighOrderDerivativesPhase2(final T[] start,
+ final T[] end,
+ final Array2DRowFieldMatrix<T> highOrder) {
+ final T[][] data = highOrder.getDataRef();
+ for (int i = 0; i < data.length; ++i) {
+ final T[] dataI = data[i];
+ final T c1I = c1[i];
+ for (int j = 0; j < dataI.length; ++j) {
+ dataI[j] = dataI[j].add(c1I.multiply(start[j].subtract(end[j])));
+ }
+ }
+ }
+
+}
diff --git a/src/main/java/org/apache/commons/math3/ode/nonstiff/AdamsNordsieckTransformer.java b/src/main/java/org/apache/commons/math3/ode/nonstiff/AdamsNordsieckTransformer.java
new file mode 100644
index 0000000..1c54b17
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/ode/nonstiff/AdamsNordsieckTransformer.java
@@ -0,0 +1,361 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math3.ode.nonstiff;
+
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.Map;
+
+import org.apache.commons.math3.fraction.BigFraction;
+import org.apache.commons.math3.linear.Array2DRowFieldMatrix;
+import org.apache.commons.math3.linear.Array2DRowRealMatrix;
+import org.apache.commons.math3.linear.ArrayFieldVector;
+import org.apache.commons.math3.linear.FieldDecompositionSolver;
+import org.apache.commons.math3.linear.FieldLUDecomposition;
+import org.apache.commons.math3.linear.FieldMatrix;
+import org.apache.commons.math3.linear.MatrixUtils;
+import org.apache.commons.math3.linear.QRDecomposition;
+import org.apache.commons.math3.linear.RealMatrix;
+
+/** Transformer to Nordsieck vectors for Adams integrators.
+ * <p>This class is used by {@link AdamsBashforthIntegrator Adams-Bashforth} and
+ * {@link AdamsMoultonIntegrator Adams-Moulton} integrators to convert between
+ * classical representation with several previous first derivatives and Nordsieck
+ * representation with higher order scaled derivatives.</p>
+ *
+ * <p>We define scaled derivatives s<sub>i</sub>(n) at step n as:
+ * <pre>
+ * s<sub>1</sub>(n) = h y'<sub>n</sub> for first derivative
+ * s<sub>2</sub>(n) = h<sup>2</sup>/2 y''<sub>n</sub> for second derivative
+ * s<sub>3</sub>(n) = h<sup>3</sup>/6 y'''<sub>n</sub> for third derivative
+ * ...
+ * s<sub>k</sub>(n) = h<sup>k</sup>/k! y<sup>(k)</sup><sub>n</sub> for k<sup>th</sup> derivative
+ * </pre></p>
+ *
+ * <p>With the previous definition, the classical representation of multistep methods
+ * uses first derivatives only, i.e. it handles y<sub>n</sub>, s<sub>1</sub>(n) and
+ * q<sub>n</sub> where q<sub>n</sub> is defined as:
+ * <pre>
+ * q<sub>n</sub> = [ s<sub>1</sub>(n-1) s<sub>1</sub>(n-2) ... s<sub>1</sub>(n-(k-1)) ]<sup>T</sup>
+ * </pre>
+ * (we omit the k index in the notation for clarity).</p>
+ *
+ * <p>Another possible representation uses the Nordsieck vector with
+ * higher degrees scaled derivatives all taken at the same step, i.e it handles y<sub>n</sub>,
+ * s<sub>1</sub>(n) and r<sub>n</sub>) where r<sub>n</sub> is defined as:
+ * <pre>
+ * r<sub>n</sub> = [ s<sub>2</sub>(n), s<sub>3</sub>(n) ... s<sub>k</sub>(n) ]<sup>T</sup>
+ * </pre>
+ * (here again we omit the k index in the notation for clarity)
+ * </p>
+ *
+ * <p>Taylor series formulas show that for any index offset i, s<sub>1</sub>(n-i) can be
+ * computed from s<sub>1</sub>(n), s<sub>2</sub>(n) ... s<sub>k</sub>(n), the formula being exact
+ * for degree k polynomials.
+ * <pre>
+ * s<sub>1</sub>(n-i) = s<sub>1</sub>(n) + &sum;<sub>j&gt;0</sub> (j+1) (-i)<sup>j</sup> s<sub>j+1</sub>(n)
+ * </pre>
+ * The previous formula can be used with several values for i to compute the transform between
+ * classical representation and Nordsieck vector at step end. The transform between r<sub>n</sub>
+ * and q<sub>n</sub> resulting from the Taylor series formulas above is:
+ * <pre>
+ * q<sub>n</sub> = s<sub>1</sub>(n) u + P r<sub>n</sub>
+ * </pre>
+ * where u is the [ 1 1 ... 1 ]<sup>T</sup> vector and P is the (k-1)&times;(k-1) matrix built
+ * with the (j+1) (-i)<sup>j</sup> terms with i being the row number starting from 1 and j being
+ * the column number starting from 1:
+ * <pre>
+ * [ -2 3 -4 5 ... ]
+ * [ -4 12 -32 80 ... ]
+ * P = [ -6 27 -108 405 ... ]
+ * [ -8 48 -256 1280 ... ]
+ * [ ... ]
+ * </pre></p>
+ *
+ * <p>Changing -i into +i in the formula above can be used to compute a similar transform between
+ * classical representation and Nordsieck vector at step start. The resulting matrix is simply
+ * the absolute value of matrix P.</p>
+ *
+ * <p>For {@link AdamsBashforthIntegrator Adams-Bashforth} method, the Nordsieck vector
+ * at step n+1 is computed from the Nordsieck vector at step n as follows:
+ * <ul>
+ * <li>y<sub>n+1</sub> = y<sub>n</sub> + s<sub>1</sub>(n) + u<sup>T</sup> r<sub>n</sub></li>
+ * <li>s<sub>1</sub>(n+1) = h f(t<sub>n+1</sub>, y<sub>n+1</sub>)</li>
+ * <li>r<sub>n+1</sub> = (s<sub>1</sub>(n) - s<sub>1</sub>(n+1)) P<sup>-1</sup> u + P<sup>-1</sup> A P r<sub>n</sub></li>
+ * </ul>
+ * where A is a rows shifting matrix (the lower left part is an identity matrix):
+ * <pre>
+ * [ 0 0 ... 0 0 | 0 ]
+ * [ ---------------+---]
+ * [ 1 0 ... 0 0 | 0 ]
+ * A = [ 0 1 ... 0 0 | 0 ]
+ * [ ... | 0 ]
+ * [ 0 0 ... 1 0 | 0 ]
+ * [ 0 0 ... 0 1 | 0 ]
+ * </pre></p>
+ *
+ * <p>For {@link AdamsMoultonIntegrator Adams-Moulton} method, the predicted Nordsieck vector
+ * at step n+1 is computed from the Nordsieck vector at step n as follows:
+ * <ul>
+ * <li>Y<sub>n+1</sub> = y<sub>n</sub> + s<sub>1</sub>(n) + u<sup>T</sup> r<sub>n</sub></li>
+ * <li>S<sub>1</sub>(n+1) = h f(t<sub>n+1</sub>, Y<sub>n+1</sub>)</li>
+ * <li>R<sub>n+1</sub> = (s<sub>1</sub>(n) - s<sub>1</sub>(n+1)) P<sup>-1</sup> u + P<sup>-1</sup> A P r<sub>n</sub></li>
+ * </ul>
+ * From this predicted vector, the corrected vector is computed as follows:
+ * <ul>
+ * <li>y<sub>n+1</sub> = y<sub>n</sub> + S<sub>1</sub>(n+1) + [ -1 +1 -1 +1 ... &plusmn;1 ] r<sub>n+1</sub></li>
+ * <li>s<sub>1</sub>(n+1) = h f(t<sub>n+1</sub>, y<sub>n+1</sub>)</li>
+ * <li>r<sub>n+1</sub> = R<sub>n+1</sub> + (s<sub>1</sub>(n+1) - S<sub>1</sub>(n+1)) P<sup>-1</sup> u</li>
+ * </ul>
+ * where the upper case Y<sub>n+1</sub>, S<sub>1</sub>(n+1) and R<sub>n+1</sub> represent the
+ * predicted states whereas the lower case y<sub>n+1</sub>, s<sub>n+1</sub> and r<sub>n+1</sub>
+ * represent the corrected states.</p>
+ *
+ * <p>We observe that both methods use similar update formulas. In both cases a P<sup>-1</sup>u
+ * vector and a P<sup>-1</sup> A P matrix are used that do not depend on the state,
+ * they only depend on k. This class handles these transformations.</p>
+ *
+ * @since 2.0
+ */
+public class AdamsNordsieckTransformer {
+
+ /** Cache for already computed coefficients. */
+ private static final Map<Integer, AdamsNordsieckTransformer> CACHE =
+ new HashMap<Integer, AdamsNordsieckTransformer>();
+
+ /** Update matrix for the higher order derivatives h<sup>2</sup>/2 y'', h<sup>3</sup>/6 y''' ... */
+ private final Array2DRowRealMatrix update;
+
+ /** Update coefficients of the higher order derivatives wrt y'. */
+ private final double[] c1;
+
+ /** Simple constructor.
+ * @param n number of steps of the multistep method
+ * (excluding the one being computed)
+ */
+ private AdamsNordsieckTransformer(final int n) {
+
+ final int rows = n - 1;
+
+ // compute exact coefficients
+ FieldMatrix<BigFraction> bigP = buildP(rows);
+ FieldDecompositionSolver<BigFraction> pSolver =
+ new FieldLUDecomposition<BigFraction>(bigP).getSolver();
+
+ BigFraction[] u = new BigFraction[rows];
+ Arrays.fill(u, BigFraction.ONE);
+ BigFraction[] bigC1 = pSolver.solve(new ArrayFieldVector<BigFraction>(u, false)).toArray();
+
+ // update coefficients are computed by combining transform from
+ // Nordsieck to multistep, then shifting rows to represent step advance
+ // then applying inverse transform
+ BigFraction[][] shiftedP = bigP.getData();
+ for (int i = shiftedP.length - 1; i > 0; --i) {
+ // shift rows
+ shiftedP[i] = shiftedP[i - 1];
+ }
+ shiftedP[0] = new BigFraction[rows];
+ Arrays.fill(shiftedP[0], BigFraction.ZERO);
+ FieldMatrix<BigFraction> bigMSupdate =
+ pSolver.solve(new Array2DRowFieldMatrix<BigFraction>(shiftedP, false));
+
+ // convert coefficients to double
+ update = MatrixUtils.bigFractionMatrixToRealMatrix(bigMSupdate);
+ c1 = new double[rows];
+ for (int i = 0; i < rows; ++i) {
+ c1[i] = bigC1[i].doubleValue();
+ }
+
+ }
+
+ /** Get the Nordsieck transformer for a given number of steps.
+ * @param nSteps number of steps of the multistep method
+ * (excluding the one being computed)
+ * @return Nordsieck transformer for the specified number of steps
+ */
+ public static AdamsNordsieckTransformer getInstance(final int nSteps) {
+ synchronized(CACHE) {
+ AdamsNordsieckTransformer t = CACHE.get(nSteps);
+ if (t == null) {
+ t = new AdamsNordsieckTransformer(nSteps);
+ CACHE.put(nSteps, t);
+ }
+ return t;
+ }
+ }
+
+ /** Get the number of steps of the method
+ * (excluding the one being computed).
+ * @return number of steps of the method
+ * (excluding the one being computed)
+ * @deprecated as of 3.6, this method is not used anymore
+ */
+ @Deprecated
+ public int getNSteps() {
+ return c1.length;
+ }
+
+ /** Build the P matrix.
+ * <p>The P matrix general terms are shifted (j+1) (-i)<sup>j</sup> terms
+ * with i being the row number starting from 1 and j being the column
+ * number starting from 1:
+ * <pre>
+ * [ -2 3 -4 5 ... ]
+ * [ -4 12 -32 80 ... ]
+ * P = [ -6 27 -108 405 ... ]
+ * [ -8 48 -256 1280 ... ]
+ * [ ... ]
+ * </pre></p>
+ * @param rows number of rows of the matrix
+ * @return P matrix
+ */
+ private FieldMatrix<BigFraction> buildP(final int rows) {
+
+ final BigFraction[][] pData = new BigFraction[rows][rows];
+
+ for (int i = 1; i <= pData.length; ++i) {
+ // build the P matrix elements from Taylor series formulas
+ final BigFraction[] pI = pData[i - 1];
+ final int factor = -i;
+ int aj = factor;
+ for (int j = 1; j <= pI.length; ++j) {
+ pI[j - 1] = new BigFraction(aj * (j + 1));
+ aj *= factor;
+ }
+ }
+
+ return new Array2DRowFieldMatrix<BigFraction>(pData, false);
+
+ }
+
+ /** Initialize the high order scaled derivatives at step start.
+ * @param h step size to use for scaling
+ * @param t first steps times
+ * @param y first steps states
+ * @param yDot first steps derivatives
+ * @return Nordieck vector at start of first step (h<sup>2</sup>/2 y''<sub>n</sub>,
+ * h<sup>3</sup>/6 y'''<sub>n</sub> ... h<sup>k</sup>/k! y<sup>(k)</sup><sub>n</sub>)
+ */
+
+ public Array2DRowRealMatrix initializeHighOrderDerivatives(final double h, final double[] t,
+ final double[][] y,
+ final double[][] yDot) {
+
+ // using Taylor series with di = ti - t0, we get:
+ // y(ti) - y(t0) - di y'(t0) = di^2 / h^2 s2 + ... + di^k / h^k sk + O(h^k)
+ // y'(ti) - y'(t0) = 2 di / h^2 s2 + ... + k di^(k-1) / h^k sk + O(h^(k-1))
+ // we write these relations for i = 1 to i= 1+n/2 as a set of n + 2 linear
+ // equations depending on the Nordsieck vector [s2 ... sk rk], so s2 to sk correspond
+ // to the appropriately truncated Taylor expansion, and rk is the Taylor remainder.
+ // The goal is to have s2 to sk as accurate as possible considering the fact the sum is
+ // truncated and we don't want the error terms to be included in s2 ... sk, so we need
+ // to solve also for the remainder
+ final double[][] a = new double[c1.length + 1][c1.length + 1];
+ final double[][] b = new double[c1.length + 1][y[0].length];
+ final double[] y0 = y[0];
+ final double[] yDot0 = yDot[0];
+ for (int i = 1; i < y.length; ++i) {
+
+ final double di = t[i] - t[0];
+ final double ratio = di / h;
+ double dikM1Ohk = 1 / h;
+
+ // linear coefficients of equations
+ // y(ti) - y(t0) - di y'(t0) and y'(ti) - y'(t0)
+ final double[] aI = a[2 * i - 2];
+ final double[] aDotI = (2 * i - 1) < a.length ? a[2 * i - 1] : null;
+ for (int j = 0; j < aI.length; ++j) {
+ dikM1Ohk *= ratio;
+ aI[j] = di * dikM1Ohk;
+ if (aDotI != null) {
+ aDotI[j] = (j + 2) * dikM1Ohk;
+ }
+ }
+
+ // expected value of the previous equations
+ final double[] yI = y[i];
+ final double[] yDotI = yDot[i];
+ final double[] bI = b[2 * i - 2];
+ final double[] bDotI = (2 * i - 1) < b.length ? b[2 * i - 1] : null;
+ for (int j = 0; j < yI.length; ++j) {
+ bI[j] = yI[j] - y0[j] - di * yDot0[j];
+ if (bDotI != null) {
+ bDotI[j] = yDotI[j] - yDot0[j];
+ }
+ }
+
+ }
+
+ // solve the linear system to get the best estimate of the Nordsieck vector [s2 ... sk],
+ // with the additional terms s(k+1) and c grabbing the parts after the truncated Taylor expansion
+ final QRDecomposition decomposition = new QRDecomposition(new Array2DRowRealMatrix(a, false));
+ final RealMatrix x = decomposition.getSolver().solve(new Array2DRowRealMatrix(b, false));
+
+ // extract just the Nordsieck vector [s2 ... sk]
+ final Array2DRowRealMatrix truncatedX = new Array2DRowRealMatrix(x.getRowDimension() - 1, x.getColumnDimension());
+ for (int i = 0; i < truncatedX.getRowDimension(); ++i) {
+ for (int j = 0; j < truncatedX.getColumnDimension(); ++j) {
+ truncatedX.setEntry(i, j, x.getEntry(i, j));
+ }
+ }
+ return truncatedX;
+
+ }
+
+ /** Update the high order scaled derivatives for Adams integrators (phase 1).
+ * <p>The complete update of high order derivatives has a form similar to:
+ * <pre>
+ * r<sub>n+1</sub> = (s<sub>1</sub>(n) - s<sub>1</sub>(n+1)) P<sup>-1</sup> u + P<sup>-1</sup> A P r<sub>n</sub>
+ * </pre>
+ * this method computes the P<sup>-1</sup> A P r<sub>n</sub> part.</p>
+ * @param highOrder high order scaled derivatives
+ * (h<sup>2</sup>/2 y'', ... h<sup>k</sup>/k! y(k))
+ * @return updated high order derivatives
+ * @see #updateHighOrderDerivativesPhase2(double[], double[], Array2DRowRealMatrix)
+ */
+ public Array2DRowRealMatrix updateHighOrderDerivativesPhase1(final Array2DRowRealMatrix highOrder) {
+ return update.multiply(highOrder);
+ }
+
+ /** Update the high order scaled derivatives Adams integrators (phase 2).
+ * <p>The complete update of high order derivatives has a form similar to:
+ * <pre>
+ * r<sub>n+1</sub> = (s<sub>1</sub>(n) - s<sub>1</sub>(n+1)) P<sup>-1</sup> u + P<sup>-1</sup> A P r<sub>n</sub>
+ * </pre>
+ * this method computes the (s<sub>1</sub>(n) - s<sub>1</sub>(n+1)) P<sup>-1</sup> u part.</p>
+ * <p>Phase 1 of the update must already have been performed.</p>
+ * @param start first order scaled derivatives at step start
+ * @param end first order scaled derivatives at step end
+ * @param highOrder high order scaled derivatives, will be modified
+ * (h<sup>2</sup>/2 y'', ... h<sup>k</sup>/k! y(k))
+ * @see #updateHighOrderDerivativesPhase1(Array2DRowRealMatrix)
+ */
+ public void updateHighOrderDerivativesPhase2(final double[] start,
+ final double[] end,
+ final Array2DRowRealMatrix highOrder) {
+ final double[][] data = highOrder.getDataRef();
+ for (int i = 0; i < data.length; ++i) {
+ final double[] dataI = data[i];
+ final double c1I = c1[i];
+ for (int j = 0; j < dataI.length; ++j) {
+ dataI[j] += c1I * (start[j] - end[j]);
+ }
+ }
+ }
+
+}
diff --git a/src/main/java/org/apache/commons/math3/ode/nonstiff/AdaptiveStepsizeFieldIntegrator.java b/src/main/java/org/apache/commons/math3/ode/nonstiff/AdaptiveStepsizeFieldIntegrator.java
new file mode 100644
index 0000000..c8e592b
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/ode/nonstiff/AdaptiveStepsizeFieldIntegrator.java
@@ -0,0 +1,366 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math3.ode.nonstiff;
+
+import org.apache.commons.math3.Field;
+import org.apache.commons.math3.RealFieldElement;
+import org.apache.commons.math3.exception.DimensionMismatchException;
+import org.apache.commons.math3.exception.MaxCountExceededException;
+import org.apache.commons.math3.exception.NumberIsTooSmallException;
+import org.apache.commons.math3.exception.util.LocalizedFormats;
+import org.apache.commons.math3.ode.AbstractFieldIntegrator;
+import org.apache.commons.math3.ode.FieldEquationsMapper;
+import org.apache.commons.math3.ode.FieldODEState;
+import org.apache.commons.math3.ode.FieldODEStateAndDerivative;
+import org.apache.commons.math3.util.FastMath;
+import org.apache.commons.math3.util.MathArrays;
+import org.apache.commons.math3.util.MathUtils;
+
+/**
+ * This abstract class holds the common part of all adaptive
+ * stepsize integrators for Ordinary Differential Equations.
+ *
+ * <p>These algorithms perform integration with stepsize control, which
+ * means the user does not specify the integration step but rather a
+ * tolerance on error. The error threshold is computed as
+ * <pre>
+ * threshold_i = absTol_i + relTol_i * max (abs (ym), abs (ym+1))
+ * </pre>
+ * where absTol_i is the absolute tolerance for component i of the
+ * state vector and relTol_i is the relative tolerance for the same
+ * component. The user can also use only two scalar values absTol and
+ * relTol which will be used for all components.
+ * </p>
+ * <p>
+ * Note that <em>only</em> the {@link FieldODEState#getState() main part}
+ * of the state vector is used for stepsize control. The {@link
+ * FieldODEState#getSecondaryState(int) secondary parts} of the state
+ * vector are explicitly ignored for stepsize control.
+ * </p>
+ *
+ * <p>If the estimated error for ym+1 is such that
+ * <pre>
+ * sqrt((sum (errEst_i / threshold_i)^2 ) / n) < 1
+ * </pre>
+ *
+ * (where n is the main set dimension) then the step is accepted,
+ * otherwise the step is rejected and a new attempt is made with a new
+ * stepsize.</p>
+ *
+ * @param <T> the type of the field elements
+ * @since 3.6
+ *
+ */
+
+public abstract class AdaptiveStepsizeFieldIntegrator<T extends RealFieldElement<T>>
+ extends AbstractFieldIntegrator<T> {
+
+ /** Allowed absolute scalar error. */
+ protected double scalAbsoluteTolerance;
+
+ /** Allowed relative scalar error. */
+ protected double scalRelativeTolerance;
+
+ /** Allowed absolute vectorial error. */
+ protected double[] vecAbsoluteTolerance;
+
+ /** Allowed relative vectorial error. */
+ protected double[] vecRelativeTolerance;
+
+ /** Main set dimension. */
+ protected int mainSetDimension;
+
+ /** User supplied initial step. */
+ private T initialStep;
+
+ /** Minimal step. */
+ private T minStep;
+
+ /** Maximal step. */
+ private T maxStep;
+
+ /** Build an integrator with the given stepsize bounds.
+ * The default step handler does nothing.
+ * @param field field to which the time and state vector elements belong
+ * @param name name of the method
+ * @param minStep minimal step (sign is irrelevant, regardless of
+ * integration direction, forward or backward), the last step can
+ * be smaller than this
+ * @param maxStep maximal step (sign is irrelevant, regardless of
+ * integration direction, forward or backward), the last step can
+ * be smaller than this
+ * @param scalAbsoluteTolerance allowed absolute error
+ * @param scalRelativeTolerance allowed relative error
+ */
+ public AdaptiveStepsizeFieldIntegrator(final Field<T> field, final String name,
+ final double minStep, final double maxStep,
+ final double scalAbsoluteTolerance,
+ final double scalRelativeTolerance) {
+
+ super(field, name);
+ setStepSizeControl(minStep, maxStep, scalAbsoluteTolerance, scalRelativeTolerance);
+ resetInternalState();
+
+ }
+
+ /** Build an integrator with the given stepsize bounds.
+ * The default step handler does nothing.
+ * @param field field to which the time and state vector elements belong
+ * @param name name of the method
+ * @param minStep minimal step (sign is irrelevant, regardless of
+ * integration direction, forward or backward), the last step can
+ * be smaller than this
+ * @param maxStep maximal step (sign is irrelevant, regardless of
+ * integration direction, forward or backward), the last step can
+ * be smaller than this
+ * @param vecAbsoluteTolerance allowed absolute error
+ * @param vecRelativeTolerance allowed relative error
+ */
+ public AdaptiveStepsizeFieldIntegrator(final Field<T> field, final String name,
+ final double minStep, final double maxStep,
+ final double[] vecAbsoluteTolerance,
+ final double[] vecRelativeTolerance) {
+
+ super(field, name);
+ setStepSizeControl(minStep, maxStep, vecAbsoluteTolerance, vecRelativeTolerance);
+ resetInternalState();
+
+ }
+
+ /** Set the adaptive step size control parameters.
+ * <p>
+ * A side effect of this method is to also reset the initial
+ * step so it will be automatically computed by the integrator
+ * if {@link #setInitialStepSize(RealFieldElement) setInitialStepSize}
+ * is not called by the user.
+ * </p>
+ * @param minimalStep minimal step (must be positive even for backward
+ * integration), the last step can be smaller than this
+ * @param maximalStep maximal step (must be positive even for backward
+ * integration)
+ * @param absoluteTolerance allowed absolute error
+ * @param relativeTolerance allowed relative error
+ */
+ public void setStepSizeControl(final double minimalStep, final double maximalStep,
+ final double absoluteTolerance,
+ final double relativeTolerance) {
+
+ minStep = getField().getZero().add(FastMath.abs(minimalStep));
+ maxStep = getField().getZero().add(FastMath.abs(maximalStep));
+ initialStep = getField().getOne().negate();
+
+ scalAbsoluteTolerance = absoluteTolerance;
+ scalRelativeTolerance = relativeTolerance;
+ vecAbsoluteTolerance = null;
+ vecRelativeTolerance = null;
+
+ }
+
+ /** Set the adaptive step size control parameters.
+ * <p>
+ * A side effect of this method is to also reset the initial
+ * step so it will be automatically computed by the integrator
+ * if {@link #setInitialStepSize(RealFieldElement) setInitialStepSize}
+ * is not called by the user.
+ * </p>
+ * @param minimalStep minimal step (must be positive even for backward
+ * integration), the last step can be smaller than this
+ * @param maximalStep maximal step (must be positive even for backward
+ * integration)
+ * @param absoluteTolerance allowed absolute error
+ * @param relativeTolerance allowed relative error
+ */
+ public void setStepSizeControl(final double minimalStep, final double maximalStep,
+ final double[] absoluteTolerance,
+ final double[] relativeTolerance) {
+
+ minStep = getField().getZero().add(FastMath.abs(minimalStep));
+ maxStep = getField().getZero().add(FastMath.abs(maximalStep));
+ initialStep = getField().getOne().negate();
+
+ scalAbsoluteTolerance = 0;
+ scalRelativeTolerance = 0;
+ vecAbsoluteTolerance = absoluteTolerance.clone();
+ vecRelativeTolerance = relativeTolerance.clone();
+
+ }
+
+ /** Set the initial step size.
+ * <p>This method allows the user to specify an initial positive
+ * step size instead of letting the integrator guess it by
+ * itself. If this method is not called before integration is
+ * started, the initial step size will be estimated by the
+ * integrator.</p>
+ * @param initialStepSize initial step size to use (must be positive even
+ * for backward integration ; providing a negative value or a value
+ * outside of the min/max step interval will lead the integrator to
+ * ignore the value and compute the initial step size by itself)
+ */
+ public void setInitialStepSize(final T initialStepSize) {
+ if (initialStepSize.subtract(minStep).getReal() < 0 ||
+ initialStepSize.subtract(maxStep).getReal() > 0) {
+ initialStep = getField().getOne().negate();
+ } else {
+ initialStep = initialStepSize;
+ }
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ protected void sanityChecks(final FieldODEState<T> eqn, final T t)
+ throws DimensionMismatchException, NumberIsTooSmallException {
+
+ super.sanityChecks(eqn, t);
+
+ mainSetDimension = eqn.getStateDimension();
+
+ if (vecAbsoluteTolerance != null && vecAbsoluteTolerance.length != mainSetDimension) {
+ throw new DimensionMismatchException(mainSetDimension, vecAbsoluteTolerance.length);
+ }
+
+ if (vecRelativeTolerance != null && vecRelativeTolerance.length != mainSetDimension) {
+ throw new DimensionMismatchException(mainSetDimension, vecRelativeTolerance.length);
+ }
+
+ }
+
+ /** Initialize the integration step.
+ * @param forward forward integration indicator
+ * @param order order of the method
+ * @param scale scaling vector for the state vector (can be shorter than state vector)
+ * @param state0 state at integration start time
+ * @param mapper mapper for all the equations
+ * @return first integration step
+ * @exception MaxCountExceededException if the number of functions evaluations is exceeded
+ * @exception DimensionMismatchException if arrays dimensions do not match equations settings
+ */
+ public T initializeStep(final boolean forward, final int order, final T[] scale,
+ final FieldODEStateAndDerivative<T> state0,
+ final FieldEquationsMapper<T> mapper)
+ throws MaxCountExceededException, DimensionMismatchException {
+
+ if (initialStep.getReal() > 0) {
+ // use the user provided value
+ return forward ? initialStep : initialStep.negate();
+ }
+
+ // very rough first guess : h = 0.01 * ||y/scale|| / ||y'/scale||
+ // this guess will be used to perform an Euler step
+ final T[] y0 = mapper.mapState(state0);
+ final T[] yDot0 = mapper.mapDerivative(state0);
+ T yOnScale2 = getField().getZero();
+ T yDotOnScale2 = getField().getZero();
+ for (int j = 0; j < scale.length; ++j) {
+ final T ratio = y0[j].divide(scale[j]);
+ yOnScale2 = yOnScale2.add(ratio.multiply(ratio));
+ final T ratioDot = yDot0[j].divide(scale[j]);
+ yDotOnScale2 = yDotOnScale2.add(ratioDot.multiply(ratioDot));
+ }
+
+ T h = (yOnScale2.getReal() < 1.0e-10 || yDotOnScale2.getReal() < 1.0e-10) ?
+ getField().getZero().add(1.0e-6) :
+ yOnScale2.divide(yDotOnScale2).sqrt().multiply(0.01);
+ if (! forward) {
+ h = h.negate();
+ }
+
+ // perform an Euler step using the preceding rough guess
+ final T[] y1 = MathArrays.buildArray(getField(), y0.length);
+ for (int j = 0; j < y0.length; ++j) {
+ y1[j] = y0[j].add(yDot0[j].multiply(h));
+ }
+ final T[] yDot1 = computeDerivatives(state0.getTime().add(h), y1);
+
+ // estimate the second derivative of the solution
+ T yDDotOnScale = getField().getZero();
+ for (int j = 0; j < scale.length; ++j) {
+ final T ratioDotDot = yDot1[j].subtract(yDot0[j]).divide(scale[j]);
+ yDDotOnScale = yDDotOnScale.add(ratioDotDot.multiply(ratioDotDot));
+ }
+ yDDotOnScale = yDDotOnScale.sqrt().divide(h);
+
+ // step size is computed such that
+ // h^order * max (||y'/tol||, ||y''/tol||) = 0.01
+ final T maxInv2 = MathUtils.max(yDotOnScale2.sqrt(), yDDotOnScale);
+ final T h1 = maxInv2.getReal() < 1.0e-15 ?
+ MathUtils.max(getField().getZero().add(1.0e-6), h.abs().multiply(0.001)) :
+ maxInv2.multiply(100).reciprocal().pow(1.0 / order);
+ h = MathUtils.min(h.abs().multiply(100), h1);
+ h = MathUtils.max(h, state0.getTime().abs().multiply(1.0e-12)); // avoids cancellation when computing t1 - t0
+ h = MathUtils.max(minStep, MathUtils.min(maxStep, h));
+ if (! forward) {
+ h = h.negate();
+ }
+
+ return h;
+
+ }
+
+ /** Filter the integration step.
+ * @param h signed step
+ * @param forward forward integration indicator
+ * @param acceptSmall if true, steps smaller than the minimal value
+ * are silently increased up to this value, if false such small
+ * steps generate an exception
+ * @return a bounded integration step (h if no bound is reach, or a bounded value)
+ * @exception NumberIsTooSmallException if the step is too small and acceptSmall is false
+ */
+ protected T filterStep(final T h, final boolean forward, final boolean acceptSmall)
+ throws NumberIsTooSmallException {
+
+ T filteredH = h;
+ if (h.abs().subtract(minStep).getReal() < 0) {
+ if (acceptSmall) {
+ filteredH = forward ? minStep : minStep.negate();
+ } else {
+ throw new NumberIsTooSmallException(LocalizedFormats.MINIMAL_STEPSIZE_REACHED_DURING_INTEGRATION,
+ h.abs().getReal(), minStep.getReal(), true);
+ }
+ }
+
+ if (filteredH.subtract(maxStep).getReal() > 0) {
+ filteredH = maxStep;
+ } else if (filteredH.add(maxStep).getReal() < 0) {
+ filteredH = maxStep.negate();
+ }
+
+ return filteredH;
+
+ }
+
+ /** Reset internal state to dummy values. */
+ protected void resetInternalState() {
+ setStepStart(null);
+ setStepSize(minStep.multiply(maxStep).sqrt());
+ }
+
+ /** Get the minimal step.
+ * @return minimal step
+ */
+ public T getMinStep() {
+ return minStep;
+ }
+
+ /** Get the maximal step.
+ * @return maximal step
+ */
+ public T getMaxStep() {
+ return maxStep;
+ }
+
+}
diff --git a/src/main/java/org/apache/commons/math3/ode/nonstiff/AdaptiveStepsizeIntegrator.java b/src/main/java/org/apache/commons/math3/ode/nonstiff/AdaptiveStepsizeIntegrator.java
new file mode 100644
index 0000000..ab79bbf
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/ode/nonstiff/AdaptiveStepsizeIntegrator.java
@@ -0,0 +1,375 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math3.ode.nonstiff;
+
+import org.apache.commons.math3.exception.DimensionMismatchException;
+import org.apache.commons.math3.exception.MaxCountExceededException;
+import org.apache.commons.math3.exception.NoBracketingException;
+import org.apache.commons.math3.exception.NumberIsTooSmallException;
+import org.apache.commons.math3.exception.util.LocalizedFormats;
+import org.apache.commons.math3.ode.AbstractIntegrator;
+import org.apache.commons.math3.ode.ExpandableStatefulODE;
+import org.apache.commons.math3.util.FastMath;
+
+/**
+ * This abstract class holds the common part of all adaptive
+ * stepsize integrators for Ordinary Differential Equations.
+ *
+ * <p>These algorithms perform integration with stepsize control, which
+ * means the user does not specify the integration step but rather a
+ * tolerance on error. The error threshold is computed as
+ * <pre>
+ * threshold_i = absTol_i + relTol_i * max (abs (ym), abs (ym+1))
+ * </pre>
+ * where absTol_i is the absolute tolerance for component i of the
+ * state vector and relTol_i is the relative tolerance for the same
+ * component. The user can also use only two scalar values absTol and
+ * relTol which will be used for all components.
+ * </p>
+ * <p>
+ * If the Ordinary Differential Equations is an {@link ExpandableStatefulODE
+ * extended ODE} rather than a {@link
+ * org.apache.commons.math3.ode.FirstOrderDifferentialEquations basic ODE}, then
+ * <em>only</em> the {@link ExpandableStatefulODE#getPrimaryState() primary part}
+ * of the state vector is used for stepsize control, not the complete state vector.
+ * </p>
+ *
+ * <p>If the estimated error for ym+1 is such that
+ * <pre>
+ * sqrt((sum (errEst_i / threshold_i)^2 ) / n) < 1
+ * </pre>
+ *
+ * (where n is the main set dimension) then the step is accepted,
+ * otherwise the step is rejected and a new attempt is made with a new
+ * stepsize.</p>
+ *
+ * @since 1.2
+ *
+ */
+
+public abstract class AdaptiveStepsizeIntegrator
+ extends AbstractIntegrator {
+
+ /** Allowed absolute scalar error. */
+ protected double scalAbsoluteTolerance;
+
+ /** Allowed relative scalar error. */
+ protected double scalRelativeTolerance;
+
+ /** Allowed absolute vectorial error. */
+ protected double[] vecAbsoluteTolerance;
+
+ /** Allowed relative vectorial error. */
+ protected double[] vecRelativeTolerance;
+
+ /** Main set dimension. */
+ protected int mainSetDimension;
+
+ /** User supplied initial step. */
+ private double initialStep;
+
+ /** Minimal step. */
+ private double minStep;
+
+ /** Maximal step. */
+ private double maxStep;
+
+ /** Build an integrator with the given stepsize bounds.
+ * The default step handler does nothing.
+ * @param name name of the method
+ * @param minStep minimal step (sign is irrelevant, regardless of
+ * integration direction, forward or backward), the last step can
+ * be smaller than this
+ * @param maxStep maximal step (sign is irrelevant, regardless of
+ * integration direction, forward or backward), the last step can
+ * be smaller than this
+ * @param scalAbsoluteTolerance allowed absolute error
+ * @param scalRelativeTolerance allowed relative error
+ */
+ public AdaptiveStepsizeIntegrator(final String name,
+ final double minStep, final double maxStep,
+ final double scalAbsoluteTolerance,
+ final double scalRelativeTolerance) {
+
+ super(name);
+ setStepSizeControl(minStep, maxStep, scalAbsoluteTolerance, scalRelativeTolerance);
+ resetInternalState();
+
+ }
+
+ /** Build an integrator with the given stepsize bounds.
+ * The default step handler does nothing.
+ * @param name name of the method
+ * @param minStep minimal step (sign is irrelevant, regardless of
+ * integration direction, forward or backward), the last step can
+ * be smaller than this
+ * @param maxStep maximal step (sign is irrelevant, regardless of
+ * integration direction, forward or backward), the last step can
+ * be smaller than this
+ * @param vecAbsoluteTolerance allowed absolute error
+ * @param vecRelativeTolerance allowed relative error
+ */
+ public AdaptiveStepsizeIntegrator(final String name,
+ final double minStep, final double maxStep,
+ final double[] vecAbsoluteTolerance,
+ final double[] vecRelativeTolerance) {
+
+ super(name);
+ setStepSizeControl(minStep, maxStep, vecAbsoluteTolerance, vecRelativeTolerance);
+ resetInternalState();
+
+ }
+
+ /** Set the adaptive step size control parameters.
+ * <p>
+ * A side effect of this method is to also reset the initial
+ * step so it will be automatically computed by the integrator
+ * if {@link #setInitialStepSize(double) setInitialStepSize}
+ * is not called by the user.
+ * </p>
+ * @param minimalStep minimal step (must be positive even for backward
+ * integration), the last step can be smaller than this
+ * @param maximalStep maximal step (must be positive even for backward
+ * integration)
+ * @param absoluteTolerance allowed absolute error
+ * @param relativeTolerance allowed relative error
+ */
+ public void setStepSizeControl(final double minimalStep, final double maximalStep,
+ final double absoluteTolerance,
+ final double relativeTolerance) {
+
+ minStep = FastMath.abs(minimalStep);
+ maxStep = FastMath.abs(maximalStep);
+ initialStep = -1;
+
+ scalAbsoluteTolerance = absoluteTolerance;
+ scalRelativeTolerance = relativeTolerance;
+ vecAbsoluteTolerance = null;
+ vecRelativeTolerance = null;
+
+ }
+
+ /** Set the adaptive step size control parameters.
+ * <p>
+ * A side effect of this method is to also reset the initial
+ * step so it will be automatically computed by the integrator
+ * if {@link #setInitialStepSize(double) setInitialStepSize}
+ * is not called by the user.
+ * </p>
+ * @param minimalStep minimal step (must be positive even for backward
+ * integration), the last step can be smaller than this
+ * @param maximalStep maximal step (must be positive even for backward
+ * integration)
+ * @param absoluteTolerance allowed absolute error
+ * @param relativeTolerance allowed relative error
+ */
+ public void setStepSizeControl(final double minimalStep, final double maximalStep,
+ final double[] absoluteTolerance,
+ final double[] relativeTolerance) {
+
+ minStep = FastMath.abs(minimalStep);
+ maxStep = FastMath.abs(maximalStep);
+ initialStep = -1;
+
+ scalAbsoluteTolerance = 0;
+ scalRelativeTolerance = 0;
+ vecAbsoluteTolerance = absoluteTolerance.clone();
+ vecRelativeTolerance = relativeTolerance.clone();
+
+ }
+
+ /** Set the initial step size.
+ * <p>This method allows the user to specify an initial positive
+ * step size instead of letting the integrator guess it by
+ * itself. If this method is not called before integration is
+ * started, the initial step size will be estimated by the
+ * integrator.</p>
+ * @param initialStepSize initial step size to use (must be positive even
+ * for backward integration ; providing a negative value or a value
+ * outside of the min/max step interval will lead the integrator to
+ * ignore the value and compute the initial step size by itself)
+ */
+ public void setInitialStepSize(final double initialStepSize) {
+ if ((initialStepSize < minStep) || (initialStepSize > maxStep)) {
+ initialStep = -1.0;
+ } else {
+ initialStep = initialStepSize;
+ }
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ protected void sanityChecks(final ExpandableStatefulODE equations, final double t)
+ throws DimensionMismatchException, NumberIsTooSmallException {
+
+ super.sanityChecks(equations, t);
+
+ mainSetDimension = equations.getPrimaryMapper().getDimension();
+
+ if ((vecAbsoluteTolerance != null) && (vecAbsoluteTolerance.length != mainSetDimension)) {
+ throw new DimensionMismatchException(mainSetDimension, vecAbsoluteTolerance.length);
+ }
+
+ if ((vecRelativeTolerance != null) && (vecRelativeTolerance.length != mainSetDimension)) {
+ throw new DimensionMismatchException(mainSetDimension, vecRelativeTolerance.length);
+ }
+
+ }
+
+ /** Initialize the integration step.
+ * @param forward forward integration indicator
+ * @param order order of the method
+ * @param scale scaling vector for the state vector (can be shorter than state vector)
+ * @param t0 start time
+ * @param y0 state vector at t0
+ * @param yDot0 first time derivative of y0
+ * @param y1 work array for a state vector
+ * @param yDot1 work array for the first time derivative of y1
+ * @return first integration step
+ * @exception MaxCountExceededException if the number of functions evaluations is exceeded
+ * @exception DimensionMismatchException if arrays dimensions do not match equations settings
+ */
+ public double initializeStep(final boolean forward, final int order, final double[] scale,
+ final double t0, final double[] y0, final double[] yDot0,
+ final double[] y1, final double[] yDot1)
+ throws MaxCountExceededException, DimensionMismatchException {
+
+ if (initialStep > 0) {
+ // use the user provided value
+ return forward ? initialStep : -initialStep;
+ }
+
+ // very rough first guess : h = 0.01 * ||y/scale|| / ||y'/scale||
+ // this guess will be used to perform an Euler step
+ double ratio;
+ double yOnScale2 = 0;
+ double yDotOnScale2 = 0;
+ for (int j = 0; j < scale.length; ++j) {
+ ratio = y0[j] / scale[j];
+ yOnScale2 += ratio * ratio;
+ ratio = yDot0[j] / scale[j];
+ yDotOnScale2 += ratio * ratio;
+ }
+
+ double h = ((yOnScale2 < 1.0e-10) || (yDotOnScale2 < 1.0e-10)) ?
+ 1.0e-6 : (0.01 * FastMath.sqrt(yOnScale2 / yDotOnScale2));
+ if (! forward) {
+ h = -h;
+ }
+
+ // perform an Euler step using the preceding rough guess
+ for (int j = 0; j < y0.length; ++j) {
+ y1[j] = y0[j] + h * yDot0[j];
+ }
+ computeDerivatives(t0 + h, y1, yDot1);
+
+ // estimate the second derivative of the solution
+ double yDDotOnScale = 0;
+ for (int j = 0; j < scale.length; ++j) {
+ ratio = (yDot1[j] - yDot0[j]) / scale[j];
+ yDDotOnScale += ratio * ratio;
+ }
+ yDDotOnScale = FastMath.sqrt(yDDotOnScale) / h;
+
+ // step size is computed such that
+ // h^order * max (||y'/tol||, ||y''/tol||) = 0.01
+ final double maxInv2 = FastMath.max(FastMath.sqrt(yDotOnScale2), yDDotOnScale);
+ final double h1 = (maxInv2 < 1.0e-15) ?
+ FastMath.max(1.0e-6, 0.001 * FastMath.abs(h)) :
+ FastMath.pow(0.01 / maxInv2, 1.0 / order);
+ h = FastMath.min(100.0 * FastMath.abs(h), h1);
+ h = FastMath.max(h, 1.0e-12 * FastMath.abs(t0)); // avoids cancellation when computing t1 - t0
+ if (h < getMinStep()) {
+ h = getMinStep();
+ }
+ if (h > getMaxStep()) {
+ h = getMaxStep();
+ }
+ if (! forward) {
+ h = -h;
+ }
+
+ return h;
+
+ }
+
+ /** Filter the integration step.
+ * @param h signed step
+ * @param forward forward integration indicator
+ * @param acceptSmall if true, steps smaller than the minimal value
+ * are silently increased up to this value, if false such small
+ * steps generate an exception
+ * @return a bounded integration step (h if no bound is reach, or a bounded value)
+ * @exception NumberIsTooSmallException if the step is too small and acceptSmall is false
+ */
+ protected double filterStep(final double h, final boolean forward, final boolean acceptSmall)
+ throws NumberIsTooSmallException {
+
+ double filteredH = h;
+ if (FastMath.abs(h) < minStep) {
+ if (acceptSmall) {
+ filteredH = forward ? minStep : -minStep;
+ } else {
+ throw new NumberIsTooSmallException(LocalizedFormats.MINIMAL_STEPSIZE_REACHED_DURING_INTEGRATION,
+ FastMath.abs(h), minStep, true);
+ }
+ }
+
+ if (filteredH > maxStep) {
+ filteredH = maxStep;
+ } else if (filteredH < -maxStep) {
+ filteredH = -maxStep;
+ }
+
+ return filteredH;
+
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public abstract void integrate (ExpandableStatefulODE equations, double t)
+ throws NumberIsTooSmallException, DimensionMismatchException,
+ MaxCountExceededException, NoBracketingException;
+
+ /** {@inheritDoc} */
+ @Override
+ public double getCurrentStepStart() {
+ return stepStart;
+ }
+
+ /** Reset internal state to dummy values. */
+ protected void resetInternalState() {
+ stepStart = Double.NaN;
+ stepSize = FastMath.sqrt(minStep * maxStep);
+ }
+
+ /** Get the minimal step.
+ * @return minimal step
+ */
+ public double getMinStep() {
+ return minStep;
+ }
+
+ /** Get the maximal step.
+ * @return maximal step
+ */
+ public double getMaxStep() {
+ return maxStep;
+ }
+
+}
diff --git a/src/main/java/org/apache/commons/math3/ode/nonstiff/ClassicalRungeKuttaFieldIntegrator.java b/src/main/java/org/apache/commons/math3/ode/nonstiff/ClassicalRungeKuttaFieldIntegrator.java
new file mode 100644
index 0000000..81e6d69
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/ode/nonstiff/ClassicalRungeKuttaFieldIntegrator.java
@@ -0,0 +1,111 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math3.ode.nonstiff;
+
+import org.apache.commons.math3.Field;
+import org.apache.commons.math3.RealFieldElement;
+import org.apache.commons.math3.ode.FieldEquationsMapper;
+import org.apache.commons.math3.ode.FieldODEStateAndDerivative;
+import org.apache.commons.math3.util.MathArrays;
+
+/**
+ * This class implements the classical fourth order Runge-Kutta
+ * integrator for Ordinary Differential Equations (it is the most
+ * often used Runge-Kutta method).
+ *
+ * <p>This method is an explicit Runge-Kutta method, its Butcher-array
+ * is the following one :
+ * <pre>
+ * 0 | 0 0 0 0
+ * 1/2 | 1/2 0 0 0
+ * 1/2 | 0 1/2 0 0
+ * 1 | 0 0 1 0
+ * |--------------------
+ * | 1/6 1/3 1/3 1/6
+ * </pre>
+ * </p>
+ *
+ * @see EulerFieldIntegrator
+ * @see GillFieldIntegrator
+ * @see MidpointFieldIntegrator
+ * @see ThreeEighthesFieldIntegrator
+ * @see LutherFieldIntegrator
+ * @param <T> the type of the field elements
+ * @since 3.6
+ */
+
+public class ClassicalRungeKuttaFieldIntegrator<T extends RealFieldElement<T>>
+ extends RungeKuttaFieldIntegrator<T> {
+
+ /** Simple constructor.
+ * Build a fourth-order Runge-Kutta integrator with the given step.
+ * @param field field to which the time and state vector elements belong
+ * @param step integration step
+ */
+ public ClassicalRungeKuttaFieldIntegrator(final Field<T> field, final T step) {
+ super(field, "classical Runge-Kutta", step);
+ }
+
+ /** {@inheritDoc} */
+ public T[] getC() {
+ final T[] c = MathArrays.buildArray(getField(), 3);
+ c[0] = getField().getOne().multiply(0.5);
+ c[1] = c[0];
+ c[2] = getField().getOne();
+ return c;
+ }
+
+ /** {@inheritDoc} */
+ public T[][] getA() {
+ final T[][] a = MathArrays.buildArray(getField(), 3, -1);
+ for (int i = 0; i < a.length; ++i) {
+ a[i] = MathArrays.buildArray(getField(), i + 1);
+ }
+ a[0][0] = fraction(1, 2);
+ a[1][0] = getField().getZero();
+ a[1][1] = a[0][0];
+ a[2][0] = getField().getZero();
+ a[2][1] = getField().getZero();
+ a[2][2] = getField().getOne();
+ return a;
+ }
+
+ /** {@inheritDoc} */
+ public T[] getB() {
+ final T[] b = MathArrays.buildArray(getField(), 4);
+ b[0] = fraction(1, 6);
+ b[1] = fraction(1, 3);
+ b[2] = b[1];
+ b[3] = b[0];
+ return b;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ protected ClassicalRungeKuttaFieldStepInterpolator<T>
+ createInterpolator(final boolean forward, T[][] yDotK,
+ final FieldODEStateAndDerivative<T> globalPreviousState,
+ final FieldODEStateAndDerivative<T> globalCurrentState,
+ final FieldEquationsMapper<T> mapper) {
+ return new ClassicalRungeKuttaFieldStepInterpolator<T>(getField(), forward, yDotK,
+ globalPreviousState, globalCurrentState,
+ globalPreviousState, globalCurrentState,
+ mapper);
+ }
+
+}
diff --git a/src/main/java/org/apache/commons/math3/ode/nonstiff/ClassicalRungeKuttaFieldStepInterpolator.java b/src/main/java/org/apache/commons/math3/ode/nonstiff/ClassicalRungeKuttaFieldStepInterpolator.java
new file mode 100644
index 0000000..4ad8f4e
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/ode/nonstiff/ClassicalRungeKuttaFieldStepInterpolator.java
@@ -0,0 +1,136 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math3.ode.nonstiff;
+
+import org.apache.commons.math3.Field;
+import org.apache.commons.math3.RealFieldElement;
+import org.apache.commons.math3.ode.FieldEquationsMapper;
+import org.apache.commons.math3.ode.FieldODEStateAndDerivative;
+
+/**
+ * This class implements a step interpolator for the classical fourth
+ * order Runge-Kutta integrator.
+ *
+ * <p>This interpolator allows to compute dense output inside the last
+ * step computed. The interpolation equation is consistent with the
+ * integration scheme :
+ * <ul>
+ * <li>Using reference point at step start:<br>
+ * y(t<sub>n</sub> + &theta; h) = y (t<sub>n</sub>)
+ * + &theta; (h/6) [ (6 - 9 &theta; + 4 &theta;<sup>2</sup>) y'<sub>1</sub>
+ * + ( 6 &theta; - 4 &theta;<sup>2</sup>) (y'<sub>2</sub> + y'<sub>3</sub>)
+ * + ( -3 &theta; + 4 &theta;<sup>2</sup>) y'<sub>4</sub>
+ * ]
+ * </li>
+ * <li>Using reference point at step end:<br>
+ * y(t<sub>n</sub> + &theta; h) = y (t<sub>n</sub> + h)
+ * + (1 - &theta;) (h/6) [ (-4 &theta;^2 + 5 &theta; - 1) y'<sub>1</sub>
+ * +(4 &theta;^2 - 2 &theta; - 2) (y'<sub>2</sub> + y'<sub>3</sub>)
+ * -(4 &theta;^2 + &theta; + 1) y'<sub>4</sub>
+ * ]
+ * </li>
+ * </ul>
+ * </p>
+ *
+ * where &theta; belongs to [0 ; 1] and where y'<sub>1</sub> to y'<sub>4</sub> are the four
+ * evaluations of the derivatives already computed during the
+ * step.</p>
+ *
+ * @see ClassicalRungeKuttaFieldIntegrator
+ * @param <T> the type of the field elements
+ * @since 3.6
+ */
+
+class ClassicalRungeKuttaFieldStepInterpolator<T extends RealFieldElement<T>>
+ extends RungeKuttaFieldStepInterpolator<T> {
+
+ /** Simple constructor.
+ * @param field field to which the time and state vector elements belong
+ * @param forward integration direction indicator
+ * @param yDotK slopes at the intermediate points
+ * @param globalPreviousState start of the global step
+ * @param globalCurrentState end of the global step
+ * @param softPreviousState start of the restricted step
+ * @param softCurrentState end of the restricted step
+ * @param mapper equations mapper for the all equations
+ */
+ ClassicalRungeKuttaFieldStepInterpolator(final Field<T> field, final boolean forward,
+ final T[][] yDotK,
+ final FieldODEStateAndDerivative<T> globalPreviousState,
+ final FieldODEStateAndDerivative<T> globalCurrentState,
+ final FieldODEStateAndDerivative<T> softPreviousState,
+ final FieldODEStateAndDerivative<T> softCurrentState,
+ final FieldEquationsMapper<T> mapper) {
+ super(field, forward, yDotK,
+ globalPreviousState, globalCurrentState, softPreviousState, softCurrentState,
+ mapper);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ protected ClassicalRungeKuttaFieldStepInterpolator<T> create(final Field<T> newField, final boolean newForward, final T[][] newYDotK,
+ final FieldODEStateAndDerivative<T> newGlobalPreviousState,
+ final FieldODEStateAndDerivative<T> newGlobalCurrentState,
+ final FieldODEStateAndDerivative<T> newSoftPreviousState,
+ final FieldODEStateAndDerivative<T> newSoftCurrentState,
+ final FieldEquationsMapper<T> newMapper) {
+ return new ClassicalRungeKuttaFieldStepInterpolator<T>(newField, newForward, newYDotK,
+ newGlobalPreviousState, newGlobalCurrentState,
+ newSoftPreviousState, newSoftCurrentState,
+ newMapper);
+ }
+
+ /** {@inheritDoc} */
+ @SuppressWarnings("unchecked")
+ @Override
+ protected FieldODEStateAndDerivative<T> computeInterpolatedStateAndDerivatives(final FieldEquationsMapper<T> mapper,
+ final T time, final T theta,
+ final T thetaH, final T oneMinusThetaH) {
+
+ final T one = time.getField().getOne();
+ final T oneMinusTheta = one.subtract(theta);
+ final T oneMinus2Theta = one.subtract(theta.multiply(2));
+ final T coeffDot1 = oneMinusTheta.multiply(oneMinus2Theta);
+ final T coeffDot23 = theta.multiply(oneMinusTheta).multiply(2);
+ final T coeffDot4 = theta.multiply(oneMinus2Theta).negate();
+ final T[] interpolatedState;
+ final T[] interpolatedDerivatives;
+
+ if (getGlobalPreviousState() != null && theta.getReal() <= 0.5) {
+ final T fourTheta2 = theta.multiply(theta).multiply(4);
+ final T s = thetaH.divide(6.0);
+ final T coeff1 = s.multiply(fourTheta2.subtract(theta.multiply(9)).add(6));
+ final T coeff23 = s.multiply(theta.multiply(6).subtract(fourTheta2));
+ final T coeff4 = s.multiply(fourTheta2.subtract(theta.multiply(3)));
+ interpolatedState = previousStateLinearCombination(coeff1, coeff23, coeff23, coeff4);
+ interpolatedDerivatives = derivativeLinearCombination(coeffDot1, coeffDot23, coeffDot23, coeffDot4);
+ } else {
+ final T fourTheta = theta.multiply(4);
+ final T s = oneMinusThetaH.divide(6);
+ final T coeff1 = s.multiply(theta.multiply(fourTheta.negate().add(5)).subtract(1));
+ final T coeff23 = s.multiply(theta.multiply(fourTheta.subtract(2)).subtract(2));
+ final T coeff4 = s.multiply(theta.multiply(fourTheta.negate().subtract(1)).subtract(1));
+ interpolatedState = currentStateLinearCombination(coeff1, coeff23, coeff23, coeff4);
+ interpolatedDerivatives = derivativeLinearCombination(coeffDot1, coeffDot23, coeffDot23, coeffDot4);
+ }
+
+ return new FieldODEStateAndDerivative<T>(time, interpolatedState, interpolatedDerivatives);
+
+ }
+
+}
diff --git a/src/main/java/org/apache/commons/math3/ode/nonstiff/ClassicalRungeKuttaIntegrator.java b/src/main/java/org/apache/commons/math3/ode/nonstiff/ClassicalRungeKuttaIntegrator.java
new file mode 100644
index 0000000..ca915f1
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/ode/nonstiff/ClassicalRungeKuttaIntegrator.java
@@ -0,0 +1,75 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math3.ode.nonstiff;
+
+
+/**
+ * This class implements the classical fourth order Runge-Kutta
+ * integrator for Ordinary Differential Equations (it is the most
+ * often used Runge-Kutta method).
+ *
+ * <p>This method is an explicit Runge-Kutta method, its Butcher-array
+ * is the following one :
+ * <pre>
+ * 0 | 0 0 0 0
+ * 1/2 | 1/2 0 0 0
+ * 1/2 | 0 1/2 0 0
+ * 1 | 0 0 1 0
+ * |--------------------
+ * | 1/6 1/3 1/3 1/6
+ * </pre>
+ * </p>
+ *
+ * @see EulerIntegrator
+ * @see GillIntegrator
+ * @see MidpointIntegrator
+ * @see ThreeEighthesIntegrator
+ * @see LutherIntegrator
+ * @since 1.2
+ */
+
+public class ClassicalRungeKuttaIntegrator extends RungeKuttaIntegrator {
+
+ /** Time steps Butcher array. */
+ private static final double[] STATIC_C = {
+ 1.0 / 2.0, 1.0 / 2.0, 1.0
+ };
+
+ /** Internal weights Butcher array. */
+ private static final double[][] STATIC_A = {
+ { 1.0 / 2.0 },
+ { 0.0, 1.0 / 2.0 },
+ { 0.0, 0.0, 1.0 }
+ };
+
+ /** Propagation weights Butcher array. */
+ private static final double[] STATIC_B = {
+ 1.0 / 6.0, 1.0 / 3.0, 1.0 / 3.0, 1.0 / 6.0
+ };
+
+ /** Simple constructor.
+ * Build a fourth-order Runge-Kutta integrator with the given
+ * step.
+ * @param step integration step
+ */
+ public ClassicalRungeKuttaIntegrator(final double step) {
+ super("classical Runge-Kutta", STATIC_C, STATIC_A, STATIC_B,
+ new ClassicalRungeKuttaStepInterpolator(), step);
+ }
+
+}
diff --git a/src/main/java/org/apache/commons/math3/ode/nonstiff/ClassicalRungeKuttaStepInterpolator.java b/src/main/java/org/apache/commons/math3/ode/nonstiff/ClassicalRungeKuttaStepInterpolator.java
new file mode 100644
index 0000000..296f5b5
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/ode/nonstiff/ClassicalRungeKuttaStepInterpolator.java
@@ -0,0 +1,136 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math3.ode.nonstiff;
+
+import org.apache.commons.math3.ode.sampling.StepInterpolator;
+
+/**
+ * This class implements a step interpolator for the classical fourth
+ * order Runge-Kutta integrator.
+ *
+ * <p>This interpolator allows to compute dense output inside the last
+ * step computed. The interpolation equation is consistent with the
+ * integration scheme :
+ * <ul>
+ * <li>Using reference point at step start:<br>
+ * y(t<sub>n</sub> + &theta; h) = y (t<sub>n</sub>)
+ * + &theta; (h/6) [ (6 - 9 &theta; + 4 &theta;<sup>2</sup>) y'<sub>1</sub>
+ * + ( 6 &theta; - 4 &theta;<sup>2</sup>) (y'<sub>2</sub> + y'<sub>3</sub>)
+ * + ( -3 &theta; + 4 &theta;<sup>2</sup>) y'<sub>4</sub>
+ * ]
+ * </li>
+ * <li>Using reference point at step end:<br>
+ * y(t<sub>n</sub> + &theta; h) = y (t<sub>n</sub> + h)
+ * + (1 - &theta;) (h/6) [ (-4 &theta;^2 + 5 &theta; - 1) y'<sub>1</sub>
+ * +(4 &theta;^2 - 2 &theta; - 2) (y'<sub>2</sub> + y'<sub>3</sub>)
+ * -(4 &theta;^2 + &theta; + 1) y'<sub>4</sub>
+ * ]
+ * </li>
+ * </ul>
+ * </p>
+ *
+ * where &theta; belongs to [0 ; 1] and where y'<sub>1</sub> to y'<sub>4</sub> are the four
+ * evaluations of the derivatives already computed during the
+ * step.</p>
+ *
+ * @see ClassicalRungeKuttaIntegrator
+ * @since 1.2
+ */
+
+class ClassicalRungeKuttaStepInterpolator
+ extends RungeKuttaStepInterpolator {
+
+ /** Serializable version identifier. */
+ private static final long serialVersionUID = 20111120L;
+
+ /** Simple constructor.
+ * This constructor builds an instance that is not usable yet, the
+ * {@link RungeKuttaStepInterpolator#reinitialize} method should be
+ * called before using the instance in order to initialize the
+ * internal arrays. This constructor is used only in order to delay
+ * the initialization in some cases. The {@link RungeKuttaIntegrator}
+ * class uses the prototyping design pattern to create the step
+ * interpolators by cloning an uninitialized model and latter initializing
+ * the copy.
+ */
+ // CHECKSTYLE: stop RedundantModifier
+ // the public modifier here is needed for serialization
+ public ClassicalRungeKuttaStepInterpolator() {
+ }
+ // CHECKSTYLE: resume RedundantModifier
+
+ /** Copy constructor.
+ * @param interpolator interpolator to copy from. The copy is a deep
+ * copy: its arrays are separated from the original arrays of the
+ * instance
+ */
+ ClassicalRungeKuttaStepInterpolator(final ClassicalRungeKuttaStepInterpolator interpolator) {
+ super(interpolator);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ protected StepInterpolator doCopy() {
+ return new ClassicalRungeKuttaStepInterpolator(this);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ protected void computeInterpolatedStateAndDerivatives(final double theta,
+ final double oneMinusThetaH) {
+
+ final double oneMinusTheta = 1 - theta;
+ final double oneMinus2Theta = 1 - 2 * theta;
+ final double coeffDot1 = oneMinusTheta * oneMinus2Theta;
+ final double coeffDot23 = 2 * theta * oneMinusTheta;
+ final double coeffDot4 = -theta * oneMinus2Theta;
+ if ((previousState != null) && (theta <= 0.5)) {
+ final double fourTheta2 = 4 * theta * theta;
+ final double s = theta * h / 6.0;
+ final double coeff1 = s * ( 6 - 9 * theta + fourTheta2);
+ final double coeff23 = s * ( 6 * theta - fourTheta2);
+ final double coeff4 = s * (-3 * theta + fourTheta2);
+ for (int i = 0; i < interpolatedState.length; ++i) {
+ final double yDot1 = yDotK[0][i];
+ final double yDot23 = yDotK[1][i] + yDotK[2][i];
+ final double yDot4 = yDotK[3][i];
+ interpolatedState[i] =
+ previousState[i] + coeff1 * yDot1 + coeff23 * yDot23 + coeff4 * yDot4;
+ interpolatedDerivatives[i] =
+ coeffDot1 * yDot1 + coeffDot23 * yDot23 + coeffDot4 * yDot4;
+ }
+ } else {
+ final double fourTheta = 4 * theta;
+ final double s = oneMinusThetaH / 6.0;
+ final double coeff1 = s * ((-fourTheta + 5) * theta - 1);
+ final double coeff23 = s * (( fourTheta - 2) * theta - 2);
+ final double coeff4 = s * ((-fourTheta - 1) * theta - 1);
+ for (int i = 0; i < interpolatedState.length; ++i) {
+ final double yDot1 = yDotK[0][i];
+ final double yDot23 = yDotK[1][i] + yDotK[2][i];
+ final double yDot4 = yDotK[3][i];
+ interpolatedState[i] =
+ currentState[i] + coeff1 * yDot1 + coeff23 * yDot23 + coeff4 * yDot4;
+ interpolatedDerivatives[i] =
+ coeffDot1 * yDot1 + coeffDot23 * yDot23 + coeffDot4 * yDot4;
+ }
+ }
+
+ }
+
+}
diff --git a/src/main/java/org/apache/commons/math3/ode/nonstiff/DormandPrince54FieldIntegrator.java b/src/main/java/org/apache/commons/math3/ode/nonstiff/DormandPrince54FieldIntegrator.java
new file mode 100644
index 0000000..5cdd828
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/ode/nonstiff/DormandPrince54FieldIntegrator.java
@@ -0,0 +1,232 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math3.ode.nonstiff;
+
+import org.apache.commons.math3.Field;
+import org.apache.commons.math3.RealFieldElement;
+import org.apache.commons.math3.ode.FieldEquationsMapper;
+import org.apache.commons.math3.ode.FieldODEStateAndDerivative;
+import org.apache.commons.math3.util.MathArrays;
+import org.apache.commons.math3.util.MathUtils;
+
+
+/**
+ * This class implements the 5(4) Dormand-Prince integrator for Ordinary
+ * Differential Equations.
+
+ * <p>This integrator is an embedded Runge-Kutta integrator
+ * of order 5(4) used in local extrapolation mode (i.e. the solution
+ * is computed using the high order formula) with stepsize control
+ * (and automatic step initialization) and continuous output. This
+ * method uses 7 functions evaluations per step. However, since this
+ * is an <i>fsal</i>, the last evaluation of one step is the same as
+ * the first evaluation of the next step and hence can be avoided. So
+ * the cost is really 6 functions evaluations per step.</p>
+ *
+ * <p>This method has been published (whithout the continuous output
+ * that was added by Shampine in 1986) in the following article :
+ * <pre>
+ * A family of embedded Runge-Kutta formulae
+ * J. R. Dormand and P. J. Prince
+ * Journal of Computational and Applied Mathematics
+ * volume 6, no 1, 1980, pp. 19-26
+ * </pre></p>
+ *
+ * @param <T> the type of the field elements
+ * @since 3.6
+ */
+
+public class DormandPrince54FieldIntegrator<T extends RealFieldElement<T>>
+ extends EmbeddedRungeKuttaFieldIntegrator<T> {
+
+ /** Integrator method name. */
+ private static final String METHOD_NAME = "Dormand-Prince 5(4)";
+
+ /** Error array, element 1. */
+ private final T e1;
+
+ // element 2 is zero, so it is neither stored nor used
+
+ /** Error array, element 3. */
+ private final T e3;
+
+ /** Error array, element 4. */
+ private final T e4;
+
+ /** Error array, element 5. */
+ private final T e5;
+
+ /** Error array, element 6. */
+ private final T e6;
+
+ /** Error array, element 7. */
+ private final T e7;
+
+ /** Simple constructor.
+ * Build a fifth order Dormand-Prince integrator with the given step bounds
+ * @param field field to which the time and state vector elements belong
+ * @param minStep minimal step (sign is irrelevant, regardless of
+ * integration direction, forward or backward), the last step can
+ * be smaller than this
+ * @param maxStep maximal step (sign is irrelevant, regardless of
+ * integration direction, forward or backward), the last step can
+ * be smaller than this
+ * @param scalAbsoluteTolerance allowed absolute error
+ * @param scalRelativeTolerance allowed relative error
+ */
+ public DormandPrince54FieldIntegrator(final Field<T> field,
+ final double minStep, final double maxStep,
+ final double scalAbsoluteTolerance,
+ final double scalRelativeTolerance) {
+ super(field, METHOD_NAME, 6,
+ minStep, maxStep, scalAbsoluteTolerance, scalRelativeTolerance);
+ e1 = fraction( 71, 57600);
+ e3 = fraction( -71, 16695);
+ e4 = fraction( 71, 1920);
+ e5 = fraction(-17253, 339200);
+ e6 = fraction( 22, 525);
+ e7 = fraction( -1, 40);
+ }
+
+ /** Simple constructor.
+ * Build a fifth order Dormand-Prince integrator with the given step bounds
+ * @param field field to which the time and state vector elements belong
+ * @param minStep minimal step (sign is irrelevant, regardless of
+ * integration direction, forward or backward), the last step can
+ * be smaller than this
+ * @param maxStep maximal step (sign is irrelevant, regardless of
+ * integration direction, forward or backward), the last step can
+ * be smaller than this
+ * @param vecAbsoluteTolerance allowed absolute error
+ * @param vecRelativeTolerance allowed relative error
+ */
+ public DormandPrince54FieldIntegrator(final Field<T> field,
+ final double minStep, final double maxStep,
+ final double[] vecAbsoluteTolerance,
+ final double[] vecRelativeTolerance) {
+ super(field, METHOD_NAME, 6,
+ minStep, maxStep, vecAbsoluteTolerance, vecRelativeTolerance);
+ e1 = fraction( 71, 57600);
+ e3 = fraction( -71, 16695);
+ e4 = fraction( 71, 1920);
+ e5 = fraction(-17253, 339200);
+ e6 = fraction( 22, 525);
+ e7 = fraction( -1, 40);
+ }
+
+ /** {@inheritDoc} */
+ public T[] getC() {
+ final T[] c = MathArrays.buildArray(getField(), 6);
+ c[0] = fraction(1, 5);
+ c[1] = fraction(3, 10);
+ c[2] = fraction(4, 5);
+ c[3] = fraction(8, 9);
+ c[4] = getField().getOne();
+ c[5] = getField().getOne();
+ return c;
+ }
+
+ /** {@inheritDoc} */
+ public T[][] getA() {
+ final T[][] a = MathArrays.buildArray(getField(), 6, -1);
+ for (int i = 0; i < a.length; ++i) {
+ a[i] = MathArrays.buildArray(getField(), i + 1);
+ }
+ a[0][0] = fraction( 1, 5);
+ a[1][0] = fraction( 3, 40);
+ a[1][1] = fraction( 9, 40);
+ a[2][0] = fraction( 44, 45);
+ a[2][1] = fraction( -56, 15);
+ a[2][2] = fraction( 32, 9);
+ a[3][0] = fraction( 19372, 6561);
+ a[3][1] = fraction(-25360, 2187);
+ a[3][2] = fraction( 64448, 6561);
+ a[3][3] = fraction( -212, 729);
+ a[4][0] = fraction( 9017, 3168);
+ a[4][1] = fraction( -355, 33);
+ a[4][2] = fraction( 46732, 5247);
+ a[4][3] = fraction( 49, 176);
+ a[4][4] = fraction( -5103, 18656);
+ a[5][0] = fraction( 35, 384);
+ a[5][1] = getField().getZero();
+ a[5][2] = fraction( 500, 1113);
+ a[5][3] = fraction( 125, 192);
+ a[5][4] = fraction( -2187, 6784);
+ a[5][5] = fraction( 11, 84);
+ return a;
+ }
+
+ /** {@inheritDoc} */
+ public T[] getB() {
+ final T[] b = MathArrays.buildArray(getField(), 7);
+ b[0] = fraction( 35, 384);
+ b[1] = getField().getZero();
+ b[2] = fraction( 500, 1113);
+ b[3] = fraction( 125, 192);
+ b[4] = fraction(-2187, 6784);
+ b[5] = fraction( 11, 84);
+ b[6] = getField().getZero();
+ return b;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ protected DormandPrince54FieldStepInterpolator<T>
+ createInterpolator(final boolean forward, T[][] yDotK,
+ final FieldODEStateAndDerivative<T> globalPreviousState,
+ final FieldODEStateAndDerivative<T> globalCurrentState, final FieldEquationsMapper<T> mapper) {
+ return new DormandPrince54FieldStepInterpolator<T>(getField(), forward, yDotK,
+ globalPreviousState, globalCurrentState,
+ globalPreviousState, globalCurrentState,
+ mapper);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public int getOrder() {
+ return 5;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ protected T estimateError(final T[][] yDotK, final T[] y0, final T[] y1, final T h) {
+
+ T error = getField().getZero();
+
+ for (int j = 0; j < mainSetDimension; ++j) {
+ final T errSum = yDotK[0][j].multiply(e1).
+ add(yDotK[2][j].multiply(e3)).
+ add(yDotK[3][j].multiply(e4)).
+ add(yDotK[4][j].multiply(e5)).
+ add(yDotK[5][j].multiply(e6)).
+ add(yDotK[6][j].multiply(e7));
+
+ final T yScale = MathUtils.max(y0[j].abs(), y1[j].abs());
+ final T tol = (vecAbsoluteTolerance == null) ?
+ yScale.multiply(scalRelativeTolerance).add(scalAbsoluteTolerance) :
+ yScale.multiply(vecRelativeTolerance[j]).add(vecAbsoluteTolerance[j]);
+ final T ratio = h.multiply(errSum).divide(tol);
+ error = error.add(ratio.multiply(ratio));
+
+ }
+
+ return error.divide(mainSetDimension).sqrt();
+
+ }
+
+}
diff --git a/src/main/java/org/apache/commons/math3/ode/nonstiff/DormandPrince54FieldStepInterpolator.java b/src/main/java/org/apache/commons/math3/ode/nonstiff/DormandPrince54FieldStepInterpolator.java
new file mode 100644
index 0000000..62a6fa3
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/ode/nonstiff/DormandPrince54FieldStepInterpolator.java
@@ -0,0 +1,249 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math3.ode.nonstiff;
+
+import org.apache.commons.math3.Field;
+import org.apache.commons.math3.RealFieldElement;
+import org.apache.commons.math3.ode.FieldEquationsMapper;
+import org.apache.commons.math3.ode.FieldODEStateAndDerivative;
+
+/**
+ * This class represents an interpolator over the last step during an
+ * ODE integration for the 5(4) Dormand-Prince integrator.
+ *
+ * @see DormandPrince54Integrator
+ *
+ * @param <T> the type of the field elements
+ * @since 3.6
+ */
+
+class DormandPrince54FieldStepInterpolator<T extends RealFieldElement<T>>
+ extends RungeKuttaFieldStepInterpolator<T> {
+
+ /** Last row of the Butcher-array internal weights, element 0. */
+ private final T a70;
+
+ // element 1 is zero, so it is neither stored nor used
+
+ /** Last row of the Butcher-array internal weights, element 2. */
+ private final T a72;
+
+ /** Last row of the Butcher-array internal weights, element 3. */
+ private final T a73;
+
+ /** Last row of the Butcher-array internal weights, element 4. */
+ private final T a74;
+
+ /** Last row of the Butcher-array internal weights, element 5. */
+ private final T a75;
+
+ /** Shampine (1986) Dense output, element 0. */
+ private final T d0;
+
+ // element 1 is zero, so it is neither stored nor used
+
+ /** Shampine (1986) Dense output, element 2. */
+ private final T d2;
+
+ /** Shampine (1986) Dense output, element 3. */
+ private final T d3;
+
+ /** Shampine (1986) Dense output, element 4. */
+ private final T d4;
+
+ /** Shampine (1986) Dense output, element 5. */
+ private final T d5;
+
+ /** Shampine (1986) Dense output, element 6. */
+ private final T d6;
+
+ /** Simple constructor.
+ * @param field field to which the time and state vector elements belong
+ * @param forward integration direction indicator
+ * @param yDotK slopes at the intermediate points
+ * @param globalPreviousState start of the global step
+ * @param globalCurrentState end of the global step
+ * @param softPreviousState start of the restricted step
+ * @param softCurrentState end of the restricted step
+ * @param mapper equations mapper for the all equations
+ */
+ DormandPrince54FieldStepInterpolator(final Field<T> field, final boolean forward,
+ final T[][] yDotK,
+ final FieldODEStateAndDerivative<T> globalPreviousState,
+ final FieldODEStateAndDerivative<T> globalCurrentState,
+ final FieldODEStateAndDerivative<T> softPreviousState,
+ final FieldODEStateAndDerivative<T> softCurrentState,
+ final FieldEquationsMapper<T> mapper) {
+ super(field, forward, yDotK,
+ globalPreviousState, globalCurrentState, softPreviousState, softCurrentState,
+ mapper);
+ final T one = field.getOne();
+ a70 = one.multiply( 35.0).divide( 384.0);
+ a72 = one.multiply( 500.0).divide(1113.0);
+ a73 = one.multiply( 125.0).divide( 192.0);
+ a74 = one.multiply(-2187.0).divide(6784.0);
+ a75 = one.multiply( 11.0).divide( 84.0);
+ d0 = one.multiply(-12715105075.0).divide( 11282082432.0);
+ d2 = one.multiply( 87487479700.0).divide( 32700410799.0);
+ d3 = one.multiply(-10690763975.0).divide( 1880347072.0);
+ d4 = one.multiply(701980252875.0).divide(199316789632.0);
+ d5 = one.multiply( -1453857185.0).divide( 822651844.0);
+ d6 = one.multiply( 69997945.0).divide( 29380423.0);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ protected DormandPrince54FieldStepInterpolator<T> create(final Field<T> newField, final boolean newForward, final T[][] newYDotK,
+ final FieldODEStateAndDerivative<T> newGlobalPreviousState,
+ final FieldODEStateAndDerivative<T> newGlobalCurrentState,
+ final FieldODEStateAndDerivative<T> newSoftPreviousState,
+ final FieldODEStateAndDerivative<T> newSoftCurrentState,
+ final FieldEquationsMapper<T> newMapper) {
+ return new DormandPrince54FieldStepInterpolator<T>(newField, newForward, newYDotK,
+ newGlobalPreviousState, newGlobalCurrentState,
+ newSoftPreviousState, newSoftCurrentState,
+ newMapper);
+ }
+ /** {@inheritDoc} */
+ @SuppressWarnings("unchecked")
+ @Override
+ protected FieldODEStateAndDerivative<T> computeInterpolatedStateAndDerivatives(final FieldEquationsMapper<T> mapper,
+ final T time, final T theta,
+ final T thetaH, final T oneMinusThetaH) {
+
+ // interpolate
+ final T one = time.getField().getOne();
+ final T eta = one.subtract(theta);
+ final T twoTheta = theta.multiply(2);
+ final T dot2 = one.subtract(twoTheta);
+ final T dot3 = theta.multiply(theta.multiply(-3).add(2));
+ final T dot4 = twoTheta.multiply(theta.multiply(twoTheta.subtract(3)).add(1));
+ final T[] interpolatedState;
+ final T[] interpolatedDerivatives;
+ if (getGlobalPreviousState() != null && theta.getReal() <= 0.5) {
+ final T f1 = thetaH;
+ final T f2 = f1.multiply(eta);
+ final T f3 = f2.multiply(theta);
+ final T f4 = f3.multiply(eta);
+ final T coeff0 = f1.multiply(a70).
+ subtract(f2.multiply(a70.subtract(1))).
+ add(f3.multiply(a70.multiply(2).subtract(1))).
+ add(f4.multiply(d0));
+ final T coeff1 = time.getField().getZero();
+ final T coeff2 = f1.multiply(a72).
+ subtract(f2.multiply(a72)).
+ add(f3.multiply(a72.multiply(2))).
+ add(f4.multiply(d2));
+ final T coeff3 = f1.multiply(a73).
+ subtract(f2.multiply(a73)).
+ add(f3.multiply(a73.multiply(2))).
+ add(f4.multiply(d3));
+ final T coeff4 = f1.multiply(a74).
+ subtract(f2.multiply(a74)).
+ add(f3.multiply(a74.multiply(2))).
+ add(f4.multiply(d4));
+ final T coeff5 = f1.multiply(a75).
+ subtract(f2.multiply(a75)).
+ add(f3.multiply(a75.multiply(2))).
+ add(f4.multiply(d5));
+ final T coeff6 = f4.multiply(d6).subtract(f3);
+ final T coeffDot0 = a70.
+ subtract(dot2.multiply(a70.subtract(1))).
+ add(dot3.multiply(a70.multiply(2).subtract(1))).
+ add(dot4.multiply(d0));
+ final T coeffDot1 = time.getField().getZero();
+ final T coeffDot2 = a72.
+ subtract(dot2.multiply(a72)).
+ add(dot3.multiply(a72.multiply(2))).
+ add(dot4.multiply(d2));
+ final T coeffDot3 = a73.
+ subtract(dot2.multiply(a73)).
+ add(dot3.multiply(a73.multiply(2))).
+ add(dot4.multiply(d3));
+ final T coeffDot4 = a74.
+ subtract(dot2.multiply(a74)).
+ add(dot3.multiply(a74.multiply(2))).
+ add(dot4.multiply(d4));
+ final T coeffDot5 = a75.
+ subtract(dot2.multiply(a75)).
+ add(dot3.multiply(a75.multiply(2))).
+ add(dot4.multiply(d5));
+ final T coeffDot6 = dot4.multiply(d6).subtract(dot3);
+ interpolatedState = previousStateLinearCombination(coeff0, coeff1, coeff2, coeff3,
+ coeff4, coeff5, coeff6);
+ interpolatedDerivatives = derivativeLinearCombination(coeffDot0, coeffDot1, coeffDot2, coeffDot3,
+ coeffDot4, coeffDot5, coeffDot6);
+ } else {
+ final T f1 = oneMinusThetaH.negate();
+ final T f2 = oneMinusThetaH.multiply(theta);
+ final T f3 = f2.multiply(theta);
+ final T f4 = f3.multiply(eta);
+ final T coeff0 = f1.multiply(a70).
+ subtract(f2.multiply(a70.subtract(1))).
+ add(f3.multiply(a70.multiply(2).subtract(1))).
+ add(f4.multiply(d0));
+ final T coeff1 = time.getField().getZero();
+ final T coeff2 = f1.multiply(a72).
+ subtract(f2.multiply(a72)).
+ add(f3.multiply(a72.multiply(2))).
+ add(f4.multiply(d2));
+ final T coeff3 = f1.multiply(a73).
+ subtract(f2.multiply(a73)).
+ add(f3.multiply(a73.multiply(2))).
+ add(f4.multiply(d3));
+ final T coeff4 = f1.multiply(a74).
+ subtract(f2.multiply(a74)).
+ add(f3.multiply(a74.multiply(2))).
+ add(f4.multiply(d4));
+ final T coeff5 = f1.multiply(a75).
+ subtract(f2.multiply(a75)).
+ add(f3.multiply(a75.multiply(2))).
+ add(f4.multiply(d5));
+ final T coeff6 = f4.multiply(d6).subtract(f3);
+ final T coeffDot0 = a70.
+ subtract(dot2.multiply(a70.subtract(1))).
+ add(dot3.multiply(a70.multiply(2).subtract(1))).
+ add(dot4.multiply(d0));
+ final T coeffDot1 = time.getField().getZero();
+ final T coeffDot2 = a72.
+ subtract(dot2.multiply(a72)).
+ add(dot3.multiply(a72.multiply(2))).
+ add(dot4.multiply(d2));
+ final T coeffDot3 = a73.
+ subtract(dot2.multiply(a73)).
+ add(dot3.multiply(a73.multiply(2))).
+ add(dot4.multiply(d3));
+ final T coeffDot4 = a74.
+ subtract(dot2.multiply(a74)).
+ add(dot3.multiply(a74.multiply(2))).
+ add(dot4.multiply(d4));
+ final T coeffDot5 = a75.
+ subtract(dot2.multiply(a75)).
+ add(dot3.multiply(a75.multiply(2))).
+ add(dot4.multiply(d5));
+ final T coeffDot6 = dot4.multiply(d6).subtract(dot3);
+ interpolatedState = currentStateLinearCombination(coeff0, coeff1, coeff2, coeff3,
+ coeff4, coeff5, coeff6);
+ interpolatedDerivatives = derivativeLinearCombination(coeffDot0, coeffDot1, coeffDot2, coeffDot3,
+ coeffDot4, coeffDot5, coeffDot6);
+ }
+ return new FieldODEStateAndDerivative<T>(time, interpolatedState, interpolatedDerivatives);
+
+ }
+
+}
diff --git a/src/main/java/org/apache/commons/math3/ode/nonstiff/DormandPrince54Integrator.java b/src/main/java/org/apache/commons/math3/ode/nonstiff/DormandPrince54Integrator.java
new file mode 100644
index 0000000..45229b2
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/ode/nonstiff/DormandPrince54Integrator.java
@@ -0,0 +1,161 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math3.ode.nonstiff;
+
+import org.apache.commons.math3.util.FastMath;
+
+
+/**
+ * This class implements the 5(4) Dormand-Prince integrator for Ordinary
+ * Differential Equations.
+
+ * <p>This integrator is an embedded Runge-Kutta integrator
+ * of order 5(4) used in local extrapolation mode (i.e. the solution
+ * is computed using the high order formula) with stepsize control
+ * (and automatic step initialization) and continuous output. This
+ * method uses 7 functions evaluations per step. However, since this
+ * is an <i>fsal</i>, the last evaluation of one step is the same as
+ * the first evaluation of the next step and hence can be avoided. So
+ * the cost is really 6 functions evaluations per step.</p>
+ *
+ * <p>This method has been published (whithout the continuous output
+ * that was added by Shampine in 1986) in the following article :
+ * <pre>
+ * A family of embedded Runge-Kutta formulae
+ * J. R. Dormand and P. J. Prince
+ * Journal of Computational and Applied Mathematics
+ * volume 6, no 1, 1980, pp. 19-26
+ * </pre></p>
+ *
+ * @since 1.2
+ */
+
+public class DormandPrince54Integrator extends EmbeddedRungeKuttaIntegrator {
+
+ /** Integrator method name. */
+ private static final String METHOD_NAME = "Dormand-Prince 5(4)";
+
+ /** Time steps Butcher array. */
+ private static final double[] STATIC_C = {
+ 1.0/5.0, 3.0/10.0, 4.0/5.0, 8.0/9.0, 1.0, 1.0
+ };
+
+ /** Internal weights Butcher array. */
+ private static final double[][] STATIC_A = {
+ {1.0/5.0},
+ {3.0/40.0, 9.0/40.0},
+ {44.0/45.0, -56.0/15.0, 32.0/9.0},
+ {19372.0/6561.0, -25360.0/2187.0, 64448.0/6561.0, -212.0/729.0},
+ {9017.0/3168.0, -355.0/33.0, 46732.0/5247.0, 49.0/176.0, -5103.0/18656.0},
+ {35.0/384.0, 0.0, 500.0/1113.0, 125.0/192.0, -2187.0/6784.0, 11.0/84.0}
+ };
+
+ /** Propagation weights Butcher array. */
+ private static final double[] STATIC_B = {
+ 35.0/384.0, 0.0, 500.0/1113.0, 125.0/192.0, -2187.0/6784.0, 11.0/84.0, 0.0
+ };
+
+ /** Error array, element 1. */
+ private static final double E1 = 71.0 / 57600.0;
+
+ // element 2 is zero, so it is neither stored nor used
+
+ /** Error array, element 3. */
+ private static final double E3 = -71.0 / 16695.0;
+
+ /** Error array, element 4. */
+ private static final double E4 = 71.0 / 1920.0;
+
+ /** Error array, element 5. */
+ private static final double E5 = -17253.0 / 339200.0;
+
+ /** Error array, element 6. */
+ private static final double E6 = 22.0 / 525.0;
+
+ /** Error array, element 7. */
+ private static final double E7 = -1.0 / 40.0;
+
+ /** Simple constructor.
+ * Build a fifth order Dormand-Prince integrator with the given step bounds
+ * @param minStep minimal step (sign is irrelevant, regardless of
+ * integration direction, forward or backward), the last step can
+ * be smaller than this
+ * @param maxStep maximal step (sign is irrelevant, regardless of
+ * integration direction, forward or backward), the last step can
+ * be smaller than this
+ * @param scalAbsoluteTolerance allowed absolute error
+ * @param scalRelativeTolerance allowed relative error
+ */
+ public DormandPrince54Integrator(final double minStep, final double maxStep,
+ final double scalAbsoluteTolerance,
+ final double scalRelativeTolerance) {
+ super(METHOD_NAME, true, STATIC_C, STATIC_A, STATIC_B, new DormandPrince54StepInterpolator(),
+ minStep, maxStep, scalAbsoluteTolerance, scalRelativeTolerance);
+ }
+
+ /** Simple constructor.
+ * Build a fifth order Dormand-Prince integrator with the given step bounds
+ * @param minStep minimal step (sign is irrelevant, regardless of
+ * integration direction, forward or backward), the last step can
+ * be smaller than this
+ * @param maxStep maximal step (sign is irrelevant, regardless of
+ * integration direction, forward or backward), the last step can
+ * be smaller than this
+ * @param vecAbsoluteTolerance allowed absolute error
+ * @param vecRelativeTolerance allowed relative error
+ */
+ public DormandPrince54Integrator(final double minStep, final double maxStep,
+ final double[] vecAbsoluteTolerance,
+ final double[] vecRelativeTolerance) {
+ super(METHOD_NAME, true, STATIC_C, STATIC_A, STATIC_B, new DormandPrince54StepInterpolator(),
+ minStep, maxStep, vecAbsoluteTolerance, vecRelativeTolerance);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public int getOrder() {
+ return 5;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ protected double estimateError(final double[][] yDotK,
+ final double[] y0, final double[] y1,
+ final double h) {
+
+ double error = 0;
+
+ for (int j = 0; j < mainSetDimension; ++j) {
+ final double errSum = E1 * yDotK[0][j] + E3 * yDotK[2][j] +
+ E4 * yDotK[3][j] + E5 * yDotK[4][j] +
+ E6 * yDotK[5][j] + E7 * yDotK[6][j];
+
+ final double yScale = FastMath.max(FastMath.abs(y0[j]), FastMath.abs(y1[j]));
+ final double tol = (vecAbsoluteTolerance == null) ?
+ (scalAbsoluteTolerance + scalRelativeTolerance * yScale) :
+ (vecAbsoluteTolerance[j] + vecRelativeTolerance[j] * yScale);
+ final double ratio = h * errSum / tol;
+ error += ratio * ratio;
+
+ }
+
+ return FastMath.sqrt(error / mainSetDimension);
+
+ }
+
+}
diff --git a/src/main/java/org/apache/commons/math3/ode/nonstiff/DormandPrince54StepInterpolator.java b/src/main/java/org/apache/commons/math3/ode/nonstiff/DormandPrince54StepInterpolator.java
new file mode 100644
index 0000000..04aa970
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/ode/nonstiff/DormandPrince54StepInterpolator.java
@@ -0,0 +1,225 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math3.ode.nonstiff;
+
+import org.apache.commons.math3.ode.AbstractIntegrator;
+import org.apache.commons.math3.ode.EquationsMapper;
+import org.apache.commons.math3.ode.sampling.StepInterpolator;
+
+/**
+ * This class represents an interpolator over the last step during an
+ * ODE integration for the 5(4) Dormand-Prince integrator.
+ *
+ * @see DormandPrince54Integrator
+ *
+ * @since 1.2
+ */
+
+class DormandPrince54StepInterpolator
+ extends RungeKuttaStepInterpolator {
+
+ /** Last row of the Butcher-array internal weights, element 0. */
+ private static final double A70 = 35.0 / 384.0;
+
+ // element 1 is zero, so it is neither stored nor used
+
+ /** Last row of the Butcher-array internal weights, element 2. */
+ private static final double A72 = 500.0 / 1113.0;
+
+ /** Last row of the Butcher-array internal weights, element 3. */
+ private static final double A73 = 125.0 / 192.0;
+
+ /** Last row of the Butcher-array internal weights, element 4. */
+ private static final double A74 = -2187.0 / 6784.0;
+
+ /** Last row of the Butcher-array internal weights, element 5. */
+ private static final double A75 = 11.0 / 84.0;
+
+ /** Shampine (1986) Dense output, element 0. */
+ private static final double D0 = -12715105075.0 / 11282082432.0;
+
+ // element 1 is zero, so it is neither stored nor used
+
+ /** Shampine (1986) Dense output, element 2. */
+ private static final double D2 = 87487479700.0 / 32700410799.0;
+
+ /** Shampine (1986) Dense output, element 3. */
+ private static final double D3 = -10690763975.0 / 1880347072.0;
+
+ /** Shampine (1986) Dense output, element 4. */
+ private static final double D4 = 701980252875.0 / 199316789632.0;
+
+ /** Shampine (1986) Dense output, element 5. */
+ private static final double D5 = -1453857185.0 / 822651844.0;
+
+ /** Shampine (1986) Dense output, element 6. */
+ private static final double D6 = 69997945.0 / 29380423.0;
+
+ /** Serializable version identifier. */
+ private static final long serialVersionUID = 20111120L;
+
+ /** First vector for interpolation. */
+ private double[] v1;
+
+ /** Second vector for interpolation. */
+ private double[] v2;
+
+ /** Third vector for interpolation. */
+ private double[] v3;
+
+ /** Fourth vector for interpolation. */
+ private double[] v4;
+
+ /** Initialization indicator for the interpolation vectors. */
+ private boolean vectorsInitialized;
+
+ /** Simple constructor.
+ * This constructor builds an instance that is not usable yet, the
+ * {@link #reinitialize} method should be called before using the
+ * instance in order to initialize the internal arrays. This
+ * constructor is used only in order to delay the initialization in
+ * some cases. The {@link EmbeddedRungeKuttaIntegrator} uses the
+ * prototyping design pattern to create the step interpolators by
+ * cloning an uninitialized model and latter initializing the copy.
+ */
+ // CHECKSTYLE: stop RedundantModifier
+ // the public modifier here is needed for serialization
+ public DormandPrince54StepInterpolator() {
+ super();
+ v1 = null;
+ v2 = null;
+ v3 = null;
+ v4 = null;
+ vectorsInitialized = false;
+ }
+ // CHECKSTYLE: resume RedundantModifier
+
+ /** Copy constructor.
+ * @param interpolator interpolator to copy from. The copy is a deep
+ * copy: its arrays are separated from the original arrays of the
+ * instance
+ */
+ DormandPrince54StepInterpolator(final DormandPrince54StepInterpolator interpolator) {
+
+ super(interpolator);
+
+ if (interpolator.v1 == null) {
+
+ v1 = null;
+ v2 = null;
+ v3 = null;
+ v4 = null;
+ vectorsInitialized = false;
+
+ } else {
+
+ v1 = interpolator.v1.clone();
+ v2 = interpolator.v2.clone();
+ v3 = interpolator.v3.clone();
+ v4 = interpolator.v4.clone();
+ vectorsInitialized = interpolator.vectorsInitialized;
+
+ }
+
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ protected StepInterpolator doCopy() {
+ return new DormandPrince54StepInterpolator(this);
+ }
+
+
+ /** {@inheritDoc} */
+ @Override
+ public void reinitialize(final AbstractIntegrator integrator,
+ final double[] y, final double[][] yDotK, final boolean forward,
+ final EquationsMapper primaryMapper,
+ final EquationsMapper[] secondaryMappers) {
+ super.reinitialize(integrator, y, yDotK, forward, primaryMapper, secondaryMappers);
+ v1 = null;
+ v2 = null;
+ v3 = null;
+ v4 = null;
+ vectorsInitialized = false;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void storeTime(final double t) {
+ super.storeTime(t);
+ vectorsInitialized = false;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ protected void computeInterpolatedStateAndDerivatives(final double theta,
+ final double oneMinusThetaH) {
+
+ if (! vectorsInitialized) {
+
+ if (v1 == null) {
+ v1 = new double[interpolatedState.length];
+ v2 = new double[interpolatedState.length];
+ v3 = new double[interpolatedState.length];
+ v4 = new double[interpolatedState.length];
+ }
+
+ // no step finalization is needed for this interpolator
+
+ // we need to compute the interpolation vectors for this time step
+ for (int i = 0; i < interpolatedState.length; ++i) {
+ final double yDot0 = yDotK[0][i];
+ final double yDot2 = yDotK[2][i];
+ final double yDot3 = yDotK[3][i];
+ final double yDot4 = yDotK[4][i];
+ final double yDot5 = yDotK[5][i];
+ final double yDot6 = yDotK[6][i];
+ v1[i] = A70 * yDot0 + A72 * yDot2 + A73 * yDot3 + A74 * yDot4 + A75 * yDot5;
+ v2[i] = yDot0 - v1[i];
+ v3[i] = v1[i] - v2[i] - yDot6;
+ v4[i] = D0 * yDot0 + D2 * yDot2 + D3 * yDot3 + D4 * yDot4 + D5 * yDot5 + D6 * yDot6;
+ }
+
+ vectorsInitialized = true;
+
+ }
+
+ // interpolate
+ final double eta = 1 - theta;
+ final double twoTheta = 2 * theta;
+ final double dot2 = 1 - twoTheta;
+ final double dot3 = theta * (2 - 3 * theta);
+ final double dot4 = twoTheta * (1 + theta * (twoTheta - 3));
+ if ((previousState != null) && (theta <= 0.5)) {
+ for (int i = 0; i < interpolatedState.length; ++i) {
+ interpolatedState[i] =
+ previousState[i] + theta * h * (v1[i] + eta * (v2[i] + theta * (v3[i] + eta * v4[i])));
+ interpolatedDerivatives[i] = v1[i] + dot2 * v2[i] + dot3 * v3[i] + dot4 * v4[i];
+ }
+ } else {
+ for (int i = 0; i < interpolatedState.length; ++i) {
+ interpolatedState[i] =
+ currentState[i] - oneMinusThetaH * (v1[i] - theta * (v2[i] + theta * (v3[i] + eta * v4[i])));
+ interpolatedDerivatives[i] = v1[i] + dot2 * v2[i] + dot3 * v3[i] + dot4 * v4[i];
+ }
+ }
+
+ }
+
+}
diff --git a/src/main/java/org/apache/commons/math3/ode/nonstiff/DormandPrince853FieldIntegrator.java b/src/main/java/org/apache/commons/math3/ode/nonstiff/DormandPrince853FieldIntegrator.java
new file mode 100644
index 0000000..3111756
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/ode/nonstiff/DormandPrince853FieldIntegrator.java
@@ -0,0 +1,454 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math3.ode.nonstiff;
+
+import org.apache.commons.math3.Field;
+import org.apache.commons.math3.RealFieldElement;
+import org.apache.commons.math3.ode.FieldEquationsMapper;
+import org.apache.commons.math3.ode.FieldODEStateAndDerivative;
+import org.apache.commons.math3.util.MathArrays;
+import org.apache.commons.math3.util.MathUtils;
+
+
+/**
+ * This class implements the 8(5,3) Dormand-Prince integrator for Ordinary
+ * Differential Equations.
+ *
+ * <p>This integrator is an embedded Runge-Kutta integrator
+ * of order 8(5,3) used in local extrapolation mode (i.e. the solution
+ * is computed using the high order formula) with stepsize control
+ * (and automatic step initialization) and continuous output. This
+ * method uses 12 functions evaluations per step for integration and 4
+ * evaluations for interpolation. However, since the first
+ * interpolation evaluation is the same as the first integration
+ * evaluation of the next step, we have included it in the integrator
+ * rather than in the interpolator and specified the method was an
+ * <i>fsal</i>. Hence, despite we have 13 stages here, the cost is
+ * really 12 evaluations per step even if no interpolation is done,
+ * and the overcost of interpolation is only 3 evaluations.</p>
+ *
+ * <p>This method is based on an 8(6) method by Dormand and Prince
+ * (i.e. order 8 for the integration and order 6 for error estimation)
+ * modified by Hairer and Wanner to use a 5th order error estimator
+ * with 3rd order correction. This modification was introduced because
+ * the original method failed in some cases (wrong steps can be
+ * accepted when step size is too large, for example in the
+ * Brusselator problem) and also had <i>severe difficulties when
+ * applied to problems with discontinuities</i>. This modification is
+ * explained in the second edition of the first volume (Nonstiff
+ * Problems) of the reference book by Hairer, Norsett and Wanner:
+ * <i>Solving Ordinary Differential Equations</i> (Springer-Verlag,
+ * ISBN 3-540-56670-8).</p>
+ *
+ * @param <T> the type of the field elements
+ * @since 3.6
+ */
+
+public class DormandPrince853FieldIntegrator<T extends RealFieldElement<T>>
+ extends EmbeddedRungeKuttaFieldIntegrator<T> {
+
+ /** Integrator method name. */
+ private static final String METHOD_NAME = "Dormand-Prince 8 (5, 3)";
+
+ /** First error weights array, element 1. */
+ private final T e1_01;
+
+ // elements 2 to 5 are zero, so they are neither stored nor used
+
+ /** First error weights array, element 6. */
+ private final T e1_06;
+
+ /** First error weights array, element 7. */
+ private final T e1_07;
+
+ /** First error weights array, element 8. */
+ private final T e1_08;
+
+ /** First error weights array, element 9. */
+ private final T e1_09;
+
+ /** First error weights array, element 10. */
+ private final T e1_10;
+
+ /** First error weights array, element 11. */
+ private final T e1_11;
+
+ /** First error weights array, element 12. */
+ private final T e1_12;
+
+
+ /** Second error weights array, element 1. */
+ private final T e2_01;
+
+ // elements 2 to 5 are zero, so they are neither stored nor used
+
+ /** Second error weights array, element 6. */
+ private final T e2_06;
+
+ /** Second error weights array, element 7. */
+ private final T e2_07;
+
+ /** Second error weights array, element 8. */
+ private final T e2_08;
+
+ /** Second error weights array, element 9. */
+ private final T e2_09;
+
+ /** Second error weights array, element 10. */
+ private final T e2_10;
+
+ /** Second error weights array, element 11. */
+ private final T e2_11;
+
+ /** Second error weights array, element 12. */
+ private final T e2_12;
+
+ /** Simple constructor.
+ * Build an eighth order Dormand-Prince integrator with the given step bounds
+ * @param field field to which the time and state vector elements belong
+ * @param minStep minimal step (sign is irrelevant, regardless of
+ * integration direction, forward or backward), the last step can
+ * be smaller than this
+ * @param maxStep maximal step (sign is irrelevant, regardless of
+ * integration direction, forward or backward), the last step can
+ * be smaller than this
+ * @param scalAbsoluteTolerance allowed absolute error
+ * @param scalRelativeTolerance allowed relative error
+ */
+ public DormandPrince853FieldIntegrator(final Field<T> field,
+ final double minStep, final double maxStep,
+ final double scalAbsoluteTolerance,
+ final double scalRelativeTolerance) {
+ super(field, METHOD_NAME, 12,
+ minStep, maxStep, scalAbsoluteTolerance, scalRelativeTolerance);
+ e1_01 = fraction( 116092271.0, 8848465920.0);
+ e1_06 = fraction( -1871647.0, 1527680.0);
+ e1_07 = fraction( -69799717.0, 140793660.0);
+ e1_08 = fraction( 1230164450203.0, 739113984000.0);
+ e1_09 = fraction(-1980813971228885.0, 5654156025964544.0);
+ e1_10 = fraction( 464500805.0, 1389975552.0);
+ e1_11 = fraction( 1606764981773.0, 19613062656000.0);
+ e1_12 = fraction( -137909.0, 6168960.0);
+ e2_01 = fraction( -364463.0, 1920240.0);
+ e2_06 = fraction( 3399327.0, 763840.0);
+ e2_07 = fraction( 66578432.0, 35198415.0);
+ e2_08 = fraction( -1674902723.0, 288716400.0);
+ e2_09 = fraction( -74684743568175.0, 176692375811392.0);
+ e2_10 = fraction( -734375.0, 4826304.0);
+ e2_11 = fraction( 171414593.0, 851261400.0);
+ e2_12 = fraction( 69869.0, 3084480.0);
+ }
+
+ /** Simple constructor.
+ * Build an eighth order Dormand-Prince integrator with the given step bounds
+ * @param field field to which the time and state vector elements belong
+ * @param minStep minimal step (sign is irrelevant, regardless of
+ * integration direction, forward or backward), the last step can
+ * be smaller than this
+ * @param maxStep maximal step (sign is irrelevant, regardless of
+ * integration direction, forward or backward), the last step can
+ * be smaller than this
+ * @param vecAbsoluteTolerance allowed absolute error
+ * @param vecRelativeTolerance allowed relative error
+ */
+ public DormandPrince853FieldIntegrator(final Field<T> field,
+ final double minStep, final double maxStep,
+ final double[] vecAbsoluteTolerance,
+ final double[] vecRelativeTolerance) {
+ super(field, METHOD_NAME, 12,
+ minStep, maxStep, vecAbsoluteTolerance, vecRelativeTolerance);
+ e1_01 = fraction( 116092271.0, 8848465920.0);
+ e1_06 = fraction( -1871647.0, 1527680.0);
+ e1_07 = fraction( -69799717.0, 140793660.0);
+ e1_08 = fraction( 1230164450203.0, 739113984000.0);
+ e1_09 = fraction(-1980813971228885.0, 5654156025964544.0);
+ e1_10 = fraction( 464500805.0, 1389975552.0);
+ e1_11 = fraction( 1606764981773.0, 19613062656000.0);
+ e1_12 = fraction( -137909.0, 6168960.0);
+ e2_01 = fraction( -364463.0, 1920240.0);
+ e2_06 = fraction( 3399327.0, 763840.0);
+ e2_07 = fraction( 66578432.0, 35198415.0);
+ e2_08 = fraction( -1674902723.0, 288716400.0);
+ e2_09 = fraction( -74684743568175.0, 176692375811392.0);
+ e2_10 = fraction( -734375.0, 4826304.0);
+ e2_11 = fraction( 171414593.0, 851261400.0);
+ e2_12 = fraction( 69869.0, 3084480.0);
+ }
+
+ /** {@inheritDoc} */
+ public T[] getC() {
+
+ final T sqrt6 = getField().getOne().multiply(6).sqrt();
+
+ final T[] c = MathArrays.buildArray(getField(), 15);
+ c[ 0] = sqrt6.add(-6).divide(-67.5);
+ c[ 1] = sqrt6.add(-6).divide(-45.0);
+ c[ 2] = sqrt6.add(-6).divide(-30.0);
+ c[ 3] = sqrt6.add( 6).divide( 30.0);
+ c[ 4] = fraction(1, 3);
+ c[ 5] = fraction(1, 4);
+ c[ 6] = fraction(4, 13);
+ c[ 7] = fraction(127, 195);
+ c[ 8] = fraction(3, 5);
+ c[ 9] = fraction(6, 7);
+ c[10] = getField().getOne();
+ c[11] = getField().getOne();
+ c[12] = fraction(1.0, 10.0);
+ c[13] = fraction(1.0, 5.0);
+ c[14] = fraction(7.0, 9.0);
+
+ return c;
+
+ }
+
+ /** {@inheritDoc} */
+ public T[][] getA() {
+
+ final T sqrt6 = getField().getOne().multiply(6).sqrt();
+
+ final T[][] a = MathArrays.buildArray(getField(), 15, -1);
+ for (int i = 0; i < a.length; ++i) {
+ a[i] = MathArrays.buildArray(getField(), i + 1);
+ }
+
+ a[ 0][ 0] = sqrt6.add(-6).divide(-67.5);
+
+ a[ 1][ 0] = sqrt6.add(-6).divide(-180);
+ a[ 1][ 1] = sqrt6.add(-6).divide( -60);
+
+ a[ 2][ 0] = sqrt6.add(-6).divide(-120);
+ a[ 2][ 1] = getField().getZero();
+ a[ 2][ 2] = sqrt6.add(-6).divide( -40);
+
+ a[ 3][ 0] = sqrt6.multiply(107).add(462).divide( 3000);
+ a[ 3][ 1] = getField().getZero();
+ a[ 3][ 2] = sqrt6.multiply(197).add(402).divide(-1000);
+ a[ 3][ 3] = sqrt6.multiply( 73).add(168).divide( 375);
+
+ a[ 4][ 0] = fraction(1, 27);
+ a[ 4][ 1] = getField().getZero();
+ a[ 4][ 2] = getField().getZero();
+ a[ 4][ 3] = sqrt6.add( 16).divide( 108);
+ a[ 4][ 4] = sqrt6.add(-16).divide(-108);
+
+ a[ 5][ 0] = fraction(19, 512);
+ a[ 5][ 1] = getField().getZero();
+ a[ 5][ 2] = getField().getZero();
+ a[ 5][ 3] = sqrt6.multiply( 23).add(118).divide(1024);
+ a[ 5][ 4] = sqrt6.multiply(-23).add(118).divide(1024);
+ a[ 5][ 5] = fraction(-9, 512);
+
+ a[ 6][ 0] = fraction(13772, 371293);
+ a[ 6][ 1] = getField().getZero();
+ a[ 6][ 2] = getField().getZero();
+ a[ 6][ 3] = sqrt6.multiply( 4784).add(51544).divide(371293);
+ a[ 6][ 4] = sqrt6.multiply(-4784).add(51544).divide(371293);
+ a[ 6][ 5] = fraction(-5688, 371293);
+ a[ 6][ 6] = fraction( 3072, 371293);
+
+ a[ 7][ 0] = fraction(58656157643.0, 93983540625.0);
+ a[ 7][ 1] = getField().getZero();
+ a[ 7][ 2] = getField().getZero();
+ a[ 7][ 3] = sqrt6.multiply(-318801444819.0).add(-1324889724104.0).divide(626556937500.0);
+ a[ 7][ 4] = sqrt6.multiply( 318801444819.0).add(-1324889724104.0).divide(626556937500.0);
+ a[ 7][ 5] = fraction(96044563816.0, 3480871875.0);
+ a[ 7][ 6] = fraction(5682451879168.0, 281950621875.0);
+ a[ 7][ 7] = fraction(-165125654.0, 3796875.0);
+
+ a[ 8][ 0] = fraction(8909899.0, 18653125.0);
+ a[ 8][ 1] = getField().getZero();
+ a[ 8][ 2] = getField().getZero();
+ a[ 8][ 3] = sqrt6.multiply(-1137963.0).add(-4521408.0).divide(2937500.0);
+ a[ 8][ 4] = sqrt6.multiply( 1137963.0).add(-4521408.0).divide(2937500.0);
+ a[ 8][ 5] = fraction(96663078.0, 4553125.0);
+ a[ 8][ 6] = fraction(2107245056.0, 137915625.0);
+ a[ 8][ 7] = fraction(-4913652016.0, 147609375.0);
+ a[ 8][ 8] = fraction(-78894270.0, 3880452869.0);
+
+ a[ 9][ 0] = fraction(-20401265806.0, 21769653311.0);
+ a[ 9][ 1] = getField().getZero();
+ a[ 9][ 2] = getField().getZero();
+ a[ 9][ 3] = sqrt6.multiply( 94326.0).add(354216.0).divide(112847.0);
+ a[ 9][ 4] = sqrt6.multiply(-94326.0).add(354216.0).divide(112847.0);
+ a[ 9][ 5] = fraction(-43306765128.0, 5313852383.0);
+ a[ 9][ 6] = fraction(-20866708358144.0, 1126708119789.0);
+ a[ 9][ 7] = fraction(14886003438020.0, 654632330667.0);
+ a[ 9][ 8] = fraction(35290686222309375.0, 14152473387134411.0);
+ a[ 9][ 9] = fraction(-1477884375.0, 485066827.0);
+
+ a[10][ 0] = fraction(39815761.0, 17514443.0);
+ a[10][ 1] = getField().getZero();
+ a[10][ 2] = getField().getZero();
+ a[10][ 3] = sqrt6.multiply(-960905.0).add(-3457480.0).divide(551636.0);
+ a[10][ 4] = sqrt6.multiply( 960905.0).add(-3457480.0).divide(551636.0);
+ a[10][ 5] = fraction(-844554132.0, 47026969.0);
+ a[10][ 6] = fraction(8444996352.0, 302158619.0);
+ a[10][ 7] = fraction(-2509602342.0, 877790785.0);
+ a[10][ 8] = fraction(-28388795297996250.0, 3199510091356783.0);
+ a[10][ 9] = fraction(226716250.0, 18341897.0);
+ a[10][10] = fraction(1371316744.0, 2131383595.0);
+
+ // the following stage is both for interpolation and the first stage in next step
+ // (the coefficients are identical to the B array)
+ a[11][ 0] = fraction(104257.0, 1920240.0);
+ a[11][ 1] = getField().getZero();
+ a[11][ 2] = getField().getZero();
+ a[11][ 3] = getField().getZero();
+ a[11][ 4] = getField().getZero();
+ a[11][ 5] = fraction(3399327.0, 763840.0);
+ a[11][ 6] = fraction(66578432.0, 35198415.0);
+ a[11][ 7] = fraction(-1674902723.0, 288716400.0);
+ a[11][ 8] = fraction(54980371265625.0, 176692375811392.0);
+ a[11][ 9] = fraction(-734375.0, 4826304.0);
+ a[11][10] = fraction(171414593.0, 851261400.0);
+ a[11][11] = fraction(137909.0, 3084480.0);
+
+ // the following stages are for interpolation only
+ a[12][ 0] = fraction( 13481885573.0, 240030000000.0);
+ a[12][ 1] = getField().getZero();
+ a[12][ 2] = getField().getZero();
+ a[12][ 3] = getField().getZero();
+ a[12][ 4] = getField().getZero();
+ a[12][ 5] = getField().getZero();
+ a[12][ 6] = fraction( 139418837528.0, 549975234375.0);
+ a[12][ 7] = fraction( -11108320068443.0, 45111937500000.0);
+ a[12][ 8] = fraction(-1769651421925959.0, 14249385146080000.0);
+ a[12][ 9] = fraction( 57799439.0, 377055000.0);
+ a[12][10] = fraction( 793322643029.0, 96734250000000.0);
+ a[12][11] = fraction( 1458939311.0, 192780000000.0);
+ a[12][12] = fraction( -4149.0, 500000.0);
+
+ a[13][ 0] = fraction( 1595561272731.0, 50120273500000.0);
+ a[13][ 1] = getField().getZero();
+ a[13][ 2] = getField().getZero();
+ a[13][ 3] = getField().getZero();
+ a[13][ 4] = getField().getZero();
+ a[13][ 5] = fraction( 975183916491.0, 34457688031250.0);
+ a[13][ 6] = fraction( 38492013932672.0, 718912673015625.0);
+ a[13][ 7] = fraction(-1114881286517557.0, 20298710767500000.0);
+ a[13][ 8] = getField().getZero();
+ a[13][ 9] = getField().getZero();
+ a[13][10] = fraction( -2538710946863.0, 23431227861250000.0);
+ a[13][11] = fraction( 8824659001.0, 23066716781250.0);
+ a[13][12] = fraction( -11518334563.0, 33831184612500.0);
+ a[13][13] = fraction( 1912306948.0, 13532473845.0);
+
+ a[14][ 0] = fraction( -13613986967.0, 31741908048.0);
+ a[14][ 1] = getField().getZero();
+ a[14][ 2] = getField().getZero();
+ a[14][ 3] = getField().getZero();
+ a[14][ 4] = getField().getZero();
+ a[14][ 5] = fraction( -4755612631.0, 1012344804.0);
+ a[14][ 6] = fraction( 42939257944576.0, 5588559685701.0);
+ a[14][ 7] = fraction( 77881972900277.0, 19140370552944.0);
+ a[14][ 8] = fraction( 22719829234375.0, 63689648654052.0);
+ a[14][ 9] = getField().getZero();
+ a[14][10] = getField().getZero();
+ a[14][11] = getField().getZero();
+ a[14][12] = fraction( -1199007803.0, 857031517296.0);
+ a[14][13] = fraction( 157882067000.0, 53564469831.0);
+ a[14][14] = fraction( -290468882375.0, 31741908048.0);
+
+ return a;
+
+ }
+
+ /** {@inheritDoc} */
+ public T[] getB() {
+ final T[] b = MathArrays.buildArray(getField(), 16);
+ b[ 0] = fraction(104257, 1920240);
+ b[ 1] = getField().getZero();
+ b[ 2] = getField().getZero();
+ b[ 3] = getField().getZero();
+ b[ 4] = getField().getZero();
+ b[ 5] = fraction( 3399327.0, 763840.0);
+ b[ 6] = fraction( 66578432.0, 35198415.0);
+ b[ 7] = fraction( -1674902723.0, 288716400.0);
+ b[ 8] = fraction( 54980371265625.0, 176692375811392.0);
+ b[ 9] = fraction( -734375.0, 4826304.0);
+ b[10] = fraction( 171414593.0, 851261400.0);
+ b[11] = fraction( 137909.0, 3084480.0);
+ b[12] = getField().getZero();
+ b[13] = getField().getZero();
+ b[14] = getField().getZero();
+ b[15] = getField().getZero();
+ return b;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ protected DormandPrince853FieldStepInterpolator<T>
+ createInterpolator(final boolean forward, T[][] yDotK,
+ final FieldODEStateAndDerivative<T> globalPreviousState,
+ final FieldODEStateAndDerivative<T> globalCurrentState, final FieldEquationsMapper<T> mapper) {
+ return new DormandPrince853FieldStepInterpolator<T>(getField(), forward, yDotK,
+ globalPreviousState, globalCurrentState,
+ globalPreviousState, globalCurrentState,
+ mapper);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public int getOrder() {
+ return 8;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ protected T estimateError(final T[][] yDotK, final T[] y0, final T[] y1, final T h) {
+ T error1 = h.getField().getZero();
+ T error2 = h.getField().getZero();
+
+ for (int j = 0; j < mainSetDimension; ++j) {
+ final T errSum1 = yDotK[ 0][j].multiply(e1_01).
+ add(yDotK[ 5][j].multiply(e1_06)).
+ add(yDotK[ 6][j].multiply(e1_07)).
+ add(yDotK[ 7][j].multiply(e1_08)).
+ add(yDotK[ 8][j].multiply(e1_09)).
+ add(yDotK[ 9][j].multiply(e1_10)).
+ add(yDotK[10][j].multiply(e1_11)).
+ add(yDotK[11][j].multiply(e1_12));
+ final T errSum2 = yDotK[ 0][j].multiply(e2_01).
+ add(yDotK[ 5][j].multiply(e2_06)).
+ add(yDotK[ 6][j].multiply(e2_07)).
+ add(yDotK[ 7][j].multiply(e2_08)).
+ add(yDotK[ 8][j].multiply(e2_09)).
+ add(yDotK[ 9][j].multiply(e2_10)).
+ add(yDotK[10][j].multiply(e2_11)).
+ add(yDotK[11][j].multiply(e2_12));
+
+ final T yScale = MathUtils.max(y0[j].abs(), y1[j].abs());
+ final T tol = vecAbsoluteTolerance == null ?
+ yScale.multiply(scalRelativeTolerance).add(scalAbsoluteTolerance) :
+ yScale.multiply(vecRelativeTolerance[j]).add(vecAbsoluteTolerance[j]);
+ final T ratio1 = errSum1.divide(tol);
+ error1 = error1.add(ratio1.multiply(ratio1));
+ final T ratio2 = errSum2.divide(tol);
+ error2 = error2.add(ratio2.multiply(ratio2));
+ }
+
+ T den = error1.add(error2.multiply(0.01));
+ if (den.getReal() <= 0.0) {
+ den = h.getField().getOne();
+ }
+
+ return h.abs().multiply(error1).divide(den.multiply(mainSetDimension).sqrt());
+
+ }
+
+}
diff --git a/src/main/java/org/apache/commons/math3/ode/nonstiff/DormandPrince853FieldStepInterpolator.java b/src/main/java/org/apache/commons/math3/ode/nonstiff/DormandPrince853FieldStepInterpolator.java
new file mode 100644
index 0000000..c706e2a
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/ode/nonstiff/DormandPrince853FieldStepInterpolator.java
@@ -0,0 +1,302 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math3.ode.nonstiff;
+
+import org.apache.commons.math3.Field;
+import org.apache.commons.math3.RealFieldElement;
+import org.apache.commons.math3.exception.MaxCountExceededException;
+import org.apache.commons.math3.ode.FieldEquationsMapper;
+import org.apache.commons.math3.ode.FieldODEStateAndDerivative;
+import org.apache.commons.math3.util.MathArrays;
+
+/**
+ * This class represents an interpolator over the last step during an
+ * ODE integration for the 8(5,3) Dormand-Prince integrator.
+ *
+ * @see DormandPrince853FieldIntegrator
+ *
+ * @param <T> the type of the field elements
+ * @since 3.6
+ */
+
+class DormandPrince853FieldStepInterpolator<T extends RealFieldElement<T>>
+ extends RungeKuttaFieldStepInterpolator<T> {
+
+ /** Interpolation weights.
+ * (beware that only the non-null values are in the table)
+ */
+ private final T[][] d;
+
+ /** Simple constructor.
+ * @param field field to which the time and state vector elements belong
+ * @param forward integration direction indicator
+ * @param yDotK slopes at the intermediate points
+ * @param globalPreviousState start of the global step
+ * @param globalCurrentState end of the global step
+ * @param softPreviousState start of the restricted step
+ * @param softCurrentState end of the restricted step
+ * @param mapper equations mapper for the all equations
+ */
+ DormandPrince853FieldStepInterpolator(final Field<T> field, final boolean forward,
+ final T[][] yDotK,
+ final FieldODEStateAndDerivative<T> globalPreviousState,
+ final FieldODEStateAndDerivative<T> globalCurrentState,
+ final FieldODEStateAndDerivative<T> softPreviousState,
+ final FieldODEStateAndDerivative<T> softCurrentState,
+ final FieldEquationsMapper<T> mapper) {
+ super(field, forward, yDotK,
+ globalPreviousState, globalCurrentState, softPreviousState, softCurrentState,
+ mapper);
+ // interpolation weights
+ d = MathArrays.buildArray(field, 7, 16);
+
+ // this row is the same as the b array
+ d[0][ 0] = fraction(field, 104257, 1920240);
+ d[0][ 1] = field.getZero();
+ d[0][ 2] = field.getZero();
+ d[0][ 3] = field.getZero();
+ d[0][ 4] = field.getZero();
+ d[0][ 5] = fraction(field, 3399327.0, 763840.0);
+ d[0][ 6] = fraction(field, 66578432.0, 35198415.0);
+ d[0][ 7] = fraction(field, -1674902723.0, 288716400.0);
+ d[0][ 8] = fraction(field, 54980371265625.0, 176692375811392.0);
+ d[0][ 9] = fraction(field, -734375.0, 4826304.0);
+ d[0][10] = fraction(field, 171414593.0, 851261400.0);
+ d[0][11] = fraction(field, 137909.0, 3084480.0);
+ d[0][12] = field.getZero();
+ d[0][13] = field.getZero();
+ d[0][14] = field.getZero();
+ d[0][15] = field.getZero();
+
+ d[1][ 0] = d[0][ 0].negate().add(1);
+ d[1][ 1] = d[0][ 1].negate();
+ d[1][ 2] = d[0][ 2].negate();
+ d[1][ 3] = d[0][ 3].negate();
+ d[1][ 4] = d[0][ 4].negate();
+ d[1][ 5] = d[0][ 5].negate();
+ d[1][ 6] = d[0][ 6].negate();
+ d[1][ 7] = d[0][ 7].negate();
+ d[1][ 8] = d[0][ 8].negate();
+ d[1][ 9] = d[0][ 9].negate();
+ d[1][10] = d[0][10].negate();
+ d[1][11] = d[0][11].negate();
+ d[1][12] = d[0][12].negate(); // really 0
+ d[1][13] = d[0][13].negate(); // really 0
+ d[1][14] = d[0][14].negate(); // really 0
+ d[1][15] = d[0][15].negate(); // really 0
+
+ d[2][ 0] = d[0][ 0].multiply(2).subtract(1);
+ d[2][ 1] = d[0][ 1].multiply(2);
+ d[2][ 2] = d[0][ 2].multiply(2);
+ d[2][ 3] = d[0][ 3].multiply(2);
+ d[2][ 4] = d[0][ 4].multiply(2);
+ d[2][ 5] = d[0][ 5].multiply(2);
+ d[2][ 6] = d[0][ 6].multiply(2);
+ d[2][ 7] = d[0][ 7].multiply(2);
+ d[2][ 8] = d[0][ 8].multiply(2);
+ d[2][ 9] = d[0][ 9].multiply(2);
+ d[2][10] = d[0][10].multiply(2);
+ d[2][11] = d[0][11].multiply(2);
+ d[2][12] = d[0][12].multiply(2).subtract(1); // really -1
+ d[2][13] = d[0][13].multiply(2); // really 0
+ d[2][14] = d[0][14].multiply(2); // really 0
+ d[2][15] = d[0][15].multiply(2); // really 0
+
+ d[3][ 0] = fraction(field, -17751989329.0, 2106076560.0);
+ d[3][ 1] = field.getZero();
+ d[3][ 2] = field.getZero();
+ d[3][ 3] = field.getZero();
+ d[3][ 4] = field.getZero();
+ d[3][ 5] = fraction(field, 4272954039.0, 7539864640.0);
+ d[3][ 6] = fraction(field, -118476319744.0, 38604839385.0);
+ d[3][ 7] = fraction(field, 755123450731.0, 316657731600.0);
+ d[3][ 8] = fraction(field, 3692384461234828125.0, 1744130441634250432.0);
+ d[3][ 9] = fraction(field, -4612609375.0, 5293382976.0);
+ d[3][10] = fraction(field, 2091772278379.0, 933644586600.0);
+ d[3][11] = fraction(field, 2136624137.0, 3382989120.0);
+ d[3][12] = fraction(field, -126493.0, 1421424.0);
+ d[3][13] = fraction(field, 98350000.0, 5419179.0);
+ d[3][14] = fraction(field, -18878125.0, 2053168.0);
+ d[3][15] = fraction(field, -1944542619.0, 438351368.0);
+
+ d[4][ 0] = fraction(field, 32941697297.0, 3159114840.0);
+ d[4][ 1] = field.getZero();
+ d[4][ 2] = field.getZero();
+ d[4][ 3] = field.getZero();
+ d[4][ 4] = field.getZero();
+ d[4][ 5] = fraction(field, 456696183123.0, 1884966160.0);
+ d[4][ 6] = fraction(field, 19132610714624.0, 115814518155.0);
+ d[4][ 7] = fraction(field, -177904688592943.0, 474986597400.0);
+ d[4][ 8] = fraction(field, -4821139941836765625.0, 218016305204281304.0);
+ d[4][ 9] = fraction(field, 30702015625.0, 3970037232.0);
+ d[4][10] = fraction(field, -85916079474274.0, 2800933759800.0);
+ d[4][11] = fraction(field, -5919468007.0, 634310460.0);
+ d[4][12] = fraction(field, 2479159.0, 157936.0);
+ d[4][13] = fraction(field, -18750000.0, 602131.0);
+ d[4][14] = fraction(field, -19203125.0, 2053168.0);
+ d[4][15] = fraction(field, 15700361463.0, 438351368.0);
+
+ d[5][ 0] = fraction(field, 12627015655.0, 631822968.0);
+ d[5][ 1] = field.getZero();
+ d[5][ 2] = field.getZero();
+ d[5][ 3] = field.getZero();
+ d[5][ 4] = field.getZero();
+ d[5][ 5] = fraction(field, -72955222965.0, 188496616.0);
+ d[5][ 6] = fraction(field, -13145744952320.0, 69488710893.0);
+ d[5][ 7] = fraction(field, 30084216194513.0, 56998391688.0);
+ d[5][ 8] = fraction(field, -296858761006640625.0, 25648977082856624.0);
+ d[5][ 9] = fraction(field, 569140625.0, 82709109.0);
+ d[5][10] = fraction(field, -18684190637.0, 18672891732.0);
+ d[5][11] = fraction(field, 69644045.0, 89549712.0);
+ d[5][12] = fraction(field, -11847025.0, 4264272.0);
+ d[5][13] = fraction(field, -978650000.0, 16257537.0);
+ d[5][14] = fraction(field, 519371875.0, 6159504.0);
+ d[5][15] = fraction(field, 5256837225.0, 438351368.0);
+
+ d[6][ 0] = fraction(field, -450944925.0, 17550638.0);
+ d[6][ 1] = field.getZero();
+ d[6][ 2] = field.getZero();
+ d[6][ 3] = field.getZero();
+ d[6][ 4] = field.getZero();
+ d[6][ 5] = fraction(field, -14532122925.0, 94248308.0);
+ d[6][ 6] = fraction(field, -595876966400.0, 2573655959.0);
+ d[6][ 7] = fraction(field, 188748653015.0, 527762886.0);
+ d[6][ 8] = fraction(field, 2545485458115234375.0, 27252038150535163.0);
+ d[6][ 9] = fraction(field, -1376953125.0, 36759604.0);
+ d[6][10] = fraction(field, 53995596795.0, 518691437.0);
+ d[6][11] = fraction(field, 210311225.0, 7047894.0);
+ d[6][12] = fraction(field, -1718875.0, 39484.0);
+ d[6][13] = fraction(field, 58000000.0, 602131.0);
+ d[6][14] = fraction(field, -1546875.0, 39484.0);
+ d[6][15] = fraction(field, -1262172375.0, 8429834.0);
+
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ protected DormandPrince853FieldStepInterpolator<T> create(final Field<T> newField, final boolean newForward, final T[][] newYDotK,
+ final FieldODEStateAndDerivative<T> newGlobalPreviousState,
+ final FieldODEStateAndDerivative<T> newGlobalCurrentState,
+ final FieldODEStateAndDerivative<T> newSoftPreviousState,
+ final FieldODEStateAndDerivative<T> newSoftCurrentState,
+ final FieldEquationsMapper<T> newMapper) {
+ return new DormandPrince853FieldStepInterpolator<T>(newField, newForward, newYDotK,
+ newGlobalPreviousState, newGlobalCurrentState,
+ newSoftPreviousState, newSoftCurrentState,
+ newMapper);
+ }
+
+ /** Create a fraction.
+ * @param field field to which the elements belong
+ * @param p numerator
+ * @param q denominator
+ * @return p/q computed in the instance field
+ */
+ private T fraction(final Field<T> field, final double p, final double q) {
+ return field.getZero().add(p).divide(q);
+ }
+
+ /** {@inheritDoc} */
+ @SuppressWarnings("unchecked")
+ @Override
+ protected FieldODEStateAndDerivative<T> computeInterpolatedStateAndDerivatives(final FieldEquationsMapper<T> mapper,
+ final T time, final T theta,
+ final T thetaH, final T oneMinusThetaH)
+ throws MaxCountExceededException {
+
+ final T one = time.getField().getOne();
+ final T eta = one.subtract(theta);
+ final T twoTheta = theta.multiply(2);
+ final T theta2 = theta.multiply(theta);
+ final T dot1 = one.subtract(twoTheta);
+ final T dot2 = theta.multiply(theta.multiply(-3).add(2));
+ final T dot3 = twoTheta.multiply(theta.multiply(twoTheta.subtract(3)).add(1));
+ final T dot4 = theta2.multiply(theta.multiply(theta.multiply(5).subtract(8)).add(3));
+ final T dot5 = theta2.multiply(theta.multiply(theta.multiply(theta.multiply(-6).add(15)).subtract(12)).add(3));
+ final T dot6 = theta2.multiply(theta.multiply(theta.multiply(theta.multiply(theta.multiply(-7).add(18)).subtract(15)).add(4)));
+ final T[] interpolatedState;
+ final T[] interpolatedDerivatives;
+
+
+ if (getGlobalPreviousState() != null && theta.getReal() <= 0.5) {
+ final T f0 = thetaH;
+ final T f1 = f0.multiply(eta);
+ final T f2 = f1.multiply(theta);
+ final T f3 = f2.multiply(eta);
+ final T f4 = f3.multiply(theta);
+ final T f5 = f4.multiply(eta);
+ final T f6 = f5.multiply(theta);
+ final T[] p = MathArrays.buildArray(time.getField(), 16);
+ final T[] q = MathArrays.buildArray(time.getField(), 16);
+ for (int i = 0; i < p.length; ++i) {
+ p[i] = f0.multiply(d[0][i]).
+ add(f1.multiply(d[1][i])).
+ add(f2.multiply(d[2][i])).
+ add(f3.multiply(d[3][i])).
+ add(f4.multiply(d[4][i])).
+ add(f5.multiply(d[5][i])).
+ add(f6.multiply(d[6][i]));
+ q[i] = d[0][i].
+ add(dot1.multiply(d[1][i])).
+ add(dot2.multiply(d[2][i])).
+ add(dot3.multiply(d[3][i])).
+ add(dot4.multiply(d[4][i])).
+ add(dot5.multiply(d[5][i])).
+ add(dot6.multiply(d[6][i]));
+ }
+ interpolatedState = previousStateLinearCombination(p[0], p[1], p[ 2], p[ 3], p[ 4], p[ 5], p[ 6], p[ 7],
+ p[8], p[9], p[10], p[11], p[12], p[13], p[14], p[15]);
+ interpolatedDerivatives = derivativeLinearCombination(q[0], q[1], q[ 2], q[ 3], q[ 4], q[ 5], q[ 6], q[ 7],
+ q[8], q[9], q[10], q[11], q[12], q[13], q[14], q[15]);
+ } else {
+ final T f0 = oneMinusThetaH.negate();
+ final T f1 = f0.multiply(theta).negate();
+ final T f2 = f1.multiply(theta);
+ final T f3 = f2.multiply(eta);
+ final T f4 = f3.multiply(theta);
+ final T f5 = f4.multiply(eta);
+ final T f6 = f5.multiply(theta);
+ final T[] p = MathArrays.buildArray(time.getField(), 16);
+ final T[] q = MathArrays.buildArray(time.getField(), 16);
+ for (int i = 0; i < p.length; ++i) {
+ p[i] = f0.multiply(d[0][i]).
+ add(f1.multiply(d[1][i])).
+ add(f2.multiply(d[2][i])).
+ add(f3.multiply(d[3][i])).
+ add(f4.multiply(d[4][i])).
+ add(f5.multiply(d[5][i])).
+ add(f6.multiply(d[6][i]));
+ q[i] = d[0][i].
+ add(dot1.multiply(d[1][i])).
+ add(dot2.multiply(d[2][i])).
+ add(dot3.multiply(d[3][i])).
+ add(dot4.multiply(d[4][i])).
+ add(dot5.multiply(d[5][i])).
+ add(dot6.multiply(d[6][i]));
+ }
+ interpolatedState = currentStateLinearCombination(p[0], p[1], p[ 2], p[ 3], p[ 4], p[ 5], p[ 6], p[ 7],
+ p[8], p[9], p[10], p[11], p[12], p[13], p[14], p[15]);
+ interpolatedDerivatives = derivativeLinearCombination(q[0], q[1], q[ 2], q[ 3], q[ 4], q[ 5], q[ 6], q[ 7],
+ q[8], q[9], q[10], q[11], q[12], q[13], q[14], q[15]);
+ }
+
+ return new FieldODEStateAndDerivative<T>(time, interpolatedState, interpolatedDerivatives);
+
+ }
+
+}
diff --git a/src/main/java/org/apache/commons/math3/ode/nonstiff/DormandPrince853Integrator.java b/src/main/java/org/apache/commons/math3/ode/nonstiff/DormandPrince853Integrator.java
new file mode 100644
index 0000000..895cb88
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/ode/nonstiff/DormandPrince853Integrator.java
@@ -0,0 +1,286 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math3.ode.nonstiff;
+
+import org.apache.commons.math3.util.FastMath;
+
+
+/**
+ * This class implements the 8(5,3) Dormand-Prince integrator for Ordinary
+ * Differential Equations.
+ *
+ * <p>This integrator is an embedded Runge-Kutta integrator
+ * of order 8(5,3) used in local extrapolation mode (i.e. the solution
+ * is computed using the high order formula) with stepsize control
+ * (and automatic step initialization) and continuous output. This
+ * method uses 12 functions evaluations per step for integration and 4
+ * evaluations for interpolation. However, since the first
+ * interpolation evaluation is the same as the first integration
+ * evaluation of the next step, we have included it in the integrator
+ * rather than in the interpolator and specified the method was an
+ * <i>fsal</i>. Hence, despite we have 13 stages here, the cost is
+ * really 12 evaluations per step even if no interpolation is done,
+ * and the overcost of interpolation is only 3 evaluations.</p>
+ *
+ * <p>This method is based on an 8(6) method by Dormand and Prince
+ * (i.e. order 8 for the integration and order 6 for error estimation)
+ * modified by Hairer and Wanner to use a 5th order error estimator
+ * with 3rd order correction. This modification was introduced because
+ * the original method failed in some cases (wrong steps can be
+ * accepted when step size is too large, for example in the
+ * Brusselator problem) and also had <i>severe difficulties when
+ * applied to problems with discontinuities</i>. This modification is
+ * explained in the second edition of the first volume (Nonstiff
+ * Problems) of the reference book by Hairer, Norsett and Wanner:
+ * <i>Solving Ordinary Differential Equations</i> (Springer-Verlag,
+ * ISBN 3-540-56670-8).</p>
+ *
+ * @since 1.2
+ */
+
+public class DormandPrince853Integrator extends EmbeddedRungeKuttaIntegrator {
+
+ /** Integrator method name. */
+ private static final String METHOD_NAME = "Dormand-Prince 8 (5, 3)";
+
+ /** Time steps Butcher array. */
+ private static final double[] STATIC_C = {
+ (12.0 - 2.0 * FastMath.sqrt(6.0)) / 135.0, (6.0 - FastMath.sqrt(6.0)) / 45.0, (6.0 - FastMath.sqrt(6.0)) / 30.0,
+ (6.0 + FastMath.sqrt(6.0)) / 30.0, 1.0/3.0, 1.0/4.0, 4.0/13.0, 127.0/195.0, 3.0/5.0,
+ 6.0/7.0, 1.0, 1.0
+ };
+
+ /** Internal weights Butcher array. */
+ private static final double[][] STATIC_A = {
+
+ // k2
+ {(12.0 - 2.0 * FastMath.sqrt(6.0)) / 135.0},
+
+ // k3
+ {(6.0 - FastMath.sqrt(6.0)) / 180.0, (6.0 - FastMath.sqrt(6.0)) / 60.0},
+
+ // k4
+ {(6.0 - FastMath.sqrt(6.0)) / 120.0, 0.0, (6.0 - FastMath.sqrt(6.0)) / 40.0},
+
+ // k5
+ {(462.0 + 107.0 * FastMath.sqrt(6.0)) / 3000.0, 0.0,
+ (-402.0 - 197.0 * FastMath.sqrt(6.0)) / 1000.0, (168.0 + 73.0 * FastMath.sqrt(6.0)) / 375.0},
+
+ // k6
+ {1.0 / 27.0, 0.0, 0.0, (16.0 + FastMath.sqrt(6.0)) / 108.0, (16.0 - FastMath.sqrt(6.0)) / 108.0},
+
+ // k7
+ {19.0 / 512.0, 0.0, 0.0, (118.0 + 23.0 * FastMath.sqrt(6.0)) / 1024.0,
+ (118.0 - 23.0 * FastMath.sqrt(6.0)) / 1024.0, -9.0 / 512.0},
+
+ // k8
+ {13772.0 / 371293.0, 0.0, 0.0, (51544.0 + 4784.0 * FastMath.sqrt(6.0)) / 371293.0,
+ (51544.0 - 4784.0 * FastMath.sqrt(6.0)) / 371293.0, -5688.0 / 371293.0, 3072.0 / 371293.0},
+
+ // k9
+ {58656157643.0 / 93983540625.0, 0.0, 0.0,
+ (-1324889724104.0 - 318801444819.0 * FastMath.sqrt(6.0)) / 626556937500.0,
+ (-1324889724104.0 + 318801444819.0 * FastMath.sqrt(6.0)) / 626556937500.0,
+ 96044563816.0 / 3480871875.0, 5682451879168.0 / 281950621875.0,
+ -165125654.0 / 3796875.0},
+
+ // k10
+ {8909899.0 / 18653125.0, 0.0, 0.0,
+ (-4521408.0 - 1137963.0 * FastMath.sqrt(6.0)) / 2937500.0,
+ (-4521408.0 + 1137963.0 * FastMath.sqrt(6.0)) / 2937500.0,
+ 96663078.0 / 4553125.0, 2107245056.0 / 137915625.0,
+ -4913652016.0 / 147609375.0, -78894270.0 / 3880452869.0},
+
+ // k11
+ {-20401265806.0 / 21769653311.0, 0.0, 0.0,
+ (354216.0 + 94326.0 * FastMath.sqrt(6.0)) / 112847.0,
+ (354216.0 - 94326.0 * FastMath.sqrt(6.0)) / 112847.0,
+ -43306765128.0 / 5313852383.0, -20866708358144.0 / 1126708119789.0,
+ 14886003438020.0 / 654632330667.0, 35290686222309375.0 / 14152473387134411.0,
+ -1477884375.0 / 485066827.0},
+
+ // k12
+ {39815761.0 / 17514443.0, 0.0, 0.0,
+ (-3457480.0 - 960905.0 * FastMath.sqrt(6.0)) / 551636.0,
+ (-3457480.0 + 960905.0 * FastMath.sqrt(6.0)) / 551636.0,
+ -844554132.0 / 47026969.0, 8444996352.0 / 302158619.0,
+ -2509602342.0 / 877790785.0, -28388795297996250.0 / 3199510091356783.0,
+ 226716250.0 / 18341897.0, 1371316744.0 / 2131383595.0},
+
+ // k13 should be for interpolation only, but since it is the same
+ // stage as the first evaluation of the next step, we perform it
+ // here at no cost by specifying this is an fsal method
+ {104257.0/1920240.0, 0.0, 0.0, 0.0, 0.0, 3399327.0/763840.0,
+ 66578432.0/35198415.0, -1674902723.0/288716400.0,
+ 54980371265625.0/176692375811392.0, -734375.0/4826304.0,
+ 171414593.0/851261400.0, 137909.0/3084480.0}
+
+ };
+
+ /** Propagation weights Butcher array. */
+ private static final double[] STATIC_B = {
+ 104257.0/1920240.0,
+ 0.0,
+ 0.0,
+ 0.0,
+ 0.0,
+ 3399327.0/763840.0,
+ 66578432.0/35198415.0,
+ -1674902723.0/288716400.0,
+ 54980371265625.0/176692375811392.0,
+ -734375.0/4826304.0,
+ 171414593.0/851261400.0,
+ 137909.0/3084480.0,
+ 0.0
+ };
+
+ /** First error weights array, element 1. */
+ private static final double E1_01 = 116092271.0 / 8848465920.0;
+
+ // elements 2 to 5 are zero, so they are neither stored nor used
+
+ /** First error weights array, element 6. */
+ private static final double E1_06 = -1871647.0 / 1527680.0;
+
+ /** First error weights array, element 7. */
+ private static final double E1_07 = -69799717.0 / 140793660.0;
+
+ /** First error weights array, element 8. */
+ private static final double E1_08 = 1230164450203.0 / 739113984000.0;
+
+ /** First error weights array, element 9. */
+ private static final double E1_09 = -1980813971228885.0 / 5654156025964544.0;
+
+ /** First error weights array, element 10. */
+ private static final double E1_10 = 464500805.0 / 1389975552.0;
+
+ /** First error weights array, element 11. */
+ private static final double E1_11 = 1606764981773.0 / 19613062656000.0;
+
+ /** First error weights array, element 12. */
+ private static final double E1_12 = -137909.0 / 6168960.0;
+
+
+ /** Second error weights array, element 1. */
+ private static final double E2_01 = -364463.0 / 1920240.0;
+
+ // elements 2 to 5 are zero, so they are neither stored nor used
+
+ /** Second error weights array, element 6. */
+ private static final double E2_06 = 3399327.0 / 763840.0;
+
+ /** Second error weights array, element 7. */
+ private static final double E2_07 = 66578432.0 / 35198415.0;
+
+ /** Second error weights array, element 8. */
+ private static final double E2_08 = -1674902723.0 / 288716400.0;
+
+ /** Second error weights array, element 9. */
+ private static final double E2_09 = -74684743568175.0 / 176692375811392.0;
+
+ /** Second error weights array, element 10. */
+ private static final double E2_10 = -734375.0 / 4826304.0;
+
+ /** Second error weights array, element 11. */
+ private static final double E2_11 = 171414593.0 / 851261400.0;
+
+ /** Second error weights array, element 12. */
+ private static final double E2_12 = 69869.0 / 3084480.0;
+
+ /** Simple constructor.
+ * Build an eighth order Dormand-Prince integrator with the given step bounds
+ * @param minStep minimal step (sign is irrelevant, regardless of
+ * integration direction, forward or backward), the last step can
+ * be smaller than this
+ * @param maxStep maximal step (sign is irrelevant, regardless of
+ * integration direction, forward or backward), the last step can
+ * be smaller than this
+ * @param scalAbsoluteTolerance allowed absolute error
+ * @param scalRelativeTolerance allowed relative error
+ */
+ public DormandPrince853Integrator(final double minStep, final double maxStep,
+ final double scalAbsoluteTolerance,
+ final double scalRelativeTolerance) {
+ super(METHOD_NAME, true, STATIC_C, STATIC_A, STATIC_B,
+ new DormandPrince853StepInterpolator(),
+ minStep, maxStep, scalAbsoluteTolerance, scalRelativeTolerance);
+ }
+
+ /** Simple constructor.
+ * Build an eighth order Dormand-Prince integrator with the given step bounds
+ * @param minStep minimal step (sign is irrelevant, regardless of
+ * integration direction, forward or backward), the last step can
+ * be smaller than this
+ * @param maxStep maximal step (sign is irrelevant, regardless of
+ * integration direction, forward or backward), the last step can
+ * be smaller than this
+ * @param vecAbsoluteTolerance allowed absolute error
+ * @param vecRelativeTolerance allowed relative error
+ */
+ public DormandPrince853Integrator(final double minStep, final double maxStep,
+ final double[] vecAbsoluteTolerance,
+ final double[] vecRelativeTolerance) {
+ super(METHOD_NAME, true, STATIC_C, STATIC_A, STATIC_B,
+ new DormandPrince853StepInterpolator(),
+ minStep, maxStep, vecAbsoluteTolerance, vecRelativeTolerance);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public int getOrder() {
+ return 8;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ protected double estimateError(final double[][] yDotK,
+ final double[] y0, final double[] y1,
+ final double h) {
+ double error1 = 0;
+ double error2 = 0;
+
+ for (int j = 0; j < mainSetDimension; ++j) {
+ final double errSum1 = E1_01 * yDotK[0][j] + E1_06 * yDotK[5][j] +
+ E1_07 * yDotK[6][j] + E1_08 * yDotK[7][j] +
+ E1_09 * yDotK[8][j] + E1_10 * yDotK[9][j] +
+ E1_11 * yDotK[10][j] + E1_12 * yDotK[11][j];
+ final double errSum2 = E2_01 * yDotK[0][j] + E2_06 * yDotK[5][j] +
+ E2_07 * yDotK[6][j] + E2_08 * yDotK[7][j] +
+ E2_09 * yDotK[8][j] + E2_10 * yDotK[9][j] +
+ E2_11 * yDotK[10][j] + E2_12 * yDotK[11][j];
+
+ final double yScale = FastMath.max(FastMath.abs(y0[j]), FastMath.abs(y1[j]));
+ final double tol = (vecAbsoluteTolerance == null) ?
+ (scalAbsoluteTolerance + scalRelativeTolerance * yScale) :
+ (vecAbsoluteTolerance[j] + vecRelativeTolerance[j] * yScale);
+ final double ratio1 = errSum1 / tol;
+ error1 += ratio1 * ratio1;
+ final double ratio2 = errSum2 / tol;
+ error2 += ratio2 * ratio2;
+ }
+
+ double den = error1 + 0.01 * error2;
+ if (den <= 0.0) {
+ den = 1.0;
+ }
+
+ return FastMath.abs(h) * error1 / FastMath.sqrt(mainSetDimension * den);
+
+ }
+
+}
diff --git a/src/main/java/org/apache/commons/math3/ode/nonstiff/DormandPrince853StepInterpolator.java b/src/main/java/org/apache/commons/math3/ode/nonstiff/DormandPrince853StepInterpolator.java
new file mode 100644
index 0000000..6d6ff6c
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/ode/nonstiff/DormandPrince853StepInterpolator.java
@@ -0,0 +1,501 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math3.ode.nonstiff;
+
+import java.io.IOException;
+import java.io.ObjectInput;
+import java.io.ObjectOutput;
+
+import org.apache.commons.math3.exception.MaxCountExceededException;
+import org.apache.commons.math3.ode.AbstractIntegrator;
+import org.apache.commons.math3.ode.EquationsMapper;
+import org.apache.commons.math3.ode.sampling.StepInterpolator;
+
+/**
+ * This class represents an interpolator over the last step during an
+ * ODE integration for the 8(5,3) Dormand-Prince integrator.
+ *
+ * @see DormandPrince853Integrator
+ *
+ * @since 1.2
+ */
+
+class DormandPrince853StepInterpolator
+ extends RungeKuttaStepInterpolator {
+
+ /** Serializable version identifier. */
+ private static final long serialVersionUID = 20111120L;
+
+ /** Propagation weights, element 1. */
+ private static final double B_01 = 104257.0 / 1920240.0;
+
+ // elements 2 to 5 are zero, so they are neither stored nor used
+
+ /** Propagation weights, element 6. */
+ private static final double B_06 = 3399327.0 / 763840.0;
+
+ /** Propagation weights, element 7. */
+ private static final double B_07 = 66578432.0 / 35198415.0;
+
+ /** Propagation weights, element 8. */
+ private static final double B_08 = -1674902723.0 / 288716400.0;
+
+ /** Propagation weights, element 9. */
+ private static final double B_09 = 54980371265625.0 / 176692375811392.0;
+
+ /** Propagation weights, element 10. */
+ private static final double B_10 = -734375.0 / 4826304.0;
+
+ /** Propagation weights, element 11. */
+ private static final double B_11 = 171414593.0 / 851261400.0;
+
+ /** Propagation weights, element 12. */
+ private static final double B_12 = 137909.0 / 3084480.0;
+
+ /** Time step for stage 14 (interpolation only). */
+ private static final double C14 = 1.0 / 10.0;
+
+ /** Internal weights for stage 14, element 1. */
+ private static final double K14_01 = 13481885573.0 / 240030000000.0 - B_01;
+
+ // elements 2 to 5 are zero, so they are neither stored nor used
+
+ /** Internal weights for stage 14, element 6. */
+ private static final double K14_06 = 0.0 - B_06;
+
+ /** Internal weights for stage 14, element 7. */
+ private static final double K14_07 = 139418837528.0 / 549975234375.0 - B_07;
+
+ /** Internal weights for stage 14, element 8. */
+ private static final double K14_08 = -11108320068443.0 / 45111937500000.0 - B_08;
+
+ /** Internal weights for stage 14, element 9. */
+ private static final double K14_09 = -1769651421925959.0 / 14249385146080000.0 - B_09;
+
+ /** Internal weights for stage 14, element 10. */
+ private static final double K14_10 = 57799439.0 / 377055000.0 - B_10;
+
+ /** Internal weights for stage 14, element 11. */
+ private static final double K14_11 = 793322643029.0 / 96734250000000.0 - B_11;
+
+ /** Internal weights for stage 14, element 12. */
+ private static final double K14_12 = 1458939311.0 / 192780000000.0 - B_12;
+
+ /** Internal weights for stage 14, element 13. */
+ private static final double K14_13 = -4149.0 / 500000.0;
+
+ /** Time step for stage 15 (interpolation only). */
+ private static final double C15 = 1.0 / 5.0;
+
+
+ /** Internal weights for stage 15, element 1. */
+ private static final double K15_01 = 1595561272731.0 / 50120273500000.0 - B_01;
+
+ // elements 2 to 5 are zero, so they are neither stored nor used
+
+ /** Internal weights for stage 15, element 6. */
+ private static final double K15_06 = 975183916491.0 / 34457688031250.0 - B_06;
+
+ /** Internal weights for stage 15, element 7. */
+ private static final double K15_07 = 38492013932672.0 / 718912673015625.0 - B_07;
+
+ /** Internal weights for stage 15, element 8. */
+ private static final double K15_08 = -1114881286517557.0 / 20298710767500000.0 - B_08;
+
+ /** Internal weights for stage 15, element 9. */
+ private static final double K15_09 = 0.0 - B_09;
+
+ /** Internal weights for stage 15, element 10. */
+ private static final double K15_10 = 0.0 - B_10;
+
+ /** Internal weights for stage 15, element 11. */
+ private static final double K15_11 = -2538710946863.0 / 23431227861250000.0 - B_11;
+
+ /** Internal weights for stage 15, element 12. */
+ private static final double K15_12 = 8824659001.0 / 23066716781250.0 - B_12;
+
+ /** Internal weights for stage 15, element 13. */
+ private static final double K15_13 = -11518334563.0 / 33831184612500.0;
+
+ /** Internal weights for stage 15, element 14. */
+ private static final double K15_14 = 1912306948.0 / 13532473845.0;
+
+ /** Time step for stage 16 (interpolation only). */
+ private static final double C16 = 7.0 / 9.0;
+
+
+ /** Internal weights for stage 16, element 1. */
+ private static final double K16_01 = -13613986967.0 / 31741908048.0 - B_01;
+
+ // elements 2 to 5 are zero, so they are neither stored nor used
+
+ /** Internal weights for stage 16, element 6. */
+ private static final double K16_06 = -4755612631.0 / 1012344804.0 - B_06;
+
+ /** Internal weights for stage 16, element 7. */
+ private static final double K16_07 = 42939257944576.0 / 5588559685701.0 - B_07;
+
+ /** Internal weights for stage 16, element 8. */
+ private static final double K16_08 = 77881972900277.0 / 19140370552944.0 - B_08;
+
+ /** Internal weights for stage 16, element 9. */
+ private static final double K16_09 = 22719829234375.0 / 63689648654052.0 - B_09;
+
+ /** Internal weights for stage 16, element 10. */
+ private static final double K16_10 = 0.0 - B_10;
+
+ /** Internal weights for stage 16, element 11. */
+ private static final double K16_11 = 0.0 - B_11;
+
+ /** Internal weights for stage 16, element 12. */
+ private static final double K16_12 = 0.0 - B_12;
+
+ /** Internal weights for stage 16, element 13. */
+ private static final double K16_13 = -1199007803.0 / 857031517296.0;
+
+ /** Internal weights for stage 16, element 14. */
+ private static final double K16_14 = 157882067000.0 / 53564469831.0;
+
+ /** Internal weights for stage 16, element 15. */
+ private static final double K16_15 = -290468882375.0 / 31741908048.0;
+
+ /** Interpolation weights.
+ * (beware that only the non-null values are in the table)
+ */
+ private static final double[][] D = {
+
+ { -17751989329.0 / 2106076560.0, 4272954039.0 / 7539864640.0,
+ -118476319744.0 / 38604839385.0, 755123450731.0 / 316657731600.0,
+ 3692384461234828125.0 / 1744130441634250432.0, -4612609375.0 / 5293382976.0,
+ 2091772278379.0 / 933644586600.0, 2136624137.0 / 3382989120.0,
+ -126493.0 / 1421424.0, 98350000.0 / 5419179.0,
+ -18878125.0 / 2053168.0, -1944542619.0 / 438351368.0},
+
+ { 32941697297.0 / 3159114840.0, 456696183123.0 / 1884966160.0,
+ 19132610714624.0 / 115814518155.0, -177904688592943.0 / 474986597400.0,
+ -4821139941836765625.0 / 218016305204281304.0, 30702015625.0 / 3970037232.0,
+ -85916079474274.0 / 2800933759800.0, -5919468007.0 / 634310460.0,
+ 2479159.0 / 157936.0, -18750000.0 / 602131.0,
+ -19203125.0 / 2053168.0, 15700361463.0 / 438351368.0},
+
+ { 12627015655.0 / 631822968.0, -72955222965.0 / 188496616.0,
+ -13145744952320.0 / 69488710893.0, 30084216194513.0 / 56998391688.0,
+ -296858761006640625.0 / 25648977082856624.0, 569140625.0 / 82709109.0,
+ -18684190637.0 / 18672891732.0, 69644045.0 / 89549712.0,
+ -11847025.0 / 4264272.0, -978650000.0 / 16257537.0,
+ 519371875.0 / 6159504.0, 5256837225.0 / 438351368.0},
+
+ { -450944925.0 / 17550638.0, -14532122925.0 / 94248308.0,
+ -595876966400.0 / 2573655959.0, 188748653015.0 / 527762886.0,
+ 2545485458115234375.0 / 27252038150535163.0, -1376953125.0 / 36759604.0,
+ 53995596795.0 / 518691437.0, 210311225.0 / 7047894.0,
+ -1718875.0 / 39484.0, 58000000.0 / 602131.0,
+ -1546875.0 / 39484.0, -1262172375.0 / 8429834.0}
+
+ };
+
+ /** Last evaluations. */
+ private double[][] yDotKLast;
+
+ /** Vectors for interpolation. */
+ private double[][] v;
+
+ /** Initialization indicator for the interpolation vectors. */
+ private boolean vectorsInitialized;
+
+ /** Simple constructor.
+ * This constructor builds an instance that is not usable yet, the
+ * {@link #reinitialize} method should be called before using the
+ * instance in order to initialize the internal arrays. This
+ * constructor is used only in order to delay the initialization in
+ * some cases. The {@link EmbeddedRungeKuttaIntegrator} uses the
+ * prototyping design pattern to create the step interpolators by
+ * cloning an uninitialized model and latter initializing the copy.
+ */
+ // CHECKSTYLE: stop RedundantModifier
+ // the public modifier here is needed for serialization
+ public DormandPrince853StepInterpolator() {
+ super();
+ yDotKLast = null;
+ v = null;
+ vectorsInitialized = false;
+ }
+ // CHECKSTYLE: resume RedundantModifier
+
+ /** Copy constructor.
+ * @param interpolator interpolator to copy from. The copy is a deep
+ * copy: its arrays are separated from the original arrays of the
+ * instance
+ */
+ DormandPrince853StepInterpolator(final DormandPrince853StepInterpolator interpolator) {
+
+ super(interpolator);
+
+ if (interpolator.currentState == null) {
+
+ yDotKLast = null;
+ v = null;
+ vectorsInitialized = false;
+
+ } else {
+
+ final int dimension = interpolator.currentState.length;
+
+ yDotKLast = new double[3][];
+ for (int k = 0; k < yDotKLast.length; ++k) {
+ yDotKLast[k] = new double[dimension];
+ System.arraycopy(interpolator.yDotKLast[k], 0, yDotKLast[k], 0,
+ dimension);
+ }
+
+ v = new double[7][];
+ for (int k = 0; k < v.length; ++k) {
+ v[k] = new double[dimension];
+ System.arraycopy(interpolator.v[k], 0, v[k], 0, dimension);
+ }
+
+ vectorsInitialized = interpolator.vectorsInitialized;
+
+ }
+
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ protected StepInterpolator doCopy() {
+ return new DormandPrince853StepInterpolator(this);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void reinitialize(final AbstractIntegrator integrator,
+ final double[] y, final double[][] yDotK, final boolean forward,
+ final EquationsMapper primaryMapper,
+ final EquationsMapper[] secondaryMappers) {
+
+ super.reinitialize(integrator, y, yDotK, forward, primaryMapper, secondaryMappers);
+
+ final int dimension = currentState.length;
+
+ yDotKLast = new double[3][];
+ for (int k = 0; k < yDotKLast.length; ++k) {
+ yDotKLast[k] = new double[dimension];
+ }
+
+ v = new double[7][];
+ for (int k = 0; k < v.length; ++k) {
+ v[k] = new double[dimension];
+ }
+
+ vectorsInitialized = false;
+
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void storeTime(final double t) {
+ super.storeTime(t);
+ vectorsInitialized = false;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ protected void computeInterpolatedStateAndDerivatives(final double theta,
+ final double oneMinusThetaH)
+ throws MaxCountExceededException {
+
+ if (! vectorsInitialized) {
+
+ if (v == null) {
+ v = new double[7][];
+ for (int k = 0; k < 7; ++k) {
+ v[k] = new double[interpolatedState.length];
+ }
+ }
+
+ // perform the last evaluations if they have not been done yet
+ finalizeStep();
+
+ // compute the interpolation vectors for this time step
+ for (int i = 0; i < interpolatedState.length; ++i) {
+ final double yDot1 = yDotK[0][i];
+ final double yDot6 = yDotK[5][i];
+ final double yDot7 = yDotK[6][i];
+ final double yDot8 = yDotK[7][i];
+ final double yDot9 = yDotK[8][i];
+ final double yDot10 = yDotK[9][i];
+ final double yDot11 = yDotK[10][i];
+ final double yDot12 = yDotK[11][i];
+ final double yDot13 = yDotK[12][i];
+ final double yDot14 = yDotKLast[0][i];
+ final double yDot15 = yDotKLast[1][i];
+ final double yDot16 = yDotKLast[2][i];
+ v[0][i] = B_01 * yDot1 + B_06 * yDot6 + B_07 * yDot7 +
+ B_08 * yDot8 + B_09 * yDot9 + B_10 * yDot10 +
+ B_11 * yDot11 + B_12 * yDot12;
+ v[1][i] = yDot1 - v[0][i];
+ v[2][i] = v[0][i] - v[1][i] - yDotK[12][i];
+ for (int k = 0; k < D.length; ++k) {
+ v[k+3][i] = D[k][0] * yDot1 + D[k][1] * yDot6 + D[k][2] * yDot7 +
+ D[k][3] * yDot8 + D[k][4] * yDot9 + D[k][5] * yDot10 +
+ D[k][6] * yDot11 + D[k][7] * yDot12 + D[k][8] * yDot13 +
+ D[k][9] * yDot14 + D[k][10] * yDot15 + D[k][11] * yDot16;
+ }
+ }
+
+ vectorsInitialized = true;
+
+ }
+
+ final double eta = 1 - theta;
+ final double twoTheta = 2 * theta;
+ final double theta2 = theta * theta;
+ final double dot1 = 1 - twoTheta;
+ final double dot2 = theta * (2 - 3 * theta);
+ final double dot3 = twoTheta * (1 + theta * (twoTheta -3));
+ final double dot4 = theta2 * (3 + theta * (5 * theta - 8));
+ final double dot5 = theta2 * (3 + theta * (-12 + theta * (15 - 6 * theta)));
+ final double dot6 = theta2 * theta * (4 + theta * (-15 + theta * (18 - 7 * theta)));
+
+ if ((previousState != null) && (theta <= 0.5)) {
+ for (int i = 0; i < interpolatedState.length; ++i) {
+ interpolatedState[i] = previousState[i] +
+ theta * h * (v[0][i] +
+ eta * (v[1][i] +
+ theta * (v[2][i] +
+ eta * (v[3][i] +
+ theta * (v[4][i] +
+ eta * (v[5][i] +
+ theta * (v[6][i])))))));
+ interpolatedDerivatives[i] = v[0][i] + dot1 * v[1][i] + dot2 * v[2][i] +
+ dot3 * v[3][i] + dot4 * v[4][i] +
+ dot5 * v[5][i] + dot6 * v[6][i];
+ }
+ } else {
+ for (int i = 0; i < interpolatedState.length; ++i) {
+ interpolatedState[i] = currentState[i] -
+ oneMinusThetaH * (v[0][i] -
+ theta * (v[1][i] +
+ theta * (v[2][i] +
+ eta * (v[3][i] +
+ theta * (v[4][i] +
+ eta * (v[5][i] +
+ theta * (v[6][i])))))));
+ interpolatedDerivatives[i] = v[0][i] + dot1 * v[1][i] + dot2 * v[2][i] +
+ dot3 * v[3][i] + dot4 * v[4][i] +
+ dot5 * v[5][i] + dot6 * v[6][i];
+ }
+ }
+
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ protected void doFinalize() throws MaxCountExceededException {
+
+ if (currentState == null) {
+ // we are finalizing an uninitialized instance
+ return;
+ }
+
+ double s;
+ final double[] yTmp = new double[currentState.length];
+ final double pT = getGlobalPreviousTime();
+
+ // k14
+ for (int j = 0; j < currentState.length; ++j) {
+ s = K14_01 * yDotK[0][j] + K14_06 * yDotK[5][j] + K14_07 * yDotK[6][j] +
+ K14_08 * yDotK[7][j] + K14_09 * yDotK[8][j] + K14_10 * yDotK[9][j] +
+ K14_11 * yDotK[10][j] + K14_12 * yDotK[11][j] + K14_13 * yDotK[12][j];
+ yTmp[j] = currentState[j] + h * s;
+ }
+ integrator.computeDerivatives(pT + C14 * h, yTmp, yDotKLast[0]);
+
+ // k15
+ for (int j = 0; j < currentState.length; ++j) {
+ s = K15_01 * yDotK[0][j] + K15_06 * yDotK[5][j] + K15_07 * yDotK[6][j] +
+ K15_08 * yDotK[7][j] + K15_09 * yDotK[8][j] + K15_10 * yDotK[9][j] +
+ K15_11 * yDotK[10][j] + K15_12 * yDotK[11][j] + K15_13 * yDotK[12][j] +
+ K15_14 * yDotKLast[0][j];
+ yTmp[j] = currentState[j] + h * s;
+ }
+ integrator.computeDerivatives(pT + C15 * h, yTmp, yDotKLast[1]);
+
+ // k16
+ for (int j = 0; j < currentState.length; ++j) {
+ s = K16_01 * yDotK[0][j] + K16_06 * yDotK[5][j] + K16_07 * yDotK[6][j] +
+ K16_08 * yDotK[7][j] + K16_09 * yDotK[8][j] + K16_10 * yDotK[9][j] +
+ K16_11 * yDotK[10][j] + K16_12 * yDotK[11][j] + K16_13 * yDotK[12][j] +
+ K16_14 * yDotKLast[0][j] + K16_15 * yDotKLast[1][j];
+ yTmp[j] = currentState[j] + h * s;
+ }
+ integrator.computeDerivatives(pT + C16 * h, yTmp, yDotKLast[2]);
+
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void writeExternal(final ObjectOutput out)
+ throws IOException {
+
+ try {
+ // save the local attributes
+ finalizeStep();
+ } catch (MaxCountExceededException mcee) {
+ final IOException ioe = new IOException(mcee.getLocalizedMessage());
+ ioe.initCause(mcee);
+ throw ioe;
+ }
+
+ final int dimension = (currentState == null) ? -1 : currentState.length;
+ out.writeInt(dimension);
+ for (int i = 0; i < dimension; ++i) {
+ out.writeDouble(yDotKLast[0][i]);
+ out.writeDouble(yDotKLast[1][i]);
+ out.writeDouble(yDotKLast[2][i]);
+ }
+
+ // save the state of the base class
+ super.writeExternal(out);
+
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void readExternal(final ObjectInput in)
+ throws IOException, ClassNotFoundException {
+
+ // read the local attributes
+ yDotKLast = new double[3][];
+ final int dimension = in.readInt();
+ yDotKLast[0] = (dimension < 0) ? null : new double[dimension];
+ yDotKLast[1] = (dimension < 0) ? null : new double[dimension];
+ yDotKLast[2] = (dimension < 0) ? null : new double[dimension];
+
+ for (int i = 0; i < dimension; ++i) {
+ yDotKLast[0][i] = in.readDouble();
+ yDotKLast[1][i] = in.readDouble();
+ yDotKLast[2][i] = in.readDouble();
+ }
+
+ // read the base state
+ super.readExternal(in);
+
+ }
+
+}
diff --git a/src/main/java/org/apache/commons/math3/ode/nonstiff/EmbeddedRungeKuttaFieldIntegrator.java b/src/main/java/org/apache/commons/math3/ode/nonstiff/EmbeddedRungeKuttaFieldIntegrator.java
new file mode 100644
index 0000000..036cf01
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/ode/nonstiff/EmbeddedRungeKuttaFieldIntegrator.java
@@ -0,0 +1,385 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math3.ode.nonstiff;
+
+import org.apache.commons.math3.Field;
+import org.apache.commons.math3.RealFieldElement;
+import org.apache.commons.math3.exception.DimensionMismatchException;
+import org.apache.commons.math3.exception.MaxCountExceededException;
+import org.apache.commons.math3.exception.NoBracketingException;
+import org.apache.commons.math3.exception.NumberIsTooSmallException;
+import org.apache.commons.math3.ode.FieldEquationsMapper;
+import org.apache.commons.math3.ode.FieldExpandableODE;
+import org.apache.commons.math3.ode.FieldODEState;
+import org.apache.commons.math3.ode.FieldODEStateAndDerivative;
+import org.apache.commons.math3.util.MathArrays;
+import org.apache.commons.math3.util.MathUtils;
+
+/**
+ * This class implements the common part of all embedded Runge-Kutta
+ * integrators for Ordinary Differential Equations.
+ *
+ * <p>These methods are embedded explicit Runge-Kutta methods with two
+ * sets of coefficients allowing to estimate the error, their Butcher
+ * arrays are as follows :
+ * <pre>
+ * 0 |
+ * c2 | a21
+ * c3 | a31 a32
+ * ... | ...
+ * cs | as1 as2 ... ass-1
+ * |--------------------------
+ * | b1 b2 ... bs-1 bs
+ * | b'1 b'2 ... b's-1 b's
+ * </pre>
+ * </p>
+ *
+ * <p>In fact, we rather use the array defined by ej = bj - b'j to
+ * compute directly the error rather than computing two estimates and
+ * then comparing them.</p>
+ *
+ * <p>Some methods are qualified as <i>fsal</i> (first same as last)
+ * methods. This means the last evaluation of the derivatives in one
+ * step is the same as the first in the next step. Then, this
+ * evaluation can be reused from one step to the next one and the cost
+ * of such a method is really s-1 evaluations despite the method still
+ * has s stages. This behaviour is true only for successful steps, if
+ * the step is rejected after the error estimation phase, no
+ * evaluation is saved. For an <i>fsal</i> method, we have cs = 1 and
+ * asi = bi for all i.</p>
+ *
+ * @param <T> the type of the field elements
+ * @since 3.6
+ */
+
+public abstract class EmbeddedRungeKuttaFieldIntegrator<T extends RealFieldElement<T>>
+ extends AdaptiveStepsizeFieldIntegrator<T>
+ implements FieldButcherArrayProvider<T> {
+
+ /** Index of the pre-computed derivative for <i>fsal</i> methods. */
+ private final int fsal;
+
+ /** Time steps from Butcher array (without the first zero). */
+ private final T[] c;
+
+ /** Internal weights from Butcher array (without the first empty row). */
+ private final T[][] a;
+
+ /** External weights for the high order method from Butcher array. */
+ private final T[] b;
+
+ /** Stepsize control exponent. */
+ private final T exp;
+
+ /** Safety factor for stepsize control. */
+ private T safety;
+
+ /** Minimal reduction factor for stepsize control. */
+ private T minReduction;
+
+ /** Maximal growth factor for stepsize control. */
+ private T maxGrowth;
+
+ /** Build a Runge-Kutta integrator with the given Butcher array.
+ * @param field field to which the time and state vector elements belong
+ * @param name name of the method
+ * @param fsal index of the pre-computed derivative for <i>fsal</i> methods
+ * or -1 if method is not <i>fsal</i>
+ * @param minStep minimal step (sign is irrelevant, regardless of
+ * integration direction, forward or backward), the last step can
+ * be smaller than this
+ * @param maxStep maximal step (sign is irrelevant, regardless of
+ * integration direction, forward or backward), the last step can
+ * be smaller than this
+ * @param scalAbsoluteTolerance allowed absolute error
+ * @param scalRelativeTolerance allowed relative error
+ */
+ protected EmbeddedRungeKuttaFieldIntegrator(final Field<T> field, final String name, final int fsal,
+ final double minStep, final double maxStep,
+ final double scalAbsoluteTolerance,
+ final double scalRelativeTolerance) {
+
+ super(field, name, minStep, maxStep, scalAbsoluteTolerance, scalRelativeTolerance);
+
+ this.fsal = fsal;
+ this.c = getC();
+ this.a = getA();
+ this.b = getB();
+
+ exp = field.getOne().divide(-getOrder());
+
+ // set the default values of the algorithm control parameters
+ setSafety(field.getZero().add(0.9));
+ setMinReduction(field.getZero().add(0.2));
+ setMaxGrowth(field.getZero().add(10.0));
+
+ }
+
+ /** Build a Runge-Kutta integrator with the given Butcher array.
+ * @param field field to which the time and state vector elements belong
+ * @param name name of the method
+ * @param fsal index of the pre-computed derivative for <i>fsal</i> methods
+ * or -1 if method is not <i>fsal</i>
+ * @param minStep minimal step (must be positive even for backward
+ * integration), the last step can be smaller than this
+ * @param maxStep maximal step (must be positive even for backward
+ * integration)
+ * @param vecAbsoluteTolerance allowed absolute error
+ * @param vecRelativeTolerance allowed relative error
+ */
+ protected EmbeddedRungeKuttaFieldIntegrator(final Field<T> field, final String name, final int fsal,
+ final double minStep, final double maxStep,
+ final double[] vecAbsoluteTolerance,
+ final double[] vecRelativeTolerance) {
+
+ super(field, name, minStep, maxStep, vecAbsoluteTolerance, vecRelativeTolerance);
+
+ this.fsal = fsal;
+ this.c = getC();
+ this.a = getA();
+ this.b = getB();
+
+ exp = field.getOne().divide(-getOrder());
+
+ // set the default values of the algorithm control parameters
+ setSafety(field.getZero().add(0.9));
+ setMinReduction(field.getZero().add(0.2));
+ setMaxGrowth(field.getZero().add(10.0));
+
+ }
+
+ /** Create a fraction.
+ * @param p numerator
+ * @param q denominator
+ * @return p/q computed in the instance field
+ */
+ protected T fraction(final int p, final int q) {
+ return getField().getOne().multiply(p).divide(q);
+ }
+
+ /** Create a fraction.
+ * @param p numerator
+ * @param q denominator
+ * @return p/q computed in the instance field
+ */
+ protected T fraction(final double p, final double q) {
+ return getField().getOne().multiply(p).divide(q);
+ }
+
+ /** Create an interpolator.
+ * @param forward integration direction indicator
+ * @param yDotK slopes at the intermediate points
+ * @param globalPreviousState start of the global step
+ * @param globalCurrentState end of the global step
+ * @param mapper equations mapper for the all equations
+ * @return external weights for the high order method from Butcher array
+ */
+ protected abstract RungeKuttaFieldStepInterpolator<T> createInterpolator(boolean forward, T[][] yDotK,
+ final FieldODEStateAndDerivative<T> globalPreviousState,
+ final FieldODEStateAndDerivative<T> globalCurrentState,
+ FieldEquationsMapper<T> mapper);
+ /** Get the order of the method.
+ * @return order of the method
+ */
+ public abstract int getOrder();
+
+ /** Get the safety factor for stepsize control.
+ * @return safety factor
+ */
+ public T getSafety() {
+ return safety;
+ }
+
+ /** Set the safety factor for stepsize control.
+ * @param safety safety factor
+ */
+ public void setSafety(final T safety) {
+ this.safety = safety;
+ }
+
+ /** {@inheritDoc} */
+ public FieldODEStateAndDerivative<T> integrate(final FieldExpandableODE<T> equations,
+ final FieldODEState<T> initialState, final T finalTime)
+ throws NumberIsTooSmallException, DimensionMismatchException,
+ MaxCountExceededException, NoBracketingException {
+
+ sanityChecks(initialState, finalTime);
+ final T t0 = initialState.getTime();
+ final T[] y0 = equations.getMapper().mapState(initialState);
+ setStepStart(initIntegration(equations, t0, y0, finalTime));
+ final boolean forward = finalTime.subtract(initialState.getTime()).getReal() > 0;
+
+ // create some internal working arrays
+ final int stages = c.length + 1;
+ T[] y = y0;
+ final T[][] yDotK = MathArrays.buildArray(getField(), stages, -1);
+ final T[] yTmp = MathArrays.buildArray(getField(), y0.length);
+
+ // set up integration control objects
+ T hNew = getField().getZero();
+ boolean firstTime = true;
+
+ // main integration loop
+ setIsLastStep(false);
+ do {
+
+ // iterate over step size, ensuring local normalized error is smaller than 1
+ T error = getField().getZero().add(10);
+ while (error.subtract(1.0).getReal() >= 0) {
+
+ // first stage
+ y = equations.getMapper().mapState(getStepStart());
+ yDotK[0] = equations.getMapper().mapDerivative(getStepStart());
+
+ if (firstTime) {
+ final T[] scale = MathArrays.buildArray(getField(), mainSetDimension);
+ if (vecAbsoluteTolerance == null) {
+ for (int i = 0; i < scale.length; ++i) {
+ scale[i] = y[i].abs().multiply(scalRelativeTolerance).add(scalAbsoluteTolerance);
+ }
+ } else {
+ for (int i = 0; i < scale.length; ++i) {
+ scale[i] = y[i].abs().multiply(vecRelativeTolerance[i]).add(vecAbsoluteTolerance[i]);
+ }
+ }
+ hNew = initializeStep(forward, getOrder(), scale, getStepStart(), equations.getMapper());
+ firstTime = false;
+ }
+
+ setStepSize(hNew);
+ if (forward) {
+ if (getStepStart().getTime().add(getStepSize()).subtract(finalTime).getReal() >= 0) {
+ setStepSize(finalTime.subtract(getStepStart().getTime()));
+ }
+ } else {
+ if (getStepStart().getTime().add(getStepSize()).subtract(finalTime).getReal() <= 0) {
+ setStepSize(finalTime.subtract(getStepStart().getTime()));
+ }
+ }
+
+ // next stages
+ for (int k = 1; k < stages; ++k) {
+
+ for (int j = 0; j < y0.length; ++j) {
+ T sum = yDotK[0][j].multiply(a[k-1][0]);
+ for (int l = 1; l < k; ++l) {
+ sum = sum.add(yDotK[l][j].multiply(a[k-1][l]));
+ }
+ yTmp[j] = y[j].add(getStepSize().multiply(sum));
+ }
+
+ yDotK[k] = computeDerivatives(getStepStart().getTime().add(getStepSize().multiply(c[k-1])), yTmp);
+
+ }
+
+ // estimate the state at the end of the step
+ for (int j = 0; j < y0.length; ++j) {
+ T sum = yDotK[0][j].multiply(b[0]);
+ for (int l = 1; l < stages; ++l) {
+ sum = sum.add(yDotK[l][j].multiply(b[l]));
+ }
+ yTmp[j] = y[j].add(getStepSize().multiply(sum));
+ }
+
+ // estimate the error at the end of the step
+ error = estimateError(yDotK, y, yTmp, getStepSize());
+ if (error.subtract(1.0).getReal() >= 0) {
+ // reject the step and attempt to reduce error by stepsize control
+ final T factor = MathUtils.min(maxGrowth,
+ MathUtils.max(minReduction, safety.multiply(error.pow(exp))));
+ hNew = filterStep(getStepSize().multiply(factor), forward, false);
+ }
+
+ }
+ final T stepEnd = getStepStart().getTime().add(getStepSize());
+ final T[] yDotTmp = (fsal >= 0) ? yDotK[fsal] : computeDerivatives(stepEnd, yTmp);
+ final FieldODEStateAndDerivative<T> stateTmp = new FieldODEStateAndDerivative<T>(stepEnd, yTmp, yDotTmp);
+
+ // local error is small enough: accept the step, trigger events and step handlers
+ System.arraycopy(yTmp, 0, y, 0, y0.length);
+ setStepStart(acceptStep(createInterpolator(forward, yDotK, getStepStart(), stateTmp, equations.getMapper()),
+ finalTime));
+
+ if (!isLastStep()) {
+
+ // stepsize control for next step
+ final T factor = MathUtils.min(maxGrowth,
+ MathUtils.max(minReduction, safety.multiply(error.pow(exp))));
+ final T scaledH = getStepSize().multiply(factor);
+ final T nextT = getStepStart().getTime().add(scaledH);
+ final boolean nextIsLast = forward ?
+ nextT.subtract(finalTime).getReal() >= 0 :
+ nextT.subtract(finalTime).getReal() <= 0;
+ hNew = filterStep(scaledH, forward, nextIsLast);
+
+ final T filteredNextT = getStepStart().getTime().add(hNew);
+ final boolean filteredNextIsLast = forward ?
+ filteredNextT.subtract(finalTime).getReal() >= 0 :
+ filteredNextT.subtract(finalTime).getReal() <= 0;
+ if (filteredNextIsLast) {
+ hNew = finalTime.subtract(getStepStart().getTime());
+ }
+
+ }
+
+ } while (!isLastStep());
+
+ final FieldODEStateAndDerivative<T> finalState = getStepStart();
+ resetInternalState();
+ return finalState;
+
+ }
+
+ /** Get the minimal reduction factor for stepsize control.
+ * @return minimal reduction factor
+ */
+ public T getMinReduction() {
+ return minReduction;
+ }
+
+ /** Set the minimal reduction factor for stepsize control.
+ * @param minReduction minimal reduction factor
+ */
+ public void setMinReduction(final T minReduction) {
+ this.minReduction = minReduction;
+ }
+
+ /** Get the maximal growth factor for stepsize control.
+ * @return maximal growth factor
+ */
+ public T getMaxGrowth() {
+ return maxGrowth;
+ }
+
+ /** Set the maximal growth factor for stepsize control.
+ * @param maxGrowth maximal growth factor
+ */
+ public void setMaxGrowth(final T maxGrowth) {
+ this.maxGrowth = maxGrowth;
+ }
+
+ /** Compute the error ratio.
+ * @param yDotK derivatives computed during the first stages
+ * @param y0 estimate of the step at the start of the step
+ * @param y1 estimate of the step at the end of the step
+ * @param h current step
+ * @return error ratio, greater than 1 if step should be rejected
+ */
+ protected abstract T estimateError(T[][] yDotK, T[] y0, T[] y1, T h);
+
+}
diff --git a/src/main/java/org/apache/commons/math3/ode/nonstiff/EmbeddedRungeKuttaIntegrator.java b/src/main/java/org/apache/commons/math3/ode/nonstiff/EmbeddedRungeKuttaIntegrator.java
new file mode 100644
index 0000000..098d2e5
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/ode/nonstiff/EmbeddedRungeKuttaIntegrator.java
@@ -0,0 +1,380 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math3.ode.nonstiff;
+
+import org.apache.commons.math3.exception.DimensionMismatchException;
+import org.apache.commons.math3.exception.MaxCountExceededException;
+import org.apache.commons.math3.exception.NoBracketingException;
+import org.apache.commons.math3.exception.NumberIsTooSmallException;
+import org.apache.commons.math3.ode.ExpandableStatefulODE;
+import org.apache.commons.math3.util.FastMath;
+
+/**
+ * This class implements the common part of all embedded Runge-Kutta
+ * integrators for Ordinary Differential Equations.
+ *
+ * <p>These methods are embedded explicit Runge-Kutta methods with two
+ * sets of coefficients allowing to estimate the error, their Butcher
+ * arrays are as follows :
+ * <pre>
+ * 0 |
+ * c2 | a21
+ * c3 | a31 a32
+ * ... | ...
+ * cs | as1 as2 ... ass-1
+ * |--------------------------
+ * | b1 b2 ... bs-1 bs
+ * | b'1 b'2 ... b's-1 b's
+ * </pre>
+ * </p>
+ *
+ * <p>In fact, we rather use the array defined by ej = bj - b'j to
+ * compute directly the error rather than computing two estimates and
+ * then comparing them.</p>
+ *
+ * <p>Some methods are qualified as <i>fsal</i> (first same as last)
+ * methods. This means the last evaluation of the derivatives in one
+ * step is the same as the first in the next step. Then, this
+ * evaluation can be reused from one step to the next one and the cost
+ * of such a method is really s-1 evaluations despite the method still
+ * has s stages. This behaviour is true only for successful steps, if
+ * the step is rejected after the error estimation phase, no
+ * evaluation is saved. For an <i>fsal</i> method, we have cs = 1 and
+ * asi = bi for all i.</p>
+ *
+ * @since 1.2
+ */
+
+public abstract class EmbeddedRungeKuttaIntegrator
+ extends AdaptiveStepsizeIntegrator {
+
+ /** Indicator for <i>fsal</i> methods. */
+ private final boolean fsal;
+
+ /** Time steps from Butcher array (without the first zero). */
+ private final double[] c;
+
+ /** Internal weights from Butcher array (without the first empty row). */
+ private final double[][] a;
+
+ /** External weights for the high order method from Butcher array. */
+ private final double[] b;
+
+ /** Prototype of the step interpolator. */
+ private final RungeKuttaStepInterpolator prototype;
+
+ /** Stepsize control exponent. */
+ private final double exp;
+
+ /** Safety factor for stepsize control. */
+ private double safety;
+
+ /** Minimal reduction factor for stepsize control. */
+ private double minReduction;
+
+ /** Maximal growth factor for stepsize control. */
+ private double maxGrowth;
+
+ /** Build a Runge-Kutta integrator with the given Butcher array.
+ * @param name name of the method
+ * @param fsal indicate that the method is an <i>fsal</i>
+ * @param c time steps from Butcher array (without the first zero)
+ * @param a internal weights from Butcher array (without the first empty row)
+ * @param b propagation weights for the high order method from Butcher array
+ * @param prototype prototype of the step interpolator to use
+ * @param minStep minimal step (sign is irrelevant, regardless of
+ * integration direction, forward or backward), the last step can
+ * be smaller than this
+ * @param maxStep maximal step (sign is irrelevant, regardless of
+ * integration direction, forward or backward), the last step can
+ * be smaller than this
+ * @param scalAbsoluteTolerance allowed absolute error
+ * @param scalRelativeTolerance allowed relative error
+ */
+ protected EmbeddedRungeKuttaIntegrator(final String name, final boolean fsal,
+ final double[] c, final double[][] a, final double[] b,
+ final RungeKuttaStepInterpolator prototype,
+ final double minStep, final double maxStep,
+ final double scalAbsoluteTolerance,
+ final double scalRelativeTolerance) {
+
+ super(name, minStep, maxStep, scalAbsoluteTolerance, scalRelativeTolerance);
+
+ this.fsal = fsal;
+ this.c = c;
+ this.a = a;
+ this.b = b;
+ this.prototype = prototype;
+
+ exp = -1.0 / getOrder();
+
+ // set the default values of the algorithm control parameters
+ setSafety(0.9);
+ setMinReduction(0.2);
+ setMaxGrowth(10.0);
+
+ }
+
+ /** Build a Runge-Kutta integrator with the given Butcher array.
+ * @param name name of the method
+ * @param fsal indicate that the method is an <i>fsal</i>
+ * @param c time steps from Butcher array (without the first zero)
+ * @param a internal weights from Butcher array (without the first empty row)
+ * @param b propagation weights for the high order method from Butcher array
+ * @param prototype prototype of the step interpolator to use
+ * @param minStep minimal step (must be positive even for backward
+ * integration), the last step can be smaller than this
+ * @param maxStep maximal step (must be positive even for backward
+ * integration)
+ * @param vecAbsoluteTolerance allowed absolute error
+ * @param vecRelativeTolerance allowed relative error
+ */
+ protected EmbeddedRungeKuttaIntegrator(final String name, final boolean fsal,
+ final double[] c, final double[][] a, final double[] b,
+ final RungeKuttaStepInterpolator prototype,
+ final double minStep, final double maxStep,
+ final double[] vecAbsoluteTolerance,
+ final double[] vecRelativeTolerance) {
+
+ super(name, minStep, maxStep, vecAbsoluteTolerance, vecRelativeTolerance);
+
+ this.fsal = fsal;
+ this.c = c;
+ this.a = a;
+ this.b = b;
+ this.prototype = prototype;
+
+ exp = -1.0 / getOrder();
+
+ // set the default values of the algorithm control parameters
+ setSafety(0.9);
+ setMinReduction(0.2);
+ setMaxGrowth(10.0);
+
+ }
+
+ /** Get the order of the method.
+ * @return order of the method
+ */
+ public abstract int getOrder();
+
+ /** Get the safety factor for stepsize control.
+ * @return safety factor
+ */
+ public double getSafety() {
+ return safety;
+ }
+
+ /** Set the safety factor for stepsize control.
+ * @param safety safety factor
+ */
+ public void setSafety(final double safety) {
+ this.safety = safety;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void integrate(final ExpandableStatefulODE equations, final double t)
+ throws NumberIsTooSmallException, DimensionMismatchException,
+ MaxCountExceededException, NoBracketingException {
+
+ sanityChecks(equations, t);
+ setEquations(equations);
+ final boolean forward = t > equations.getTime();
+
+ // create some internal working arrays
+ final double[] y0 = equations.getCompleteState();
+ final double[] y = y0.clone();
+ final int stages = c.length + 1;
+ final double[][] yDotK = new double[stages][y.length];
+ final double[] yTmp = y0.clone();
+ final double[] yDotTmp = new double[y.length];
+
+ // set up an interpolator sharing the integrator arrays
+ final RungeKuttaStepInterpolator interpolator = (RungeKuttaStepInterpolator) prototype.copy();
+ interpolator.reinitialize(this, yTmp, yDotK, forward,
+ equations.getPrimaryMapper(), equations.getSecondaryMappers());
+ interpolator.storeTime(equations.getTime());
+
+ // set up integration control objects
+ stepStart = equations.getTime();
+ double hNew = 0;
+ boolean firstTime = true;
+ initIntegration(equations.getTime(), y0, t);
+
+ // main integration loop
+ isLastStep = false;
+ do {
+
+ interpolator.shift();
+
+ // iterate over step size, ensuring local normalized error is smaller than 1
+ double error = 10;
+ while (error >= 1.0) {
+
+ if (firstTime || !fsal) {
+ // first stage
+ computeDerivatives(stepStart, y, yDotK[0]);
+ }
+
+ if (firstTime) {
+ final double[] scale = new double[mainSetDimension];
+ if (vecAbsoluteTolerance == null) {
+ for (int i = 0; i < scale.length; ++i) {
+ scale[i] = scalAbsoluteTolerance + scalRelativeTolerance * FastMath.abs(y[i]);
+ }
+ } else {
+ for (int i = 0; i < scale.length; ++i) {
+ scale[i] = vecAbsoluteTolerance[i] + vecRelativeTolerance[i] * FastMath.abs(y[i]);
+ }
+ }
+ hNew = initializeStep(forward, getOrder(), scale,
+ stepStart, y, yDotK[0], yTmp, yDotK[1]);
+ firstTime = false;
+ }
+
+ stepSize = hNew;
+ if (forward) {
+ if (stepStart + stepSize >= t) {
+ stepSize = t - stepStart;
+ }
+ } else {
+ if (stepStart + stepSize <= t) {
+ stepSize = t - stepStart;
+ }
+ }
+
+ // next stages
+ for (int k = 1; k < stages; ++k) {
+
+ for (int j = 0; j < y0.length; ++j) {
+ double sum = a[k-1][0] * yDotK[0][j];
+ for (int l = 1; l < k; ++l) {
+ sum += a[k-1][l] * yDotK[l][j];
+ }
+ yTmp[j] = y[j] + stepSize * sum;
+ }
+
+ computeDerivatives(stepStart + c[k-1] * stepSize, yTmp, yDotK[k]);
+
+ }
+
+ // estimate the state at the end of the step
+ for (int j = 0; j < y0.length; ++j) {
+ double sum = b[0] * yDotK[0][j];
+ for (int l = 1; l < stages; ++l) {
+ sum += b[l] * yDotK[l][j];
+ }
+ yTmp[j] = y[j] + stepSize * sum;
+ }
+
+ // estimate the error at the end of the step
+ error = estimateError(yDotK, y, yTmp, stepSize);
+ if (error >= 1.0) {
+ // reject the step and attempt to reduce error by stepsize control
+ final double factor =
+ FastMath.min(maxGrowth,
+ FastMath.max(minReduction, safety * FastMath.pow(error, exp)));
+ hNew = filterStep(stepSize * factor, forward, false);
+ }
+
+ }
+
+ // local error is small enough: accept the step, trigger events and step handlers
+ interpolator.storeTime(stepStart + stepSize);
+ System.arraycopy(yTmp, 0, y, 0, y0.length);
+ System.arraycopy(yDotK[stages - 1], 0, yDotTmp, 0, y0.length);
+ stepStart = acceptStep(interpolator, y, yDotTmp, t);
+ System.arraycopy(y, 0, yTmp, 0, y.length);
+
+ if (!isLastStep) {
+
+ // prepare next step
+ interpolator.storeTime(stepStart);
+
+ if (fsal) {
+ // save the last evaluation for the next step
+ System.arraycopy(yDotTmp, 0, yDotK[0], 0, y0.length);
+ }
+
+ // stepsize control for next step
+ final double factor =
+ FastMath.min(maxGrowth, FastMath.max(minReduction, safety * FastMath.pow(error, exp)));
+ final double scaledH = stepSize * factor;
+ final double nextT = stepStart + scaledH;
+ final boolean nextIsLast = forward ? (nextT >= t) : (nextT <= t);
+ hNew = filterStep(scaledH, forward, nextIsLast);
+
+ final double filteredNextT = stepStart + hNew;
+ final boolean filteredNextIsLast = forward ? (filteredNextT >= t) : (filteredNextT <= t);
+ if (filteredNextIsLast) {
+ hNew = t - stepStart;
+ }
+
+ }
+
+ } while (!isLastStep);
+
+ // dispatch results
+ equations.setTime(stepStart);
+ equations.setCompleteState(y);
+
+ resetInternalState();
+
+ }
+
+ /** Get the minimal reduction factor for stepsize control.
+ * @return minimal reduction factor
+ */
+ public double getMinReduction() {
+ return minReduction;
+ }
+
+ /** Set the minimal reduction factor for stepsize control.
+ * @param minReduction minimal reduction factor
+ */
+ public void setMinReduction(final double minReduction) {
+ this.minReduction = minReduction;
+ }
+
+ /** Get the maximal growth factor for stepsize control.
+ * @return maximal growth factor
+ */
+ public double getMaxGrowth() {
+ return maxGrowth;
+ }
+
+ /** Set the maximal growth factor for stepsize control.
+ * @param maxGrowth maximal growth factor
+ */
+ public void setMaxGrowth(final double maxGrowth) {
+ this.maxGrowth = maxGrowth;
+ }
+
+ /** Compute the error ratio.
+ * @param yDotK derivatives computed during the first stages
+ * @param y0 estimate of the step at the start of the step
+ * @param y1 estimate of the step at the end of the step
+ * @param h current step
+ * @return error ratio, greater than 1 if step should be rejected
+ */
+ protected abstract double estimateError(double[][] yDotK,
+ double[] y0, double[] y1,
+ double h);
+
+}
diff --git a/src/main/java/org/apache/commons/math3/ode/nonstiff/EulerFieldIntegrator.java b/src/main/java/org/apache/commons/math3/ode/nonstiff/EulerFieldIntegrator.java
new file mode 100644
index 0000000..38516cf
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/ode/nonstiff/EulerFieldIntegrator.java
@@ -0,0 +1,96 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math3.ode.nonstiff;
+
+import org.apache.commons.math3.Field;
+import org.apache.commons.math3.RealFieldElement;
+import org.apache.commons.math3.ode.FieldEquationsMapper;
+import org.apache.commons.math3.ode.FieldODEStateAndDerivative;
+import org.apache.commons.math3.util.MathArrays;
+
+/**
+ * This class implements a simple Euler integrator for Ordinary
+ * Differential Equations.
+ *
+ * <p>The Euler algorithm is the simplest one that can be used to
+ * integrate ordinary differential equations. It is a simple inversion
+ * of the forward difference expression :
+ * <code>f'=(f(t+h)-f(t))/h</code> which leads to
+ * <code>f(t+h)=f(t)+hf'</code>. The interpolation scheme used for
+ * dense output is the linear scheme already used for integration.</p>
+ *
+ * <p>This algorithm looks cheap because it needs only one function
+ * evaluation per step. However, as it uses linear estimates, it needs
+ * very small steps to achieve high accuracy, and small steps lead to
+ * numerical errors and instabilities.</p>
+ *
+ * <p>This algorithm is almost never used and has been included in
+ * this package only as a comparison reference for more useful
+ * integrators.</p>
+ *
+ * @see MidpointFieldIntegrator
+ * @see ClassicalRungeKuttaFieldIntegrator
+ * @see GillFieldIntegrator
+ * @see ThreeEighthesFieldIntegrator
+ * @see LutherFieldIntegrator
+ * @param <T> the type of the field elements
+ * @since 3.6
+ */
+
+public class EulerFieldIntegrator<T extends RealFieldElement<T>> extends RungeKuttaFieldIntegrator<T> {
+
+ /** Simple constructor.
+ * Build an Euler integrator with the given step.
+ * @param field field to which the time and state vector elements belong
+ * @param step integration step
+ */
+ public EulerFieldIntegrator(final Field<T> field, final T step) {
+ super(field, "Euler", step);
+ }
+
+ /** {@inheritDoc} */
+ public T[] getC() {
+ return MathArrays.buildArray(getField(), 0);
+ }
+
+ /** {@inheritDoc} */
+ public T[][] getA() {
+ return MathArrays.buildArray(getField(), 0, 0);
+ }
+
+ /** {@inheritDoc} */
+ public T[] getB() {
+ final T[] b = MathArrays.buildArray(getField(), 1);
+ b[0] = getField().getOne();
+ return b;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ protected EulerFieldStepInterpolator<T>
+ createInterpolator(final boolean forward, T[][] yDotK,
+ final FieldODEStateAndDerivative<T> globalPreviousState,
+ final FieldODEStateAndDerivative<T> globalCurrentState,
+ final FieldEquationsMapper<T> mapper) {
+ return new EulerFieldStepInterpolator<T>(getField(), forward, yDotK,
+ globalPreviousState, globalCurrentState,
+ globalPreviousState, globalCurrentState,
+ mapper);
+ }
+
+}
diff --git a/src/main/java/org/apache/commons/math3/ode/nonstiff/EulerFieldStepInterpolator.java b/src/main/java/org/apache/commons/math3/ode/nonstiff/EulerFieldStepInterpolator.java
new file mode 100644
index 0000000..0907b8e
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/ode/nonstiff/EulerFieldStepInterpolator.java
@@ -0,0 +1,108 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math3.ode.nonstiff;
+
+import org.apache.commons.math3.Field;
+import org.apache.commons.math3.RealFieldElement;
+import org.apache.commons.math3.ode.FieldEquationsMapper;
+import org.apache.commons.math3.ode.FieldODEStateAndDerivative;
+
+/**
+ * This class implements a linear interpolator for step.
+ *
+ * <p>This interpolator computes dense output inside the last
+ * step computed. The interpolation equation is consistent with the
+ * integration scheme :
+ * <ul>
+ * <li>Using reference point at step start:<br>
+ * y(t<sub>n</sub> + &theta; h) = y (t<sub>n</sub>) + &theta; h y'
+ * </li>
+ * <li>Using reference point at step end:<br>
+ * y(t<sub>n</sub> + &theta; h) = y (t<sub>n</sub> + h) - (1-&theta;) h y'
+ * </li>
+ * </ul>
+ * </p>
+ *
+ * where &theta; belongs to [0 ; 1] and where y' is the evaluation of
+ * the derivatives already computed during the step.</p>
+ *
+ * @see EulerFieldIntegrator
+ * @param <T> the type of the field elements
+ * @since 3.6
+ */
+
+class EulerFieldStepInterpolator<T extends RealFieldElement<T>>
+ extends RungeKuttaFieldStepInterpolator<T> {
+
+ /** Simple constructor.
+ * @param field field to which the time and state vector elements belong
+ * @param forward integration direction indicator
+ * @param yDotK slopes at the intermediate points
+ * @param globalPreviousState start of the global step
+ * @param globalCurrentState end of the global step
+ * @param softPreviousState start of the restricted step
+ * @param softCurrentState end of the restricted step
+ * @param mapper equations mapper for the all equations
+ */
+ EulerFieldStepInterpolator(final Field<T> field, final boolean forward,
+ final T[][] yDotK,
+ final FieldODEStateAndDerivative<T> globalPreviousState,
+ final FieldODEStateAndDerivative<T> globalCurrentState,
+ final FieldODEStateAndDerivative<T> softPreviousState,
+ final FieldODEStateAndDerivative<T> softCurrentState,
+ final FieldEquationsMapper<T> mapper) {
+ super(field, forward, yDotK,
+ globalPreviousState, globalCurrentState, softPreviousState, softCurrentState,
+ mapper);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ protected EulerFieldStepInterpolator<T> create(final Field<T> newField, final boolean newForward, final T[][] newYDotK,
+ final FieldODEStateAndDerivative<T> newGlobalPreviousState,
+ final FieldODEStateAndDerivative<T> newGlobalCurrentState,
+ final FieldODEStateAndDerivative<T> newSoftPreviousState,
+ final FieldODEStateAndDerivative<T> newSoftCurrentState,
+ final FieldEquationsMapper<T> newMapper) {
+ return new EulerFieldStepInterpolator<T>(newField, newForward, newYDotK,
+ newGlobalPreviousState, newGlobalCurrentState,
+ newSoftPreviousState, newSoftCurrentState,
+ newMapper);
+ }
+
+ /** {@inheritDoc} */
+ @SuppressWarnings("unchecked")
+ @Override
+ protected FieldODEStateAndDerivative<T> computeInterpolatedStateAndDerivatives(final FieldEquationsMapper<T> mapper,
+ final T time, final T theta,
+ final T thetaH, final T oneMinusThetaH) {
+ final T[] interpolatedState;
+ final T[] interpolatedDerivatives;
+ if (getGlobalPreviousState() != null && theta.getReal() <= 0.5) {
+ interpolatedState = previousStateLinearCombination(thetaH);
+ interpolatedDerivatives = derivativeLinearCombination(time.getField().getOne());
+ } else {
+ interpolatedState = currentStateLinearCombination(oneMinusThetaH.negate());
+ interpolatedDerivatives = derivativeLinearCombination(time.getField().getOne());
+ }
+
+ return new FieldODEStateAndDerivative<T>(time, interpolatedState, interpolatedDerivatives);
+
+ }
+
+}
diff --git a/src/main/java/org/apache/commons/math3/ode/nonstiff/EulerIntegrator.java b/src/main/java/org/apache/commons/math3/ode/nonstiff/EulerIntegrator.java
new file mode 100644
index 0000000..22c15c5
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/ode/nonstiff/EulerIntegrator.java
@@ -0,0 +1,72 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math3.ode.nonstiff;
+
+
+/**
+ * This class implements a simple Euler integrator for Ordinary
+ * Differential Equations.
+ *
+ * <p>The Euler algorithm is the simplest one that can be used to
+ * integrate ordinary differential equations. It is a simple inversion
+ * of the forward difference expression :
+ * <code>f'=(f(t+h)-f(t))/h</code> which leads to
+ * <code>f(t+h)=f(t)+hf'</code>. The interpolation scheme used for
+ * dense output is the linear scheme already used for integration.</p>
+ *
+ * <p>This algorithm looks cheap because it needs only one function
+ * evaluation per step. However, as it uses linear estimates, it needs
+ * very small steps to achieve high accuracy, and small steps lead to
+ * numerical errors and instabilities.</p>
+ *
+ * <p>This algorithm is almost never used and has been included in
+ * this package only as a comparison reference for more useful
+ * integrators.</p>
+ *
+ * @see MidpointIntegrator
+ * @see ClassicalRungeKuttaIntegrator
+ * @see GillIntegrator
+ * @see ThreeEighthesIntegrator
+ * @see LutherIntegrator
+ * @since 1.2
+ */
+
+public class EulerIntegrator extends RungeKuttaIntegrator {
+
+ /** Time steps Butcher array. */
+ private static final double[] STATIC_C = {
+ };
+
+ /** Internal weights Butcher array. */
+ private static final double[][] STATIC_A = {
+ };
+
+ /** Propagation weights Butcher array. */
+ private static final double[] STATIC_B = {
+ 1.0
+ };
+
+ /** Simple constructor.
+ * Build an Euler integrator with the given step.
+ * @param step integration step
+ */
+ public EulerIntegrator(final double step) {
+ super("Euler", STATIC_C, STATIC_A, STATIC_B, new EulerStepInterpolator(), step);
+ }
+
+}
diff --git a/src/main/java/org/apache/commons/math3/ode/nonstiff/EulerStepInterpolator.java b/src/main/java/org/apache/commons/math3/ode/nonstiff/EulerStepInterpolator.java
new file mode 100644
index 0000000..331cb14
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/ode/nonstiff/EulerStepInterpolator.java
@@ -0,0 +1,102 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math3.ode.nonstiff;
+
+import org.apache.commons.math3.ode.sampling.StepInterpolator;
+
+/**
+ * This class implements a linear interpolator for step.
+ *
+ * <p>This interpolator computes dense output inside the last
+ * step computed. The interpolation equation is consistent with the
+ * integration scheme :
+ * <ul>
+ * <li>Using reference point at step start:<br>
+ * y(t<sub>n</sub> + &theta; h) = y (t<sub>n</sub>) + &theta; h y'
+ * </li>
+ * <li>Using reference point at step end:<br>
+ * y(t<sub>n</sub> + &theta; h) = y (t<sub>n</sub> + h) - (1-&theta;) h y'
+ * </li>
+ * </ul>
+ * </p>
+ *
+ * where &theta; belongs to [0 ; 1] and where y' is the evaluation of
+ * the derivatives already computed during the step.</p>
+ *
+ * @see EulerIntegrator
+ * @since 1.2
+ */
+
+class EulerStepInterpolator
+ extends RungeKuttaStepInterpolator {
+
+ /** Serializable version identifier. */
+ private static final long serialVersionUID = 20111120L;
+
+ /** Simple constructor.
+ * This constructor builds an instance that is not usable yet, the
+ * {@link
+ * org.apache.commons.math3.ode.sampling.AbstractStepInterpolator#reinitialize}
+ * method should be called before using the instance in order to
+ * initialize the internal arrays. This constructor is used only
+ * in order to delay the initialization in some cases. The {@link
+ * RungeKuttaIntegrator} class uses the prototyping design pattern
+ * to create the step interpolators by cloning an uninitialized model
+ * and later initializing the copy.
+ */
+ // CHECKSTYLE: stop RedundantModifier
+ // the public modifier here is needed for serialization
+ public EulerStepInterpolator() {
+ }
+ // CHECKSTYLE: resume RedundantModifier
+
+ /** Copy constructor.
+ * @param interpolator interpolator to copy from. The copy is a deep
+ * copy: its arrays are separated from the original arrays of the
+ * instance
+ */
+ EulerStepInterpolator(final EulerStepInterpolator interpolator) {
+ super(interpolator);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ protected StepInterpolator doCopy() {
+ return new EulerStepInterpolator(this);
+ }
+
+
+ /** {@inheritDoc} */
+ @Override
+ protected void computeInterpolatedStateAndDerivatives(final double theta,
+ final double oneMinusThetaH) {
+ if ((previousState != null) && (theta <= 0.5)) {
+ for (int i = 0; i < interpolatedState.length; ++i) {
+ interpolatedState[i] = previousState[i] + theta * h * yDotK[0][i];
+ }
+ System.arraycopy(yDotK[0], 0, interpolatedDerivatives, 0, interpolatedDerivatives.length);
+ } else {
+ for (int i = 0; i < interpolatedState.length; ++i) {
+ interpolatedState[i] = currentState[i] - oneMinusThetaH * yDotK[0][i];
+ }
+ System.arraycopy(yDotK[0], 0, interpolatedDerivatives, 0, interpolatedDerivatives.length);
+ }
+
+ }
+
+}
diff --git a/src/main/java/org/apache/commons/math3/ode/nonstiff/FieldButcherArrayProvider.java b/src/main/java/org/apache/commons/math3/ode/nonstiff/FieldButcherArrayProvider.java
new file mode 100644
index 0000000..b37d4cb
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/ode/nonstiff/FieldButcherArrayProvider.java
@@ -0,0 +1,46 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math3.ode.nonstiff;
+
+import org.apache.commons.math3.RealFieldElement;
+
+/** This interface represents an integrator based on Butcher arrays.
+ * @see RungeKuttaFieldIntegrator
+ * @see EmbeddedRungeKuttaFieldIntegrator
+ * @param <T> the type of the field elements
+ * @since 3.6
+ */
+
+public interface FieldButcherArrayProvider<T extends RealFieldElement<T>> {
+
+ /** Get the time steps from Butcher array (without the first zero).
+ * @return time steps from Butcher array (without the first zero
+ */
+ T[] getC();
+
+ /** Get the internal weights from Butcher array (without the first empty row).
+ * @return internal weights from Butcher array (without the first empty row)
+ */
+ T[][] getA();
+
+ /** Get the external weights for the high order method from Butcher array.
+ * @return external weights for the high order method from Butcher array
+ */
+ T[] getB();
+
+}
diff --git a/src/main/java/org/apache/commons/math3/ode/nonstiff/GillFieldIntegrator.java b/src/main/java/org/apache/commons/math3/ode/nonstiff/GillFieldIntegrator.java
new file mode 100644
index 0000000..d5f7c64
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/ode/nonstiff/GillFieldIntegrator.java
@@ -0,0 +1,121 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math3.ode.nonstiff;
+
+import org.apache.commons.math3.Field;
+import org.apache.commons.math3.RealFieldElement;
+import org.apache.commons.math3.ode.FieldEquationsMapper;
+import org.apache.commons.math3.ode.FieldODEStateAndDerivative;
+import org.apache.commons.math3.util.MathArrays;
+
+
+/**
+ * This class implements the Gill fourth order Runge-Kutta
+ * integrator for Ordinary Differential Equations .
+
+ * <p>This method is an explicit Runge-Kutta method, its Butcher-array
+ * is the following one :
+ * <pre>
+ * 0 | 0 0 0 0
+ * 1/2 | 1/2 0 0 0
+ * 1/2 | (q-1)/2 (2-q)/2 0 0
+ * 1 | 0 -q/2 (2+q)/2 0
+ * |-------------------------------
+ * | 1/6 (2-q)/6 (2+q)/6 1/6
+ * </pre>
+ * where q = sqrt(2)</p>
+ *
+ * @see EulerFieldIntegrator
+ * @see ClassicalRungeKuttaFieldIntegrator
+ * @see MidpointFieldIntegrator
+ * @see ThreeEighthesFieldIntegrator
+ * @see LutherFieldIntegrator
+ * @param <T> the type of the field elements
+ * @since 3.6
+ */
+
+public class GillFieldIntegrator<T extends RealFieldElement<T>>
+ extends RungeKuttaFieldIntegrator<T> {
+
+ /** Simple constructor.
+ * Build a fourth-order Gill integrator with the given step.
+ * @param field field to which the time and state vector elements belong
+ * @param step integration step
+ */
+ public GillFieldIntegrator(final Field<T> field, final T step) {
+ super(field, "Gill", step);
+ }
+
+ /** {@inheritDoc} */
+ public T[] getC() {
+ final T[] c = MathArrays.buildArray(getField(), 3);
+ c[0] = fraction(1, 2);
+ c[1] = c[0];
+ c[2] = getField().getOne();
+ return c;
+ }
+
+ /** {@inheritDoc} */
+ public T[][] getA() {
+
+ final T two = getField().getZero().add(2);
+ final T sqrtTwo = two.sqrt();
+
+ final T[][] a = MathArrays.buildArray(getField(), 3, -1);
+ for (int i = 0; i < a.length; ++i) {
+ a[i] = MathArrays.buildArray(getField(), i + 1);
+ }
+ a[0][0] = fraction(1, 2);
+ a[1][0] = sqrtTwo.subtract(1).multiply(0.5);
+ a[1][1] = sqrtTwo.subtract(2).multiply(-0.5);
+ a[2][0] = getField().getZero();
+ a[2][1] = sqrtTwo.multiply(-0.5);
+ a[2][2] = sqrtTwo.add(2).multiply(0.5);
+ return a;
+ }
+
+ /** {@inheritDoc} */
+ public T[] getB() {
+
+ final T two = getField().getZero().add(2);
+ final T sqrtTwo = two.sqrt();
+
+ final T[] b = MathArrays.buildArray(getField(), 4);
+ b[0] = fraction(1, 6);
+ b[1] = sqrtTwo.subtract(2).divide(-6);
+ b[2] = sqrtTwo.add(2).divide(6);
+ b[3] = b[0];
+
+ return b;
+
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ protected GillFieldStepInterpolator<T>
+ createInterpolator(final boolean forward, T[][] yDotK,
+ final FieldODEStateAndDerivative<T> globalPreviousState,
+ final FieldODEStateAndDerivative<T> globalCurrentState,
+ final FieldEquationsMapper<T> mapper) {
+ return new GillFieldStepInterpolator<T>(getField(), forward, yDotK,
+ globalPreviousState, globalCurrentState,
+ globalPreviousState, globalCurrentState,
+ mapper);
+ }
+
+}
diff --git a/src/main/java/org/apache/commons/math3/ode/nonstiff/GillFieldStepInterpolator.java b/src/main/java/org/apache/commons/math3/ode/nonstiff/GillFieldStepInterpolator.java
new file mode 100644
index 0000000..5639ed5
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/ode/nonstiff/GillFieldStepInterpolator.java
@@ -0,0 +1,148 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math3.ode.nonstiff;
+
+import org.apache.commons.math3.Field;
+import org.apache.commons.math3.RealFieldElement;
+import org.apache.commons.math3.ode.FieldEquationsMapper;
+import org.apache.commons.math3.ode.FieldODEStateAndDerivative;
+
+/**
+ * This class implements a step interpolator for the Gill fourth
+ * order Runge-Kutta integrator.
+ *
+ * <p>This interpolator allows to compute dense output inside the last
+ * step computed. The interpolation equation is consistent with the
+ * integration scheme :
+ * <ul>
+ * <li>Using reference point at step start:<br>
+ * y(t<sub>n</sub> + &theta; h) = y (t<sub>n</sub>)
+ * + &theta; (h/6) [ (6 - 9 &theta; + 4 &theta;<sup>2</sup>) y'<sub>1</sub>
+ * + ( 6 &theta; - 4 &theta;<sup>2</sup>) ((1-1/&radic;2) y'<sub>2</sub> + (1+1/&radic;2)) y'<sub>3</sub>)
+ * + ( - 3 &theta; + 4 &theta;<sup>2</sup>) y'<sub>4</sub>
+ * ]
+ * </li>
+ * <li>Using reference point at step start:<br>
+ * y(t<sub>n</sub> + &theta; h) = y (t<sub>n</sub> + h)
+ * - (1 - &theta;) (h/6) [ (1 - 5 &theta; + 4 &theta;<sup>2</sup>) y'<sub>1</sub>
+ * + (2 + 2 &theta; - 4 &theta;<sup>2</sup>) ((1-1/&radic;2) y'<sub>2</sub> + (1+1/&radic;2)) y'<sub>3</sub>)
+ * + (1 + &theta; + 4 &theta;<sup>2</sup>) y'<sub>4</sub>
+ * ]
+ * </li>
+ * </ul>
+ * </p>
+ * where &theta; belongs to [0 ; 1] and where y'<sub>1</sub> to y'<sub>4</sub>
+ * are the four evaluations of the derivatives already computed during
+ * the step.</p>
+ *
+ * @see GillFieldIntegrator
+ * @param <T> the type of the field elements
+ * @since 3.6
+ */
+
+class GillFieldStepInterpolator<T extends RealFieldElement<T>>
+ extends RungeKuttaFieldStepInterpolator<T> {
+
+ /** First Gill coefficient. */
+ private final T one_minus_inv_sqrt_2;
+
+ /** Second Gill coefficient. */
+ private final T one_plus_inv_sqrt_2;
+
+ /** Simple constructor.
+ * @param field field to which the time and state vector elements belong
+ * @param forward integration direction indicator
+ * @param yDotK slopes at the intermediate points
+ * @param globalPreviousState start of the global step
+ * @param globalCurrentState end of the global step
+ * @param softPreviousState start of the restricted step
+ * @param softCurrentState end of the restricted step
+ * @param mapper equations mapper for the all equations
+ */
+ GillFieldStepInterpolator(final Field<T> field, final boolean forward,
+ final T[][] yDotK,
+ final FieldODEStateAndDerivative<T> globalPreviousState,
+ final FieldODEStateAndDerivative<T> globalCurrentState,
+ final FieldODEStateAndDerivative<T> softPreviousState,
+ final FieldODEStateAndDerivative<T> softCurrentState,
+ final FieldEquationsMapper<T> mapper) {
+ super(field, forward, yDotK,
+ globalPreviousState, globalCurrentState, softPreviousState, softCurrentState,
+ mapper);
+ final T sqrt = field.getZero().add(0.5).sqrt();
+ one_minus_inv_sqrt_2 = field.getOne().subtract(sqrt);
+ one_plus_inv_sqrt_2 = field.getOne().add(sqrt);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ protected GillFieldStepInterpolator<T> create(final Field<T> newField, final boolean newForward, final T[][] newYDotK,
+ final FieldODEStateAndDerivative<T> newGlobalPreviousState,
+ final FieldODEStateAndDerivative<T> newGlobalCurrentState,
+ final FieldODEStateAndDerivative<T> newSoftPreviousState,
+ final FieldODEStateAndDerivative<T> newSoftCurrentState,
+ final FieldEquationsMapper<T> newMapper) {
+ return new GillFieldStepInterpolator<T>(newField, newForward, newYDotK,
+ newGlobalPreviousState, newGlobalCurrentState,
+ newSoftPreviousState, newSoftCurrentState,
+ newMapper);
+ }
+
+ /** {@inheritDoc} */
+ @SuppressWarnings("unchecked")
+ @Override
+ protected FieldODEStateAndDerivative<T> computeInterpolatedStateAndDerivatives(final FieldEquationsMapper<T> mapper,
+ final T time, final T theta,
+ final T thetaH, final T oneMinusThetaH) {
+
+ final T one = time.getField().getOne();
+ final T twoTheta = theta.multiply(2);
+ final T fourTheta2 = twoTheta.multiply(twoTheta);
+ final T coeffDot1 = theta.multiply(twoTheta.subtract(3)).add(1);
+ final T cDot23 = twoTheta.multiply(one.subtract(theta));
+ final T coeffDot2 = cDot23.multiply(one_minus_inv_sqrt_2);
+ final T coeffDot3 = cDot23.multiply(one_plus_inv_sqrt_2);
+ final T coeffDot4 = theta.multiply(twoTheta.subtract(1));
+ final T[] interpolatedState;
+ final T[] interpolatedDerivatives;
+
+ if (getGlobalPreviousState() != null && theta.getReal() <= 0.5) {
+ final T s = thetaH.divide(6.0);
+ final T c23 = s.multiply(theta.multiply(6).subtract(fourTheta2));
+ final T coeff1 = s.multiply(fourTheta2.subtract(theta.multiply(9)).add(6));
+ final T coeff2 = c23.multiply(one_minus_inv_sqrt_2);
+ final T coeff3 = c23.multiply(one_plus_inv_sqrt_2);
+ final T coeff4 = s.multiply(fourTheta2.subtract(theta.multiply(3)));
+ interpolatedState = previousStateLinearCombination(coeff1, coeff2, coeff3, coeff4);
+ interpolatedDerivatives = derivativeLinearCombination(coeffDot1, coeffDot2, coeffDot3, coeffDot4);
+ } else {
+ final T s = oneMinusThetaH.divide(-6.0);
+ final T c23 = s.multiply(twoTheta.add(2).subtract(fourTheta2));
+ final T coeff1 = s.multiply(fourTheta2.subtract(theta.multiply(5)).add(1));
+ final T coeff2 = c23.multiply(one_minus_inv_sqrt_2);
+ final T coeff3 = c23.multiply(one_plus_inv_sqrt_2);
+ final T coeff4 = s.multiply(fourTheta2.add(theta).add(1));
+ interpolatedState = currentStateLinearCombination(coeff1, coeff2, coeff3, coeff4);
+ interpolatedDerivatives = derivativeLinearCombination(coeffDot1, coeffDot2, coeffDot3, coeffDot4);
+ }
+
+ return new FieldODEStateAndDerivative<T>(time, interpolatedState, interpolatedDerivatives);
+
+ }
+
+}
diff --git a/src/main/java/org/apache/commons/math3/ode/nonstiff/GillIntegrator.java b/src/main/java/org/apache/commons/math3/ode/nonstiff/GillIntegrator.java
new file mode 100644
index 0000000..5273e02
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/ode/nonstiff/GillIntegrator.java
@@ -0,0 +1,74 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math3.ode.nonstiff;
+
+import org.apache.commons.math3.util.FastMath;
+
+
+/**
+ * This class implements the Gill fourth order Runge-Kutta
+ * integrator for Ordinary Differential Equations .
+
+ * <p>This method is an explicit Runge-Kutta method, its Butcher-array
+ * is the following one :
+ * <pre>
+ * 0 | 0 0 0 0
+ * 1/2 | 1/2 0 0 0
+ * 1/2 | (q-1)/2 (2-q)/2 0 0
+ * 1 | 0 -q/2 (2+q)/2 0
+ * |-------------------------------
+ * | 1/6 (2-q)/6 (2+q)/6 1/6
+ * </pre>
+ * where q = sqrt(2)</p>
+ *
+ * @see EulerIntegrator
+ * @see ClassicalRungeKuttaIntegrator
+ * @see MidpointIntegrator
+ * @see ThreeEighthesIntegrator
+ * @see LutherIntegrator
+ * @since 1.2
+ */
+
+public class GillIntegrator extends RungeKuttaIntegrator {
+
+ /** Time steps Butcher array. */
+ private static final double[] STATIC_C = {
+ 1.0 / 2.0, 1.0 / 2.0, 1.0
+ };
+
+ /** Internal weights Butcher array. */
+ private static final double[][] STATIC_A = {
+ { 1.0 / 2.0 },
+ { (FastMath.sqrt(2.0) - 1.0) / 2.0, (2.0 - FastMath.sqrt(2.0)) / 2.0 },
+ { 0.0, -FastMath.sqrt(2.0) / 2.0, (2.0 + FastMath.sqrt(2.0)) / 2.0 }
+ };
+
+ /** Propagation weights Butcher array. */
+ private static final double[] STATIC_B = {
+ 1.0 / 6.0, (2.0 - FastMath.sqrt(2.0)) / 6.0, (2.0 + FastMath.sqrt(2.0)) / 6.0, 1.0 / 6.0
+ };
+
+ /** Simple constructor.
+ * Build a fourth-order Gill integrator with the given step.
+ * @param step integration step
+ */
+ public GillIntegrator(final double step) {
+ super("Gill", STATIC_C, STATIC_A, STATIC_B, new GillStepInterpolator(), step);
+ }
+
+}
diff --git a/src/main/java/org/apache/commons/math3/ode/nonstiff/GillStepInterpolator.java b/src/main/java/org/apache/commons/math3/ode/nonstiff/GillStepInterpolator.java
new file mode 100644
index 0000000..72dec69
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/ode/nonstiff/GillStepInterpolator.java
@@ -0,0 +1,151 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math3.ode.nonstiff;
+
+import org.apache.commons.math3.ode.sampling.StepInterpolator;
+import org.apache.commons.math3.util.FastMath;
+
+/**
+ * This class implements a step interpolator for the Gill fourth
+ * order Runge-Kutta integrator.
+ *
+ * <p>This interpolator allows to compute dense output inside the last
+ * step computed. The interpolation equation is consistent with the
+ * integration scheme :
+ * <ul>
+ * <li>Using reference point at step start:<br>
+ * y(t<sub>n</sub> + &theta; h) = y (t<sub>n</sub>)
+ * + &theta; (h/6) [ (6 - 9 &theta; + 4 &theta;<sup>2</sup>) y'<sub>1</sub>
+ * + ( 6 &theta; - 4 &theta;<sup>2</sup>) ((1-1/&radic;2) y'<sub>2</sub> + (1+1/&radic;2)) y'<sub>3</sub>)
+ * + ( - 3 &theta; + 4 &theta;<sup>2</sup>) y'<sub>4</sub>
+ * ]
+ * </li>
+ * <li>Using reference point at step start:<br>
+ * y(t<sub>n</sub> + &theta; h) = y (t<sub>n</sub> + h)
+ * - (1 - &theta;) (h/6) [ (1 - 5 &theta; + 4 &theta;<sup>2</sup>) y'<sub>1</sub>
+ * + (2 + 2 &theta; - 4 &theta;<sup>2</sup>) ((1-1/&radic;2) y'<sub>2</sub> + (1+1/&radic;2)) y'<sub>3</sub>)
+ * + (1 + &theta; + 4 &theta;<sup>2</sup>) y'<sub>4</sub>
+ * ]
+ * </li>
+ * </ul>
+ * </p>
+ * where &theta; belongs to [0 ; 1] and where y'<sub>1</sub> to y'<sub>4</sub>
+ * are the four evaluations of the derivatives already computed during
+ * the step.</p>
+ *
+ * @see GillIntegrator
+ * @since 1.2
+ */
+
+class GillStepInterpolator
+ extends RungeKuttaStepInterpolator {
+
+ /** First Gill coefficient. */
+ private static final double ONE_MINUS_INV_SQRT_2 = 1 - FastMath.sqrt(0.5);
+
+ /** Second Gill coefficient. */
+ private static final double ONE_PLUS_INV_SQRT_2 = 1 + FastMath.sqrt(0.5);
+
+ /** Serializable version identifier. */
+ private static final long serialVersionUID = 20111120L;
+
+ /** Simple constructor.
+ * This constructor builds an instance that is not usable yet, the
+ * {@link
+ * org.apache.commons.math3.ode.sampling.AbstractStepInterpolator#reinitialize}
+ * method should be called before using the instance in order to
+ * initialize the internal arrays. This constructor is used only
+ * in order to delay the initialization in some cases. The {@link
+ * RungeKuttaIntegrator} class uses the prototyping design pattern
+ * to create the step interpolators by cloning an uninitialized model
+ * and later initializing the copy.
+ */
+ // CHECKSTYLE: stop RedundantModifier
+ // the public modifier here is needed for serialization
+ public GillStepInterpolator() {
+ }
+ // CHECKSTYLE: resume RedundantModifier
+
+ /** Copy constructor.
+ * @param interpolator interpolator to copy from. The copy is a deep
+ * copy: its arrays are separated from the original arrays of the
+ * instance
+ */
+ GillStepInterpolator(final GillStepInterpolator interpolator) {
+ super(interpolator);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ protected StepInterpolator doCopy() {
+ return new GillStepInterpolator(this);
+ }
+
+
+ /** {@inheritDoc} */
+ @Override
+ protected void computeInterpolatedStateAndDerivatives(final double theta,
+ final double oneMinusThetaH) {
+
+ final double twoTheta = 2 * theta;
+ final double fourTheta2 = twoTheta * twoTheta;
+ final double coeffDot1 = theta * (twoTheta - 3) + 1;
+ final double cDot23 = twoTheta * (1 - theta);
+ final double coeffDot2 = cDot23 * ONE_MINUS_INV_SQRT_2;
+ final double coeffDot3 = cDot23 * ONE_PLUS_INV_SQRT_2;
+ final double coeffDot4 = theta * (twoTheta - 1);
+
+ if ((previousState != null) && (theta <= 0.5)) {
+ final double s = theta * h / 6.0;
+ final double c23 = s * (6 * theta - fourTheta2);
+ final double coeff1 = s * (6 - 9 * theta + fourTheta2);
+ final double coeff2 = c23 * ONE_MINUS_INV_SQRT_2;
+ final double coeff3 = c23 * ONE_PLUS_INV_SQRT_2;
+ final double coeff4 = s * (-3 * theta + fourTheta2);
+ for (int i = 0; i < interpolatedState.length; ++i) {
+ final double yDot1 = yDotK[0][i];
+ final double yDot2 = yDotK[1][i];
+ final double yDot3 = yDotK[2][i];
+ final double yDot4 = yDotK[3][i];
+ interpolatedState[i] =
+ previousState[i] + coeff1 * yDot1 + coeff2 * yDot2 + coeff3 * yDot3 + coeff4 * yDot4;
+ interpolatedDerivatives[i] =
+ coeffDot1 * yDot1 + coeffDot2 * yDot2 + coeffDot3 * yDot3 + coeffDot4 * yDot4;
+ }
+ } else {
+ final double s = oneMinusThetaH / 6.0;
+ final double c23 = s * (2 + twoTheta - fourTheta2);
+ final double coeff1 = s * (1 - 5 * theta + fourTheta2);
+ final double coeff2 = c23 * ONE_MINUS_INV_SQRT_2;
+ final double coeff3 = c23 * ONE_PLUS_INV_SQRT_2;
+ final double coeff4 = s * (1 + theta + fourTheta2);
+ for (int i = 0; i < interpolatedState.length; ++i) {
+ final double yDot1 = yDotK[0][i];
+ final double yDot2 = yDotK[1][i];
+ final double yDot3 = yDotK[2][i];
+ final double yDot4 = yDotK[3][i];
+ interpolatedState[i] =
+ currentState[i] - coeff1 * yDot1 - coeff2 * yDot2 - coeff3 * yDot3 - coeff4 * yDot4;
+ interpolatedDerivatives[i] =
+ coeffDot1 * yDot1 + coeffDot2 * yDot2 + coeffDot3 * yDot3 + coeffDot4 * yDot4;
+ }
+ }
+
+ }
+
+}
diff --git a/src/main/java/org/apache/commons/math3/ode/nonstiff/GraggBulirschStoerIntegrator.java b/src/main/java/org/apache/commons/math3/ode/nonstiff/GraggBulirschStoerIntegrator.java
new file mode 100644
index 0000000..50a463e
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/ode/nonstiff/GraggBulirschStoerIntegrator.java
@@ -0,0 +1,949 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math3.ode.nonstiff;
+
+import org.apache.commons.math3.analysis.solvers.UnivariateSolver;
+import org.apache.commons.math3.exception.DimensionMismatchException;
+import org.apache.commons.math3.exception.MaxCountExceededException;
+import org.apache.commons.math3.exception.NoBracketingException;
+import org.apache.commons.math3.exception.NumberIsTooSmallException;
+import org.apache.commons.math3.ode.ExpandableStatefulODE;
+import org.apache.commons.math3.ode.events.EventHandler;
+import org.apache.commons.math3.ode.sampling.AbstractStepInterpolator;
+import org.apache.commons.math3.ode.sampling.StepHandler;
+import org.apache.commons.math3.util.FastMath;
+
+/**
+ * This class implements a Gragg-Bulirsch-Stoer integrator for
+ * Ordinary Differential Equations.
+ *
+ * <p>The Gragg-Bulirsch-Stoer algorithm is one of the most efficient
+ * ones currently available for smooth problems. It uses Richardson
+ * extrapolation to estimate what would be the solution if the step
+ * size could be decreased down to zero.</p>
+ *
+ * <p>
+ * This method changes both the step size and the order during
+ * integration, in order to minimize computation cost. It is
+ * particularly well suited when a very high precision is needed. The
+ * limit where this method becomes more efficient than high-order
+ * embedded Runge-Kutta methods like {@link DormandPrince853Integrator
+ * Dormand-Prince 8(5,3)} depends on the problem. Results given in the
+ * Hairer, Norsett and Wanner book show for example that this limit
+ * occurs for accuracy around 1e-6 when integrating Saltzam-Lorenz
+ * equations (the authors note this problem is <i>extremely sensitive
+ * to the errors in the first integration steps</i>), and around 1e-11
+ * for a two dimensional celestial mechanics problems with seven
+ * bodies (pleiades problem, involving quasi-collisions for which
+ * <i>automatic step size control is essential</i>).
+ * </p>
+ *
+ * <p>
+ * This implementation is basically a reimplementation in Java of the
+ * <a
+ * href="http://www.unige.ch/math/folks/hairer/prog/nonstiff/odex.f">odex</a>
+ * fortran code by E. Hairer and G. Wanner. The redistribution policy
+ * for this code is available <a
+ * href="http://www.unige.ch/~hairer/prog/licence.txt">here</a>, for
+ * convenience, it is reproduced below.</p>
+ * </p>
+ *
+ * <table border="0" width="80%" cellpadding="10" align="center" bgcolor="#E0E0E0">
+ * <tr><td>Copyright (c) 2004, Ernst Hairer</td></tr>
+ *
+ * <tr><td>Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ * <ul>
+ * <li>Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.</li>
+ * <li>Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.</li>
+ * </ul></td></tr>
+ *
+ * <tr><td><strong>THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING,
+ * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.</strong></td></tr>
+ * </table>
+ *
+ * @since 1.2
+ */
+
+public class GraggBulirschStoerIntegrator extends AdaptiveStepsizeIntegrator {
+
+ /** Integrator method name. */
+ private static final String METHOD_NAME = "Gragg-Bulirsch-Stoer";
+
+ /** maximal order. */
+ private int maxOrder;
+
+ /** step size sequence. */
+ private int[] sequence;
+
+ /** overall cost of applying step reduction up to iteration k+1, in number of calls. */
+ private int[] costPerStep;
+
+ /** cost per unit step. */
+ private double[] costPerTimeUnit;
+
+ /** optimal steps for each order. */
+ private double[] optimalStep;
+
+ /** extrapolation coefficients. */
+ private double[][] coeff;
+
+ /** stability check enabling parameter. */
+ private boolean performTest;
+
+ /** maximal number of checks for each iteration. */
+ private int maxChecks;
+
+ /** maximal number of iterations for which checks are performed. */
+ private int maxIter;
+
+ /** stepsize reduction factor in case of stability check failure. */
+ private double stabilityReduction;
+
+ /** first stepsize control factor. */
+ private double stepControl1;
+
+ /** second stepsize control factor. */
+ private double stepControl2;
+
+ /** third stepsize control factor. */
+ private double stepControl3;
+
+ /** fourth stepsize control factor. */
+ private double stepControl4;
+
+ /** first order control factor. */
+ private double orderControl1;
+
+ /** second order control factor. */
+ private double orderControl2;
+
+ /** use interpolation error in stepsize control. */
+ private boolean useInterpolationError;
+
+ /** interpolation order control parameter. */
+ private int mudif;
+
+ /** Simple constructor.
+ * Build a Gragg-Bulirsch-Stoer integrator with the given step
+ * bounds. All tuning parameters are set to their default
+ * values. The default step handler does nothing.
+ * @param minStep minimal step (sign is irrelevant, regardless of
+ * integration direction, forward or backward), the last step can
+ * be smaller than this
+ * @param maxStep maximal step (sign is irrelevant, regardless of
+ * integration direction, forward or backward), the last step can
+ * be smaller than this
+ * @param scalAbsoluteTolerance allowed absolute error
+ * @param scalRelativeTolerance allowed relative error
+ */
+ public GraggBulirschStoerIntegrator(final double minStep, final double maxStep,
+ final double scalAbsoluteTolerance,
+ final double scalRelativeTolerance) {
+ super(METHOD_NAME, minStep, maxStep,
+ scalAbsoluteTolerance, scalRelativeTolerance);
+ setStabilityCheck(true, -1, -1, -1);
+ setControlFactors(-1, -1, -1, -1);
+ setOrderControl(-1, -1, -1);
+ setInterpolationControl(true, -1);
+ }
+
+ /** Simple constructor.
+ * Build a Gragg-Bulirsch-Stoer integrator with the given step
+ * bounds. All tuning parameters are set to their default
+ * values. The default step handler does nothing.
+ * @param minStep minimal step (must be positive even for backward
+ * integration), the last step can be smaller than this
+ * @param maxStep maximal step (must be positive even for backward
+ * integration)
+ * @param vecAbsoluteTolerance allowed absolute error
+ * @param vecRelativeTolerance allowed relative error
+ */
+ public GraggBulirschStoerIntegrator(final double minStep, final double maxStep,
+ final double[] vecAbsoluteTolerance,
+ final double[] vecRelativeTolerance) {
+ super(METHOD_NAME, minStep, maxStep,
+ vecAbsoluteTolerance, vecRelativeTolerance);
+ setStabilityCheck(true, -1, -1, -1);
+ setControlFactors(-1, -1, -1, -1);
+ setOrderControl(-1, -1, -1);
+ setInterpolationControl(true, -1);
+ }
+
+ /** Set the stability check controls.
+ * <p>The stability check is performed on the first few iterations of
+ * the extrapolation scheme. If this test fails, the step is rejected
+ * and the stepsize is reduced.</p>
+ * <p>By default, the test is performed, at most during two
+ * iterations at each step, and at most once for each of these
+ * iterations. The default stepsize reduction factor is 0.5.</p>
+ * @param performStabilityCheck if true, stability check will be performed,
+ if false, the check will be skipped
+ * @param maxNumIter maximal number of iterations for which checks are
+ * performed (the number of iterations is reset to default if negative
+ * or null)
+ * @param maxNumChecks maximal number of checks for each iteration
+ * (the number of checks is reset to default if negative or null)
+ * @param stepsizeReductionFactor stepsize reduction factor in case of
+ * failure (the factor is reset to default if lower than 0.0001 or
+ * greater than 0.9999)
+ */
+ public void setStabilityCheck(final boolean performStabilityCheck,
+ final int maxNumIter, final int maxNumChecks,
+ final double stepsizeReductionFactor) {
+
+ this.performTest = performStabilityCheck;
+ this.maxIter = (maxNumIter <= 0) ? 2 : maxNumIter;
+ this.maxChecks = (maxNumChecks <= 0) ? 1 : maxNumChecks;
+
+ if ((stepsizeReductionFactor < 0.0001) || (stepsizeReductionFactor > 0.9999)) {
+ this.stabilityReduction = 0.5;
+ } else {
+ this.stabilityReduction = stepsizeReductionFactor;
+ }
+
+ }
+
+ /** Set the step size control factors.
+
+ * <p>The new step size hNew is computed from the old one h by:
+ * <pre>
+ * hNew = h * stepControl2 / (err/stepControl1)^(1/(2k+1))
+ * </pre>
+ * where err is the scaled error and k the iteration number of the
+ * extrapolation scheme (counting from 0). The default values are
+ * 0.65 for stepControl1 and 0.94 for stepControl2.</p>
+ * <p>The step size is subject to the restriction:
+ * <pre>
+ * stepControl3^(1/(2k+1))/stepControl4 <= hNew/h <= 1/stepControl3^(1/(2k+1))
+ * </pre>
+ * The default values are 0.02 for stepControl3 and 4.0 for
+ * stepControl4.</p>
+ * @param control1 first stepsize control factor (the factor is
+ * reset to default if lower than 0.0001 or greater than 0.9999)
+ * @param control2 second stepsize control factor (the factor
+ * is reset to default if lower than 0.0001 or greater than 0.9999)
+ * @param control3 third stepsize control factor (the factor is
+ * reset to default if lower than 0.0001 or greater than 0.9999)
+ * @param control4 fourth stepsize control factor (the factor
+ * is reset to default if lower than 1.0001 or greater than 999.9)
+ */
+ public void setControlFactors(final double control1, final double control2,
+ final double control3, final double control4) {
+
+ if ((control1 < 0.0001) || (control1 > 0.9999)) {
+ this.stepControl1 = 0.65;
+ } else {
+ this.stepControl1 = control1;
+ }
+
+ if ((control2 < 0.0001) || (control2 > 0.9999)) {
+ this.stepControl2 = 0.94;
+ } else {
+ this.stepControl2 = control2;
+ }
+
+ if ((control3 < 0.0001) || (control3 > 0.9999)) {
+ this.stepControl3 = 0.02;
+ } else {
+ this.stepControl3 = control3;
+ }
+
+ if ((control4 < 1.0001) || (control4 > 999.9)) {
+ this.stepControl4 = 4.0;
+ } else {
+ this.stepControl4 = control4;
+ }
+
+ }
+
+ /** Set the order control parameters.
+ * <p>The Gragg-Bulirsch-Stoer method changes both the step size and
+ * the order during integration, in order to minimize computation
+ * cost. Each extrapolation step increases the order by 2, so the
+ * maximal order that will be used is always even, it is twice the
+ * maximal number of columns in the extrapolation table.</p>
+ * <pre>
+ * order is decreased if w(k-1) <= w(k) * orderControl1
+ * order is increased if w(k) <= w(k-1) * orderControl2
+ * </pre>
+ * <p>where w is the table of work per unit step for each order
+ * (number of function calls divided by the step length), and k is
+ * the current order.</p>
+ * <p>The default maximal order after construction is 18 (i.e. the
+ * maximal number of columns is 9). The default values are 0.8 for
+ * orderControl1 and 0.9 for orderControl2.</p>
+ * @param maximalOrder maximal order in the extrapolation table (the
+ * maximal order is reset to default if order <= 6 or odd)
+ * @param control1 first order control factor (the factor is
+ * reset to default if lower than 0.0001 or greater than 0.9999)
+ * @param control2 second order control factor (the factor
+ * is reset to default if lower than 0.0001 or greater than 0.9999)
+ */
+ public void setOrderControl(final int maximalOrder,
+ final double control1, final double control2) {
+
+ if ((maximalOrder <= 6) || (maximalOrder % 2 != 0)) {
+ this.maxOrder = 18;
+ }
+
+ if ((control1 < 0.0001) || (control1 > 0.9999)) {
+ this.orderControl1 = 0.8;
+ } else {
+ this.orderControl1 = control1;
+ }
+
+ if ((control2 < 0.0001) || (control2 > 0.9999)) {
+ this.orderControl2 = 0.9;
+ } else {
+ this.orderControl2 = control2;
+ }
+
+ // reinitialize the arrays
+ initializeArrays();
+
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void addStepHandler (final StepHandler handler) {
+
+ super.addStepHandler(handler);
+
+ // reinitialize the arrays
+ initializeArrays();
+
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void addEventHandler(final EventHandler function,
+ final double maxCheckInterval,
+ final double convergence,
+ final int maxIterationCount,
+ final UnivariateSolver solver) {
+ super.addEventHandler(function, maxCheckInterval, convergence,
+ maxIterationCount, solver);
+
+ // reinitialize the arrays
+ initializeArrays();
+
+ }
+
+ /** Initialize the integrator internal arrays. */
+ private void initializeArrays() {
+
+ final int size = maxOrder / 2;
+
+ if ((sequence == null) || (sequence.length != size)) {
+ // all arrays should be reallocated with the right size
+ sequence = new int[size];
+ costPerStep = new int[size];
+ coeff = new double[size][];
+ costPerTimeUnit = new double[size];
+ optimalStep = new double[size];
+ }
+
+ // step size sequence: 2, 6, 10, 14, ...
+ for (int k = 0; k < size; ++k) {
+ sequence[k] = 4 * k + 2;
+ }
+
+ // initialize the order selection cost array
+ // (number of function calls for each column of the extrapolation table)
+ costPerStep[0] = sequence[0] + 1;
+ for (int k = 1; k < size; ++k) {
+ costPerStep[k] = costPerStep[k-1] + sequence[k];
+ }
+
+ // initialize the extrapolation tables
+ for (int k = 0; k < size; ++k) {
+ coeff[k] = (k > 0) ? new double[k] : null;
+ for (int l = 0; l < k; ++l) {
+ final double ratio = ((double) sequence[k]) / sequence[k-l-1];
+ coeff[k][l] = 1.0 / (ratio * ratio - 1.0);
+ }
+ }
+
+ }
+
+ /** Set the interpolation order control parameter.
+ * The interpolation order for dense output is 2k - mudif + 1. The
+ * default value for mudif is 4 and the interpolation error is used
+ * in stepsize control by default.
+
+ * @param useInterpolationErrorForControl if true, interpolation error is used
+ * for stepsize control
+ * @param mudifControlParameter interpolation order control parameter (the parameter
+ * is reset to default if <= 0 or >= 7)
+ */
+ public void setInterpolationControl(final boolean useInterpolationErrorForControl,
+ final int mudifControlParameter) {
+
+ this.useInterpolationError = useInterpolationErrorForControl;
+
+ if ((mudifControlParameter <= 0) || (mudifControlParameter >= 7)) {
+ this.mudif = 4;
+ } else {
+ this.mudif = mudifControlParameter;
+ }
+
+ }
+
+ /** Update scaling array.
+ * @param y1 first state vector to use for scaling
+ * @param y2 second state vector to use for scaling
+ * @param scale scaling array to update (can be shorter than state)
+ */
+ private void rescale(final double[] y1, final double[] y2, final double[] scale) {
+ if (vecAbsoluteTolerance == null) {
+ for (int i = 0; i < scale.length; ++i) {
+ final double yi = FastMath.max(FastMath.abs(y1[i]), FastMath.abs(y2[i]));
+ scale[i] = scalAbsoluteTolerance + scalRelativeTolerance * yi;
+ }
+ } else {
+ for (int i = 0; i < scale.length; ++i) {
+ final double yi = FastMath.max(FastMath.abs(y1[i]), FastMath.abs(y2[i]));
+ scale[i] = vecAbsoluteTolerance[i] + vecRelativeTolerance[i] * yi;
+ }
+ }
+ }
+
+ /** Perform integration over one step using substeps of a modified
+ * midpoint method.
+ * @param t0 initial time
+ * @param y0 initial value of the state vector at t0
+ * @param step global step
+ * @param k iteration number (from 0 to sequence.length - 1)
+ * @param scale scaling array (can be shorter than state)
+ * @param f placeholder where to put the state vector derivatives at each substep
+ * (element 0 already contains initial derivative)
+ * @param yMiddle placeholder where to put the state vector at the middle of the step
+ * @param yEnd placeholder where to put the state vector at the end
+ * @param yTmp placeholder for one state vector
+ * @return true if computation was done properly,
+ * false if stability check failed before end of computation
+ * @exception MaxCountExceededException if the number of functions evaluations is exceeded
+ * @exception DimensionMismatchException if arrays dimensions do not match equations settings
+ */
+ private boolean tryStep(final double t0, final double[] y0, final double step, final int k,
+ final double[] scale, final double[][] f,
+ final double[] yMiddle, final double[] yEnd,
+ final double[] yTmp)
+ throws MaxCountExceededException, DimensionMismatchException {
+
+ final int n = sequence[k];
+ final double subStep = step / n;
+ final double subStep2 = 2 * subStep;
+
+ // first substep
+ double t = t0 + subStep;
+ for (int i = 0; i < y0.length; ++i) {
+ yTmp[i] = y0[i];
+ yEnd[i] = y0[i] + subStep * f[0][i];
+ }
+ computeDerivatives(t, yEnd, f[1]);
+
+ // other substeps
+ for (int j = 1; j < n; ++j) {
+
+ if (2 * j == n) {
+ // save the point at the middle of the step
+ System.arraycopy(yEnd, 0, yMiddle, 0, y0.length);
+ }
+
+ t += subStep;
+ for (int i = 0; i < y0.length; ++i) {
+ final double middle = yEnd[i];
+ yEnd[i] = yTmp[i] + subStep2 * f[j][i];
+ yTmp[i] = middle;
+ }
+
+ computeDerivatives(t, yEnd, f[j+1]);
+
+ // stability check
+ if (performTest && (j <= maxChecks) && (k < maxIter)) {
+ double initialNorm = 0.0;
+ for (int l = 0; l < scale.length; ++l) {
+ final double ratio = f[0][l] / scale[l];
+ initialNorm += ratio * ratio;
+ }
+ double deltaNorm = 0.0;
+ for (int l = 0; l < scale.length; ++l) {
+ final double ratio = (f[j+1][l] - f[0][l]) / scale[l];
+ deltaNorm += ratio * ratio;
+ }
+ if (deltaNorm > 4 * FastMath.max(1.0e-15, initialNorm)) {
+ return false;
+ }
+ }
+
+ }
+
+ // correction of the last substep (at t0 + step)
+ for (int i = 0; i < y0.length; ++i) {
+ yEnd[i] = 0.5 * (yTmp[i] + yEnd[i] + subStep * f[n][i]);
+ }
+
+ return true;
+
+ }
+
+ /** Extrapolate a vector.
+ * @param offset offset to use in the coefficients table
+ * @param k index of the last updated point
+ * @param diag working diagonal of the Aitken-Neville's
+ * triangle, without the last element
+ * @param last last element
+ */
+ private void extrapolate(final int offset, final int k,
+ final double[][] diag, final double[] last) {
+
+ // update the diagonal
+ for (int j = 1; j < k; ++j) {
+ for (int i = 0; i < last.length; ++i) {
+ // Aitken-Neville's recursive formula
+ diag[k-j-1][i] = diag[k-j][i] +
+ coeff[k+offset][j-1] * (diag[k-j][i] - diag[k-j-1][i]);
+ }
+ }
+
+ // update the last element
+ for (int i = 0; i < last.length; ++i) {
+ // Aitken-Neville's recursive formula
+ last[i] = diag[0][i] + coeff[k+offset][k-1] * (diag[0][i] - last[i]);
+ }
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void integrate(final ExpandableStatefulODE equations, final double t)
+ throws NumberIsTooSmallException, DimensionMismatchException,
+ MaxCountExceededException, NoBracketingException {
+
+ sanityChecks(equations, t);
+ setEquations(equations);
+ final boolean forward = t > equations.getTime();
+
+ // create some internal working arrays
+ final double[] y0 = equations.getCompleteState();
+ final double[] y = y0.clone();
+ final double[] yDot0 = new double[y.length];
+ final double[] y1 = new double[y.length];
+ final double[] yTmp = new double[y.length];
+ final double[] yTmpDot = new double[y.length];
+
+ final double[][] diagonal = new double[sequence.length-1][];
+ final double[][] y1Diag = new double[sequence.length-1][];
+ for (int k = 0; k < sequence.length-1; ++k) {
+ diagonal[k] = new double[y.length];
+ y1Diag[k] = new double[y.length];
+ }
+
+ final double[][][] fk = new double[sequence.length][][];
+ for (int k = 0; k < sequence.length; ++k) {
+
+ fk[k] = new double[sequence[k] + 1][];
+
+ // all substeps start at the same point, so share the first array
+ fk[k][0] = yDot0;
+
+ for (int l = 0; l < sequence[k]; ++l) {
+ fk[k][l+1] = new double[y0.length];
+ }
+
+ }
+
+ if (y != y0) {
+ System.arraycopy(y0, 0, y, 0, y0.length);
+ }
+
+ final double[] yDot1 = new double[y0.length];
+ final double[][] yMidDots = new double[1 + 2 * sequence.length][y0.length];
+
+ // initial scaling
+ final double[] scale = new double[mainSetDimension];
+ rescale(y, y, scale);
+
+ // initial order selection
+ final double tol =
+ (vecRelativeTolerance == null) ? scalRelativeTolerance : vecRelativeTolerance[0];
+ final double log10R = FastMath.log10(FastMath.max(1.0e-10, tol));
+ int targetIter = FastMath.max(1,
+ FastMath.min(sequence.length - 2,
+ (int) FastMath.floor(0.5 - 0.6 * log10R)));
+
+ // set up an interpolator sharing the integrator arrays
+ final AbstractStepInterpolator interpolator =
+ new GraggBulirschStoerStepInterpolator(y, yDot0,
+ y1, yDot1,
+ yMidDots, forward,
+ equations.getPrimaryMapper(),
+ equations.getSecondaryMappers());
+ interpolator.storeTime(equations.getTime());
+
+ stepStart = equations.getTime();
+ double hNew = 0;
+ double maxError = Double.MAX_VALUE;
+ boolean previousRejected = false;
+ boolean firstTime = true;
+ boolean newStep = true;
+ boolean firstStepAlreadyComputed = false;
+ initIntegration(equations.getTime(), y0, t);
+ costPerTimeUnit[0] = 0;
+ isLastStep = false;
+ do {
+
+ double error;
+ boolean reject = false;
+
+ if (newStep) {
+
+ interpolator.shift();
+
+ // first evaluation, at the beginning of the step
+ if (! firstStepAlreadyComputed) {
+ computeDerivatives(stepStart, y, yDot0);
+ }
+
+ if (firstTime) {
+ hNew = initializeStep(forward, 2 * targetIter + 1, scale,
+ stepStart, y, yDot0, yTmp, yTmpDot);
+ }
+
+ newStep = false;
+
+ }
+
+ stepSize = hNew;
+
+ // step adjustment near bounds
+ if ((forward && (stepStart + stepSize > t)) ||
+ ((! forward) && (stepStart + stepSize < t))) {
+ stepSize = t - stepStart;
+ }
+ final double nextT = stepStart + stepSize;
+ isLastStep = forward ? (nextT >= t) : (nextT <= t);
+
+ // iterate over several substep sizes
+ int k = -1;
+ for (boolean loop = true; loop; ) {
+
+ ++k;
+
+ // modified midpoint integration with the current substep
+ if ( ! tryStep(stepStart, y, stepSize, k, scale, fk[k],
+ (k == 0) ? yMidDots[0] : diagonal[k-1],
+ (k == 0) ? y1 : y1Diag[k-1],
+ yTmp)) {
+
+ // the stability check failed, we reduce the global step
+ hNew = FastMath.abs(filterStep(stepSize * stabilityReduction, forward, false));
+ reject = true;
+ loop = false;
+
+ } else {
+
+ // the substep was computed successfully
+ if (k > 0) {
+
+ // extrapolate the state at the end of the step
+ // using last iteration data
+ extrapolate(0, k, y1Diag, y1);
+ rescale(y, y1, scale);
+
+ // estimate the error at the end of the step.
+ error = 0;
+ for (int j = 0; j < mainSetDimension; ++j) {
+ final double e = FastMath.abs(y1[j] - y1Diag[0][j]) / scale[j];
+ error += e * e;
+ }
+ error = FastMath.sqrt(error / mainSetDimension);
+
+ if ((error > 1.0e15) || ((k > 1) && (error > maxError))) {
+ // error is too big, we reduce the global step
+ hNew = FastMath.abs(filterStep(stepSize * stabilityReduction, forward, false));
+ reject = true;
+ loop = false;
+ } else {
+
+ maxError = FastMath.max(4 * error, 1.0);
+
+ // compute optimal stepsize for this order
+ final double exp = 1.0 / (2 * k + 1);
+ double fac = stepControl2 / FastMath.pow(error / stepControl1, exp);
+ final double pow = FastMath.pow(stepControl3, exp);
+ fac = FastMath.max(pow / stepControl4, FastMath.min(1 / pow, fac));
+ optimalStep[k] = FastMath.abs(filterStep(stepSize * fac, forward, true));
+ costPerTimeUnit[k] = costPerStep[k] / optimalStep[k];
+
+ // check convergence
+ switch (k - targetIter) {
+
+ case -1 :
+ if ((targetIter > 1) && ! previousRejected) {
+
+ // check if we can stop iterations now
+ if (error <= 1.0) {
+ // convergence have been reached just before targetIter
+ loop = false;
+ } else {
+ // estimate if there is a chance convergence will
+ // be reached on next iteration, using the
+ // asymptotic evolution of error
+ final double ratio = ((double) sequence [targetIter] * sequence[targetIter + 1]) /
+ (sequence[0] * sequence[0]);
+ if (error > ratio * ratio) {
+ // we don't expect to converge on next iteration
+ // we reject the step immediately and reduce order
+ reject = true;
+ loop = false;
+ targetIter = k;
+ if ((targetIter > 1) &&
+ (costPerTimeUnit[targetIter-1] <
+ orderControl1 * costPerTimeUnit[targetIter])) {
+ --targetIter;
+ }
+ hNew = optimalStep[targetIter];
+ }
+ }
+ }
+ break;
+
+ case 0:
+ if (error <= 1.0) {
+ // convergence has been reached exactly at targetIter
+ loop = false;
+ } else {
+ // estimate if there is a chance convergence will
+ // be reached on next iteration, using the
+ // asymptotic evolution of error
+ final double ratio = ((double) sequence[k+1]) / sequence[0];
+ if (error > ratio * ratio) {
+ // we don't expect to converge on next iteration
+ // we reject the step immediately
+ reject = true;
+ loop = false;
+ if ((targetIter > 1) &&
+ (costPerTimeUnit[targetIter-1] <
+ orderControl1 * costPerTimeUnit[targetIter])) {
+ --targetIter;
+ }
+ hNew = optimalStep[targetIter];
+ }
+ }
+ break;
+
+ case 1 :
+ if (error > 1.0) {
+ reject = true;
+ if ((targetIter > 1) &&
+ (costPerTimeUnit[targetIter-1] <
+ orderControl1 * costPerTimeUnit[targetIter])) {
+ --targetIter;
+ }
+ hNew = optimalStep[targetIter];
+ }
+ loop = false;
+ break;
+
+ default :
+ if ((firstTime || isLastStep) && (error <= 1.0)) {
+ loop = false;
+ }
+ break;
+
+ }
+
+ }
+ }
+ }
+ }
+
+ if (! reject) {
+ // derivatives at end of step
+ computeDerivatives(stepStart + stepSize, y1, yDot1);
+ }
+
+ // dense output handling
+ double hInt = getMaxStep();
+ if (! reject) {
+
+ // extrapolate state at middle point of the step
+ for (int j = 1; j <= k; ++j) {
+ extrapolate(0, j, diagonal, yMidDots[0]);
+ }
+
+ final int mu = 2 * k - mudif + 3;
+
+ for (int l = 0; l < mu; ++l) {
+
+ // derivative at middle point of the step
+ final int l2 = l / 2;
+ double factor = FastMath.pow(0.5 * sequence[l2], l);
+ int middleIndex = fk[l2].length / 2;
+ for (int i = 0; i < y0.length; ++i) {
+ yMidDots[l+1][i] = factor * fk[l2][middleIndex + l][i];
+ }
+ for (int j = 1; j <= k - l2; ++j) {
+ factor = FastMath.pow(0.5 * sequence[j + l2], l);
+ middleIndex = fk[l2+j].length / 2;
+ for (int i = 0; i < y0.length; ++i) {
+ diagonal[j-1][i] = factor * fk[l2+j][middleIndex+l][i];
+ }
+ extrapolate(l2, j, diagonal, yMidDots[l+1]);
+ }
+ for (int i = 0; i < y0.length; ++i) {
+ yMidDots[l+1][i] *= stepSize;
+ }
+
+ // compute centered differences to evaluate next derivatives
+ for (int j = (l + 1) / 2; j <= k; ++j) {
+ for (int m = fk[j].length - 1; m >= 2 * (l + 1); --m) {
+ for (int i = 0; i < y0.length; ++i) {
+ fk[j][m][i] -= fk[j][m-2][i];
+ }
+ }
+ }
+
+ }
+
+ if (mu >= 0) {
+
+ // estimate the dense output coefficients
+ final GraggBulirschStoerStepInterpolator gbsInterpolator
+ = (GraggBulirschStoerStepInterpolator) interpolator;
+ gbsInterpolator.computeCoefficients(mu, stepSize);
+
+ if (useInterpolationError) {
+ // use the interpolation error to limit stepsize
+ final double interpError = gbsInterpolator.estimateError(scale);
+ hInt = FastMath.abs(stepSize / FastMath.max(FastMath.pow(interpError, 1.0 / (mu+4)),
+ 0.01));
+ if (interpError > 10.0) {
+ hNew = hInt;
+ reject = true;
+ }
+ }
+
+ }
+
+ }
+
+ if (! reject) {
+
+ // Discrete events handling
+ interpolator.storeTime(stepStart + stepSize);
+ stepStart = acceptStep(interpolator, y1, yDot1, t);
+
+ // prepare next step
+ interpolator.storeTime(stepStart);
+ System.arraycopy(y1, 0, y, 0, y0.length);
+ System.arraycopy(yDot1, 0, yDot0, 0, y0.length);
+ firstStepAlreadyComputed = true;
+
+ int optimalIter;
+ if (k == 1) {
+ optimalIter = 2;
+ if (previousRejected) {
+ optimalIter = 1;
+ }
+ } else if (k <= targetIter) {
+ optimalIter = k;
+ if (costPerTimeUnit[k-1] < orderControl1 * costPerTimeUnit[k]) {
+ optimalIter = k-1;
+ } else if (costPerTimeUnit[k] < orderControl2 * costPerTimeUnit[k-1]) {
+ optimalIter = FastMath.min(k+1, sequence.length - 2);
+ }
+ } else {
+ optimalIter = k - 1;
+ if ((k > 2) &&
+ (costPerTimeUnit[k-2] < orderControl1 * costPerTimeUnit[k-1])) {
+ optimalIter = k - 2;
+ }
+ if (costPerTimeUnit[k] < orderControl2 * costPerTimeUnit[optimalIter]) {
+ optimalIter = FastMath.min(k, sequence.length - 2);
+ }
+ }
+
+ if (previousRejected) {
+ // after a rejected step neither order nor stepsize
+ // should increase
+ targetIter = FastMath.min(optimalIter, k);
+ hNew = FastMath.min(FastMath.abs(stepSize), optimalStep[targetIter]);
+ } else {
+ // stepsize control
+ if (optimalIter <= k) {
+ hNew = optimalStep[optimalIter];
+ } else {
+ if ((k < targetIter) &&
+ (costPerTimeUnit[k] < orderControl2 * costPerTimeUnit[k-1])) {
+ hNew = filterStep(optimalStep[k] * costPerStep[optimalIter+1] / costPerStep[k],
+ forward, false);
+ } else {
+ hNew = filterStep(optimalStep[k] * costPerStep[optimalIter] / costPerStep[k],
+ forward, false);
+ }
+ }
+
+ targetIter = optimalIter;
+
+ }
+
+ newStep = true;
+
+ }
+
+ hNew = FastMath.min(hNew, hInt);
+ if (! forward) {
+ hNew = -hNew;
+ }
+
+ firstTime = false;
+
+ if (reject) {
+ isLastStep = false;
+ previousRejected = true;
+ } else {
+ previousRejected = false;
+ }
+
+ } while (!isLastStep);
+
+ // dispatch results
+ equations.setTime(stepStart);
+ equations.setCompleteState(y);
+
+ resetInternalState();
+
+ }
+
+}
diff --git a/src/main/java/org/apache/commons/math3/ode/nonstiff/GraggBulirschStoerStepInterpolator.java b/src/main/java/org/apache/commons/math3/ode/nonstiff/GraggBulirschStoerStepInterpolator.java
new file mode 100644
index 0000000..33c2705
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/ode/nonstiff/GraggBulirschStoerStepInterpolator.java
@@ -0,0 +1,407 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math3.ode.nonstiff;
+
+import java.io.IOException;
+import java.io.ObjectInput;
+import java.io.ObjectOutput;
+
+import org.apache.commons.math3.ode.EquationsMapper;
+import org.apache.commons.math3.ode.sampling.AbstractStepInterpolator;
+import org.apache.commons.math3.ode.sampling.StepInterpolator;
+import org.apache.commons.math3.util.FastMath;
+
+/**
+ * This class implements an interpolator for the Gragg-Bulirsch-Stoer
+ * integrator.
+ *
+ * <p>This interpolator compute dense output inside the last step
+ * produced by a Gragg-Bulirsch-Stoer integrator.</p>
+ *
+ * <p>
+ * This implementation is basically a reimplementation in Java of the
+ * <a
+ * href="http://www.unige.ch/math/folks/hairer/prog/nonstiff/odex.f">odex</a>
+ * fortran code by E. Hairer and G. Wanner. The redistribution policy
+ * for this code is available <a
+ * href="http://www.unige.ch/~hairer/prog/licence.txt">here</a>, for
+ * convenience, it is reproduced below.</p>
+ * </p>
+ *
+ * <table border="0" width="80%" cellpadding="10" align="center" bgcolor="#E0E0E0">
+ * <tr><td>Copyright (c) 2004, Ernst Hairer</td></tr>
+ *
+ * <tr><td>Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ * <ul>
+ * <li>Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.</li>
+ * <li>Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.</li>
+ * </ul></td></tr>
+ *
+ * <tr><td><strong>THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING,
+ * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.</strong></td></tr>
+ * </table>
+ *
+ * @see GraggBulirschStoerIntegrator
+ * @since 1.2
+ */
+
+class GraggBulirschStoerStepInterpolator
+ extends AbstractStepInterpolator {
+
+ /** Serializable version identifier. */
+ private static final long serialVersionUID = 20110928L;
+
+ /** Slope at the beginning of the step. */
+ private double[] y0Dot;
+
+ /** State at the end of the step. */
+ private double[] y1;
+
+ /** Slope at the end of the step. */
+ private double[] y1Dot;
+
+ /** Derivatives at the middle of the step.
+ * element 0 is state at midpoint, element 1 is first derivative ...
+ */
+ private double[][] yMidDots;
+
+ /** Interpolation polynomials. */
+ private double[][] polynomials;
+
+ /** Error coefficients for the interpolation. */
+ private double[] errfac;
+
+ /** Degree of the interpolation polynomials. */
+ private int currentDegree;
+
+ /** Simple constructor.
+ * This constructor should not be used directly, it is only intended
+ * for the serialization process.
+ */
+ // CHECKSTYLE: stop RedundantModifier
+ // the public modifier here is needed for serialization
+ public GraggBulirschStoerStepInterpolator() {
+ y0Dot = null;
+ y1 = null;
+ y1Dot = null;
+ yMidDots = null;
+ resetTables(-1);
+ }
+ // CHECKSTYLE: resume RedundantModifier
+
+ /** Simple constructor.
+ * @param y reference to the integrator array holding the current state
+ * @param y0Dot reference to the integrator array holding the slope
+ * at the beginning of the step
+ * @param y1 reference to the integrator array holding the state at
+ * the end of the step
+ * @param y1Dot reference to the integrator array holding the slope
+ * at the end of the step
+ * @param yMidDots reference to the integrator array holding the
+ * derivatives at the middle point of the step
+ * @param forward integration direction indicator
+ * @param primaryMapper equations mapper for the primary equations set
+ * @param secondaryMappers equations mappers for the secondary equations sets
+ */
+ GraggBulirschStoerStepInterpolator(final double[] y, final double[] y0Dot,
+ final double[] y1, final double[] y1Dot,
+ final double[][] yMidDots,
+ final boolean forward,
+ final EquationsMapper primaryMapper,
+ final EquationsMapper[] secondaryMappers) {
+
+ super(y, forward, primaryMapper, secondaryMappers);
+ this.y0Dot = y0Dot;
+ this.y1 = y1;
+ this.y1Dot = y1Dot;
+ this.yMidDots = yMidDots;
+
+ resetTables(yMidDots.length + 4);
+
+ }
+
+ /** Copy constructor.
+ * @param interpolator interpolator to copy from. The copy is a deep
+ * copy: its arrays are separated from the original arrays of the
+ * instance
+ */
+ GraggBulirschStoerStepInterpolator(final GraggBulirschStoerStepInterpolator interpolator) {
+
+ super(interpolator);
+
+ final int dimension = currentState.length;
+
+ // the interpolator has been finalized,
+ // the following arrays are not needed anymore
+ y0Dot = null;
+ y1 = null;
+ y1Dot = null;
+ yMidDots = null;
+
+ // copy the interpolation polynomials (up to the current degree only)
+ if (interpolator.polynomials == null) {
+ polynomials = null;
+ currentDegree = -1;
+ } else {
+ resetTables(interpolator.currentDegree);
+ for (int i = 0; i < polynomials.length; ++i) {
+ polynomials[i] = new double[dimension];
+ System.arraycopy(interpolator.polynomials[i], 0,
+ polynomials[i], 0, dimension);
+ }
+ currentDegree = interpolator.currentDegree;
+ }
+
+ }
+
+ /** Reallocate the internal tables.
+ * Reallocate the internal tables in order to be able to handle
+ * interpolation polynomials up to the given degree
+ * @param maxDegree maximal degree to handle
+ */
+ private void resetTables(final int maxDegree) {
+
+ if (maxDegree < 0) {
+ polynomials = null;
+ errfac = null;
+ currentDegree = -1;
+ } else {
+
+ final double[][] newPols = new double[maxDegree + 1][];
+ if (polynomials != null) {
+ System.arraycopy(polynomials, 0, newPols, 0, polynomials.length);
+ for (int i = polynomials.length; i < newPols.length; ++i) {
+ newPols[i] = new double[currentState.length];
+ }
+ } else {
+ for (int i = 0; i < newPols.length; ++i) {
+ newPols[i] = new double[currentState.length];
+ }
+ }
+ polynomials = newPols;
+
+ // initialize the error factors array for interpolation
+ if (maxDegree <= 4) {
+ errfac = null;
+ } else {
+ errfac = new double[maxDegree - 4];
+ for (int i = 0; i < errfac.length; ++i) {
+ final int ip5 = i + 5;
+ errfac[i] = 1.0 / (ip5 * ip5);
+ final double e = 0.5 * FastMath.sqrt (((double) (i + 1)) / ip5);
+ for (int j = 0; j <= i; ++j) {
+ errfac[i] *= e / (j + 1);
+ }
+ }
+ }
+
+ currentDegree = 0;
+
+ }
+
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ protected StepInterpolator doCopy() {
+ return new GraggBulirschStoerStepInterpolator(this);
+ }
+
+
+ /** Compute the interpolation coefficients for dense output.
+ * @param mu degree of the interpolation polynomial
+ * @param h current step
+ */
+ public void computeCoefficients(final int mu, final double h) {
+
+ if ((polynomials == null) || (polynomials.length <= (mu + 4))) {
+ resetTables(mu + 4);
+ }
+
+ currentDegree = mu + 4;
+
+ for (int i = 0; i < currentState.length; ++i) {
+
+ final double yp0 = h * y0Dot[i];
+ final double yp1 = h * y1Dot[i];
+ final double ydiff = y1[i] - currentState[i];
+ final double aspl = ydiff - yp1;
+ final double bspl = yp0 - ydiff;
+
+ polynomials[0][i] = currentState[i];
+ polynomials[1][i] = ydiff;
+ polynomials[2][i] = aspl;
+ polynomials[3][i] = bspl;
+
+ if (mu < 0) {
+ return;
+ }
+
+ // compute the remaining coefficients
+ final double ph0 = 0.5 * (currentState[i] + y1[i]) + 0.125 * (aspl + bspl);
+ polynomials[4][i] = 16 * (yMidDots[0][i] - ph0);
+
+ if (mu > 0) {
+ final double ph1 = ydiff + 0.25 * (aspl - bspl);
+ polynomials[5][i] = 16 * (yMidDots[1][i] - ph1);
+
+ if (mu > 1) {
+ final double ph2 = yp1 - yp0;
+ polynomials[6][i] = 16 * (yMidDots[2][i] - ph2 + polynomials[4][i]);
+
+ if (mu > 2) {
+ final double ph3 = 6 * (bspl - aspl);
+ polynomials[7][i] = 16 * (yMidDots[3][i] - ph3 + 3 * polynomials[5][i]);
+
+ for (int j = 4; j <= mu; ++j) {
+ final double fac1 = 0.5 * j * (j - 1);
+ final double fac2 = 2 * fac1 * (j - 2) * (j - 3);
+ polynomials[j+4][i] =
+ 16 * (yMidDots[j][i] + fac1 * polynomials[j+2][i] - fac2 * polynomials[j][i]);
+ }
+
+ }
+ }
+ }
+ }
+
+ }
+
+ /** Estimate interpolation error.
+ * @param scale scaling array
+ * @return estimate of the interpolation error
+ */
+ public double estimateError(final double[] scale) {
+ double error = 0;
+ if (currentDegree >= 5) {
+ for (int i = 0; i < scale.length; ++i) {
+ final double e = polynomials[currentDegree][i] / scale[i];
+ error += e * e;
+ }
+ error = FastMath.sqrt(error / scale.length) * errfac[currentDegree - 5];
+ }
+ return error;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ protected void computeInterpolatedStateAndDerivatives(final double theta,
+ final double oneMinusThetaH) {
+
+ final int dimension = currentState.length;
+
+ final double oneMinusTheta = 1.0 - theta;
+ final double theta05 = theta - 0.5;
+ final double tOmT = theta * oneMinusTheta;
+ final double t4 = tOmT * tOmT;
+ final double t4Dot = 2 * tOmT * (1 - 2 * theta);
+ final double dot1 = 1.0 / h;
+ final double dot2 = theta * (2 - 3 * theta) / h;
+ final double dot3 = ((3 * theta - 4) * theta + 1) / h;
+
+ for (int i = 0; i < dimension; ++i) {
+
+ final double p0 = polynomials[0][i];
+ final double p1 = polynomials[1][i];
+ final double p2 = polynomials[2][i];
+ final double p3 = polynomials[3][i];
+ interpolatedState[i] = p0 + theta * (p1 + oneMinusTheta * (p2 * theta + p3 * oneMinusTheta));
+ interpolatedDerivatives[i] = dot1 * p1 + dot2 * p2 + dot3 * p3;
+
+ if (currentDegree > 3) {
+ double cDot = 0;
+ double c = polynomials[currentDegree][i];
+ for (int j = currentDegree - 1; j > 3; --j) {
+ final double d = 1.0 / (j - 3);
+ cDot = d * (theta05 * cDot + c);
+ c = polynomials[j][i] + c * d * theta05;
+ }
+ interpolatedState[i] += t4 * c;
+ interpolatedDerivatives[i] += (t4 * cDot + t4Dot * c) / h;
+ }
+
+ }
+
+ if (h == 0) {
+ // in this degenerated case, the previous computation leads to NaN for derivatives
+ // we fix this by using the derivatives at midpoint
+ System.arraycopy(yMidDots[1], 0, interpolatedDerivatives, 0, dimension);
+ }
+
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void writeExternal(final ObjectOutput out)
+ throws IOException {
+
+ final int dimension = (currentState == null) ? -1 : currentState.length;
+
+ // save the state of the base class
+ writeBaseExternal(out);
+
+ // save the local attributes (but not the temporary vectors)
+ out.writeInt(currentDegree);
+ for (int k = 0; k <= currentDegree; ++k) {
+ for (int l = 0; l < dimension; ++l) {
+ out.writeDouble(polynomials[k][l]);
+ }
+ }
+
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void readExternal(final ObjectInput in)
+ throws IOException, ClassNotFoundException {
+
+ // read the base class
+ final double t = readBaseExternal(in);
+ final int dimension = (currentState == null) ? -1 : currentState.length;
+
+ // read the local attributes
+ final int degree = in.readInt();
+ resetTables(degree);
+ currentDegree = degree;
+
+ for (int k = 0; k <= currentDegree; ++k) {
+ for (int l = 0; l < dimension; ++l) {
+ polynomials[k][l] = in.readDouble();
+ }
+ }
+
+ // we can now set the interpolated time and state
+ setInterpolatedTime(t);
+
+ }
+
+}
diff --git a/src/main/java/org/apache/commons/math3/ode/nonstiff/HighamHall54FieldIntegrator.java b/src/main/java/org/apache/commons/math3/ode/nonstiff/HighamHall54FieldIntegrator.java
new file mode 100644
index 0000000..d7d6f63
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/ode/nonstiff/HighamHall54FieldIntegrator.java
@@ -0,0 +1,205 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math3.ode.nonstiff;
+
+import org.apache.commons.math3.Field;
+import org.apache.commons.math3.RealFieldElement;
+import org.apache.commons.math3.ode.FieldEquationsMapper;
+import org.apache.commons.math3.ode.FieldODEStateAndDerivative;
+import org.apache.commons.math3.util.MathArrays;
+import org.apache.commons.math3.util.MathUtils;
+
+
+/**
+ * This class implements the 5(4) Higham and Hall integrator for
+ * Ordinary Differential Equations.
+ *
+ * <p>This integrator is an embedded Runge-Kutta integrator
+ * of order 5(4) used in local extrapolation mode (i.e. the solution
+ * is computed using the high order formula) with stepsize control
+ * (and automatic step initialization) and continuous output. This
+ * method uses 7 functions evaluations per step.</p>
+ *
+ * @param <T> the type of the field elements
+ * @since 3.6
+ */
+
+public class HighamHall54FieldIntegrator<T extends RealFieldElement<T>>
+ extends EmbeddedRungeKuttaFieldIntegrator<T> {
+
+ /** Integrator method name. */
+ private static final String METHOD_NAME = "Higham-Hall 5(4)";
+
+ /** Error weights Butcher array. */
+ private final T[] e ;
+
+ /** Simple constructor.
+ * Build a fifth order Higham and Hall integrator with the given step bounds
+ * @param field field to which the time and state vector elements belong
+ * @param minStep minimal step (sign is irrelevant, regardless of
+ * integration direction, forward or backward), the last step can
+ * be smaller than this
+ * @param maxStep maximal step (sign is irrelevant, regardless of
+ * integration direction, forward or backward), the last step can
+ * be smaller than this
+ * @param scalAbsoluteTolerance allowed absolute error
+ * @param scalRelativeTolerance allowed relative error
+ */
+ public HighamHall54FieldIntegrator(final Field<T> field,
+ final double minStep, final double maxStep,
+ final double scalAbsoluteTolerance,
+ final double scalRelativeTolerance) {
+ super(field, METHOD_NAME, -1,
+ minStep, maxStep, scalAbsoluteTolerance, scalRelativeTolerance);
+ e = MathArrays.buildArray(field, 7);
+ e[0] = fraction(-1, 20);
+ e[1] = field.getZero();
+ e[2] = fraction(81, 160);
+ e[3] = fraction(-6, 5);
+ e[4] = fraction(25, 32);
+ e[5] = fraction( 1, 16);
+ e[6] = fraction(-1, 10);
+ }
+
+ /** Simple constructor.
+ * Build a fifth order Higham and Hall integrator with the given step bounds
+ * @param field field to which the time and state vector elements belong
+ * @param minStep minimal step (sign is irrelevant, regardless of
+ * integration direction, forward or backward), the last step can
+ * be smaller than this
+ * @param maxStep maximal step (sign is irrelevant, regardless of
+ * integration direction, forward or backward), the last step can
+ * be smaller than this
+ * @param vecAbsoluteTolerance allowed absolute error
+ * @param vecRelativeTolerance allowed relative error
+ */
+ public HighamHall54FieldIntegrator(final Field<T> field,
+ final double minStep, final double maxStep,
+ final double[] vecAbsoluteTolerance,
+ final double[] vecRelativeTolerance) {
+ super(field, METHOD_NAME, -1,
+ minStep, maxStep, vecAbsoluteTolerance, vecRelativeTolerance);
+ e = MathArrays.buildArray(field, 7);
+ e[0] = fraction(-1, 20);
+ e[1] = field.getZero();
+ e[2] = fraction(81, 160);
+ e[3] = fraction(-6, 5);
+ e[4] = fraction(25, 32);
+ e[5] = fraction( 1, 16);
+ e[6] = fraction(-1, 10);
+ }
+
+ /** {@inheritDoc} */
+ public T[] getC() {
+ final T[] c = MathArrays.buildArray(getField(), 6);
+ c[0] = fraction(2, 9);
+ c[1] = fraction(1, 3);
+ c[2] = fraction(1, 2);
+ c[3] = fraction(3, 5);
+ c[4] = getField().getOne();
+ c[5] = getField().getOne();
+ return c;
+ }
+
+ /** {@inheritDoc} */
+ public T[][] getA() {
+ final T[][] a = MathArrays.buildArray(getField(), 6, -1);
+ for (int i = 0; i < a.length; ++i) {
+ a[i] = MathArrays.buildArray(getField(), i + 1);
+ }
+ a[0][0] = fraction( 2, 9);
+ a[1][0] = fraction( 1, 12);
+ a[1][1] = fraction( 1, 4);
+ a[2][0] = fraction( 1, 8);
+ a[2][1] = getField().getZero();
+ a[2][2] = fraction( 3, 8);
+ a[3][0] = fraction( 91, 500);
+ a[3][1] = fraction( -27, 100);
+ a[3][2] = fraction( 78, 125);
+ a[3][3] = fraction( 8, 125);
+ a[4][0] = fraction( -11, 20);
+ a[4][1] = fraction( 27, 20);
+ a[4][2] = fraction( 12, 5);
+ a[4][3] = fraction( -36, 5);
+ a[4][4] = fraction( 5, 1);
+ a[5][0] = fraction( 1, 12);
+ a[5][1] = getField().getZero();
+ a[5][2] = fraction( 27, 32);
+ a[5][3] = fraction( -4, 3);
+ a[5][4] = fraction( 125, 96);
+ a[5][5] = fraction( 5, 48);
+ return a;
+ }
+
+ /** {@inheritDoc} */
+ public T[] getB() {
+ final T[] b = MathArrays.buildArray(getField(), 7);
+ b[0] = fraction( 1, 12);
+ b[1] = getField().getZero();
+ b[2] = fraction( 27, 32);
+ b[3] = fraction( -4, 3);
+ b[4] = fraction(125, 96);
+ b[5] = fraction( 5, 48);
+ b[6] = getField().getZero();
+ return b;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ protected HighamHall54FieldStepInterpolator<T>
+ createInterpolator(final boolean forward, T[][] yDotK,
+ final FieldODEStateAndDerivative<T> globalPreviousState,
+ final FieldODEStateAndDerivative<T> globalCurrentState, final FieldEquationsMapper<T> mapper) {
+ return new HighamHall54FieldStepInterpolator<T>(getField(), forward, yDotK,
+ globalPreviousState, globalCurrentState,
+ globalPreviousState, globalCurrentState,
+ mapper);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public int getOrder() {
+ return 5;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ protected T estimateError(final T[][] yDotK, final T[] y0, final T[] y1, final T h) {
+
+ T error = getField().getZero();
+
+ for (int j = 0; j < mainSetDimension; ++j) {
+ T errSum = yDotK[0][j].multiply(e[0]);
+ for (int l = 1; l < e.length; ++l) {
+ errSum = errSum.add(yDotK[l][j].multiply(e[l]));
+ }
+
+ final T yScale = MathUtils.max(y0[j].abs(), y1[j].abs());
+ final T tol = (vecAbsoluteTolerance == null) ?
+ yScale.multiply(scalRelativeTolerance).add(scalAbsoluteTolerance) :
+ yScale.multiply(vecRelativeTolerance[j]).add(vecAbsoluteTolerance[j]);
+ final T ratio = h.multiply(errSum).divide(tol);
+ error = error.add(ratio.multiply(ratio));
+
+ }
+
+ return error.divide(mainSetDimension).sqrt();
+
+ }
+
+}
diff --git a/src/main/java/org/apache/commons/math3/ode/nonstiff/HighamHall54FieldStepInterpolator.java b/src/main/java/org/apache/commons/math3/ode/nonstiff/HighamHall54FieldStepInterpolator.java
new file mode 100644
index 0000000..10240fd
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/ode/nonstiff/HighamHall54FieldStepInterpolator.java
@@ -0,0 +1,116 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math3.ode.nonstiff;
+
+import org.apache.commons.math3.Field;
+import org.apache.commons.math3.RealFieldElement;
+import org.apache.commons.math3.ode.FieldEquationsMapper;
+import org.apache.commons.math3.ode.FieldODEStateAndDerivative;
+
+/**
+ * This class represents an interpolator over the last step during an
+ * ODE integration for the 5(4) Higham and Hall integrator.
+ *
+ * @see HighamHall54FieldIntegrator
+ *
+ * @param <T> the type of the field elements
+ * @since 3.6
+ */
+
+class HighamHall54FieldStepInterpolator<T extends RealFieldElement<T>>
+ extends RungeKuttaFieldStepInterpolator<T> {
+
+ /** Simple constructor.
+ * @param field field to which the time and state vector elements belong
+ * @param forward integration direction indicator
+ * @param yDotK slopes at the intermediate points
+ * @param globalPreviousState start of the global step
+ * @param globalCurrentState end of the global step
+ * @param softPreviousState start of the restricted step
+ * @param softCurrentState end of the restricted step
+ * @param mapper equations mapper for the all equations
+ */
+ HighamHall54FieldStepInterpolator(final Field<T> field, final boolean forward,
+ final T[][] yDotK,
+ final FieldODEStateAndDerivative<T> globalPreviousState,
+ final FieldODEStateAndDerivative<T> globalCurrentState,
+ final FieldODEStateAndDerivative<T> softPreviousState,
+ final FieldODEStateAndDerivative<T> softCurrentState,
+ final FieldEquationsMapper<T> mapper) {
+ super(field, forward, yDotK,
+ globalPreviousState, globalCurrentState, softPreviousState, softCurrentState,
+ mapper);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ protected HighamHall54FieldStepInterpolator<T> create(final Field<T> newField, final boolean newForward, final T[][] newYDotK,
+ final FieldODEStateAndDerivative<T> newGlobalPreviousState,
+ final FieldODEStateAndDerivative<T> newGlobalCurrentState,
+ final FieldODEStateAndDerivative<T> newSoftPreviousState,
+ final FieldODEStateAndDerivative<T> newSoftCurrentState,
+ final FieldEquationsMapper<T> newMapper) {
+ return new HighamHall54FieldStepInterpolator<T>(newField, newForward, newYDotK,
+ newGlobalPreviousState, newGlobalCurrentState,
+ newSoftPreviousState, newSoftCurrentState,
+ newMapper);
+ }
+
+ /** {@inheritDoc} */
+ @SuppressWarnings("unchecked")
+ @Override
+ protected FieldODEStateAndDerivative<T> computeInterpolatedStateAndDerivatives(final FieldEquationsMapper<T> mapper,
+ final T time, final T theta,
+ final T thetaH, final T oneMinusThetaH) {
+
+ final T bDot0 = theta.multiply(theta.multiply(theta.multiply( -10.0 ).add( 16.0 )).add(-15.0 / 2.0)).add(1);
+ final T bDot1 = time.getField().getZero();
+ final T bDot2 = theta.multiply(theta.multiply(theta.multiply( 135.0 / 2.0).add(-729.0 / 8.0)).add(459.0 / 16.0));
+ final T bDot3 = theta.multiply(theta.multiply(theta.multiply(-120.0 ).add( 152.0 )).add(-44.0 ));
+ final T bDot4 = theta.multiply(theta.multiply(theta.multiply( 125.0 / 2.0).add(-625.0 / 8.0)).add(375.0 / 16.0));
+ final T bDot5 = theta.multiply( 5.0 / 8.0).multiply(theta.multiply(2).subtract(1));
+ final T[] interpolatedState;
+ final T[] interpolatedDerivatives;
+
+ if (getGlobalPreviousState() != null && theta.getReal() <= 0.5) {
+ final T b0 = thetaH.multiply(theta.multiply(theta.multiply(theta.multiply( -5.0 / 2.0).add( 16.0 / 3.0)).add(-15.0 / 4.0)).add(1));
+ final T b1 = time.getField().getZero();
+ final T b2 = thetaH.multiply(theta.multiply(theta.multiply(theta.multiply(135.0 / 8.0).add(-243.0 / 8.0)).add(459.0 / 32.0)));
+ final T b3 = thetaH.multiply(theta.multiply(theta.multiply(theta.multiply(-30.0 ).add( 152.0 / 3.0)).add(-22.0 )));
+ final T b4 = thetaH.multiply(theta.multiply(theta.multiply(theta.multiply(125.0 / 8.0).add(-625.0 / 24.0)).add(375.0 / 32.0)));
+ final T b5 = thetaH.multiply(theta.multiply(theta.multiply( 5.0 / 12.0 ).add( -5.0 / 16.0)));
+ interpolatedState = previousStateLinearCombination(b0, b1, b2, b3, b4, b5);
+ interpolatedDerivatives = derivativeLinearCombination(bDot0, bDot1, bDot2, bDot3, bDot4, bDot5);
+ } else {
+ final T theta2 = theta.multiply(theta);
+ final T h = thetaH.divide(theta);
+ final T b0 = h.multiply( theta.multiply(theta.multiply(theta.multiply(theta.multiply(-5.0 / 2.0).add( 16.0 / 3.0)).add( -15.0 / 4.0)).add( 1.0 )).add( -1.0 / 12.0));
+ final T b1 = time.getField().getZero();
+ final T b2 = h.multiply(theta2.multiply(theta.multiply(theta.multiply( 135.0 / 8.0 ).add(-243.0 / 8.0)).add(459.0 / 32.0)).add( -27.0 / 32.0));
+ final T b3 = h.multiply(theta2.multiply(theta.multiply(theta.multiply( -30.0 ).add( 152.0 / 3.0)).add(-22.0 )).add( 4.0 / 3.0));
+ final T b4 = h.multiply(theta2.multiply(theta.multiply(theta.multiply( 125.0 / 8.0 ).add(-625.0 / 24.0)).add(375.0 / 32.0)).add(-125.0 / 96.0));
+ final T b5 = h.multiply(theta2.multiply(theta.multiply( 5.0 / 12.0 ).add(-5.0 / 16.0)).add( -5.0 / 48.0));
+ interpolatedState = currentStateLinearCombination(b0, b1, b2, b3, b4, b5);
+ interpolatedDerivatives = derivativeLinearCombination(bDot0, bDot1, bDot2, bDot3, bDot4, bDot5);
+ }
+
+ return new FieldODEStateAndDerivative<T>(time, interpolatedState, interpolatedDerivatives);
+
+ }
+
+}
diff --git a/src/main/java/org/apache/commons/math3/ode/nonstiff/HighamHall54Integrator.java b/src/main/java/org/apache/commons/math3/ode/nonstiff/HighamHall54Integrator.java
new file mode 100644
index 0000000..c48c4f9
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/ode/nonstiff/HighamHall54Integrator.java
@@ -0,0 +1,135 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math3.ode.nonstiff;
+
+import org.apache.commons.math3.util.FastMath;
+
+
+/**
+ * This class implements the 5(4) Higham and Hall integrator for
+ * Ordinary Differential Equations.
+ *
+ * <p>This integrator is an embedded Runge-Kutta integrator
+ * of order 5(4) used in local extrapolation mode (i.e. the solution
+ * is computed using the high order formula) with stepsize control
+ * (and automatic step initialization) and continuous output. This
+ * method uses 7 functions evaluations per step.</p>
+ *
+ * @since 1.2
+ */
+
+public class HighamHall54Integrator extends EmbeddedRungeKuttaIntegrator {
+
+ /** Integrator method name. */
+ private static final String METHOD_NAME = "Higham-Hall 5(4)";
+
+ /** Time steps Butcher array. */
+ private static final double[] STATIC_C = {
+ 2.0/9.0, 1.0/3.0, 1.0/2.0, 3.0/5.0, 1.0, 1.0
+ };
+
+ /** Internal weights Butcher array. */
+ private static final double[][] STATIC_A = {
+ {2.0/9.0},
+ {1.0/12.0, 1.0/4.0},
+ {1.0/8.0, 0.0, 3.0/8.0},
+ {91.0/500.0, -27.0/100.0, 78.0/125.0, 8.0/125.0},
+ {-11.0/20.0, 27.0/20.0, 12.0/5.0, -36.0/5.0, 5.0},
+ {1.0/12.0, 0.0, 27.0/32.0, -4.0/3.0, 125.0/96.0, 5.0/48.0}
+ };
+
+ /** Propagation weights Butcher array. */
+ private static final double[] STATIC_B = {
+ 1.0/12.0, 0.0, 27.0/32.0, -4.0/3.0, 125.0/96.0, 5.0/48.0, 0.0
+ };
+
+ /** Error weights Butcher array. */
+ private static final double[] STATIC_E = {
+ -1.0/20.0, 0.0, 81.0/160.0, -6.0/5.0, 25.0/32.0, 1.0/16.0, -1.0/10.0
+ };
+
+ /** Simple constructor.
+ * Build a fifth order Higham and Hall integrator with the given step bounds
+ * @param minStep minimal step (sign is irrelevant, regardless of
+ * integration direction, forward or backward), the last step can
+ * be smaller than this
+ * @param maxStep maximal step (sign is irrelevant, regardless of
+ * integration direction, forward or backward), the last step can
+ * be smaller than this
+ * @param scalAbsoluteTolerance allowed absolute error
+ * @param scalRelativeTolerance allowed relative error
+ */
+ public HighamHall54Integrator(final double minStep, final double maxStep,
+ final double scalAbsoluteTolerance,
+ final double scalRelativeTolerance) {
+ super(METHOD_NAME, false, STATIC_C, STATIC_A, STATIC_B, new HighamHall54StepInterpolator(),
+ minStep, maxStep, scalAbsoluteTolerance, scalRelativeTolerance);
+ }
+
+ /** Simple constructor.
+ * Build a fifth order Higham and Hall integrator with the given step bounds
+ * @param minStep minimal step (sign is irrelevant, regardless of
+ * integration direction, forward or backward), the last step can
+ * be smaller than this
+ * @param maxStep maximal step (sign is irrelevant, regardless of
+ * integration direction, forward or backward), the last step can
+ * be smaller than this
+ * @param vecAbsoluteTolerance allowed absolute error
+ * @param vecRelativeTolerance allowed relative error
+ */
+ public HighamHall54Integrator(final double minStep, final double maxStep,
+ final double[] vecAbsoluteTolerance,
+ final double[] vecRelativeTolerance) {
+ super(METHOD_NAME, false, STATIC_C, STATIC_A, STATIC_B, new HighamHall54StepInterpolator(),
+ minStep, maxStep, vecAbsoluteTolerance, vecRelativeTolerance);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public int getOrder() {
+ return 5;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ protected double estimateError(final double[][] yDotK,
+ final double[] y0, final double[] y1,
+ final double h) {
+
+ double error = 0;
+
+ for (int j = 0; j < mainSetDimension; ++j) {
+ double errSum = STATIC_E[0] * yDotK[0][j];
+ for (int l = 1; l < STATIC_E.length; ++l) {
+ errSum += STATIC_E[l] * yDotK[l][j];
+ }
+
+ final double yScale = FastMath.max(FastMath.abs(y0[j]), FastMath.abs(y1[j]));
+ final double tol = (vecAbsoluteTolerance == null) ?
+ (scalAbsoluteTolerance + scalRelativeTolerance * yScale) :
+ (vecAbsoluteTolerance[j] + vecRelativeTolerance[j] * yScale);
+ final double ratio = h * errSum / tol;
+ error += ratio * ratio;
+
+ }
+
+ return FastMath.sqrt(error / mainSetDimension);
+
+ }
+
+}
diff --git a/src/main/java/org/apache/commons/math3/ode/nonstiff/HighamHall54StepInterpolator.java b/src/main/java/org/apache/commons/math3/ode/nonstiff/HighamHall54StepInterpolator.java
new file mode 100644
index 0000000..682ec7c
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/ode/nonstiff/HighamHall54StepInterpolator.java
@@ -0,0 +1,122 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math3.ode.nonstiff;
+
+import org.apache.commons.math3.ode.sampling.StepInterpolator;
+
+/**
+ * This class represents an interpolator over the last step during an
+ * ODE integration for the 5(4) Higham and Hall integrator.
+ *
+ * @see HighamHall54Integrator
+ *
+ * @since 1.2
+ */
+
+class HighamHall54StepInterpolator
+ extends RungeKuttaStepInterpolator {
+
+ /** Serializable version identifier */
+ private static final long serialVersionUID = 20111120L;
+
+ /** Simple constructor.
+ * This constructor builds an instance that is not usable yet, the
+ * {@link
+ * org.apache.commons.math3.ode.sampling.AbstractStepInterpolator#reinitialize}
+ * method should be called before using the instance in order to
+ * initialize the internal arrays. This constructor is used only
+ * in order to delay the initialization in some cases. The {@link
+ * EmbeddedRungeKuttaIntegrator} uses the prototyping design pattern
+ * to create the step interpolators by cloning an uninitialized model
+ * and later initializing the copy.
+ */
+ // CHECKSTYLE: stop RedundantModifier
+ // the public modifier here is needed for serialization
+ public HighamHall54StepInterpolator() {
+ super();
+ }
+ // CHECKSTYLE: resume RedundantModifier
+
+ /** Copy constructor.
+ * @param interpolator interpolator to copy from. The copy is a deep
+ * copy: its arrays are separated from the original arrays of the
+ * instance
+ */
+ HighamHall54StepInterpolator(final HighamHall54StepInterpolator interpolator) {
+ super(interpolator);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ protected StepInterpolator doCopy() {
+ return new HighamHall54StepInterpolator(this);
+ }
+
+
+ /** {@inheritDoc} */
+ @Override
+ protected void computeInterpolatedStateAndDerivatives(final double theta,
+ final double oneMinusThetaH) {
+
+ final double bDot0 = 1 + theta * (-15.0/2.0 + theta * (16.0 - 10.0 * theta));
+ final double bDot2 = theta * (459.0/16.0 + theta * (-729.0/8.0 + 135.0/2.0 * theta));
+ final double bDot3 = theta * (-44.0 + theta * (152.0 - 120.0 * theta));
+ final double bDot4 = theta * (375.0/16.0 + theta * (-625.0/8.0 + 125.0/2.0 * theta));
+ final double bDot5 = theta * 5.0/8.0 * (2 * theta - 1);
+
+ if ((previousState != null) && (theta <= 0.5)) {
+ final double hTheta = h * theta;
+ final double b0 = hTheta * (1.0 + theta * (-15.0/4.0 + theta * (16.0/3.0 - 5.0/2.0 * theta)));
+ final double b2 = hTheta * ( theta * (459.0/32.0 + theta * (-243.0/8.0 + theta * 135.0/8.0)));
+ final double b3 = hTheta * ( theta * (-22.0 + theta * (152.0/3.0 + theta * -30.0)));
+ final double b4 = hTheta * ( theta * (375.0/32.0 + theta * (-625.0/24.0 + theta * 125.0/8.0)));
+ final double b5 = hTheta * ( theta * (-5.0/16.0 + theta * 5.0/12.0));
+ for (int i = 0; i < interpolatedState.length; ++i) {
+ final double yDot0 = yDotK[0][i];
+ final double yDot2 = yDotK[2][i];
+ final double yDot3 = yDotK[3][i];
+ final double yDot4 = yDotK[4][i];
+ final double yDot5 = yDotK[5][i];
+ interpolatedState[i] =
+ previousState[i] + b0 * yDot0 + b2 * yDot2 + b3 * yDot3 + b4 * yDot4 + b5 * yDot5;
+ interpolatedDerivatives[i] =
+ bDot0 * yDot0 + bDot2 * yDot2 + bDot3 * yDot3 + bDot4 * yDot4 + bDot5 * yDot5;
+ }
+ } else {
+ final double theta2 = theta * theta;
+ final double b0 = h * (-1.0/12.0 + theta * (1.0 + theta * (-15.0/4.0 + theta * (16.0/3.0 + theta * -5.0/2.0))));
+ final double b2 = h * (-27.0/32.0 + theta2 * (459.0/32.0 + theta * (-243.0/8.0 + theta * 135.0/8.0)));
+ final double b3 = h * (4.0/3.0 + theta2 * (-22.0 + theta * (152.0/3.0 + theta * -30.0)));
+ final double b4 = h * (-125.0/96.0 + theta2 * (375.0/32.0 + theta * (-625.0/24.0 + theta * 125.0/8.0)));
+ final double b5 = h * (-5.0/48.0 + theta2 * (-5.0/16.0 + theta * 5.0/12.0));
+ for (int i = 0; i < interpolatedState.length; ++i) {
+ final double yDot0 = yDotK[0][i];
+ final double yDot2 = yDotK[2][i];
+ final double yDot3 = yDotK[3][i];
+ final double yDot4 = yDotK[4][i];
+ final double yDot5 = yDotK[5][i];
+ interpolatedState[i] =
+ currentState[i] + b0 * yDot0 + b2 * yDot2 + b3 * yDot3 + b4 * yDot4 + b5 * yDot5;
+ interpolatedDerivatives[i] =
+ bDot0 * yDot0 + bDot2 * yDot2 + bDot3 * yDot3 + bDot4 * yDot4 + bDot5 * yDot5;
+ }
+ }
+
+ }
+
+}
diff --git a/src/main/java/org/apache/commons/math3/ode/nonstiff/LutherFieldIntegrator.java b/src/main/java/org/apache/commons/math3/ode/nonstiff/LutherFieldIntegrator.java
new file mode 100644
index 0000000..7b57b5a
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/ode/nonstiff/LutherFieldIntegrator.java
@@ -0,0 +1,146 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math3.ode.nonstiff;
+
+import org.apache.commons.math3.Field;
+import org.apache.commons.math3.RealFieldElement;
+import org.apache.commons.math3.ode.FieldEquationsMapper;
+import org.apache.commons.math3.ode.FieldODEStateAndDerivative;
+import org.apache.commons.math3.util.MathArrays;
+
+
+/**
+ * This class implements the Luther sixth order Runge-Kutta
+ * integrator for Ordinary Differential Equations.
+
+ * <p>
+ * This method is described in H. A. Luther 1968 paper <a
+ * href="http://www.ams.org/journals/mcom/1968-22-102/S0025-5718-68-99876-1/S0025-5718-68-99876-1.pdf">
+ * An explicit Sixth-Order Runge-Kutta Formula</a>.
+ * </p>
+
+ * <p>This method is an explicit Runge-Kutta method, its Butcher-array
+ * is the following one :
+ * <pre>
+ * 0 | 0 0 0 0 0 0
+ * 1 | 1 0 0 0 0 0
+ * 1/2 | 3/8 1/8 0 0 0 0
+ * 2/3 | 8/27 2/27 8/27 0 0 0
+ * (7-q)/14 | ( -21 + 9q)/392 ( -56 + 8q)/392 ( 336 - 48q)/392 ( -63 + 3q)/392 0 0
+ * (7+q)/14 | (-1155 - 255q)/1960 ( -280 - 40q)/1960 ( 0 - 320q)/1960 ( 63 + 363q)/1960 ( 2352 + 392q)/1960 0
+ * 1 | ( 330 + 105q)/180 ( 120 + 0q)/180 ( -200 + 280q)/180 ( 126 - 189q)/180 ( -686 - 126q)/180 ( 490 - 70q)/180
+ * |--------------------------------------------------------------------------------------------------------------------------------------------------
+ * | 1/20 0 16/45 0 49/180 49/180 1/20
+ * </pre>
+ * where q = &radic;21</p>
+ *
+ * @see EulerFieldIntegrator
+ * @see ClassicalRungeKuttaFieldIntegrator
+ * @see GillFieldIntegrator
+ * @see MidpointFieldIntegrator
+ * @see ThreeEighthesFieldIntegrator
+ * @param <T> the type of the field elements
+ * @since 3.6
+ */
+
+public class LutherFieldIntegrator<T extends RealFieldElement<T>>
+ extends RungeKuttaFieldIntegrator<T> {
+
+ /** Simple constructor.
+ * Build a fourth-order Luther integrator with the given step.
+ * @param field field to which the time and state vector elements belong
+ * @param step integration step
+ */
+ public LutherFieldIntegrator(final Field<T> field, final T step) {
+ super(field, "Luther", step);
+ }
+
+ /** {@inheritDoc} */
+ public T[] getC() {
+ final T q = getField().getZero().add(21).sqrt();
+ final T[] c = MathArrays.buildArray(getField(), 6);
+ c[0] = getField().getOne();
+ c[1] = fraction(1, 2);
+ c[2] = fraction(2, 3);
+ c[3] = q.subtract(7).divide(-14);
+ c[4] = q.add(7).divide(14);
+ c[5] = getField().getOne();
+ return c;
+ }
+
+ /** {@inheritDoc} */
+ public T[][] getA() {
+ final T q = getField().getZero().add(21).sqrt();
+ final T[][] a = MathArrays.buildArray(getField(), 6, -1);
+ for (int i = 0; i < a.length; ++i) {
+ a[i] = MathArrays.buildArray(getField(), i + 1);
+ }
+ a[0][0] = getField().getOne();
+ a[1][0] = fraction(3, 8);
+ a[1][1] = fraction(1, 8);
+ a[2][0] = fraction(8, 27);
+ a[2][1] = fraction(2, 27);
+ a[2][2] = a[2][0];
+ a[3][0] = q.multiply( 9).add( -21).divide( 392);
+ a[3][1] = q.multiply( 8).add( -56).divide( 392);
+ a[3][2] = q.multiply( -48).add( 336).divide( 392);
+ a[3][3] = q.multiply( 3).add( -63).divide( 392);
+ a[4][0] = q.multiply(-255).add(-1155).divide(1960);
+ a[4][1] = q.multiply( -40).add( -280).divide(1960);
+ a[4][2] = q.multiply(-320) .divide(1960);
+ a[4][3] = q.multiply( 363).add( 63).divide(1960);
+ a[4][4] = q.multiply( 392).add( 2352).divide(1960);
+ a[5][0] = q.multiply( 105).add( 330).divide( 180);
+ a[5][1] = fraction(2, 3);
+ a[5][2] = q.multiply( 280).add( -200).divide( 180);
+ a[5][3] = q.multiply(-189).add( 126).divide( 180);
+ a[5][4] = q.multiply(-126).add( -686).divide( 180);
+ a[5][5] = q.multiply( -70).add( 490).divide( 180);
+ return a;
+ }
+
+ /** {@inheritDoc} */
+ public T[] getB() {
+
+ final T[] b = MathArrays.buildArray(getField(), 7);
+ b[0] = fraction( 1, 20);
+ b[1] = getField().getZero();
+ b[2] = fraction(16, 45);
+ b[3] = getField().getZero();
+ b[4] = fraction(49, 180);
+ b[5] = b[4];
+ b[6] = b[0];
+
+ return b;
+
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ protected LutherFieldStepInterpolator<T>
+ createInterpolator(final boolean forward, T[][] yDotK,
+ final FieldODEStateAndDerivative<T> globalPreviousState,
+ final FieldODEStateAndDerivative<T> globalCurrentState,
+ final FieldEquationsMapper<T> mapper) {
+ return new LutherFieldStepInterpolator<T>(getField(), forward, yDotK,
+ globalPreviousState, globalCurrentState,
+ globalPreviousState, globalCurrentState,
+ mapper);
+ }
+
+}
diff --git a/src/main/java/org/apache/commons/math3/ode/nonstiff/LutherFieldStepInterpolator.java b/src/main/java/org/apache/commons/math3/ode/nonstiff/LutherFieldStepInterpolator.java
new file mode 100644
index 0000000..9e38a96
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/ode/nonstiff/LutherFieldStepInterpolator.java
@@ -0,0 +1,224 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math3.ode.nonstiff;
+
+import org.apache.commons.math3.Field;
+import org.apache.commons.math3.RealFieldElement;
+import org.apache.commons.math3.ode.FieldEquationsMapper;
+import org.apache.commons.math3.ode.FieldODEStateAndDerivative;
+
+/**
+ * This class represents an interpolator over the last step during an
+ * ODE integration for the 6th order Luther integrator.
+ *
+ * <p>This interpolator computes dense output inside the last
+ * step computed. The interpolation equation is consistent with the
+ * integration scheme.</p>
+ *
+ * @see LutherFieldIntegrator
+ * @param <T> the type of the field elements
+ * @since 3.6
+ */
+
+class LutherFieldStepInterpolator<T extends RealFieldElement<T>>
+ extends RungeKuttaFieldStepInterpolator<T> {
+
+ /** -49 - 49 q. */
+ private final T c5a;
+
+ /** 392 + 287 q. */
+ private final T c5b;
+
+ /** -637 - 357 q. */
+ private final T c5c;
+
+ /** 833 + 343 q. */
+ private final T c5d;
+
+ /** -49 + 49 q. */
+ private final T c6a;
+
+ /** -392 - 287 q. */
+ private final T c6b;
+
+ /** -637 + 357 q. */
+ private final T c6c;
+
+ /** 833 - 343 q. */
+ private final T c6d;
+
+ /** 49 + 49 q. */
+ private final T d5a;
+
+ /** -1372 - 847 q. */
+ private final T d5b;
+
+ /** 2254 + 1029 q */
+ private final T d5c;
+
+ /** 49 - 49 q. */
+ private final T d6a;
+
+ /** -1372 + 847 q. */
+ private final T d6b;
+
+ /** 2254 - 1029 q */
+ private final T d6c;
+
+ /** Simple constructor.
+ * @param field field to which the time and state vector elements belong
+ * @param forward integration direction indicator
+ * @param yDotK slopes at the intermediate points
+ * @param globalPreviousState start of the global step
+ * @param globalCurrentState end of the global step
+ * @param softPreviousState start of the restricted step
+ * @param softCurrentState end of the restricted step
+ * @param mapper equations mapper for the all equations
+ */
+ LutherFieldStepInterpolator(final Field<T> field, final boolean forward,
+ final T[][] yDotK,
+ final FieldODEStateAndDerivative<T> globalPreviousState,
+ final FieldODEStateAndDerivative<T> globalCurrentState,
+ final FieldODEStateAndDerivative<T> softPreviousState,
+ final FieldODEStateAndDerivative<T> softCurrentState,
+ final FieldEquationsMapper<T> mapper) {
+ super(field, forward, yDotK,
+ globalPreviousState, globalCurrentState, softPreviousState, softCurrentState,
+ mapper);
+ final T q = field.getZero().add(21).sqrt();
+ c5a = q.multiply( -49).add( -49);
+ c5b = q.multiply( 287).add( 392);
+ c5c = q.multiply( -357).add( -637);
+ c5d = q.multiply( 343).add( 833);
+ c6a = q.multiply( 49).add( -49);
+ c6b = q.multiply( -287).add( 392);
+ c6c = q.multiply( 357).add( -637);
+ c6d = q.multiply( -343).add( 833);
+ d5a = q.multiply( 49).add( 49);
+ d5b = q.multiply( -847).add(-1372);
+ d5c = q.multiply( 1029).add( 2254);
+ d6a = q.multiply( -49).add( 49);
+ d6b = q.multiply( 847).add(-1372);
+ d6c = q.multiply(-1029).add( 2254);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ protected LutherFieldStepInterpolator<T> create(final Field<T> newField, final boolean newForward, final T[][] newYDotK,
+ final FieldODEStateAndDerivative<T> newGlobalPreviousState,
+ final FieldODEStateAndDerivative<T> newGlobalCurrentState,
+ final FieldODEStateAndDerivative<T> newSoftPreviousState,
+ final FieldODEStateAndDerivative<T> newSoftCurrentState,
+ final FieldEquationsMapper<T> newMapper) {
+ return new LutherFieldStepInterpolator<T>(newField, newForward, newYDotK,
+ newGlobalPreviousState, newGlobalCurrentState,
+ newSoftPreviousState, newSoftCurrentState,
+ newMapper);
+ }
+
+ /** {@inheritDoc} */
+ @SuppressWarnings("unchecked")
+ @Override
+ protected FieldODEStateAndDerivative<T> computeInterpolatedStateAndDerivatives(final FieldEquationsMapper<T> mapper,
+ final T time, final T theta,
+ final T thetaH, final T oneMinusThetaH) {
+
+ // the coefficients below have been computed by solving the
+ // order conditions from a theorem from Butcher (1963), using
+ // the method explained in Folkmar Bornemann paper "Runge-Kutta
+ // Methods, Trees, and Maple", Center of Mathematical Sciences, Munich
+ // University of Technology, February 9, 2001
+ //<http://wwwzenger.informatik.tu-muenchen.de/selcuk/sjam012101.html>
+
+ // the method is implemented in the rkcheck tool
+ // <https://www.spaceroots.org/software/rkcheck/index.html>.
+ // Running it for order 5 gives the following order conditions
+ // for an interpolator:
+ // order 1 conditions
+ // \sum_{i=1}^{i=s}\left(b_{i} \right) =1
+ // order 2 conditions
+ // \sum_{i=1}^{i=s}\left(b_{i} c_{i}\right) = \frac{\theta}{2}
+ // order 3 conditions
+ // \sum_{i=2}^{i=s}\left(b_{i} \sum_{j=1}^{j=i-1}{\left(a_{i,j} c_{j} \right)}\right) = \frac{\theta^{2}}{6}
+ // \sum_{i=1}^{i=s}\left(b_{i} c_{i}^{2}\right) = \frac{\theta^{2}}{3}
+ // order 4 conditions
+ // \sum_{i=3}^{i=s}\left(b_{i} \sum_{j=2}^{j=i-1}{\left(a_{i,j} \sum_{k=1}^{k=j-1}{\left(a_{j,k} c_{k} \right)} \right)}\right) = \frac{\theta^{3}}{24}
+ // \sum_{i=2}^{i=s}\left(b_{i} \sum_{j=1}^{j=i-1}{\left(a_{i,j} c_{j}^{2} \right)}\right) = \frac{\theta^{3}}{12}
+ // \sum_{i=2}^{i=s}\left(b_{i} c_{i}\sum_{j=1}^{j=i-1}{\left(a_{i,j} c_{j} \right)}\right) = \frac{\theta^{3}}{8}
+ // \sum_{i=1}^{i=s}\left(b_{i} c_{i}^{3}\right) = \frac{\theta^{3}}{4}
+ // order 5 conditions
+ // \sum_{i=4}^{i=s}\left(b_{i} \sum_{j=3}^{j=i-1}{\left(a_{i,j} \sum_{k=2}^{k=j-1}{\left(a_{j,k} \sum_{l=1}^{l=k-1}{\left(a_{k,l} c_{l} \right)} \right)} \right)}\right) = \frac{\theta^{4}}{120}
+ // \sum_{i=3}^{i=s}\left(b_{i} \sum_{j=2}^{j=i-1}{\left(a_{i,j} \sum_{k=1}^{k=j-1}{\left(a_{j,k} c_{k}^{2} \right)} \right)}\right) = \frac{\theta^{4}}{60}
+ // \sum_{i=3}^{i=s}\left(b_{i} \sum_{j=2}^{j=i-1}{\left(a_{i,j} c_{j}\sum_{k=1}^{k=j-1}{\left(a_{j,k} c_{k} \right)} \right)}\right) = \frac{\theta^{4}}{40}
+ // \sum_{i=2}^{i=s}\left(b_{i} \sum_{j=1}^{j=i-1}{\left(a_{i,j} c_{j}^{3} \right)}\right) = \frac{\theta^{4}}{20}
+ // \sum_{i=3}^{i=s}\left(b_{i} c_{i}\sum_{j=2}^{j=i-1}{\left(a_{i,j} \sum_{k=1}^{k=j-1}{\left(a_{j,k} c_{k} \right)} \right)}\right) = \frac{\theta^{4}}{30}
+ // \sum_{i=2}^{i=s}\left(b_{i} c_{i}\sum_{j=1}^{j=i-1}{\left(a_{i,j} c_{j}^{2} \right)}\right) = \frac{\theta^{4}}{15}
+ // \sum_{i=2}^{i=s}\left(b_{i} \left(\sum_{j=1}^{j=i-1}{\left(a_{i,j} c_{j} \right)} \right)^{2}\right) = \frac{\theta^{4}}{20}
+ // \sum_{i=2}^{i=s}\left(b_{i} c_{i}^{2}\sum_{j=1}^{j=i-1}{\left(a_{i,j} c_{j} \right)}\right) = \frac{\theta^{4}}{10}
+ // \sum_{i=1}^{i=s}\left(b_{i} c_{i}^{4}\right) = \frac{\theta^{4}}{5}
+
+ // The a_{j,k} and c_{k} are given by the integrator Butcher arrays. What remains to solve
+ // are the b_i for the interpolator. They are found by solving the above equations.
+ // For a given interpolator, some equations are redundant, so in our case when we select
+ // all equations from order 1 to 4, we still don't have enough independent equations
+ // to solve from b_1 to b_7. We need to also select one equation from order 5. Here,
+ // we selected the last equation. It appears this choice implied at least the last 3 equations
+ // are fulfilled, but some of the former ones are not, so the resulting interpolator is order 5.
+ // At the end, we get the b_i as polynomials in theta.
+
+ final T coeffDot1 = theta.multiply(theta.multiply(theta.multiply(theta.multiply( 21 ).add( -47 )).add( 36 )).add( -54 / 5.0)).add(1);
+ final T coeffDot2 = time.getField().getZero();
+ final T coeffDot3 = theta.multiply(theta.multiply(theta.multiply(theta.multiply( 112 ).add(-608 / 3.0)).add( 320 / 3.0 )).add(-208 / 15.0));
+ final T coeffDot4 = theta.multiply(theta.multiply(theta.multiply(theta.multiply( -567 / 5.0).add( 972 / 5.0)).add( -486 / 5.0 )).add( 324 / 25.0));
+ final T coeffDot5 = theta.multiply(theta.multiply(theta.multiply(theta.multiply(c5a.divide(5)).add(c5b.divide(15))).add(c5c.divide(30))).add(c5d.divide(150)));
+ final T coeffDot6 = theta.multiply(theta.multiply(theta.multiply(theta.multiply(c6a.divide(5)).add(c6b.divide(15))).add(c6c.divide(30))).add(c6d.divide(150)));
+ final T coeffDot7 = theta.multiply(theta.multiply(theta.multiply( 3.0 ).add( -3 )).add( 3 / 5.0));
+ final T[] interpolatedState;
+ final T[] interpolatedDerivatives;
+
+ if (getGlobalPreviousState() != null && theta.getReal() <= 0.5) {
+
+ final T s = thetaH;
+ final T coeff1 = s.multiply(theta.multiply(theta.multiply(theta.multiply(theta.multiply( 21 / 5.0).add( -47 / 4.0)).add( 12 )).add( -27 / 5.0)).add(1));
+ final T coeff2 = time.getField().getZero();
+ final T coeff3 = s.multiply(theta.multiply(theta.multiply(theta.multiply(theta.multiply( 112 / 5.0).add(-152 / 3.0)).add( 320 / 9.0 )).add(-104 / 15.0)));
+ final T coeff4 = s.multiply(theta.multiply(theta.multiply(theta.multiply(theta.multiply(-567 / 25.0).add( 243 / 5.0)).add( -162 / 5.0 )).add( 162 / 25.0)));
+ final T coeff5 = s.multiply(theta.multiply(theta.multiply(theta.multiply(theta.multiply(c5a.divide(25)).add(c5b.divide(60))).add(c5c.divide(90))).add(c5d.divide(300))));
+ final T coeff6 = s.multiply(theta.multiply(theta.multiply(theta.multiply(theta.multiply(c6a.divide(25)).add(c6b.divide(60))).add(c6c.divide(90))).add(c6d.divide(300))));
+ final T coeff7 = s.multiply(theta.multiply(theta.multiply(theta.multiply( 3 / 4.0 ).add( -1 )).add( 3 / 10.0)));
+ interpolatedState = previousStateLinearCombination(coeff1, coeff2, coeff3, coeff4, coeff5, coeff6, coeff7);
+ interpolatedDerivatives = derivativeLinearCombination(coeffDot1, coeffDot2, coeffDot3, coeffDot4, coeffDot5, coeffDot6, coeffDot7);
+ } else {
+
+ final T s = oneMinusThetaH;
+ final T coeff1 = s.multiply(theta.multiply(theta.multiply(theta.multiply(theta.multiply( -21 / 5.0).add( 151 / 20.0)).add( -89 / 20.0)).add( 19 / 20.0)).add(- 1 / 20.0));
+ final T coeff2 = time.getField().getZero();
+ final T coeff3 = s.multiply(theta.multiply(theta.multiply(theta.multiply(theta.multiply(-112 / 5.0).add( 424 / 15.0)).add( -328 / 45.0)).add( -16 / 45.0)).add(-16 / 45.0));
+ final T coeff4 = s.multiply(theta.multiply(theta.multiply(theta.multiply(theta.multiply( 567 / 25.0).add( -648 / 25.0)).add( 162 / 25.0))));
+ final T coeff5 = s.multiply(theta.multiply(theta.multiply(theta.multiply(theta.multiply(d5a.divide(25)).add(d5b.divide(300))).add(d5c.divide(900))).add( -49 / 180.0)).add(-49 / 180.0));
+ final T coeff6 = s.multiply(theta.multiply(theta.multiply(theta.multiply(theta.multiply(d6a.divide(25)).add(d6b.divide(300))).add(d6c.divide(900))).add( -49 / 180.0)).add(-49 / 180.0));
+ final T coeff7 = s.multiply( theta.multiply(theta.multiply(theta.multiply( -3 / 4.0 ).add( 1 / 4.0)).add( -1 / 20.0)).add( -1 / 20.0));
+ interpolatedState = currentStateLinearCombination(coeff1, coeff2, coeff3, coeff4, coeff5, coeff6, coeff7);
+ interpolatedDerivatives = derivativeLinearCombination(coeffDot1, coeffDot2, coeffDot3, coeffDot4, coeffDot5, coeffDot6, coeffDot7);
+ }
+
+ return new FieldODEStateAndDerivative<T>(time, interpolatedState, interpolatedDerivatives);
+
+ }
+
+}
diff --git a/src/main/java/org/apache/commons/math3/ode/nonstiff/LutherIntegrator.java b/src/main/java/org/apache/commons/math3/ode/nonstiff/LutherIntegrator.java
new file mode 100644
index 0000000..d3d0d6a
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/ode/nonstiff/LutherIntegrator.java
@@ -0,0 +1,89 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math3.ode.nonstiff;
+
+import org.apache.commons.math3.util.FastMath;
+
+
+/**
+ * This class implements the Luther sixth order Runge-Kutta
+ * integrator for Ordinary Differential Equations.
+
+ * <p>
+ * This method is described in H. A. Luther 1968 paper <a
+ * href="http://www.ams.org/journals/mcom/1968-22-102/S0025-5718-68-99876-1/S0025-5718-68-99876-1.pdf">
+ * An explicit Sixth-Order Runge-Kutta Formula</a>.
+ * </p>
+
+ * <p>This method is an explicit Runge-Kutta method, its Butcher-array
+ * is the following one :
+ * <pre>
+ * 0 | 0 0 0 0 0 0
+ * 1 | 1 0 0 0 0 0
+ * 1/2 | 3/8 1/8 0 0 0 0
+ * 2/3 | 8/27 2/27 8/27 0 0 0
+ * (7-q)/14 | ( -21 + 9q)/392 ( -56 + 8q)/392 ( 336 - 48q)/392 ( -63 + 3q)/392 0 0
+ * (7+q)/14 | (-1155 - 255q)/1960 ( -280 - 40q)/1960 ( 0 - 320q)/1960 ( 63 + 363q)/1960 ( 2352 + 392q)/1960 0
+ * 1 | ( 330 + 105q)/180 ( 120 + 0q)/180 ( -200 + 280q)/180 ( 126 - 189q)/180 ( -686 - 126q)/180 ( 490 - 70q)/180
+ * |--------------------------------------------------------------------------------------------------------------------------------------------------
+ * | 1/20 0 16/45 0 49/180 49/180 1/20
+ * </pre>
+ * where q = &radic;21</p>
+ *
+ * @see EulerIntegrator
+ * @see ClassicalRungeKuttaIntegrator
+ * @see GillIntegrator
+ * @see MidpointIntegrator
+ * @see ThreeEighthesIntegrator
+ * @since 3.3
+ */
+
+public class LutherIntegrator extends RungeKuttaIntegrator {
+
+ /** Square root. */
+ private static final double Q = FastMath.sqrt(21);
+
+ /** Time steps Butcher array. */
+ private static final double[] STATIC_C = {
+ 1.0, 1.0 / 2.0, 2.0 / 3.0, (7.0 - Q) / 14.0, (7.0 + Q) / 14.0, 1.0
+ };
+
+ /** Internal weights Butcher array. */
+ private static final double[][] STATIC_A = {
+ { 1.0 },
+ { 3.0 / 8.0, 1.0 / 8.0 },
+ { 8.0 / 27.0, 2.0 / 27.0, 8.0 / 27.0 },
+ { ( -21.0 + 9.0 * Q) / 392.0, ( -56.0 + 8.0 * Q) / 392.0, ( 336.0 - 48.0 * Q) / 392.0, (-63.0 + 3.0 * Q) / 392.0 },
+ { (-1155.0 - 255.0 * Q) / 1960.0, (-280.0 - 40.0 * Q) / 1960.0, ( 0.0 - 320.0 * Q) / 1960.0, ( 63.0 + 363.0 * Q) / 1960.0, (2352.0 + 392.0 * Q) / 1960.0 },
+ { ( 330.0 + 105.0 * Q) / 180.0, ( 120.0 + 0.0 * Q) / 180.0, (-200.0 + 280.0 * Q) / 180.0, (126.0 - 189.0 * Q) / 180.0, (-686.0 - 126.0 * Q) / 180.0, (490.0 - 70.0 * Q) / 180.0 }
+ };
+
+ /** Propagation weights Butcher array. */
+ private static final double[] STATIC_B = {
+ 1.0 / 20.0, 0, 16.0 / 45.0, 0, 49.0 / 180.0, 49.0 / 180.0, 1.0 / 20.0
+ };
+
+ /** Simple constructor.
+ * Build a fourth-order Luther integrator with the given step.
+ * @param step integration step
+ */
+ public LutherIntegrator(final double step) {
+ super("Luther", STATIC_C, STATIC_A, STATIC_B, new LutherStepInterpolator(), step);
+ }
+
+}
diff --git a/src/main/java/org/apache/commons/math3/ode/nonstiff/LutherStepInterpolator.java b/src/main/java/org/apache/commons/math3/ode/nonstiff/LutherStepInterpolator.java
new file mode 100644
index 0000000..207a9ea
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/ode/nonstiff/LutherStepInterpolator.java
@@ -0,0 +1,182 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math3.ode.nonstiff;
+
+import org.apache.commons.math3.ode.sampling.StepInterpolator;
+import org.apache.commons.math3.util.FastMath;
+
+/**
+ * This class represents an interpolator over the last step during an
+ * ODE integration for the 6th order Luther integrator.
+ *
+ * <p>This interpolator computes dense output inside the last
+ * step computed. The interpolation equation is consistent with the
+ * integration scheme.</p>
+ *
+ * @see LutherIntegrator
+ * @since 3.3
+ */
+
+class LutherStepInterpolator extends RungeKuttaStepInterpolator {
+
+ /** Serializable version identifier */
+ private static final long serialVersionUID = 20140416L;
+
+ /** Square root. */
+ private static final double Q = FastMath.sqrt(21);
+
+ /** Simple constructor.
+ * This constructor builds an instance that is not usable yet, the
+ * {@link
+ * org.apache.commons.math3.ode.sampling.AbstractStepInterpolator#reinitialize}
+ * method should be called before using the instance in order to
+ * initialize the internal arrays. This constructor is used only
+ * in order to delay the initialization in some cases. The {@link
+ * RungeKuttaIntegrator} class uses the prototyping design pattern
+ * to create the step interpolators by cloning an uninitialized model
+ * and later initializing the copy.
+ */
+ // CHECKSTYLE: stop RedundantModifier
+ // the public modifier here is needed for serialization
+ public LutherStepInterpolator() {
+ }
+ // CHECKSTYLE: resume RedundantModifier
+
+ /** Copy constructor.
+ * @param interpolator interpolator to copy from. The copy is a deep
+ * copy: its arrays are separated from the original arrays of the
+ * instance
+ */
+ LutherStepInterpolator(final LutherStepInterpolator interpolator) {
+ super(interpolator);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ protected StepInterpolator doCopy() {
+ return new LutherStepInterpolator(this);
+ }
+
+
+ /** {@inheritDoc} */
+ @Override
+ protected void computeInterpolatedStateAndDerivatives(final double theta,
+ final double oneMinusThetaH) {
+
+ // the coefficients below have been computed by solving the
+ // order conditions from a theorem from Butcher (1963), using
+ // the method explained in Folkmar Bornemann paper "Runge-Kutta
+ // Methods, Trees, and Maple", Center of Mathematical Sciences, Munich
+ // University of Technology, February 9, 2001
+ //<http://wwwzenger.informatik.tu-muenchen.de/selcuk/sjam012101.html>
+
+ // the method is implemented in the rkcheck tool
+ // <https://www.spaceroots.org/software/rkcheck/index.html>.
+ // Running it for order 5 gives the following order conditions
+ // for an interpolator:
+ // order 1 conditions
+ // \sum_{i=1}^{i=s}\left(b_{i} \right) =1
+ // order 2 conditions
+ // \sum_{i=1}^{i=s}\left(b_{i} c_{i}\right) = \frac{\theta}{2}
+ // order 3 conditions
+ // \sum_{i=2}^{i=s}\left(b_{i} \sum_{j=1}^{j=i-1}{\left(a_{i,j} c_{j} \right)}\right) = \frac{\theta^{2}}{6}
+ // \sum_{i=1}^{i=s}\left(b_{i} c_{i}^{2}\right) = \frac{\theta^{2}}{3}
+ // order 4 conditions
+ // \sum_{i=3}^{i=s}\left(b_{i} \sum_{j=2}^{j=i-1}{\left(a_{i,j} \sum_{k=1}^{k=j-1}{\left(a_{j,k} c_{k} \right)} \right)}\right) = \frac{\theta^{3}}{24}
+ // \sum_{i=2}^{i=s}\left(b_{i} \sum_{j=1}^{j=i-1}{\left(a_{i,j} c_{j}^{2} \right)}\right) = \frac{\theta^{3}}{12}
+ // \sum_{i=2}^{i=s}\left(b_{i} c_{i}\sum_{j=1}^{j=i-1}{\left(a_{i,j} c_{j} \right)}\right) = \frac{\theta^{3}}{8}
+ // \sum_{i=1}^{i=s}\left(b_{i} c_{i}^{3}\right) = \frac{\theta^{3}}{4}
+ // order 5 conditions
+ // \sum_{i=4}^{i=s}\left(b_{i} \sum_{j=3}^{j=i-1}{\left(a_{i,j} \sum_{k=2}^{k=j-1}{\left(a_{j,k} \sum_{l=1}^{l=k-1}{\left(a_{k,l} c_{l} \right)} \right)} \right)}\right) = \frac{\theta^{4}}{120}
+ // \sum_{i=3}^{i=s}\left(b_{i} \sum_{j=2}^{j=i-1}{\left(a_{i,j} \sum_{k=1}^{k=j-1}{\left(a_{j,k} c_{k}^{2} \right)} \right)}\right) = \frac{\theta^{4}}{60}
+ // \sum_{i=3}^{i=s}\left(b_{i} \sum_{j=2}^{j=i-1}{\left(a_{i,j} c_{j}\sum_{k=1}^{k=j-1}{\left(a_{j,k} c_{k} \right)} \right)}\right) = \frac{\theta^{4}}{40}
+ // \sum_{i=2}^{i=s}\left(b_{i} \sum_{j=1}^{j=i-1}{\left(a_{i,j} c_{j}^{3} \right)}\right) = \frac{\theta^{4}}{20}
+ // \sum_{i=3}^{i=s}\left(b_{i} c_{i}\sum_{j=2}^{j=i-1}{\left(a_{i,j} \sum_{k=1}^{k=j-1}{\left(a_{j,k} c_{k} \right)} \right)}\right) = \frac{\theta^{4}}{30}
+ // \sum_{i=2}^{i=s}\left(b_{i} c_{i}\sum_{j=1}^{j=i-1}{\left(a_{i,j} c_{j}^{2} \right)}\right) = \frac{\theta^{4}}{15}
+ // \sum_{i=2}^{i=s}\left(b_{i} \left(\sum_{j=1}^{j=i-1}{\left(a_{i,j} c_{j} \right)} \right)^{2}\right) = \frac{\theta^{4}}{20}
+ // \sum_{i=2}^{i=s}\left(b_{i} c_{i}^{2}\sum_{j=1}^{j=i-1}{\left(a_{i,j} c_{j} \right)}\right) = \frac{\theta^{4}}{10}
+ // \sum_{i=1}^{i=s}\left(b_{i} c_{i}^{4}\right) = \frac{\theta^{4}}{5}
+
+ // The a_{j,k} and c_{k} are given by the integrator Butcher arrays. What remains to solve
+ // are the b_i for the interpolator. They are found by solving the above equations.
+ // For a given interpolator, some equations are redundant, so in our case when we select
+ // all equations from order 1 to 4, we still don't have enough independent equations
+ // to solve from b_1 to b_7. We need to also select one equation from order 5. Here,
+ // we selected the last equation. It appears this choice implied at least the last 3 equations
+ // are fulfilled, but some of the former ones are not, so the resulting interpolator is order 5.
+ // At the end, we get the b_i as polynomials in theta.
+
+ final double coeffDot1 = 1 + theta * ( -54 / 5.0 + theta * ( 36 + theta * ( -47 + theta * 21)));
+ final double coeffDot2 = 0;
+ final double coeffDot3 = theta * (-208 / 15.0 + theta * ( 320 / 3.0 + theta * (-608 / 3.0 + theta * 112)));
+ final double coeffDot4 = theta * ( 324 / 25.0 + theta * ( -486 / 5.0 + theta * ( 972 / 5.0 + theta * -567 / 5.0)));
+ final double coeffDot5 = theta * ((833 + 343 * Q) / 150.0 + theta * ((-637 - 357 * Q) / 30.0 + theta * ((392 + 287 * Q) / 15.0 + theta * (-49 - 49 * Q) / 5.0)));
+ final double coeffDot6 = theta * ((833 - 343 * Q) / 150.0 + theta * ((-637 + 357 * Q) / 30.0 + theta * ((392 - 287 * Q) / 15.0 + theta * (-49 + 49 * Q) / 5.0)));
+ final double coeffDot7 = theta * ( 3 / 5.0 + theta * ( -3 + theta * 3));
+
+ if ((previousState != null) && (theta <= 0.5)) {
+
+ final double coeff1 = 1 + theta * ( -27 / 5.0 + theta * ( 12 + theta * ( -47 / 4.0 + theta * 21 / 5.0)));
+ final double coeff2 = 0;
+ final double coeff3 = theta * (-104 / 15.0 + theta * ( 320 / 9.0 + theta * (-152 / 3.0 + theta * 112 / 5.0)));
+ final double coeff4 = theta * ( 162 / 25.0 + theta * ( -162 / 5.0 + theta * ( 243 / 5.0 + theta * -567 / 25.0)));
+ final double coeff5 = theta * ((833 + 343 * Q) / 300.0 + theta * ((-637 - 357 * Q) / 90.0 + theta * ((392 + 287 * Q) / 60.0 + theta * (-49 - 49 * Q) / 25.0)));
+ final double coeff6 = theta * ((833 - 343 * Q) / 300.0 + theta * ((-637 + 357 * Q) / 90.0 + theta * ((392 - 287 * Q) / 60.0 + theta * (-49 + 49 * Q) / 25.0)));
+ final double coeff7 = theta * ( 3 / 10.0 + theta * ( -1 + theta * ( 3 / 4.0)));
+ for (int i = 0; i < interpolatedState.length; ++i) {
+ final double yDot1 = yDotK[0][i];
+ final double yDot2 = yDotK[1][i];
+ final double yDot3 = yDotK[2][i];
+ final double yDot4 = yDotK[3][i];
+ final double yDot5 = yDotK[4][i];
+ final double yDot6 = yDotK[5][i];
+ final double yDot7 = yDotK[6][i];
+ interpolatedState[i] = previousState[i] +
+ theta * h * (coeff1 * yDot1 + coeff2 * yDot2 + coeff3 * yDot3 +
+ coeff4 * yDot4 + coeff5 * yDot5 + coeff6 * yDot6 + coeff7 * yDot7);
+ interpolatedDerivatives[i] = coeffDot1 * yDot1 + coeffDot2 * yDot2 + coeffDot3 * yDot3 +
+ coeffDot4 * yDot4 + coeffDot5 * yDot5 + coeffDot6 * yDot6 + coeffDot7 * yDot7;
+ }
+ } else {
+
+ final double coeff1 = -1 / 20.0 + theta * ( 19 / 20.0 + theta * ( -89 / 20.0 + theta * ( 151 / 20.0 + theta * -21 / 5.0)));
+ final double coeff2 = 0;
+ final double coeff3 = -16 / 45.0 + theta * ( -16 / 45.0 + theta * ( -328 / 45.0 + theta * ( 424 / 15.0 + theta * -112 / 5.0)));
+ final double coeff4 = theta * ( theta * ( 162 / 25.0 + theta * ( -648 / 25.0 + theta * 567 / 25.0)));
+ final double coeff5 = -49 / 180.0 + theta * ( -49 / 180.0 + theta * ((2254 + 1029 * Q) / 900.0 + theta * ((-1372 - 847 * Q) / 300.0 + theta * ( 49 + 49 * Q) / 25.0)));
+ final double coeff6 = -49 / 180.0 + theta * ( -49 / 180.0 + theta * ((2254 - 1029 * Q) / 900.0 + theta * ((-1372 + 847 * Q) / 300.0 + theta * ( 49 - 49 * Q) / 25.0)));
+ final double coeff7 = -1 / 20.0 + theta * ( -1 / 20.0 + theta * ( 1 / 4.0 + theta * ( -3 / 4.0)));
+ for (int i = 0; i < interpolatedState.length; ++i) {
+ final double yDot1 = yDotK[0][i];
+ final double yDot2 = yDotK[1][i];
+ final double yDot3 = yDotK[2][i];
+ final double yDot4 = yDotK[3][i];
+ final double yDot5 = yDotK[4][i];
+ final double yDot6 = yDotK[5][i];
+ final double yDot7 = yDotK[6][i];
+ interpolatedState[i] = currentState[i] +
+ oneMinusThetaH * (coeff1 * yDot1 + coeff2 * yDot2 + coeff3 * yDot3 +
+ coeff4 * yDot4 + coeff5 * yDot5 + coeff6 * yDot6 + coeff7 * yDot7);
+ interpolatedDerivatives[i] = coeffDot1 * yDot1 + coeffDot2 * yDot2 + coeffDot3 * yDot3 +
+ coeffDot4 * yDot4 + coeffDot5 * yDot5 + coeffDot6 * yDot6 + coeffDot7 * yDot7;
+ }
+ }
+
+ }
+
+}
diff --git a/src/main/java/org/apache/commons/math3/ode/nonstiff/MidpointFieldIntegrator.java b/src/main/java/org/apache/commons/math3/ode/nonstiff/MidpointFieldIntegrator.java
new file mode 100644
index 0000000..e0dd8cf
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/ode/nonstiff/MidpointFieldIntegrator.java
@@ -0,0 +1,96 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math3.ode.nonstiff;
+
+import org.apache.commons.math3.Field;
+import org.apache.commons.math3.RealFieldElement;
+import org.apache.commons.math3.ode.FieldEquationsMapper;
+import org.apache.commons.math3.ode.FieldODEStateAndDerivative;
+import org.apache.commons.math3.util.MathArrays;
+
+/**
+ * This class implements a second order Runge-Kutta integrator for
+ * Ordinary Differential Equations.
+ *
+ * <p>This method is an explicit Runge-Kutta method, its Butcher-array
+ * is the following one :
+ * <pre>
+ * 0 | 0 0
+ * 1/2 | 1/2 0
+ * |----------
+ * | 0 1
+ * </pre>
+ * </p>
+ *
+ * @see EulerFieldIntegrator
+ * @see ClassicalRungeKuttaFieldIntegrator
+ * @see GillFieldIntegrator
+ * @see ThreeEighthesFieldIntegrator
+ * @see LutherFieldIntegrator
+ *
+ * @param <T> the type of the field elements
+ * @since 3.6
+ */
+
+public class MidpointFieldIntegrator<T extends RealFieldElement<T>> extends RungeKuttaFieldIntegrator<T> {
+
+ /** Simple constructor.
+ * Build a midpoint integrator with the given step.
+ * @param field field to which the time and state vector elements belong
+ * @param step integration step
+ */
+ public MidpointFieldIntegrator(final Field<T> field, final T step) {
+ super(field, "midpoint", step);
+ }
+
+ /** {@inheritDoc} */
+ public T[] getC() {
+ final T[] c = MathArrays.buildArray(getField(), 1);
+ c[0] = getField().getOne().multiply(0.5);
+ return c;
+ }
+
+ /** {@inheritDoc} */
+ public T[][] getA() {
+ final T[][] a = MathArrays.buildArray(getField(), 1, 1);
+ a[0][0] = fraction(1, 2);
+ return a;
+ }
+
+ /** {@inheritDoc} */
+ public T[] getB() {
+ final T[] b = MathArrays.buildArray(getField(), 2);
+ b[0] = getField().getZero();
+ b[1] = getField().getOne();
+ return b;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ protected MidpointFieldStepInterpolator<T>
+ createInterpolator(final boolean forward, T[][] yDotK,
+ final FieldODEStateAndDerivative<T> globalPreviousState,
+ final FieldODEStateAndDerivative<T> globalCurrentState,
+ final FieldEquationsMapper<T> mapper) {
+ return new MidpointFieldStepInterpolator<T>(getField(), forward, yDotK,
+ globalPreviousState, globalCurrentState,
+ globalPreviousState, globalCurrentState,
+ mapper);
+ }
+
+}
diff --git a/src/main/java/org/apache/commons/math3/ode/nonstiff/MidpointFieldStepInterpolator.java b/src/main/java/org/apache/commons/math3/ode/nonstiff/MidpointFieldStepInterpolator.java
new file mode 100644
index 0000000..911e3b2
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/ode/nonstiff/MidpointFieldStepInterpolator.java
@@ -0,0 +1,118 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math3.ode.nonstiff;
+
+import org.apache.commons.math3.Field;
+import org.apache.commons.math3.RealFieldElement;
+import org.apache.commons.math3.ode.FieldEquationsMapper;
+import org.apache.commons.math3.ode.FieldODEStateAndDerivative;
+
+/**
+ * This class implements a step interpolator for second order
+ * Runge-Kutta integrator.
+ *
+ * <p>This interpolator computes dense output inside the last
+ * step computed. The interpolation equation is consistent with the
+ * integration scheme :
+ * <ul>
+ * <li>Using reference point at step start:<br>
+ * y(t<sub>n</sub> + &theta; h) = y (t<sub>n</sub>) + &theta; h [(1 - &theta;) y'<sub>1</sub> + &theta; y'<sub>2</sub>]
+ * </li>
+ * <li>Using reference point at step end:<br>
+ * y(t<sub>n</sub> + &theta; h) = y (t<sub>n</sub> + h) + (1-&theta;) h [&theta; y'<sub>1</sub> - (1+&theta;) y'<sub>2</sub>]
+ * </li>
+ * </ul>
+ * </p>
+ *
+ * where &theta; belongs to [0 ; 1] and where y'<sub>1</sub> and y'<sub>2</sub> are the two
+ * evaluations of the derivatives already computed during the
+ * step.</p>
+ *
+ * @see MidpointFieldIntegrator
+ * @param <T> the type of the field elements
+ * @since 3.6
+ */
+
+class MidpointFieldStepInterpolator<T extends RealFieldElement<T>>
+ extends RungeKuttaFieldStepInterpolator<T> {
+
+ /** Simple constructor.
+ * @param field field to which the time and state vector elements belong
+ * @param forward integration direction indicator
+ * @param yDotK slopes at the intermediate points
+ * @param globalPreviousState start of the global step
+ * @param globalCurrentState end of the global step
+ * @param softPreviousState start of the restricted step
+ * @param softCurrentState end of the restricted step
+ * @param mapper equations mapper for the all equations
+ */
+ MidpointFieldStepInterpolator(final Field<T> field, final boolean forward,
+ final T[][] yDotK,
+ final FieldODEStateAndDerivative<T> globalPreviousState,
+ final FieldODEStateAndDerivative<T> globalCurrentState,
+ final FieldODEStateAndDerivative<T> softPreviousState,
+ final FieldODEStateAndDerivative<T> softCurrentState,
+ final FieldEquationsMapper<T> mapper) {
+ super(field, forward, yDotK,
+ globalPreviousState, globalCurrentState, softPreviousState, softCurrentState,
+ mapper);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ protected MidpointFieldStepInterpolator<T> create(final Field<T> newField, final boolean newForward, final T[][] newYDotK,
+ final FieldODEStateAndDerivative<T> newGlobalPreviousState,
+ final FieldODEStateAndDerivative<T> newGlobalCurrentState,
+ final FieldODEStateAndDerivative<T> newSoftPreviousState,
+ final FieldODEStateAndDerivative<T> newSoftCurrentState,
+ final FieldEquationsMapper<T> newMapper) {
+ return new MidpointFieldStepInterpolator<T>(newField, newForward, newYDotK,
+ newGlobalPreviousState, newGlobalCurrentState,
+ newSoftPreviousState, newSoftCurrentState,
+ newMapper);
+ }
+
+ /** {@inheritDoc} */
+ @SuppressWarnings("unchecked")
+ @Override
+ protected FieldODEStateAndDerivative<T> computeInterpolatedStateAndDerivatives(final FieldEquationsMapper<T> mapper,
+ final T time, final T theta,
+ final T thetaH, final T oneMinusThetaH) {
+
+ final T coeffDot2 = theta.multiply(2);
+ final T coeffDot1 = time.getField().getOne().subtract(coeffDot2);
+ final T[] interpolatedState;
+ final T[] interpolatedDerivatives;
+
+ if (getGlobalPreviousState() != null && theta.getReal() <= 0.5) {
+ final T coeff1 = theta.multiply(oneMinusThetaH);
+ final T coeff2 = theta.multiply(thetaH);
+ interpolatedState = previousStateLinearCombination(coeff1, coeff2);
+ interpolatedDerivatives = derivativeLinearCombination(coeffDot1, coeffDot2);
+ } else {
+ final T coeff1 = oneMinusThetaH.multiply(theta);
+ final T coeff2 = oneMinusThetaH.multiply(theta.add(1)).negate();
+ interpolatedState = currentStateLinearCombination(coeff1, coeff2);
+ interpolatedDerivatives = derivativeLinearCombination(coeffDot1, coeffDot2);
+ }
+
+ return new FieldODEStateAndDerivative<T>(time, interpolatedState, interpolatedDerivatives);
+
+ }
+
+}
diff --git a/src/main/java/org/apache/commons/math3/ode/nonstiff/MidpointIntegrator.java b/src/main/java/org/apache/commons/math3/ode/nonstiff/MidpointIntegrator.java
new file mode 100644
index 0000000..fa834a1
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/ode/nonstiff/MidpointIntegrator.java
@@ -0,0 +1,69 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math3.ode.nonstiff;
+
+
+/**
+ * This class implements a second order Runge-Kutta integrator for
+ * Ordinary Differential Equations.
+ *
+ * <p>This method is an explicit Runge-Kutta method, its Butcher-array
+ * is the following one :
+ * <pre>
+ * 0 | 0 0
+ * 1/2 | 1/2 0
+ * |----------
+ * | 0 1
+ * </pre>
+ * </p>
+ *
+ * @see EulerIntegrator
+ * @see ClassicalRungeKuttaIntegrator
+ * @see GillIntegrator
+ * @see ThreeEighthesIntegrator
+ * @see LutherIntegrator
+ *
+ * @since 1.2
+ */
+
+public class MidpointIntegrator extends RungeKuttaIntegrator {
+
+ /** Time steps Butcher array. */
+ private static final double[] STATIC_C = {
+ 1.0 / 2.0
+ };
+
+ /** Internal weights Butcher array. */
+ private static final double[][] STATIC_A = {
+ { 1.0 / 2.0 }
+ };
+
+ /** Propagation weights Butcher array. */
+ private static final double[] STATIC_B = {
+ 0.0, 1.0
+ };
+
+ /** Simple constructor.
+ * Build a midpoint integrator with the given step.
+ * @param step integration step
+ */
+ public MidpointIntegrator(final double step) {
+ super("midpoint", STATIC_C, STATIC_A, STATIC_B, new MidpointStepInterpolator(), step);
+ }
+
+}
diff --git a/src/main/java/org/apache/commons/math3/ode/nonstiff/MidpointStepInterpolator.java b/src/main/java/org/apache/commons/math3/ode/nonstiff/MidpointStepInterpolator.java
new file mode 100644
index 0000000..89a5dc1
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/ode/nonstiff/MidpointStepInterpolator.java
@@ -0,0 +1,116 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math3.ode.nonstiff;
+
+import org.apache.commons.math3.ode.sampling.StepInterpolator;
+
+/**
+ * This class implements a step interpolator for second order
+ * Runge-Kutta integrator.
+ *
+ * <p>This interpolator computes dense output inside the last
+ * step computed. The interpolation equation is consistent with the
+ * integration scheme :
+ * <ul>
+ * <li>Using reference point at step start:<br>
+ * y(t<sub>n</sub> + &theta; h) = y (t<sub>n</sub>) + &theta; h [(1 - &theta;) y'<sub>1</sub> + &theta; y'<sub>2</sub>]
+ * </li>
+ * <li>Using reference point at step end:<br>
+ * y(t<sub>n</sub> + &theta; h) = y (t<sub>n</sub> + h) + (1-&theta;) h [&theta; y'<sub>1</sub> - (1+&theta;) y'<sub>2</sub>]
+ * </li>
+ * </ul>
+ * </p>
+ *
+ * where &theta; belongs to [0 ; 1] and where y'<sub>1</sub> and y'<sub>2</sub> are the two
+ * evaluations of the derivatives already computed during the
+ * step.</p>
+ *
+ * @see MidpointIntegrator
+ * @since 1.2
+ */
+
+class MidpointStepInterpolator
+ extends RungeKuttaStepInterpolator {
+
+ /** Serializable version identifier */
+ private static final long serialVersionUID = 20111120L;
+
+ /** Simple constructor.
+ * This constructor builds an instance that is not usable yet, the
+ * {@link
+ * org.apache.commons.math3.ode.sampling.AbstractStepInterpolator#reinitialize}
+ * method should be called before using the instance in order to
+ * initialize the internal arrays. This constructor is used only
+ * in order to delay the initialization in some cases. The {@link
+ * RungeKuttaIntegrator} class uses the prototyping design pattern
+ * to create the step interpolators by cloning an uninitialized model
+ * and later initializing the copy.
+ */
+ // CHECKSTYLE: stop RedundantModifier
+ // the public modifier here is needed for serialization
+ public MidpointStepInterpolator() {
+ }
+ // CHECKSTYLE: resume RedundantModifier
+
+ /** Copy constructor.
+ * @param interpolator interpolator to copy from. The copy is a deep
+ * copy: its arrays are separated from the original arrays of the
+ * instance
+ */
+ MidpointStepInterpolator(final MidpointStepInterpolator interpolator) {
+ super(interpolator);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ protected StepInterpolator doCopy() {
+ return new MidpointStepInterpolator(this);
+ }
+
+
+ /** {@inheritDoc} */
+ @Override
+ protected void computeInterpolatedStateAndDerivatives(final double theta,
+ final double oneMinusThetaH) {
+
+ final double coeffDot2 = 2 * theta;
+ final double coeffDot1 = 1 - coeffDot2;
+
+ if ((previousState != null) && (theta <= 0.5)) {
+ final double coeff1 = theta * oneMinusThetaH;
+ final double coeff2 = theta * theta * h;
+ for (int i = 0; i < interpolatedState.length; ++i) {
+ final double yDot1 = yDotK[0][i];
+ final double yDot2 = yDotK[1][i];
+ interpolatedState[i] = previousState[i] + coeff1 * yDot1 + coeff2 * yDot2;
+ interpolatedDerivatives[i] = coeffDot1 * yDot1 + coeffDot2 * yDot2;
+ }
+ } else {
+ final double coeff1 = oneMinusThetaH * theta;
+ final double coeff2 = oneMinusThetaH * (1.0 + theta);
+ for (int i = 0; i < interpolatedState.length; ++i) {
+ final double yDot1 = yDotK[0][i];
+ final double yDot2 = yDotK[1][i];
+ interpolatedState[i] = currentState[i] + coeff1 * yDot1 - coeff2 * yDot2;
+ interpolatedDerivatives[i] = coeffDot1 * yDot1 + coeffDot2 * yDot2;
+ }
+ }
+
+ }
+
+}
diff --git a/src/main/java/org/apache/commons/math3/ode/nonstiff/RungeKuttaFieldIntegrator.java b/src/main/java/org/apache/commons/math3/ode/nonstiff/RungeKuttaFieldIntegrator.java
new file mode 100644
index 0000000..a97e9f5
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/ode/nonstiff/RungeKuttaFieldIntegrator.java
@@ -0,0 +1,273 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math3.ode.nonstiff;
+
+
+import org.apache.commons.math3.Field;
+import org.apache.commons.math3.RealFieldElement;
+import org.apache.commons.math3.exception.DimensionMismatchException;
+import org.apache.commons.math3.exception.MaxCountExceededException;
+import org.apache.commons.math3.exception.NoBracketingException;
+import org.apache.commons.math3.exception.NumberIsTooSmallException;
+import org.apache.commons.math3.ode.AbstractFieldIntegrator;
+import org.apache.commons.math3.ode.FieldEquationsMapper;
+import org.apache.commons.math3.ode.FieldExpandableODE;
+import org.apache.commons.math3.ode.FirstOrderFieldDifferentialEquations;
+import org.apache.commons.math3.ode.FieldODEState;
+import org.apache.commons.math3.ode.FieldODEStateAndDerivative;
+import org.apache.commons.math3.util.MathArrays;
+
+/**
+ * This class implements the common part of all fixed step Runge-Kutta
+ * integrators for Ordinary Differential Equations.
+ *
+ * <p>These methods are explicit Runge-Kutta methods, their Butcher
+ * arrays are as follows :
+ * <pre>
+ * 0 |
+ * c2 | a21
+ * c3 | a31 a32
+ * ... | ...
+ * cs | as1 as2 ... ass-1
+ * |--------------------------
+ * | b1 b2 ... bs-1 bs
+ * </pre>
+ * </p>
+ *
+ * @see EulerFieldIntegrator
+ * @see ClassicalRungeKuttaFieldIntegrator
+ * @see GillFieldIntegrator
+ * @see MidpointFieldIntegrator
+ * @param <T> the type of the field elements
+ * @since 3.6
+ */
+
+public abstract class RungeKuttaFieldIntegrator<T extends RealFieldElement<T>>
+ extends AbstractFieldIntegrator<T>
+ implements FieldButcherArrayProvider<T> {
+
+ /** Time steps from Butcher array (without the first zero). */
+ private final T[] c;
+
+ /** Internal weights from Butcher array (without the first empty row). */
+ private final T[][] a;
+
+ /** External weights for the high order method from Butcher array. */
+ private final T[] b;
+
+ /** Integration step. */
+ private final T step;
+
+ /** Simple constructor.
+ * Build a Runge-Kutta integrator with the given
+ * step. The default step handler does nothing.
+ * @param field field to which the time and state vector elements belong
+ * @param name name of the method
+ * @param step integration step
+ */
+ protected RungeKuttaFieldIntegrator(final Field<T> field, final String name, final T step) {
+ super(field, name);
+ this.c = getC();
+ this.a = getA();
+ this.b = getB();
+ this.step = step.abs();
+ }
+
+ /** Create a fraction.
+ * @param p numerator
+ * @param q denominator
+ * @return p/q computed in the instance field
+ */
+ protected T fraction(final int p, final int q) {
+ return getField().getZero().add(p).divide(q);
+ }
+
+ /** Create an interpolator.
+ * @param forward integration direction indicator
+ * @param yDotK slopes at the intermediate points
+ * @param globalPreviousState start of the global step
+ * @param globalCurrentState end of the global step
+ * @param mapper equations mapper for the all equations
+ * @return external weights for the high order method from Butcher array
+ */
+ protected abstract RungeKuttaFieldStepInterpolator<T> createInterpolator(boolean forward, T[][] yDotK,
+ final FieldODEStateAndDerivative<T> globalPreviousState,
+ final FieldODEStateAndDerivative<T> globalCurrentState,
+ FieldEquationsMapper<T> mapper);
+
+ /** {@inheritDoc} */
+ public FieldODEStateAndDerivative<T> integrate(final FieldExpandableODE<T> equations,
+ final FieldODEState<T> initialState, final T finalTime)
+ throws NumberIsTooSmallException, DimensionMismatchException,
+ MaxCountExceededException, NoBracketingException {
+
+ sanityChecks(initialState, finalTime);
+ final T t0 = initialState.getTime();
+ final T[] y0 = equations.getMapper().mapState(initialState);
+ setStepStart(initIntegration(equations, t0, y0, finalTime));
+ final boolean forward = finalTime.subtract(initialState.getTime()).getReal() > 0;
+
+ // create some internal working arrays
+ final int stages = c.length + 1;
+ T[] y = y0;
+ final T[][] yDotK = MathArrays.buildArray(getField(), stages, -1);
+ final T[] yTmp = MathArrays.buildArray(getField(), y0.length);
+
+ // set up integration control objects
+ if (forward) {
+ if (getStepStart().getTime().add(step).subtract(finalTime).getReal() >= 0) {
+ setStepSize(finalTime.subtract(getStepStart().getTime()));
+ } else {
+ setStepSize(step);
+ }
+ } else {
+ if (getStepStart().getTime().subtract(step).subtract(finalTime).getReal() <= 0) {
+ setStepSize(finalTime.subtract(getStepStart().getTime()));
+ } else {
+ setStepSize(step.negate());
+ }
+ }
+
+ // main integration loop
+ setIsLastStep(false);
+ do {
+
+ // first stage
+ y = equations.getMapper().mapState(getStepStart());
+ yDotK[0] = equations.getMapper().mapDerivative(getStepStart());
+
+ // next stages
+ for (int k = 1; k < stages; ++k) {
+
+ for (int j = 0; j < y0.length; ++j) {
+ T sum = yDotK[0][j].multiply(a[k-1][0]);
+ for (int l = 1; l < k; ++l) {
+ sum = sum.add(yDotK[l][j].multiply(a[k-1][l]));
+ }
+ yTmp[j] = y[j].add(getStepSize().multiply(sum));
+ }
+
+ yDotK[k] = computeDerivatives(getStepStart().getTime().add(getStepSize().multiply(c[k-1])), yTmp);
+
+ }
+
+ // estimate the state at the end of the step
+ for (int j = 0; j < y0.length; ++j) {
+ T sum = yDotK[0][j].multiply(b[0]);
+ for (int l = 1; l < stages; ++l) {
+ sum = sum.add(yDotK[l][j].multiply(b[l]));
+ }
+ yTmp[j] = y[j].add(getStepSize().multiply(sum));
+ }
+ final T stepEnd = getStepStart().getTime().add(getStepSize());
+ final T[] yDotTmp = computeDerivatives(stepEnd, yTmp);
+ final FieldODEStateAndDerivative<T> stateTmp = new FieldODEStateAndDerivative<T>(stepEnd, yTmp, yDotTmp);
+
+ // discrete events handling
+ System.arraycopy(yTmp, 0, y, 0, y0.length);
+ setStepStart(acceptStep(createInterpolator(forward, yDotK, getStepStart(), stateTmp, equations.getMapper()),
+ finalTime));
+
+ if (!isLastStep()) {
+
+ // stepsize control for next step
+ final T nextT = getStepStart().getTime().add(getStepSize());
+ final boolean nextIsLast = forward ?
+ (nextT.subtract(finalTime).getReal() >= 0) :
+ (nextT.subtract(finalTime).getReal() <= 0);
+ if (nextIsLast) {
+ setStepSize(finalTime.subtract(getStepStart().getTime()));
+ }
+ }
+
+ } while (!isLastStep());
+
+ final FieldODEStateAndDerivative<T> finalState = getStepStart();
+ setStepStart(null);
+ setStepSize(null);
+ return finalState;
+
+ }
+
+ /** Fast computation of a single step of ODE integration.
+ * <p>This method is intended for the limited use case of
+ * very fast computation of only one step without using any of the
+ * rich features of general integrators that may take some time
+ * to set up (i.e. no step handlers, no events handlers, no additional
+ * states, no interpolators, no error control, no evaluations count,
+ * no sanity checks ...). It handles the strict minimum of computation,
+ * so it can be embedded in outer loops.</p>
+ * <p>
+ * This method is <em>not</em> used at all by the {@link #integrate(FieldExpandableODE,
+ * FieldODEState, RealFieldElement)} method. It also completely ignores the step set at
+ * construction time, and uses only a single step to go from {@code t0} to {@code t}.
+ * </p>
+ * <p>
+ * As this method does not use any of the state-dependent features of the integrator,
+ * it should be reasonably thread-safe <em>if and only if</em> the provided differential
+ * equations are themselves thread-safe.
+ * </p>
+ * @param equations differential equations to integrate
+ * @param t0 initial time
+ * @param y0 initial value of the state vector at t0
+ * @param t target time for the integration
+ * (can be set to a value smaller than {@code t0} for backward integration)
+ * @return state vector at {@code t}
+ */
+ public T[] singleStep(final FirstOrderFieldDifferentialEquations<T> equations,
+ final T t0, final T[] y0, final T t) {
+
+ // create some internal working arrays
+ final T[] y = y0.clone();
+ final int stages = c.length + 1;
+ final T[][] yDotK = MathArrays.buildArray(getField(), stages, -1);
+ final T[] yTmp = y0.clone();
+
+ // first stage
+ final T h = t.subtract(t0);
+ yDotK[0] = equations.computeDerivatives(t0, y);
+
+ // next stages
+ for (int k = 1; k < stages; ++k) {
+
+ for (int j = 0; j < y0.length; ++j) {
+ T sum = yDotK[0][j].multiply(a[k-1][0]);
+ for (int l = 1; l < k; ++l) {
+ sum = sum.add(yDotK[l][j].multiply(a[k-1][l]));
+ }
+ yTmp[j] = y[j].add(h.multiply(sum));
+ }
+
+ yDotK[k] = equations.computeDerivatives(t0.add(h.multiply(c[k-1])), yTmp);
+
+ }
+
+ // estimate the state at the end of the step
+ for (int j = 0; j < y0.length; ++j) {
+ T sum = yDotK[0][j].multiply(b[0]);
+ for (int l = 1; l < stages; ++l) {
+ sum = sum.add(yDotK[l][j].multiply(b[l]));
+ }
+ y[j] = y[j].add(h.multiply(sum));
+ }
+
+ return y;
+
+ }
+
+}
diff --git a/src/main/java/org/apache/commons/math3/ode/nonstiff/RungeKuttaFieldStepInterpolator.java b/src/main/java/org/apache/commons/math3/ode/nonstiff/RungeKuttaFieldStepInterpolator.java
new file mode 100644
index 0000000..7d92d78
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/ode/nonstiff/RungeKuttaFieldStepInterpolator.java
@@ -0,0 +1,143 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math3.ode.nonstiff;
+
+import org.apache.commons.math3.Field;
+import org.apache.commons.math3.RealFieldElement;
+import org.apache.commons.math3.ode.FieldEquationsMapper;
+import org.apache.commons.math3.ode.FieldODEStateAndDerivative;
+import org.apache.commons.math3.ode.sampling.AbstractFieldStepInterpolator;
+import org.apache.commons.math3.util.MathArrays;
+
+/** This class represents an interpolator over the last step during an
+ * ODE integration for Runge-Kutta and embedded Runge-Kutta integrators.
+ *
+ * @see RungeKuttaFieldIntegrator
+ * @see EmbeddedRungeKuttaFieldIntegrator
+ *
+ * @param <T> the type of the field elements
+ * @since 3.6
+ */
+
+abstract class RungeKuttaFieldStepInterpolator<T extends RealFieldElement<T>>
+ extends AbstractFieldStepInterpolator<T> {
+
+ /** Field to which the time and state vector elements belong. */
+ private final Field<T> field;
+
+ /** Slopes at the intermediate points. */
+ private final T[][] yDotK;
+
+ /** Simple constructor.
+ * @param field field to which the time and state vector elements belong
+ * @param forward integration direction indicator
+ * @param yDotK slopes at the intermediate points
+ * @param globalPreviousState start of the global step
+ * @param globalCurrentState end of the global step
+ * @param softPreviousState start of the restricted step
+ * @param softCurrentState end of the restricted step
+ * @param mapper equations mapper for the all equations
+ */
+ protected RungeKuttaFieldStepInterpolator(final Field<T> field, final boolean forward,
+ final T[][] yDotK,
+ final FieldODEStateAndDerivative<T> globalPreviousState,
+ final FieldODEStateAndDerivative<T> globalCurrentState,
+ final FieldODEStateAndDerivative<T> softPreviousState,
+ final FieldODEStateAndDerivative<T> softCurrentState,
+ final FieldEquationsMapper<T> mapper) {
+ super(forward, globalPreviousState, globalCurrentState, softPreviousState, softCurrentState, mapper);
+ this.field = field;
+ this.yDotK = MathArrays.buildArray(field, yDotK.length, -1);
+ for (int i = 0; i < yDotK.length; ++i) {
+ this.yDotK[i] = yDotK[i].clone();
+ }
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ protected RungeKuttaFieldStepInterpolator<T> create(boolean newForward,
+ FieldODEStateAndDerivative<T> newGlobalPreviousState,
+ FieldODEStateAndDerivative<T> newGlobalCurrentState,
+ FieldODEStateAndDerivative<T> newSoftPreviousState,
+ FieldODEStateAndDerivative<T> newSoftCurrentState,
+ FieldEquationsMapper<T> newMapper) {
+ return create(field, newForward, yDotK,
+ newGlobalPreviousState, newGlobalCurrentState,
+ newSoftPreviousState, newSoftCurrentState,
+ newMapper);
+ }
+
+ /** Create a new instance.
+ * @param newField field to which the time and state vector elements belong
+ * @param newForward integration direction indicator
+ * @param newYDotK slopes at the intermediate points
+ * @param newGlobalPreviousState start of the global step
+ * @param newGlobalCurrentState end of the global step
+ * @param newSoftPreviousState start of the restricted step
+ * @param newSoftCurrentState end of the restricted step
+ * @param newMapper equations mapper for the all equations
+ * @return a new instance
+ */
+ protected abstract RungeKuttaFieldStepInterpolator<T> create(Field<T> newField, boolean newForward, T[][] newYDotK,
+ FieldODEStateAndDerivative<T> newGlobalPreviousState,
+ FieldODEStateAndDerivative<T> newGlobalCurrentState,
+ FieldODEStateAndDerivative<T> newSoftPreviousState,
+ FieldODEStateAndDerivative<T> newSoftCurrentState,
+ FieldEquationsMapper<T> newMapper);
+
+ /** Compute a state by linear combination added to previous state.
+ * @param coefficients coefficients to apply to the method staged derivatives
+ * @return combined state
+ */
+ protected final T[] previousStateLinearCombination(final T ... coefficients) {
+ return combine(getPreviousState().getState(),
+ coefficients);
+ }
+
+ /** Compute a state by linear combination added to current state.
+ * @param coefficients coefficients to apply to the method staged derivatives
+ * @return combined state
+ */
+ protected T[] currentStateLinearCombination(final T ... coefficients) {
+ return combine(getCurrentState().getState(),
+ coefficients);
+ }
+
+ /** Compute a state derivative by linear combination.
+ * @param coefficients coefficients to apply to the method staged derivatives
+ * @return combined state
+ */
+ protected T[] derivativeLinearCombination(final T ... coefficients) {
+ return combine(MathArrays.buildArray(field, yDotK[0].length), coefficients);
+ }
+
+ /** Linearly combine arrays.
+ * @param a array to add to
+ * @param coefficients coefficients to apply to the method staged derivatives
+ * @return a itself, as a convenience for fluent API
+ */
+ private T[] combine(final T[] a, final T ... coefficients) {
+ for (int i = 0; i < a.length; ++i) {
+ for (int k = 0; k < coefficients.length; ++k) {
+ a[i] = a[i].add(coefficients[k].multiply(yDotK[k][i]));
+ }
+ }
+ return a;
+ }
+
+}
diff --git a/src/main/java/org/apache/commons/math3/ode/nonstiff/RungeKuttaIntegrator.java b/src/main/java/org/apache/commons/math3/ode/nonstiff/RungeKuttaIntegrator.java
new file mode 100644
index 0000000..5f7d5d8
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/ode/nonstiff/RungeKuttaIntegrator.java
@@ -0,0 +1,269 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math3.ode.nonstiff;
+
+
+import org.apache.commons.math3.exception.DimensionMismatchException;
+import org.apache.commons.math3.exception.MaxCountExceededException;
+import org.apache.commons.math3.exception.NoBracketingException;
+import org.apache.commons.math3.exception.NumberIsTooSmallException;
+import org.apache.commons.math3.ode.AbstractIntegrator;
+import org.apache.commons.math3.ode.ExpandableStatefulODE;
+import org.apache.commons.math3.ode.FirstOrderDifferentialEquations;
+import org.apache.commons.math3.util.FastMath;
+
+/**
+ * This class implements the common part of all fixed step Runge-Kutta
+ * integrators for Ordinary Differential Equations.
+ *
+ * <p>These methods are explicit Runge-Kutta methods, their Butcher
+ * arrays are as follows :
+ * <pre>
+ * 0 |
+ * c2 | a21
+ * c3 | a31 a32
+ * ... | ...
+ * cs | as1 as2 ... ass-1
+ * |--------------------------
+ * | b1 b2 ... bs-1 bs
+ * </pre>
+ * </p>
+ *
+ * @see EulerIntegrator
+ * @see ClassicalRungeKuttaIntegrator
+ * @see GillIntegrator
+ * @see MidpointIntegrator
+ * @since 1.2
+ */
+
+public abstract class RungeKuttaIntegrator extends AbstractIntegrator {
+
+ /** Time steps from Butcher array (without the first zero). */
+ private final double[] c;
+
+ /** Internal weights from Butcher array (without the first empty row). */
+ private final double[][] a;
+
+ /** External weights for the high order method from Butcher array. */
+ private final double[] b;
+
+ /** Prototype of the step interpolator. */
+ private final RungeKuttaStepInterpolator prototype;
+
+ /** Integration step. */
+ private final double step;
+
+ /** Simple constructor.
+ * Build a Runge-Kutta integrator with the given
+ * step. The default step handler does nothing.
+ * @param name name of the method
+ * @param c time steps from Butcher array (without the first zero)
+ * @param a internal weights from Butcher array (without the first empty row)
+ * @param b propagation weights for the high order method from Butcher array
+ * @param prototype prototype of the step interpolator to use
+ * @param step integration step
+ */
+ protected RungeKuttaIntegrator(final String name,
+ final double[] c, final double[][] a, final double[] b,
+ final RungeKuttaStepInterpolator prototype,
+ final double step) {
+ super(name);
+ this.c = c;
+ this.a = a;
+ this.b = b;
+ this.prototype = prototype;
+ this.step = FastMath.abs(step);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void integrate(final ExpandableStatefulODE equations, final double t)
+ throws NumberIsTooSmallException, DimensionMismatchException,
+ MaxCountExceededException, NoBracketingException {
+
+ sanityChecks(equations, t);
+ setEquations(equations);
+ final boolean forward = t > equations.getTime();
+
+ // create some internal working arrays
+ final double[] y0 = equations.getCompleteState();
+ final double[] y = y0.clone();
+ final int stages = c.length + 1;
+ final double[][] yDotK = new double[stages][];
+ for (int i = 0; i < stages; ++i) {
+ yDotK [i] = new double[y0.length];
+ }
+ final double[] yTmp = y0.clone();
+ final double[] yDotTmp = new double[y0.length];
+
+ // set up an interpolator sharing the integrator arrays
+ final RungeKuttaStepInterpolator interpolator = (RungeKuttaStepInterpolator) prototype.copy();
+ interpolator.reinitialize(this, yTmp, yDotK, forward,
+ equations.getPrimaryMapper(), equations.getSecondaryMappers());
+ interpolator.storeTime(equations.getTime());
+
+ // set up integration control objects
+ stepStart = equations.getTime();
+ if (forward) {
+ if (stepStart + step >= t) {
+ stepSize = t - stepStart;
+ } else {
+ stepSize = step;
+ }
+ } else {
+ if (stepStart - step <= t) {
+ stepSize = t - stepStart;
+ } else {
+ stepSize = -step;
+ }
+ }
+ initIntegration(equations.getTime(), y0, t);
+
+ // main integration loop
+ isLastStep = false;
+ do {
+
+ interpolator.shift();
+
+ // first stage
+ computeDerivatives(stepStart, y, yDotK[0]);
+
+ // next stages
+ for (int k = 1; k < stages; ++k) {
+
+ for (int j = 0; j < y0.length; ++j) {
+ double sum = a[k-1][0] * yDotK[0][j];
+ for (int l = 1; l < k; ++l) {
+ sum += a[k-1][l] * yDotK[l][j];
+ }
+ yTmp[j] = y[j] + stepSize * sum;
+ }
+
+ computeDerivatives(stepStart + c[k-1] * stepSize, yTmp, yDotK[k]);
+
+ }
+
+ // estimate the state at the end of the step
+ for (int j = 0; j < y0.length; ++j) {
+ double sum = b[0] * yDotK[0][j];
+ for (int l = 1; l < stages; ++l) {
+ sum += b[l] * yDotK[l][j];
+ }
+ yTmp[j] = y[j] + stepSize * sum;
+ }
+
+ // discrete events handling
+ interpolator.storeTime(stepStart + stepSize);
+ System.arraycopy(yTmp, 0, y, 0, y0.length);
+ System.arraycopy(yDotK[stages - 1], 0, yDotTmp, 0, y0.length);
+ stepStart = acceptStep(interpolator, y, yDotTmp, t);
+
+ if (!isLastStep) {
+
+ // prepare next step
+ interpolator.storeTime(stepStart);
+
+ // stepsize control for next step
+ final double nextT = stepStart + stepSize;
+ final boolean nextIsLast = forward ? (nextT >= t) : (nextT <= t);
+ if (nextIsLast) {
+ stepSize = t - stepStart;
+ }
+ }
+
+ } while (!isLastStep);
+
+ // dispatch results
+ equations.setTime(stepStart);
+ equations.setCompleteState(y);
+
+ stepStart = Double.NaN;
+ stepSize = Double.NaN;
+
+ }
+
+ /** Fast computation of a single step of ODE integration.
+ * <p>This method is intended for the limited use case of
+ * very fast computation of only one step without using any of the
+ * rich features of general integrators that may take some time
+ * to set up (i.e. no step handlers, no events handlers, no additional
+ * states, no interpolators, no error control, no evaluations count,
+ * no sanity checks ...). It handles the strict minimum of computation,
+ * so it can be embedded in outer loops.</p>
+ * <p>
+ * This method is <em>not</em> used at all by the {@link #integrate(ExpandableStatefulODE, double)}
+ * method. It also completely ignores the step set at construction time, and
+ * uses only a single step to go from {@code t0} to {@code t}.
+ * </p>
+ * <p>
+ * As this method does not use any of the state-dependent features of the integrator,
+ * it should be reasonably thread-safe <em>if and only if</em> the provided differential
+ * equations are themselves thread-safe.
+ * </p>
+ * @param equations differential equations to integrate
+ * @param t0 initial time
+ * @param y0 initial value of the state vector at t0
+ * @param t target time for the integration
+ * (can be set to a value smaller than {@code t0} for backward integration)
+ * @return state vector at {@code t}
+ */
+ public double[] singleStep(final FirstOrderDifferentialEquations equations,
+ final double t0, final double[] y0, final double t) {
+
+ // create some internal working arrays
+ final double[] y = y0.clone();
+ final int stages = c.length + 1;
+ final double[][] yDotK = new double[stages][];
+ for (int i = 0; i < stages; ++i) {
+ yDotK [i] = new double[y0.length];
+ }
+ final double[] yTmp = y0.clone();
+
+ // first stage
+ final double h = t - t0;
+ equations.computeDerivatives(t0, y, yDotK[0]);
+
+ // next stages
+ for (int k = 1; k < stages; ++k) {
+
+ for (int j = 0; j < y0.length; ++j) {
+ double sum = a[k-1][0] * yDotK[0][j];
+ for (int l = 1; l < k; ++l) {
+ sum += a[k-1][l] * yDotK[l][j];
+ }
+ yTmp[j] = y[j] + h * sum;
+ }
+
+ equations.computeDerivatives(t0 + c[k-1] * h, yTmp, yDotK[k]);
+
+ }
+
+ // estimate the state at the end of the step
+ for (int j = 0; j < y0.length; ++j) {
+ double sum = b[0] * yDotK[0][j];
+ for (int l = 1; l < stages; ++l) {
+ sum += b[l] * yDotK[l][j];
+ }
+ y[j] += h * sum;
+ }
+
+ return y;
+
+ }
+
+}
diff --git a/src/main/java/org/apache/commons/math3/ode/nonstiff/RungeKuttaStepInterpolator.java b/src/main/java/org/apache/commons/math3/ode/nonstiff/RungeKuttaStepInterpolator.java
new file mode 100644
index 0000000..1ae7cb9
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/ode/nonstiff/RungeKuttaStepInterpolator.java
@@ -0,0 +1,211 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math3.ode.nonstiff;
+
+import java.io.IOException;
+import java.io.ObjectInput;
+import java.io.ObjectOutput;
+
+import org.apache.commons.math3.ode.AbstractIntegrator;
+import org.apache.commons.math3.ode.EquationsMapper;
+import org.apache.commons.math3.ode.sampling.AbstractStepInterpolator;
+
+/** This class represents an interpolator over the last step during an
+ * ODE integration for Runge-Kutta and embedded Runge-Kutta integrators.
+ *
+ * @see RungeKuttaIntegrator
+ * @see EmbeddedRungeKuttaIntegrator
+ *
+ * @since 1.2
+ */
+
+abstract class RungeKuttaStepInterpolator
+ extends AbstractStepInterpolator {
+
+ /** Previous state. */
+ protected double[] previousState;
+
+ /** Slopes at the intermediate points */
+ protected double[][] yDotK;
+
+ /** Reference to the integrator. */
+ protected AbstractIntegrator integrator;
+
+ /** Simple constructor.
+ * This constructor builds an instance that is not usable yet, the
+ * {@link #reinitialize} method should be called before using the
+ * instance in order to initialize the internal arrays. This
+ * constructor is used only in order to delay the initialization in
+ * some cases. The {@link RungeKuttaIntegrator} and {@link
+ * EmbeddedRungeKuttaIntegrator} classes use the prototyping design
+ * pattern to create the step interpolators by cloning an
+ * uninitialized model and latter initializing the copy.
+ */
+ protected RungeKuttaStepInterpolator() {
+ previousState = null;
+ yDotK = null;
+ integrator = null;
+ }
+
+ /** Copy constructor.
+
+ * <p>The copied interpolator should have been finalized before the
+ * copy, otherwise the copy will not be able to perform correctly any
+ * interpolation and will throw a {@link NullPointerException}
+ * later. Since we don't want this constructor to throw the
+ * exceptions finalization may involve and since we don't want this
+ * method to modify the state of the copied interpolator,
+ * finalization is <strong>not</strong> done automatically, it
+ * remains under user control.</p>
+
+ * <p>The copy is a deep copy: its arrays are separated from the
+ * original arrays of the instance.</p>
+
+ * @param interpolator interpolator to copy from.
+
+ */
+ RungeKuttaStepInterpolator(final RungeKuttaStepInterpolator interpolator) {
+
+ super(interpolator);
+
+ if (interpolator.currentState != null) {
+
+ previousState = interpolator.previousState.clone();
+
+ yDotK = new double[interpolator.yDotK.length][];
+ for (int k = 0; k < interpolator.yDotK.length; ++k) {
+ yDotK[k] = interpolator.yDotK[k].clone();
+ }
+
+ } else {
+ previousState = null;
+ yDotK = null;
+ }
+
+ // we cannot keep any reference to the equations in the copy
+ // the interpolator should have been finalized before
+ integrator = null;
+
+ }
+
+ /** Reinitialize the instance
+ * <p>Some Runge-Kutta integrators need fewer functions evaluations
+ * than their counterpart step interpolators. So the interpolator
+ * should perform the last evaluations they need by themselves. The
+ * {@link RungeKuttaIntegrator RungeKuttaIntegrator} and {@link
+ * EmbeddedRungeKuttaIntegrator EmbeddedRungeKuttaIntegrator}
+ * abstract classes call this method in order to let the step
+ * interpolator perform the evaluations it needs. These evaluations
+ * will be performed during the call to <code>doFinalize</code> if
+ * any, i.e. only if the step handler either calls the {@link
+ * AbstractStepInterpolator#finalizeStep finalizeStep} method or the
+ * {@link AbstractStepInterpolator#getInterpolatedState
+ * getInterpolatedState} method (for an interpolator which needs a
+ * finalization) or if it clones the step interpolator.</p>
+ * @param rkIntegrator integrator being used
+ * @param y reference to the integrator array holding the state at
+ * the end of the step
+ * @param yDotArray reference to the integrator array holding all the
+ * intermediate slopes
+ * @param forward integration direction indicator
+ * @param primaryMapper equations mapper for the primary equations set
+ * @param secondaryMappers equations mappers for the secondary equations sets
+ */
+ public void reinitialize(final AbstractIntegrator rkIntegrator,
+ final double[] y, final double[][] yDotArray, final boolean forward,
+ final EquationsMapper primaryMapper,
+ final EquationsMapper[] secondaryMappers) {
+ reinitialize(y, forward, primaryMapper, secondaryMappers);
+ this.previousState = null;
+ this.yDotK = yDotArray;
+ this.integrator = rkIntegrator;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void shift() {
+ previousState = currentState.clone();
+ super.shift();
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void writeExternal(final ObjectOutput out)
+ throws IOException {
+
+ // save the state of the base class
+ writeBaseExternal(out);
+
+ // save the local attributes
+ final int n = (currentState == null) ? -1 : currentState.length;
+ for (int i = 0; i < n; ++i) {
+ out.writeDouble(previousState[i]);
+ }
+
+ final int kMax = (yDotK == null) ? -1 : yDotK.length;
+ out.writeInt(kMax);
+ for (int k = 0; k < kMax; ++k) {
+ for (int i = 0; i < n; ++i) {
+ out.writeDouble(yDotK[k][i]);
+ }
+ }
+
+ // we do not save any reference to the equations
+
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void readExternal(final ObjectInput in)
+ throws IOException, ClassNotFoundException {
+
+ // read the base class
+ final double t = readBaseExternal(in);
+
+ // read the local attributes
+ final int n = (currentState == null) ? -1 : currentState.length;
+ if (n < 0) {
+ previousState = null;
+ } else {
+ previousState = new double[n];
+ for (int i = 0; i < n; ++i) {
+ previousState[i] = in.readDouble();
+ }
+ }
+
+ final int kMax = in.readInt();
+ yDotK = (kMax < 0) ? null : new double[kMax][];
+ for (int k = 0; k < kMax; ++k) {
+ yDotK[k] = (n < 0) ? null : new double[n];
+ for (int i = 0; i < n; ++i) {
+ yDotK[k][i] = in.readDouble();
+ }
+ }
+
+ integrator = null;
+
+ if (currentState != null) {
+ // we can now set the interpolated time and state
+ setInterpolatedTime(t);
+ } else {
+ interpolatedTime = t;
+ }
+
+ }
+
+}
diff --git a/src/main/java/org/apache/commons/math3/ode/nonstiff/ThreeEighthesFieldIntegrator.java b/src/main/java/org/apache/commons/math3/ode/nonstiff/ThreeEighthesFieldIntegrator.java
new file mode 100644
index 0000000..7e91de8
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/ode/nonstiff/ThreeEighthesFieldIntegrator.java
@@ -0,0 +1,110 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math3.ode.nonstiff;
+
+import org.apache.commons.math3.Field;
+import org.apache.commons.math3.RealFieldElement;
+import org.apache.commons.math3.ode.FieldEquationsMapper;
+import org.apache.commons.math3.ode.FieldODEStateAndDerivative;
+import org.apache.commons.math3.util.MathArrays;
+
+/**
+ * This class implements the 3/8 fourth order Runge-Kutta
+ * integrator for Ordinary Differential Equations.
+ *
+ * <p>This method is an explicit Runge-Kutta method, its Butcher-array
+ * is the following one :
+ * <pre>
+ * 0 | 0 0 0 0
+ * 1/3 | 1/3 0 0 0
+ * 2/3 |-1/3 1 0 0
+ * 1 | 1 -1 1 0
+ * |--------------------
+ * | 1/8 3/8 3/8 1/8
+ * </pre>
+ * </p>
+ *
+ * @see EulerFieldIntegrator
+ * @see ClassicalRungeKuttaFieldIntegrator
+ * @see GillFieldIntegrator
+ * @see MidpointFieldIntegrator
+ * @see LutherFieldIntegrator
+ * @param <T> the type of the field elements
+ * @since 3.6
+ */
+
+public class ThreeEighthesFieldIntegrator<T extends RealFieldElement<T>>
+ extends RungeKuttaFieldIntegrator<T> {
+
+ /** Simple constructor.
+ * Build a 3/8 integrator with the given step.
+ * @param field field to which the time and state vector elements belong
+ * @param step integration step
+ */
+ public ThreeEighthesFieldIntegrator(final Field<T> field, final T step) {
+ super(field, "3/8", step);
+ }
+
+ /** {@inheritDoc} */
+ public T[] getC() {
+ final T[] c = MathArrays.buildArray(getField(), 3);
+ c[0] = fraction(1, 3);
+ c[1] = c[0].add(c[0]);
+ c[2] = getField().getOne();
+ return c;
+ }
+
+ /** {@inheritDoc} */
+ public T[][] getA() {
+ final T[][] a = MathArrays.buildArray(getField(), 3, -1);
+ for (int i = 0; i < a.length; ++i) {
+ a[i] = MathArrays.buildArray(getField(), i + 1);
+ }
+ a[0][0] = fraction(1, 3);
+ a[1][0] = a[0][0].negate();
+ a[1][1] = getField().getOne();
+ a[2][0] = getField().getOne();
+ a[2][1] = getField().getOne().negate();
+ a[2][2] = getField().getOne();
+ return a;
+ }
+
+ /** {@inheritDoc} */
+ public T[] getB() {
+ final T[] b = MathArrays.buildArray(getField(), 4);
+ b[0] = fraction(1, 8);
+ b[1] = fraction(3, 8);
+ b[2] = b[1];
+ b[3] = b[0];
+ return b;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ protected ThreeEighthesFieldStepInterpolator<T>
+ createInterpolator(final boolean forward, T[][] yDotK,
+ final FieldODEStateAndDerivative<T> globalPreviousState,
+ final FieldODEStateAndDerivative<T> globalCurrentState,
+ final FieldEquationsMapper<T> mapper) {
+ return new ThreeEighthesFieldStepInterpolator<T>(getField(), forward, yDotK,
+ globalPreviousState, globalCurrentState,
+ globalPreviousState, globalCurrentState,
+ mapper);
+ }
+
+}
diff --git a/src/main/java/org/apache/commons/math3/ode/nonstiff/ThreeEighthesFieldStepInterpolator.java b/src/main/java/org/apache/commons/math3/ode/nonstiff/ThreeEighthesFieldStepInterpolator.java
new file mode 100644
index 0000000..14a4eb8
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/ode/nonstiff/ThreeEighthesFieldStepInterpolator.java
@@ -0,0 +1,139 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math3.ode.nonstiff;
+
+import org.apache.commons.math3.Field;
+import org.apache.commons.math3.RealFieldElement;
+import org.apache.commons.math3.ode.FieldEquationsMapper;
+import org.apache.commons.math3.ode.FieldODEStateAndDerivative;
+
+/**
+ * This class implements a step interpolator for the 3/8 fourth
+ * order Runge-Kutta integrator.
+ *
+ * <p>This interpolator allows to compute dense output inside the last
+ * step computed. The interpolation equation is consistent with the
+ * integration scheme :
+ * <ul>
+ * <li>Using reference point at step start:<br>
+ * y(t<sub>n</sub> + &theta; h) = y (t<sub>n</sub>)
+ * + &theta; (h/8) [ (8 - 15 &theta; + 8 &theta;<sup>2</sup>) y'<sub>1</sub>
+ * + 3 * (15 &theta; - 12 &theta;<sup>2</sup>) y'<sub>2</sub>
+ * + 3 &theta; y'<sub>3</sub>
+ * + (-3 &theta; + 4 &theta;<sup>2</sup>) y'<sub>4</sub>
+ * ]
+ * </li>
+ * <li>Using reference point at step end:<br>
+ * y(t<sub>n</sub> + &theta; h) = y (t<sub>n</sub> + h)
+ * - (1 - &theta;) (h/8) [(1 - 7 &theta; + 8 &theta;<sup>2</sup>) y'<sub>1</sub>
+ * + 3 (1 + &theta; - 4 &theta;<sup>2</sup>) y'<sub>2</sub>
+ * + 3 (1 + &theta;) y'<sub>3</sub>
+ * + (1 + &theta; + 4 &theta;<sup>2</sup>) y'<sub>4</sub>
+ * ]
+ * </li>
+ * </ul>
+ * </p>
+ *
+ * where &theta; belongs to [0 ; 1] and where y'<sub>1</sub> to y'<sub>4</sub> are the four
+ * evaluations of the derivatives already computed during the
+ * step.</p>
+ *
+ * @see ThreeEighthesFieldIntegrator
+ * @param <T> the type of the field elements
+ * @since 3.6
+ */
+
+class ThreeEighthesFieldStepInterpolator<T extends RealFieldElement<T>>
+ extends RungeKuttaFieldStepInterpolator<T> {
+
+ /** Simple constructor.
+ * @param field field to which the time and state vector elements belong
+ * @param forward integration direction indicator
+ * @param yDotK slopes at the intermediate points
+ * @param globalPreviousState start of the global step
+ * @param globalCurrentState end of the global step
+ * @param softPreviousState start of the restricted step
+ * @param softCurrentState end of the restricted step
+ * @param mapper equations mapper for the all equations
+ */
+ ThreeEighthesFieldStepInterpolator(final Field<T> field, final boolean forward,
+ final T[][] yDotK,
+ final FieldODEStateAndDerivative<T> globalPreviousState,
+ final FieldODEStateAndDerivative<T> globalCurrentState,
+ final FieldODEStateAndDerivative<T> softPreviousState,
+ final FieldODEStateAndDerivative<T> softCurrentState,
+ final FieldEquationsMapper<T> mapper) {
+ super(field, forward, yDotK,
+ globalPreviousState, globalCurrentState, softPreviousState, softCurrentState,
+ mapper);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ protected ThreeEighthesFieldStepInterpolator<T> create(final Field<T> newField, final boolean newForward, final T[][] newYDotK,
+ final FieldODEStateAndDerivative<T> newGlobalPreviousState,
+ final FieldODEStateAndDerivative<T> newGlobalCurrentState,
+ final FieldODEStateAndDerivative<T> newSoftPreviousState,
+ final FieldODEStateAndDerivative<T> newSoftCurrentState,
+ final FieldEquationsMapper<T> newMapper) {
+ return new ThreeEighthesFieldStepInterpolator<T>(newField, newForward, newYDotK,
+ newGlobalPreviousState, newGlobalCurrentState,
+ newSoftPreviousState, newSoftCurrentState,
+ newMapper);
+ }
+
+ /** {@inheritDoc} */
+ @SuppressWarnings("unchecked")
+ @Override
+ protected FieldODEStateAndDerivative<T> computeInterpolatedStateAndDerivatives(final FieldEquationsMapper<T> mapper,
+ final T time, final T theta,
+ final T thetaH, final T oneMinusThetaH) {
+
+ final T coeffDot3 = theta.multiply(0.75);
+ final T coeffDot1 = coeffDot3.multiply(theta.multiply(4).subtract(5)).add(1);
+ final T coeffDot2 = coeffDot3.multiply(theta.multiply(-6).add(5));
+ final T coeffDot4 = coeffDot3.multiply(theta.multiply(2).subtract(1));
+ final T[] interpolatedState;
+ final T[] interpolatedDerivatives;
+
+ if (getGlobalPreviousState() != null && theta.getReal() <= 0.5) {
+ final T s = thetaH.divide(8);
+ final T fourTheta2 = theta.multiply(theta).multiply(4);
+ final T coeff1 = s.multiply(fourTheta2.multiply(2).subtract(theta.multiply(15)).add(8));
+ final T coeff2 = s.multiply(theta.multiply(5).subtract(fourTheta2)).multiply(3);
+ final T coeff3 = s.multiply(theta).multiply(3);
+ final T coeff4 = s.multiply(fourTheta2.subtract(theta.multiply(3)));
+ interpolatedState = previousStateLinearCombination(coeff1, coeff2, coeff3, coeff4);
+ interpolatedDerivatives = derivativeLinearCombination(coeffDot1, coeffDot2, coeffDot3, coeffDot4);
+ } else {
+ final T s = oneMinusThetaH.divide(-8);
+ final T fourTheta2 = theta.multiply(theta).multiply(4);
+ final T thetaPlus1 = theta.add(1);
+ final T coeff1 = s.multiply(fourTheta2.multiply(2).subtract(theta.multiply(7)).add(1));
+ final T coeff2 = s.multiply(thetaPlus1.subtract(fourTheta2)).multiply(3);
+ final T coeff3 = s.multiply(thetaPlus1).multiply(3);
+ final T coeff4 = s.multiply(thetaPlus1.add(fourTheta2));
+ interpolatedState = currentStateLinearCombination(coeff1, coeff2, coeff3, coeff4);
+ interpolatedDerivatives = derivativeLinearCombination(coeffDot1, coeffDot2, coeffDot3, coeffDot4);
+ }
+
+ return new FieldODEStateAndDerivative<T>(time, interpolatedState, interpolatedDerivatives);
+
+ }
+
+}
diff --git a/src/main/java/org/apache/commons/math3/ode/nonstiff/ThreeEighthesIntegrator.java b/src/main/java/org/apache/commons/math3/ode/nonstiff/ThreeEighthesIntegrator.java
new file mode 100644
index 0000000..c5f8216
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/ode/nonstiff/ThreeEighthesIntegrator.java
@@ -0,0 +1,72 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math3.ode.nonstiff;
+
+
+/**
+ * This class implements the 3/8 fourth order Runge-Kutta
+ * integrator for Ordinary Differential Equations.
+ *
+ * <p>This method is an explicit Runge-Kutta method, its Butcher-array
+ * is the following one :
+ * <pre>
+ * 0 | 0 0 0 0
+ * 1/3 | 1/3 0 0 0
+ * 2/3 |-1/3 1 0 0
+ * 1 | 1 -1 1 0
+ * |--------------------
+ * | 1/8 3/8 3/8 1/8
+ * </pre>
+ * </p>
+ *
+ * @see EulerIntegrator
+ * @see ClassicalRungeKuttaIntegrator
+ * @see GillIntegrator
+ * @see MidpointIntegrator
+ * @see LutherIntegrator
+ * @since 1.2
+ */
+
+public class ThreeEighthesIntegrator extends RungeKuttaIntegrator {
+
+ /** Time steps Butcher array. */
+ private static final double[] STATIC_C = {
+ 1.0 / 3.0, 2.0 / 3.0, 1.0
+ };
+
+ /** Internal weights Butcher array. */
+ private static final double[][] STATIC_A = {
+ { 1.0 / 3.0 },
+ { -1.0 / 3.0, 1.0 },
+ { 1.0, -1.0, 1.0 }
+ };
+
+ /** Propagation weights Butcher array. */
+ private static final double[] STATIC_B = {
+ 1.0 / 8.0, 3.0 / 8.0, 3.0 / 8.0, 1.0 / 8.0
+ };
+
+ /** Simple constructor.
+ * Build a 3/8 integrator with the given step.
+ * @param step integration step
+ */
+ public ThreeEighthesIntegrator(final double step) {
+ super("3/8", STATIC_C, STATIC_A, STATIC_B, new ThreeEighthesStepInterpolator(), step);
+ }
+
+}
diff --git a/src/main/java/org/apache/commons/math3/ode/nonstiff/ThreeEighthesStepInterpolator.java b/src/main/java/org/apache/commons/math3/ode/nonstiff/ThreeEighthesStepInterpolator.java
new file mode 100644
index 0000000..df288fd
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/ode/nonstiff/ThreeEighthesStepInterpolator.java
@@ -0,0 +1,146 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math3.ode.nonstiff;
+
+import org.apache.commons.math3.ode.sampling.StepInterpolator;
+
+/**
+ * This class implements a step interpolator for the 3/8 fourth
+ * order Runge-Kutta integrator.
+ *
+ * <p>This interpolator allows to compute dense output inside the last
+ * step computed. The interpolation equation is consistent with the
+ * integration scheme :
+ * <ul>
+ * <li>Using reference point at step start:<br>
+ * y(t<sub>n</sub> + &theta; h) = y (t<sub>n</sub>)
+ * + &theta; (h/8) [ (8 - 15 &theta; + 8 &theta;<sup>2</sup>) y'<sub>1</sub>
+ * + 3 * (15 &theta; - 12 &theta;<sup>2</sup>) y'<sub>2</sub>
+ * + 3 &theta; y'<sub>3</sub>
+ * + (-3 &theta; + 4 &theta;<sup>2</sup>) y'<sub>4</sub>
+ * ]
+ * </li>
+ * <li>Using reference point at step end:<br>
+ * y(t<sub>n</sub> + &theta; h) = y (t<sub>n</sub> + h)
+ * - (1 - &theta;) (h/8) [(1 - 7 &theta; + 8 &theta;<sup>2</sup>) y'<sub>1</sub>
+ * + 3 (1 + &theta; - 4 &theta;<sup>2</sup>) y'<sub>2</sub>
+ * + 3 (1 + &theta;) y'<sub>3</sub>
+ * + (1 + &theta; + 4 &theta;<sup>2</sup>) y'<sub>4</sub>
+ * ]
+ * </li>
+ * </ul>
+ * </p>
+ *
+ * where &theta; belongs to [0 ; 1] and where y'<sub>1</sub> to y'<sub>4</sub> are the four
+ * evaluations of the derivatives already computed during the
+ * step.</p>
+ *
+ * @see ThreeEighthesIntegrator
+ * @since 1.2
+ */
+
+class ThreeEighthesStepInterpolator
+ extends RungeKuttaStepInterpolator {
+
+ /** Serializable version identifier */
+ private static final long serialVersionUID = 20111120L;
+
+ /** Simple constructor.
+ * This constructor builds an instance that is not usable yet, the
+ * {@link
+ * org.apache.commons.math3.ode.sampling.AbstractStepInterpolator#reinitialize}
+ * method should be called before using the instance in order to
+ * initialize the internal arrays. This constructor is used only
+ * in order to delay the initialization in some cases. The {@link
+ * RungeKuttaIntegrator} class uses the prototyping design pattern
+ * to create the step interpolators by cloning an uninitialized model
+ * and later initializing the copy.
+ */
+ // CHECKSTYLE: stop RedundantModifier
+ // the public modifier here is needed for serialization
+ public ThreeEighthesStepInterpolator() {
+ }
+ // CHECKSTYLE: resume RedundantModifier
+
+ /** Copy constructor.
+ * @param interpolator interpolator to copy from. The copy is a deep
+ * copy: its arrays are separated from the original arrays of the
+ * instance
+ */
+ ThreeEighthesStepInterpolator(final ThreeEighthesStepInterpolator interpolator) {
+ super(interpolator);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ protected StepInterpolator doCopy() {
+ return new ThreeEighthesStepInterpolator(this);
+ }
+
+
+ /** {@inheritDoc} */
+ @Override
+ protected void computeInterpolatedStateAndDerivatives(final double theta,
+ final double oneMinusThetaH) {
+
+ final double coeffDot3 = 0.75 * theta;
+ final double coeffDot1 = coeffDot3 * (4 * theta - 5) + 1;
+ final double coeffDot2 = coeffDot3 * (5 - 6 * theta);
+ final double coeffDot4 = coeffDot3 * (2 * theta - 1);
+
+ if ((previousState != null) && (theta <= 0.5)) {
+ final double s = theta * h / 8.0;
+ final double fourTheta2 = 4 * theta * theta;
+ final double coeff1 = s * (8 - 15 * theta + 2 * fourTheta2);
+ final double coeff2 = 3 * s * (5 * theta - fourTheta2);
+ final double coeff3 = 3 * s * theta;
+ final double coeff4 = s * (-3 * theta + fourTheta2);
+ for (int i = 0; i < interpolatedState.length; ++i) {
+ final double yDot1 = yDotK[0][i];
+ final double yDot2 = yDotK[1][i];
+ final double yDot3 = yDotK[2][i];
+ final double yDot4 = yDotK[3][i];
+ interpolatedState[i] =
+ previousState[i] + coeff1 * yDot1 + coeff2 * yDot2 + coeff3 * yDot3 + coeff4 * yDot4;
+ interpolatedDerivatives[i] =
+ coeffDot1 * yDot1 + coeffDot2 * yDot2 + coeffDot3 * yDot3 + coeffDot4 * yDot4;
+
+ }
+ } else {
+ final double s = oneMinusThetaH / 8.0;
+ final double fourTheta2 = 4 * theta * theta;
+ final double coeff1 = s * (1 - 7 * theta + 2 * fourTheta2);
+ final double coeff2 = 3 * s * (1 + theta - fourTheta2);
+ final double coeff3 = 3 * s * (1 + theta);
+ final double coeff4 = s * (1 + theta + fourTheta2);
+ for (int i = 0; i < interpolatedState.length; ++i) {
+ final double yDot1 = yDotK[0][i];
+ final double yDot2 = yDotK[1][i];
+ final double yDot3 = yDotK[2][i];
+ final double yDot4 = yDotK[3][i];
+ interpolatedState[i] =
+ currentState[i] - coeff1 * yDot1 - coeff2 * yDot2 - coeff3 * yDot3 - coeff4 * yDot4;
+ interpolatedDerivatives[i] =
+ coeffDot1 * yDot1 + coeffDot2 * yDot2 + coeffDot3 * yDot3 + coeffDot4 * yDot4;
+
+ }
+ }
+
+ }
+
+}
diff --git a/src/main/java/org/apache/commons/math3/ode/nonstiff/package-info.java b/src/main/java/org/apache/commons/math3/ode/nonstiff/package-info.java
new file mode 100644
index 0000000..b2387ce
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/ode/nonstiff/package-info.java
@@ -0,0 +1,25 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/**
+ *
+ * <p>
+ * This package provides classes to solve non-stiff Ordinary Differential Equations problems.
+ * </p>
+ *
+ *
+ */
+package org.apache.commons.math3.ode.nonstiff;
diff --git a/src/main/java/org/apache/commons/math3/ode/package-info.java b/src/main/java/org/apache/commons/math3/ode/package-info.java
new file mode 100644
index 0000000..1e412f8
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/ode/package-info.java
@@ -0,0 +1,120 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/**
+ * This package provides classes to solve Ordinary Differential Equations problems.
+ *
+ * <p>This package solves Initial Value Problems of the form <code>y'=f(t,y)</code> with <code>
+ * t<sub>0</sub></code> and <code>y(t<sub>0</sub>)=y<sub>0</sub></code> known. The provided
+ * integrators compute an estimate of <code>y(t)</code> from <code>t=t<sub>0</sub></code> to <code>
+ * t=t<sub>1</sub></code>. It is also possible to get thederivatives with respect to the initial
+ * state <code>dy(t)/dy(t<sub>0</sub>)</code> or the derivatives with respect to some ODE parameters
+ * <code>dy(t)/dp</code>.
+ *
+ * <p>All integrators provide dense output. This means that besides computing the state vector at
+ * discrete times, they also provide a cheap mean to get the state between the time steps. They do
+ * so through classes extending the {@link org.apache.commons.math3.ode.sampling.StepInterpolator
+ * StepInterpolator} abstract class, which are made available to the user at the end of each step.
+ *
+ * <p>All integrators handle multiple discrete events detection based on switching functions. This
+ * means that the integrator can be driven by user specified discrete events. The steps are
+ * shortened as needed to ensure the events occur at step boundaries (even if the integrator is a
+ * fixed-step integrator). When the events are triggered, integration can be stopped (this is called
+ * a G-stop facility), the state vector can be changed, or integration can simply go on. The latter
+ * case is useful to handle discontinuities in the differential equations gracefully and get
+ * accurate dense output even close to the discontinuity.
+ *
+ * <p>The user should describe his problem in his own classes (<code>UserProblem</code> in the
+ * diagram below) which should implement the {@link
+ * org.apache.commons.math3.ode.FirstOrderDifferentialEquations FirstOrderDifferentialEquations}
+ * interface. Then he should pass it to the integrator he prefers among all the classes that
+ * implement the {@link org.apache.commons.math3.ode.FirstOrderIntegrator FirstOrderIntegrator}
+ * interface.
+ *
+ * <p>The solution of the integration problem is provided by two means. The first one is aimed
+ * towards simple use: the state vector at the end of the integration process is copied in the
+ * <code>y</code> array of the {@link org.apache.commons.math3.ode.FirstOrderIntegrator#integrate
+ * FirstOrderIntegrator.integrate} method. The second one should be used when more in-depth
+ * information is needed throughout the integration process. The user can register an object
+ * implementing the {@link org.apache.commons.math3.ode.sampling.StepHandler StepHandler} interface
+ * or a {@link org.apache.commons.math3.ode.sampling.StepNormalizer StepNormalizer} object wrapping
+ * a user-specified object implementing the {@link
+ * org.apache.commons.math3.ode.sampling.FixedStepHandler FixedStepHandler} interface into the
+ * integrator before calling the {@link org.apache.commons.math3.ode.FirstOrderIntegrator#integrate
+ * FirstOrderIntegrator.integrate} method. The user object will be called appropriately during the
+ * integration process, allowing the user to process intermediate results. The default step handler
+ * does nothing.
+ *
+ * <p>{@link org.apache.commons.math3.ode.ContinuousOutputModel ContinuousOutputModel} is a
+ * special-purpose step handler that is able to store all steps and to provide transparent access to
+ * any intermediate result once the integration is over. An important feature of this class is that
+ * it implements the <code>Serializable</code> interface. This means that a complete continuous
+ * model of the integrated function throughout the integration range can be serialized and reused
+ * later (if stored into a persistent medium like a filesystem or a database) or elsewhere (if sent
+ * to another application). Only the result of the integration is stored, there is no reference to
+ * the integrated problem by itself.
+ *
+ * <p>Other default implementations of the {@link org.apache.commons.math3.ode.sampling.StepHandler
+ * StepHandler} interface are available for general needs ({@link
+ * org.apache.commons.math3.ode.sampling.DummyStepHandler DummyStepHandler}, {@link
+ * org.apache.commons.math3.ode.sampling.StepNormalizer StepNormalizer}) and custom implementations
+ * can be developed for specific needs. As an example, if an application is to be completely driven
+ * by the integration process, then most of the application code will be run inside a step handler
+ * specific to this application.
+ *
+ * <p>Some integrators (the simple ones) use fixed steps that are set at creation time. The more
+ * efficient integrators use variable steps that are handled internally in order to control the
+ * integration error with respect to a specified accuracy (these integrators extend the {@link
+ * org.apache.commons.math3.ode.nonstiff.AdaptiveStepsizeIntegrator AdaptiveStepsizeIntegrator}
+ * abstract class). In this case, the step handler which is called after each successful step shows
+ * up the variable stepsize. The {@link org.apache.commons.math3.ode.sampling.StepNormalizer
+ * StepNormalizer} class can be used to convert the variable stepsize into a fixed stepsize that can
+ * be handled by classes implementing the {@link
+ * org.apache.commons.math3.ode.sampling.FixedStepHandler FixedStepHandler} interface. Adaptive
+ * stepsize integrators can automatically compute the initial stepsize by themselves, however the
+ * user can specify it if he prefers to retain full control over the integration or if the automatic
+ * guess is wrong.
+ *
+ * <p>
+ *
+ * <table border="1" align="center">
+ * <tr BGCOLOR="#CCCCFF"><td colspan=2><font size="+2">Fixed Step Integrators</font></td></tr>
+ * <tr BGCOLOR="#EEEEFF"><font size="+1"><td>Name</td><td>Order</td></font></tr>
+ * <tr><td>{@link org.apache.commons.math3.ode.nonstiff.EulerIntegrator Euler}</td><td>1</td></tr>
+ * <tr><td>{@link org.apache.commons.math3.ode.nonstiff.MidpointIntegrator Midpoint}</td><td>2</td></tr>
+ * <tr><td>{@link org.apache.commons.math3.ode.nonstiff.ClassicalRungeKuttaIntegrator Classical Runge-Kutta}</td><td>4</td></tr>
+ * <tr><td>{@link org.apache.commons.math3.ode.nonstiff.GillIntegrator Gill}</td><td>4</td></tr>
+ * <tr><td>{@link org.apache.commons.math3.ode.nonstiff.ThreeEighthesIntegrator 3/8}</td><td>4</td></tr>
+ * <tr><td>{@link org.apache.commons.math3.ode.nonstiff.LutherIntegrator Luther}</td><td>6</td></tr>
+ * </table>
+ *
+ * <table border="1" align="center">
+ * <tr BGCOLOR="#CCCCFF"><td colspan=3><font size="+2">Adaptive Stepsize Integrators</font></td></tr>
+ * <tr BGCOLOR="#EEEEFF"><font size="+1"><td>Name</td><td>Integration Order</td><td>Error Estimation Order</td></font></tr>
+ * <tr><td>{@link org.apache.commons.math3.ode.nonstiff.HighamHall54Integrator Higham and Hall}</td><td>5</td><td>4</td></tr>
+ * <tr><td>{@link org.apache.commons.math3.ode.nonstiff.DormandPrince54Integrator Dormand-Prince 5(4)}</td><td>5</td><td>4</td></tr>
+ * <tr><td>{@link org.apache.commons.math3.ode.nonstiff.DormandPrince853Integrator Dormand-Prince 8(5,3)}</td><td>8</td><td>5 and 3</td></tr>
+ * <tr><td>{@link org.apache.commons.math3.ode.nonstiff.GraggBulirschStoerIntegrator Gragg-Bulirsch-Stoer}</td><td>variable (up to 18 by default)</td><td>variable</td></tr>
+ * <tr><td>{@link org.apache.commons.math3.ode.nonstiff.AdamsBashforthIntegrator Adams-Bashforth}</td><td>variable</td><td>variable</td></tr>
+ * <tr><td>{@link org.apache.commons.math3.ode.nonstiff.AdamsMoultonIntegrator Adams-Moulton}</td><td>variable</td><td>variable</td></tr>
+ * </table>
+ *
+ * <p>In the table above, the {@link org.apache.commons.math3.ode.nonstiff.AdamsBashforthIntegrator
+ * Adams-Bashforth} and {@link org.apache.commons.math3.ode.nonstiff.AdamsMoultonIntegrator
+ * Adams-Moulton} integrators appear as variable-step ones. This is an experimental extension to the
+ * classical algorithms using the Nordsieck vector representation.
+ */
+package org.apache.commons.math3.ode;
diff --git a/src/main/java/org/apache/commons/math3/ode/sampling/AbstractFieldStepInterpolator.java b/src/main/java/org/apache/commons/math3/ode/sampling/AbstractFieldStepInterpolator.java
new file mode 100644
index 0000000..e674752
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/ode/sampling/AbstractFieldStepInterpolator.java
@@ -0,0 +1,171 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math3.ode.sampling;
+
+import org.apache.commons.math3.RealFieldElement;
+import org.apache.commons.math3.exception.MaxCountExceededException;
+import org.apache.commons.math3.ode.FieldEquationsMapper;
+import org.apache.commons.math3.ode.FieldODEStateAndDerivative;
+
+/** This abstract class represents an interpolator over the last step
+ * during an ODE integration.
+ *
+ * <p>The various ODE integrators provide objects extending this class
+ * to the step handlers. The handlers can use these objects to
+ * retrieve the state vector at intermediate times between the
+ * previous and the current grid points (dense output).</p>
+ *
+ * @see org.apache.commons.math3.ode.FirstOrderFieldIntegrator
+ * @see StepHandler
+ *
+ * @param <T> the type of the field elements
+ * @since 3.6
+ */
+
+public abstract class AbstractFieldStepInterpolator<T extends RealFieldElement<T>>
+ implements FieldStepInterpolator<T> {
+
+ /** Global previous state. */
+ private final FieldODEStateAndDerivative<T> globalPreviousState;
+
+ /** Global current state. */
+ private final FieldODEStateAndDerivative<T> globalCurrentState;
+
+ /** Soft previous state. */
+ private final FieldODEStateAndDerivative<T> softPreviousState;
+
+ /** Soft current state. */
+ private final FieldODEStateAndDerivative<T> softCurrentState;
+
+ /** integration direction. */
+ private final boolean forward;
+
+ /** Mapper for ODE equations primary and secondary components. */
+ private FieldEquationsMapper<T> mapper;
+
+ /** Simple constructor.
+ * @param isForward integration direction indicator
+ * @param globalPreviousState start of the global step
+ * @param globalCurrentState end of the global step
+ * @param softPreviousState start of the restricted step
+ * @param softCurrentState end of the restricted step
+ * @param equationsMapper mapper for ODE equations primary and secondary components
+ */
+ protected AbstractFieldStepInterpolator(final boolean isForward,
+ final FieldODEStateAndDerivative<T> globalPreviousState,
+ final FieldODEStateAndDerivative<T> globalCurrentState,
+ final FieldODEStateAndDerivative<T> softPreviousState,
+ final FieldODEStateAndDerivative<T> softCurrentState,
+ final FieldEquationsMapper<T> equationsMapper) {
+ this.forward = isForward;
+ this.globalPreviousState = globalPreviousState;
+ this.globalCurrentState = globalCurrentState;
+ this.softPreviousState = softPreviousState;
+ this.softCurrentState = softCurrentState;
+ this.mapper = equationsMapper;
+ }
+
+ /** Create a new restricted version of the instance.
+ * <p>
+ * The instance is not changed at all.
+ * </p>
+ * @param previousState start of the restricted step
+ * @param currentState end of the restricted step
+ * @return restricted version of the instance
+ * @see #getPreviousState()
+ * @see #getCurrentState()
+ */
+ public AbstractFieldStepInterpolator<T> restrictStep(final FieldODEStateAndDerivative<T> previousState,
+ final FieldODEStateAndDerivative<T> currentState) {
+ return create(forward, globalPreviousState, globalCurrentState, previousState, currentState, mapper);
+ }
+
+ /** Create a new instance.
+ * @param newForward integration direction indicator
+ * @param newGlobalPreviousState start of the global step
+ * @param newGlobalCurrentState end of the global step
+ * @param newSoftPreviousState start of the restricted step
+ * @param newSoftCurrentState end of the restricted step
+ * @param newMapper equations mapper for the all equations
+ * @return a new instance
+ */
+ protected abstract AbstractFieldStepInterpolator<T> create(boolean newForward,
+ FieldODEStateAndDerivative<T> newGlobalPreviousState,
+ FieldODEStateAndDerivative<T> newGlobalCurrentState,
+ FieldODEStateAndDerivative<T> newSoftPreviousState,
+ FieldODEStateAndDerivative<T> newSoftCurrentState,
+ FieldEquationsMapper<T> newMapper);
+
+ /**
+ * Get the previous global grid point state.
+ * @return previous global grid point state
+ */
+ public FieldODEStateAndDerivative<T> getGlobalPreviousState() {
+ return globalPreviousState;
+ }
+
+ /**
+ * Get the current global grid point state.
+ * @return current global grid point state
+ */
+ public FieldODEStateAndDerivative<T> getGlobalCurrentState() {
+ return globalCurrentState;
+ }
+
+ /** {@inheritDoc} */
+ public FieldODEStateAndDerivative<T> getPreviousState() {
+ return softPreviousState;
+ }
+
+ /** {@inheritDoc} */
+ public FieldODEStateAndDerivative<T> getCurrentState() {
+ return softCurrentState;
+ }
+
+ /** {@inheritDoc} */
+ public FieldODEStateAndDerivative<T> getInterpolatedState(final T time) {
+ final T thetaH = time.subtract(globalPreviousState.getTime());
+ final T oneMinusThetaH = globalCurrentState.getTime().subtract(time);
+ final T theta = thetaH.divide(globalCurrentState.getTime().subtract(globalPreviousState.getTime()));
+ return computeInterpolatedStateAndDerivatives(mapper, time, theta, thetaH, oneMinusThetaH);
+ }
+
+ /** {@inheritDoc} */
+ public boolean isForward() {
+ return forward;
+ }
+
+ /** Compute the state and derivatives at the interpolated time.
+ * This is the main processing method that should be implemented by
+ * the derived classes to perform the interpolation.
+ * @param equationsMapper mapper for ODE equations primary and secondary components
+ * @param time interpolation time
+ * @param theta normalized interpolation abscissa within the step
+ * (theta is zero at the previous time step and one at the current time step)
+ * @param thetaH time gap between the previous time and the interpolated time
+ * @param oneMinusThetaH time gap between the interpolated time and
+ * the current time
+ * @return interpolated state and derivatives
+ * @exception MaxCountExceededException if the number of functions evaluations is exceeded
+ */
+ protected abstract FieldODEStateAndDerivative<T> computeInterpolatedStateAndDerivatives(FieldEquationsMapper<T> equationsMapper,
+ T time, T theta,
+ T thetaH, T oneMinusThetaH)
+ throws MaxCountExceededException;
+
+}
diff --git a/src/main/java/org/apache/commons/math3/ode/sampling/AbstractStepInterpolator.java b/src/main/java/org/apache/commons/math3/ode/sampling/AbstractStepInterpolator.java
new file mode 100644
index 0000000..1fbc04a
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/ode/sampling/AbstractStepInterpolator.java
@@ -0,0 +1,605 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math3.ode.sampling;
+
+import java.io.IOException;
+import java.io.ObjectInput;
+import java.io.ObjectOutput;
+
+import org.apache.commons.math3.exception.MaxCountExceededException;
+import org.apache.commons.math3.ode.EquationsMapper;
+
+/** This abstract class represents an interpolator over the last step
+ * during an ODE integration.
+ *
+ * <p>The various ODE integrators provide objects extending this class
+ * to the step handlers. The handlers can use these objects to
+ * retrieve the state vector at intermediate times between the
+ * previous and the current grid points (dense output).</p>
+ *
+ * @see org.apache.commons.math3.ode.FirstOrderIntegrator
+ * @see org.apache.commons.math3.ode.SecondOrderIntegrator
+ * @see StepHandler
+ *
+ * @since 1.2
+ *
+ */
+
+public abstract class AbstractStepInterpolator
+ implements StepInterpolator {
+
+ /** current time step */
+ protected double h;
+
+ /** current state */
+ protected double[] currentState;
+
+ /** interpolated time */
+ protected double interpolatedTime;
+
+ /** interpolated state */
+ protected double[] interpolatedState;
+
+ /** interpolated derivatives */
+ protected double[] interpolatedDerivatives;
+
+ /** interpolated primary state */
+ protected double[] interpolatedPrimaryState;
+
+ /** interpolated primary derivatives */
+ protected double[] interpolatedPrimaryDerivatives;
+
+ /** interpolated secondary state */
+ protected double[][] interpolatedSecondaryState;
+
+ /** interpolated secondary derivatives */
+ protected double[][] interpolatedSecondaryDerivatives;
+
+ /** global previous time */
+ private double globalPreviousTime;
+
+ /** global current time */
+ private double globalCurrentTime;
+
+ /** soft previous time */
+ private double softPreviousTime;
+
+ /** soft current time */
+ private double softCurrentTime;
+
+ /** indicate if the step has been finalized or not. */
+ private boolean finalized;
+
+ /** integration direction. */
+ private boolean forward;
+
+ /** indicator for dirty state. */
+ private boolean dirtyState;
+
+ /** Equations mapper for the primary equations set. */
+ private EquationsMapper primaryMapper;
+
+ /** Equations mappers for the secondary equations sets. */
+ private EquationsMapper[] secondaryMappers;
+
+ /** Simple constructor.
+ * This constructor builds an instance that is not usable yet, the
+ * {@link #reinitialize} method should be called before using the
+ * instance in order to initialize the internal arrays. This
+ * constructor is used only in order to delay the initialization in
+ * some cases. As an example, the {@link
+ * org.apache.commons.math3.ode.nonstiff.EmbeddedRungeKuttaIntegrator}
+ * class uses the prototyping design pattern to create the step
+ * interpolators by cloning an uninitialized model and latter
+ * initializing the copy.
+ */
+ protected AbstractStepInterpolator() {
+ globalPreviousTime = Double.NaN;
+ globalCurrentTime = Double.NaN;
+ softPreviousTime = Double.NaN;
+ softCurrentTime = Double.NaN;
+ h = Double.NaN;
+ interpolatedTime = Double.NaN;
+ currentState = null;
+ finalized = false;
+ this.forward = true;
+ this.dirtyState = true;
+ primaryMapper = null;
+ secondaryMappers = null;
+ allocateInterpolatedArrays(-1);
+ }
+
+ /** Simple constructor.
+ * @param y reference to the integrator array holding the state at
+ * the end of the step
+ * @param forward integration direction indicator
+ * @param primaryMapper equations mapper for the primary equations set
+ * @param secondaryMappers equations mappers for the secondary equations sets
+ */
+ protected AbstractStepInterpolator(final double[] y, final boolean forward,
+ final EquationsMapper primaryMapper,
+ final EquationsMapper[] secondaryMappers) {
+
+ globalPreviousTime = Double.NaN;
+ globalCurrentTime = Double.NaN;
+ softPreviousTime = Double.NaN;
+ softCurrentTime = Double.NaN;
+ h = Double.NaN;
+ interpolatedTime = Double.NaN;
+ currentState = y;
+ finalized = false;
+ this.forward = forward;
+ this.dirtyState = true;
+ this.primaryMapper = primaryMapper;
+ this.secondaryMappers = (secondaryMappers == null) ? null : secondaryMappers.clone();
+ allocateInterpolatedArrays(y.length);
+
+ }
+
+ /** Copy constructor.
+
+ * <p>The copied interpolator should have been finalized before the
+ * copy, otherwise the copy will not be able to perform correctly
+ * any derivative computation and will throw a {@link
+ * NullPointerException} later. Since we don't want this constructor
+ * to throw the exceptions finalization may involve and since we
+ * don't want this method to modify the state of the copied
+ * interpolator, finalization is <strong>not</strong> done
+ * automatically, it remains under user control.</p>
+
+ * <p>The copy is a deep copy: its arrays are separated from the
+ * original arrays of the instance.</p>
+
+ * @param interpolator interpolator to copy from.
+
+ */
+ protected AbstractStepInterpolator(final AbstractStepInterpolator interpolator) {
+
+ globalPreviousTime = interpolator.globalPreviousTime;
+ globalCurrentTime = interpolator.globalCurrentTime;
+ softPreviousTime = interpolator.softPreviousTime;
+ softCurrentTime = interpolator.softCurrentTime;
+ h = interpolator.h;
+ interpolatedTime = interpolator.interpolatedTime;
+
+ if (interpolator.currentState == null) {
+ currentState = null;
+ primaryMapper = null;
+ secondaryMappers = null;
+ allocateInterpolatedArrays(-1);
+ } else {
+ currentState = interpolator.currentState.clone();
+ interpolatedState = interpolator.interpolatedState.clone();
+ interpolatedDerivatives = interpolator.interpolatedDerivatives.clone();
+ interpolatedPrimaryState = interpolator.interpolatedPrimaryState.clone();
+ interpolatedPrimaryDerivatives = interpolator.interpolatedPrimaryDerivatives.clone();
+ interpolatedSecondaryState = new double[interpolator.interpolatedSecondaryState.length][];
+ interpolatedSecondaryDerivatives = new double[interpolator.interpolatedSecondaryDerivatives.length][];
+ for (int i = 0; i < interpolatedSecondaryState.length; ++i) {
+ interpolatedSecondaryState[i] = interpolator.interpolatedSecondaryState[i].clone();
+ interpolatedSecondaryDerivatives[i] = interpolator.interpolatedSecondaryDerivatives[i].clone();
+ }
+ }
+
+ finalized = interpolator.finalized;
+ forward = interpolator.forward;
+ dirtyState = interpolator.dirtyState;
+ primaryMapper = interpolator.primaryMapper;
+ secondaryMappers = (interpolator.secondaryMappers == null) ?
+ null : interpolator.secondaryMappers.clone();
+
+ }
+
+ /** Allocate the various interpolated states arrays.
+ * @param dimension total dimension (negative if arrays should be set to null)
+ */
+ private void allocateInterpolatedArrays(final int dimension) {
+ if (dimension < 0) {
+ interpolatedState = null;
+ interpolatedDerivatives = null;
+ interpolatedPrimaryState = null;
+ interpolatedPrimaryDerivatives = null;
+ interpolatedSecondaryState = null;
+ interpolatedSecondaryDerivatives = null;
+ } else {
+ interpolatedState = new double[dimension];
+ interpolatedDerivatives = new double[dimension];
+ interpolatedPrimaryState = new double[primaryMapper.getDimension()];
+ interpolatedPrimaryDerivatives = new double[primaryMapper.getDimension()];
+ if (secondaryMappers == null) {
+ interpolatedSecondaryState = null;
+ interpolatedSecondaryDerivatives = null;
+ } else {
+ interpolatedSecondaryState = new double[secondaryMappers.length][];
+ interpolatedSecondaryDerivatives = new double[secondaryMappers.length][];
+ for (int i = 0; i < secondaryMappers.length; ++i) {
+ interpolatedSecondaryState[i] = new double[secondaryMappers[i].getDimension()];
+ interpolatedSecondaryDerivatives[i] = new double[secondaryMappers[i].getDimension()];
+ }
+ }
+ }
+ }
+
+ /** Reinitialize the instance
+ * @param y reference to the integrator array holding the state at the end of the step
+ * @param isForward integration direction indicator
+ * @param primary equations mapper for the primary equations set
+ * @param secondary equations mappers for the secondary equations sets
+ */
+ protected void reinitialize(final double[] y, final boolean isForward,
+ final EquationsMapper primary,
+ final EquationsMapper[] secondary) {
+
+ globalPreviousTime = Double.NaN;
+ globalCurrentTime = Double.NaN;
+ softPreviousTime = Double.NaN;
+ softCurrentTime = Double.NaN;
+ h = Double.NaN;
+ interpolatedTime = Double.NaN;
+ currentState = y;
+ finalized = false;
+ this.forward = isForward;
+ this.dirtyState = true;
+ this.primaryMapper = primary;
+ this.secondaryMappers = secondary.clone();
+ allocateInterpolatedArrays(y.length);
+
+ }
+
+ /** {@inheritDoc} */
+ public StepInterpolator copy() throws MaxCountExceededException {
+
+ // finalize the step before performing copy
+ finalizeStep();
+
+ // create the new independent instance
+ return doCopy();
+
+ }
+
+ /** Really copy the finalized instance.
+ * <p>This method is called by {@link #copy()} after the
+ * step has been finalized. It must perform a deep copy
+ * to have an new instance completely independent for the
+ * original instance.
+ * @return a copy of the finalized instance
+ */
+ protected abstract StepInterpolator doCopy();
+
+ /** Shift one step forward.
+ * Copy the current time into the previous time, hence preparing the
+ * interpolator for future calls to {@link #storeTime storeTime}
+ */
+ public void shift() {
+ globalPreviousTime = globalCurrentTime;
+ softPreviousTime = globalPreviousTime;
+ softCurrentTime = globalCurrentTime;
+ }
+
+ /** Store the current step time.
+ * @param t current time
+ */
+ public void storeTime(final double t) {
+
+ globalCurrentTime = t;
+ softCurrentTime = globalCurrentTime;
+ h = globalCurrentTime - globalPreviousTime;
+ setInterpolatedTime(t);
+
+ // the step is not finalized anymore
+ finalized = false;
+
+ }
+
+ /** Restrict step range to a limited part of the global step.
+ * <p>
+ * This method can be used to restrict a step and make it appear
+ * as if the original step was smaller. Calling this method
+ * <em>only</em> changes the value returned by {@link #getPreviousTime()},
+ * it does not change any other property
+ * </p>
+ * @param softPreviousTime start of the restricted step
+ * @since 2.2
+ */
+ public void setSoftPreviousTime(final double softPreviousTime) {
+ this.softPreviousTime = softPreviousTime;
+ }
+
+ /** Restrict step range to a limited part of the global step.
+ * <p>
+ * This method can be used to restrict a step and make it appear
+ * as if the original step was smaller. Calling this method
+ * <em>only</em> changes the value returned by {@link #getCurrentTime()},
+ * it does not change any other property
+ * </p>
+ * @param softCurrentTime end of the restricted step
+ * @since 2.2
+ */
+ public void setSoftCurrentTime(final double softCurrentTime) {
+ this.softCurrentTime = softCurrentTime;
+ }
+
+ /**
+ * Get the previous global grid point time.
+ * @return previous global grid point time
+ */
+ public double getGlobalPreviousTime() {
+ return globalPreviousTime;
+ }
+
+ /**
+ * Get the current global grid point time.
+ * @return current global grid point time
+ */
+ public double getGlobalCurrentTime() {
+ return globalCurrentTime;
+ }
+
+ /**
+ * Get the previous soft grid point time.
+ * @return previous soft grid point time
+ * @see #setSoftPreviousTime(double)
+ */
+ public double getPreviousTime() {
+ return softPreviousTime;
+ }
+
+ /**
+ * Get the current soft grid point time.
+ * @return current soft grid point time
+ * @see #setSoftCurrentTime(double)
+ */
+ public double getCurrentTime() {
+ return softCurrentTime;
+ }
+
+ /** {@inheritDoc} */
+ public double getInterpolatedTime() {
+ return interpolatedTime;
+ }
+
+ /** {@inheritDoc} */
+ public void setInterpolatedTime(final double time) {
+ interpolatedTime = time;
+ dirtyState = true;
+ }
+
+ /** {@inheritDoc} */
+ public boolean isForward() {
+ return forward;
+ }
+
+ /** Compute the state and derivatives at the interpolated time.
+ * This is the main processing method that should be implemented by
+ * the derived classes to perform the interpolation.
+ * @param theta normalized interpolation abscissa within the step
+ * (theta is zero at the previous time step and one at the current time step)
+ * @param oneMinusThetaH time gap between the interpolated time and
+ * the current time
+ * @exception MaxCountExceededException if the number of functions evaluations is exceeded
+ */
+ protected abstract void computeInterpolatedStateAndDerivatives(double theta,
+ double oneMinusThetaH)
+ throws MaxCountExceededException;
+
+ /** Lazy evaluation of complete interpolated state.
+ * @exception MaxCountExceededException if the number of functions evaluations is exceeded
+ */
+ private void evaluateCompleteInterpolatedState()
+ throws MaxCountExceededException {
+ // lazy evaluation of the state
+ if (dirtyState) {
+ final double oneMinusThetaH = globalCurrentTime - interpolatedTime;
+ final double theta = (h == 0) ? 0 : (h - oneMinusThetaH) / h;
+ computeInterpolatedStateAndDerivatives(theta, oneMinusThetaH);
+ dirtyState = false;
+ }
+ }
+
+ /** {@inheritDoc} */
+ public double[] getInterpolatedState() throws MaxCountExceededException {
+ evaluateCompleteInterpolatedState();
+ primaryMapper.extractEquationData(interpolatedState,
+ interpolatedPrimaryState);
+ return interpolatedPrimaryState;
+ }
+
+ /** {@inheritDoc} */
+ public double[] getInterpolatedDerivatives() throws MaxCountExceededException {
+ evaluateCompleteInterpolatedState();
+ primaryMapper.extractEquationData(interpolatedDerivatives,
+ interpolatedPrimaryDerivatives);
+ return interpolatedPrimaryDerivatives;
+ }
+
+ /** {@inheritDoc} */
+ public double[] getInterpolatedSecondaryState(final int index) throws MaxCountExceededException {
+ evaluateCompleteInterpolatedState();
+ secondaryMappers[index].extractEquationData(interpolatedState,
+ interpolatedSecondaryState[index]);
+ return interpolatedSecondaryState[index];
+ }
+
+ /** {@inheritDoc} */
+ public double[] getInterpolatedSecondaryDerivatives(final int index) throws MaxCountExceededException {
+ evaluateCompleteInterpolatedState();
+ secondaryMappers[index].extractEquationData(interpolatedDerivatives,
+ interpolatedSecondaryDerivatives[index]);
+ return interpolatedSecondaryDerivatives[index];
+ }
+
+ /**
+ * Finalize the step.
+
+ * <p>Some embedded Runge-Kutta integrators need fewer functions
+ * evaluations than their counterpart step interpolators. These
+ * interpolators should perform the last evaluations they need by
+ * themselves only if they need them. This method triggers these
+ * extra evaluations. It can be called directly by the user step
+ * handler and it is called automatically if {@link
+ * #setInterpolatedTime} is called.</p>
+
+ * <p>Once this method has been called, <strong>no</strong> other
+ * evaluation will be performed on this step. If there is a need to
+ * have some side effects between the step handler and the
+ * differential equations (for example update some data in the
+ * equations once the step has been done), it is advised to call
+ * this method explicitly from the step handler before these side
+ * effects are set up. If the step handler induces no side effect,
+ * then this method can safely be ignored, it will be called
+ * transparently as needed.</p>
+
+ * <p><strong>Warning</strong>: since the step interpolator provided
+ * to the step handler as a parameter of the {@link
+ * StepHandler#handleStep handleStep} is valid only for the duration
+ * of the {@link StepHandler#handleStep handleStep} call, one cannot
+ * simply store a reference and reuse it later. One should first
+ * finalize the instance, then copy this finalized instance into a
+ * new object that can be kept.</p>
+
+ * <p>This method calls the protected <code>doFinalize</code> method
+ * if it has never been called during this step and set a flag
+ * indicating that it has been called once. It is the <code>
+ * doFinalize</code> method which should perform the evaluations.
+ * This wrapping prevents from calling <code>doFinalize</code> several
+ * times and hence evaluating the differential equations too often.
+ * Therefore, subclasses are not allowed not reimplement it, they
+ * should rather reimplement <code>doFinalize</code>.</p>
+
+ * @exception MaxCountExceededException if the number of functions evaluations is exceeded
+
+ */
+ public final void finalizeStep() throws MaxCountExceededException {
+ if (! finalized) {
+ doFinalize();
+ finalized = true;
+ }
+ }
+
+ /**
+ * Really finalize the step.
+ * The default implementation of this method does nothing.
+ * @exception MaxCountExceededException if the number of functions evaluations is exceeded
+ */
+ protected void doFinalize() throws MaxCountExceededException {
+ }
+
+ /** {@inheritDoc} */
+ public abstract void writeExternal(ObjectOutput out)
+ throws IOException;
+
+ /** {@inheritDoc} */
+ public abstract void readExternal(ObjectInput in)
+ throws IOException, ClassNotFoundException;
+
+ /** Save the base state of the instance.
+ * This method performs step finalization if it has not been done
+ * before.
+ * @param out stream where to save the state
+ * @exception IOException in case of write error
+ */
+ protected void writeBaseExternal(final ObjectOutput out)
+ throws IOException {
+
+ if (currentState == null) {
+ out.writeInt(-1);
+ } else {
+ out.writeInt(currentState.length);
+ }
+ out.writeDouble(globalPreviousTime);
+ out.writeDouble(globalCurrentTime);
+ out.writeDouble(softPreviousTime);
+ out.writeDouble(softCurrentTime);
+ out.writeDouble(h);
+ out.writeBoolean(forward);
+ out.writeObject(primaryMapper);
+ out.write(secondaryMappers.length);
+ for (final EquationsMapper mapper : secondaryMappers) {
+ out.writeObject(mapper);
+ }
+
+ if (currentState != null) {
+ for (int i = 0; i < currentState.length; ++i) {
+ out.writeDouble(currentState[i]);
+ }
+ }
+
+ out.writeDouble(interpolatedTime);
+
+ // we do not store the interpolated state,
+ // it will be recomputed as needed after reading
+
+ try {
+ // finalize the step (and don't bother saving the now true flag)
+ finalizeStep();
+ } catch (MaxCountExceededException mcee) {
+ final IOException ioe = new IOException(mcee.getLocalizedMessage());
+ ioe.initCause(mcee);
+ throw ioe;
+ }
+
+ }
+
+ /** Read the base state of the instance.
+ * This method does <strong>neither</strong> set the interpolated
+ * time nor state. It is up to the derived class to reset it
+ * properly calling the {@link #setInterpolatedTime} method later,
+ * once all rest of the object state has been set up properly.
+ * @param in stream where to read the state from
+ * @return interpolated time to be set later by the caller
+ * @exception IOException in case of read error
+ * @exception ClassNotFoundException if an equation mapper class
+ * cannot be found
+ */
+ protected double readBaseExternal(final ObjectInput in)
+ throws IOException, ClassNotFoundException {
+
+ final int dimension = in.readInt();
+ globalPreviousTime = in.readDouble();
+ globalCurrentTime = in.readDouble();
+ softPreviousTime = in.readDouble();
+ softCurrentTime = in.readDouble();
+ h = in.readDouble();
+ forward = in.readBoolean();
+ primaryMapper = (EquationsMapper) in.readObject();
+ secondaryMappers = new EquationsMapper[in.read()];
+ for (int i = 0; i < secondaryMappers.length; ++i) {
+ secondaryMappers[i] = (EquationsMapper) in.readObject();
+ }
+ dirtyState = true;
+
+ if (dimension < 0) {
+ currentState = null;
+ } else {
+ currentState = new double[dimension];
+ for (int i = 0; i < currentState.length; ++i) {
+ currentState[i] = in.readDouble();
+ }
+ }
+
+ // we do NOT handle the interpolated time and state here
+ interpolatedTime = Double.NaN;
+ allocateInterpolatedArrays(dimension);
+
+ finalized = true;
+
+ return in.readDouble();
+
+ }
+
+}
diff --git a/src/main/java/org/apache/commons/math3/ode/sampling/DummyStepHandler.java b/src/main/java/org/apache/commons/math3/ode/sampling/DummyStepHandler.java
new file mode 100644
index 0000000..c1d4fe3
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/ode/sampling/DummyStepHandler.java
@@ -0,0 +1,89 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math3.ode.sampling;
+
+/**
+ * This class is a step handler that does nothing.
+
+ * <p>This class is provided as a convenience for users who are only
+ * interested in the final state of an integration and not in the
+ * intermediate steps. Its handleStep method does nothing.</p>
+ *
+ * <p>Since this class has no internal state, it is implemented using
+ * the Singleton design pattern. This means that only one instance is
+ * ever created, which can be retrieved using the getInstance
+ * method. This explains why there is no public constructor.</p>
+ *
+ * @see StepHandler
+ * @since 1.2
+ */
+
+public class DummyStepHandler implements StepHandler {
+
+ /** Private constructor.
+ * The constructor is private to prevent users from creating
+ * instances (Singleton design-pattern).
+ */
+ private DummyStepHandler() {
+ }
+
+ /** Get the only instance.
+ * @return the only instance
+ */
+ public static DummyStepHandler getInstance() {
+ return LazyHolder.INSTANCE;
+ }
+
+ /** {@inheritDoc} */
+ public void init(double t0, double[] y0, double t) {
+ }
+
+ /**
+ * Handle the last accepted step.
+ * This method does nothing in this class.
+ * @param interpolator interpolator for the last accepted step. For
+ * efficiency purposes, the various integrators reuse the same
+ * object on each call, so if the instance wants to keep it across
+ * all calls (for example to provide at the end of the integration a
+ * continuous model valid throughout the integration range), it
+ * should build a local copy using the clone method and store this
+ * copy.
+ * @param isLast true if the step is the last one
+ */
+ public void handleStep(final StepInterpolator interpolator, final boolean isLast) {
+ }
+
+ // CHECKSTYLE: stop HideUtilityClassConstructor
+ /** Holder for the instance.
+ * <p>We use here the Initialization On Demand Holder Idiom.</p>
+ */
+ private static class LazyHolder {
+ /** Cached field instance. */
+ private static final DummyStepHandler INSTANCE = new DummyStepHandler();
+ }
+ // CHECKSTYLE: resume HideUtilityClassConstructor
+
+ /** Handle deserialization of the singleton.
+ * @return the singleton instance
+ */
+ private Object readResolve() {
+ // return the singleton instance
+ return LazyHolder.INSTANCE;
+ }
+
+}
diff --git a/src/main/java/org/apache/commons/math3/ode/sampling/FieldFixedStepHandler.java b/src/main/java/org/apache/commons/math3/ode/sampling/FieldFixedStepHandler.java
new file mode 100644
index 0000000..9d4fd70
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/ode/sampling/FieldFixedStepHandler.java
@@ -0,0 +1,69 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math3.ode.sampling;
+
+import org.apache.commons.math3.RealFieldElement;
+import org.apache.commons.math3.ode.FieldODEStateAndDerivative;
+
+/**
+ * This interface represents a handler that should be called after
+ * each successful fixed step.
+
+ * <p>This interface should be implemented by anyone who is interested
+ * in getting the solution of an ordinary differential equation at
+ * fixed time steps. Objects implementing this interface should be
+ * wrapped within an instance of {@link FieldStepNormalizer} that itself
+ * is used as the general {@link FieldStepHandler} by the integrator. The
+ * {@link FieldStepNormalizer} object is called according to the integrator
+ * internal algorithms and it calls objects implementing this
+ * interface as necessary at fixed time steps.</p>
+ *
+ * @see FieldStepHandler
+ * @see FieldStepNormalizer
+ * @see FieldStepInterpolator
+ * @param <T> the type of the field elements
+ * @since 3.6
+ */
+
+public interface FieldFixedStepHandler<T extends RealFieldElement<T>> {
+
+ /** Initialize step handler at the start of an ODE integration.
+ * <p>
+ * This method is called once at the start of the integration. It
+ * may be used by the step handler to initialize some internal data
+ * if needed.
+ * </p>
+ * @param initialState initial time, state vector and derivative
+ * @param finalTime target time for the integration
+ */
+ void init(FieldODEStateAndDerivative<T> initialState, T finalTime);
+
+ /**
+ * Handle the last accepted step
+ * @param state current value of the independent <i>time</i> variable,
+ * state vector and derivative
+ * For efficiency purposes, the {@link FieldStepNormalizer} class reuses
+ * the same array on each call, so if
+ * the instance wants to keep it across all calls (for example to
+ * provide at the end of the integration a complete array of all
+ * steps), it should build a local copy store this copy.
+ * @param isLast true if the step is the last one
+ */
+ void handleStep(FieldODEStateAndDerivative<T> state, boolean isLast);
+
+}
diff --git a/src/main/java/org/apache/commons/math3/ode/sampling/FieldStepHandler.java b/src/main/java/org/apache/commons/math3/ode/sampling/FieldStepHandler.java
new file mode 100644
index 0000000..c56911b
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/ode/sampling/FieldStepHandler.java
@@ -0,0 +1,75 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math3.ode.sampling;
+
+import org.apache.commons.math3.RealFieldElement;
+import org.apache.commons.math3.exception.MaxCountExceededException;
+import org.apache.commons.math3.ode.FieldODEStateAndDerivative;
+
+/**
+ * This interface represents a handler that should be called after
+ * each successful step.
+ *
+ * <p>The ODE integrators compute the evolution of the state vector at
+ * some grid points that depend on their own internal algorithm. Once
+ * they have found a new grid point (possibly after having computed
+ * several evaluation of the derivative at intermediate points), they
+ * provide it to objects implementing this interface. These objects
+ * typically either ignore the intermediate steps and wait for the
+ * last one, store the points in an ephemeris, or forward them to
+ * specialized processing or output methods.</p>
+ *
+ * @see org.apache.commons.math3.ode.FirstOrderFieldIntegrator
+ * @see FieldStepInterpolator
+ * @param <T> the type of the field elements
+ * @since 3.6
+ */
+
+public interface FieldStepHandler<T extends RealFieldElement<T>> {
+
+ /** Initialize step handler at the start of an ODE integration.
+ * <p>
+ * This method is called once at the start of the integration. It
+ * may be used by the step handler to initialize some internal data
+ * if needed.
+ * </p>
+ * @param initialState initial time, state vector and derivative
+ * @param finalTime target time for the integration
+ */
+ void init(FieldODEStateAndDerivative<T> initialState, T finalTime);
+
+ /**
+ * Handle the last accepted step
+ * @param interpolator interpolator for the last accepted step. For
+ * efficiency purposes, the various integrators reuse the same
+ * object on each call, so if the instance wants to keep it across
+ * all calls (for example to provide at the end of the integration a
+ * continuous model valid throughout the integration range, as the
+ * {@link org.apache.commons.math3.ode.ContinuousOutputModel
+ * ContinuousOutputModel} class does), it should build a local copy
+ * using the clone method of the interpolator and store this copy.
+ * Keeping only a reference to the interpolator and reusing it will
+ * result in unpredictable behavior (potentially crashing the application).
+ * @param isLast true if the step is the last one
+ * @exception MaxCountExceededException if the interpolator throws one because
+ * the number of functions evaluations is exceeded
+ */
+ void handleStep(FieldStepInterpolator<T> interpolator, boolean isLast)
+ throws MaxCountExceededException;
+
+}
diff --git a/src/main/java/org/apache/commons/math3/ode/sampling/FieldStepInterpolator.java b/src/main/java/org/apache/commons/math3/ode/sampling/FieldStepInterpolator.java
new file mode 100644
index 0000000..a005fb1
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/ode/sampling/FieldStepInterpolator.java
@@ -0,0 +1,76 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math3.ode.sampling;
+
+import org.apache.commons.math3.RealFieldElement;
+import org.apache.commons.math3.ode.FieldODEStateAndDerivative;
+
+/** This interface represents an interpolator over the last step
+ * during an ODE integration.
+ *
+ * <p>The various ODE integrators provide objects implementing this
+ * interface to the step handlers. These objects are often custom
+ * objects tightly bound to the integrator internal algorithms. The
+ * handlers can use these objects to retrieve the state vector at
+ * intermediate times between the previous and the current grid points
+ * (this feature is often called dense output).</p>
+ *
+ * @param <T> the type of the field elements
+ * @see org.apache.commons.math3.ode.FirstOrderFieldIntegrator
+ * @see FieldStepHandler
+ * @since 3.6
+ */
+
+public interface FieldStepInterpolator<T extends RealFieldElement<T>> {
+
+ /**
+ * Get the state at previous grid point time.
+ * @return state at previous grid point time
+ */
+ FieldODEStateAndDerivative<T> getPreviousState();
+
+ /**
+ * Get the state at current grid point time.
+ * @return state at current grid point time
+ */
+ FieldODEStateAndDerivative<T> getCurrentState();
+
+ /**
+ * Get the state at interpolated time.
+ * <p>Setting the time outside of the current step is allowed, but
+ * should be used with care since the accuracy of the interpolator will
+ * probably be very poor far from this step. This allowance has been
+ * added to simplify implementation of search algorithms near the
+ * step endpoints.</p>
+ * @param time time of the interpolated point
+ * @return state at interpolated time
+ */
+ FieldODEStateAndDerivative<T> getInterpolatedState(T time);
+
+ /** Check if the natural integration direction is forward.
+ * <p>This method provides the integration direction as specified by
+ * the integrator itself, it avoid some nasty problems in
+ * degenerated cases like null steps due to cancellation at step
+ * initialization, step control or discrete events
+ * triggering.</p>
+ * @return true if the integration variable (time) increases during
+ * integration
+ */
+ boolean isForward();
+
+}
diff --git a/src/main/java/org/apache/commons/math3/ode/sampling/FieldStepNormalizer.java b/src/main/java/org/apache/commons/math3/ode/sampling/FieldStepNormalizer.java
new file mode 100644
index 0000000..892fcf7
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/ode/sampling/FieldStepNormalizer.java
@@ -0,0 +1,273 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math3.ode.sampling;
+
+import org.apache.commons.math3.RealFieldElement;
+import org.apache.commons.math3.exception.MaxCountExceededException;
+import org.apache.commons.math3.ode.FieldODEStateAndDerivative;
+import org.apache.commons.math3.util.FastMath;
+import org.apache.commons.math3.util.Precision;
+
+/**
+ * This class wraps an object implementing {@link FieldFixedStepHandler}
+ * into a {@link FieldStepHandler}.
+
+ * <p>This wrapper allows to use fixed step handlers with general
+ * integrators which cannot guaranty their integration steps will
+ * remain constant and therefore only accept general step
+ * handlers.</p>
+ *
+ * <p>The stepsize used is selected at construction time. The {@link
+ * FieldFixedStepHandler#handleStep handleStep} method of the underlying
+ * {@link FieldFixedStepHandler} object is called at normalized times. The
+ * normalized times can be influenced by the {@link StepNormalizerMode} and
+ * {@link StepNormalizerBounds}.</p>
+ *
+ * <p>There is no constraint on the integrator, it can use any time step
+ * it needs (time steps longer or shorter than the fixed time step and
+ * non-integer ratios are all allowed).</p>
+ *
+ * <p>
+ * <table border="1" align="center">
+ * <tr BGCOLOR="#CCCCFF"><td colspan=6><font size="+2">Examples (step size = 0.5)</font></td></tr>
+ * <tr BGCOLOR="#EEEEFF"><font size="+1"><td>Start time</td><td>End time</td>
+ * <td>Direction</td><td>{@link StepNormalizerMode Mode}</td>
+ * <td>{@link StepNormalizerBounds Bounds}</td><td>Output</td></font></tr>
+ * <tr><td>0.3</td><td>3.1</td><td>forward</td><td>{@link StepNormalizerMode#INCREMENT INCREMENT}</td><td>{@link StepNormalizerBounds#NEITHER NEITHER}</td><td>0.8, 1.3, 1.8, 2.3, 2.8</td></tr>
+ * <tr><td>0.3</td><td>3.1</td><td>forward</td><td>{@link StepNormalizerMode#INCREMENT INCREMENT}</td><td>{@link StepNormalizerBounds#FIRST FIRST}</td><td>0.3, 0.8, 1.3, 1.8, 2.3, 2.8</td></tr>
+ * <tr><td>0.3</td><td>3.1</td><td>forward</td><td>{@link StepNormalizerMode#INCREMENT INCREMENT}</td><td>{@link StepNormalizerBounds#LAST LAST}</td><td>0.8, 1.3, 1.8, 2.3, 2.8, 3.1</td></tr>
+ * <tr><td>0.3</td><td>3.1</td><td>forward</td><td>{@link StepNormalizerMode#INCREMENT INCREMENT}</td><td>{@link StepNormalizerBounds#BOTH BOTH}</td><td>0.3, 0.8, 1.3, 1.8, 2.3, 2.8, 3.1</td></tr>
+ * <tr><td>0.3</td><td>3.1</td><td>forward</td><td>{@link StepNormalizerMode#MULTIPLES MULTIPLES}</td><td>{@link StepNormalizerBounds#NEITHER NEITHER}</td><td>0.5, 1.0, 1.5, 2.0, 2.5, 3.0</td></tr>
+ * <tr><td>0.3</td><td>3.1</td><td>forward</td><td>{@link StepNormalizerMode#MULTIPLES MULTIPLES}</td><td>{@link StepNormalizerBounds#FIRST FIRST}</td><td>0.3, 0.5, 1.0, 1.5, 2.0, 2.5, 3.0</td></tr>
+ * <tr><td>0.3</td><td>3.1</td><td>forward</td><td>{@link StepNormalizerMode#MULTIPLES MULTIPLES}</td><td>{@link StepNormalizerBounds#LAST LAST}</td><td>0.5, 1.0, 1.5, 2.0, 2.5, 3.0, 3.1</td></tr>
+ * <tr><td>0.3</td><td>3.1</td><td>forward</td><td>{@link StepNormalizerMode#MULTIPLES MULTIPLES}</td><td>{@link StepNormalizerBounds#BOTH BOTH}</td><td>0.3, 0.5, 1.0, 1.5, 2.0, 2.5, 3.0, 3.1</td></tr>
+ * <tr><td>0.0</td><td>3.0</td><td>forward</td><td>{@link StepNormalizerMode#INCREMENT INCREMENT}</td><td>{@link StepNormalizerBounds#NEITHER NEITHER}</td><td>0.5, 1.0, 1.5, 2.0, 2.5, 3.0</td></tr>
+ * <tr><td>0.0</td><td>3.0</td><td>forward</td><td>{@link StepNormalizerMode#INCREMENT INCREMENT}</td><td>{@link StepNormalizerBounds#FIRST FIRST}</td><td>0.0, 0.5, 1.0, 1.5, 2.0, 2.5, 3.0</td></tr>
+ * <tr><td>0.0</td><td>3.0</td><td>forward</td><td>{@link StepNormalizerMode#INCREMENT INCREMENT}</td><td>{@link StepNormalizerBounds#LAST LAST}</td><td>0.5, 1.0, 1.5, 2.0, 2.5, 3.0</td></tr>
+ * <tr><td>0.0</td><td>3.0</td><td>forward</td><td>{@link StepNormalizerMode#INCREMENT INCREMENT}</td><td>{@link StepNormalizerBounds#BOTH BOTH}</td><td>0.0, 0.5, 1.0, 1.5, 2.0, 2.5, 3.0</td></tr>
+ * <tr><td>0.0</td><td>3.0</td><td>forward</td><td>{@link StepNormalizerMode#MULTIPLES MULTIPLES}</td><td>{@link StepNormalizerBounds#NEITHER NEITHER}</td><td>0.5, 1.0, 1.5, 2.0, 2.5, 3.0</td></tr>
+ * <tr><td>0.0</td><td>3.0</td><td>forward</td><td>{@link StepNormalizerMode#MULTIPLES MULTIPLES}</td><td>{@link StepNormalizerBounds#FIRST FIRST}</td><td>0.0, 0.5, 1.0, 1.5, 2.0, 2.5, 3.0</td></tr>
+ * <tr><td>0.0</td><td>3.0</td><td>forward</td><td>{@link StepNormalizerMode#MULTIPLES MULTIPLES}</td><td>{@link StepNormalizerBounds#LAST LAST}</td><td>0.5, 1.0, 1.5, 2.0, 2.5, 3.0</td></tr>
+ * <tr><td>0.0</td><td>3.0</td><td>forward</td><td>{@link StepNormalizerMode#MULTIPLES MULTIPLES}</td><td>{@link StepNormalizerBounds#BOTH BOTH}</td><td>0.0, 0.5, 1.0, 1.5, 2.0, 2.5, 3.0</td></tr>
+ * <tr><td>3.1</td><td>0.3</td><td>backward</td><td>{@link StepNormalizerMode#INCREMENT INCREMENT}</td><td>{@link StepNormalizerBounds#NEITHER NEITHER}</td><td>2.6, 2.1, 1.6, 1.1, 0.6</td></tr>
+ * <tr><td>3.1</td><td>0.3</td><td>backward</td><td>{@link StepNormalizerMode#INCREMENT INCREMENT}</td><td>{@link StepNormalizerBounds#FIRST FIRST}</td><td>3.1, 2.6, 2.1, 1.6, 1.1, 0.6</td></tr>
+ * <tr><td>3.1</td><td>0.3</td><td>backward</td><td>{@link StepNormalizerMode#INCREMENT INCREMENT}</td><td>{@link StepNormalizerBounds#LAST LAST}</td><td>2.6, 2.1, 1.6, 1.1, 0.6, 0.3</td></tr>
+ * <tr><td>3.1</td><td>0.3</td><td>backward</td><td>{@link StepNormalizerMode#INCREMENT INCREMENT}</td><td>{@link StepNormalizerBounds#BOTH BOTH}</td><td>3.1, 2.6, 2.1, 1.6, 1.1, 0.6, 0.3</td></tr>
+ * <tr><td>3.1</td><td>0.3</td><td>backward</td><td>{@link StepNormalizerMode#MULTIPLES MULTIPLES}</td><td>{@link StepNormalizerBounds#NEITHER NEITHER}</td><td>3.0, 2.5, 2.0, 1.5, 1.0, 0.5</td></tr>
+ * <tr><td>3.1</td><td>0.3</td><td>backward</td><td>{@link StepNormalizerMode#MULTIPLES MULTIPLES}</td><td>{@link StepNormalizerBounds#FIRST FIRST}</td><td>3.1, 3.0, 2.5, 2.0, 1.5, 1.0, 0.5</td></tr>
+ * <tr><td>3.1</td><td>0.3</td><td>backward</td><td>{@link StepNormalizerMode#MULTIPLES MULTIPLES}</td><td>{@link StepNormalizerBounds#LAST LAST}</td><td>3.0, 2.5, 2.0, 1.5, 1.0, 0.5, 0.3</td></tr>
+ * <tr><td>3.1</td><td>0.3</td><td>backward</td><td>{@link StepNormalizerMode#MULTIPLES MULTIPLES}</td><td>{@link StepNormalizerBounds#BOTH BOTH}</td><td>3.1, 3.0, 2.5, 2.0, 1.5, 1.0, 0.5, 0.3</td></tr>
+ * <tr><td>3.0</td><td>0.0</td><td>backward</td><td>{@link StepNormalizerMode#INCREMENT INCREMENT}</td><td>{@link StepNormalizerBounds#NEITHER NEITHER}</td><td>2.5, 2.0, 1.5, 1.0, 0.5, 0.0</td></tr>
+ * <tr><td>3.0</td><td>0.0</td><td>backward</td><td>{@link StepNormalizerMode#INCREMENT INCREMENT}</td><td>{@link StepNormalizerBounds#FIRST FIRST}</td><td>3.0, 2.5, 2.0, 1.5, 1.0, 0.5, 0.0</td></tr>
+ * <tr><td>3.0</td><td>0.0</td><td>backward</td><td>{@link StepNormalizerMode#INCREMENT INCREMENT}</td><td>{@link StepNormalizerBounds#LAST LAST}</td><td>2.5, 2.0, 1.5, 1.0, 0.5, 0.0</td></tr>
+ * <tr><td>3.0</td><td>0.0</td><td>backward</td><td>{@link StepNormalizerMode#INCREMENT INCREMENT}</td><td>{@link StepNormalizerBounds#BOTH BOTH}</td><td>3.0, 2.5, 2.0, 1.5, 1.0, 0.5, 0.0</td></tr>
+ * <tr><td>3.0</td><td>0.0</td><td>backward</td><td>{@link StepNormalizerMode#MULTIPLES MULTIPLES}</td><td>{@link StepNormalizerBounds#NEITHER NEITHER}</td><td>2.5, 2.0, 1.5, 1.0, 0.5, 0.0</td></tr>
+ * <tr><td>3.0</td><td>0.0</td><td>backward</td><td>{@link StepNormalizerMode#MULTIPLES MULTIPLES}</td><td>{@link StepNormalizerBounds#FIRST FIRST}</td><td>3.0, 2.5, 2.0, 1.5, 1.0, 0.5, 0.0</td></tr>
+ * <tr><td>3.0</td><td>0.0</td><td>backward</td><td>{@link StepNormalizerMode#MULTIPLES MULTIPLES}</td><td>{@link StepNormalizerBounds#LAST LAST}</td><td>2.5, 2.0, 1.5, 1.0, 0.5, 0.0</td></tr>
+ * <tr><td>3.0</td><td>0.0</td><td>backward</td><td>{@link StepNormalizerMode#MULTIPLES MULTIPLES}</td><td>{@link StepNormalizerBounds#BOTH BOTH}</td><td>3.0, 2.5, 2.0, 1.5, 1.0, 0.5, 0.0</td></tr>
+ * </table>
+ * </p>
+ *
+ * @param <T> the type of the field elements
+ * @see FieldStepHandler
+ * @see FieldFixedStepHandler
+ * @see StepNormalizerMode
+ * @see StepNormalizerBounds
+ * @since 3.6
+ */
+
+public class FieldStepNormalizer<T extends RealFieldElement<T>> implements FieldStepHandler<T> {
+
+ /** Fixed time step. */
+ private double h;
+
+ /** Underlying step handler. */
+ private final FieldFixedStepHandler<T> handler;
+
+ /** First step state. */
+ private FieldODEStateAndDerivative<T> first;
+
+ /** Last step step. */
+ private FieldODEStateAndDerivative<T> last;
+
+ /** Integration direction indicator. */
+ private boolean forward;
+
+ /** The step normalizer bounds settings to use. */
+ private final StepNormalizerBounds bounds;
+
+ /** The step normalizer mode to use. */
+ private final StepNormalizerMode mode;
+
+ /** Simple constructor. Uses {@link StepNormalizerMode#INCREMENT INCREMENT}
+ * mode, and {@link StepNormalizerBounds#FIRST FIRST} bounds setting, for
+ * backwards compatibility.
+ * @param h fixed time step (sign is not used)
+ * @param handler fixed time step handler to wrap
+ */
+ public FieldStepNormalizer(final double h, final FieldFixedStepHandler<T> handler) {
+ this(h, handler, StepNormalizerMode.INCREMENT,
+ StepNormalizerBounds.FIRST);
+ }
+
+ /** Simple constructor. Uses {@link StepNormalizerBounds#FIRST FIRST}
+ * bounds setting.
+ * @param h fixed time step (sign is not used)
+ * @param handler fixed time step handler to wrap
+ * @param mode step normalizer mode to use
+ * @since 3.0
+ */
+ public FieldStepNormalizer(final double h, final FieldFixedStepHandler<T> handler,
+ final StepNormalizerMode mode) {
+ this(h, handler, mode, StepNormalizerBounds.FIRST);
+ }
+
+ /** Simple constructor. Uses {@link StepNormalizerMode#INCREMENT INCREMENT}
+ * mode.
+ * @param h fixed time step (sign is not used)
+ * @param handler fixed time step handler to wrap
+ * @param bounds step normalizer bounds setting to use
+ * @since 3.0
+ */
+ public FieldStepNormalizer(final double h, final FieldFixedStepHandler<T> handler,
+ final StepNormalizerBounds bounds) {
+ this(h, handler, StepNormalizerMode.INCREMENT, bounds);
+ }
+
+ /** Simple constructor.
+ * @param h fixed time step (sign is not used)
+ * @param handler fixed time step handler to wrap
+ * @param mode step normalizer mode to use
+ * @param bounds step normalizer bounds setting to use
+ * @since 3.0
+ */
+ public FieldStepNormalizer(final double h, final FieldFixedStepHandler<T> handler,
+ final StepNormalizerMode mode, final StepNormalizerBounds bounds) {
+ this.h = FastMath.abs(h);
+ this.handler = handler;
+ this.mode = mode;
+ this.bounds = bounds;
+ first = null;
+ last = null;
+ forward = true;
+ }
+
+ /** {@inheritDoc} */
+ public void init(final FieldODEStateAndDerivative<T> initialState, final T finalTime) {
+
+ first = null;
+ last = null;
+ forward = true;
+
+ // initialize the underlying handler
+ handler.init(initialState, finalTime);
+
+ }
+
+ /**
+ * Handle the last accepted step
+ * @param interpolator interpolator for the last accepted step. For
+ * efficiency purposes, the various integrators reuse the same
+ * object on each call, so if the instance wants to keep it across
+ * all calls (for example to provide at the end of the integration a
+ * continuous model valid throughout the integration range), it
+ * should build a local copy using the clone method and store this
+ * copy.
+ * @param isLast true if the step is the last one
+ * @exception MaxCountExceededException if the interpolator throws one because
+ * the number of functions evaluations is exceeded
+ */
+ public void handleStep(final FieldStepInterpolator<T> interpolator, final boolean isLast)
+ throws MaxCountExceededException {
+ // The first time, update the last state with the start information.
+ if (last == null) {
+
+ first = interpolator.getPreviousState();
+ last = first;
+
+ // Take the integration direction into account.
+ forward = interpolator.isForward();
+ if (!forward) {
+ h = -h;
+ }
+ }
+
+ // Calculate next normalized step time.
+ T nextTime = (mode == StepNormalizerMode.INCREMENT) ?
+ last.getTime().add(h) :
+ last.getTime().getField().getZero().add((FastMath.floor(last.getTime().getReal() / h) + 1) * h);
+ if (mode == StepNormalizerMode.MULTIPLES &&
+ Precision.equals(nextTime.getReal(), last.getTime().getReal(), 1)) {
+ nextTime = nextTime.add(h);
+ }
+
+ // Process normalized steps as long as they are in the current step.
+ boolean nextInStep = isNextInStep(nextTime, interpolator);
+ while (nextInStep) {
+ // Output the stored previous step.
+ doNormalizedStep(false);
+
+ // Store the next step as last step.
+ last = interpolator.getInterpolatedState(nextTime);
+
+ // Move on to the next step.
+ nextTime = nextTime.add(h);
+ nextInStep = isNextInStep(nextTime, interpolator);
+ }
+
+ if (isLast) {
+ // There will be no more steps. The stored one should be given to
+ // the handler. We may have to output one more step. Only the last
+ // one of those should be flagged as being the last.
+ final boolean addLast = bounds.lastIncluded() &&
+ last.getTime().getReal() != interpolator.getCurrentState().getTime().getReal();
+ doNormalizedStep(!addLast);
+ if (addLast) {
+ last = interpolator.getCurrentState();
+ doNormalizedStep(true);
+ }
+ }
+ }
+
+ /**
+ * Returns a value indicating whether the next normalized time is in the
+ * current step.
+ * @param nextTime the next normalized time
+ * @param interpolator interpolator for the last accepted step, to use to
+ * get the end time of the current step
+ * @return value indicating whether the next normalized time is in the
+ * current step
+ */
+ private boolean isNextInStep(final T nextTime, final FieldStepInterpolator<T> interpolator) {
+ return forward ?
+ nextTime.getReal() <= interpolator.getCurrentState().getTime().getReal() :
+ nextTime.getReal() >= interpolator.getCurrentState().getTime().getReal();
+ }
+
+ /**
+ * Invokes the underlying step handler for the current normalized step.
+ * @param isLast true if the step is the last one
+ */
+ private void doNormalizedStep(final boolean isLast) {
+ if (!bounds.firstIncluded() && first.getTime().getReal() == last.getTime().getReal()) {
+ return;
+ }
+ handler.handleStep(last, isLast);
+ }
+
+}
diff --git a/src/main/java/org/apache/commons/math3/ode/sampling/FixedStepHandler.java b/src/main/java/org/apache/commons/math3/ode/sampling/FixedStepHandler.java
new file mode 100644
index 0000000..bbe6ac5
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/ode/sampling/FixedStepHandler.java
@@ -0,0 +1,71 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math3.ode.sampling;
+
+
+/**
+ * This interface represents a handler that should be called after
+ * each successful fixed step.
+
+ * <p>This interface should be implemented by anyone who is interested
+ * in getting the solution of an ordinary differential equation at
+ * fixed time steps. Objects implementing this interface should be
+ * wrapped within an instance of {@link StepNormalizer} that itself
+ * is used as the general {@link StepHandler} by the integrator. The
+ * {@link StepNormalizer} object is called according to the integrator
+ * internal algorithms and it calls objects implementing this
+ * interface as necessary at fixed time steps.</p>
+ *
+ * @see StepHandler
+ * @see StepNormalizer
+ * @since 1.2
+ */
+
+public interface FixedStepHandler {
+
+ /** Initialize step handler at the start of an ODE integration.
+ * <p>
+ * This method is called once at the start of the integration. It
+ * may be used by the step handler to initialize some internal data
+ * if needed.
+ * </p>
+ * @param t0 start value of the independent <i>time</i> variable
+ * @param y0 array containing the start value of the state vector
+ * @param t target time for the integration
+ */
+ void init(double t0, double[] y0, double t);
+
+ /**
+ * Handle the last accepted step
+ * @param t time of the current step
+ * @param y state vector at t. For efficiency purposes, the {@link
+ * StepNormalizer} class reuses the same array on each call, so if
+ * the instance wants to keep it across all calls (for example to
+ * provide at the end of the integration a complete array of all
+ * steps), it should build a local copy store this copy.
+ * @param yDot derivatives of the state vector state vector at t.
+ * For efficiency purposes, the {@link StepNormalizer} class reuses
+ * the same array on each call, so if
+ * the instance wants to keep it across all calls (for example to
+ * provide at the end of the integration a complete array of all
+ * steps), it should build a local copy store this copy.
+ * @param isLast true if the step is the last one
+ */
+ void handleStep(double t, double[] y, double[] yDot, boolean isLast);
+
+}
diff --git a/src/main/java/org/apache/commons/math3/ode/sampling/NordsieckStepInterpolator.java b/src/main/java/org/apache/commons/math3/ode/sampling/NordsieckStepInterpolator.java
new file mode 100644
index 0000000..39b05ab
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/ode/sampling/NordsieckStepInterpolator.java
@@ -0,0 +1,293 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math3.ode.sampling;
+
+import java.io.IOException;
+import java.io.ObjectInput;
+import java.io.ObjectOutput;
+import java.util.Arrays;
+
+import org.apache.commons.math3.exception.MaxCountExceededException;
+import org.apache.commons.math3.linear.Array2DRowRealMatrix;
+import org.apache.commons.math3.ode.EquationsMapper;
+import org.apache.commons.math3.util.FastMath;
+
+/**
+ * This class implements an interpolator for integrators using Nordsieck representation.
+ *
+ * <p>This interpolator computes dense output around the current point.
+ * The interpolation equation is based on Taylor series formulas.
+ *
+ * @see org.apache.commons.math3.ode.nonstiff.AdamsBashforthIntegrator
+ * @see org.apache.commons.math3.ode.nonstiff.AdamsMoultonIntegrator
+ * @since 2.0
+ */
+
+public class NordsieckStepInterpolator extends AbstractStepInterpolator {
+
+ /** Serializable version identifier */
+ private static final long serialVersionUID = -7179861704951334960L;
+
+ /** State variation. */
+ protected double[] stateVariation;
+
+ /** Step size used in the first scaled derivative and Nordsieck vector. */
+ private double scalingH;
+
+ /** Reference time for all arrays.
+ * <p>Sometimes, the reference time is the same as previousTime,
+ * sometimes it is the same as currentTime, so we use a separate
+ * field to avoid any confusion.
+ * </p>
+ */
+ private double referenceTime;
+
+ /** First scaled derivative. */
+ private double[] scaled;
+
+ /** Nordsieck vector. */
+ private Array2DRowRealMatrix nordsieck;
+
+ /** Simple constructor.
+ * This constructor builds an instance that is not usable yet, the
+ * {@link AbstractStepInterpolator#reinitialize} method should be called
+ * before using the instance in order to initialize the internal arrays. This
+ * constructor is used only in order to delay the initialization in
+ * some cases.
+ */
+ public NordsieckStepInterpolator() {
+ }
+
+ /** Copy constructor.
+ * @param interpolator interpolator to copy from. The copy is a deep
+ * copy: its arrays are separated from the original arrays of the
+ * instance
+ */
+ public NordsieckStepInterpolator(final NordsieckStepInterpolator interpolator) {
+ super(interpolator);
+ scalingH = interpolator.scalingH;
+ referenceTime = interpolator.referenceTime;
+ if (interpolator.scaled != null) {
+ scaled = interpolator.scaled.clone();
+ }
+ if (interpolator.nordsieck != null) {
+ nordsieck = new Array2DRowRealMatrix(interpolator.nordsieck.getDataRef(), true);
+ }
+ if (interpolator.stateVariation != null) {
+ stateVariation = interpolator.stateVariation.clone();
+ }
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ protected StepInterpolator doCopy() {
+ return new NordsieckStepInterpolator(this);
+ }
+
+ /** Reinitialize the instance.
+ * <p>Beware that all arrays <em>must</em> be references to integrator
+ * arrays, in order to ensure proper update without copy.</p>
+ * @param y reference to the integrator array holding the state at
+ * the end of the step
+ * @param forward integration direction indicator
+ * @param primaryMapper equations mapper for the primary equations set
+ * @param secondaryMappers equations mappers for the secondary equations sets
+ */
+ @Override
+ public void reinitialize(final double[] y, final boolean forward,
+ final EquationsMapper primaryMapper,
+ final EquationsMapper[] secondaryMappers) {
+ super.reinitialize(y, forward, primaryMapper, secondaryMappers);
+ stateVariation = new double[y.length];
+ }
+
+ /** Reinitialize the instance.
+ * <p>Beware that all arrays <em>must</em> be references to integrator
+ * arrays, in order to ensure proper update without copy.</p>
+ * @param time time at which all arrays are defined
+ * @param stepSize step size used in the scaled and Nordsieck arrays
+ * @param scaledDerivative reference to the integrator array holding the first
+ * scaled derivative
+ * @param nordsieckVector reference to the integrator matrix holding the
+ * Nordsieck vector
+ */
+ public void reinitialize(final double time, final double stepSize,
+ final double[] scaledDerivative,
+ final Array2DRowRealMatrix nordsieckVector) {
+ this.referenceTime = time;
+ this.scalingH = stepSize;
+ this.scaled = scaledDerivative;
+ this.nordsieck = nordsieckVector;
+
+ // make sure the state and derivatives will depend on the new arrays
+ setInterpolatedTime(getInterpolatedTime());
+
+ }
+
+ /** Rescale the instance.
+ * <p>Since the scaled and Nordsieck arrays are shared with the caller,
+ * this method has the side effect of rescaling this arrays in the caller too.</p>
+ * @param stepSize new step size to use in the scaled and Nordsieck arrays
+ */
+ public void rescale(final double stepSize) {
+
+ final double ratio = stepSize / scalingH;
+ for (int i = 0; i < scaled.length; ++i) {
+ scaled[i] *= ratio;
+ }
+
+ final double[][] nData = nordsieck.getDataRef();
+ double power = ratio;
+ for (int i = 0; i < nData.length; ++i) {
+ power *= ratio;
+ final double[] nDataI = nData[i];
+ for (int j = 0; j < nDataI.length; ++j) {
+ nDataI[j] *= power;
+ }
+ }
+
+ scalingH = stepSize;
+
+ }
+
+ /**
+ * Get the state vector variation from current to interpolated state.
+ * <p>This method is aimed at computing y(t<sub>interpolation</sub>)
+ * -y(t<sub>current</sub>) accurately by avoiding the cancellation errors
+ * that would occur if the subtraction were performed explicitly.</p>
+ * <p>The returned vector is a reference to a reused array, so
+ * it should not be modified and it should be copied if it needs
+ * to be preserved across several calls.</p>
+ * @return state vector at time {@link #getInterpolatedTime}
+ * @see #getInterpolatedDerivatives()
+ * @exception MaxCountExceededException if the number of functions evaluations is exceeded
+ */
+ public double[] getInterpolatedStateVariation() throws MaxCountExceededException {
+ // compute and ignore interpolated state
+ // to make sure state variation is computed as a side effect
+ getInterpolatedState();
+ return stateVariation;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ protected void computeInterpolatedStateAndDerivatives(final double theta, final double oneMinusThetaH) {
+
+ final double x = interpolatedTime - referenceTime;
+ final double normalizedAbscissa = x / scalingH;
+
+ Arrays.fill(stateVariation, 0.0);
+ Arrays.fill(interpolatedDerivatives, 0.0);
+
+ // apply Taylor formula from high order to low order,
+ // for the sake of numerical accuracy
+ final double[][] nData = nordsieck.getDataRef();
+ for (int i = nData.length - 1; i >= 0; --i) {
+ final int order = i + 2;
+ final double[] nDataI = nData[i];
+ final double power = FastMath.pow(normalizedAbscissa, order);
+ for (int j = 0; j < nDataI.length; ++j) {
+ final double d = nDataI[j] * power;
+ stateVariation[j] += d;
+ interpolatedDerivatives[j] += order * d;
+ }
+ }
+
+ for (int j = 0; j < currentState.length; ++j) {
+ stateVariation[j] += scaled[j] * normalizedAbscissa;
+ interpolatedState[j] = currentState[j] + stateVariation[j];
+ interpolatedDerivatives[j] =
+ (interpolatedDerivatives[j] + scaled[j] * normalizedAbscissa) / x;
+ }
+
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void writeExternal(final ObjectOutput out)
+ throws IOException {
+
+ // save the state of the base class
+ writeBaseExternal(out);
+
+ // save the local attributes
+ out.writeDouble(scalingH);
+ out.writeDouble(referenceTime);
+
+ final int n = (currentState == null) ? -1 : currentState.length;
+ if (scaled == null) {
+ out.writeBoolean(false);
+ } else {
+ out.writeBoolean(true);
+ for (int j = 0; j < n; ++j) {
+ out.writeDouble(scaled[j]);
+ }
+ }
+
+ if (nordsieck == null) {
+ out.writeBoolean(false);
+ } else {
+ out.writeBoolean(true);
+ out.writeObject(nordsieck);
+ }
+
+ // we don't save state variation, it will be recomputed
+
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void readExternal(final ObjectInput in)
+ throws IOException, ClassNotFoundException {
+
+ // read the base class
+ final double t = readBaseExternal(in);
+
+ // read the local attributes
+ scalingH = in.readDouble();
+ referenceTime = in.readDouble();
+
+ final int n = (currentState == null) ? -1 : currentState.length;
+ final boolean hasScaled = in.readBoolean();
+ if (hasScaled) {
+ scaled = new double[n];
+ for (int j = 0; j < n; ++j) {
+ scaled[j] = in.readDouble();
+ }
+ } else {
+ scaled = null;
+ }
+
+ final boolean hasNordsieck = in.readBoolean();
+ if (hasNordsieck) {
+ nordsieck = (Array2DRowRealMatrix) in.readObject();
+ } else {
+ nordsieck = null;
+ }
+
+ if (hasScaled && hasNordsieck) {
+ // we can now set the interpolated time and state
+ stateVariation = new double[n];
+ setInterpolatedTime(t);
+ } else {
+ stateVariation = null;
+ }
+
+ }
+
+}
diff --git a/src/main/java/org/apache/commons/math3/ode/sampling/StepHandler.java b/src/main/java/org/apache/commons/math3/ode/sampling/StepHandler.java
new file mode 100644
index 0000000..f4c63df
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/ode/sampling/StepHandler.java
@@ -0,0 +1,75 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math3.ode.sampling;
+
+import org.apache.commons.math3.exception.MaxCountExceededException;
+
+
+/**
+ * This interface represents a handler that should be called after
+ * each successful step.
+ *
+ * <p>The ODE integrators compute the evolution of the state vector at
+ * some grid points that depend on their own internal algorithm. Once
+ * they have found a new grid point (possibly after having computed
+ * several evaluation of the derivative at intermediate points), they
+ * provide it to objects implementing this interface. These objects
+ * typically either ignore the intermediate steps and wait for the
+ * last one, store the points in an ephemeris, or forward them to
+ * specialized processing or output methods.</p>
+ *
+ * @see org.apache.commons.math3.ode.FirstOrderIntegrator
+ * @see org.apache.commons.math3.ode.SecondOrderIntegrator
+ * @see StepInterpolator
+ * @since 1.2
+ */
+
+public interface StepHandler {
+
+ /** Initialize step handler at the start of an ODE integration.
+ * <p>
+ * This method is called once at the start of the integration. It
+ * may be used by the step handler to initialize some internal data
+ * if needed.
+ * </p>
+ * @param t0 start value of the independent <i>time</i> variable
+ * @param y0 array containing the start value of the state vector
+ * @param t target time for the integration
+ */
+ void init(double t0, double[] y0, double t);
+
+ /**
+ * Handle the last accepted step
+ * @param interpolator interpolator for the last accepted step. For
+ * efficiency purposes, the various integrators reuse the same
+ * object on each call, so if the instance wants to keep it across
+ * all calls (for example to provide at the end of the integration a
+ * continuous model valid throughout the integration range, as the
+ * {@link org.apache.commons.math3.ode.ContinuousOutputModel
+ * ContinuousOutputModel} class does), it should build a local copy
+ * using the clone method of the interpolator and store this copy.
+ * Keeping only a reference to the interpolator and reusing it will
+ * result in unpredictable behavior (potentially crashing the application).
+ * @param isLast true if the step is the last one
+ * @exception MaxCountExceededException if the interpolator throws one because
+ * the number of functions evaluations is exceeded
+ */
+ void handleStep(StepInterpolator interpolator, boolean isLast)
+ throws MaxCountExceededException;
+
+}
diff --git a/src/main/java/org/apache/commons/math3/ode/sampling/StepInterpolator.java b/src/main/java/org/apache/commons/math3/ode/sampling/StepInterpolator.java
new file mode 100644
index 0000000..5d27bf2
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/ode/sampling/StepInterpolator.java
@@ -0,0 +1,181 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math3.ode.sampling;
+
+import java.io.Externalizable;
+
+import org.apache.commons.math3.exception.MaxCountExceededException;
+
+/** This interface represents an interpolator over the last step
+ * during an ODE integration.
+ *
+ * <p>The various ODE integrators provide objects implementing this
+ * interface to the step handlers. These objects are often custom
+ * objects tightly bound to the integrator internal algorithms. The
+ * handlers can use these objects to retrieve the state vector at
+ * intermediate times between the previous and the current grid points
+ * (this feature is often called dense output).</p>
+ * <p>One important thing to note is that the step handlers may be so
+ * tightly bound to the integrators that they often share some internal
+ * state arrays. This imply that one should <em>never</em> use a direct
+ * reference to a step interpolator outside of the step handler, either
+ * for future use or for use in another thread. If such a need arise, the
+ * step interpolator <em>must</em> be copied using the dedicated
+ * {@link #copy()} method.
+ * </p>
+ *
+ * @see org.apache.commons.math3.ode.FirstOrderIntegrator
+ * @see org.apache.commons.math3.ode.SecondOrderIntegrator
+ * @see StepHandler
+ * @since 1.2
+ */
+
+public interface StepInterpolator extends Externalizable {
+
+ /**
+ * Get the previous grid point time.
+ * @return previous grid point time
+ */
+ double getPreviousTime();
+
+ /**
+ * Get the current grid point time.
+ * @return current grid point time
+ */
+ double getCurrentTime();
+
+ /**
+ * Get the time of the interpolated point.
+ * If {@link #setInterpolatedTime} has not been called, it returns
+ * the current grid point time.
+ * @return interpolation point time
+ */
+ double getInterpolatedTime();
+
+ /**
+ * Set the time of the interpolated point.
+ * <p>Setting the time outside of the current step is now allowed, but
+ * should be used with care since the accuracy of the interpolator will
+ * probably be very poor far from this step. This allowance has been
+ * added to simplify implementation of search algorithms near the
+ * step endpoints.</p>
+ * <p>Setting the time changes the instance internal state. This includes
+ * the internal arrays returned in {@link #getInterpolatedState()},
+ * {@link #getInterpolatedDerivatives()}, {@link
+ * #getInterpolatedSecondaryState(int)} and {@link
+ * #getInterpolatedSecondaryDerivatives(int)}. So if their content must be preserved
+ * across several calls, user must copy them.</p>
+ * @param time time of the interpolated point
+ * @see #getInterpolatedState()
+ * @see #getInterpolatedDerivatives()
+ * @see #getInterpolatedSecondaryState(int)
+ * @see #getInterpolatedSecondaryDerivatives(int)
+ */
+ void setInterpolatedTime(double time);
+
+ /**
+ * Get the state vector of the interpolated point.
+ * <p>The returned vector is a reference to a reused array, so
+ * it should not be modified and it should be copied if it needs
+ * to be preserved across several calls to the associated
+ * {@link #setInterpolatedTime(double)} method.</p>
+ * @return state vector at time {@link #getInterpolatedTime}
+ * @see #getInterpolatedDerivatives()
+ * @see #getInterpolatedSecondaryState(int)
+ * @see #getInterpolatedSecondaryDerivatives(int)
+ * @see #setInterpolatedTime(double)
+ * @exception MaxCountExceededException if the number of functions evaluations is exceeded
+ */
+ double[] getInterpolatedState() throws MaxCountExceededException;
+
+ /**
+ * Get the derivatives of the state vector of the interpolated point.
+ * <p>The returned vector is a reference to a reused array, so
+ * it should not be modified and it should be copied if it needs
+ * to be preserved across several calls to the associated
+ * {@link #setInterpolatedTime(double)} method.</p>
+ * @return derivatives of the state vector at time {@link #getInterpolatedTime}
+ * @see #getInterpolatedState()
+ * @see #getInterpolatedSecondaryState(int)
+ * @see #getInterpolatedSecondaryDerivatives(int)
+ * @see #setInterpolatedTime(double)
+ * @since 2.0
+ * @exception MaxCountExceededException if the number of functions evaluations is exceeded
+ */
+ double[] getInterpolatedDerivatives() throws MaxCountExceededException;
+
+ /** Get the interpolated secondary state corresponding to the secondary equations.
+ * <p>The returned vector is a reference to a reused array, so
+ * it should not be modified and it should be copied if it needs
+ * to be preserved across several calls to the associated
+ * {@link #setInterpolatedTime(double)} method.</p>
+ * @param index index of the secondary set, as returned by {@link
+ * org.apache.commons.math3.ode.ExpandableStatefulODE#addSecondaryEquations(
+ * org.apache.commons.math3.ode.SecondaryEquations)
+ * ExpandableStatefulODE.addSecondaryEquations(SecondaryEquations)}
+ * @return interpolated secondary state at the current interpolation date
+ * @see #getInterpolatedState()
+ * @see #getInterpolatedDerivatives()
+ * @see #getInterpolatedSecondaryDerivatives(int)
+ * @see #setInterpolatedTime(double)
+ * @since 3.0
+ * @exception MaxCountExceededException if the number of functions evaluations is exceeded
+ */
+ double[] getInterpolatedSecondaryState(int index) throws MaxCountExceededException;
+
+ /** Get the interpolated secondary derivatives corresponding to the secondary equations.
+ * <p>The returned vector is a reference to a reused array, so
+ * it should not be modified and it should be copied if it needs
+ * to be preserved across several calls.</p>
+ * @param index index of the secondary set, as returned by {@link
+ * org.apache.commons.math3.ode.ExpandableStatefulODE#addSecondaryEquations(
+ * org.apache.commons.math3.ode.SecondaryEquations)
+ * ExpandableStatefulODE.addSecondaryEquations(SecondaryEquations)}
+ * @return interpolated secondary derivatives at the current interpolation date
+ * @see #getInterpolatedState()
+ * @see #getInterpolatedDerivatives()
+ * @see #getInterpolatedSecondaryState(int)
+ * @see #setInterpolatedTime(double)
+ * @since 3.0
+ * @exception MaxCountExceededException if the number of functions evaluations is exceeded
+ */
+ double[] getInterpolatedSecondaryDerivatives(int index) throws MaxCountExceededException;
+
+ /** Check if the natural integration direction is forward.
+ * <p>This method provides the integration direction as specified by
+ * the integrator itself, it avoid some nasty problems in
+ * degenerated cases like null steps due to cancellation at step
+ * initialization, step control or discrete events
+ * triggering.</p>
+ * @return true if the integration variable (time) increases during
+ * integration
+ */
+ boolean isForward();
+
+ /** Copy the instance.
+ * <p>The copied instance is guaranteed to be independent from the
+ * original one. Both can be used with different settings for
+ * interpolated time without any side effect.</p>
+ * @return a deep copy of the instance, which can be used independently.
+ * @see #setInterpolatedTime(double)
+ * @exception MaxCountExceededException if the number of functions evaluations is exceeded
+ * during step finalization
+ */
+ StepInterpolator copy() throws MaxCountExceededException;
+
+}
diff --git a/src/main/java/org/apache/commons/math3/ode/sampling/StepNormalizer.java b/src/main/java/org/apache/commons/math3/ode/sampling/StepNormalizer.java
new file mode 100644
index 0000000..307c0d9
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/ode/sampling/StepNormalizer.java
@@ -0,0 +1,300 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math3.ode.sampling;
+
+import org.apache.commons.math3.exception.MaxCountExceededException;
+import org.apache.commons.math3.util.FastMath;
+import org.apache.commons.math3.util.Precision;
+
+/**
+ * This class wraps an object implementing {@link FixedStepHandler}
+ * into a {@link StepHandler}.
+
+ * <p>This wrapper allows to use fixed step handlers with general
+ * integrators which cannot guaranty their integration steps will
+ * remain constant and therefore only accept general step
+ * handlers.</p>
+ *
+ * <p>The stepsize used is selected at construction time. The {@link
+ * FixedStepHandler#handleStep handleStep} method of the underlying
+ * {@link FixedStepHandler} object is called at normalized times. The
+ * normalized times can be influenced by the {@link StepNormalizerMode} and
+ * {@link StepNormalizerBounds}.</p>
+ *
+ * <p>There is no constraint on the integrator, it can use any time step
+ * it needs (time steps longer or shorter than the fixed time step and
+ * non-integer ratios are all allowed).</p>
+ *
+ * <p>
+ * <table border="1" align="center">
+ * <tr BGCOLOR="#CCCCFF"><td colspan=6><font size="+2">Examples (step size = 0.5)</font></td></tr>
+ * <tr BGCOLOR="#EEEEFF"><font size="+1"><td>Start time</td><td>End time</td>
+ * <td>Direction</td><td>{@link StepNormalizerMode Mode}</td>
+ * <td>{@link StepNormalizerBounds Bounds}</td><td>Output</td></font></tr>
+ * <tr><td>0.3</td><td>3.1</td><td>forward</td><td>{@link StepNormalizerMode#INCREMENT INCREMENT}</td><td>{@link StepNormalizerBounds#NEITHER NEITHER}</td><td>0.8, 1.3, 1.8, 2.3, 2.8</td></tr>
+ * <tr><td>0.3</td><td>3.1</td><td>forward</td><td>{@link StepNormalizerMode#INCREMENT INCREMENT}</td><td>{@link StepNormalizerBounds#FIRST FIRST}</td><td>0.3, 0.8, 1.3, 1.8, 2.3, 2.8</td></tr>
+ * <tr><td>0.3</td><td>3.1</td><td>forward</td><td>{@link StepNormalizerMode#INCREMENT INCREMENT}</td><td>{@link StepNormalizerBounds#LAST LAST}</td><td>0.8, 1.3, 1.8, 2.3, 2.8, 3.1</td></tr>
+ * <tr><td>0.3</td><td>3.1</td><td>forward</td><td>{@link StepNormalizerMode#INCREMENT INCREMENT}</td><td>{@link StepNormalizerBounds#BOTH BOTH}</td><td>0.3, 0.8, 1.3, 1.8, 2.3, 2.8, 3.1</td></tr>
+ * <tr><td>0.3</td><td>3.1</td><td>forward</td><td>{@link StepNormalizerMode#MULTIPLES MULTIPLES}</td><td>{@link StepNormalizerBounds#NEITHER NEITHER}</td><td>0.5, 1.0, 1.5, 2.0, 2.5, 3.0</td></tr>
+ * <tr><td>0.3</td><td>3.1</td><td>forward</td><td>{@link StepNormalizerMode#MULTIPLES MULTIPLES}</td><td>{@link StepNormalizerBounds#FIRST FIRST}</td><td>0.3, 0.5, 1.0, 1.5, 2.0, 2.5, 3.0</td></tr>
+ * <tr><td>0.3</td><td>3.1</td><td>forward</td><td>{@link StepNormalizerMode#MULTIPLES MULTIPLES}</td><td>{@link StepNormalizerBounds#LAST LAST}</td><td>0.5, 1.0, 1.5, 2.0, 2.5, 3.0, 3.1</td></tr>
+ * <tr><td>0.3</td><td>3.1</td><td>forward</td><td>{@link StepNormalizerMode#MULTIPLES MULTIPLES}</td><td>{@link StepNormalizerBounds#BOTH BOTH}</td><td>0.3, 0.5, 1.0, 1.5, 2.0, 2.5, 3.0, 3.1</td></tr>
+ * <tr><td>0.0</td><td>3.0</td><td>forward</td><td>{@link StepNormalizerMode#INCREMENT INCREMENT}</td><td>{@link StepNormalizerBounds#NEITHER NEITHER}</td><td>0.5, 1.0, 1.5, 2.0, 2.5, 3.0</td></tr>
+ * <tr><td>0.0</td><td>3.0</td><td>forward</td><td>{@link StepNormalizerMode#INCREMENT INCREMENT}</td><td>{@link StepNormalizerBounds#FIRST FIRST}</td><td>0.0, 0.5, 1.0, 1.5, 2.0, 2.5, 3.0</td></tr>
+ * <tr><td>0.0</td><td>3.0</td><td>forward</td><td>{@link StepNormalizerMode#INCREMENT INCREMENT}</td><td>{@link StepNormalizerBounds#LAST LAST}</td><td>0.5, 1.0, 1.5, 2.0, 2.5, 3.0</td></tr>
+ * <tr><td>0.0</td><td>3.0</td><td>forward</td><td>{@link StepNormalizerMode#INCREMENT INCREMENT}</td><td>{@link StepNormalizerBounds#BOTH BOTH}</td><td>0.0, 0.5, 1.0, 1.5, 2.0, 2.5, 3.0</td></tr>
+ * <tr><td>0.0</td><td>3.0</td><td>forward</td><td>{@link StepNormalizerMode#MULTIPLES MULTIPLES}</td><td>{@link StepNormalizerBounds#NEITHER NEITHER}</td><td>0.5, 1.0, 1.5, 2.0, 2.5, 3.0</td></tr>
+ * <tr><td>0.0</td><td>3.0</td><td>forward</td><td>{@link StepNormalizerMode#MULTIPLES MULTIPLES}</td><td>{@link StepNormalizerBounds#FIRST FIRST}</td><td>0.0, 0.5, 1.0, 1.5, 2.0, 2.5, 3.0</td></tr>
+ * <tr><td>0.0</td><td>3.0</td><td>forward</td><td>{@link StepNormalizerMode#MULTIPLES MULTIPLES}</td><td>{@link StepNormalizerBounds#LAST LAST}</td><td>0.5, 1.0, 1.5, 2.0, 2.5, 3.0</td></tr>
+ * <tr><td>0.0</td><td>3.0</td><td>forward</td><td>{@link StepNormalizerMode#MULTIPLES MULTIPLES}</td><td>{@link StepNormalizerBounds#BOTH BOTH}</td><td>0.0, 0.5, 1.0, 1.5, 2.0, 2.5, 3.0</td></tr>
+ * <tr><td>3.1</td><td>0.3</td><td>backward</td><td>{@link StepNormalizerMode#INCREMENT INCREMENT}</td><td>{@link StepNormalizerBounds#NEITHER NEITHER}</td><td>2.6, 2.1, 1.6, 1.1, 0.6</td></tr>
+ * <tr><td>3.1</td><td>0.3</td><td>backward</td><td>{@link StepNormalizerMode#INCREMENT INCREMENT}</td><td>{@link StepNormalizerBounds#FIRST FIRST}</td><td>3.1, 2.6, 2.1, 1.6, 1.1, 0.6</td></tr>
+ * <tr><td>3.1</td><td>0.3</td><td>backward</td><td>{@link StepNormalizerMode#INCREMENT INCREMENT}</td><td>{@link StepNormalizerBounds#LAST LAST}</td><td>2.6, 2.1, 1.6, 1.1, 0.6, 0.3</td></tr>
+ * <tr><td>3.1</td><td>0.3</td><td>backward</td><td>{@link StepNormalizerMode#INCREMENT INCREMENT}</td><td>{@link StepNormalizerBounds#BOTH BOTH}</td><td>3.1, 2.6, 2.1, 1.6, 1.1, 0.6, 0.3</td></tr>
+ * <tr><td>3.1</td><td>0.3</td><td>backward</td><td>{@link StepNormalizerMode#MULTIPLES MULTIPLES}</td><td>{@link StepNormalizerBounds#NEITHER NEITHER}</td><td>3.0, 2.5, 2.0, 1.5, 1.0, 0.5</td></tr>
+ * <tr><td>3.1</td><td>0.3</td><td>backward</td><td>{@link StepNormalizerMode#MULTIPLES MULTIPLES}</td><td>{@link StepNormalizerBounds#FIRST FIRST}</td><td>3.1, 3.0, 2.5, 2.0, 1.5, 1.0, 0.5</td></tr>
+ * <tr><td>3.1</td><td>0.3</td><td>backward</td><td>{@link StepNormalizerMode#MULTIPLES MULTIPLES}</td><td>{@link StepNormalizerBounds#LAST LAST}</td><td>3.0, 2.5, 2.0, 1.5, 1.0, 0.5, 0.3</td></tr>
+ * <tr><td>3.1</td><td>0.3</td><td>backward</td><td>{@link StepNormalizerMode#MULTIPLES MULTIPLES}</td><td>{@link StepNormalizerBounds#BOTH BOTH}</td><td>3.1, 3.0, 2.5, 2.0, 1.5, 1.0, 0.5, 0.3</td></tr>
+ * <tr><td>3.0</td><td>0.0</td><td>backward</td><td>{@link StepNormalizerMode#INCREMENT INCREMENT}</td><td>{@link StepNormalizerBounds#NEITHER NEITHER}</td><td>2.5, 2.0, 1.5, 1.0, 0.5, 0.0</td></tr>
+ * <tr><td>3.0</td><td>0.0</td><td>backward</td><td>{@link StepNormalizerMode#INCREMENT INCREMENT}</td><td>{@link StepNormalizerBounds#FIRST FIRST}</td><td>3.0, 2.5, 2.0, 1.5, 1.0, 0.5, 0.0</td></tr>
+ * <tr><td>3.0</td><td>0.0</td><td>backward</td><td>{@link StepNormalizerMode#INCREMENT INCREMENT}</td><td>{@link StepNormalizerBounds#LAST LAST}</td><td>2.5, 2.0, 1.5, 1.0, 0.5, 0.0</td></tr>
+ * <tr><td>3.0</td><td>0.0</td><td>backward</td><td>{@link StepNormalizerMode#INCREMENT INCREMENT}</td><td>{@link StepNormalizerBounds#BOTH BOTH}</td><td>3.0, 2.5, 2.0, 1.5, 1.0, 0.5, 0.0</td></tr>
+ * <tr><td>3.0</td><td>0.0</td><td>backward</td><td>{@link StepNormalizerMode#MULTIPLES MULTIPLES}</td><td>{@link StepNormalizerBounds#NEITHER NEITHER}</td><td>2.5, 2.0, 1.5, 1.0, 0.5, 0.0</td></tr>
+ * <tr><td>3.0</td><td>0.0</td><td>backward</td><td>{@link StepNormalizerMode#MULTIPLES MULTIPLES}</td><td>{@link StepNormalizerBounds#FIRST FIRST}</td><td>3.0, 2.5, 2.0, 1.5, 1.0, 0.5, 0.0</td></tr>
+ * <tr><td>3.0</td><td>0.0</td><td>backward</td><td>{@link StepNormalizerMode#MULTIPLES MULTIPLES}</td><td>{@link StepNormalizerBounds#LAST LAST}</td><td>2.5, 2.0, 1.5, 1.0, 0.5, 0.0</td></tr>
+ * <tr><td>3.0</td><td>0.0</td><td>backward</td><td>{@link StepNormalizerMode#MULTIPLES MULTIPLES}</td><td>{@link StepNormalizerBounds#BOTH BOTH}</td><td>3.0, 2.5, 2.0, 1.5, 1.0, 0.5, 0.0</td></tr>
+ * </table>
+ * </p>
+ *
+ * @see StepHandler
+ * @see FixedStepHandler
+ * @see StepNormalizerMode
+ * @see StepNormalizerBounds
+ * @since 1.2
+ */
+
+public class StepNormalizer implements StepHandler {
+ /** Fixed time step. */
+ private double h;
+
+ /** Underlying step handler. */
+ private final FixedStepHandler handler;
+
+ /** First step time. */
+ private double firstTime;
+
+ /** Last step time. */
+ private double lastTime;
+
+ /** Last state vector. */
+ private double[] lastState;
+
+ /** Last derivatives vector. */
+ private double[] lastDerivatives;
+
+ /** Integration direction indicator. */
+ private boolean forward;
+
+ /** The step normalizer bounds settings to use. */
+ private final StepNormalizerBounds bounds;
+
+ /** The step normalizer mode to use. */
+ private final StepNormalizerMode mode;
+
+ /** Simple constructor. Uses {@link StepNormalizerMode#INCREMENT INCREMENT}
+ * mode, and {@link StepNormalizerBounds#FIRST FIRST} bounds setting, for
+ * backwards compatibility.
+ * @param h fixed time step (sign is not used)
+ * @param handler fixed time step handler to wrap
+ */
+ public StepNormalizer(final double h, final FixedStepHandler handler) {
+ this(h, handler, StepNormalizerMode.INCREMENT,
+ StepNormalizerBounds.FIRST);
+ }
+
+ /** Simple constructor. Uses {@link StepNormalizerBounds#FIRST FIRST}
+ * bounds setting.
+ * @param h fixed time step (sign is not used)
+ * @param handler fixed time step handler to wrap
+ * @param mode step normalizer mode to use
+ * @since 3.0
+ */
+ public StepNormalizer(final double h, final FixedStepHandler handler,
+ final StepNormalizerMode mode) {
+ this(h, handler, mode, StepNormalizerBounds.FIRST);
+ }
+
+ /** Simple constructor. Uses {@link StepNormalizerMode#INCREMENT INCREMENT}
+ * mode.
+ * @param h fixed time step (sign is not used)
+ * @param handler fixed time step handler to wrap
+ * @param bounds step normalizer bounds setting to use
+ * @since 3.0
+ */
+ public StepNormalizer(final double h, final FixedStepHandler handler,
+ final StepNormalizerBounds bounds) {
+ this(h, handler, StepNormalizerMode.INCREMENT, bounds);
+ }
+
+ /** Simple constructor.
+ * @param h fixed time step (sign is not used)
+ * @param handler fixed time step handler to wrap
+ * @param mode step normalizer mode to use
+ * @param bounds step normalizer bounds setting to use
+ * @since 3.0
+ */
+ public StepNormalizer(final double h, final FixedStepHandler handler,
+ final StepNormalizerMode mode,
+ final StepNormalizerBounds bounds) {
+ this.h = FastMath.abs(h);
+ this.handler = handler;
+ this.mode = mode;
+ this.bounds = bounds;
+ firstTime = Double.NaN;
+ lastTime = Double.NaN;
+ lastState = null;
+ lastDerivatives = null;
+ forward = true;
+ }
+
+ /** {@inheritDoc} */
+ public void init(double t0, double[] y0, double t) {
+
+ firstTime = Double.NaN;
+ lastTime = Double.NaN;
+ lastState = null;
+ lastDerivatives = null;
+ forward = true;
+
+ // initialize the underlying handler
+ handler.init(t0, y0, t);
+
+ }
+
+ /**
+ * Handle the last accepted step
+ * @param interpolator interpolator for the last accepted step. For
+ * efficiency purposes, the various integrators reuse the same
+ * object on each call, so if the instance wants to keep it across
+ * all calls (for example to provide at the end of the integration a
+ * continuous model valid throughout the integration range), it
+ * should build a local copy using the clone method and store this
+ * copy.
+ * @param isLast true if the step is the last one
+ * @exception MaxCountExceededException if the interpolator throws one because
+ * the number of functions evaluations is exceeded
+ */
+ public void handleStep(final StepInterpolator interpolator, final boolean isLast)
+ throws MaxCountExceededException {
+ // The first time, update the last state with the start information.
+ if (lastState == null) {
+ firstTime = interpolator.getPreviousTime();
+ lastTime = interpolator.getPreviousTime();
+ interpolator.setInterpolatedTime(lastTime);
+ lastState = interpolator.getInterpolatedState().clone();
+ lastDerivatives = interpolator.getInterpolatedDerivatives().clone();
+
+ // Take the integration direction into account.
+ forward = interpolator.getCurrentTime() >= lastTime;
+ if (!forward) {
+ h = -h;
+ }
+ }
+
+ // Calculate next normalized step time.
+ double nextTime = (mode == StepNormalizerMode.INCREMENT) ?
+ lastTime + h :
+ (FastMath.floor(lastTime / h) + 1) * h;
+ if (mode == StepNormalizerMode.MULTIPLES &&
+ Precision.equals(nextTime, lastTime, 1)) {
+ nextTime += h;
+ }
+
+ // Process normalized steps as long as they are in the current step.
+ boolean nextInStep = isNextInStep(nextTime, interpolator);
+ while (nextInStep) {
+ // Output the stored previous step.
+ doNormalizedStep(false);
+
+ // Store the next step as last step.
+ storeStep(interpolator, nextTime);
+
+ // Move on to the next step.
+ nextTime += h;
+ nextInStep = isNextInStep(nextTime, interpolator);
+ }
+
+ if (isLast) {
+ // There will be no more steps. The stored one should be given to
+ // the handler. We may have to output one more step. Only the last
+ // one of those should be flagged as being the last.
+ boolean addLast = bounds.lastIncluded() &&
+ lastTime != interpolator.getCurrentTime();
+ doNormalizedStep(!addLast);
+ if (addLast) {
+ storeStep(interpolator, interpolator.getCurrentTime());
+ doNormalizedStep(true);
+ }
+ }
+ }
+
+ /**
+ * Returns a value indicating whether the next normalized time is in the
+ * current step.
+ * @param nextTime the next normalized time
+ * @param interpolator interpolator for the last accepted step, to use to
+ * get the end time of the current step
+ * @return value indicating whether the next normalized time is in the
+ * current step
+ */
+ private boolean isNextInStep(double nextTime,
+ StepInterpolator interpolator) {
+ return forward ?
+ nextTime <= interpolator.getCurrentTime() :
+ nextTime >= interpolator.getCurrentTime();
+ }
+
+ /**
+ * Invokes the underlying step handler for the current normalized step.
+ * @param isLast true if the step is the last one
+ */
+ private void doNormalizedStep(boolean isLast) {
+ if (!bounds.firstIncluded() && firstTime == lastTime) {
+ return;
+ }
+ handler.handleStep(lastTime, lastState, lastDerivatives, isLast);
+ }
+
+ /** Stores the interpolated information for the given time in the current
+ * state.
+ * @param interpolator interpolator for the last accepted step, to use to
+ * get the interpolated information
+ * @param t the time for which to store the interpolated information
+ * @exception MaxCountExceededException if the interpolator throws one because
+ * the number of functions evaluations is exceeded
+ */
+ private void storeStep(StepInterpolator interpolator, double t)
+ throws MaxCountExceededException {
+ lastTime = t;
+ interpolator.setInterpolatedTime(lastTime);
+ System.arraycopy(interpolator.getInterpolatedState(), 0,
+ lastState, 0, lastState.length);
+ System.arraycopy(interpolator.getInterpolatedDerivatives(), 0,
+ lastDerivatives, 0, lastDerivatives.length);
+ }
+}
diff --git a/src/main/java/org/apache/commons/math3/ode/sampling/StepNormalizerBounds.java b/src/main/java/org/apache/commons/math3/ode/sampling/StepNormalizerBounds.java
new file mode 100644
index 0000000..ca35e82
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/ode/sampling/StepNormalizerBounds.java
@@ -0,0 +1,84 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math3.ode.sampling;
+
+/** {@link StepNormalizer Step normalizer} bounds settings. They influence
+ * whether the underlying fixed step size step handler is called for the first
+ * and last points. Note that if the last point coincides with a normalized
+ * point, then the underlying fixed step size step handler is always called,
+ * regardless of these settings.
+ * @see FieldStepNormalizer
+ * @see StepNormalizer
+ * @see StepNormalizerMode
+ * @since 3.0
+ */
+public enum StepNormalizerBounds {
+ /** Do not include the first and last points. */
+ NEITHER(false, false),
+
+ /** Include the first point, but not the last point. */
+ FIRST(true, false),
+
+ /** Include the last point, but not the first point. */
+ LAST(false, true),
+
+ /** Include both the first and last points. */
+ BOTH(true, true);
+
+ /** Whether the first point should be passed to the underlying fixed
+ * step size step handler.
+ */
+ private final boolean first;
+
+ /** Whether the last point should be passed to the underlying fixed
+ * step size step handler.
+ */
+ private final boolean last;
+
+ /**
+ * Simple constructor.
+ * @param first Whether the first point should be passed to the
+ * underlying fixed step size step handler.
+ * @param last Whether the last point should be passed to the
+ * underlying fixed step size step handler.
+ */
+ StepNormalizerBounds(final boolean first, final boolean last) {
+ this.first = first;
+ this.last = last;
+ }
+
+ /**
+ * Returns a value indicating whether the first point should be passed
+ * to the underlying fixed step size step handler.
+ * @return value indicating whether the first point should be passed
+ * to the underlying fixed step size step handler.
+ */
+ public boolean firstIncluded() {
+ return first;
+ }
+
+ /**
+ * Returns a value indicating whether the last point should be passed
+ * to the underlying fixed step size step handler.
+ * @return value indicating whether the last point should be passed
+ * to the underlying fixed step size step handler.
+ */
+ public boolean lastIncluded() {
+ return last;
+ }
+}
diff --git a/src/main/java/org/apache/commons/math3/ode/sampling/StepNormalizerMode.java b/src/main/java/org/apache/commons/math3/ode/sampling/StepNormalizerMode.java
new file mode 100644
index 0000000..c6f4c69
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/ode/sampling/StepNormalizerMode.java
@@ -0,0 +1,70 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math3.ode.sampling;
+
+
+/** {@link StepNormalizer Step normalizer} modes. Determines how the step size
+ * is interpreted.
+ * @see FieldStepNormalizer
+ * @see StepNormalizer
+ * @see StepNormalizerBounds
+ * @since 3.0
+ */
+public enum StepNormalizerMode {
+ /**
+ * Steps are fixed increments of the start value. In other words, they
+ * are relative to the start value.
+ *
+ * <p>If the integration start time is t0, then the points handled by
+ * the underlying fixed step size step handler are t0 (depending on
+ * the {@link StepNormalizerBounds bounds settings}), t0+h, t0+2h, ...</p>
+ *
+ * <p>If the integration range is an integer multiple of the step size
+ * (h), then the last point handled will be the end point of the
+ * integration (tend). If not, the last point may be the end point
+ * tend, or it may be a point belonging to the interval [tend - h ;
+ * tend], depending on the {@link StepNormalizerBounds bounds settings}.
+ * </p>
+ *
+ * @see StepNormalizer
+ * @see StepNormalizerBounds
+ */
+ INCREMENT,
+
+ /** Steps are multiples of a fixed value. In other words, they are
+ * relative to the first multiple of the step size that is encountered
+ * after the start value.
+ *
+ * <p>If the integration start time is t0, and the first multiple of
+ * the fixed step size that is encountered is t1, then the points
+ * handled by the underlying fixed step size step handler are t0
+ * (depending on the {@link StepNormalizerBounds bounds settings}), t1,
+ * t1+h, t1+2h, ...</p>
+ *
+ * <p>If the end point of the integration range (tend) is an integer
+ * multiple of the step size (h) added to t1, then the last point
+ * handled will be the end point of the integration (tend). If not,
+ * the last point may be the end point tend, or it may be a point
+ * belonging to the interval [tend - h ; tend], depending on the
+ * {@link StepNormalizerBounds bounds settings}.</p>
+ *
+ * @see StepNormalizer
+ * @see StepNormalizerBounds
+ */
+ MULTIPLES;
+}
diff --git a/src/main/java/org/apache/commons/math3/ode/sampling/package-info.java b/src/main/java/org/apache/commons/math3/ode/sampling/package-info.java
new file mode 100644
index 0000000..29c65e0
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/ode/sampling/package-info.java
@@ -0,0 +1,60 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/**
+ *
+ * <p>
+ * This package provides classes to handle sampling steps during
+ * Ordinary Differential Equations integration.
+ * </p>
+ *
+ * <p>
+ * In addition to computing the evolution of the state vector at some grid points, all
+ * ODE integrators also build up interpolation models of this evolution <em>inside</em> the
+ * last computed step. If users are interested in these interpolators, they can register a
+ * {@link org.apache.commons.math3.ode.sampling.StepHandler StepHandler} instance using the
+ * {@link org.apache.commons.math3.ode.FirstOrderIntegrator#addStepHandler addStepHandler}
+ * method which is supported by all integrators. The integrator will call this instance
+ * at the end of each accepted step and provide it the interpolator. The user can do
+ * whatever he wants with this interpolator, which computes both the state and its
+ * time-derivative. A typical use of step handler is to provide some output to monitor
+ * the integration process.
+ * </p>
+ *
+ * <p>
+ * In a sense, this is a kind of Inversion Of Control: rather than having the master
+ * application driving the slave integrator by providing the target end value for
+ * the free variable, we get a master integrator scheduling the free variable
+ * evolution and calling the slave application callbacks that were registered at
+ * configuration time.
+ * </p>
+ *
+ * <p>
+ * Since some integrators may use variable step size, the generic {@link
+ * org.apache.commons.math3.ode.sampling.StepHandler StepHandler} interface can be called
+ * either at regular or irregular rate. This interface allows to navigate to any location
+ * within the last computed step, thanks to the provided {@link
+ * org.apache.commons.math3.ode.sampling.StepInterpolator StepInterpolator} object.
+ * If regular output is desired (for example in order to write an ephemeris file), then
+ * the simpler {@link org.apache.commons.math3.ode.sampling.FixedStepHandler FixedStepHandler}
+ * interface can be used. Objects implementing this interface should be wrapped within a
+ * {@link org.apache.commons.math3.ode.sampling.StepNormalizer StepNormalizer} instance
+ * in order to be registered to the integrator.
+ * </p>
+ *
+ *
+ */
+package org.apache.commons.math3.ode.sampling;
diff --git a/src/main/java/org/apache/commons/math3/optim/AbstractConvergenceChecker.java b/src/main/java/org/apache/commons/math3/optim/AbstractConvergenceChecker.java
new file mode 100644
index 0000000..19c3f62
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/optim/AbstractConvergenceChecker.java
@@ -0,0 +1,60 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math3.optim;
+
+/**
+ * Base class for all convergence checker implementations.
+ *
+ * @param <PAIR> Type of (point, value) pair.
+ * @since 3.0
+ */
+public abstract class AbstractConvergenceChecker<PAIR> implements ConvergenceChecker<PAIR> {
+ /** Relative tolerance threshold. */
+ private final double relativeThreshold;
+
+ /** Absolute tolerance threshold. */
+ private final double absoluteThreshold;
+
+ /**
+ * Build an instance with a specified thresholds.
+ *
+ * @param relativeThreshold relative tolerance threshold
+ * @param absoluteThreshold absolute tolerance threshold
+ */
+ public AbstractConvergenceChecker(
+ final double relativeThreshold, final double absoluteThreshold) {
+ this.relativeThreshold = relativeThreshold;
+ this.absoluteThreshold = absoluteThreshold;
+ }
+
+ /**
+ * @return the relative threshold.
+ */
+ public double getRelativeThreshold() {
+ return relativeThreshold;
+ }
+
+ /**
+ * @return the absolute threshold.
+ */
+ public double getAbsoluteThreshold() {
+ return absoluteThreshold;
+ }
+
+ /** {@inheritDoc} */
+ public abstract boolean converged(int iteration, PAIR previous, PAIR current);
+}
diff --git a/src/main/java/org/apache/commons/math3/optim/AbstractOptimizationProblem.java b/src/main/java/org/apache/commons/math3/optim/AbstractOptimizationProblem.java
new file mode 100644
index 0000000..84da354
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/optim/AbstractOptimizationProblem.java
@@ -0,0 +1,102 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math3.optim;
+
+import org.apache.commons.math3.exception.TooManyEvaluationsException;
+import org.apache.commons.math3.exception.TooManyIterationsException;
+import org.apache.commons.math3.util.Incrementor;
+
+/**
+ * Base class for implementing optimization problems. It contains the boiler-plate code for counting
+ * the number of evaluations of the objective function and the number of iterations of the
+ * algorithm, and storing the convergence checker.
+ *
+ * @param <PAIR> Type of the point/value pair returned by the optimization algorithm.
+ * @since 3.3
+ */
+public abstract class AbstractOptimizationProblem<PAIR> implements OptimizationProblem<PAIR> {
+
+ /** Callback to use for the evaluation counter. */
+ private static final MaxEvalCallback MAX_EVAL_CALLBACK = new MaxEvalCallback();
+
+ /** Callback to use for the iteration counter. */
+ private static final MaxIterCallback MAX_ITER_CALLBACK = new MaxIterCallback();
+
+ /** max evaluations */
+ private final int maxEvaluations;
+
+ /** max iterations */
+ private final int maxIterations;
+
+ /** Convergence checker. */
+ private final ConvergenceChecker<PAIR> checker;
+
+ /**
+ * Create an {@link AbstractOptimizationProblem} from the given data.
+ *
+ * @param maxEvaluations the number of allowed model function evaluations.
+ * @param maxIterations the number of allowed iterations.
+ * @param checker the convergence checker.
+ */
+ protected AbstractOptimizationProblem(
+ final int maxEvaluations,
+ final int maxIterations,
+ final ConvergenceChecker<PAIR> checker) {
+ this.maxEvaluations = maxEvaluations;
+ this.maxIterations = maxIterations;
+ this.checker = checker;
+ }
+
+ /** {@inheritDoc} */
+ public Incrementor getEvaluationCounter() {
+ return new Incrementor(this.maxEvaluations, MAX_EVAL_CALLBACK);
+ }
+
+ /** {@inheritDoc} */
+ public Incrementor getIterationCounter() {
+ return new Incrementor(this.maxIterations, MAX_ITER_CALLBACK);
+ }
+
+ /** {@inheritDoc} */
+ public ConvergenceChecker<PAIR> getConvergenceChecker() {
+ return checker;
+ }
+
+ /** Defines the action to perform when reaching the maximum number of evaluations. */
+ private static class MaxEvalCallback implements Incrementor.MaxCountExceededCallback {
+ /**
+ * {@inheritDoc}
+ *
+ * @throws TooManyEvaluationsException
+ */
+ public void trigger(int max) {
+ throw new TooManyEvaluationsException(max);
+ }
+ }
+
+ /** Defines the action to perform when reaching the maximum number of evaluations. */
+ private static class MaxIterCallback implements Incrementor.MaxCountExceededCallback {
+ /**
+ * {@inheritDoc}
+ *
+ * @throws TooManyIterationsException
+ */
+ public void trigger(int max) {
+ throw new TooManyIterationsException(max);
+ }
+ }
+}
diff --git a/src/main/java/org/apache/commons/math3/optim/BaseMultiStartMultivariateOptimizer.java b/src/main/java/org/apache/commons/math3/optim/BaseMultiStartMultivariateOptimizer.java
new file mode 100644
index 0000000..ede4e59
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/optim/BaseMultiStartMultivariateOptimizer.java
@@ -0,0 +1,221 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math3.optim;
+
+import org.apache.commons.math3.exception.MathIllegalStateException;
+import org.apache.commons.math3.exception.NotStrictlyPositiveException;
+import org.apache.commons.math3.exception.TooManyEvaluationsException;
+import org.apache.commons.math3.random.RandomVectorGenerator;
+
+/**
+ * Base class multi-start optimizer for a multivariate function. <br>
+ * This class wraps an optimizer in order to use it several times in turn with different starting
+ * points (trying to avoid being trapped in a local extremum when looking for a global one). <em>It
+ * is not a "user" class.</em>
+ *
+ * @param <PAIR> Type of the point/value pair returned by the optimization algorithm.
+ * @since 3.0
+ */
+public abstract class BaseMultiStartMultivariateOptimizer<PAIR>
+ extends BaseMultivariateOptimizer<PAIR> {
+ /** Underlying classical optimizer. */
+ private final BaseMultivariateOptimizer<PAIR> optimizer;
+
+ /** Number of evaluations already performed for all starts. */
+ private int totalEvaluations;
+
+ /** Number of starts to go. */
+ private int starts;
+
+ /** Random generator for multi-start. */
+ private RandomVectorGenerator generator;
+
+ /** Optimization data. */
+ private OptimizationData[] optimData;
+
+ /**
+ * Location in {@link #optimData} where the updated maximum number of evaluations will be
+ * stored.
+ */
+ private int maxEvalIndex = -1;
+
+ /** Location in {@link #optimData} where the updated start value will be stored. */
+ private int initialGuessIndex = -1;
+
+ /**
+ * Create a multi-start optimizer from a single-start optimizer.
+ *
+ * <p>Note that if there are bounds constraints (see {@link #getLowerBound()} and {@link
+ * #getUpperBound()}), then a simple rejection algorithm is used at each restart. This implies
+ * that the random vector generator should have a good probability to generate vectors in the
+ * bounded domain, otherwise the rejection algorithm will hit the {@link #getMaxEvaluations()}
+ * count without generating a proper restart point. Users must be take great care of the <a
+ * href="http://en.wikipedia.org/wiki/Curse_of_dimensionality">curse of dimensionality</a>.
+ *
+ * @param optimizer Single-start optimizer to wrap.
+ * @param starts Number of starts to perform. If {@code starts == 1}, the {@link
+ * #optimize(OptimizationData[]) optimize} will return the same solution as the given {@code
+ * optimizer} would return.
+ * @param generator Random vector generator to use for restarts.
+ * @throws NotStrictlyPositiveException if {@code starts < 1}.
+ */
+ public BaseMultiStartMultivariateOptimizer(
+ final BaseMultivariateOptimizer<PAIR> optimizer,
+ final int starts,
+ final RandomVectorGenerator generator) {
+ super(optimizer.getConvergenceChecker());
+
+ if (starts < 1) {
+ throw new NotStrictlyPositiveException(starts);
+ }
+
+ this.optimizer = optimizer;
+ this.starts = starts;
+ this.generator = generator;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public int getEvaluations() {
+ return totalEvaluations;
+ }
+
+ /**
+ * Gets all the optima found during the last call to {@code optimize}. The optimizer stores all
+ * the optima found during a set of restarts. The {@code optimize} method returns the best point
+ * only. This method returns all the points found at the end of each starts, including the best
+ * one already returned by the {@code optimize} method. <br>
+ * The returned array as one element for each start as specified in the constructor. It is
+ * ordered with the results from the runs that did converge first, sorted from best to worst
+ * objective value (i.e in ascending order if minimizing and in descending order if maximizing),
+ * followed by {@code null} elements corresponding to the runs that did not converge. This means
+ * all elements will be {@code null} if the {@code optimize} method did throw an exception. This
+ * also means that if the first element is not {@code null}, it is the best point found across
+ * all starts. <br>
+ * The behaviour is undefined if this method is called before {@code optimize}; it will likely
+ * throw {@code NullPointerException}.
+ *
+ * @return an array containing the optima sorted from best to worst.
+ */
+ public abstract PAIR[] getOptima();
+
+ /**
+ * {@inheritDoc}
+ *
+ * @throws MathIllegalStateException if {@code optData} does not contain an instance of {@link
+ * MaxEval} or {@link InitialGuess}.
+ */
+ @Override
+ public PAIR optimize(OptimizationData... optData) {
+ // Store arguments in order to pass them to the internal optimizer.
+ optimData = optData;
+ // Set up base class and perform computations.
+ return super.optimize(optData);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ protected PAIR doOptimize() {
+ // Remove all instances of "MaxEval" and "InitialGuess" from the
+ // array that will be passed to the internal optimizer.
+ // The former is to enforce smaller numbers of allowed evaluations
+ // (according to how many have been used up already), and the latter
+ // to impose a different start value for each start.
+ for (int i = 0; i < optimData.length; i++) {
+ if (optimData[i] instanceof MaxEval) {
+ optimData[i] = null;
+ maxEvalIndex = i;
+ }
+ if (optimData[i] instanceof InitialGuess) {
+ optimData[i] = null;
+ initialGuessIndex = i;
+ continue;
+ }
+ }
+ if (maxEvalIndex == -1) {
+ throw new MathIllegalStateException();
+ }
+ if (initialGuessIndex == -1) {
+ throw new MathIllegalStateException();
+ }
+
+ RuntimeException lastException = null;
+ totalEvaluations = 0;
+ clear();
+
+ final int maxEval = getMaxEvaluations();
+ final double[] min = getLowerBound();
+ final double[] max = getUpperBound();
+ final double[] startPoint = getStartPoint();
+
+ // Multi-start loop.
+ for (int i = 0; i < starts; i++) {
+ // CHECKSTYLE: stop IllegalCatch
+ try {
+ // Decrease number of allowed evaluations.
+ optimData[maxEvalIndex] = new MaxEval(maxEval - totalEvaluations);
+ // New start value.
+ double[] s = null;
+ if (i == 0) {
+ s = startPoint;
+ } else {
+ int attempts = 0;
+ while (s == null) {
+ if (attempts++ >= getMaxEvaluations()) {
+ throw new TooManyEvaluationsException(getMaxEvaluations());
+ }
+ s = generator.nextVector();
+ for (int k = 0; s != null && k < s.length; ++k) {
+ if ((min != null && s[k] < min[k]) || (max != null && s[k] > max[k])) {
+ // reject the vector
+ s = null;
+ }
+ }
+ }
+ }
+ optimData[initialGuessIndex] = new InitialGuess(s);
+ // Optimize.
+ final PAIR result = optimizer.optimize(optimData);
+ store(result);
+ } catch (RuntimeException mue) {
+ lastException = mue;
+ }
+ // CHECKSTYLE: resume IllegalCatch
+
+ totalEvaluations += optimizer.getEvaluations();
+ }
+
+ final PAIR[] optima = getOptima();
+ if (optima.length == 0) {
+ // All runs failed.
+ throw lastException; // Cannot be null if starts >= 1.
+ }
+
+ // Return the best optimum.
+ return optima[0];
+ }
+
+ /**
+ * Method that will be called in order to store each found optimum.
+ *
+ * @param optimum Result of an optimization run.
+ */
+ protected abstract void store(PAIR optimum);
+
+ /** Method that will called in order to clear all stored optima. */
+ protected abstract void clear();
+}
diff --git a/src/main/java/org/apache/commons/math3/optim/BaseMultivariateOptimizer.java b/src/main/java/org/apache/commons/math3/optim/BaseMultivariateOptimizer.java
new file mode 100644
index 0000000..e70ab8e
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/optim/BaseMultivariateOptimizer.java
@@ -0,0 +1,152 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math3.optim;
+
+import org.apache.commons.math3.exception.DimensionMismatchException;
+import org.apache.commons.math3.exception.NumberIsTooLargeException;
+import org.apache.commons.math3.exception.NumberIsTooSmallException;
+
+/**
+ * Base class for implementing optimizers for multivariate functions. It contains the boiler-plate
+ * code for initial guess and bounds specifications. <em>It is not a "user" class.</em>
+ *
+ * @param <PAIR> Type of the point/value pair returned by the optimization algorithm.
+ * @since 3.1
+ */
+public abstract class BaseMultivariateOptimizer<PAIR> extends BaseOptimizer<PAIR> {
+ /** Initial guess. */
+ private double[] start;
+
+ /** Lower bounds. */
+ private double[] lowerBound;
+
+ /** Upper bounds. */
+ private double[] upperBound;
+
+ /**
+ * @param checker Convergence checker.
+ */
+ protected BaseMultivariateOptimizer(ConvergenceChecker<PAIR> checker) {
+ super(checker);
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @param optData Optimization data. In addition to those documented in {@link
+ * BaseOptimizer#parseOptimizationData(OptimizationData[]) BaseOptimizer}, this method will
+ * register the following data:
+ * <ul>
+ * <li>{@link InitialGuess}
+ * <li>{@link SimpleBounds}
+ * </ul>
+ *
+ * @return {@inheritDoc}
+ */
+ @Override
+ public PAIR optimize(OptimizationData... optData) {
+ // Perform optimization.
+ return super.optimize(optData);
+ }
+
+ /**
+ * Scans the list of (required and optional) optimization data that characterize the problem.
+ *
+ * @param optData Optimization data. The following data will be looked for:
+ * <ul>
+ * <li>{@link InitialGuess}
+ * <li>{@link SimpleBounds}
+ * </ul>
+ */
+ @Override
+ protected void parseOptimizationData(OptimizationData... optData) {
+ // Allow base class to register its own data.
+ super.parseOptimizationData(optData);
+
+ // The existing values (as set by the previous call) are reused if
+ // not provided in the argument list.
+ for (OptimizationData data : optData) {
+ if (data instanceof InitialGuess) {
+ start = ((InitialGuess) data).getInitialGuess();
+ continue;
+ }
+ if (data instanceof SimpleBounds) {
+ final SimpleBounds bounds = (SimpleBounds) data;
+ lowerBound = bounds.getLower();
+ upperBound = bounds.getUpper();
+ continue;
+ }
+ }
+
+ // Check input consistency.
+ checkParameters();
+ }
+
+ /**
+ * Gets the initial guess.
+ *
+ * @return the initial guess, or {@code null} if not set.
+ */
+ public double[] getStartPoint() {
+ return start == null ? null : start.clone();
+ }
+
+ /**
+ * @return the lower bounds, or {@code null} if not set.
+ */
+ public double[] getLowerBound() {
+ return lowerBound == null ? null : lowerBound.clone();
+ }
+
+ /**
+ * @return the upper bounds, or {@code null} if not set.
+ */
+ public double[] getUpperBound() {
+ return upperBound == null ? null : upperBound.clone();
+ }
+
+ /** Check parameters consistency. */
+ private void checkParameters() {
+ if (start != null) {
+ final int dim = start.length;
+ if (lowerBound != null) {
+ if (lowerBound.length != dim) {
+ throw new DimensionMismatchException(lowerBound.length, dim);
+ }
+ for (int i = 0; i < dim; i++) {
+ final double v = start[i];
+ final double lo = lowerBound[i];
+ if (v < lo) {
+ throw new NumberIsTooSmallException(v, lo, true);
+ }
+ }
+ }
+ if (upperBound != null) {
+ if (upperBound.length != dim) {
+ throw new DimensionMismatchException(upperBound.length, dim);
+ }
+ for (int i = 0; i < dim; i++) {
+ final double v = start[i];
+ final double hi = upperBound[i];
+ if (v > hi) {
+ throw new NumberIsTooLargeException(v, hi, true);
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/src/main/java/org/apache/commons/math3/optim/BaseOptimizer.java b/src/main/java/org/apache/commons/math3/optim/BaseOptimizer.java
new file mode 100644
index 0000000..80f7527
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/optim/BaseOptimizer.java
@@ -0,0 +1,233 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math3.optim;
+
+import org.apache.commons.math3.exception.TooManyEvaluationsException;
+import org.apache.commons.math3.exception.TooManyIterationsException;
+import org.apache.commons.math3.util.Incrementor;
+
+/**
+ * Base class for implementing optimizers. It contains the boiler-plate code for counting the number
+ * of evaluations of the objective function and the number of iterations of the algorithm, and
+ * storing the convergence checker. <em>It is not a "user" class.</em>
+ *
+ * @param <PAIR> Type of the point/value pair returned by the optimization algorithm.
+ * @since 3.1
+ */
+public abstract class BaseOptimizer<PAIR> {
+ /** Evaluations counter. */
+ protected final Incrementor evaluations;
+
+ /** Iterations counter. */
+ protected final Incrementor iterations;
+
+ /** Convergence checker. */
+ private final ConvergenceChecker<PAIR> checker;
+
+ /**
+ * @param checker Convergence checker.
+ */
+ protected BaseOptimizer(ConvergenceChecker<PAIR> checker) {
+ this(checker, 0, Integer.MAX_VALUE);
+ }
+
+ /**
+ * @param checker Convergence checker.
+ * @param maxEval Maximum number of objective function evaluations.
+ * @param maxIter Maximum number of algorithm iterations.
+ */
+ protected BaseOptimizer(ConvergenceChecker<PAIR> checker, int maxEval, int maxIter) {
+ this.checker = checker;
+
+ evaluations = new Incrementor(maxEval, new MaxEvalCallback());
+ iterations = new Incrementor(maxIter, new MaxIterCallback());
+ }
+
+ /**
+ * Gets the maximal number of function evaluations.
+ *
+ * @return the maximal number of function evaluations.
+ */
+ public int getMaxEvaluations() {
+ return evaluations.getMaximalCount();
+ }
+
+ /**
+ * Gets the number of evaluations of the objective function. The number of evaluations
+ * corresponds to the last call to the {@code optimize} method. It is 0 if the method has not
+ * been called yet.
+ *
+ * @return the number of evaluations of the objective function.
+ */
+ public int getEvaluations() {
+ return evaluations.getCount();
+ }
+
+ /**
+ * Gets the maximal number of iterations.
+ *
+ * @return the maximal number of iterations.
+ */
+ public int getMaxIterations() {
+ return iterations.getMaximalCount();
+ }
+
+ /**
+ * Gets the number of iterations performed by the algorithm. The number iterations corresponds
+ * to the last call to the {@code optimize} method. It is 0 if the method has not been called
+ * yet.
+ *
+ * @return the number of evaluations of the objective function.
+ */
+ public int getIterations() {
+ return iterations.getCount();
+ }
+
+ /**
+ * Gets the convergence checker.
+ *
+ * @return the object used to check for convergence.
+ */
+ public ConvergenceChecker<PAIR> getConvergenceChecker() {
+ return checker;
+ }
+
+ /**
+ * Stores data and performs the optimization.
+ *
+ * <p>The list of parameters is open-ended so that sub-classes can extend it with arguments
+ * specific to their concrete implementations.
+ *
+ * <p>When the method is called multiple times, instance data is overwritten only when actually
+ * present in the list of arguments: when not specified, data set in a previous call is retained
+ * (and thus is optional in subsequent calls).
+ *
+ * <p>Important note: Subclasses <em>must</em> override {@link
+ * #parseOptimizationData(OptimizationData[])} if they need to register their own options; but
+ * then, they <em>must</em> also call {@code super.parseOptimizationData(optData)} within that
+ * method.
+ *
+ * @param optData Optimization data. This method will register the following data:
+ * <ul>
+ * <li>{@link MaxEval}
+ * <li>{@link MaxIter}
+ * </ul>
+ *
+ * @return a point/value pair that satisfies the convergence criteria.
+ * @throws TooManyEvaluationsException if the maximal number of evaluations is exceeded.
+ * @throws TooManyIterationsException if the maximal number of iterations is exceeded.
+ */
+ public PAIR optimize(OptimizationData... optData)
+ throws TooManyEvaluationsException, TooManyIterationsException {
+ // Parse options.
+ parseOptimizationData(optData);
+
+ // Reset counters.
+ evaluations.resetCount();
+ iterations.resetCount();
+ // Perform optimization.
+ return doOptimize();
+ }
+
+ /**
+ * Performs the optimization.
+ *
+ * @return a point/value pair that satisfies the convergence criteria.
+ * @throws TooManyEvaluationsException if the maximal number of evaluations is exceeded.
+ * @throws TooManyIterationsException if the maximal number of iterations is exceeded.
+ */
+ public PAIR optimize() throws TooManyEvaluationsException, TooManyIterationsException {
+ // Reset counters.
+ evaluations.resetCount();
+ iterations.resetCount();
+ // Perform optimization.
+ return doOptimize();
+ }
+
+ /**
+ * Performs the bulk of the optimization algorithm.
+ *
+ * @return the point/value pair giving the optimal value of the objective function.
+ */
+ protected abstract PAIR doOptimize();
+
+ /**
+ * Increment the evaluation count.
+ *
+ * @throws TooManyEvaluationsException if the allowed evaluations have been exhausted.
+ */
+ protected void incrementEvaluationCount() throws TooManyEvaluationsException {
+ evaluations.incrementCount();
+ }
+
+ /**
+ * Increment the iteration count.
+ *
+ * @throws TooManyIterationsException if the allowed iterations have been exhausted.
+ */
+ protected void incrementIterationCount() throws TooManyIterationsException {
+ iterations.incrementCount();
+ }
+
+ /**
+ * Scans the list of (required and optional) optimization data that characterize the problem.
+ *
+ * @param optData Optimization data. The following data will be looked for:
+ * <ul>
+ * <li>{@link MaxEval}
+ * <li>{@link MaxIter}
+ * </ul>
+ */
+ protected void parseOptimizationData(OptimizationData... optData) {
+ // The existing values (as set by the previous call) are reused if
+ // not provided in the argument list.
+ for (OptimizationData data : optData) {
+ if (data instanceof MaxEval) {
+ evaluations.setMaximalCount(((MaxEval) data).getMaxEval());
+ continue;
+ }
+ if (data instanceof MaxIter) {
+ iterations.setMaximalCount(((MaxIter) data).getMaxIter());
+ continue;
+ }
+ }
+ }
+
+ /** Defines the action to perform when reaching the maximum number of evaluations. */
+ private static class MaxEvalCallback implements Incrementor.MaxCountExceededCallback {
+ /**
+ * {@inheritDoc}
+ *
+ * @throws TooManyEvaluationsException
+ */
+ public void trigger(int max) {
+ throw new TooManyEvaluationsException(max);
+ }
+ }
+
+ /** Defines the action to perform when reaching the maximum number of evaluations. */
+ private static class MaxIterCallback implements Incrementor.MaxCountExceededCallback {
+ /**
+ * {@inheritDoc}
+ *
+ * @throws TooManyIterationsException
+ */
+ public void trigger(int max) {
+ throw new TooManyIterationsException(max);
+ }
+ }
+}
diff --git a/src/main/java/org/apache/commons/math3/optim/ConvergenceChecker.java b/src/main/java/org/apache/commons/math3/optim/ConvergenceChecker.java
new file mode 100644
index 0000000..8064560
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/optim/ConvergenceChecker.java
@@ -0,0 +1,48 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math3.optim;
+
+/**
+ * This interface specifies how to check if an optimization algorithm has converged. <br>
+ * Deciding if convergence has been reached is a problem-dependent issue. The user should provide a
+ * class implementing this interface to allow the optimization algorithm to stop its search
+ * according to the problem at hand. <br>
+ * For convenience, three implementations that fit simple needs are already provided: {@link
+ * SimpleValueChecker}, {@link SimpleVectorValueChecker} and {@link SimplePointChecker}. The first
+ * two consider that convergence is reached when the objective function value does not change much
+ * anymore, it does not use the point set at all. The third one considers that convergence is
+ * reached when the input point set does not change much anymore, it does not use objective function
+ * value at all.
+ *
+ * @param <PAIR> Type of the (point, objective value) pair.
+ * @see org.apache.commons.math3.optim.SimplePointChecker
+ * @see org.apache.commons.math3.optim.SimpleValueChecker
+ * @see org.apache.commons.math3.optim.SimpleVectorValueChecker
+ * @since 3.0
+ */
+public interface ConvergenceChecker<PAIR> {
+ /**
+ * Check if the optimization algorithm has converged.
+ *
+ * @param iteration Current iteration.
+ * @param previous Best point in the previous iteration.
+ * @param current Best point in the current iteration.
+ * @return {@code true} if the algorithm is considered to have converged.
+ */
+ boolean converged(int iteration, PAIR previous, PAIR current);
+}
diff --git a/src/main/java/org/apache/commons/math3/optim/InitialGuess.java b/src/main/java/org/apache/commons/math3/optim/InitialGuess.java
new file mode 100644
index 0000000..9323c6f
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/optim/InitialGuess.java
@@ -0,0 +1,45 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math3.optim;
+
+/**
+ * Starting point (first guess) of the optimization procedure. <br>
+ * Immutable class.
+ *
+ * @since 3.1
+ */
+public class InitialGuess implements OptimizationData {
+ /** Initial guess. */
+ private final double[] init;
+
+ /**
+ * @param startPoint Initial guess.
+ */
+ public InitialGuess(double[] startPoint) {
+ init = startPoint.clone();
+ }
+
+ /**
+ * Gets the initial guess.
+ *
+ * @return the initial guess.
+ */
+ public double[] getInitialGuess() {
+ return init.clone();
+ }
+}
diff --git a/src/main/java/org/apache/commons/math3/optim/MaxEval.java b/src/main/java/org/apache/commons/math3/optim/MaxEval.java
new file mode 100644
index 0000000..3c6478e
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/optim/MaxEval.java
@@ -0,0 +1,60 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math3.optim;
+
+import org.apache.commons.math3.exception.NotStrictlyPositiveException;
+
+/**
+ * Maximum number of evaluations of the function to be optimized.
+ *
+ * @since 3.1
+ */
+public class MaxEval implements OptimizationData {
+ /** Allowed number of evalutations. */
+ private final int maxEval;
+
+ /**
+ * @param max Allowed number of evalutations.
+ * @throws NotStrictlyPositiveException if {@code max <= 0}.
+ */
+ public MaxEval(int max) {
+ if (max <= 0) {
+ throw new NotStrictlyPositiveException(max);
+ }
+
+ maxEval = max;
+ }
+
+ /**
+ * Gets the maximum number of evaluations.
+ *
+ * @return the allowed number of evaluations.
+ */
+ public int getMaxEval() {
+ return maxEval;
+ }
+
+ /**
+ * Factory method that creates instance of this class that represents a virtually unlimited
+ * number of evaluations.
+ *
+ * @return a new instance suitable for allowing {@link Integer#MAX_VALUE} evaluations.
+ */
+ public static MaxEval unlimited() {
+ return new MaxEval(Integer.MAX_VALUE);
+ }
+}
diff --git a/src/main/java/org/apache/commons/math3/optim/MaxIter.java b/src/main/java/org/apache/commons/math3/optim/MaxIter.java
new file mode 100644
index 0000000..dc9c917
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/optim/MaxIter.java
@@ -0,0 +1,60 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math3.optim;
+
+import org.apache.commons.math3.exception.NotStrictlyPositiveException;
+
+/**
+ * Maximum number of iterations performed by an (iterative) algorithm.
+ *
+ * @since 3.1
+ */
+public class MaxIter implements OptimizationData {
+ /** Allowed number of evalutations. */
+ private final int maxIter;
+
+ /**
+ * @param max Allowed number of iterations.
+ * @throws NotStrictlyPositiveException if {@code max <= 0}.
+ */
+ public MaxIter(int max) {
+ if (max <= 0) {
+ throw new NotStrictlyPositiveException(max);
+ }
+
+ maxIter = max;
+ }
+
+ /**
+ * Gets the maximum number of evaluations.
+ *
+ * @return the allowed number of evaluations.
+ */
+ public int getMaxIter() {
+ return maxIter;
+ }
+
+ /**
+ * Factory method that creates instance of this class that represents a virtually unlimited
+ * number of iterations.
+ *
+ * @return a new instance suitable for allowing {@link Integer#MAX_VALUE} evaluations.
+ */
+ public static MaxIter unlimited() {
+ return new MaxIter(Integer.MAX_VALUE);
+ }
+}
diff --git a/src/main/java/org/apache/commons/math3/optim/OptimizationData.java b/src/main/java/org/apache/commons/math3/optim/OptimizationData.java
new file mode 100644
index 0000000..4f55472
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/optim/OptimizationData.java
@@ -0,0 +1,26 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math3.optim;
+
+/**
+ * Marker interface. Implementations will provide functionality (optional or required) needed by the
+ * optimizers, and those will need to check the actual type of the arguments and perform the
+ * appropriate cast in order to access the data they need.
+ *
+ * @since 3.1
+ */
+public interface OptimizationData {}
diff --git a/src/main/java/org/apache/commons/math3/optim/OptimizationProblem.java b/src/main/java/org/apache/commons/math3/optim/OptimizationProblem.java
new file mode 100644
index 0000000..1b77117
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/optim/OptimizationProblem.java
@@ -0,0 +1,52 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math3.optim;
+
+import org.apache.commons.math3.util.Incrementor;
+
+/**
+ * Common settings for all optimization problems. Includes divergence and convergence criteria.
+ *
+ * @param <PAIR> The type of value the {@link #getConvergenceChecker() convergence checker} will
+ * operate on. It should include the value of the model function and point where it was
+ * evaluated.
+ * @since 3.3
+ */
+public interface OptimizationProblem<PAIR> {
+ /**
+ * Get a independent Incrementor that counts up to the maximum number of evaluations and then
+ * throws an exception.
+ *
+ * @return a counter for the evaluations.
+ */
+ Incrementor getEvaluationCounter();
+
+ /**
+ * Get a independent Incrementor that counts up to the maximum number of iterations and then
+ * throws an exception.
+ *
+ * @return a counter for the evaluations.
+ */
+ Incrementor getIterationCounter();
+
+ /**
+ * Gets the convergence checker.
+ *
+ * @return the object used to check for convergence.
+ */
+ ConvergenceChecker<PAIR> getConvergenceChecker();
+}
diff --git a/src/main/java/org/apache/commons/math3/optim/PointValuePair.java b/src/main/java/org/apache/commons/math3/optim/PointValuePair.java
new file mode 100644
index 0000000..8f7cc01
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/optim/PointValuePair.java
@@ -0,0 +1,116 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math3.optim;
+
+import org.apache.commons.math3.util.Pair;
+
+import java.io.Serializable;
+
+/**
+ * This class holds a point and the value of an objective function at that point.
+ *
+ * @see PointVectorValuePair
+ * @see org.apache.commons.math3.analysis.MultivariateFunction
+ * @since 3.0
+ */
+public class PointValuePair extends Pair<double[], Double> implements Serializable {
+ /** Serializable UID. */
+ private static final long serialVersionUID = 20120513L;
+
+ /**
+ * Builds a point/objective function value pair.
+ *
+ * @param point Point coordinates. This instance will store a copy of the array, not the array
+ * passed as argument.
+ * @param value Value of the objective function at the point.
+ */
+ public PointValuePair(final double[] point, final double value) {
+ this(point, value, true);
+ }
+
+ /**
+ * Builds a point/objective function value pair.
+ *
+ * @param point Point coordinates.
+ * @param value Value of the objective function at the point.
+ * @param copyArray if {@code true}, the input array will be copied, otherwise it will be
+ * referenced.
+ */
+ public PointValuePair(final double[] point, final double value, final boolean copyArray) {
+ super(copyArray ? ((point == null) ? null : point.clone()) : point, value);
+ }
+
+ /**
+ * Gets the point.
+ *
+ * @return a copy of the stored point.
+ */
+ public double[] getPoint() {
+ final double[] p = getKey();
+ return p == null ? null : p.clone();
+ }
+
+ /**
+ * Gets a reference to the point.
+ *
+ * @return a reference to the internal array storing the point.
+ */
+ public double[] getPointRef() {
+ return getKey();
+ }
+
+ /**
+ * Replace the instance with a data transfer object for serialization.
+ *
+ * @return data transfer object that will be serialized
+ */
+ private Object writeReplace() {
+ return new DataTransferObject(getKey(), getValue());
+ }
+
+ /** Internal class used only for serialization. */
+ private static class DataTransferObject implements Serializable {
+ /** Serializable UID. */
+ private static final long serialVersionUID = 20120513L;
+
+ /** Point coordinates. @Serial */
+ private final double[] point;
+
+ /** Value of the objective function at the point. @Serial */
+ private final double value;
+
+ /**
+ * Simple constructor.
+ *
+ * @param point Point coordinates.
+ * @param value Value of the objective function at the point.
+ */
+ DataTransferObject(final double[] point, final double value) {
+ this.point = point.clone();
+ this.value = value;
+ }
+
+ /**
+ * Replace the deserialized data transfer object with a {@link PointValuePair}.
+ *
+ * @return replacement {@link PointValuePair}
+ */
+ private Object readResolve() {
+ return new PointValuePair(point, value, false);
+ }
+ }
+}
diff --git a/src/main/java/org/apache/commons/math3/optim/PointVectorValuePair.java b/src/main/java/org/apache/commons/math3/optim/PointVectorValuePair.java
new file mode 100644
index 0000000..c0ba93e
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/optim/PointVectorValuePair.java
@@ -0,0 +1,139 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math3.optim;
+
+import org.apache.commons.math3.util.Pair;
+
+import java.io.Serializable;
+
+/**
+ * This class holds a point and the vectorial value of an objective function at that point.
+ *
+ * @see PointValuePair
+ * @see org.apache.commons.math3.analysis.MultivariateVectorFunction
+ * @since 3.0
+ */
+public class PointVectorValuePair extends Pair<double[], double[]> implements Serializable {
+ /** Serializable UID. */
+ private static final long serialVersionUID = 20120513L;
+
+ /**
+ * Builds a point/objective function value pair.
+ *
+ * @param point Point coordinates. This instance will store a copy of the array, not the array
+ * passed as argument.
+ * @param value Value of the objective function at the point.
+ */
+ public PointVectorValuePair(final double[] point, final double[] value) {
+ this(point, value, true);
+ }
+
+ /**
+ * Build a point/objective function value pair.
+ *
+ * @param point Point coordinates.
+ * @param value Value of the objective function at the point.
+ * @param copyArray if {@code true}, the input arrays will be copied, otherwise they will be
+ * referenced.
+ */
+ public PointVectorValuePair(
+ final double[] point, final double[] value, final boolean copyArray) {
+ super(
+ copyArray ? ((point == null) ? null : point.clone()) : point,
+ copyArray ? ((value == null) ? null : value.clone()) : value);
+ }
+
+ /**
+ * Gets the point.
+ *
+ * @return a copy of the stored point.
+ */
+ public double[] getPoint() {
+ final double[] p = getKey();
+ return p == null ? null : p.clone();
+ }
+
+ /**
+ * Gets a reference to the point.
+ *
+ * @return a reference to the internal array storing the point.
+ */
+ public double[] getPointRef() {
+ return getKey();
+ }
+
+ /**
+ * Gets the value of the objective function.
+ *
+ * @return a copy of the stored value of the objective function.
+ */
+ @Override
+ public double[] getValue() {
+ final double[] v = super.getValue();
+ return v == null ? null : v.clone();
+ }
+
+ /**
+ * Gets a reference to the value of the objective function.
+ *
+ * @return a reference to the internal array storing the value of the objective function.
+ */
+ public double[] getValueRef() {
+ return super.getValue();
+ }
+
+ /**
+ * Replace the instance with a data transfer object for serialization.
+ *
+ * @return data transfer object that will be serialized
+ */
+ private Object writeReplace() {
+ return new DataTransferObject(getKey(), getValue());
+ }
+
+ /** Internal class used only for serialization. */
+ private static class DataTransferObject implements Serializable {
+ /** Serializable UID. */
+ private static final long serialVersionUID = 20120513L;
+
+ /** Point coordinates. @Serial */
+ private final double[] point;
+
+ /** Value of the objective function at the point. @Serial */
+ private final double[] value;
+
+ /**
+ * Simple constructor.
+ *
+ * @param point Point coordinates.
+ * @param value Value of the objective function at the point.
+ */
+ DataTransferObject(final double[] point, final double[] value) {
+ this.point = point.clone();
+ this.value = value.clone();
+ }
+
+ /**
+ * Replace the deserialized data transfer object with a {@link PointValuePair}.
+ *
+ * @return replacement {@link PointValuePair}
+ */
+ private Object readResolve() {
+ return new PointVectorValuePair(point, value, false);
+ }
+ }
+}
diff --git a/src/main/java/org/apache/commons/math3/optim/SimpleBounds.java b/src/main/java/org/apache/commons/math3/optim/SimpleBounds.java
new file mode 100644
index 0000000..8fffb25
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/optim/SimpleBounds.java
@@ -0,0 +1,77 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math3.optim;
+
+import java.util.Arrays;
+
+/**
+ * Simple optimization constraints: lower and upper bounds. The valid range of the parameters is an
+ * interval that can be infinite (in one or both directions). <br>
+ * Immutable class.
+ *
+ * @since 3.1
+ */
+public class SimpleBounds implements OptimizationData {
+ /** Lower bounds. */
+ private final double[] lower;
+
+ /** Upper bounds. */
+ private final double[] upper;
+
+ /**
+ * @param lB Lower bounds.
+ * @param uB Upper bounds.
+ */
+ public SimpleBounds(double[] lB, double[] uB) {
+ lower = lB.clone();
+ upper = uB.clone();
+ }
+
+ /**
+ * Gets the lower bounds.
+ *
+ * @return the lower bounds.
+ */
+ public double[] getLower() {
+ return lower.clone();
+ }
+
+ /**
+ * Gets the upper bounds.
+ *
+ * @return the upper bounds.
+ */
+ public double[] getUpper() {
+ return upper.clone();
+ }
+
+ /**
+ * Factory method that creates instance of this class that represents unbounded ranges.
+ *
+ * @param dim Number of parameters.
+ * @return a new instance suitable for passing to an optimizer that requires bounds
+ * specification.
+ */
+ public static SimpleBounds unbounded(int dim) {
+ final double[] lB = new double[dim];
+ Arrays.fill(lB, Double.NEGATIVE_INFINITY);
+ final double[] uB = new double[dim];
+ Arrays.fill(uB, Double.POSITIVE_INFINITY);
+
+ return new SimpleBounds(lB, uB);
+ }
+}
diff --git a/src/main/java/org/apache/commons/math3/optim/SimplePointChecker.java b/src/main/java/org/apache/commons/math3/optim/SimplePointChecker.java
new file mode 100644
index 0000000..f831de6
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/optim/SimplePointChecker.java
@@ -0,0 +1,117 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math3.optim;
+
+import org.apache.commons.math3.exception.NotStrictlyPositiveException;
+import org.apache.commons.math3.util.FastMath;
+import org.apache.commons.math3.util.Pair;
+
+/**
+ * Simple implementation of the {@link ConvergenceChecker} interface using only point coordinates.
+ *
+ * <p>Convergence is considered to have been reached if either the relative difference between each
+ * point coordinate are smaller than a threshold or if either the absolute difference between the
+ * point coordinates are smaller than another threshold. <br>
+ * The {@link #converged(int,Pair,Pair) converged} method will also return {@code true} if the
+ * number of iterations has been set (see {@link #SimplePointChecker(double,double,int) this
+ * constructor}).
+ *
+ * @param <PAIR> Type of the (point, value) pair. The type of the "value" part of the pair (not used
+ * by this class).
+ * @since 3.0
+ */
+public class SimplePointChecker<PAIR extends Pair<double[], ? extends Object>>
+ extends AbstractConvergenceChecker<PAIR> {
+ /**
+ * If {@link #maxIterationCount} is set to this value, the number of iterations will never cause
+ * {@link #converged(int, Pair, Pair)} to return {@code true}.
+ */
+ private static final int ITERATION_CHECK_DISABLED = -1;
+
+ /**
+ * Number of iterations after which the {@link #converged(int, Pair, Pair)} method will return
+ * true (unless the check is disabled).
+ */
+ private final int maxIterationCount;
+
+ /**
+ * Build an instance with specified thresholds. In order to perform only relative checks, the
+ * absolute tolerance must be set to a negative value. In order to perform only absolute checks,
+ * the relative tolerance must be set to a negative value.
+ *
+ * @param relativeThreshold relative tolerance threshold
+ * @param absoluteThreshold absolute tolerance threshold
+ */
+ public SimplePointChecker(final double relativeThreshold, final double absoluteThreshold) {
+ super(relativeThreshold, absoluteThreshold);
+ maxIterationCount = ITERATION_CHECK_DISABLED;
+ }
+
+ /**
+ * Builds an instance with specified thresholds. In order to perform only relative checks, the
+ * absolute tolerance must be set to a negative value. In order to perform only absolute checks,
+ * the relative tolerance must be set to a negative value.
+ *
+ * @param relativeThreshold Relative tolerance threshold.
+ * @param absoluteThreshold Absolute tolerance threshold.
+ * @param maxIter Maximum iteration count.
+ * @throws NotStrictlyPositiveException if {@code maxIter <= 0}.
+ * @since 3.1
+ */
+ public SimplePointChecker(
+ final double relativeThreshold, final double absoluteThreshold, final int maxIter) {
+ super(relativeThreshold, absoluteThreshold);
+
+ if (maxIter <= 0) {
+ throw new NotStrictlyPositiveException(maxIter);
+ }
+ maxIterationCount = maxIter;
+ }
+
+ /**
+ * Check if the optimization algorithm has converged considering the last two points. This
+ * method may be called several times from the same algorithm iteration with different points.
+ * This can be detected by checking the iteration number at each call if needed. Each time this
+ * method is called, the previous and current point correspond to points with the same role at
+ * each iteration, so they can be compared. As an example, simplex-based algorithms call this
+ * method for all points of the simplex, not only for the best or worst ones.
+ *
+ * @param iteration Index of current iteration
+ * @param previous Best point in the previous iteration.
+ * @param current Best point in the current iteration.
+ * @return {@code true} if the arguments satify the convergence criterion.
+ */
+ @Override
+ public boolean converged(final int iteration, final PAIR previous, final PAIR current) {
+ if (maxIterationCount != ITERATION_CHECK_DISABLED && iteration >= maxIterationCount) {
+ return true;
+ }
+
+ final double[] p = previous.getKey();
+ final double[] c = current.getKey();
+ for (int i = 0; i < p.length; ++i) {
+ final double pi = p[i];
+ final double ci = c[i];
+ final double difference = FastMath.abs(pi - ci);
+ final double size = FastMath.max(FastMath.abs(pi), FastMath.abs(ci));
+ if (difference > size * getRelativeThreshold() && difference > getAbsoluteThreshold()) {
+ return false;
+ }
+ }
+ return true;
+ }
+}
diff --git a/src/main/java/org/apache/commons/math3/optim/SimpleValueChecker.java b/src/main/java/org/apache/commons/math3/optim/SimpleValueChecker.java
new file mode 100644
index 0000000..968fc6e
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/optim/SimpleValueChecker.java
@@ -0,0 +1,113 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math3.optim;
+
+import org.apache.commons.math3.exception.NotStrictlyPositiveException;
+import org.apache.commons.math3.util.FastMath;
+
+/**
+ * Simple implementation of the {@link ConvergenceChecker} interface using only objective function
+ * values.
+ *
+ * <p>Convergence is considered to have been reached if either the relative difference between the
+ * objective function values is smaller than a threshold or if either the absolute difference
+ * between the objective function values is smaller than another threshold. <br>
+ * The {@link #converged(int,PointValuePair,PointValuePair) converged} method will also return
+ * {@code true} if the number of iterations has been set (see {@link
+ * #SimpleValueChecker(double,double,int) this constructor}).
+ *
+ * @since 3.0
+ */
+public class SimpleValueChecker extends AbstractConvergenceChecker<PointValuePair> {
+ /**
+ * If {@link #maxIterationCount} is set to this value, the number of iterations will never cause
+ * {@link #converged(int,PointValuePair,PointValuePair)} to return {@code true}.
+ */
+ private static final int ITERATION_CHECK_DISABLED = -1;
+
+ /**
+ * Number of iterations after which the {@link #converged(int,PointValuePair,PointValuePair)}
+ * method will return true (unless the check is disabled).
+ */
+ private final int maxIterationCount;
+
+ /**
+ * Build an instance with specified thresholds.
+ *
+ * <p>In order to perform only relative checks, the absolute tolerance must be set to a negative
+ * value. In order to perform only absolute checks, the relative tolerance must be set to a
+ * negative value.
+ *
+ * @param relativeThreshold relative tolerance threshold
+ * @param absoluteThreshold absolute tolerance threshold
+ */
+ public SimpleValueChecker(final double relativeThreshold, final double absoluteThreshold) {
+ super(relativeThreshold, absoluteThreshold);
+ maxIterationCount = ITERATION_CHECK_DISABLED;
+ }
+
+ /**
+ * Builds an instance with specified thresholds.
+ *
+ * <p>In order to perform only relative checks, the absolute tolerance must be set to a negative
+ * value. In order to perform only absolute checks, the relative tolerance must be set to a
+ * negative value.
+ *
+ * @param relativeThreshold relative tolerance threshold
+ * @param absoluteThreshold absolute tolerance threshold
+ * @param maxIter Maximum iteration count.
+ * @throws NotStrictlyPositiveException if {@code maxIter <= 0}.
+ * @since 3.1
+ */
+ public SimpleValueChecker(
+ final double relativeThreshold, final double absoluteThreshold, final int maxIter) {
+ super(relativeThreshold, absoluteThreshold);
+
+ if (maxIter <= 0) {
+ throw new NotStrictlyPositiveException(maxIter);
+ }
+ maxIterationCount = maxIter;
+ }
+
+ /**
+ * Check if the optimization algorithm has converged considering the last two points. This
+ * method may be called several time from the same algorithm iteration with different points.
+ * This can be detected by checking the iteration number at each call if needed. Each time this
+ * method is called, the previous and current point correspond to points with the same role at
+ * each iteration, so they can be compared. As an example, simplex-based algorithms call this
+ * method for all points of the simplex, not only for the best or worst ones.
+ *
+ * @param iteration Index of current iteration
+ * @param previous Best point in the previous iteration.
+ * @param current Best point in the current iteration.
+ * @return {@code true} if the algorithm has converged.
+ */
+ @Override
+ public boolean converged(
+ final int iteration, final PointValuePair previous, final PointValuePair current) {
+ if (maxIterationCount != ITERATION_CHECK_DISABLED && iteration >= maxIterationCount) {
+ return true;
+ }
+
+ final double p = previous.getValue();
+ final double c = current.getValue();
+ final double difference = FastMath.abs(p - c);
+ final double size = FastMath.max(FastMath.abs(p), FastMath.abs(c));
+ return difference <= size * getRelativeThreshold() || difference <= getAbsoluteThreshold();
+ }
+}
diff --git a/src/main/java/org/apache/commons/math3/optim/SimpleVectorValueChecker.java b/src/main/java/org/apache/commons/math3/optim/SimpleVectorValueChecker.java
new file mode 100644
index 0000000..0327bd1
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/optim/SimpleVectorValueChecker.java
@@ -0,0 +1,125 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math3.optim;
+
+import org.apache.commons.math3.exception.NotStrictlyPositiveException;
+import org.apache.commons.math3.util.FastMath;
+
+/**
+ * Simple implementation of the {@link ConvergenceChecker} interface using only objective function
+ * values.
+ *
+ * <p>Convergence is considered to have been reached if either the relative difference between the
+ * objective function values is smaller than a threshold or if either the absolute difference
+ * between the objective function values is smaller than another threshold for all vectors elements.
+ * <br>
+ * The {@link #converged(int,PointVectorValuePair,PointVectorValuePair) converged} method will also
+ * return {@code true} if the number of iterations has been set (see {@link
+ * #SimpleVectorValueChecker(double,double,int) this constructor}).
+ *
+ * @since 3.0
+ */
+public class SimpleVectorValueChecker extends AbstractConvergenceChecker<PointVectorValuePair> {
+ /**
+ * If {@link #maxIterationCount} is set to this value, the number of iterations will never cause
+ * {@link #converged(int,PointVectorValuePair,PointVectorValuePair)} to return {@code true}.
+ */
+ private static final int ITERATION_CHECK_DISABLED = -1;
+
+ /**
+ * Number of iterations after which the {@link
+ * #converged(int,PointVectorValuePair,PointVectorValuePair)} method will return true (unless
+ * the check is disabled).
+ */
+ private final int maxIterationCount;
+
+ /**
+ * Build an instance with specified thresholds.
+ *
+ * <p>In order to perform only relative checks, the absolute tolerance must be set to a negative
+ * value. In order to perform only absolute checks, the relative tolerance must be set to a
+ * negative value.
+ *
+ * @param relativeThreshold relative tolerance threshold
+ * @param absoluteThreshold absolute tolerance threshold
+ */
+ public SimpleVectorValueChecker(
+ final double relativeThreshold, final double absoluteThreshold) {
+ super(relativeThreshold, absoluteThreshold);
+ maxIterationCount = ITERATION_CHECK_DISABLED;
+ }
+
+ /**
+ * Builds an instance with specified tolerance thresholds and iteration count.
+ *
+ * <p>In order to perform only relative checks, the absolute tolerance must be set to a negative
+ * value. In order to perform only absolute checks, the relative tolerance must be set to a
+ * negative value.
+ *
+ * @param relativeThreshold Relative tolerance threshold.
+ * @param absoluteThreshold Absolute tolerance threshold.
+ * @param maxIter Maximum iteration count.
+ * @throws NotStrictlyPositiveException if {@code maxIter <= 0}.
+ * @since 3.1
+ */
+ public SimpleVectorValueChecker(
+ final double relativeThreshold, final double absoluteThreshold, final int maxIter) {
+ super(relativeThreshold, absoluteThreshold);
+
+ if (maxIter <= 0) {
+ throw new NotStrictlyPositiveException(maxIter);
+ }
+ maxIterationCount = maxIter;
+ }
+
+ /**
+ * Check if the optimization algorithm has converged considering the last two points. This
+ * method may be called several times from the same algorithm iteration with different points.
+ * This can be detected by checking the iteration number at each call if needed. Each time this
+ * method is called, the previous and current point correspond to points with the same role at
+ * each iteration, so they can be compared. As an example, simplex-based algorithms call this
+ * method for all points of the simplex, not only for the best or worst ones.
+ *
+ * @param iteration Index of current iteration
+ * @param previous Best point in the previous iteration.
+ * @param current Best point in the current iteration.
+ * @return {@code true} if the arguments satify the convergence criterion.
+ */
+ @Override
+ public boolean converged(
+ final int iteration,
+ final PointVectorValuePair previous,
+ final PointVectorValuePair current) {
+ if (maxIterationCount != ITERATION_CHECK_DISABLED && iteration >= maxIterationCount) {
+ return true;
+ }
+
+ final double[] p = previous.getValueRef();
+ final double[] c = current.getValueRef();
+ for (int i = 0; i < p.length; ++i) {
+ final double pi = p[i];
+ final double ci = c[i];
+ final double difference = FastMath.abs(pi - ci);
+ final double size = FastMath.max(FastMath.abs(pi), FastMath.abs(ci));
+ if (difference > size * getRelativeThreshold() && difference > getAbsoluteThreshold()) {
+ return false;
+ }
+ }
+ return true;
+ }
+}
diff --git a/src/main/java/org/apache/commons/math3/optim/linear/LinearConstraint.java b/src/main/java/org/apache/commons/math3/optim/linear/LinearConstraint.java
new file mode 100644
index 0000000..b9fa390
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/optim/linear/LinearConstraint.java
@@ -0,0 +1,230 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math3.optim.linear;
+
+import java.io.IOException;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+import java.io.Serializable;
+import org.apache.commons.math3.linear.MatrixUtils;
+import org.apache.commons.math3.linear.RealVector;
+import org.apache.commons.math3.linear.ArrayRealVector;
+
+/**
+ * A linear constraint for a linear optimization problem.
+ * <p>
+ * A linear constraint has one of the forms:
+ * <ul>
+ * <li>c<sub>1</sub>x<sub>1</sub> + ... c<sub>n</sub>x<sub>n</sub> = v</li>
+ * <li>c<sub>1</sub>x<sub>1</sub> + ... c<sub>n</sub>x<sub>n</sub> &lt;= v</li>
+ * <li>c<sub>1</sub>x<sub>1</sub> + ... c<sub>n</sub>x<sub>n</sub> >= v</li>
+ * <li>l<sub>1</sub>x<sub>1</sub> + ... l<sub>n</sub>x<sub>n</sub> + l<sub>cst</sub> =
+ * r<sub>1</sub>x<sub>1</sub> + ... r<sub>n</sub>x<sub>n</sub> + r<sub>cst</sub></li>
+ * <li>l<sub>1</sub>x<sub>1</sub> + ... l<sub>n</sub>x<sub>n</sub> + l<sub>cst</sub> &lt;=
+ * r<sub>1</sub>x<sub>1</sub> + ... r<sub>n</sub>x<sub>n</sub> + r<sub>cst</sub></li>
+ * <li>l<sub>1</sub>x<sub>1</sub> + ... l<sub>n</sub>x<sub>n</sub> + l<sub>cst</sub> >=
+ * r<sub>1</sub>x<sub>1</sub> + ... r<sub>n</sub>x<sub>n</sub> + r<sub>cst</sub></li>
+ * </ul>
+ * The c<sub>i</sub>, l<sub>i</sub> or r<sub>i</sub> are the coefficients of the constraints, the x<sub>i</sub>
+ * are the coordinates of the current point and v is the value of the constraint.
+ * </p>
+ *
+ * @since 2.0
+ */
+public class LinearConstraint implements Serializable {
+ /** Serializable version identifier. */
+ private static final long serialVersionUID = -764632794033034092L;
+ /** Coefficients of the constraint (left hand side). */
+ private final transient RealVector coefficients;
+ /** Relationship between left and right hand sides (=, &lt;=, >=). */
+ private final Relationship relationship;
+ /** Value of the constraint (right hand side). */
+ private final double value;
+
+ /**
+ * Build a constraint involving a single linear equation.
+ * <p>
+ * A linear constraint with a single linear equation has one of the forms:
+ * <ul>
+ * <li>c<sub>1</sub>x<sub>1</sub> + ... c<sub>n</sub>x<sub>n</sub> = v</li>
+ * <li>c<sub>1</sub>x<sub>1</sub> + ... c<sub>n</sub>x<sub>n</sub> &lt;= v</li>
+ * <li>c<sub>1</sub>x<sub>1</sub> + ... c<sub>n</sub>x<sub>n</sub> >= v</li>
+ * </ul>
+ * </p>
+ * @param coefficients The coefficients of the constraint (left hand side)
+ * @param relationship The type of (in)equality used in the constraint
+ * @param value The value of the constraint (right hand side)
+ */
+ public LinearConstraint(final double[] coefficients,
+ final Relationship relationship,
+ final double value) {
+ this(new ArrayRealVector(coefficients), relationship, value);
+ }
+
+ /**
+ * Build a constraint involving a single linear equation.
+ * <p>
+ * A linear constraint with a single linear equation has one of the forms:
+ * <ul>
+ * <li>c<sub>1</sub>x<sub>1</sub> + ... c<sub>n</sub>x<sub>n</sub> = v</li>
+ * <li>c<sub>1</sub>x<sub>1</sub> + ... c<sub>n</sub>x<sub>n</sub> &lt;= v</li>
+ * <li>c<sub>1</sub>x<sub>1</sub> + ... c<sub>n</sub>x<sub>n</sub> >= v</li>
+ * </ul>
+ * </p>
+ * @param coefficients The coefficients of the constraint (left hand side)
+ * @param relationship The type of (in)equality used in the constraint
+ * @param value The value of the constraint (right hand side)
+ */
+ public LinearConstraint(final RealVector coefficients,
+ final Relationship relationship,
+ final double value) {
+ this.coefficients = coefficients;
+ this.relationship = relationship;
+ this.value = value;
+ }
+
+ /**
+ * Build a constraint involving two linear equations.
+ * <p>
+ * A linear constraint with two linear equation has one of the forms:
+ * <ul>
+ * <li>l<sub>1</sub>x<sub>1</sub> + ... l<sub>n</sub>x<sub>n</sub> + l<sub>cst</sub> =
+ * r<sub>1</sub>x<sub>1</sub> + ... r<sub>n</sub>x<sub>n</sub> + r<sub>cst</sub></li>
+ * <li>l<sub>1</sub>x<sub>1</sub> + ... l<sub>n</sub>x<sub>n</sub> + l<sub>cst</sub> &lt;=
+ * r<sub>1</sub>x<sub>1</sub> + ... r<sub>n</sub>x<sub>n</sub> + r<sub>cst</sub></li>
+ * <li>l<sub>1</sub>x<sub>1</sub> + ... l<sub>n</sub>x<sub>n</sub> + l<sub>cst</sub> >=
+ * r<sub>1</sub>x<sub>1</sub> + ... r<sub>n</sub>x<sub>n</sub> + r<sub>cst</sub></li>
+ * </ul>
+ * </p>
+ * @param lhsCoefficients The coefficients of the linear expression on the left hand side of the constraint
+ * @param lhsConstant The constant term of the linear expression on the left hand side of the constraint
+ * @param relationship The type of (in)equality used in the constraint
+ * @param rhsCoefficients The coefficients of the linear expression on the right hand side of the constraint
+ * @param rhsConstant The constant term of the linear expression on the right hand side of the constraint
+ */
+ public LinearConstraint(final double[] lhsCoefficients, final double lhsConstant,
+ final Relationship relationship,
+ final double[] rhsCoefficients, final double rhsConstant) {
+ double[] sub = new double[lhsCoefficients.length];
+ for (int i = 0; i < sub.length; ++i) {
+ sub[i] = lhsCoefficients[i] - rhsCoefficients[i];
+ }
+ this.coefficients = new ArrayRealVector(sub, false);
+ this.relationship = relationship;
+ this.value = rhsConstant - lhsConstant;
+ }
+
+ /**
+ * Build a constraint involving two linear equations.
+ * <p>
+ * A linear constraint with two linear equation has one of the forms:
+ * <ul>
+ * <li>l<sub>1</sub>x<sub>1</sub> + ... l<sub>n</sub>x<sub>n</sub> + l<sub>cst</sub> =
+ * r<sub>1</sub>x<sub>1</sub> + ... r<sub>n</sub>x<sub>n</sub> + r<sub>cst</sub></li>
+ * <li>l<sub>1</sub>x<sub>1</sub> + ... l<sub>n</sub>x<sub>n</sub> + l<sub>cst</sub> &lt;=
+ * r<sub>1</sub>x<sub>1</sub> + ... r<sub>n</sub>x<sub>n</sub> + r<sub>cst</sub></li>
+ * <li>l<sub>1</sub>x<sub>1</sub> + ... l<sub>n</sub>x<sub>n</sub> + l<sub>cst</sub> >=
+ * r<sub>1</sub>x<sub>1</sub> + ... r<sub>n</sub>x<sub>n</sub> + r<sub>cst</sub></li>
+ * </ul>
+ * </p>
+ * @param lhsCoefficients The coefficients of the linear expression on the left hand side of the constraint
+ * @param lhsConstant The constant term of the linear expression on the left hand side of the constraint
+ * @param relationship The type of (in)equality used in the constraint
+ * @param rhsCoefficients The coefficients of the linear expression on the right hand side of the constraint
+ * @param rhsConstant The constant term of the linear expression on the right hand side of the constraint
+ */
+ public LinearConstraint(final RealVector lhsCoefficients, final double lhsConstant,
+ final Relationship relationship,
+ final RealVector rhsCoefficients, final double rhsConstant) {
+ this.coefficients = lhsCoefficients.subtract(rhsCoefficients);
+ this.relationship = relationship;
+ this.value = rhsConstant - lhsConstant;
+ }
+
+ /**
+ * Gets the coefficients of the constraint (left hand side).
+ *
+ * @return the coefficients of the constraint (left hand side).
+ */
+ public RealVector getCoefficients() {
+ return coefficients;
+ }
+
+ /**
+ * Gets the relationship between left and right hand sides.
+ *
+ * @return the relationship between left and right hand sides.
+ */
+ public Relationship getRelationship() {
+ return relationship;
+ }
+
+ /**
+ * Gets the value of the constraint (right hand side).
+ *
+ * @return the value of the constraint (right hand side).
+ */
+ public double getValue() {
+ return value;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public boolean equals(Object other) {
+ if (this == other) {
+ return true;
+ }
+ if (other instanceof LinearConstraint) {
+ LinearConstraint rhs = (LinearConstraint) other;
+ return relationship == rhs.relationship &&
+ value == rhs.value &&
+ coefficients.equals(rhs.coefficients);
+ }
+ return false;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public int hashCode() {
+ return relationship.hashCode() ^
+ Double.valueOf(value).hashCode() ^
+ coefficients.hashCode();
+ }
+
+ /**
+ * Serialize the instance.
+ * @param oos stream where object should be written
+ * @throws IOException if object cannot be written to stream
+ */
+ private void writeObject(ObjectOutputStream oos)
+ throws IOException {
+ oos.defaultWriteObject();
+ MatrixUtils.serializeRealVector(coefficients, oos);
+ }
+
+ /**
+ * Deserialize the instance.
+ * @param ois stream from which the object should be read
+ * @throws ClassNotFoundException if a class in the stream cannot be found
+ * @throws IOException if object cannot be read from the stream
+ */
+ private void readObject(ObjectInputStream ois)
+ throws ClassNotFoundException, IOException {
+ ois.defaultReadObject();
+ MatrixUtils.deserializeRealVector(this, "coefficients", ois);
+ }
+}
diff --git a/src/main/java/org/apache/commons/math3/optim/linear/LinearConstraintSet.java b/src/main/java/org/apache/commons/math3/optim/linear/LinearConstraintSet.java
new file mode 100644
index 0000000..d54bd61
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/optim/linear/LinearConstraintSet.java
@@ -0,0 +1,63 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math3.optim.linear;
+
+import java.util.LinkedHashSet;
+import java.util.Set;
+import java.util.Collection;
+import java.util.Collections;
+
+import org.apache.commons.math3.optim.OptimizationData;
+
+/**
+ * Class that represents a set of {@link LinearConstraint linear constraints}.
+ *
+ * @since 3.1
+ */
+public class LinearConstraintSet implements OptimizationData {
+ /** Set of constraints. */
+ private final Set<LinearConstraint> linearConstraints = new LinkedHashSet<LinearConstraint>();
+
+ /**
+ * Creates a set containing the given constraints.
+ *
+ * @param constraints Constraints.
+ */
+ public LinearConstraintSet(LinearConstraint... constraints) {
+ for (LinearConstraint c : constraints) {
+ linearConstraints.add(c);
+ }
+ }
+
+ /**
+ * Creates a set containing the given constraints.
+ *
+ * @param constraints Constraints.
+ */
+ public LinearConstraintSet(Collection<LinearConstraint> constraints) {
+ linearConstraints.addAll(constraints);
+ }
+
+ /**
+ * Gets the set of linear constraints.
+ *
+ * @return the constraints.
+ */
+ public Collection<LinearConstraint> getConstraints() {
+ return Collections.unmodifiableSet(linearConstraints);
+ }
+}
diff --git a/src/main/java/org/apache/commons/math3/optim/linear/LinearObjectiveFunction.java b/src/main/java/org/apache/commons/math3/optim/linear/LinearObjectiveFunction.java
new file mode 100644
index 0000000..6cff81f
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/optim/linear/LinearObjectiveFunction.java
@@ -0,0 +1,150 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math3.optim.linear;
+
+import java.io.IOException;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+import java.io.Serializable;
+import org.apache.commons.math3.analysis.MultivariateFunction;
+import org.apache.commons.math3.linear.MatrixUtils;
+import org.apache.commons.math3.linear.RealVector;
+import org.apache.commons.math3.linear.ArrayRealVector;
+import org.apache.commons.math3.optim.OptimizationData;
+
+/**
+ * An objective function for a linear optimization problem.
+ * <p>
+ * A linear objective function has one the form:
+ * <pre>
+ * c<sub>1</sub>x<sub>1</sub> + ... c<sub>n</sub>x<sub>n</sub> + d
+ * </pre>
+ * The c<sub>i</sub> and d are the coefficients of the equation,
+ * the x<sub>i</sub> are the coordinates of the current point.
+ * </p>
+ *
+ * @since 2.0
+ */
+public class LinearObjectiveFunction
+ implements MultivariateFunction,
+ OptimizationData,
+ Serializable {
+ /** Serializable version identifier. */
+ private static final long serialVersionUID = -4531815507568396090L;
+ /** Coefficients of the linear equation (c<sub>i</sub>). */
+ private final transient RealVector coefficients;
+ /** Constant term of the linear equation. */
+ private final double constantTerm;
+
+ /**
+ * @param coefficients Coefficients for the linear equation being optimized.
+ * @param constantTerm Constant term of the linear equation.
+ */
+ public LinearObjectiveFunction(double[] coefficients, double constantTerm) {
+ this(new ArrayRealVector(coefficients), constantTerm);
+ }
+
+ /**
+ * @param coefficients Coefficients for the linear equation being optimized.
+ * @param constantTerm Constant term of the linear equation.
+ */
+ public LinearObjectiveFunction(RealVector coefficients, double constantTerm) {
+ this.coefficients = coefficients;
+ this.constantTerm = constantTerm;
+ }
+
+ /**
+ * Gets the coefficients of the linear equation being optimized.
+ *
+ * @return coefficients of the linear equation being optimized.
+ */
+ public RealVector getCoefficients() {
+ return coefficients;
+ }
+
+ /**
+ * Gets the constant of the linear equation being optimized.
+ *
+ * @return constant of the linear equation being optimized.
+ */
+ public double getConstantTerm() {
+ return constantTerm;
+ }
+
+ /**
+ * Computes the value of the linear equation at the current point.
+ *
+ * @param point Point at which linear equation must be evaluated.
+ * @return the value of the linear equation at the current point.
+ */
+ public double value(final double[] point) {
+ return value(new ArrayRealVector(point, false));
+ }
+
+ /**
+ * Computes the value of the linear equation at the current point.
+ *
+ * @param point Point at which linear equation must be evaluated.
+ * @return the value of the linear equation at the current point.
+ */
+ public double value(final RealVector point) {
+ return coefficients.dotProduct(point) + constantTerm;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public boolean equals(Object other) {
+ if (this == other) {
+ return true;
+ }
+ if (other instanceof LinearObjectiveFunction) {
+ LinearObjectiveFunction rhs = (LinearObjectiveFunction) other;
+ return (constantTerm == rhs.constantTerm) && coefficients.equals(rhs.coefficients);
+ }
+
+ return false;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public int hashCode() {
+ return Double.valueOf(constantTerm).hashCode() ^ coefficients.hashCode();
+ }
+
+ /**
+ * Serialize the instance.
+ * @param oos stream where object should be written
+ * @throws IOException if object cannot be written to stream
+ */
+ private void writeObject(ObjectOutputStream oos)
+ throws IOException {
+ oos.defaultWriteObject();
+ MatrixUtils.serializeRealVector(coefficients, oos);
+ }
+
+ /**
+ * Deserialize the instance.
+ * @param ois stream from which the object should be read
+ * @throws ClassNotFoundException if a class in the stream cannot be found
+ * @throws IOException if object cannot be read from the stream
+ */
+ private void readObject(ObjectInputStream ois)
+ throws ClassNotFoundException, IOException {
+ ois.defaultReadObject();
+ MatrixUtils.deserializeRealVector(this, "coefficients", ois);
+ }
+}
diff --git a/src/main/java/org/apache/commons/math3/optim/linear/LinearOptimizer.java b/src/main/java/org/apache/commons/math3/optim/linear/LinearOptimizer.java
new file mode 100644
index 0000000..7e80687
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/optim/linear/LinearOptimizer.java
@@ -0,0 +1,131 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math3.optim.linear;
+
+import java.util.Collection;
+import java.util.Collections;
+import org.apache.commons.math3.exception.TooManyIterationsException;
+import org.apache.commons.math3.optim.OptimizationData;
+import org.apache.commons.math3.optim.PointValuePair;
+import org.apache.commons.math3.optim.nonlinear.scalar.MultivariateOptimizer;
+
+/**
+ * Base class for implementing linear optimizers.
+ *
+ * @since 3.1
+ */
+public abstract class LinearOptimizer
+ extends MultivariateOptimizer {
+ /**
+ * Linear objective function.
+ */
+ private LinearObjectiveFunction function;
+ /**
+ * Linear constraints.
+ */
+ private Collection<LinearConstraint> linearConstraints;
+ /**
+ * Whether to restrict the variables to non-negative values.
+ */
+ private boolean nonNegative;
+
+ /**
+ * Simple constructor with default settings.
+ *
+ */
+ protected LinearOptimizer() {
+ super(null); // No convergence checker.
+ }
+
+ /**
+ * @return {@code true} if the variables are restricted to non-negative values.
+ */
+ protected boolean isRestrictedToNonNegative() {
+ return nonNegative;
+ }
+
+ /**
+ * @return the optimization type.
+ */
+ protected LinearObjectiveFunction getFunction() {
+ return function;
+ }
+
+ /**
+ * @return the optimization type.
+ */
+ protected Collection<LinearConstraint> getConstraints() {
+ return Collections.unmodifiableCollection(linearConstraints);
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @param optData Optimization data. In addition to those documented in
+ * {@link MultivariateOptimizer#parseOptimizationData(OptimizationData[])
+ * MultivariateOptimizer}, this method will register the following data:
+ * <ul>
+ * <li>{@link LinearObjectiveFunction}</li>
+ * <li>{@link LinearConstraintSet}</li>
+ * <li>{@link NonNegativeConstraint}</li>
+ * </ul>
+ * @return {@inheritDoc}
+ * @throws TooManyIterationsException if the maximal number of
+ * iterations is exceeded.
+ */
+ @Override
+ public PointValuePair optimize(OptimizationData... optData)
+ throws TooManyIterationsException {
+ // Set up base class and perform computation.
+ return super.optimize(optData);
+ }
+
+ /**
+ * Scans the list of (required and optional) optimization data that
+ * characterize the problem.
+ *
+ * @param optData Optimization data.
+ * The following data will be looked for:
+ * <ul>
+ * <li>{@link LinearObjectiveFunction}</li>
+ * <li>{@link LinearConstraintSet}</li>
+ * <li>{@link NonNegativeConstraint}</li>
+ * </ul>
+ */
+ @Override
+ protected void parseOptimizationData(OptimizationData... optData) {
+ // Allow base class to register its own data.
+ super.parseOptimizationData(optData);
+
+ // The existing values (as set by the previous call) are reused if
+ // not provided in the argument list.
+ for (OptimizationData data : optData) {
+ if (data instanceof LinearObjectiveFunction) {
+ function = (LinearObjectiveFunction) data;
+ continue;
+ }
+ if (data instanceof LinearConstraintSet) {
+ linearConstraints = ((LinearConstraintSet) data).getConstraints();
+ continue;
+ }
+ if (data instanceof NonNegativeConstraint) {
+ nonNegative = ((NonNegativeConstraint) data).isRestrictedToNonNegative();
+ continue;
+ }
+ }
+ }
+}
diff --git a/src/main/java/org/apache/commons/math3/optim/linear/NoFeasibleSolutionException.java b/src/main/java/org/apache/commons/math3/optim/linear/NoFeasibleSolutionException.java
new file mode 100644
index 0000000..cbe8321
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/optim/linear/NoFeasibleSolutionException.java
@@ -0,0 +1,37 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math3.optim.linear;
+
+import org.apache.commons.math3.exception.MathIllegalStateException;
+import org.apache.commons.math3.exception.util.LocalizedFormats;
+
+/**
+ * This class represents exceptions thrown by optimizers when no solution fulfills the constraints.
+ *
+ * @since 2.0
+ */
+public class NoFeasibleSolutionException extends MathIllegalStateException {
+ /** Serializable version identifier. */
+ private static final long serialVersionUID = -3044253632189082760L;
+
+ /**
+ * Simple constructor using a default message.
+ */
+ public NoFeasibleSolutionException() {
+ super(LocalizedFormats.NO_FEASIBLE_SOLUTION);
+ }
+}
diff --git a/src/main/java/org/apache/commons/math3/optim/linear/NonNegativeConstraint.java b/src/main/java/org/apache/commons/math3/optim/linear/NonNegativeConstraint.java
new file mode 100644
index 0000000..dafcb63
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/optim/linear/NonNegativeConstraint.java
@@ -0,0 +1,47 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math3.optim.linear;
+
+import org.apache.commons.math3.optim.OptimizationData;
+
+/**
+ * A constraint for a linear optimization problem indicating whether all
+ * variables must be restricted to non-negative values.
+ *
+ * @since 3.1
+ */
+public class NonNegativeConstraint implements OptimizationData {
+ /** Whether the variables are all positive. */
+ private final boolean isRestricted;
+
+ /**
+ * @param restricted If {@code true}, all the variables must be positive.
+ */
+ public NonNegativeConstraint(boolean restricted) {
+ isRestricted = restricted;
+ }
+
+ /**
+ * Indicates whether all the variables must be restricted to non-negative
+ * values.
+ *
+ * @return {@code true} if all the variables must be positive.
+ */
+ public boolean isRestrictedToNonNegative() {
+ return isRestricted;
+ }
+}
diff --git a/src/main/java/org/apache/commons/math3/optim/linear/PivotSelectionRule.java b/src/main/java/org/apache/commons/math3/optim/linear/PivotSelectionRule.java
new file mode 100644
index 0000000..a2a2765
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/optim/linear/PivotSelectionRule.java
@@ -0,0 +1,38 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math3.optim.linear;
+
+import org.apache.commons.math3.optim.OptimizationData;
+
+/**
+ * Pivot selection rule to the use for a Simplex solver.
+ *
+ * @since 3.3
+ */
+public enum PivotSelectionRule implements OptimizationData {
+ /**
+ * The classical rule, the variable with the most negative coefficient
+ * in the objective function row will be chosen as entering variable.
+ */
+ DANTZIG,
+ /**
+ * The first variable with a negative coefficient in the objective function
+ * row will be chosen as entering variable. This rule guarantees to prevent
+ * cycles, but may take longer to find an optimal solution.
+ */
+ BLAND
+}
diff --git a/src/main/java/org/apache/commons/math3/optim/linear/Relationship.java b/src/main/java/org/apache/commons/math3/optim/linear/Relationship.java
new file mode 100644
index 0000000..f88c938
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/optim/linear/Relationship.java
@@ -0,0 +1,65 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math3.optim.linear;
+
+/**
+ * Types of relationships between two cells in a Solver {@link LinearConstraint}.
+ *
+ * @since 2.0
+ */
+public enum Relationship {
+ /** Equality relationship. */
+ EQ("="),
+ /** Lesser than or equal relationship. */
+ LEQ("<="),
+ /** Greater than or equal relationship. */
+ GEQ(">=");
+
+ /** Display string for the relationship. */
+ private final String stringValue;
+
+ /**
+ * Simple constructor.
+ *
+ * @param stringValue Display string for the relationship.
+ */
+ Relationship(String stringValue) {
+ this.stringValue = stringValue;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public String toString() {
+ return stringValue;
+ }
+
+ /**
+ * Gets the relationship obtained when multiplying all coefficients by -1.
+ *
+ * @return the opposite relationship.
+ */
+ public Relationship oppositeRelationship() {
+ switch (this) {
+ case LEQ :
+ return GEQ;
+ case GEQ :
+ return LEQ;
+ default :
+ return EQ;
+ }
+ }
+}
diff --git a/src/main/java/org/apache/commons/math3/optim/linear/SimplexSolver.java b/src/main/java/org/apache/commons/math3/optim/linear/SimplexSolver.java
new file mode 100644
index 0000000..e95b657
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/optim/linear/SimplexSolver.java
@@ -0,0 +1,407 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math3.optim.linear;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.apache.commons.math3.exception.TooManyIterationsException;
+import org.apache.commons.math3.optim.OptimizationData;
+import org.apache.commons.math3.optim.PointValuePair;
+import org.apache.commons.math3.util.FastMath;
+import org.apache.commons.math3.util.Precision;
+
+/**
+ * Solves a linear problem using the "Two-Phase Simplex" method.
+ * <p>
+ * The {@link SimplexSolver} supports the following {@link OptimizationData} data provided
+ * as arguments to {@link #optimize(OptimizationData...)}:
+ * <ul>
+ * <li>objective function: {@link LinearObjectiveFunction} - mandatory</li>
+ * <li>linear constraints {@link LinearConstraintSet} - mandatory</li>
+ * <li>type of optimization: {@link org.apache.commons.math3.optim.nonlinear.scalar.GoalType GoalType}
+ * - optional, default: {@link org.apache.commons.math3.optim.nonlinear.scalar.GoalType#MINIMIZE MINIMIZE}</li>
+ * <li>whether to allow negative values as solution: {@link NonNegativeConstraint} - optional, default: true</li>
+ * <li>pivot selection rule: {@link PivotSelectionRule} - optional, default {@link PivotSelectionRule#DANTZIG}</li>
+ * <li>callback for the best solution: {@link SolutionCallback} - optional</li>
+ * <li>maximum number of iterations: {@link org.apache.commons.math3.optim.MaxIter} - optional, default: {@link Integer#MAX_VALUE}</li>
+ * </ul>
+ * <p>
+ * <b>Note:</b> Depending on the problem definition, the default convergence criteria
+ * may be too strict, resulting in {@link NoFeasibleSolutionException} or
+ * {@link TooManyIterationsException}. In such a case it is advised to adjust these
+ * criteria with more appropriate values, e.g. relaxing the epsilon value.
+ * <p>
+ * Default convergence criteria:
+ * <ul>
+ * <li>Algorithm convergence: 1e-6</li>
+ * <li>Floating-point comparisons: 10 ulp</li>
+ * <li>Cut-Off value: 1e-10</li>
+ * </ul>
+ * <p>
+ * The cut-off value has been introduced to handle the case of very small pivot elements
+ * in the Simplex tableau, as these may lead to numerical instabilities and degeneracy.
+ * Potential pivot elements smaller than this value will be treated as if they were zero
+ * and are thus not considered by the pivot selection mechanism. The default value is safe
+ * for many problems, but may need to be adjusted in case of very small coefficients
+ * used in either the {@link LinearConstraint} or {@link LinearObjectiveFunction}.
+ *
+ * @since 2.0
+ */
+public class SimplexSolver extends LinearOptimizer {
+ /** Default amount of error to accept in floating point comparisons (as ulps). */
+ static final int DEFAULT_ULPS = 10;
+
+ /** Default cut-off value. */
+ static final double DEFAULT_CUT_OFF = 1e-10;
+
+ /** Default amount of error to accept for algorithm convergence. */
+ private static final double DEFAULT_EPSILON = 1.0e-6;
+
+ /** Amount of error to accept for algorithm convergence. */
+ private final double epsilon;
+
+ /** Amount of error to accept in floating point comparisons (as ulps). */
+ private final int maxUlps;
+
+ /**
+ * Cut-off value for entries in the tableau: values smaller than the cut-off
+ * are treated as zero to improve numerical stability.
+ */
+ private final double cutOff;
+
+ /** The pivot selection method to use. */
+ private PivotSelectionRule pivotSelection;
+
+ /**
+ * The solution callback to access the best solution found so far in case
+ * the optimizer fails to find an optimal solution within the iteration limits.
+ */
+ private SolutionCallback solutionCallback;
+
+ /**
+ * Builds a simplex solver with default settings.
+ */
+ public SimplexSolver() {
+ this(DEFAULT_EPSILON, DEFAULT_ULPS, DEFAULT_CUT_OFF);
+ }
+
+ /**
+ * Builds a simplex solver with a specified accepted amount of error.
+ *
+ * @param epsilon Amount of error to accept for algorithm convergence.
+ */
+ public SimplexSolver(final double epsilon) {
+ this(epsilon, DEFAULT_ULPS, DEFAULT_CUT_OFF);
+ }
+
+ /**
+ * Builds a simplex solver with a specified accepted amount of error.
+ *
+ * @param epsilon Amount of error to accept for algorithm convergence.
+ * @param maxUlps Amount of error to accept in floating point comparisons.
+ */
+ public SimplexSolver(final double epsilon, final int maxUlps) {
+ this(epsilon, maxUlps, DEFAULT_CUT_OFF);
+ }
+
+ /**
+ * Builds a simplex solver with a specified accepted amount of error.
+ *
+ * @param epsilon Amount of error to accept for algorithm convergence.
+ * @param maxUlps Amount of error to accept in floating point comparisons.
+ * @param cutOff Values smaller than the cutOff are treated as zero.
+ */
+ public SimplexSolver(final double epsilon, final int maxUlps, final double cutOff) {
+ this.epsilon = epsilon;
+ this.maxUlps = maxUlps;
+ this.cutOff = cutOff;
+ this.pivotSelection = PivotSelectionRule.DANTZIG;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @param optData Optimization data. In addition to those documented in
+ * {@link LinearOptimizer#optimize(OptimizationData...)
+ * LinearOptimizer}, this method will register the following data:
+ * <ul>
+ * <li>{@link SolutionCallback}</li>
+ * <li>{@link PivotSelectionRule}</li>
+ * </ul>
+ *
+ * @return {@inheritDoc}
+ * @throws TooManyIterationsException if the maximal number of iterations is exceeded.
+ */
+ @Override
+ public PointValuePair optimize(OptimizationData... optData)
+ throws TooManyIterationsException {
+ // Set up base class and perform computation.
+ return super.optimize(optData);
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @param optData Optimization data.
+ * In addition to those documented in
+ * {@link LinearOptimizer#parseOptimizationData(OptimizationData[])
+ * LinearOptimizer}, this method will register the following data:
+ * <ul>
+ * <li>{@link SolutionCallback}</li>
+ * <li>{@link PivotSelectionRule}</li>
+ * </ul>
+ */
+ @Override
+ protected void parseOptimizationData(OptimizationData... optData) {
+ // Allow base class to register its own data.
+ super.parseOptimizationData(optData);
+
+ // reset the callback before parsing
+ solutionCallback = null;
+
+ for (OptimizationData data : optData) {
+ if (data instanceof SolutionCallback) {
+ solutionCallback = (SolutionCallback) data;
+ continue;
+ }
+ if (data instanceof PivotSelectionRule) {
+ pivotSelection = (PivotSelectionRule) data;
+ continue;
+ }
+ }
+ }
+
+ /**
+ * Returns the column with the most negative coefficient in the objective function row.
+ *
+ * @param tableau Simple tableau for the problem.
+ * @return the column with the most negative coefficient.
+ */
+ private Integer getPivotColumn(SimplexTableau tableau) {
+ double minValue = 0;
+ Integer minPos = null;
+ for (int i = tableau.getNumObjectiveFunctions(); i < tableau.getWidth() - 1; i++) {
+ final double entry = tableau.getEntry(0, i);
+ // check if the entry is strictly smaller than the current minimum
+ // do not use a ulp/epsilon check
+ if (entry < minValue) {
+ minValue = entry;
+ minPos = i;
+
+ // Bland's rule: chose the entering column with the lowest index
+ if (pivotSelection == PivotSelectionRule.BLAND && isValidPivotColumn(tableau, i)) {
+ break;
+ }
+ }
+ }
+ return minPos;
+ }
+
+ /**
+ * Checks whether the given column is valid pivot column, i.e. will result
+ * in a valid pivot row.
+ * <p>
+ * When applying Bland's rule to select the pivot column, it may happen that
+ * there is no corresponding pivot row. This method will check if the selected
+ * pivot column will return a valid pivot row.
+ *
+ * @param tableau simplex tableau for the problem
+ * @param col the column to test
+ * @return {@code true} if the pivot column is valid, {@code false} otherwise
+ */
+ private boolean isValidPivotColumn(SimplexTableau tableau, int col) {
+ for (int i = tableau.getNumObjectiveFunctions(); i < tableau.getHeight(); i++) {
+ final double entry = tableau.getEntry(i, col);
+
+ // do the same check as in getPivotRow
+ if (Precision.compareTo(entry, 0d, cutOff) > 0) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Returns the row with the minimum ratio as given by the minimum ratio test (MRT).
+ *
+ * @param tableau Simplex tableau for the problem.
+ * @param col Column to test the ratio of (see {@link #getPivotColumn(SimplexTableau)}).
+ * @return the row with the minimum ratio.
+ */
+ private Integer getPivotRow(SimplexTableau tableau, final int col) {
+ // create a list of all the rows that tie for the lowest score in the minimum ratio test
+ List<Integer> minRatioPositions = new ArrayList<Integer>();
+ double minRatio = Double.MAX_VALUE;
+ for (int i = tableau.getNumObjectiveFunctions(); i < tableau.getHeight(); i++) {
+ final double rhs = tableau.getEntry(i, tableau.getWidth() - 1);
+ final double entry = tableau.getEntry(i, col);
+
+ // only consider pivot elements larger than the cutOff threshold
+ // selecting others may lead to degeneracy or numerical instabilities
+ if (Precision.compareTo(entry, 0d, cutOff) > 0) {
+ final double ratio = FastMath.abs(rhs / entry);
+ // check if the entry is strictly equal to the current min ratio
+ // do not use a ulp/epsilon check
+ final int cmp = Double.compare(ratio, minRatio);
+ if (cmp == 0) {
+ minRatioPositions.add(i);
+ } else if (cmp < 0) {
+ minRatio = ratio;
+ minRatioPositions.clear();
+ minRatioPositions.add(i);
+ }
+ }
+ }
+
+ if (minRatioPositions.size() == 0) {
+ return null;
+ } else if (minRatioPositions.size() > 1) {
+ // there's a degeneracy as indicated by a tie in the minimum ratio test
+
+ // 1. check if there's an artificial variable that can be forced out of the basis
+ if (tableau.getNumArtificialVariables() > 0) {
+ for (Integer row : minRatioPositions) {
+ for (int i = 0; i < tableau.getNumArtificialVariables(); i++) {
+ int column = i + tableau.getArtificialVariableOffset();
+ final double entry = tableau.getEntry(row, column);
+ if (Precision.equals(entry, 1d, maxUlps) && row.equals(tableau.getBasicRow(column))) {
+ return row;
+ }
+ }
+ }
+ }
+
+ // 2. apply Bland's rule to prevent cycling:
+ // take the row for which the corresponding basic variable has the smallest index
+ //
+ // see http://www.stanford.edu/class/msande310/blandrule.pdf
+ // see http://en.wikipedia.org/wiki/Bland%27s_rule (not equivalent to the above paper)
+
+ Integer minRow = null;
+ int minIndex = tableau.getWidth();
+ for (Integer row : minRatioPositions) {
+ final int basicVar = tableau.getBasicVariable(row);
+ if (basicVar < minIndex) {
+ minIndex = basicVar;
+ minRow = row;
+ }
+ }
+ return minRow;
+ }
+ return minRatioPositions.get(0);
+ }
+
+ /**
+ * Runs one iteration of the Simplex method on the given model.
+ *
+ * @param tableau Simple tableau for the problem.
+ * @throws TooManyIterationsException if the allowed number of iterations has been exhausted.
+ * @throws UnboundedSolutionException if the model is found not to have a bounded solution.
+ */
+ protected void doIteration(final SimplexTableau tableau)
+ throws TooManyIterationsException,
+ UnboundedSolutionException {
+
+ incrementIterationCount();
+
+ Integer pivotCol = getPivotColumn(tableau);
+ Integer pivotRow = getPivotRow(tableau, pivotCol);
+ if (pivotRow == null) {
+ throw new UnboundedSolutionException();
+ }
+
+ tableau.performRowOperations(pivotCol, pivotRow);
+ }
+
+ /**
+ * Solves Phase 1 of the Simplex method.
+ *
+ * @param tableau Simple tableau for the problem.
+ * @throws TooManyIterationsException if the allowed number of iterations has been exhausted.
+ * @throws UnboundedSolutionException if the model is found not to have a bounded solution.
+ * @throws NoFeasibleSolutionException if there is no feasible solution?
+ */
+ protected void solvePhase1(final SimplexTableau tableau)
+ throws TooManyIterationsException,
+ UnboundedSolutionException,
+ NoFeasibleSolutionException {
+
+ // make sure we're in Phase 1
+ if (tableau.getNumArtificialVariables() == 0) {
+ return;
+ }
+
+ while (!tableau.isOptimal()) {
+ doIteration(tableau);
+ }
+
+ // if W is not zero then we have no feasible solution
+ if (!Precision.equals(tableau.getEntry(0, tableau.getRhsOffset()), 0d, epsilon)) {
+ throw new NoFeasibleSolutionException();
+ }
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public PointValuePair doOptimize()
+ throws TooManyIterationsException,
+ UnboundedSolutionException,
+ NoFeasibleSolutionException {
+
+ // reset the tableau to indicate a non-feasible solution in case
+ // we do not pass phase 1 successfully
+ if (solutionCallback != null) {
+ solutionCallback.setTableau(null);
+ }
+
+ final SimplexTableau tableau =
+ new SimplexTableau(getFunction(),
+ getConstraints(),
+ getGoalType(),
+ isRestrictedToNonNegative(),
+ epsilon,
+ maxUlps);
+
+ solvePhase1(tableau);
+ tableau.dropPhase1Objective();
+
+ // after phase 1, we are sure to have a feasible solution
+ if (solutionCallback != null) {
+ solutionCallback.setTableau(tableau);
+ }
+
+ while (!tableau.isOptimal()) {
+ doIteration(tableau);
+ }
+
+ // check that the solution respects the nonNegative restriction in case
+ // the epsilon/cutOff values are too large for the actual linear problem
+ // (e.g. with very small constraint coefficients), the solver might actually
+ // find a non-valid solution (with negative coefficients).
+ final PointValuePair solution = tableau.getSolution();
+ if (isRestrictedToNonNegative()) {
+ final double[] coeff = solution.getPoint();
+ for (int i = 0; i < coeff.length; i++) {
+ if (Precision.compareTo(coeff[i], 0, epsilon) < 0) {
+ throw new NoFeasibleSolutionException();
+ }
+ }
+ }
+ return solution;
+ }
+}
diff --git a/src/main/java/org/apache/commons/math3/optim/linear/SimplexTableau.java b/src/main/java/org/apache/commons/math3/optim/linear/SimplexTableau.java
new file mode 100644
index 0000000..31e71d2
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/optim/linear/SimplexTableau.java
@@ -0,0 +1,713 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math3.optim.linear;
+
+import java.io.IOException;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+import java.io.Serializable;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+import java.util.TreeSet;
+
+import org.apache.commons.math3.linear.Array2DRowRealMatrix;
+import org.apache.commons.math3.linear.MatrixUtils;
+import org.apache.commons.math3.linear.RealVector;
+import org.apache.commons.math3.optim.nonlinear.scalar.GoalType;
+import org.apache.commons.math3.optim.PointValuePair;
+import org.apache.commons.math3.util.Precision;
+
+/**
+ * A tableau for use in the Simplex method.
+ *
+ * <p>
+ * Example:
+ * <pre>
+ * W | Z | x1 | x2 | x- | s1 | s2 | a1 | RHS
+ * ---------------------------------------------------
+ * -1 0 0 0 0 0 0 1 0 &lt;= phase 1 objective
+ * 0 1 -15 -10 0 0 0 0 0 &lt;= phase 2 objective
+ * 0 0 1 0 0 1 0 0 2 &lt;= constraint 1
+ * 0 0 0 1 0 0 1 0 3 &lt;= constraint 2
+ * 0 0 1 1 0 0 0 1 4 &lt;= constraint 3
+ * </pre>
+ * W: Phase 1 objective function</br>
+ * Z: Phase 2 objective function</br>
+ * x1 &amp; x2: Decision variables</br>
+ * x-: Extra decision variable to allow for negative values</br>
+ * s1 &amp; s2: Slack/Surplus variables</br>
+ * a1: Artificial variable</br>
+ * RHS: Right hand side</br>
+ * </p>
+ * @since 2.0
+ */
+class SimplexTableau implements Serializable {
+
+ /** Column label for negative vars. */
+ private static final String NEGATIVE_VAR_COLUMN_LABEL = "x-";
+
+ /** Serializable version identifier. */
+ private static final long serialVersionUID = -1369660067587938365L;
+
+ /** Linear objective function. */
+ private final LinearObjectiveFunction f;
+
+ /** Linear constraints. */
+ private final List<LinearConstraint> constraints;
+
+ /** Whether to restrict the variables to non-negative values. */
+ private final boolean restrictToNonNegative;
+
+ /** The variables each column represents */
+ private final List<String> columnLabels = new ArrayList<String>();
+
+ /** Simple tableau. */
+ private transient Array2DRowRealMatrix tableau;
+
+ /** Number of decision variables. */
+ private final int numDecisionVariables;
+
+ /** Number of slack variables. */
+ private final int numSlackVariables;
+
+ /** Number of artificial variables. */
+ private int numArtificialVariables;
+
+ /** Amount of error to accept when checking for optimality. */
+ private final double epsilon;
+
+ /** Amount of error to accept in floating point comparisons. */
+ private final int maxUlps;
+
+ /** Maps basic variables to row they are basic in. */
+ private int[] basicVariables;
+
+ /** Maps rows to their corresponding basic variables. */
+ private int[] basicRows;
+
+ /**
+ * Builds a tableau for a linear problem.
+ *
+ * @param f Linear objective function.
+ * @param constraints Linear constraints.
+ * @param goalType Optimization goal: either {@link GoalType#MAXIMIZE}
+ * or {@link GoalType#MINIMIZE}.
+ * @param restrictToNonNegative Whether to restrict the variables to non-negative values.
+ * @param epsilon Amount of error to accept when checking for optimality.
+ */
+ SimplexTableau(final LinearObjectiveFunction f,
+ final Collection<LinearConstraint> constraints,
+ final GoalType goalType,
+ final boolean restrictToNonNegative,
+ final double epsilon) {
+ this(f, constraints, goalType, restrictToNonNegative, epsilon, SimplexSolver.DEFAULT_ULPS);
+ }
+
+ /**
+ * Build a tableau for a linear problem.
+ * @param f linear objective function
+ * @param constraints linear constraints
+ * @param goalType type of optimization goal: either {@link GoalType#MAXIMIZE} or {@link GoalType#MINIMIZE}
+ * @param restrictToNonNegative whether to restrict the variables to non-negative values
+ * @param epsilon amount of error to accept when checking for optimality
+ * @param maxUlps amount of error to accept in floating point comparisons
+ */
+ SimplexTableau(final LinearObjectiveFunction f,
+ final Collection<LinearConstraint> constraints,
+ final GoalType goalType,
+ final boolean restrictToNonNegative,
+ final double epsilon,
+ final int maxUlps) {
+ this.f = f;
+ this.constraints = normalizeConstraints(constraints);
+ this.restrictToNonNegative = restrictToNonNegative;
+ this.epsilon = epsilon;
+ this.maxUlps = maxUlps;
+ this.numDecisionVariables = f.getCoefficients().getDimension() + (restrictToNonNegative ? 0 : 1);
+ this.numSlackVariables = getConstraintTypeCounts(Relationship.LEQ) +
+ getConstraintTypeCounts(Relationship.GEQ);
+ this.numArtificialVariables = getConstraintTypeCounts(Relationship.EQ) +
+ getConstraintTypeCounts(Relationship.GEQ);
+ this.tableau = createTableau(goalType == GoalType.MAXIMIZE);
+ // initialize the basic variables for phase 1:
+ // we know that only slack or artificial variables can be basic
+ initializeBasicVariables(getSlackVariableOffset());
+ initializeColumnLabels();
+ }
+
+ /**
+ * Initialize the labels for the columns.
+ */
+ protected void initializeColumnLabels() {
+ if (getNumObjectiveFunctions() == 2) {
+ columnLabels.add("W");
+ }
+ columnLabels.add("Z");
+ for (int i = 0; i < getOriginalNumDecisionVariables(); i++) {
+ columnLabels.add("x" + i);
+ }
+ if (!restrictToNonNegative) {
+ columnLabels.add(NEGATIVE_VAR_COLUMN_LABEL);
+ }
+ for (int i = 0; i < getNumSlackVariables(); i++) {
+ columnLabels.add("s" + i);
+ }
+ for (int i = 0; i < getNumArtificialVariables(); i++) {
+ columnLabels.add("a" + i);
+ }
+ columnLabels.add("RHS");
+ }
+
+ /**
+ * Create the tableau by itself.
+ * @param maximize if true, goal is to maximize the objective function
+ * @return created tableau
+ */
+ protected Array2DRowRealMatrix createTableau(final boolean maximize) {
+
+ // create a matrix of the correct size
+ int width = numDecisionVariables + numSlackVariables +
+ numArtificialVariables + getNumObjectiveFunctions() + 1; // + 1 is for RHS
+ int height = constraints.size() + getNumObjectiveFunctions();
+ Array2DRowRealMatrix matrix = new Array2DRowRealMatrix(height, width);
+
+ // initialize the objective function rows
+ if (getNumObjectiveFunctions() == 2) {
+ matrix.setEntry(0, 0, -1);
+ }
+
+ int zIndex = (getNumObjectiveFunctions() == 1) ? 0 : 1;
+ matrix.setEntry(zIndex, zIndex, maximize ? 1 : -1);
+ RealVector objectiveCoefficients = maximize ? f.getCoefficients().mapMultiply(-1) : f.getCoefficients();
+ copyArray(objectiveCoefficients.toArray(), matrix.getDataRef()[zIndex]);
+ matrix.setEntry(zIndex, width - 1, maximize ? f.getConstantTerm() : -1 * f.getConstantTerm());
+
+ if (!restrictToNonNegative) {
+ matrix.setEntry(zIndex, getSlackVariableOffset() - 1,
+ getInvertedCoefficientSum(objectiveCoefficients));
+ }
+
+ // initialize the constraint rows
+ int slackVar = 0;
+ int artificialVar = 0;
+ for (int i = 0; i < constraints.size(); i++) {
+ LinearConstraint constraint = constraints.get(i);
+ int row = getNumObjectiveFunctions() + i;
+
+ // decision variable coefficients
+ copyArray(constraint.getCoefficients().toArray(), matrix.getDataRef()[row]);
+
+ // x-
+ if (!restrictToNonNegative) {
+ matrix.setEntry(row, getSlackVariableOffset() - 1,
+ getInvertedCoefficientSum(constraint.getCoefficients()));
+ }
+
+ // RHS
+ matrix.setEntry(row, width - 1, constraint.getValue());
+
+ // slack variables
+ if (constraint.getRelationship() == Relationship.LEQ) {
+ matrix.setEntry(row, getSlackVariableOffset() + slackVar++, 1); // slack
+ } else if (constraint.getRelationship() == Relationship.GEQ) {
+ matrix.setEntry(row, getSlackVariableOffset() + slackVar++, -1); // excess
+ }
+
+ // artificial variables
+ if ((constraint.getRelationship() == Relationship.EQ) ||
+ (constraint.getRelationship() == Relationship.GEQ)) {
+ matrix.setEntry(0, getArtificialVariableOffset() + artificialVar, 1);
+ matrix.setEntry(row, getArtificialVariableOffset() + artificialVar++, 1);
+ matrix.setRowVector(0, matrix.getRowVector(0).subtract(matrix.getRowVector(row)));
+ }
+ }
+
+ return matrix;
+ }
+
+ /**
+ * Get new versions of the constraints which have positive right hand sides.
+ * @param originalConstraints original (not normalized) constraints
+ * @return new versions of the constraints
+ */
+ public List<LinearConstraint> normalizeConstraints(Collection<LinearConstraint> originalConstraints) {
+ List<LinearConstraint> normalized = new ArrayList<LinearConstraint>(originalConstraints.size());
+ for (LinearConstraint constraint : originalConstraints) {
+ normalized.add(normalize(constraint));
+ }
+ return normalized;
+ }
+
+ /**
+ * Get a new equation equivalent to this one with a positive right hand side.
+ * @param constraint reference constraint
+ * @return new equation
+ */
+ private LinearConstraint normalize(final LinearConstraint constraint) {
+ if (constraint.getValue() < 0) {
+ return new LinearConstraint(constraint.getCoefficients().mapMultiply(-1),
+ constraint.getRelationship().oppositeRelationship(),
+ -1 * constraint.getValue());
+ }
+ return new LinearConstraint(constraint.getCoefficients(),
+ constraint.getRelationship(), constraint.getValue());
+ }
+
+ /**
+ * Get the number of objective functions in this tableau.
+ * @return 2 for Phase 1. 1 for Phase 2.
+ */
+ protected final int getNumObjectiveFunctions() {
+ return this.numArtificialVariables > 0 ? 2 : 1;
+ }
+
+ /**
+ * Get a count of constraints corresponding to a specified relationship.
+ * @param relationship relationship to count
+ * @return number of constraint with the specified relationship
+ */
+ private int getConstraintTypeCounts(final Relationship relationship) {
+ int count = 0;
+ for (final LinearConstraint constraint : constraints) {
+ if (constraint.getRelationship() == relationship) {
+ ++count;
+ }
+ }
+ return count;
+ }
+
+ /**
+ * Get the -1 times the sum of all coefficients in the given array.
+ * @param coefficients coefficients to sum
+ * @return the -1 times the sum of all coefficients in the given array.
+ */
+ protected static double getInvertedCoefficientSum(final RealVector coefficients) {
+ double sum = 0;
+ for (double coefficient : coefficients.toArray()) {
+ sum -= coefficient;
+ }
+ return sum;
+ }
+
+ /**
+ * Checks whether the given column is basic.
+ * @param col index of the column to check
+ * @return the row that the variable is basic in. null if the column is not basic
+ */
+ protected Integer getBasicRow(final int col) {
+ final int row = basicVariables[col];
+ return row == -1 ? null : row;
+ }
+
+ /**
+ * Returns the variable that is basic in this row.
+ * @param row the index of the row to check
+ * @return the variable that is basic for this row.
+ */
+ protected int getBasicVariable(final int row) {
+ return basicRows[row];
+ }
+
+ /**
+ * Initializes the basic variable / row mapping.
+ * @param startColumn the column to start
+ */
+ private void initializeBasicVariables(final int startColumn) {
+ basicVariables = new int[getWidth() - 1];
+ basicRows = new int[getHeight()];
+
+ Arrays.fill(basicVariables, -1);
+
+ for (int i = startColumn; i < getWidth() - 1; i++) {
+ Integer row = findBasicRow(i);
+ if (row != null) {
+ basicVariables[i] = row;
+ basicRows[row] = i;
+ }
+ }
+ }
+
+ /**
+ * Returns the row in which the given column is basic.
+ * @param col index of the column
+ * @return the row that the variable is basic in, or {@code null} if the variable is not basic.
+ */
+ private Integer findBasicRow(final int col) {
+ Integer row = null;
+ for (int i = 0; i < getHeight(); i++) {
+ final double entry = getEntry(i, col);
+ if (Precision.equals(entry, 1d, maxUlps) && (row == null)) {
+ row = i;
+ } else if (!Precision.equals(entry, 0d, maxUlps)) {
+ return null;
+ }
+ }
+ return row;
+ }
+
+ /**
+ * Removes the phase 1 objective function, positive cost non-artificial variables,
+ * and the non-basic artificial variables from this tableau.
+ */
+ protected void dropPhase1Objective() {
+ if (getNumObjectiveFunctions() == 1) {
+ return;
+ }
+
+ final Set<Integer> columnsToDrop = new TreeSet<Integer>();
+ columnsToDrop.add(0);
+
+ // positive cost non-artificial variables
+ for (int i = getNumObjectiveFunctions(); i < getArtificialVariableOffset(); i++) {
+ final double entry = getEntry(0, i);
+ if (Precision.compareTo(entry, 0d, epsilon) > 0) {
+ columnsToDrop.add(i);
+ }
+ }
+
+ // non-basic artificial variables
+ for (int i = 0; i < getNumArtificialVariables(); i++) {
+ int col = i + getArtificialVariableOffset();
+ if (getBasicRow(col) == null) {
+ columnsToDrop.add(col);
+ }
+ }
+
+ final double[][] matrix = new double[getHeight() - 1][getWidth() - columnsToDrop.size()];
+ for (int i = 1; i < getHeight(); i++) {
+ int col = 0;
+ for (int j = 0; j < getWidth(); j++) {
+ if (!columnsToDrop.contains(j)) {
+ matrix[i - 1][col++] = getEntry(i, j);
+ }
+ }
+ }
+
+ // remove the columns in reverse order so the indices are correct
+ Integer[] drop = columnsToDrop.toArray(new Integer[columnsToDrop.size()]);
+ for (int i = drop.length - 1; i >= 0; i--) {
+ columnLabels.remove((int) drop[i]);
+ }
+
+ this.tableau = new Array2DRowRealMatrix(matrix);
+ this.numArtificialVariables = 0;
+ // need to update the basic variable mappings as row/columns have been dropped
+ initializeBasicVariables(getNumObjectiveFunctions());
+ }
+
+ /**
+ * @param src the source array
+ * @param dest the destination array
+ */
+ private void copyArray(final double[] src, final double[] dest) {
+ System.arraycopy(src, 0, dest, getNumObjectiveFunctions(), src.length);
+ }
+
+ /**
+ * Returns whether the problem is at an optimal state.
+ * @return whether the model has been solved
+ */
+ boolean isOptimal() {
+ final double[] objectiveFunctionRow = getRow(0);
+ final int end = getRhsOffset();
+ for (int i = getNumObjectiveFunctions(); i < end; i++) {
+ final double entry = objectiveFunctionRow[i];
+ if (Precision.compareTo(entry, 0d, epsilon) < 0) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ /**
+ * Get the current solution.
+ * @return current solution
+ */
+ protected PointValuePair getSolution() {
+ int negativeVarColumn = columnLabels.indexOf(NEGATIVE_VAR_COLUMN_LABEL);
+ Integer negativeVarBasicRow = negativeVarColumn > 0 ? getBasicRow(negativeVarColumn) : null;
+ double mostNegative = negativeVarBasicRow == null ? 0 : getEntry(negativeVarBasicRow, getRhsOffset());
+
+ final Set<Integer> usedBasicRows = new HashSet<Integer>();
+ final double[] coefficients = new double[getOriginalNumDecisionVariables()];
+ for (int i = 0; i < coefficients.length; i++) {
+ int colIndex = columnLabels.indexOf("x" + i);
+ if (colIndex < 0) {
+ coefficients[i] = 0;
+ continue;
+ }
+ Integer basicRow = getBasicRow(colIndex);
+ if (basicRow != null && basicRow == 0) {
+ // if the basic row is found to be the objective function row
+ // set the coefficient to 0 -> this case handles unconstrained
+ // variables that are still part of the objective function
+ coefficients[i] = 0;
+ } else if (usedBasicRows.contains(basicRow)) {
+ // if multiple variables can take a given value
+ // then we choose the first and set the rest equal to 0
+ coefficients[i] = 0 - (restrictToNonNegative ? 0 : mostNegative);
+ } else {
+ usedBasicRows.add(basicRow);
+ coefficients[i] =
+ (basicRow == null ? 0 : getEntry(basicRow, getRhsOffset())) -
+ (restrictToNonNegative ? 0 : mostNegative);
+ }
+ }
+ return new PointValuePair(coefficients, f.value(coefficients));
+ }
+
+ /**
+ * Perform the row operations of the simplex algorithm with the selected
+ * pivot column and row.
+ * @param pivotCol the pivot column
+ * @param pivotRow the pivot row
+ */
+ protected void performRowOperations(int pivotCol, int pivotRow) {
+ // set the pivot element to 1
+ final double pivotVal = getEntry(pivotRow, pivotCol);
+ divideRow(pivotRow, pivotVal);
+
+ // set the rest of the pivot column to 0
+ for (int i = 0; i < getHeight(); i++) {
+ if (i != pivotRow) {
+ final double multiplier = getEntry(i, pivotCol);
+ if (multiplier != 0.0) {
+ subtractRow(i, pivotRow, multiplier);
+ }
+ }
+ }
+
+ // update the basic variable mappings
+ final int previousBasicVariable = getBasicVariable(pivotRow);
+ basicVariables[previousBasicVariable] = -1;
+ basicVariables[pivotCol] = pivotRow;
+ basicRows[pivotRow] = pivotCol;
+ }
+
+ /**
+ * Divides one row by a given divisor.
+ * <p>
+ * After application of this operation, the following will hold:
+ * <pre>dividendRow = dividendRow / divisor</pre>
+ *
+ * @param dividendRowIndex index of the row
+ * @param divisor value of the divisor
+ */
+ protected void divideRow(final int dividendRowIndex, final double divisor) {
+ final double[] dividendRow = getRow(dividendRowIndex);
+ for (int j = 0; j < getWidth(); j++) {
+ dividendRow[j] /= divisor;
+ }
+ }
+
+ /**
+ * Subtracts a multiple of one row from another.
+ * <p>
+ * After application of this operation, the following will hold:
+ * <pre>minuendRow = minuendRow - multiple * subtrahendRow</pre>
+ *
+ * @param minuendRowIndex row index
+ * @param subtrahendRowIndex row index
+ * @param multiplier multiplication factor
+ */
+ protected void subtractRow(final int minuendRowIndex, final int subtrahendRowIndex, final double multiplier) {
+ final double[] minuendRow = getRow(minuendRowIndex);
+ final double[] subtrahendRow = getRow(subtrahendRowIndex);
+ for (int i = 0; i < getWidth(); i++) {
+ minuendRow[i] -= subtrahendRow[i] * multiplier;
+ }
+ }
+
+ /**
+ * Get the width of the tableau.
+ * @return width of the tableau
+ */
+ protected final int getWidth() {
+ return tableau.getColumnDimension();
+ }
+
+ /**
+ * Get the height of the tableau.
+ * @return height of the tableau
+ */
+ protected final int getHeight() {
+ return tableau.getRowDimension();
+ }
+
+ /**
+ * Get an entry of the tableau.
+ * @param row row index
+ * @param column column index
+ * @return entry at (row, column)
+ */
+ protected final double getEntry(final int row, final int column) {
+ return tableau.getEntry(row, column);
+ }
+
+ /**
+ * Set an entry of the tableau.
+ * @param row row index
+ * @param column column index
+ * @param value for the entry
+ */
+ protected final void setEntry(final int row, final int column, final double value) {
+ tableau.setEntry(row, column, value);
+ }
+
+ /**
+ * Get the offset of the first slack variable.
+ * @return offset of the first slack variable
+ */
+ protected final int getSlackVariableOffset() {
+ return getNumObjectiveFunctions() + numDecisionVariables;
+ }
+
+ /**
+ * Get the offset of the first artificial variable.
+ * @return offset of the first artificial variable
+ */
+ protected final int getArtificialVariableOffset() {
+ return getNumObjectiveFunctions() + numDecisionVariables + numSlackVariables;
+ }
+
+ /**
+ * Get the offset of the right hand side.
+ * @return offset of the right hand side
+ */
+ protected final int getRhsOffset() {
+ return getWidth() - 1;
+ }
+
+ /**
+ * Get the number of decision variables.
+ * <p>
+ * If variables are not restricted to positive values, this will include 1 extra decision variable to represent
+ * the absolute value of the most negative variable.
+ *
+ * @return number of decision variables
+ * @see #getOriginalNumDecisionVariables()
+ */
+ protected final int getNumDecisionVariables() {
+ return numDecisionVariables;
+ }
+
+ /**
+ * Get the original number of decision variables.
+ * @return original number of decision variables
+ * @see #getNumDecisionVariables()
+ */
+ protected final int getOriginalNumDecisionVariables() {
+ return f.getCoefficients().getDimension();
+ }
+
+ /**
+ * Get the number of slack variables.
+ * @return number of slack variables
+ */
+ protected final int getNumSlackVariables() {
+ return numSlackVariables;
+ }
+
+ /**
+ * Get the number of artificial variables.
+ * @return number of artificial variables
+ */
+ protected final int getNumArtificialVariables() {
+ return numArtificialVariables;
+ }
+
+ /**
+ * Get the row from the tableau.
+ * @param row the row index
+ * @return the reference to the underlying row data
+ */
+ protected final double[] getRow(int row) {
+ return tableau.getDataRef()[row];
+ }
+
+ /**
+ * Get the tableau data.
+ * @return tableau data
+ */
+ protected final double[][] getData() {
+ return tableau.getData();
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public boolean equals(Object other) {
+
+ if (this == other) {
+ return true;
+ }
+
+ if (other instanceof SimplexTableau) {
+ SimplexTableau rhs = (SimplexTableau) other;
+ return (restrictToNonNegative == rhs.restrictToNonNegative) &&
+ (numDecisionVariables == rhs.numDecisionVariables) &&
+ (numSlackVariables == rhs.numSlackVariables) &&
+ (numArtificialVariables == rhs.numArtificialVariables) &&
+ (epsilon == rhs.epsilon) &&
+ (maxUlps == rhs.maxUlps) &&
+ f.equals(rhs.f) &&
+ constraints.equals(rhs.constraints) &&
+ tableau.equals(rhs.tableau);
+ }
+ return false;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public int hashCode() {
+ return Boolean.valueOf(restrictToNonNegative).hashCode() ^
+ numDecisionVariables ^
+ numSlackVariables ^
+ numArtificialVariables ^
+ Double.valueOf(epsilon).hashCode() ^
+ maxUlps ^
+ f.hashCode() ^
+ constraints.hashCode() ^
+ tableau.hashCode();
+ }
+
+ /**
+ * Serialize the instance.
+ * @param oos stream where object should be written
+ * @throws IOException if object cannot be written to stream
+ */
+ private void writeObject(ObjectOutputStream oos)
+ throws IOException {
+ oos.defaultWriteObject();
+ MatrixUtils.serializeRealMatrix(tableau, oos);
+ }
+
+ /**
+ * Deserialize the instance.
+ * @param ois stream from which the object should be read
+ * @throws ClassNotFoundException if a class in the stream cannot be found
+ * @throws IOException if object cannot be read from the stream
+ */
+ private void readObject(ObjectInputStream ois)
+ throws ClassNotFoundException, IOException {
+ ois.defaultReadObject();
+ MatrixUtils.deserializeRealMatrix(this, "tableau", ois);
+ }
+}
diff --git a/src/main/java/org/apache/commons/math3/optim/linear/SolutionCallback.java b/src/main/java/org/apache/commons/math3/optim/linear/SolutionCallback.java
new file mode 100644
index 0000000..24515cc
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/optim/linear/SolutionCallback.java
@@ -0,0 +1,62 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math3.optim.linear;
+
+import org.apache.commons.math3.optim.OptimizationData;
+import org.apache.commons.math3.optim.PointValuePair;
+
+/**
+ * A callback object that can be provided to a linear optimizer to keep track
+ * of the best solution found.
+ *
+ * @since 3.3
+ */
+public class SolutionCallback implements OptimizationData {
+ /** The SimplexTableau used by the SimplexSolver. */
+ private SimplexTableau tableau;
+
+ /**
+ * Set the simplex tableau used during the optimization once a feasible
+ * solution has been found.
+ *
+ * @param tableau the simplex tableau containing a feasible solution
+ */
+ void setTableau(final SimplexTableau tableau) {
+ this.tableau = tableau;
+ }
+
+ /**
+ * Retrieve the best solution found so far.
+ * <p>
+ * <b>Note:</b> the returned solution may not be optimal, e.g. in case
+ * the optimizer did reach the iteration limits.
+ *
+ * @return the best solution found so far by the optimizer, or {@code null} if
+ * no feasible solution could be found
+ */
+ public PointValuePair getSolution() {
+ return tableau != null ? tableau.getSolution() : null;
+ }
+
+ /**
+ * Returns if the found solution is optimal.
+ * @return {@code true} if the solution is optimal, {@code false} otherwise
+ */
+ public boolean isSolutionOptimal() {
+ return tableau != null ? tableau.isOptimal() : false;
+ }
+}
diff --git a/src/main/java/org/apache/commons/math3/optim/linear/UnboundedSolutionException.java b/src/main/java/org/apache/commons/math3/optim/linear/UnboundedSolutionException.java
new file mode 100644
index 0000000..546cdd2
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/optim/linear/UnboundedSolutionException.java
@@ -0,0 +1,37 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math3.optim.linear;
+
+import org.apache.commons.math3.exception.MathIllegalStateException;
+import org.apache.commons.math3.exception.util.LocalizedFormats;
+
+/**
+ * This class represents exceptions thrown by optimizers when a solution escapes to infinity.
+ *
+ * @since 2.0
+ */
+public class UnboundedSolutionException extends MathIllegalStateException {
+ /** Serializable version identifier. */
+ private static final long serialVersionUID = 940539497277290619L;
+
+ /**
+ * Simple constructor using a default message.
+ */
+ public UnboundedSolutionException() {
+ super(LocalizedFormats.UNBOUNDED_SOLUTION);
+ }
+}
diff --git a/src/main/java/org/apache/commons/math3/optim/linear/package-info.java b/src/main/java/org/apache/commons/math3/optim/linear/package-info.java
new file mode 100644
index 0000000..b900589
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/optim/linear/package-info.java
@@ -0,0 +1,21 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * Optimization algorithms for linear constrained problems.
+ */
+package org.apache.commons.math3.optim.linear;
diff --git a/src/main/java/org/apache/commons/math3/optim/nonlinear/scalar/GoalType.java b/src/main/java/org/apache/commons/math3/optim/nonlinear/scalar/GoalType.java
new file mode 100644
index 0000000..c0457b4
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/optim/nonlinear/scalar/GoalType.java
@@ -0,0 +1,32 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math3.optim.nonlinear.scalar;
+
+import org.apache.commons.math3.optim.OptimizationData;
+
+/**
+ * Goal type for an optimization problem (minimization or maximization of
+ * a scalar function.
+ *
+ * @since 2.0
+ */
+public enum GoalType implements OptimizationData {
+ /** Maximization. */
+ MAXIMIZE,
+ /** Minimization. */
+ MINIMIZE
+}
diff --git a/src/main/java/org/apache/commons/math3/optim/nonlinear/scalar/GradientMultivariateOptimizer.java b/src/main/java/org/apache/commons/math3/optim/nonlinear/scalar/GradientMultivariateOptimizer.java
new file mode 100644
index 0000000..38a8bf7
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/optim/nonlinear/scalar/GradientMultivariateOptimizer.java
@@ -0,0 +1,102 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math3.optim.nonlinear.scalar;
+
+import org.apache.commons.math3.analysis.MultivariateVectorFunction;
+import org.apache.commons.math3.optim.ConvergenceChecker;
+import org.apache.commons.math3.optim.OptimizationData;
+import org.apache.commons.math3.optim.PointValuePair;
+import org.apache.commons.math3.exception.TooManyEvaluationsException;
+
+/**
+ * Base class for implementing optimizers for multivariate scalar
+ * differentiable functions.
+ * It contains boiler-plate code for dealing with gradient evaluation.
+ *
+ * @since 3.1
+ */
+public abstract class GradientMultivariateOptimizer
+ extends MultivariateOptimizer {
+ /**
+ * Gradient of the objective function.
+ */
+ private MultivariateVectorFunction gradient;
+
+ /**
+ * @param checker Convergence checker.
+ */
+ protected GradientMultivariateOptimizer(ConvergenceChecker<PointValuePair> checker) {
+ super(checker);
+ }
+
+ /**
+ * Compute the gradient vector.
+ *
+ * @param params Point at which the gradient must be evaluated.
+ * @return the gradient at the specified point.
+ */
+ protected double[] computeObjectiveGradient(final double[] params) {
+ return gradient.value(params);
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @param optData Optimization data. In addition to those documented in
+ * {@link MultivariateOptimizer#parseOptimizationData(OptimizationData[])
+ * MultivariateOptimizer}, this method will register the following data:
+ * <ul>
+ * <li>{@link ObjectiveFunctionGradient}</li>
+ * </ul>
+ * @return {@inheritDoc}
+ * @throws TooManyEvaluationsException if the maximal number of
+ * evaluations (of the objective function) is exceeded.
+ */
+ @Override
+ public PointValuePair optimize(OptimizationData... optData)
+ throws TooManyEvaluationsException {
+ // Set up base class and perform computation.
+ return super.optimize(optData);
+ }
+
+ /**
+ * Scans the list of (required and optional) optimization data that
+ * characterize the problem.
+ *
+ * @param optData Optimization data.
+ * The following data will be looked for:
+ * <ul>
+ * <li>{@link ObjectiveFunctionGradient}</li>
+ * </ul>
+ */
+ @Override
+ protected void parseOptimizationData(OptimizationData... optData) {
+ // Allow base class to register its own data.
+ super.parseOptimizationData(optData);
+
+ // The existing values (as set by the previous call) are reused if
+ // not provided in the argument list.
+ for (OptimizationData data : optData) {
+ if (data instanceof ObjectiveFunctionGradient) {
+ gradient = ((ObjectiveFunctionGradient) data).getObjectiveFunctionGradient();
+ // If more data must be parsed, this statement _must_ be
+ // changed to "continue".
+ break;
+ }
+ }
+ }
+}
diff --git a/src/main/java/org/apache/commons/math3/optim/nonlinear/scalar/LeastSquaresConverter.java b/src/main/java/org/apache/commons/math3/optim/nonlinear/scalar/LeastSquaresConverter.java
new file mode 100644
index 0000000..4be1f12
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/optim/nonlinear/scalar/LeastSquaresConverter.java
@@ -0,0 +1,186 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math3.optim.nonlinear.scalar;
+
+import org.apache.commons.math3.analysis.MultivariateFunction;
+import org.apache.commons.math3.analysis.MultivariateVectorFunction;
+import org.apache.commons.math3.exception.DimensionMismatchException;
+import org.apache.commons.math3.linear.RealMatrix;
+
+/**
+ * This class converts
+ * {@link MultivariateVectorFunction vectorial objective functions} to
+ * {@link MultivariateFunction scalar objective functions}
+ * when the goal is to minimize them.
+ * <br/>
+ * This class is mostly used when the vectorial objective function represents
+ * a theoretical result computed from a point set applied to a model and
+ * the models point must be adjusted to fit the theoretical result to some
+ * reference observations. The observations may be obtained for example from
+ * physical measurements whether the model is built from theoretical
+ * considerations.
+ * <br/>
+ * This class computes a possibly weighted squared sum of the residuals, which is
+ * a scalar value. The residuals are the difference between the theoretical model
+ * (i.e. the output of the vectorial objective function) and the observations. The
+ * class implements the {@link MultivariateFunction} interface and can therefore be
+ * minimized by any optimizer supporting scalar objectives functions.This is one way
+ * to perform a least square estimation. There are other ways to do this without using
+ * this converter, as some optimization algorithms directly support vectorial objective
+ * functions.
+ * <br/>
+ * This class support combination of residuals with or without weights and correlations.
+ *
+ * @see MultivariateFunction
+ * @see MultivariateVectorFunction
+ * @since 2.0
+ */
+
+public class LeastSquaresConverter implements MultivariateFunction {
+ /** Underlying vectorial function. */
+ private final MultivariateVectorFunction function;
+ /** Observations to be compared to objective function to compute residuals. */
+ private final double[] observations;
+ /** Optional weights for the residuals. */
+ private final double[] weights;
+ /** Optional scaling matrix (weight and correlations) for the residuals. */
+ private final RealMatrix scale;
+
+ /**
+ * Builds a simple converter for uncorrelated residuals with identical
+ * weights.
+ *
+ * @param function vectorial residuals function to wrap
+ * @param observations observations to be compared to objective function to compute residuals
+ */
+ public LeastSquaresConverter(final MultivariateVectorFunction function,
+ final double[] observations) {
+ this.function = function;
+ this.observations = observations.clone();
+ this.weights = null;
+ this.scale = null;
+ }
+
+ /**
+ * Builds a simple converter for uncorrelated residuals with the
+ * specified weights.
+ * <p>
+ * The scalar objective function value is computed as:
+ * <pre>
+ * objective = &sum;weight<sub>i</sub>(observation<sub>i</sub>-objective<sub>i</sub>)<sup>2</sup>
+ * </pre>
+ * </p>
+ * <p>
+ * Weights can be used for example to combine residuals with different standard
+ * deviations. As an example, consider a residuals array in which even elements
+ * are angular measurements in degrees with a 0.01&deg; standard deviation and
+ * odd elements are distance measurements in meters with a 15m standard deviation.
+ * In this case, the weights array should be initialized with value
+ * 1.0/(0.01<sup>2</sup>) in the even elements and 1.0/(15.0<sup>2</sup>) in the
+ * odd elements (i.e. reciprocals of variances).
+ * </p>
+ * <p>
+ * The array computed by the objective function, the observations array and the
+ * weights array must have consistent sizes or a {@link DimensionMismatchException}
+ * will be triggered while computing the scalar objective.
+ * </p>
+ *
+ * @param function vectorial residuals function to wrap
+ * @param observations observations to be compared to objective function to compute residuals
+ * @param weights weights to apply to the residuals
+ * @throws DimensionMismatchException if the observations vector and the weights
+ * vector dimensions do not match (objective function dimension is checked only when
+ * the {@link #value(double[])} method is called)
+ */
+ public LeastSquaresConverter(final MultivariateVectorFunction function,
+ final double[] observations,
+ final double[] weights) {
+ if (observations.length != weights.length) {
+ throw new DimensionMismatchException(observations.length, weights.length);
+ }
+ this.function = function;
+ this.observations = observations.clone();
+ this.weights = weights.clone();
+ this.scale = null;
+ }
+
+ /**
+ * Builds a simple converter for correlated residuals with the
+ * specified weights.
+ * <p>
+ * The scalar objective function value is computed as:
+ * <pre>
+ * objective = y<sup>T</sup>y with y = scale&times;(observation-objective)
+ * </pre>
+ * </p>
+ * <p>
+ * The array computed by the objective function, the observations array and the
+ * the scaling matrix must have consistent sizes or a {@link DimensionMismatchException}
+ * will be triggered while computing the scalar objective.
+ * </p>
+ *
+ * @param function vectorial residuals function to wrap
+ * @param observations observations to be compared to objective function to compute residuals
+ * @param scale scaling matrix
+ * @throws DimensionMismatchException if the observations vector and the scale
+ * matrix dimensions do not match (objective function dimension is checked only when
+ * the {@link #value(double[])} method is called)
+ */
+ public LeastSquaresConverter(final MultivariateVectorFunction function,
+ final double[] observations,
+ final RealMatrix scale) {
+ if (observations.length != scale.getColumnDimension()) {
+ throw new DimensionMismatchException(observations.length, scale.getColumnDimension());
+ }
+ this.function = function;
+ this.observations = observations.clone();
+ this.weights = null;
+ this.scale = scale.copy();
+ }
+
+ /** {@inheritDoc} */
+ public double value(final double[] point) {
+ // compute residuals
+ final double[] residuals = function.value(point);
+ if (residuals.length != observations.length) {
+ throw new DimensionMismatchException(residuals.length, observations.length);
+ }
+ for (int i = 0; i < residuals.length; ++i) {
+ residuals[i] -= observations[i];
+ }
+
+ // compute sum of squares
+ double sumSquares = 0;
+ if (weights != null) {
+ for (int i = 0; i < residuals.length; ++i) {
+ final double ri = residuals[i];
+ sumSquares += weights[i] * ri * ri;
+ }
+ } else if (scale != null) {
+ for (final double yi : scale.operate(residuals)) {
+ sumSquares += yi * yi;
+ }
+ } else {
+ for (final double ri : residuals) {
+ sumSquares += ri * ri;
+ }
+ }
+
+ return sumSquares;
+ }
+}
diff --git a/src/main/java/org/apache/commons/math3/optim/nonlinear/scalar/LineSearch.java b/src/main/java/org/apache/commons/math3/optim/nonlinear/scalar/LineSearch.java
new file mode 100644
index 0000000..4a630a2
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/optim/nonlinear/scalar/LineSearch.java
@@ -0,0 +1,138 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math3.optim.nonlinear.scalar;
+
+import org.apache.commons.math3.optim.univariate.UnivariateOptimizer;
+import org.apache.commons.math3.optim.univariate.BrentOptimizer;
+import org.apache.commons.math3.optim.univariate.BracketFinder;
+import org.apache.commons.math3.optim.univariate.UnivariatePointValuePair;
+import org.apache.commons.math3.optim.univariate.SimpleUnivariateValueChecker;
+import org.apache.commons.math3.optim.univariate.SearchInterval;
+import org.apache.commons.math3.optim.univariate.UnivariateObjectiveFunction;
+import org.apache.commons.math3.analysis.UnivariateFunction;
+import org.apache.commons.math3.optim.MaxEval;
+
+/**
+ * Class for finding the minimum of the objective function along a given
+ * direction.
+ *
+ * @since 3.3
+ */
+public class LineSearch {
+ /**
+ * Value that will pass the precondition check for {@link BrentOptimizer}
+ * but will not pass the convergence check, so that the custom checker
+ * will always decide when to stop the line search.
+ */
+ private static final double REL_TOL_UNUSED = 1e-15;
+ /**
+ * Value that will pass the precondition check for {@link BrentOptimizer}
+ * but will not pass the convergence check, so that the custom checker
+ * will always decide when to stop the line search.
+ */
+ private static final double ABS_TOL_UNUSED = Double.MIN_VALUE;
+ /**
+ * Optimizer used for line search.
+ */
+ private final UnivariateOptimizer lineOptimizer;
+ /**
+ * Automatic bracketing.
+ */
+ private final BracketFinder bracket = new BracketFinder();
+ /**
+ * Extent of the initial interval used to find an interval that
+ * brackets the optimum.
+ */
+ private final double initialBracketingRange;
+ /**
+ * Optimizer on behalf of which the line search must be performed.
+ */
+ private final MultivariateOptimizer mainOptimizer;
+
+ /**
+ * The {@code BrentOptimizer} default stopping criterion uses the
+ * tolerances to check the domain (point) values, not the function
+ * values.
+ * The {@code relativeTolerance} and {@code absoluteTolerance}
+ * arguments are thus passed to a {@link SimpleUnivariateValueChecker
+ * custom checker} that will use the function values.
+ *
+ * @param optimizer Optimizer on behalf of which the line search
+ * be performed.
+ * Its {@link MultivariateOptimizer#computeObjectiveValue(double[])
+ * computeObjectiveValue} method will be called by the
+ * {@link #search(double[],double[]) search} method.
+ * @param relativeTolerance Search will stop when the function relative
+ * difference between successive iterations is below this value.
+ * @param absoluteTolerance Search will stop when the function absolute
+ * difference between successive iterations is below this value.
+ * @param initialBracketingRange Extent of the initial interval used to
+ * find an interval that brackets the optimum.
+ * If the optimized function varies a lot in the vicinity of the optimum,
+ * it may be necessary to provide a value lower than the distance between
+ * successive local minima.
+ */
+ public LineSearch(MultivariateOptimizer optimizer,
+ double relativeTolerance,
+ double absoluteTolerance,
+ double initialBracketingRange) {
+ mainOptimizer = optimizer;
+ lineOptimizer = new BrentOptimizer(REL_TOL_UNUSED,
+ ABS_TOL_UNUSED,
+ new SimpleUnivariateValueChecker(relativeTolerance,
+ absoluteTolerance));
+ this.initialBracketingRange = initialBracketingRange;
+ }
+
+ /**
+ * Finds the number {@code alpha} that optimizes
+ * {@code f(startPoint + alpha * direction)}.
+ *
+ * @param startPoint Starting point.
+ * @param direction Search direction.
+ * @return the optimum.
+ * @throws org.apache.commons.math3.exception.TooManyEvaluationsException
+ * if the number of evaluations is exceeded.
+ */
+ public UnivariatePointValuePair search(final double[] startPoint,
+ final double[] direction) {
+ final int n = startPoint.length;
+ final UnivariateFunction f = new UnivariateFunction() {
+ /** {@inheritDoc} */
+ public double value(double alpha) {
+ final double[] x = new double[n];
+ for (int i = 0; i < n; i++) {
+ x[i] = startPoint[i] + alpha * direction[i];
+ }
+ final double obj = mainOptimizer.computeObjectiveValue(x);
+ return obj;
+ }
+ };
+
+ final GoalType goal = mainOptimizer.getGoalType();
+ bracket.search(f, goal, 0, initialBracketingRange);
+ // Passing "MAX_VALUE" as a dummy value because it is the enclosing
+ // class that counts the number of evaluations (and will eventually
+ // generate the exception).
+ return lineOptimizer.optimize(new MaxEval(Integer.MAX_VALUE),
+ new UnivariateObjectiveFunction(f),
+ goal,
+ new SearchInterval(bracket.getLo(),
+ bracket.getHi(),
+ bracket.getMid()));
+ }
+}
diff --git a/src/main/java/org/apache/commons/math3/optim/nonlinear/scalar/MultiStartMultivariateOptimizer.java b/src/main/java/org/apache/commons/math3/optim/nonlinear/scalar/MultiStartMultivariateOptimizer.java
new file mode 100644
index 0000000..86dcd70
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/optim/nonlinear/scalar/MultiStartMultivariateOptimizer.java
@@ -0,0 +1,111 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math3.optim.nonlinear.scalar;
+
+import java.util.Collections;
+import java.util.List;
+import java.util.ArrayList;
+import java.util.Comparator;
+import org.apache.commons.math3.exception.NotStrictlyPositiveException;
+import org.apache.commons.math3.exception.NullArgumentException;
+import org.apache.commons.math3.random.RandomVectorGenerator;
+import org.apache.commons.math3.optim.BaseMultiStartMultivariateOptimizer;
+import org.apache.commons.math3.optim.PointValuePair;
+
+/**
+ * Multi-start optimizer.
+ *
+ * This class wraps an optimizer in order to use it several times in
+ * turn with different starting points (trying to avoid being trapped
+ * in a local extremum when looking for a global one).
+ *
+ * @since 3.0
+ */
+public class MultiStartMultivariateOptimizer
+ extends BaseMultiStartMultivariateOptimizer<PointValuePair> {
+ /** Underlying optimizer. */
+ private final MultivariateOptimizer optimizer;
+ /** Found optima. */
+ private final List<PointValuePair> optima = new ArrayList<PointValuePair>();
+
+ /**
+ * Create a multi-start optimizer from a single-start optimizer.
+ *
+ * @param optimizer Single-start optimizer to wrap.
+ * @param starts Number of starts to perform.
+ * If {@code starts == 1}, the result will be same as if {@code optimizer}
+ * is called directly.
+ * @param generator Random vector generator to use for restarts.
+ * @throws NullArgumentException if {@code optimizer} or {@code generator}
+ * is {@code null}.
+ * @throws NotStrictlyPositiveException if {@code starts < 1}.
+ */
+ public MultiStartMultivariateOptimizer(final MultivariateOptimizer optimizer,
+ final int starts,
+ final RandomVectorGenerator generator)
+ throws NullArgumentException,
+ NotStrictlyPositiveException {
+ super(optimizer, starts, generator);
+ this.optimizer = optimizer;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public PointValuePair[] getOptima() {
+ Collections.sort(optima, getPairComparator());
+ return optima.toArray(new PointValuePair[0]);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ protected void store(PointValuePair optimum) {
+ optima.add(optimum);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ protected void clear() {
+ optima.clear();
+ }
+
+ /**
+ * @return a comparator for sorting the optima.
+ */
+ private Comparator<PointValuePair> getPairComparator() {
+ return new Comparator<PointValuePair>() {
+ /** {@inheritDoc} */
+ public int compare(final PointValuePair o1,
+ final PointValuePair o2) {
+ if (o1 == null) {
+ return (o2 == null) ? 0 : 1;
+ } else if (o2 == null) {
+ return -1;
+ }
+ final double v1 = o1.getValue();
+ final double v2 = o2.getValue();
+ return (optimizer.getGoalType() == GoalType.MINIMIZE) ?
+ Double.compare(v1, v2) : Double.compare(v2, v1);
+ }
+ };
+ }
+}
diff --git a/src/main/java/org/apache/commons/math3/optim/nonlinear/scalar/MultivariateFunctionMappingAdapter.java b/src/main/java/org/apache/commons/math3/optim/nonlinear/scalar/MultivariateFunctionMappingAdapter.java
new file mode 100644
index 0000000..3c5127c
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/optim/nonlinear/scalar/MultivariateFunctionMappingAdapter.java
@@ -0,0 +1,294 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math3.optim.nonlinear.scalar;
+
+import org.apache.commons.math3.analysis.MultivariateFunction;
+import org.apache.commons.math3.analysis.UnivariateFunction;
+import org.apache.commons.math3.analysis.function.Logit;
+import org.apache.commons.math3.analysis.function.Sigmoid;
+import org.apache.commons.math3.exception.DimensionMismatchException;
+import org.apache.commons.math3.exception.NumberIsTooSmallException;
+import org.apache.commons.math3.util.FastMath;
+import org.apache.commons.math3.util.MathUtils;
+
+/**
+ * <p>Adapter for mapping bounded {@link MultivariateFunction} to unbounded ones.</p>
+ *
+ * <p>
+ * This adapter can be used to wrap functions subject to simple bounds on
+ * parameters so they can be used by optimizers that do <em>not</em> directly
+ * support simple bounds.
+ * </p>
+ * <p>
+ * The principle is that the user function that will be wrapped will see its
+ * parameters bounded as required, i.e when its {@code value} method is called
+ * with argument array {@code point}, the elements array will fulfill requirement
+ * {@code lower[i] <= point[i] <= upper[i]} for all i. Some of the components
+ * may be unbounded or bounded only on one side if the corresponding bound is
+ * set to an infinite value. The optimizer will not manage the user function by
+ * itself, but it will handle this adapter and it is this adapter that will take
+ * care the bounds are fulfilled. The adapter {@link #value(double[])} method will
+ * be called by the optimizer with unbound parameters, and the adapter will map
+ * the unbounded value to the bounded range using appropriate functions like
+ * {@link Sigmoid} for double bounded elements for example.
+ * </p>
+ * <p>
+ * As the optimizer sees only unbounded parameters, it should be noted that the
+ * start point or simplex expected by the optimizer should be unbounded, so the
+ * user is responsible for converting his bounded point to unbounded by calling
+ * {@link #boundedToUnbounded(double[])} before providing them to the optimizer.
+ * For the same reason, the point returned by the {@link
+ * org.apache.commons.math3.optimization.BaseMultivariateOptimizer#optimize(int,
+ * MultivariateFunction, org.apache.commons.math3.optimization.GoalType, double[])}
+ * method is unbounded. So to convert this point to bounded, users must call
+ * {@link #unboundedToBounded(double[])} by themselves!</p>
+ * <p>
+ * This adapter is only a poor man solution to simple bounds optimization constraints
+ * that can be used with simple optimizers like
+ * {@link org.apache.commons.math3.optim.nonlinear.scalar.noderiv.SimplexOptimizer
+ * SimplexOptimizer}.
+ * A better solution is to use an optimizer that directly supports simple bounds like
+ * {@link org.apache.commons.math3.optim.nonlinear.scalar.noderiv.CMAESOptimizer
+ * CMAESOptimizer} or
+ * {@link org.apache.commons.math3.optim.nonlinear.scalar.noderiv.BOBYQAOptimizer
+ * BOBYQAOptimizer}.
+ * One caveat of this poor-man's solution is that behavior near the bounds may be
+ * numerically unstable as bounds are mapped from infinite values.
+ * Another caveat is that convergence values are evaluated by the optimizer with
+ * respect to unbounded variables, so there will be scales differences when
+ * converted to bounded variables.
+ * </p>
+ *
+ * @see MultivariateFunctionPenaltyAdapter
+ *
+ * @since 3.0
+ */
+public class MultivariateFunctionMappingAdapter
+ implements MultivariateFunction {
+ /** Underlying bounded function. */
+ private final MultivariateFunction bounded;
+ /** Mapping functions. */
+ private final Mapper[] mappers;
+
+ /** Simple constructor.
+ * @param bounded bounded function
+ * @param lower lower bounds for each element of the input parameters array
+ * (some elements may be set to {@code Double.NEGATIVE_INFINITY} for
+ * unbounded values)
+ * @param upper upper bounds for each element of the input parameters array
+ * (some elements may be set to {@code Double.POSITIVE_INFINITY} for
+ * unbounded values)
+ * @exception DimensionMismatchException if lower and upper bounds are not
+ * consistent, either according to dimension or to values
+ */
+ public MultivariateFunctionMappingAdapter(final MultivariateFunction bounded,
+ final double[] lower, final double[] upper) {
+ // safety checks
+ MathUtils.checkNotNull(lower);
+ MathUtils.checkNotNull(upper);
+ if (lower.length != upper.length) {
+ throw new DimensionMismatchException(lower.length, upper.length);
+ }
+ for (int i = 0; i < lower.length; ++i) {
+ // note the following test is written in such a way it also fails for NaN
+ if (!(upper[i] >= lower[i])) {
+ throw new NumberIsTooSmallException(upper[i], lower[i], true);
+ }
+ }
+
+ this.bounded = bounded;
+ this.mappers = new Mapper[lower.length];
+ for (int i = 0; i < mappers.length; ++i) {
+ if (Double.isInfinite(lower[i])) {
+ if (Double.isInfinite(upper[i])) {
+ // element is unbounded, no transformation is needed
+ mappers[i] = new NoBoundsMapper();
+ } else {
+ // element is simple-bounded on the upper side
+ mappers[i] = new UpperBoundMapper(upper[i]);
+ }
+ } else {
+ if (Double.isInfinite(upper[i])) {
+ // element is simple-bounded on the lower side
+ mappers[i] = new LowerBoundMapper(lower[i]);
+ } else {
+ // element is double-bounded
+ mappers[i] = new LowerUpperBoundMapper(lower[i], upper[i]);
+ }
+ }
+ }
+ }
+
+ /**
+ * Maps an array from unbounded to bounded.
+ *
+ * @param point Unbounded values.
+ * @return the bounded values.
+ */
+ public double[] unboundedToBounded(double[] point) {
+ // Map unbounded input point to bounded point.
+ final double[] mapped = new double[mappers.length];
+ for (int i = 0; i < mappers.length; ++i) {
+ mapped[i] = mappers[i].unboundedToBounded(point[i]);
+ }
+
+ return mapped;
+ }
+
+ /**
+ * Maps an array from bounded to unbounded.
+ *
+ * @param point Bounded values.
+ * @return the unbounded values.
+ */
+ public double[] boundedToUnbounded(double[] point) {
+ // Map bounded input point to unbounded point.
+ final double[] mapped = new double[mappers.length];
+ for (int i = 0; i < mappers.length; ++i) {
+ mapped[i] = mappers[i].boundedToUnbounded(point[i]);
+ }
+
+ return mapped;
+ }
+
+ /**
+ * Compute the underlying function value from an unbounded point.
+ * <p>
+ * This method simply bounds the unbounded point using the mappings
+ * set up at construction and calls the underlying function using
+ * the bounded point.
+ * </p>
+ * @param point unbounded value
+ * @return underlying function value
+ * @see #unboundedToBounded(double[])
+ */
+ public double value(double[] point) {
+ return bounded.value(unboundedToBounded(point));
+ }
+
+ /** Mapping interface. */
+ private interface Mapper {
+ /**
+ * Maps a value from unbounded to bounded.
+ *
+ * @param y Unbounded value.
+ * @return the bounded value.
+ */
+ double unboundedToBounded(double y);
+
+ /**
+ * Maps a value from bounded to unbounded.
+ *
+ * @param x Bounded value.
+ * @return the unbounded value.
+ */
+ double boundedToUnbounded(double x);
+ }
+
+ /** Local class for no bounds mapping. */
+ private static class NoBoundsMapper implements Mapper {
+ /** {@inheritDoc} */
+ public double unboundedToBounded(final double y) {
+ return y;
+ }
+
+ /** {@inheritDoc} */
+ public double boundedToUnbounded(final double x) {
+ return x;
+ }
+ }
+
+ /** Local class for lower bounds mapping. */
+ private static class LowerBoundMapper implements Mapper {
+ /** Low bound. */
+ private final double lower;
+
+ /**
+ * Simple constructor.
+ *
+ * @param lower lower bound
+ */
+ LowerBoundMapper(final double lower) {
+ this.lower = lower;
+ }
+
+ /** {@inheritDoc} */
+ public double unboundedToBounded(final double y) {
+ return lower + FastMath.exp(y);
+ }
+
+ /** {@inheritDoc} */
+ public double boundedToUnbounded(final double x) {
+ return FastMath.log(x - lower);
+ }
+
+ }
+
+ /** Local class for upper bounds mapping. */
+ private static class UpperBoundMapper implements Mapper {
+
+ /** Upper bound. */
+ private final double upper;
+
+ /** Simple constructor.
+ * @param upper upper bound
+ */
+ UpperBoundMapper(final double upper) {
+ this.upper = upper;
+ }
+
+ /** {@inheritDoc} */
+ public double unboundedToBounded(final double y) {
+ return upper - FastMath.exp(-y);
+ }
+
+ /** {@inheritDoc} */
+ public double boundedToUnbounded(final double x) {
+ return -FastMath.log(upper - x);
+ }
+
+ }
+
+ /** Local class for lower and bounds mapping. */
+ private static class LowerUpperBoundMapper implements Mapper {
+ /** Function from unbounded to bounded. */
+ private final UnivariateFunction boundingFunction;
+ /** Function from bounded to unbounded. */
+ private final UnivariateFunction unboundingFunction;
+
+ /**
+ * Simple constructor.
+ *
+ * @param lower lower bound
+ * @param upper upper bound
+ */
+ LowerUpperBoundMapper(final double lower, final double upper) {
+ boundingFunction = new Sigmoid(lower, upper);
+ unboundingFunction = new Logit(lower, upper);
+ }
+
+ /** {@inheritDoc} */
+ public double unboundedToBounded(final double y) {
+ return boundingFunction.value(y);
+ }
+
+ /** {@inheritDoc} */
+ public double boundedToUnbounded(final double x) {
+ return unboundingFunction.value(x);
+ }
+ }
+}
diff --git a/src/main/java/org/apache/commons/math3/optim/nonlinear/scalar/MultivariateFunctionPenaltyAdapter.java b/src/main/java/org/apache/commons/math3/optim/nonlinear/scalar/MultivariateFunctionPenaltyAdapter.java
new file mode 100644
index 0000000..931f17f
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/optim/nonlinear/scalar/MultivariateFunctionPenaltyAdapter.java
@@ -0,0 +1,186 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math3.optim.nonlinear.scalar;
+
+import org.apache.commons.math3.analysis.MultivariateFunction;
+import org.apache.commons.math3.exception.DimensionMismatchException;
+import org.apache.commons.math3.exception.NumberIsTooSmallException;
+import org.apache.commons.math3.util.FastMath;
+import org.apache.commons.math3.util.MathUtils;
+
+/**
+ * <p>Adapter extending bounded {@link MultivariateFunction} to an unbouded
+ * domain using a penalty function.</p>
+ *
+ * <p>
+ * This adapter can be used to wrap functions subject to simple bounds on
+ * parameters so they can be used by optimizers that do <em>not</em> directly
+ * support simple bounds.
+ * </p>
+ * <p>
+ * The principle is that the user function that will be wrapped will see its
+ * parameters bounded as required, i.e when its {@code value} method is called
+ * with argument array {@code point}, the elements array will fulfill requirement
+ * {@code lower[i] <= point[i] <= upper[i]} for all i. Some of the components
+ * may be unbounded or bounded only on one side if the corresponding bound is
+ * set to an infinite value. The optimizer will not manage the user function by
+ * itself, but it will handle this adapter and it is this adapter that will take
+ * care the bounds are fulfilled. The adapter {@link #value(double[])} method will
+ * be called by the optimizer with unbound parameters, and the adapter will check
+ * if the parameters is within range or not. If it is in range, then the underlying
+ * user function will be called, and if it is not the value of a penalty function
+ * will be returned instead.
+ * </p>
+ * <p>
+ * This adapter is only a poor-man's solution to simple bounds optimization
+ * constraints that can be used with simple optimizers like
+ * {@link org.apache.commons.math3.optim.nonlinear.scalar.noderiv.SimplexOptimizer
+ * SimplexOptimizer}.
+ * A better solution is to use an optimizer that directly supports simple bounds like
+ * {@link org.apache.commons.math3.optim.nonlinear.scalar.noderiv.CMAESOptimizer
+ * CMAESOptimizer} or
+ * {@link org.apache.commons.math3.optim.nonlinear.scalar.noderiv.BOBYQAOptimizer
+ * BOBYQAOptimizer}.
+ * One caveat of this poor-man's solution is that if start point or start simplex
+ * is completely outside of the allowed range, only the penalty function is used,
+ * and the optimizer may converge without ever entering the range.
+ * </p>
+ *
+ * @see MultivariateFunctionMappingAdapter
+ *
+ * @since 3.0
+ */
+public class MultivariateFunctionPenaltyAdapter
+ implements MultivariateFunction {
+ /** Underlying bounded function. */
+ private final MultivariateFunction bounded;
+ /** Lower bounds. */
+ private final double[] lower;
+ /** Upper bounds. */
+ private final double[] upper;
+ /** Penalty offset. */
+ private final double offset;
+ /** Penalty scales. */
+ private final double[] scale;
+
+ /**
+ * Simple constructor.
+ * <p>
+ * When the optimizer provided points are out of range, the value of the
+ * penalty function will be used instead of the value of the underlying
+ * function. In order for this penalty to be effective in rejecting this
+ * point during the optimization process, the penalty function value should
+ * be defined with care. This value is computed as:
+ * <pre>
+ * penalty(point) = offset + &sum;<sub>i</sub>[scale[i] * &radic;|point[i]-boundary[i]|]
+ * </pre>
+ * where indices i correspond to all the components that violates their boundaries.
+ * </p>
+ * <p>
+ * So when attempting a function minimization, offset should be larger than
+ * the maximum expected value of the underlying function and scale components
+ * should all be positive. When attempting a function maximization, offset
+ * should be lesser than the minimum expected value of the underlying function
+ * and scale components should all be negative.
+ * minimization, and lesser than the minimum expected value of the underlying
+ * function when attempting maximization.
+ * </p>
+ * <p>
+ * These choices for the penalty function have two properties. First, all out
+ * of range points will return a function value that is worse than the value
+ * returned by any in range point. Second, the penalty is worse for large
+ * boundaries violation than for small violations, so the optimizer has an hint
+ * about the direction in which it should search for acceptable points.
+ * </p>
+ * @param bounded bounded function
+ * @param lower lower bounds for each element of the input parameters array
+ * (some elements may be set to {@code Double.NEGATIVE_INFINITY} for
+ * unbounded values)
+ * @param upper upper bounds for each element of the input parameters array
+ * (some elements may be set to {@code Double.POSITIVE_INFINITY} for
+ * unbounded values)
+ * @param offset base offset of the penalty function
+ * @param scale scale of the penalty function
+ * @exception DimensionMismatchException if lower bounds, upper bounds and
+ * scales are not consistent, either according to dimension or to bounadary
+ * values
+ */
+ public MultivariateFunctionPenaltyAdapter(final MultivariateFunction bounded,
+ final double[] lower, final double[] upper,
+ final double offset, final double[] scale) {
+
+ // safety checks
+ MathUtils.checkNotNull(lower);
+ MathUtils.checkNotNull(upper);
+ MathUtils.checkNotNull(scale);
+ if (lower.length != upper.length) {
+ throw new DimensionMismatchException(lower.length, upper.length);
+ }
+ if (lower.length != scale.length) {
+ throw new DimensionMismatchException(lower.length, scale.length);
+ }
+ for (int i = 0; i < lower.length; ++i) {
+ // note the following test is written in such a way it also fails for NaN
+ if (!(upper[i] >= lower[i])) {
+ throw new NumberIsTooSmallException(upper[i], lower[i], true);
+ }
+ }
+
+ this.bounded = bounded;
+ this.lower = lower.clone();
+ this.upper = upper.clone();
+ this.offset = offset;
+ this.scale = scale.clone();
+ }
+
+ /**
+ * Computes the underlying function value from an unbounded point.
+ * <p>
+ * This method simply returns the value of the underlying function
+ * if the unbounded point already fulfills the bounds, and compute
+ * a replacement value using the offset and scale if bounds are
+ * violated, without calling the function at all.
+ * </p>
+ * @param point unbounded point
+ * @return either underlying function value or penalty function value
+ */
+ public double value(double[] point) {
+
+ for (int i = 0; i < scale.length; ++i) {
+ if ((point[i] < lower[i]) || (point[i] > upper[i])) {
+ // bound violation starting at this component
+ double sum = 0;
+ for (int j = i; j < scale.length; ++j) {
+ final double overshoot;
+ if (point[j] < lower[j]) {
+ overshoot = scale[j] * (lower[j] - point[j]);
+ } else if (point[j] > upper[j]) {
+ overshoot = scale[j] * (point[j] - upper[j]);
+ } else {
+ overshoot = 0;
+ }
+ sum += FastMath.sqrt(overshoot);
+ }
+ return offset + sum;
+ }
+ }
+
+ // all boundaries are fulfilled, we are in the expected
+ // domain of the underlying function
+ return bounded.value(point);
+ }
+}
diff --git a/src/main/java/org/apache/commons/math3/optim/nonlinear/scalar/MultivariateOptimizer.java b/src/main/java/org/apache/commons/math3/optim/nonlinear/scalar/MultivariateOptimizer.java
new file mode 100644
index 0000000..bc0bec9
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/optim/nonlinear/scalar/MultivariateOptimizer.java
@@ -0,0 +1,117 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math3.optim.nonlinear.scalar;
+
+import org.apache.commons.math3.analysis.MultivariateFunction;
+import org.apache.commons.math3.optim.BaseMultivariateOptimizer;
+import org.apache.commons.math3.optim.OptimizationData;
+import org.apache.commons.math3.optim.ConvergenceChecker;
+import org.apache.commons.math3.optim.PointValuePair;
+import org.apache.commons.math3.exception.TooManyEvaluationsException;
+
+/**
+ * Base class for a multivariate scalar function optimizer.
+ *
+ * @since 3.1
+ */
+public abstract class MultivariateOptimizer
+ extends BaseMultivariateOptimizer<PointValuePair> {
+ /** Objective function. */
+ private MultivariateFunction function;
+ /** Type of optimization. */
+ private GoalType goal;
+
+ /**
+ * @param checker Convergence checker.
+ */
+ protected MultivariateOptimizer(ConvergenceChecker<PointValuePair> checker) {
+ super(checker);
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @param optData Optimization data. In addition to those documented in
+ * {@link BaseMultivariateOptimizer#parseOptimizationData(OptimizationData[])
+ * BaseMultivariateOptimizer}, this method will register the following data:
+ * <ul>
+ * <li>{@link ObjectiveFunction}</li>
+ * <li>{@link GoalType}</li>
+ * </ul>
+ * @return {@inheritDoc}
+ * @throws TooManyEvaluationsException if the maximal number of
+ * evaluations is exceeded.
+ */
+ @Override
+ public PointValuePair optimize(OptimizationData... optData)
+ throws TooManyEvaluationsException {
+ // Set up base class and perform computation.
+ return super.optimize(optData);
+ }
+
+ /**
+ * Scans the list of (required and optional) optimization data that
+ * characterize the problem.
+ *
+ * @param optData Optimization data.
+ * The following data will be looked for:
+ * <ul>
+ * <li>{@link ObjectiveFunction}</li>
+ * <li>{@link GoalType}</li>
+ * </ul>
+ */
+ @Override
+ protected void parseOptimizationData(OptimizationData... optData) {
+ // Allow base class to register its own data.
+ super.parseOptimizationData(optData);
+
+ // The existing values (as set by the previous call) are reused if
+ // not provided in the argument list.
+ for (OptimizationData data : optData) {
+ if (data instanceof GoalType) {
+ goal = (GoalType) data;
+ continue;
+ }
+ if (data instanceof ObjectiveFunction) {
+ function = ((ObjectiveFunction) data).getObjectiveFunction();
+ continue;
+ }
+ }
+ }
+
+ /**
+ * @return the optimization type.
+ */
+ public GoalType getGoalType() {
+ return goal;
+ }
+
+ /**
+ * Computes the objective function value.
+ * This method <em>must</em> be called by subclasses to enforce the
+ * evaluation counter limit.
+ *
+ * @param params Point at which the objective function must be evaluated.
+ * @return the objective function value at the specified point.
+ * @throws TooManyEvaluationsException if the maximal number of
+ * evaluations is exceeded.
+ */
+ public double computeObjectiveValue(double[] params) {
+ super.incrementEvaluationCount();
+ return function.value(params);
+ }
+}
diff --git a/src/main/java/org/apache/commons/math3/optim/nonlinear/scalar/ObjectiveFunction.java b/src/main/java/org/apache/commons/math3/optim/nonlinear/scalar/ObjectiveFunction.java
new file mode 100644
index 0000000..643cc03
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/optim/nonlinear/scalar/ObjectiveFunction.java
@@ -0,0 +1,46 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math3.optim.nonlinear.scalar;
+
+import org.apache.commons.math3.analysis.MultivariateFunction;
+import org.apache.commons.math3.optim.OptimizationData;
+
+/**
+ * Scalar function to be optimized.
+ *
+ * @since 3.1
+ */
+public class ObjectiveFunction implements OptimizationData {
+ /** Function to be optimized. */
+ private final MultivariateFunction function;
+
+ /**
+ * @param f Function to be optimized.
+ */
+ public ObjectiveFunction(MultivariateFunction f) {
+ function = f;
+ }
+
+ /**
+ * Gets the function to be optimized.
+ *
+ * @return the objective function.
+ */
+ public MultivariateFunction getObjectiveFunction() {
+ return function;
+ }
+}
diff --git a/src/main/java/org/apache/commons/math3/optim/nonlinear/scalar/ObjectiveFunctionGradient.java b/src/main/java/org/apache/commons/math3/optim/nonlinear/scalar/ObjectiveFunctionGradient.java
new file mode 100644
index 0000000..2fcf2ee
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/optim/nonlinear/scalar/ObjectiveFunctionGradient.java
@@ -0,0 +1,46 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math3.optim.nonlinear.scalar;
+
+import org.apache.commons.math3.analysis.MultivariateVectorFunction;
+import org.apache.commons.math3.optim.OptimizationData;
+
+/**
+ * Gradient of the scalar function to be optimized.
+ *
+ * @since 3.1
+ */
+public class ObjectiveFunctionGradient implements OptimizationData {
+ /** Function to be optimized. */
+ private final MultivariateVectorFunction gradient;
+
+ /**
+ * @param g Gradient of the function to be optimized.
+ */
+ public ObjectiveFunctionGradient(MultivariateVectorFunction g) {
+ gradient = g;
+ }
+
+ /**
+ * Gets the gradient of the function to be optimized.
+ *
+ * @return the objective function gradient.
+ */
+ public MultivariateVectorFunction getObjectiveFunctionGradient() {
+ return gradient;
+ }
+}
diff --git a/src/main/java/org/apache/commons/math3/optim/nonlinear/scalar/gradient/NonLinearConjugateGradientOptimizer.java b/src/main/java/org/apache/commons/math3/optim/nonlinear/scalar/gradient/NonLinearConjugateGradientOptimizer.java
new file mode 100644
index 0000000..9074122
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/optim/nonlinear/scalar/gradient/NonLinearConjugateGradientOptimizer.java
@@ -0,0 +1,415 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math3.optim.nonlinear.scalar.gradient;
+
+import org.apache.commons.math3.analysis.solvers.UnivariateSolver;
+import org.apache.commons.math3.exception.MathInternalError;
+import org.apache.commons.math3.exception.TooManyEvaluationsException;
+import org.apache.commons.math3.exception.MathUnsupportedOperationException;
+import org.apache.commons.math3.exception.util.LocalizedFormats;
+import org.apache.commons.math3.optim.OptimizationData;
+import org.apache.commons.math3.optim.PointValuePair;
+import org.apache.commons.math3.optim.ConvergenceChecker;
+import org.apache.commons.math3.optim.nonlinear.scalar.GoalType;
+import org.apache.commons.math3.optim.nonlinear.scalar.GradientMultivariateOptimizer;
+import org.apache.commons.math3.optim.nonlinear.scalar.LineSearch;
+
+
+/**
+ * Non-linear conjugate gradient optimizer.
+ * <br/>
+ * This class supports both the Fletcher-Reeves and the Polak-Ribière
+ * update formulas for the conjugate search directions.
+ * It also supports optional preconditioning.
+ * <br/>
+ * Constraints are not supported: the call to
+ * {@link #optimize(OptimizationData[]) optimize} will throw
+ * {@link MathUnsupportedOperationException} if bounds are passed to it.
+ *
+ * @since 2.0
+ */
+public class NonLinearConjugateGradientOptimizer
+ extends GradientMultivariateOptimizer {
+ /** Update formula for the beta parameter. */
+ private final Formula updateFormula;
+ /** Preconditioner (may be null). */
+ private final Preconditioner preconditioner;
+ /** Line search algorithm. */
+ private final LineSearch line;
+
+ /**
+ * Available choices of update formulas for the updating the parameter
+ * that is used to compute the successive conjugate search directions.
+ * For non-linear conjugate gradients, there are
+ * two formulas:
+ * <ul>
+ * <li>Fletcher-Reeves formula</li>
+ * <li>Polak-Ribière formula</li>
+ * </ul>
+ *
+ * On the one hand, the Fletcher-Reeves formula is guaranteed to converge
+ * if the start point is close enough of the optimum whether the
+ * Polak-Ribière formula may not converge in rare cases. On the
+ * other hand, the Polak-Ribière formula is often faster when it
+ * does converge. Polak-Ribière is often used.
+ *
+ * @since 2.0
+ */
+ public enum Formula {
+ /** Fletcher-Reeves formula. */
+ FLETCHER_REEVES,
+ /** Polak-Ribière formula. */
+ POLAK_RIBIERE
+ }
+
+ /**
+ * The initial step is a factor with respect to the search direction
+ * (which itself is roughly related to the gradient of the function).
+ * <br/>
+ * It is used to find an interval that brackets the optimum in line
+ * search.
+ *
+ * @since 3.1
+ * @deprecated As of v3.3, this class is not used anymore.
+ * This setting is replaced by the {@code initialBracketingRange}
+ * argument to the new constructors.
+ */
+ @Deprecated
+ public static class BracketingStep implements OptimizationData {
+ /** Initial step. */
+ private final double initialStep;
+
+ /**
+ * @param step Initial step for the bracket search.
+ */
+ public BracketingStep(double step) {
+ initialStep = step;
+ }
+
+ /**
+ * Gets the initial step.
+ *
+ * @return the initial step.
+ */
+ public double getBracketingStep() {
+ return initialStep;
+ }
+ }
+
+ /**
+ * Constructor with default tolerances for the line search (1e-8) and
+ * {@link IdentityPreconditioner preconditioner}.
+ *
+ * @param updateFormula formula to use for updating the &beta; parameter,
+ * must be one of {@link Formula#FLETCHER_REEVES} or
+ * {@link Formula#POLAK_RIBIERE}.
+ * @param checker Convergence checker.
+ */
+ public NonLinearConjugateGradientOptimizer(final Formula updateFormula,
+ ConvergenceChecker<PointValuePair> checker) {
+ this(updateFormula,
+ checker,
+ 1e-8,
+ 1e-8,
+ 1e-8,
+ new IdentityPreconditioner());
+ }
+
+ /**
+ * Constructor with default {@link IdentityPreconditioner preconditioner}.
+ *
+ * @param updateFormula formula to use for updating the &beta; parameter,
+ * must be one of {@link Formula#FLETCHER_REEVES} or
+ * {@link Formula#POLAK_RIBIERE}.
+ * @param checker Convergence checker.
+ * @param lineSearchSolver Solver to use during line search.
+ * @deprecated as of 3.3. Please use
+ * {@link #NonLinearConjugateGradientOptimizer(Formula,ConvergenceChecker,double,double,double)} instead.
+ */
+ @Deprecated
+ public NonLinearConjugateGradientOptimizer(final Formula updateFormula,
+ ConvergenceChecker<PointValuePair> checker,
+ final UnivariateSolver lineSearchSolver) {
+ this(updateFormula,
+ checker,
+ lineSearchSolver,
+ new IdentityPreconditioner());
+ }
+
+ /**
+ * Constructor with default {@link IdentityPreconditioner preconditioner}.
+ *
+ * @param updateFormula formula to use for updating the &beta; parameter,
+ * must be one of {@link Formula#FLETCHER_REEVES} or
+ * {@link Formula#POLAK_RIBIERE}.
+ * @param checker Convergence checker.
+ * @param relativeTolerance Relative threshold for line search.
+ * @param absoluteTolerance Absolute threshold for line search.
+ * @param initialBracketingRange Extent of the initial interval used to
+ * find an interval that brackets the optimum in order to perform the
+ * line search.
+ *
+ * @see LineSearch#LineSearch(MultivariateOptimizer,double,double,double)
+ * @since 3.3
+ */
+ public NonLinearConjugateGradientOptimizer(final Formula updateFormula,
+ ConvergenceChecker<PointValuePair> checker,
+ double relativeTolerance,
+ double absoluteTolerance,
+ double initialBracketingRange) {
+ this(updateFormula,
+ checker,
+ relativeTolerance,
+ absoluteTolerance,
+ initialBracketingRange,
+ new IdentityPreconditioner());
+ }
+
+ /**
+ * @param updateFormula formula to use for updating the &beta; parameter,
+ * must be one of {@link Formula#FLETCHER_REEVES} or
+ * {@link Formula#POLAK_RIBIERE}.
+ * @param checker Convergence checker.
+ * @param lineSearchSolver Solver to use during line search.
+ * @param preconditioner Preconditioner.
+ * @deprecated as of 3.3. Please use
+ * {@link #NonLinearConjugateGradientOptimizer(Formula,ConvergenceChecker,double,double,double,Preconditioner)} instead.
+ */
+ @Deprecated
+ public NonLinearConjugateGradientOptimizer(final Formula updateFormula,
+ ConvergenceChecker<PointValuePair> checker,
+ final UnivariateSolver lineSearchSolver,
+ final Preconditioner preconditioner) {
+ this(updateFormula,
+ checker,
+ lineSearchSolver.getRelativeAccuracy(),
+ lineSearchSolver.getAbsoluteAccuracy(),
+ lineSearchSolver.getAbsoluteAccuracy(),
+ preconditioner);
+ }
+
+ /**
+ * @param updateFormula formula to use for updating the &beta; parameter,
+ * must be one of {@link Formula#FLETCHER_REEVES} or
+ * {@link Formula#POLAK_RIBIERE}.
+ * @param checker Convergence checker.
+ * @param preconditioner Preconditioner.
+ * @param relativeTolerance Relative threshold for line search.
+ * @param absoluteTolerance Absolute threshold for line search.
+ * @param initialBracketingRange Extent of the initial interval used to
+ * find an interval that brackets the optimum in order to perform the
+ * line search.
+ *
+ * @see LineSearch#LineSearch(MultivariateOptimizer,double,double,double)
+ * @since 3.3
+ */
+ public NonLinearConjugateGradientOptimizer(final Formula updateFormula,
+ ConvergenceChecker<PointValuePair> checker,
+ double relativeTolerance,
+ double absoluteTolerance,
+ double initialBracketingRange,
+ final Preconditioner preconditioner) {
+ super(checker);
+
+ this.updateFormula = updateFormula;
+ this.preconditioner = preconditioner;
+ line = new LineSearch(this,
+ relativeTolerance,
+ absoluteTolerance,
+ initialBracketingRange);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public PointValuePair optimize(OptimizationData... optData)
+ throws TooManyEvaluationsException {
+ // Set up base class and perform computation.
+ return super.optimize(optData);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ protected PointValuePair doOptimize() {
+ final ConvergenceChecker<PointValuePair> checker = getConvergenceChecker();
+ final double[] point = getStartPoint();
+ final GoalType goal = getGoalType();
+ final int n = point.length;
+ double[] r = computeObjectiveGradient(point);
+ if (goal == GoalType.MINIMIZE) {
+ for (int i = 0; i < n; i++) {
+ r[i] = -r[i];
+ }
+ }
+
+ // Initial search direction.
+ double[] steepestDescent = preconditioner.precondition(point, r);
+ double[] searchDirection = steepestDescent.clone();
+
+ double delta = 0;
+ for (int i = 0; i < n; ++i) {
+ delta += r[i] * searchDirection[i];
+ }
+
+ PointValuePair current = null;
+ while (true) {
+ incrementIterationCount();
+
+ final double objective = computeObjectiveValue(point);
+ PointValuePair previous = current;
+ current = new PointValuePair(point, objective);
+ if (previous != null && checker.converged(getIterations(), previous, current)) {
+ // We have found an optimum.
+ return current;
+ }
+
+ final double step = line.search(point, searchDirection).getPoint();
+
+ // Validate new point.
+ for (int i = 0; i < point.length; ++i) {
+ point[i] += step * searchDirection[i];
+ }
+
+ r = computeObjectiveGradient(point);
+ if (goal == GoalType.MINIMIZE) {
+ for (int i = 0; i < n; ++i) {
+ r[i] = -r[i];
+ }
+ }
+
+ // Compute beta.
+ final double deltaOld = delta;
+ final double[] newSteepestDescent = preconditioner.precondition(point, r);
+ delta = 0;
+ for (int i = 0; i < n; ++i) {
+ delta += r[i] * newSteepestDescent[i];
+ }
+
+ final double beta;
+ switch (updateFormula) {
+ case FLETCHER_REEVES:
+ beta = delta / deltaOld;
+ break;
+ case POLAK_RIBIERE:
+ double deltaMid = 0;
+ for (int i = 0; i < r.length; ++i) {
+ deltaMid += r[i] * steepestDescent[i];
+ }
+ beta = (delta - deltaMid) / deltaOld;
+ break;
+ default:
+ // Should never happen.
+ throw new MathInternalError();
+ }
+ steepestDescent = newSteepestDescent;
+
+ // Compute conjugate search direction.
+ if (getIterations() % n == 0 ||
+ beta < 0) {
+ // Break conjugation: reset search direction.
+ searchDirection = steepestDescent.clone();
+ } else {
+ // Compute new conjugate search direction.
+ for (int i = 0; i < n; ++i) {
+ searchDirection[i] = steepestDescent[i] + beta * searchDirection[i];
+ }
+ }
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ protected void parseOptimizationData(OptimizationData... optData) {
+ // Allow base class to register its own data.
+ super.parseOptimizationData(optData);
+
+ checkParameters();
+ }
+
+ /** Default identity preconditioner. */
+ public static class IdentityPreconditioner implements Preconditioner {
+ /** {@inheritDoc} */
+ public double[] precondition(double[] variables, double[] r) {
+ return r.clone();
+ }
+ }
+
+ // Class is not used anymore (cf. MATH-1092). However, it might
+ // be interesting to create a class similar to "LineSearch", but
+ // that will take advantage that the model's gradient is available.
+// /**
+// * Internal class for line search.
+// * <p>
+// * The function represented by this class is the dot product of
+// * the objective function gradient and the search direction. Its
+// * value is zero when the gradient is orthogonal to the search
+// * direction, i.e. when the objective function value is a local
+// * extremum along the search direction.
+// * </p>
+// */
+// private class LineSearchFunction implements UnivariateFunction {
+// /** Current point. */
+// private final double[] currentPoint;
+// /** Search direction. */
+// private final double[] searchDirection;
+
+// /**
+// * @param point Current point.
+// * @param direction Search direction.
+// */
+// public LineSearchFunction(double[] point,
+// double[] direction) {
+// currentPoint = point.clone();
+// searchDirection = direction.clone();
+// }
+
+// /** {@inheritDoc} */
+// public double value(double x) {
+// // current point in the search direction
+// final double[] shiftedPoint = currentPoint.clone();
+// for (int i = 0; i < shiftedPoint.length; ++i) {
+// shiftedPoint[i] += x * searchDirection[i];
+// }
+
+// // gradient of the objective function
+// final double[] gradient = computeObjectiveGradient(shiftedPoint);
+
+// // dot product with the search direction
+// double dotProduct = 0;
+// for (int i = 0; i < gradient.length; ++i) {
+// dotProduct += gradient[i] * searchDirection[i];
+// }
+
+// return dotProduct;
+// }
+// }
+
+ /**
+ * @throws MathUnsupportedOperationException if bounds were passed to the
+ * {@link #optimize(OptimizationData[]) optimize} method.
+ */
+ private void checkParameters() {
+ if (getLowerBound() != null ||
+ getUpperBound() != null) {
+ throw new MathUnsupportedOperationException(LocalizedFormats.CONSTRAINT);
+ }
+ }
+}
diff --git a/src/main/java/org/apache/commons/math3/optim/nonlinear/scalar/gradient/Preconditioner.java b/src/main/java/org/apache/commons/math3/optim/nonlinear/scalar/gradient/Preconditioner.java
new file mode 100644
index 0000000..3c0f8fb
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/optim/nonlinear/scalar/gradient/Preconditioner.java
@@ -0,0 +1,44 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math3.optim.nonlinear.scalar.gradient;
+
+/**
+ * This interface represents a preconditioner for differentiable scalar
+ * objective function optimizers.
+ * @since 2.0
+ */
+public interface Preconditioner {
+ /**
+ * Precondition a search direction.
+ * <p>
+ * The returned preconditioned search direction must be computed fast or
+ * the algorithm performances will drop drastically. A classical approach
+ * is to compute only the diagonal elements of the hessian and to divide
+ * the raw search direction by these elements if they are all positive.
+ * If at least one of them is negative, it is safer to return a clone of
+ * the raw search direction as if the hessian was the identity matrix. The
+ * rationale for this simplified choice is that a negative diagonal element
+ * means the current point is far from the optimum and preconditioning will
+ * not be efficient anyway in this case.
+ * </p>
+ * @param point current point at which the search direction was computed
+ * @param r raw search direction (i.e. opposite of the gradient)
+ * @return approximation of H<sup>-1</sup>r where H is the objective function hessian
+ */
+ double[] precondition(double[] point, double[] r);
+}
diff --git a/src/main/java/org/apache/commons/math3/optim/nonlinear/scalar/gradient/package-info.java b/src/main/java/org/apache/commons/math3/optim/nonlinear/scalar/gradient/package-info.java
new file mode 100644
index 0000000..9dd9c5a
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/optim/nonlinear/scalar/gradient/package-info.java
@@ -0,0 +1,21 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * This package provides optimization algorithms that require derivatives.
+ */
+package org.apache.commons.math3.optim.nonlinear.scalar.gradient;
diff --git a/src/main/java/org/apache/commons/math3/optim/nonlinear/scalar/noderiv/AbstractSimplex.java b/src/main/java/org/apache/commons/math3/optim/nonlinear/scalar/noderiv/AbstractSimplex.java
new file mode 100644
index 0000000..e959787
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/optim/nonlinear/scalar/noderiv/AbstractSimplex.java
@@ -0,0 +1,345 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math3.optim.nonlinear.scalar.noderiv;
+
+import java.util.Arrays;
+import java.util.Comparator;
+
+import org.apache.commons.math3.analysis.MultivariateFunction;
+import org.apache.commons.math3.exception.NotStrictlyPositiveException;
+import org.apache.commons.math3.exception.DimensionMismatchException;
+import org.apache.commons.math3.exception.ZeroException;
+import org.apache.commons.math3.exception.OutOfRangeException;
+import org.apache.commons.math3.exception.NullArgumentException;
+import org.apache.commons.math3.exception.MathIllegalArgumentException;
+import org.apache.commons.math3.exception.util.LocalizedFormats;
+import org.apache.commons.math3.optim.PointValuePair;
+import org.apache.commons.math3.optim.OptimizationData;
+
+/**
+ * This class implements the simplex concept.
+ * It is intended to be used in conjunction with {@link SimplexOptimizer}.
+ * <br/>
+ * The initial configuration of the simplex is set by the constructors
+ * {@link #AbstractSimplex(double[])} or {@link #AbstractSimplex(double[][])}.
+ * The other {@link #AbstractSimplex(int) constructor} will set all steps
+ * to 1, thus building a default configuration from a unit hypercube.
+ * <br/>
+ * Users <em>must</em> call the {@link #build(double[]) build} method in order
+ * to create the data structure that will be acted on by the other methods of
+ * this class.
+ *
+ * @see SimplexOptimizer
+ * @since 3.0
+ */
+public abstract class AbstractSimplex implements OptimizationData {
+ /** Simplex. */
+ private PointValuePair[] simplex;
+ /** Start simplex configuration. */
+ private double[][] startConfiguration;
+ /** Simplex dimension (must be equal to {@code simplex.length - 1}). */
+ private final int dimension;
+
+ /**
+ * Build a unit hypercube simplex.
+ *
+ * @param n Dimension of the simplex.
+ */
+ protected AbstractSimplex(int n) {
+ this(n, 1d);
+ }
+
+ /**
+ * Build a hypercube simplex with the given side length.
+ *
+ * @param n Dimension of the simplex.
+ * @param sideLength Length of the sides of the hypercube.
+ */
+ protected AbstractSimplex(int n,
+ double sideLength) {
+ this(createHypercubeSteps(n, sideLength));
+ }
+
+ /**
+ * The start configuration for simplex is built from a box parallel to
+ * the canonical axes of the space. The simplex is the subset of vertices
+ * of a box parallel to the canonical axes. It is built as the path followed
+ * while traveling from one vertex of the box to the diagonally opposite
+ * vertex moving only along the box edges. The first vertex of the box will
+ * be located at the start point of the optimization.
+ * As an example, in dimension 3 a simplex has 4 vertices. Setting the
+ * steps to (1, 10, 2) and the start point to (1, 1, 1) would imply the
+ * start simplex would be: { (1, 1, 1), (2, 1, 1), (2, 11, 1), (2, 11, 3) }.
+ * The first vertex would be set to the start point at (1, 1, 1) and the
+ * last vertex would be set to the diagonally opposite vertex at (2, 11, 3).
+ *
+ * @param steps Steps along the canonical axes representing box edges. They
+ * may be negative but not zero.
+ * @throws NullArgumentException if {@code steps} is {@code null}.
+ * @throws ZeroException if one of the steps is zero.
+ */
+ protected AbstractSimplex(final double[] steps) {
+ if (steps == null) {
+ throw new NullArgumentException();
+ }
+ if (steps.length == 0) {
+ throw new ZeroException();
+ }
+ dimension = steps.length;
+
+ // Only the relative position of the n final vertices with respect
+ // to the first one are stored.
+ startConfiguration = new double[dimension][dimension];
+ for (int i = 0; i < dimension; i++) {
+ final double[] vertexI = startConfiguration[i];
+ for (int j = 0; j < i + 1; j++) {
+ if (steps[j] == 0) {
+ throw new ZeroException(LocalizedFormats.EQUAL_VERTICES_IN_SIMPLEX);
+ }
+ System.arraycopy(steps, 0, vertexI, 0, j + 1);
+ }
+ }
+ }
+
+ /**
+ * The real initial simplex will be set up by moving the reference
+ * simplex such that its first point is located at the start point of the
+ * optimization.
+ *
+ * @param referenceSimplex Reference simplex.
+ * @throws NotStrictlyPositiveException if the reference simplex does not
+ * contain at least one point.
+ * @throws DimensionMismatchException if there is a dimension mismatch
+ * in the reference simplex.
+ * @throws IllegalArgumentException if one of its vertices is duplicated.
+ */
+ protected AbstractSimplex(final double[][] referenceSimplex) {
+ if (referenceSimplex.length <= 0) {
+ throw new NotStrictlyPositiveException(LocalizedFormats.SIMPLEX_NEED_ONE_POINT,
+ referenceSimplex.length);
+ }
+ dimension = referenceSimplex.length - 1;
+
+ // Only the relative position of the n final vertices with respect
+ // to the first one are stored.
+ startConfiguration = new double[dimension][dimension];
+ final double[] ref0 = referenceSimplex[0];
+
+ // Loop over vertices.
+ for (int i = 0; i < referenceSimplex.length; i++) {
+ final double[] refI = referenceSimplex[i];
+
+ // Safety checks.
+ if (refI.length != dimension) {
+ throw new DimensionMismatchException(refI.length, dimension);
+ }
+ for (int j = 0; j < i; j++) {
+ final double[] refJ = referenceSimplex[j];
+ boolean allEquals = true;
+ for (int k = 0; k < dimension; k++) {
+ if (refI[k] != refJ[k]) {
+ allEquals = false;
+ break;
+ }
+ }
+ if (allEquals) {
+ throw new MathIllegalArgumentException(LocalizedFormats.EQUAL_VERTICES_IN_SIMPLEX,
+ i, j);
+ }
+ }
+
+ // Store vertex i position relative to vertex 0 position.
+ if (i > 0) {
+ final double[] confI = startConfiguration[i - 1];
+ for (int k = 0; k < dimension; k++) {
+ confI[k] = refI[k] - ref0[k];
+ }
+ }
+ }
+ }
+
+ /**
+ * Get simplex dimension.
+ *
+ * @return the dimension of the simplex.
+ */
+ public int getDimension() {
+ return dimension;
+ }
+
+ /**
+ * Get simplex size.
+ * After calling the {@link #build(double[]) build} method, this method will
+ * will be equivalent to {@code getDimension() + 1}.
+ *
+ * @return the size of the simplex.
+ */
+ public int getSize() {
+ return simplex.length;
+ }
+
+ /**
+ * Compute the next simplex of the algorithm.
+ *
+ * @param evaluationFunction Evaluation function.
+ * @param comparator Comparator to use to sort simplex vertices from best
+ * to worst.
+ * @throws org.apache.commons.math3.exception.TooManyEvaluationsException
+ * if the algorithm fails to converge.
+ */
+ public abstract void iterate(final MultivariateFunction evaluationFunction,
+ final Comparator<PointValuePair> comparator);
+
+ /**
+ * Build an initial simplex.
+ *
+ * @param startPoint First point of the simplex.
+ * @throws DimensionMismatchException if the start point does not match
+ * simplex dimension.
+ */
+ public void build(final double[] startPoint) {
+ if (dimension != startPoint.length) {
+ throw new DimensionMismatchException(dimension, startPoint.length);
+ }
+
+ // Set first vertex.
+ simplex = new PointValuePair[dimension + 1];
+ simplex[0] = new PointValuePair(startPoint, Double.NaN);
+
+ // Set remaining vertices.
+ for (int i = 0; i < dimension; i++) {
+ final double[] confI = startConfiguration[i];
+ final double[] vertexI = new double[dimension];
+ for (int k = 0; k < dimension; k++) {
+ vertexI[k] = startPoint[k] + confI[k];
+ }
+ simplex[i + 1] = new PointValuePair(vertexI, Double.NaN);
+ }
+ }
+
+ /**
+ * Evaluate all the non-evaluated points of the simplex.
+ *
+ * @param evaluationFunction Evaluation function.
+ * @param comparator Comparator to use to sort simplex vertices from best to worst.
+ * @throws org.apache.commons.math3.exception.TooManyEvaluationsException
+ * if the maximal number of evaluations is exceeded.
+ */
+ public void evaluate(final MultivariateFunction evaluationFunction,
+ final Comparator<PointValuePair> comparator) {
+ // Evaluate the objective function at all non-evaluated simplex points.
+ for (int i = 0; i < simplex.length; i++) {
+ final PointValuePair vertex = simplex[i];
+ final double[] point = vertex.getPointRef();
+ if (Double.isNaN(vertex.getValue())) {
+ simplex[i] = new PointValuePair(point, evaluationFunction.value(point), false);
+ }
+ }
+
+ // Sort the simplex from best to worst.
+ Arrays.sort(simplex, comparator);
+ }
+
+ /**
+ * Replace the worst point of the simplex by a new point.
+ *
+ * @param pointValuePair Point to insert.
+ * @param comparator Comparator to use for sorting the simplex vertices
+ * from best to worst.
+ */
+ protected void replaceWorstPoint(PointValuePair pointValuePair,
+ final Comparator<PointValuePair> comparator) {
+ for (int i = 0; i < dimension; i++) {
+ if (comparator.compare(simplex[i], pointValuePair) > 0) {
+ PointValuePair tmp = simplex[i];
+ simplex[i] = pointValuePair;
+ pointValuePair = tmp;
+ }
+ }
+ simplex[dimension] = pointValuePair;
+ }
+
+ /**
+ * Get the points of the simplex.
+ *
+ * @return all the simplex points.
+ */
+ public PointValuePair[] getPoints() {
+ final PointValuePair[] copy = new PointValuePair[simplex.length];
+ System.arraycopy(simplex, 0, copy, 0, simplex.length);
+ return copy;
+ }
+
+ /**
+ * Get the simplex point stored at the requested {@code index}.
+ *
+ * @param index Location.
+ * @return the point at location {@code index}.
+ */
+ public PointValuePair getPoint(int index) {
+ if (index < 0 ||
+ index >= simplex.length) {
+ throw new OutOfRangeException(index, 0, simplex.length - 1);
+ }
+ return simplex[index];
+ }
+
+ /**
+ * Store a new point at location {@code index}.
+ * Note that no deep-copy of {@code point} is performed.
+ *
+ * @param index Location.
+ * @param point New value.
+ */
+ protected void setPoint(int index, PointValuePair point) {
+ if (index < 0 ||
+ index >= simplex.length) {
+ throw new OutOfRangeException(index, 0, simplex.length - 1);
+ }
+ simplex[index] = point;
+ }
+
+ /**
+ * Replace all points.
+ * Note that no deep-copy of {@code points} is performed.
+ *
+ * @param points New Points.
+ */
+ protected void setPoints(PointValuePair[] points) {
+ if (points.length != simplex.length) {
+ throw new DimensionMismatchException(points.length, simplex.length);
+ }
+ simplex = points;
+ }
+
+ /**
+ * Create steps for a unit hypercube.
+ *
+ * @param n Dimension of the hypercube.
+ * @param sideLength Length of the sides of the hypercube.
+ * @return the steps.
+ */
+ private static double[] createHypercubeSteps(int n,
+ double sideLength) {
+ final double[] steps = new double[n];
+ for (int i = 0; i < n; i++) {
+ steps[i] = sideLength;
+ }
+ return steps;
+ }
+}
diff --git a/src/main/java/org/apache/commons/math3/optim/nonlinear/scalar/noderiv/BOBYQAOptimizer.java b/src/main/java/org/apache/commons/math3/optim/nonlinear/scalar/noderiv/BOBYQAOptimizer.java
new file mode 100644
index 0000000..e5bf39f
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/optim/nonlinear/scalar/noderiv/BOBYQAOptimizer.java
@@ -0,0 +1,2475 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+// CHECKSTYLE: stop all
+package org.apache.commons.math3.optim.nonlinear.scalar.noderiv;
+
+import org.apache.commons.math3.exception.MathIllegalStateException;
+import org.apache.commons.math3.exception.NumberIsTooSmallException;
+import org.apache.commons.math3.exception.OutOfRangeException;
+import org.apache.commons.math3.exception.util.LocalizedFormats;
+import org.apache.commons.math3.linear.Array2DRowRealMatrix;
+import org.apache.commons.math3.linear.ArrayRealVector;
+import org.apache.commons.math3.linear.RealVector;
+import org.apache.commons.math3.optim.nonlinear.scalar.GoalType;
+import org.apache.commons.math3.optim.PointValuePair;
+import org.apache.commons.math3.optim.nonlinear.scalar.MultivariateOptimizer;
+import org.apache.commons.math3.util.FastMath;
+
+/**
+ * Powell's BOBYQA algorithm. This implementation is translated and
+ * adapted from the Fortran version available
+ * <a href="http://plato.asu.edu/ftp/other_software/bobyqa.zip">here</a>.
+ * See <a href="http://www.optimization-online.org/DB_HTML/2010/05/2616.html">
+ * this paper</a> for an introduction.
+ * <br/>
+ * BOBYQA is particularly well suited for high dimensional problems
+ * where derivatives are not available. In most cases it outperforms the
+ * {@link PowellOptimizer} significantly. Stochastic algorithms like
+ * {@link CMAESOptimizer} succeed more often than BOBYQA, but are more
+ * expensive. BOBYQA could also be considered as a replacement of any
+ * derivative-based optimizer when the derivatives are approximated by
+ * finite differences.
+ *
+ * @since 3.0
+ */
+public class BOBYQAOptimizer
+ extends MultivariateOptimizer {
+ /** Minimum dimension of the problem: {@value} */
+ public static final int MINIMUM_PROBLEM_DIMENSION = 2;
+ /** Default value for {@link #initialTrustRegionRadius}: {@value} . */
+ public static final double DEFAULT_INITIAL_RADIUS = 10.0;
+ /** Default value for {@link #stoppingTrustRegionRadius}: {@value} . */
+ public static final double DEFAULT_STOPPING_RADIUS = 1E-8;
+ /** Constant 0. */
+ private static final double ZERO = 0d;
+ /** Constant 1. */
+ private static final double ONE = 1d;
+ /** Constant 2. */
+ private static final double TWO = 2d;
+ /** Constant 10. */
+ private static final double TEN = 10d;
+ /** Constant 16. */
+ private static final double SIXTEEN = 16d;
+ /** Constant 250. */
+ private static final double TWO_HUNDRED_FIFTY = 250d;
+ /** Constant -1. */
+ private static final double MINUS_ONE = -ONE;
+ /** Constant 1/2. */
+ private static final double HALF = ONE / 2;
+ /** Constant 1/4. */
+ private static final double ONE_OVER_FOUR = ONE / 4;
+ /** Constant 1/8. */
+ private static final double ONE_OVER_EIGHT = ONE / 8;
+ /** Constant 1/10. */
+ private static final double ONE_OVER_TEN = ONE / 10;
+ /** Constant 1/1000. */
+ private static final double ONE_OVER_A_THOUSAND = ONE / 1000;
+
+ /**
+ * numberOfInterpolationPoints XXX
+ */
+ private final int numberOfInterpolationPoints;
+ /**
+ * initialTrustRegionRadius XXX
+ */
+ private double initialTrustRegionRadius;
+ /**
+ * stoppingTrustRegionRadius XXX
+ */
+ private final double stoppingTrustRegionRadius;
+ /** Goal type (minimize or maximize). */
+ private boolean isMinimize;
+ /**
+ * Current best values for the variables to be optimized.
+ * The vector will be changed in-place to contain the values of the least
+ * calculated objective function values.
+ */
+ private ArrayRealVector currentBest;
+ /** Differences between the upper and lower bounds. */
+ private double[] boundDifference;
+ /**
+ * Index of the interpolation point at the trust region center.
+ */
+ private int trustRegionCenterInterpolationPointIndex;
+ /**
+ * Last <em>n</em> columns of matrix H (where <em>n</em> is the dimension
+ * of the problem).
+ * XXX "bmat" in the original code.
+ */
+ private Array2DRowRealMatrix bMatrix;
+ /**
+ * Factorization of the leading <em>npt</em> square submatrix of H, this
+ * factorization being Z Z<sup>T</sup>, which provides both the correct
+ * rank and positive semi-definiteness.
+ * XXX "zmat" in the original code.
+ */
+ private Array2DRowRealMatrix zMatrix;
+ /**
+ * Coordinates of the interpolation points relative to {@link #originShift}.
+ * XXX "xpt" in the original code.
+ */
+ private Array2DRowRealMatrix interpolationPoints;
+ /**
+ * Shift of origin that should reduce the contributions from rounding
+ * errors to values of the model and Lagrange functions.
+ * XXX "xbase" in the original code.
+ */
+ private ArrayRealVector originShift;
+ /**
+ * Values of the objective function at the interpolation points.
+ * XXX "fval" in the original code.
+ */
+ private ArrayRealVector fAtInterpolationPoints;
+ /**
+ * Displacement from {@link #originShift} of the trust region center.
+ * XXX "xopt" in the original code.
+ */
+ private ArrayRealVector trustRegionCenterOffset;
+ /**
+ * Gradient of the quadratic model at {@link #originShift} +
+ * {@link #trustRegionCenterOffset}.
+ * XXX "gopt" in the original code.
+ */
+ private ArrayRealVector gradientAtTrustRegionCenter;
+ /**
+ * Differences {@link #getLowerBound()} - {@link #originShift}.
+ * All the components of every {@link #trustRegionCenterOffset} are going
+ * to satisfy the bounds<br/>
+ * {@link #getLowerBound() lowerBound}<sub>i</sub> &le;
+ * {@link #trustRegionCenterOffset}<sub>i</sub>,<br/>
+ * with appropriate equalities when {@link #trustRegionCenterOffset} is
+ * on a constraint boundary.
+ * XXX "sl" in the original code.
+ */
+ private ArrayRealVector lowerDifference;
+ /**
+ * Differences {@link #getUpperBound()} - {@link #originShift}
+ * All the components of every {@link #trustRegionCenterOffset} are going
+ * to satisfy the bounds<br/>
+ * {@link #trustRegionCenterOffset}<sub>i</sub> &le;
+ * {@link #getUpperBound() upperBound}<sub>i</sub>,<br/>
+ * with appropriate equalities when {@link #trustRegionCenterOffset} is
+ * on a constraint boundary.
+ * XXX "su" in the original code.
+ */
+ private ArrayRealVector upperDifference;
+ /**
+ * Parameters of the implicit second derivatives of the quadratic model.
+ * XXX "pq" in the original code.
+ */
+ private ArrayRealVector modelSecondDerivativesParameters;
+ /**
+ * Point chosen by function {@link #trsbox(double,ArrayRealVector,
+ * ArrayRealVector, ArrayRealVector,ArrayRealVector,ArrayRealVector) trsbox}
+ * or {@link #altmov(int,double) altmov}.
+ * Usually {@link #originShift} + {@link #newPoint} is the vector of
+ * variables for the next evaluation of the objective function.
+ * It also satisfies the constraints indicated in {@link #lowerDifference}
+ * and {@link #upperDifference}.
+ * XXX "xnew" in the original code.
+ */
+ private ArrayRealVector newPoint;
+ /**
+ * Alternative to {@link #newPoint}, chosen by
+ * {@link #altmov(int,double) altmov}.
+ * It may replace {@link #newPoint} in order to increase the denominator
+ * in the {@link #update(double, double, int) updating procedure}.
+ * XXX "xalt" in the original code.
+ */
+ private ArrayRealVector alternativeNewPoint;
+ /**
+ * Trial step from {@link #trustRegionCenterOffset} which is usually
+ * {@link #newPoint} - {@link #trustRegionCenterOffset}.
+ * XXX "d__" in the original code.
+ */
+ private ArrayRealVector trialStepPoint;
+ /**
+ * Values of the Lagrange functions at a new point.
+ * XXX "vlag" in the original code.
+ */
+ private ArrayRealVector lagrangeValuesAtNewPoint;
+ /**
+ * Explicit second derivatives of the quadratic model.
+ * XXX "hq" in the original code.
+ */
+ private ArrayRealVector modelSecondDerivativesValues;
+
+ /**
+ * @param numberOfInterpolationPoints Number of interpolation conditions.
+ * For a problem of dimension {@code n}, its value must be in the interval
+ * {@code [n+2, (n+1)(n+2)/2]}.
+ * Choices that exceed {@code 2n+1} are not recommended.
+ */
+ public BOBYQAOptimizer(int numberOfInterpolationPoints) {
+ this(numberOfInterpolationPoints,
+ DEFAULT_INITIAL_RADIUS,
+ DEFAULT_STOPPING_RADIUS);
+ }
+
+ /**
+ * @param numberOfInterpolationPoints Number of interpolation conditions.
+ * For a problem of dimension {@code n}, its value must be in the interval
+ * {@code [n+2, (n+1)(n+2)/2]}.
+ * Choices that exceed {@code 2n+1} are not recommended.
+ * @param initialTrustRegionRadius Initial trust region radius.
+ * @param stoppingTrustRegionRadius Stopping trust region radius.
+ */
+ public BOBYQAOptimizer(int numberOfInterpolationPoints,
+ double initialTrustRegionRadius,
+ double stoppingTrustRegionRadius) {
+ super(null); // No custom convergence criterion.
+ this.numberOfInterpolationPoints = numberOfInterpolationPoints;
+ this.initialTrustRegionRadius = initialTrustRegionRadius;
+ this.stoppingTrustRegionRadius = stoppingTrustRegionRadius;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ protected PointValuePair doOptimize() {
+ final double[] lowerBound = getLowerBound();
+ final double[] upperBound = getUpperBound();
+
+ // Validity checks.
+ setup(lowerBound, upperBound);
+
+ isMinimize = (getGoalType() == GoalType.MINIMIZE);
+ currentBest = new ArrayRealVector(getStartPoint());
+
+ final double value = bobyqa(lowerBound, upperBound);
+
+ return new PointValuePair(currentBest.getDataRef(),
+ isMinimize ? value : -value);
+ }
+
+ /**
+ * This subroutine seeks the least value of a function of many variables,
+ * by applying a trust region method that forms quadratic models by
+ * interpolation. There is usually some freedom in the interpolation
+ * conditions, which is taken up by minimizing the Frobenius norm of
+ * the change to the second derivative of the model, beginning with the
+ * zero matrix. The values of the variables are constrained by upper and
+ * lower bounds. The arguments of the subroutine are as follows.
+ *
+ * N must be set to the number of variables and must be at least two.
+ * NPT is the number of interpolation conditions. Its value must be in
+ * the interval [N+2,(N+1)(N+2)/2]. Choices that exceed 2*N+1 are not
+ * recommended.
+ * Initial values of the variables must be set in X(1),X(2),...,X(N). They
+ * will be changed to the values that give the least calculated F.
+ * For I=1,2,...,N, XL(I) and XU(I) must provide the lower and upper
+ * bounds, respectively, on X(I). The construction of quadratic models
+ * requires XL(I) to be strictly less than XU(I) for each I. Further,
+ * the contribution to a model from changes to the I-th variable is
+ * damaged severely by rounding errors if XU(I)-XL(I) is too small.
+ * RHOBEG and RHOEND must be set to the initial and final values of a trust
+ * region radius, so both must be positive with RHOEND no greater than
+ * RHOBEG. Typically, RHOBEG should be about one tenth of the greatest
+ * expected change to a variable, while RHOEND should indicate the
+ * accuracy that is required in the final values of the variables. An
+ * error return occurs if any of the differences XU(I)-XL(I), I=1,...,N,
+ * is less than 2*RHOBEG.
+ * MAXFUN must be set to an upper bound on the number of calls of CALFUN.
+ * The array W will be used for working space. Its length must be at least
+ * (NPT+5)*(NPT+N)+3*N*(N+5)/2.
+ *
+ * @param lowerBound Lower bounds.
+ * @param upperBound Upper bounds.
+ * @return the value of the objective at the optimum.
+ */
+ private double bobyqa(double[] lowerBound,
+ double[] upperBound) {
+ printMethod(); // XXX
+
+ final int n = currentBest.getDimension();
+
+ // Return if there is insufficient space between the bounds. Modify the
+ // initial X if necessary in order to avoid conflicts between the bounds
+ // and the construction of the first quadratic model. The lower and upper
+ // bounds on moves from the updated X are set now, in the ISL and ISU
+ // partitions of W, in order to provide useful and exact information about
+ // components of X that become within distance RHOBEG from their bounds.
+
+ for (int j = 0; j < n; j++) {
+ final double boundDiff = boundDifference[j];
+ lowerDifference.setEntry(j, lowerBound[j] - currentBest.getEntry(j));
+ upperDifference.setEntry(j, upperBound[j] - currentBest.getEntry(j));
+ if (lowerDifference.getEntry(j) >= -initialTrustRegionRadius) {
+ if (lowerDifference.getEntry(j) >= ZERO) {
+ currentBest.setEntry(j, lowerBound[j]);
+ lowerDifference.setEntry(j, ZERO);
+ upperDifference.setEntry(j, boundDiff);
+ } else {
+ currentBest.setEntry(j, lowerBound[j] + initialTrustRegionRadius);
+ lowerDifference.setEntry(j, -initialTrustRegionRadius);
+ // Computing MAX
+ final double deltaOne = upperBound[j] - currentBest.getEntry(j);
+ upperDifference.setEntry(j, FastMath.max(deltaOne, initialTrustRegionRadius));
+ }
+ } else if (upperDifference.getEntry(j) <= initialTrustRegionRadius) {
+ if (upperDifference.getEntry(j) <= ZERO) {
+ currentBest.setEntry(j, upperBound[j]);
+ lowerDifference.setEntry(j, -boundDiff);
+ upperDifference.setEntry(j, ZERO);
+ } else {
+ currentBest.setEntry(j, upperBound[j] - initialTrustRegionRadius);
+ // Computing MIN
+ final double deltaOne = lowerBound[j] - currentBest.getEntry(j);
+ final double deltaTwo = -initialTrustRegionRadius;
+ lowerDifference.setEntry(j, FastMath.min(deltaOne, deltaTwo));
+ upperDifference.setEntry(j, initialTrustRegionRadius);
+ }
+ }
+ }
+
+ // Make the call of BOBYQB.
+
+ return bobyqb(lowerBound, upperBound);
+ } // bobyqa
+
+ // ----------------------------------------------------------------------------------------
+
+ /**
+ * The arguments N, NPT, X, XL, XU, RHOBEG, RHOEND, IPRINT and MAXFUN
+ * are identical to the corresponding arguments in SUBROUTINE BOBYQA.
+ * XBASE holds a shift of origin that should reduce the contributions
+ * from rounding errors to values of the model and Lagrange functions.
+ * XPT is a two-dimensional array that holds the coordinates of the
+ * interpolation points relative to XBASE.
+ * FVAL holds the values of F at the interpolation points.
+ * XOPT is set to the displacement from XBASE of the trust region centre.
+ * GOPT holds the gradient of the quadratic model at XBASE+XOPT.
+ * HQ holds the explicit second derivatives of the quadratic model.
+ * PQ contains the parameters of the implicit second derivatives of the
+ * quadratic model.
+ * BMAT holds the last N columns of H.
+ * ZMAT holds the factorization of the leading NPT by NPT submatrix of H,
+ * this factorization being ZMAT times ZMAT^T, which provides both the
+ * correct rank and positive semi-definiteness.
+ * NDIM is the first dimension of BMAT and has the value NPT+N.
+ * SL and SU hold the differences XL-XBASE and XU-XBASE, respectively.
+ * All the components of every XOPT are going to satisfy the bounds
+ * SL(I) .LEQ. XOPT(I) .LEQ. SU(I), with appropriate equalities when
+ * XOPT is on a constraint boundary.
+ * XNEW is chosen by SUBROUTINE TRSBOX or ALTMOV. Usually XBASE+XNEW is the
+ * vector of variables for the next call of CALFUN. XNEW also satisfies
+ * the SL and SU constraints in the way that has just been mentioned.
+ * XALT is an alternative to XNEW, chosen by ALTMOV, that may replace XNEW
+ * in order to increase the denominator in the updating of UPDATE.
+ * D is reserved for a trial step from XOPT, which is usually XNEW-XOPT.
+ * VLAG contains the values of the Lagrange functions at a new point X.
+ * They are part of a product that requires VLAG to be of length NDIM.
+ * W is a one-dimensional array that is used for working space. Its length
+ * must be at least 3*NDIM = 3*(NPT+N).
+ *
+ * @param lowerBound Lower bounds.
+ * @param upperBound Upper bounds.
+ * @return the value of the objective at the optimum.
+ */
+ private double bobyqb(double[] lowerBound,
+ double[] upperBound) {
+ printMethod(); // XXX
+
+ final int n = currentBest.getDimension();
+ final int npt = numberOfInterpolationPoints;
+ final int np = n + 1;
+ final int nptm = npt - np;
+ final int nh = n * np / 2;
+
+ final ArrayRealVector work1 = new ArrayRealVector(n);
+ final ArrayRealVector work2 = new ArrayRealVector(npt);
+ final ArrayRealVector work3 = new ArrayRealVector(npt);
+
+ double cauchy = Double.NaN;
+ double alpha = Double.NaN;
+ double dsq = Double.NaN;
+ double crvmin = Double.NaN;
+
+ // Set some constants.
+ // Parameter adjustments
+
+ // Function Body
+
+ // The call of PRELIM sets the elements of XBASE, XPT, FVAL, GOPT, HQ, PQ,
+ // BMAT and ZMAT for the first iteration, with the corresponding values of
+ // of NF and KOPT, which are the number of calls of CALFUN so far and the
+ // index of the interpolation point at the trust region centre. Then the
+ // initial XOPT is set too. The branch to label 720 occurs if MAXFUN is
+ // less than NPT. GOPT will be updated if KOPT is different from KBASE.
+
+ trustRegionCenterInterpolationPointIndex = 0;
+
+ prelim(lowerBound, upperBound);
+ double xoptsq = ZERO;
+ for (int i = 0; i < n; i++) {
+ trustRegionCenterOffset.setEntry(i, interpolationPoints.getEntry(trustRegionCenterInterpolationPointIndex, i));
+ // Computing 2nd power
+ final double deltaOne = trustRegionCenterOffset.getEntry(i);
+ xoptsq += deltaOne * deltaOne;
+ }
+ double fsave = fAtInterpolationPoints.getEntry(0);
+ final int kbase = 0;
+
+ // Complete the settings that are required for the iterative procedure.
+
+ int ntrits = 0;
+ int itest = 0;
+ int knew = 0;
+ int nfsav = getEvaluations();
+ double rho = initialTrustRegionRadius;
+ double delta = rho;
+ double diffa = ZERO;
+ double diffb = ZERO;
+ double diffc = ZERO;
+ double f = ZERO;
+ double beta = ZERO;
+ double adelt = ZERO;
+ double denom = ZERO;
+ double ratio = ZERO;
+ double dnorm = ZERO;
+ double scaden = ZERO;
+ double biglsq = ZERO;
+ double distsq = ZERO;
+
+ // Update GOPT if necessary before the first iteration and after each
+ // call of RESCUE that makes a call of CALFUN.
+
+ int state = 20;
+ for(;;) {
+ switch (state) {
+ case 20: {
+ printState(20); // XXX
+ if (trustRegionCenterInterpolationPointIndex != kbase) {
+ int ih = 0;
+ for (int j = 0; j < n; j++) {
+ for (int i = 0; i <= j; i++) {
+ if (i < j) {
+ gradientAtTrustRegionCenter.setEntry(j, gradientAtTrustRegionCenter.getEntry(j) + modelSecondDerivativesValues.getEntry(ih) * trustRegionCenterOffset.getEntry(i));
+ }
+ gradientAtTrustRegionCenter.setEntry(i, gradientAtTrustRegionCenter.getEntry(i) + modelSecondDerivativesValues.getEntry(ih) * trustRegionCenterOffset.getEntry(j));
+ ih++;
+ }
+ }
+ if (getEvaluations() > npt) {
+ for (int k = 0; k < npt; k++) {
+ double temp = ZERO;
+ for (int j = 0; j < n; j++) {
+ temp += interpolationPoints.getEntry(k, j) * trustRegionCenterOffset.getEntry(j);
+ }
+ temp *= modelSecondDerivativesParameters.getEntry(k);
+ for (int i = 0; i < n; i++) {
+ gradientAtTrustRegionCenter.setEntry(i, gradientAtTrustRegionCenter.getEntry(i) + temp * interpolationPoints.getEntry(k, i));
+ }
+ }
+ // throw new PathIsExploredException(); // XXX
+ }
+ }
+
+ // Generate the next point in the trust region that provides a small value
+ // of the quadratic model subject to the constraints on the variables.
+ // The int NTRITS is set to the number "trust region" iterations that
+ // have occurred since the last "alternative" iteration. If the length
+ // of XNEW-XOPT is less than HALF*RHO, however, then there is a branch to
+ // label 650 or 680 with NTRITS=-1, instead of calculating F at XNEW.
+
+ }
+ case 60: {
+ printState(60); // XXX
+ final ArrayRealVector gnew = new ArrayRealVector(n);
+ final ArrayRealVector xbdi = new ArrayRealVector(n);
+ final ArrayRealVector s = new ArrayRealVector(n);
+ final ArrayRealVector hs = new ArrayRealVector(n);
+ final ArrayRealVector hred = new ArrayRealVector(n);
+
+ final double[] dsqCrvmin = trsbox(delta, gnew, xbdi, s,
+ hs, hred);
+ dsq = dsqCrvmin[0];
+ crvmin = dsqCrvmin[1];
+
+ // Computing MIN
+ double deltaOne = delta;
+ double deltaTwo = FastMath.sqrt(dsq);
+ dnorm = FastMath.min(deltaOne, deltaTwo);
+ if (dnorm < HALF * rho) {
+ ntrits = -1;
+ // Computing 2nd power
+ deltaOne = TEN * rho;
+ distsq = deltaOne * deltaOne;
+ if (getEvaluations() <= nfsav + 2) {
+ state = 650; break;
+ }
+
+ // The following choice between labels 650 and 680 depends on whether or
+ // not our work with the current RHO seems to be complete. Either RHO is
+ // decreased or termination occurs if the errors in the quadratic model at
+ // the last three interpolation points compare favourably with predictions
+ // of likely improvements to the model within distance HALF*RHO of XOPT.
+
+ // Computing MAX
+ deltaOne = FastMath.max(diffa, diffb);
+ final double errbig = FastMath.max(deltaOne, diffc);
+ final double frhosq = rho * ONE_OVER_EIGHT * rho;
+ if (crvmin > ZERO &&
+ errbig > frhosq * crvmin) {
+ state = 650; break;
+ }
+ final double bdtol = errbig / rho;
+ for (int j = 0; j < n; j++) {
+ double bdtest = bdtol;
+ if (newPoint.getEntry(j) == lowerDifference.getEntry(j)) {
+ bdtest = work1.getEntry(j);
+ }
+ if (newPoint.getEntry(j) == upperDifference.getEntry(j)) {
+ bdtest = -work1.getEntry(j);
+ }
+ if (bdtest < bdtol) {
+ double curv = modelSecondDerivativesValues.getEntry((j + j * j) / 2);
+ for (int k = 0; k < npt; k++) {
+ // Computing 2nd power
+ final double d1 = interpolationPoints.getEntry(k, j);
+ curv += modelSecondDerivativesParameters.getEntry(k) * (d1 * d1);
+ }
+ bdtest += HALF * curv * rho;
+ if (bdtest < bdtol) {
+ state = 650; break;
+ }
+ // throw new PathIsExploredException(); // XXX
+ }
+ }
+ state = 680; break;
+ }
+ ++ntrits;
+
+ // Severe cancellation is likely to occur if XOPT is too far from XBASE.
+ // If the following test holds, then XBASE is shifted so that XOPT becomes
+ // zero. The appropriate changes are made to BMAT and to the second
+ // derivatives of the current model, beginning with the changes to BMAT
+ // that do not depend on ZMAT. VLAG is used temporarily for working space.
+
+ }
+ case 90: {
+ printState(90); // XXX
+ if (dsq <= xoptsq * ONE_OVER_A_THOUSAND) {
+ final double fracsq = xoptsq * ONE_OVER_FOUR;
+ double sumpq = ZERO;
+ // final RealVector sumVector
+ // = new ArrayRealVector(npt, -HALF * xoptsq).add(interpolationPoints.operate(trustRegionCenter));
+ for (int k = 0; k < npt; k++) {
+ sumpq += modelSecondDerivativesParameters.getEntry(k);
+ double sum = -HALF * xoptsq;
+ for (int i = 0; i < n; i++) {
+ sum += interpolationPoints.getEntry(k, i) * trustRegionCenterOffset.getEntry(i);
+ }
+ // sum = sumVector.getEntry(k); // XXX "testAckley" and "testDiffPow" fail.
+ work2.setEntry(k, sum);
+ final double temp = fracsq - HALF * sum;
+ for (int i = 0; i < n; i++) {
+ work1.setEntry(i, bMatrix.getEntry(k, i));
+ lagrangeValuesAtNewPoint.setEntry(i, sum * interpolationPoints.getEntry(k, i) + temp * trustRegionCenterOffset.getEntry(i));
+ final int ip = npt + i;
+ for (int j = 0; j <= i; j++) {
+ bMatrix.setEntry(ip, j,
+ bMatrix.getEntry(ip, j)
+ + work1.getEntry(i) * lagrangeValuesAtNewPoint.getEntry(j)
+ + lagrangeValuesAtNewPoint.getEntry(i) * work1.getEntry(j));
+ }
+ }
+ }
+
+ // Then the revisions of BMAT that depend on ZMAT are calculated.
+
+ for (int m = 0; m < nptm; m++) {
+ double sumz = ZERO;
+ double sumw = ZERO;
+ for (int k = 0; k < npt; k++) {
+ sumz += zMatrix.getEntry(k, m);
+ lagrangeValuesAtNewPoint.setEntry(k, work2.getEntry(k) * zMatrix.getEntry(k, m));
+ sumw += lagrangeValuesAtNewPoint.getEntry(k);
+ }
+ for (int j = 0; j < n; j++) {
+ double sum = (fracsq * sumz - HALF * sumw) * trustRegionCenterOffset.getEntry(j);
+ for (int k = 0; k < npt; k++) {
+ sum += lagrangeValuesAtNewPoint.getEntry(k) * interpolationPoints.getEntry(k, j);
+ }
+ work1.setEntry(j, sum);
+ for (int k = 0; k < npt; k++) {
+ bMatrix.setEntry(k, j,
+ bMatrix.getEntry(k, j)
+ + sum * zMatrix.getEntry(k, m));
+ }
+ }
+ for (int i = 0; i < n; i++) {
+ final int ip = i + npt;
+ final double temp = work1.getEntry(i);
+ for (int j = 0; j <= i; j++) {
+ bMatrix.setEntry(ip, j,
+ bMatrix.getEntry(ip, j)
+ + temp * work1.getEntry(j));
+ }
+ }
+ }
+
+ // The following instructions complete the shift, including the changes
+ // to the second derivative parameters of the quadratic model.
+
+ int ih = 0;
+ for (int j = 0; j < n; j++) {
+ work1.setEntry(j, -HALF * sumpq * trustRegionCenterOffset.getEntry(j));
+ for (int k = 0; k < npt; k++) {
+ work1.setEntry(j, work1.getEntry(j) + modelSecondDerivativesParameters.getEntry(k) * interpolationPoints.getEntry(k, j));
+ interpolationPoints.setEntry(k, j, interpolationPoints.getEntry(k, j) - trustRegionCenterOffset.getEntry(j));
+ }
+ for (int i = 0; i <= j; i++) {
+ modelSecondDerivativesValues.setEntry(ih,
+ modelSecondDerivativesValues.getEntry(ih)
+ + work1.getEntry(i) * trustRegionCenterOffset.getEntry(j)
+ + trustRegionCenterOffset.getEntry(i) * work1.getEntry(j));
+ bMatrix.setEntry(npt + i, j, bMatrix.getEntry(npt + j, i));
+ ih++;
+ }
+ }
+ for (int i = 0; i < n; i++) {
+ originShift.setEntry(i, originShift.getEntry(i) + trustRegionCenterOffset.getEntry(i));
+ newPoint.setEntry(i, newPoint.getEntry(i) - trustRegionCenterOffset.getEntry(i));
+ lowerDifference.setEntry(i, lowerDifference.getEntry(i) - trustRegionCenterOffset.getEntry(i));
+ upperDifference.setEntry(i, upperDifference.getEntry(i) - trustRegionCenterOffset.getEntry(i));
+ trustRegionCenterOffset.setEntry(i, ZERO);
+ }
+ xoptsq = ZERO;
+ }
+ if (ntrits == 0) {
+ state = 210; break;
+ }
+ state = 230; break;
+
+ // XBASE is also moved to XOPT by a call of RESCUE. This calculation is
+ // more expensive than the previous shift, because new matrices BMAT and
+ // ZMAT are generated from scratch, which may include the replacement of
+ // interpolation points whose positions seem to be causing near linear
+ // dependence in the interpolation conditions. Therefore RESCUE is called
+ // only if rounding errors have reduced by at least a factor of two the
+ // denominator of the formula for updating the H matrix. It provides a
+ // useful safeguard, but is not invoked in most applications of BOBYQA.
+
+ }
+ case 210: {
+ printState(210); // XXX
+ // Pick two alternative vectors of variables, relative to XBASE, that
+ // are suitable as new positions of the KNEW-th interpolation point.
+ // Firstly, XNEW is set to the point on a line through XOPT and another
+ // interpolation point that minimizes the predicted value of the next
+ // denominator, subject to ||XNEW - XOPT|| .LEQ. ADELT and to the SL
+ // and SU bounds. Secondly, XALT is set to the best feasible point on
+ // a constrained version of the Cauchy step of the KNEW-th Lagrange
+ // function, the corresponding value of the square of this function
+ // being returned in CAUCHY. The choice between these alternatives is
+ // going to be made when the denominator is calculated.
+
+ final double[] alphaCauchy = altmov(knew, adelt);
+ alpha = alphaCauchy[0];
+ cauchy = alphaCauchy[1];
+
+ for (int i = 0; i < n; i++) {
+ trialStepPoint.setEntry(i, newPoint.getEntry(i) - trustRegionCenterOffset.getEntry(i));
+ }
+
+ // Calculate VLAG and BETA for the current choice of D. The scalar
+ // product of D with XPT(K,.) is going to be held in W(NPT+K) for
+ // use when VQUAD is calculated.
+
+ }
+ case 230: {
+ printState(230); // XXX
+ for (int k = 0; k < npt; k++) {
+ double suma = ZERO;
+ double sumb = ZERO;
+ double sum = ZERO;
+ for (int j = 0; j < n; j++) {
+ suma += interpolationPoints.getEntry(k, j) * trialStepPoint.getEntry(j);
+ sumb += interpolationPoints.getEntry(k, j) * trustRegionCenterOffset.getEntry(j);
+ sum += bMatrix.getEntry(k, j) * trialStepPoint.getEntry(j);
+ }
+ work3.setEntry(k, suma * (HALF * suma + sumb));
+ lagrangeValuesAtNewPoint.setEntry(k, sum);
+ work2.setEntry(k, suma);
+ }
+ beta = ZERO;
+ for (int m = 0; m < nptm; m++) {
+ double sum = ZERO;
+ for (int k = 0; k < npt; k++) {
+ sum += zMatrix.getEntry(k, m) * work3.getEntry(k);
+ }
+ beta -= sum * sum;
+ for (int k = 0; k < npt; k++) {
+ lagrangeValuesAtNewPoint.setEntry(k, lagrangeValuesAtNewPoint.getEntry(k) + sum * zMatrix.getEntry(k, m));
+ }
+ }
+ dsq = ZERO;
+ double bsum = ZERO;
+ double dx = ZERO;
+ for (int j = 0; j < n; j++) {
+ // Computing 2nd power
+ final double d1 = trialStepPoint.getEntry(j);
+ dsq += d1 * d1;
+ double sum = ZERO;
+ for (int k = 0; k < npt; k++) {
+ sum += work3.getEntry(k) * bMatrix.getEntry(k, j);
+ }
+ bsum += sum * trialStepPoint.getEntry(j);
+ final int jp = npt + j;
+ for (int i = 0; i < n; i++) {
+ sum += bMatrix.getEntry(jp, i) * trialStepPoint.getEntry(i);
+ }
+ lagrangeValuesAtNewPoint.setEntry(jp, sum);
+ bsum += sum * trialStepPoint.getEntry(j);
+ dx += trialStepPoint.getEntry(j) * trustRegionCenterOffset.getEntry(j);
+ }
+
+ beta = dx * dx + dsq * (xoptsq + dx + dx + HALF * dsq) + beta - bsum; // Original
+ // beta += dx * dx + dsq * (xoptsq + dx + dx + HALF * dsq) - bsum; // XXX "testAckley" and "testDiffPow" fail.
+ // beta = dx * dx + dsq * (xoptsq + 2 * dx + HALF * dsq) + beta - bsum; // XXX "testDiffPow" fails.
+
+ lagrangeValuesAtNewPoint.setEntry(trustRegionCenterInterpolationPointIndex,
+ lagrangeValuesAtNewPoint.getEntry(trustRegionCenterInterpolationPointIndex) + ONE);
+
+ // If NTRITS is zero, the denominator may be increased by replacing
+ // the step D of ALTMOV by a Cauchy step. Then RESCUE may be called if
+ // rounding errors have damaged the chosen denominator.
+
+ if (ntrits == 0) {
+ // Computing 2nd power
+ final double d1 = lagrangeValuesAtNewPoint.getEntry(knew);
+ denom = d1 * d1 + alpha * beta;
+ if (denom < cauchy && cauchy > ZERO) {
+ for (int i = 0; i < n; i++) {
+ newPoint.setEntry(i, alternativeNewPoint.getEntry(i));
+ trialStepPoint.setEntry(i, newPoint.getEntry(i) - trustRegionCenterOffset.getEntry(i));
+ }
+ cauchy = ZERO; // XXX Useful statement?
+ state = 230; break;
+ }
+ // Alternatively, if NTRITS is positive, then set KNEW to the index of
+ // the next interpolation point to be deleted to make room for a trust
+ // region step. Again RESCUE may be called if rounding errors have damaged_
+ // the chosen denominator, which is the reason for attempting to select
+ // KNEW before calculating the next value of the objective function.
+
+ } else {
+ final double delsq = delta * delta;
+ scaden = ZERO;
+ biglsq = ZERO;
+ knew = 0;
+ for (int k = 0; k < npt; k++) {
+ if (k == trustRegionCenterInterpolationPointIndex) {
+ continue;
+ }
+ double hdiag = ZERO;
+ for (int m = 0; m < nptm; m++) {
+ // Computing 2nd power
+ final double d1 = zMatrix.getEntry(k, m);
+ hdiag += d1 * d1;
+ }
+ // Computing 2nd power
+ final double d2 = lagrangeValuesAtNewPoint.getEntry(k);
+ final double den = beta * hdiag + d2 * d2;
+ distsq = ZERO;
+ for (int j = 0; j < n; j++) {
+ // Computing 2nd power
+ final double d3 = interpolationPoints.getEntry(k, j) - trustRegionCenterOffset.getEntry(j);
+ distsq += d3 * d3;
+ }
+ // Computing MAX
+ // Computing 2nd power
+ final double d4 = distsq / delsq;
+ final double temp = FastMath.max(ONE, d4 * d4);
+ if (temp * den > scaden) {
+ scaden = temp * den;
+ knew = k;
+ denom = den;
+ }
+ // Computing MAX
+ // Computing 2nd power
+ final double d5 = lagrangeValuesAtNewPoint.getEntry(k);
+ biglsq = FastMath.max(biglsq, temp * (d5 * d5));
+ }
+ }
+
+ // Put the variables for the next calculation of the objective function
+ // in XNEW, with any adjustments for the bounds.
+
+ // Calculate the value of the objective function at XBASE+XNEW, unless
+ // the limit on the number of calculations of F has been reached.
+
+ }
+ case 360: {
+ printState(360); // XXX
+ for (int i = 0; i < n; i++) {
+ // Computing MIN
+ // Computing MAX
+ final double d3 = lowerBound[i];
+ final double d4 = originShift.getEntry(i) + newPoint.getEntry(i);
+ final double d1 = FastMath.max(d3, d4);
+ final double d2 = upperBound[i];
+ currentBest.setEntry(i, FastMath.min(d1, d2));
+ if (newPoint.getEntry(i) == lowerDifference.getEntry(i)) {
+ currentBest.setEntry(i, lowerBound[i]);
+ }
+ if (newPoint.getEntry(i) == upperDifference.getEntry(i)) {
+ currentBest.setEntry(i, upperBound[i]);
+ }
+ }
+
+ f = computeObjectiveValue(currentBest.toArray());
+
+ if (!isMinimize) {
+ f = -f;
+ }
+ if (ntrits == -1) {
+ fsave = f;
+ state = 720; break;
+ }
+
+ // Use the quadratic model to predict the change in F due to the step D,
+ // and set DIFF to the error of this prediction.
+
+ final double fopt = fAtInterpolationPoints.getEntry(trustRegionCenterInterpolationPointIndex);
+ double vquad = ZERO;
+ int ih = 0;
+ for (int j = 0; j < n; j++) {
+ vquad += trialStepPoint.getEntry(j) * gradientAtTrustRegionCenter.getEntry(j);
+ for (int i = 0; i <= j; i++) {
+ double temp = trialStepPoint.getEntry(i) * trialStepPoint.getEntry(j);
+ if (i == j) {
+ temp *= HALF;
+ }
+ vquad += modelSecondDerivativesValues.getEntry(ih) * temp;
+ ih++;
+ }
+ }
+ for (int k = 0; k < npt; k++) {
+ // Computing 2nd power
+ final double d1 = work2.getEntry(k);
+ final double d2 = d1 * d1; // "d1" must be squared first to prevent test failures.
+ vquad += HALF * modelSecondDerivativesParameters.getEntry(k) * d2;
+ }
+ final double diff = f - fopt - vquad;
+ diffc = diffb;
+ diffb = diffa;
+ diffa = FastMath.abs(diff);
+ if (dnorm > rho) {
+ nfsav = getEvaluations();
+ }
+
+ // Pick the next value of DELTA after a trust region step.
+
+ if (ntrits > 0) {
+ if (vquad >= ZERO) {
+ throw new MathIllegalStateException(LocalizedFormats.TRUST_REGION_STEP_FAILED, vquad);
+ }
+ ratio = (f - fopt) / vquad;
+ final double hDelta = HALF * delta;
+ if (ratio <= ONE_OVER_TEN) {
+ // Computing MIN
+ delta = FastMath.min(hDelta, dnorm);
+ } else if (ratio <= .7) {
+ // Computing MAX
+ delta = FastMath.max(hDelta, dnorm);
+ } else {
+ // Computing MAX
+ delta = FastMath.max(hDelta, 2 * dnorm);
+ }
+ if (delta <= rho * 1.5) {
+ delta = rho;
+ }
+
+ // Recalculate KNEW and DENOM if the new F is less than FOPT.
+
+ if (f < fopt) {
+ final int ksav = knew;
+ final double densav = denom;
+ final double delsq = delta * delta;
+ scaden = ZERO;
+ biglsq = ZERO;
+ knew = 0;
+ for (int k = 0; k < npt; k++) {
+ double hdiag = ZERO;
+ for (int m = 0; m < nptm; m++) {
+ // Computing 2nd power
+ final double d1 = zMatrix.getEntry(k, m);
+ hdiag += d1 * d1;
+ }
+ // Computing 2nd power
+ final double d1 = lagrangeValuesAtNewPoint.getEntry(k);
+ final double den = beta * hdiag + d1 * d1;
+ distsq = ZERO;
+ for (int j = 0; j < n; j++) {
+ // Computing 2nd power
+ final double d2 = interpolationPoints.getEntry(k, j) - newPoint.getEntry(j);
+ distsq += d2 * d2;
+ }
+ // Computing MAX
+ // Computing 2nd power
+ final double d3 = distsq / delsq;
+ final double temp = FastMath.max(ONE, d3 * d3);
+ if (temp * den > scaden) {
+ scaden = temp * den;
+ knew = k;
+ denom = den;
+ }
+ // Computing MAX
+ // Computing 2nd power
+ final double d4 = lagrangeValuesAtNewPoint.getEntry(k);
+ final double d5 = temp * (d4 * d4);
+ biglsq = FastMath.max(biglsq, d5);
+ }
+ if (scaden <= HALF * biglsq) {
+ knew = ksav;
+ denom = densav;
+ }
+ }
+ }
+
+ // Update BMAT and ZMAT, so that the KNEW-th interpolation point can be
+ // moved. Also update the second derivative terms of the model.
+
+ update(beta, denom, knew);
+
+ ih = 0;
+ final double pqold = modelSecondDerivativesParameters.getEntry(knew);
+ modelSecondDerivativesParameters.setEntry(knew, ZERO);
+ for (int i = 0; i < n; i++) {
+ final double temp = pqold * interpolationPoints.getEntry(knew, i);
+ for (int j = 0; j <= i; j++) {
+ modelSecondDerivativesValues.setEntry(ih, modelSecondDerivativesValues.getEntry(ih) + temp * interpolationPoints.getEntry(knew, j));
+ ih++;
+ }
+ }
+ for (int m = 0; m < nptm; m++) {
+ final double temp = diff * zMatrix.getEntry(knew, m);
+ for (int k = 0; k < npt; k++) {
+ modelSecondDerivativesParameters.setEntry(k, modelSecondDerivativesParameters.getEntry(k) + temp * zMatrix.getEntry(k, m));
+ }
+ }
+
+ // Include the new interpolation point, and make the changes to GOPT at
+ // the old XOPT that are caused by the updating of the quadratic model.
+
+ fAtInterpolationPoints.setEntry(knew, f);
+ for (int i = 0; i < n; i++) {
+ interpolationPoints.setEntry(knew, i, newPoint.getEntry(i));
+ work1.setEntry(i, bMatrix.getEntry(knew, i));
+ }
+ for (int k = 0; k < npt; k++) {
+ double suma = ZERO;
+ for (int m = 0; m < nptm; m++) {
+ suma += zMatrix.getEntry(knew, m) * zMatrix.getEntry(k, m);
+ }
+ double sumb = ZERO;
+ for (int j = 0; j < n; j++) {
+ sumb += interpolationPoints.getEntry(k, j) * trustRegionCenterOffset.getEntry(j);
+ }
+ final double temp = suma * sumb;
+ for (int i = 0; i < n; i++) {
+ work1.setEntry(i, work1.getEntry(i) + temp * interpolationPoints.getEntry(k, i));
+ }
+ }
+ for (int i = 0; i < n; i++) {
+ gradientAtTrustRegionCenter.setEntry(i, gradientAtTrustRegionCenter.getEntry(i) + diff * work1.getEntry(i));
+ }
+
+ // Update XOPT, GOPT and KOPT if the new calculated F is less than FOPT.
+
+ if (f < fopt) {
+ trustRegionCenterInterpolationPointIndex = knew;
+ xoptsq = ZERO;
+ ih = 0;
+ for (int j = 0; j < n; j++) {
+ trustRegionCenterOffset.setEntry(j, newPoint.getEntry(j));
+ // Computing 2nd power
+ final double d1 = trustRegionCenterOffset.getEntry(j);
+ xoptsq += d1 * d1;
+ for (int i = 0; i <= j; i++) {
+ if (i < j) {
+ gradientAtTrustRegionCenter.setEntry(j, gradientAtTrustRegionCenter.getEntry(j) + modelSecondDerivativesValues.getEntry(ih) * trialStepPoint.getEntry(i));
+ }
+ gradientAtTrustRegionCenter.setEntry(i, gradientAtTrustRegionCenter.getEntry(i) + modelSecondDerivativesValues.getEntry(ih) * trialStepPoint.getEntry(j));
+ ih++;
+ }
+ }
+ for (int k = 0; k < npt; k++) {
+ double temp = ZERO;
+ for (int j = 0; j < n; j++) {
+ temp += interpolationPoints.getEntry(k, j) * trialStepPoint.getEntry(j);
+ }
+ temp *= modelSecondDerivativesParameters.getEntry(k);
+ for (int i = 0; i < n; i++) {
+ gradientAtTrustRegionCenter.setEntry(i, gradientAtTrustRegionCenter.getEntry(i) + temp * interpolationPoints.getEntry(k, i));
+ }
+ }
+ }
+
+ // Calculate the parameters of the least Frobenius norm interpolant to
+ // the current data, the gradient of this interpolant at XOPT being put
+ // into VLAG(NPT+I), I=1,2,...,N.
+
+ if (ntrits > 0) {
+ for (int k = 0; k < npt; k++) {
+ lagrangeValuesAtNewPoint.setEntry(k, fAtInterpolationPoints.getEntry(k) - fAtInterpolationPoints.getEntry(trustRegionCenterInterpolationPointIndex));
+ work3.setEntry(k, ZERO);
+ }
+ for (int j = 0; j < nptm; j++) {
+ double sum = ZERO;
+ for (int k = 0; k < npt; k++) {
+ sum += zMatrix.getEntry(k, j) * lagrangeValuesAtNewPoint.getEntry(k);
+ }
+ for (int k = 0; k < npt; k++) {
+ work3.setEntry(k, work3.getEntry(k) + sum * zMatrix.getEntry(k, j));
+ }
+ }
+ for (int k = 0; k < npt; k++) {
+ double sum = ZERO;
+ for (int j = 0; j < n; j++) {
+ sum += interpolationPoints.getEntry(k, j) * trustRegionCenterOffset.getEntry(j);
+ }
+ work2.setEntry(k, work3.getEntry(k));
+ work3.setEntry(k, sum * work3.getEntry(k));
+ }
+ double gqsq = ZERO;
+ double gisq = ZERO;
+ for (int i = 0; i < n; i++) {
+ double sum = ZERO;
+ for (int k = 0; k < npt; k++) {
+ sum += bMatrix.getEntry(k, i) *
+ lagrangeValuesAtNewPoint.getEntry(k) + interpolationPoints.getEntry(k, i) * work3.getEntry(k);
+ }
+ if (trustRegionCenterOffset.getEntry(i) == lowerDifference.getEntry(i)) {
+ // Computing MIN
+ // Computing 2nd power
+ final double d1 = FastMath.min(ZERO, gradientAtTrustRegionCenter.getEntry(i));
+ gqsq += d1 * d1;
+ // Computing 2nd power
+ final double d2 = FastMath.min(ZERO, sum);
+ gisq += d2 * d2;
+ } else if (trustRegionCenterOffset.getEntry(i) == upperDifference.getEntry(i)) {
+ // Computing MAX
+ // Computing 2nd power
+ final double d1 = FastMath.max(ZERO, gradientAtTrustRegionCenter.getEntry(i));
+ gqsq += d1 * d1;
+ // Computing 2nd power
+ final double d2 = FastMath.max(ZERO, sum);
+ gisq += d2 * d2;
+ } else {
+ // Computing 2nd power
+ final double d1 = gradientAtTrustRegionCenter.getEntry(i);
+ gqsq += d1 * d1;
+ gisq += sum * sum;
+ }
+ lagrangeValuesAtNewPoint.setEntry(npt + i, sum);
+ }
+
+ // Test whether to replace the new quadratic model by the least Frobenius
+ // norm interpolant, making the replacement if the test is satisfied.
+
+ ++itest;
+ if (gqsq < TEN * gisq) {
+ itest = 0;
+ }
+ if (itest >= 3) {
+ for (int i = 0, max = FastMath.max(npt, nh); i < max; i++) {
+ if (i < n) {
+ gradientAtTrustRegionCenter.setEntry(i, lagrangeValuesAtNewPoint.getEntry(npt + i));
+ }
+ if (i < npt) {
+ modelSecondDerivativesParameters.setEntry(i, work2.getEntry(i));
+ }
+ if (i < nh) {
+ modelSecondDerivativesValues.setEntry(i, ZERO);
+ }
+ itest = 0;
+ }
+ }
+ }
+
+ // If a trust region step has provided a sufficient decrease in F, then
+ // branch for another trust region calculation. The case NTRITS=0 occurs
+ // when the new interpolation point was reached by an alternative step.
+
+ if (ntrits == 0) {
+ state = 60; break;
+ }
+ if (f <= fopt + ONE_OVER_TEN * vquad) {
+ state = 60; break;
+ }
+
+ // Alternatively, find out if the interpolation points are close enough
+ // to the best point so far.
+
+ // Computing MAX
+ // Computing 2nd power
+ final double d1 = TWO * delta;
+ // Computing 2nd power
+ final double d2 = TEN * rho;
+ distsq = FastMath.max(d1 * d1, d2 * d2);
+ }
+ case 650: {
+ printState(650); // XXX
+ knew = -1;
+ for (int k = 0; k < npt; k++) {
+ double sum = ZERO;
+ for (int j = 0; j < n; j++) {
+ // Computing 2nd power
+ final double d1 = interpolationPoints.getEntry(k, j) - trustRegionCenterOffset.getEntry(j);
+ sum += d1 * d1;
+ }
+ if (sum > distsq) {
+ knew = k;
+ distsq = sum;
+ }
+ }
+
+ // If KNEW is positive, then ALTMOV finds alternative new positions for
+ // the KNEW-th interpolation point within distance ADELT of XOPT. It is
+ // reached via label 90. Otherwise, there is a branch to label 60 for
+ // another trust region iteration, unless the calculations with the
+ // current RHO are complete.
+
+ if (knew >= 0) {
+ final double dist = FastMath.sqrt(distsq);
+ if (ntrits == -1) {
+ // Computing MIN
+ delta = FastMath.min(ONE_OVER_TEN * delta, HALF * dist);
+ if (delta <= rho * 1.5) {
+ delta = rho;
+ }
+ }
+ ntrits = 0;
+ // Computing MAX
+ // Computing MIN
+ final double d1 = FastMath.min(ONE_OVER_TEN * dist, delta);
+ adelt = FastMath.max(d1, rho);
+ dsq = adelt * adelt;
+ state = 90; break;
+ }
+ if (ntrits == -1) {
+ state = 680; break;
+ }
+ if (ratio > ZERO) {
+ state = 60; break;
+ }
+ if (FastMath.max(delta, dnorm) > rho) {
+ state = 60; break;
+ }
+
+ // The calculations with the current value of RHO are complete. Pick the
+ // next values of RHO and DELTA.
+ }
+ case 680: {
+ printState(680); // XXX
+ if (rho > stoppingTrustRegionRadius) {
+ delta = HALF * rho;
+ ratio = rho / stoppingTrustRegionRadius;
+ if (ratio <= SIXTEEN) {
+ rho = stoppingTrustRegionRadius;
+ } else if (ratio <= TWO_HUNDRED_FIFTY) {
+ rho = FastMath.sqrt(ratio) * stoppingTrustRegionRadius;
+ } else {
+ rho *= ONE_OVER_TEN;
+ }
+ delta = FastMath.max(delta, rho);
+ ntrits = 0;
+ nfsav = getEvaluations();
+ state = 60; break;
+ }
+
+ // Return from the calculation, after another Newton-Raphson step, if
+ // it is too short to have been tried before.
+
+ if (ntrits == -1) {
+ state = 360; break;
+ }
+ }
+ case 720: {
+ printState(720); // XXX
+ if (fAtInterpolationPoints.getEntry(trustRegionCenterInterpolationPointIndex) <= fsave) {
+ for (int i = 0; i < n; i++) {
+ // Computing MIN
+ // Computing MAX
+ final double d3 = lowerBound[i];
+ final double d4 = originShift.getEntry(i) + trustRegionCenterOffset.getEntry(i);
+ final double d1 = FastMath.max(d3, d4);
+ final double d2 = upperBound[i];
+ currentBest.setEntry(i, FastMath.min(d1, d2));
+ if (trustRegionCenterOffset.getEntry(i) == lowerDifference.getEntry(i)) {
+ currentBest.setEntry(i, lowerBound[i]);
+ }
+ if (trustRegionCenterOffset.getEntry(i) == upperDifference.getEntry(i)) {
+ currentBest.setEntry(i, upperBound[i]);
+ }
+ }
+ f = fAtInterpolationPoints.getEntry(trustRegionCenterInterpolationPointIndex);
+ }
+ return f;
+ }
+ default: {
+ throw new MathIllegalStateException(LocalizedFormats.SIMPLE_MESSAGE, "bobyqb");
+ }}}
+ } // bobyqb
+
+ // ----------------------------------------------------------------------------------------
+
+ /**
+ * The arguments N, NPT, XPT, XOPT, BMAT, ZMAT, NDIM, SL and SU all have
+ * the same meanings as the corresponding arguments of BOBYQB.
+ * KOPT is the index of the optimal interpolation point.
+ * KNEW is the index of the interpolation point that is going to be moved.
+ * ADELT is the current trust region bound.
+ * XNEW will be set to a suitable new position for the interpolation point
+ * XPT(KNEW,.). Specifically, it satisfies the SL, SU and trust region
+ * bounds and it should provide a large denominator in the next call of
+ * UPDATE. The step XNEW-XOPT from XOPT is restricted to moves along the
+ * straight lines through XOPT and another interpolation point.
+ * XALT also provides a large value of the modulus of the KNEW-th Lagrange
+ * function subject to the constraints that have been mentioned, its main
+ * difference from XNEW being that XALT-XOPT is a constrained version of
+ * the Cauchy step within the trust region. An exception is that XALT is
+ * not calculated if all components of GLAG (see below) are zero.
+ * ALPHA will be set to the KNEW-th diagonal element of the H matrix.
+ * CAUCHY will be set to the square of the KNEW-th Lagrange function at
+ * the step XALT-XOPT from XOPT for the vector XALT that is returned,
+ * except that CAUCHY is set to zero if XALT is not calculated.
+ * GLAG is a working space vector of length N for the gradient of the
+ * KNEW-th Lagrange function at XOPT.
+ * HCOL is a working space vector of length NPT for the second derivative
+ * coefficients of the KNEW-th Lagrange function.
+ * W is a working space vector of length 2N that is going to hold the
+ * constrained Cauchy step from XOPT of the Lagrange function, followed
+ * by the downhill version of XALT when the uphill step is calculated.
+ *
+ * Set the first NPT components of W to the leading elements of the
+ * KNEW-th column of the H matrix.
+ * @param knew
+ * @param adelt
+ */
+ private double[] altmov(
+ int knew,
+ double adelt
+ ) {
+ printMethod(); // XXX
+
+ final int n = currentBest.getDimension();
+ final int npt = numberOfInterpolationPoints;
+
+ final ArrayRealVector glag = new ArrayRealVector(n);
+ final ArrayRealVector hcol = new ArrayRealVector(npt);
+
+ final ArrayRealVector work1 = new ArrayRealVector(n);
+ final ArrayRealVector work2 = new ArrayRealVector(n);
+
+ for (int k = 0; k < npt; k++) {
+ hcol.setEntry(k, ZERO);
+ }
+ for (int j = 0, max = npt - n - 1; j < max; j++) {
+ final double tmp = zMatrix.getEntry(knew, j);
+ for (int k = 0; k < npt; k++) {
+ hcol.setEntry(k, hcol.getEntry(k) + tmp * zMatrix.getEntry(k, j));
+ }
+ }
+ final double alpha = hcol.getEntry(knew);
+ final double ha = HALF * alpha;
+
+ // Calculate the gradient of the KNEW-th Lagrange function at XOPT.
+
+ for (int i = 0; i < n; i++) {
+ glag.setEntry(i, bMatrix.getEntry(knew, i));
+ }
+ for (int k = 0; k < npt; k++) {
+ double tmp = ZERO;
+ for (int j = 0; j < n; j++) {
+ tmp += interpolationPoints.getEntry(k, j) * trustRegionCenterOffset.getEntry(j);
+ }
+ tmp *= hcol.getEntry(k);
+ for (int i = 0; i < n; i++) {
+ glag.setEntry(i, glag.getEntry(i) + tmp * interpolationPoints.getEntry(k, i));
+ }
+ }
+
+ // Search for a large denominator along the straight lines through XOPT
+ // and another interpolation point. SLBD and SUBD will be lower and upper
+ // bounds on the step along each of these lines in turn. PREDSQ will be
+ // set to the square of the predicted denominator for each line. PRESAV
+ // will be set to the largest admissible value of PREDSQ that occurs.
+
+ double presav = ZERO;
+ double step = Double.NaN;
+ int ksav = 0;
+ int ibdsav = 0;
+ double stpsav = 0;
+ for (int k = 0; k < npt; k++) {
+ if (k == trustRegionCenterInterpolationPointIndex) {
+ continue;
+ }
+ double dderiv = ZERO;
+ double distsq = ZERO;
+ for (int i = 0; i < n; i++) {
+ final double tmp = interpolationPoints.getEntry(k, i) - trustRegionCenterOffset.getEntry(i);
+ dderiv += glag.getEntry(i) * tmp;
+ distsq += tmp * tmp;
+ }
+ double subd = adelt / FastMath.sqrt(distsq);
+ double slbd = -subd;
+ int ilbd = 0;
+ int iubd = 0;
+ final double sumin = FastMath.min(ONE, subd);
+
+ // Revise SLBD and SUBD if necessary because of the bounds in SL and SU.
+
+ for (int i = 0; i < n; i++) {
+ final double tmp = interpolationPoints.getEntry(k, i) - trustRegionCenterOffset.getEntry(i);
+ if (tmp > ZERO) {
+ if (slbd * tmp < lowerDifference.getEntry(i) - trustRegionCenterOffset.getEntry(i)) {
+ slbd = (lowerDifference.getEntry(i) - trustRegionCenterOffset.getEntry(i)) / tmp;
+ ilbd = -i - 1;
+ }
+ if (subd * tmp > upperDifference.getEntry(i) - trustRegionCenterOffset.getEntry(i)) {
+ // Computing MAX
+ subd = FastMath.max(sumin,
+ (upperDifference.getEntry(i) - trustRegionCenterOffset.getEntry(i)) / tmp);
+ iubd = i + 1;
+ }
+ } else if (tmp < ZERO) {
+ if (slbd * tmp > upperDifference.getEntry(i) - trustRegionCenterOffset.getEntry(i)) {
+ slbd = (upperDifference.getEntry(i) - trustRegionCenterOffset.getEntry(i)) / tmp;
+ ilbd = i + 1;
+ }
+ if (subd * tmp < lowerDifference.getEntry(i) - trustRegionCenterOffset.getEntry(i)) {
+ // Computing MAX
+ subd = FastMath.max(sumin,
+ (lowerDifference.getEntry(i) - trustRegionCenterOffset.getEntry(i)) / tmp);
+ iubd = -i - 1;
+ }
+ }
+ }
+
+ // Seek a large modulus of the KNEW-th Lagrange function when the index
+ // of the other interpolation point on the line through XOPT is KNEW.
+
+ step = slbd;
+ int isbd = ilbd;
+ double vlag = Double.NaN;
+ if (k == knew) {
+ final double diff = dderiv - ONE;
+ vlag = slbd * (dderiv - slbd * diff);
+ final double d1 = subd * (dderiv - subd * diff);
+ if (FastMath.abs(d1) > FastMath.abs(vlag)) {
+ step = subd;
+ vlag = d1;
+ isbd = iubd;
+ }
+ final double d2 = HALF * dderiv;
+ final double d3 = d2 - diff * slbd;
+ final double d4 = d2 - diff * subd;
+ if (d3 * d4 < ZERO) {
+ final double d5 = d2 * d2 / diff;
+ if (FastMath.abs(d5) > FastMath.abs(vlag)) {
+ step = d2 / diff;
+ vlag = d5;
+ isbd = 0;
+ }
+ }
+
+ // Search along each of the other lines through XOPT and another point.
+
+ } else {
+ vlag = slbd * (ONE - slbd);
+ final double tmp = subd * (ONE - subd);
+ if (FastMath.abs(tmp) > FastMath.abs(vlag)) {
+ step = subd;
+ vlag = tmp;
+ isbd = iubd;
+ }
+ if (subd > HALF && FastMath.abs(vlag) < ONE_OVER_FOUR) {
+ step = HALF;
+ vlag = ONE_OVER_FOUR;
+ isbd = 0;
+ }
+ vlag *= dderiv;
+ }
+
+ // Calculate PREDSQ for the current line search and maintain PRESAV.
+
+ final double tmp = step * (ONE - step) * distsq;
+ final double predsq = vlag * vlag * (vlag * vlag + ha * tmp * tmp);
+ if (predsq > presav) {
+ presav = predsq;
+ ksav = k;
+ stpsav = step;
+ ibdsav = isbd;
+ }
+ }
+
+ // Construct XNEW in a way that satisfies the bound constraints exactly.
+
+ for (int i = 0; i < n; i++) {
+ final double tmp = trustRegionCenterOffset.getEntry(i) + stpsav * (interpolationPoints.getEntry(ksav, i) - trustRegionCenterOffset.getEntry(i));
+ newPoint.setEntry(i, FastMath.max(lowerDifference.getEntry(i),
+ FastMath.min(upperDifference.getEntry(i), tmp)));
+ }
+ if (ibdsav < 0) {
+ newPoint.setEntry(-ibdsav - 1, lowerDifference.getEntry(-ibdsav - 1));
+ }
+ if (ibdsav > 0) {
+ newPoint.setEntry(ibdsav - 1, upperDifference.getEntry(ibdsav - 1));
+ }
+
+ // Prepare for the iterative method that assembles the constrained Cauchy
+ // step in W. The sum of squares of the fixed components of W is formed in
+ // WFIXSQ, and the free components of W are set to BIGSTP.
+
+ final double bigstp = adelt + adelt;
+ int iflag = 0;
+ double cauchy = Double.NaN;
+ double csave = ZERO;
+ while (true) {
+ double wfixsq = ZERO;
+ double ggfree = ZERO;
+ for (int i = 0; i < n; i++) {
+ final double glagValue = glag.getEntry(i);
+ work1.setEntry(i, ZERO);
+ if (FastMath.min(trustRegionCenterOffset.getEntry(i) - lowerDifference.getEntry(i), glagValue) > ZERO ||
+ FastMath.max(trustRegionCenterOffset.getEntry(i) - upperDifference.getEntry(i), glagValue) < ZERO) {
+ work1.setEntry(i, bigstp);
+ // Computing 2nd power
+ ggfree += glagValue * glagValue;
+ }
+ }
+ if (ggfree == ZERO) {
+ return new double[] { alpha, ZERO };
+ }
+
+ // Investigate whether more components of W can be fixed.
+ final double tmp1 = adelt * adelt - wfixsq;
+ if (tmp1 > ZERO) {
+ step = FastMath.sqrt(tmp1 / ggfree);
+ ggfree = ZERO;
+ for (int i = 0; i < n; i++) {
+ if (work1.getEntry(i) == bigstp) {
+ final double tmp2 = trustRegionCenterOffset.getEntry(i) - step * glag.getEntry(i);
+ if (tmp2 <= lowerDifference.getEntry(i)) {
+ work1.setEntry(i, lowerDifference.getEntry(i) - trustRegionCenterOffset.getEntry(i));
+ // Computing 2nd power
+ final double d1 = work1.getEntry(i);
+ wfixsq += d1 * d1;
+ } else if (tmp2 >= upperDifference.getEntry(i)) {
+ work1.setEntry(i, upperDifference.getEntry(i) - trustRegionCenterOffset.getEntry(i));
+ // Computing 2nd power
+ final double d1 = work1.getEntry(i);
+ wfixsq += d1 * d1;
+ } else {
+ // Computing 2nd power
+ final double d1 = glag.getEntry(i);
+ ggfree += d1 * d1;
+ }
+ }
+ }
+ }
+
+ // Set the remaining free components of W and all components of XALT,
+ // except that W may be scaled later.
+
+ double gw = ZERO;
+ for (int i = 0; i < n; i++) {
+ final double glagValue = glag.getEntry(i);
+ if (work1.getEntry(i) == bigstp) {
+ work1.setEntry(i, -step * glagValue);
+ final double min = FastMath.min(upperDifference.getEntry(i),
+ trustRegionCenterOffset.getEntry(i) + work1.getEntry(i));
+ alternativeNewPoint.setEntry(i, FastMath.max(lowerDifference.getEntry(i), min));
+ } else if (work1.getEntry(i) == ZERO) {
+ alternativeNewPoint.setEntry(i, trustRegionCenterOffset.getEntry(i));
+ } else if (glagValue > ZERO) {
+ alternativeNewPoint.setEntry(i, lowerDifference.getEntry(i));
+ } else {
+ alternativeNewPoint.setEntry(i, upperDifference.getEntry(i));
+ }
+ gw += glagValue * work1.getEntry(i);
+ }
+
+ // Set CURV to the curvature of the KNEW-th Lagrange function along W.
+ // Scale W by a factor less than one if that can reduce the modulus of
+ // the Lagrange function at XOPT+W. Set CAUCHY to the final value of
+ // the square of this function.
+
+ double curv = ZERO;
+ for (int k = 0; k < npt; k++) {
+ double tmp = ZERO;
+ for (int j = 0; j < n; j++) {
+ tmp += interpolationPoints.getEntry(k, j) * work1.getEntry(j);
+ }
+ curv += hcol.getEntry(k) * tmp * tmp;
+ }
+ if (iflag == 1) {
+ curv = -curv;
+ }
+ if (curv > -gw &&
+ curv < -gw * (ONE + FastMath.sqrt(TWO))) {
+ final double scale = -gw / curv;
+ for (int i = 0; i < n; i++) {
+ final double tmp = trustRegionCenterOffset.getEntry(i) + scale * work1.getEntry(i);
+ alternativeNewPoint.setEntry(i, FastMath.max(lowerDifference.getEntry(i),
+ FastMath.min(upperDifference.getEntry(i), tmp)));
+ }
+ // Computing 2nd power
+ final double d1 = HALF * gw * scale;
+ cauchy = d1 * d1;
+ } else {
+ // Computing 2nd power
+ final double d1 = gw + HALF * curv;
+ cauchy = d1 * d1;
+ }
+
+ // If IFLAG is zero, then XALT is calculated as before after reversing
+ // the sign of GLAG. Thus two XALT vectors become available. The one that
+ // is chosen is the one that gives the larger value of CAUCHY.
+
+ if (iflag == 0) {
+ for (int i = 0; i < n; i++) {
+ glag.setEntry(i, -glag.getEntry(i));
+ work2.setEntry(i, alternativeNewPoint.getEntry(i));
+ }
+ csave = cauchy;
+ iflag = 1;
+ } else {
+ break;
+ }
+ }
+ if (csave > cauchy) {
+ for (int i = 0; i < n; i++) {
+ alternativeNewPoint.setEntry(i, work2.getEntry(i));
+ }
+ cauchy = csave;
+ }
+
+ return new double[] { alpha, cauchy };
+ } // altmov
+
+ // ----------------------------------------------------------------------------------------
+
+ /**
+ * SUBROUTINE PRELIM sets the elements of XBASE, XPT, FVAL, GOPT, HQ, PQ,
+ * BMAT and ZMAT for the first iteration, and it maintains the values of
+ * NF and KOPT. The vector X is also changed by PRELIM.
+ *
+ * The arguments N, NPT, X, XL, XU, RHOBEG, IPRINT and MAXFUN are the
+ * same as the corresponding arguments in SUBROUTINE BOBYQA.
+ * The arguments XBASE, XPT, FVAL, HQ, PQ, BMAT, ZMAT, NDIM, SL and SU
+ * are the same as the corresponding arguments in BOBYQB, the elements
+ * of SL and SU being set in BOBYQA.
+ * GOPT is usually the gradient of the quadratic model at XOPT+XBASE, but
+ * it is set by PRELIM to the gradient of the quadratic model at XBASE.
+ * If XOPT is nonzero, BOBYQB will change it to its usual value later.
+ * NF is maintaned as the number of calls of CALFUN so far.
+ * KOPT will be such that the least calculated value of F so far is at
+ * the point XPT(KOPT,.)+XBASE in the space of the variables.
+ *
+ * @param lowerBound Lower bounds.
+ * @param upperBound Upper bounds.
+ */
+ private void prelim(double[] lowerBound,
+ double[] upperBound) {
+ printMethod(); // XXX
+
+ final int n = currentBest.getDimension();
+ final int npt = numberOfInterpolationPoints;
+ final int ndim = bMatrix.getRowDimension();
+
+ final double rhosq = initialTrustRegionRadius * initialTrustRegionRadius;
+ final double recip = 1d / rhosq;
+ final int np = n + 1;
+
+ // Set XBASE to the initial vector of variables, and set the initial
+ // elements of XPT, BMAT, HQ, PQ and ZMAT to zero.
+
+ for (int j = 0; j < n; j++) {
+ originShift.setEntry(j, currentBest.getEntry(j));
+ for (int k = 0; k < npt; k++) {
+ interpolationPoints.setEntry(k, j, ZERO);
+ }
+ for (int i = 0; i < ndim; i++) {
+ bMatrix.setEntry(i, j, ZERO);
+ }
+ }
+ for (int i = 0, max = n * np / 2; i < max; i++) {
+ modelSecondDerivativesValues.setEntry(i, ZERO);
+ }
+ for (int k = 0; k < npt; k++) {
+ modelSecondDerivativesParameters.setEntry(k, ZERO);
+ for (int j = 0, max = npt - np; j < max; j++) {
+ zMatrix.setEntry(k, j, ZERO);
+ }
+ }
+
+ // Begin the initialization procedure. NF becomes one more than the number
+ // of function values so far. The coordinates of the displacement of the
+ // next initial interpolation point from XBASE are set in XPT(NF+1,.).
+
+ int ipt = 0;
+ int jpt = 0;
+ double fbeg = Double.NaN;
+ do {
+ final int nfm = getEvaluations();
+ final int nfx = nfm - n;
+ final int nfmm = nfm - 1;
+ final int nfxm = nfx - 1;
+ double stepa = 0;
+ double stepb = 0;
+ if (nfm <= 2 * n) {
+ if (nfm >= 1 &&
+ nfm <= n) {
+ stepa = initialTrustRegionRadius;
+ if (upperDifference.getEntry(nfmm) == ZERO) {
+ stepa = -stepa;
+ // throw new PathIsExploredException(); // XXX
+ }
+ interpolationPoints.setEntry(nfm, nfmm, stepa);
+ } else if (nfm > n) {
+ stepa = interpolationPoints.getEntry(nfx, nfxm);
+ stepb = -initialTrustRegionRadius;
+ if (lowerDifference.getEntry(nfxm) == ZERO) {
+ stepb = FastMath.min(TWO * initialTrustRegionRadius, upperDifference.getEntry(nfxm));
+ // throw new PathIsExploredException(); // XXX
+ }
+ if (upperDifference.getEntry(nfxm) == ZERO) {
+ stepb = FastMath.max(-TWO * initialTrustRegionRadius, lowerDifference.getEntry(nfxm));
+ // throw new PathIsExploredException(); // XXX
+ }
+ interpolationPoints.setEntry(nfm, nfxm, stepb);
+ }
+ } else {
+ final int tmp1 = (nfm - np) / n;
+ jpt = nfm - tmp1 * n - n;
+ ipt = jpt + tmp1;
+ if (ipt > n) {
+ final int tmp2 = jpt;
+ jpt = ipt - n;
+ ipt = tmp2;
+// throw new PathIsExploredException(); // XXX
+ }
+ final int iptMinus1 = ipt - 1;
+ final int jptMinus1 = jpt - 1;
+ interpolationPoints.setEntry(nfm, iptMinus1, interpolationPoints.getEntry(ipt, iptMinus1));
+ interpolationPoints.setEntry(nfm, jptMinus1, interpolationPoints.getEntry(jpt, jptMinus1));
+ }
+
+ // Calculate the next value of F. The least function value so far and
+ // its index are required.
+
+ for (int j = 0; j < n; j++) {
+ currentBest.setEntry(j, FastMath.min(FastMath.max(lowerBound[j],
+ originShift.getEntry(j) + interpolationPoints.getEntry(nfm, j)),
+ upperBound[j]));
+ if (interpolationPoints.getEntry(nfm, j) == lowerDifference.getEntry(j)) {
+ currentBest.setEntry(j, lowerBound[j]);
+ }
+ if (interpolationPoints.getEntry(nfm, j) == upperDifference.getEntry(j)) {
+ currentBest.setEntry(j, upperBound[j]);
+ }
+ }
+
+ final double objectiveValue = computeObjectiveValue(currentBest.toArray());
+ final double f = isMinimize ? objectiveValue : -objectiveValue;
+ final int numEval = getEvaluations(); // nfm + 1
+ fAtInterpolationPoints.setEntry(nfm, f);
+
+ if (numEval == 1) {
+ fbeg = f;
+ trustRegionCenterInterpolationPointIndex = 0;
+ } else if (f < fAtInterpolationPoints.getEntry(trustRegionCenterInterpolationPointIndex)) {
+ trustRegionCenterInterpolationPointIndex = nfm;
+ }
+
+ // Set the nonzero initial elements of BMAT and the quadratic model in the
+ // cases when NF is at most 2*N+1. If NF exceeds N+1, then the positions
+ // of the NF-th and (NF-N)-th interpolation points may be switched, in
+ // order that the function value at the first of them contributes to the
+ // off-diagonal second derivative terms of the initial quadratic model.
+
+ if (numEval <= 2 * n + 1) {
+ if (numEval >= 2 &&
+ numEval <= n + 1) {
+ gradientAtTrustRegionCenter.setEntry(nfmm, (f - fbeg) / stepa);
+ if (npt < numEval + n) {
+ final double oneOverStepA = ONE / stepa;
+ bMatrix.setEntry(0, nfmm, -oneOverStepA);
+ bMatrix.setEntry(nfm, nfmm, oneOverStepA);
+ bMatrix.setEntry(npt + nfmm, nfmm, -HALF * rhosq);
+ // throw new PathIsExploredException(); // XXX
+ }
+ } else if (numEval >= n + 2) {
+ final int ih = nfx * (nfx + 1) / 2 - 1;
+ final double tmp = (f - fbeg) / stepb;
+ final double diff = stepb - stepa;
+ modelSecondDerivativesValues.setEntry(ih, TWO * (tmp - gradientAtTrustRegionCenter.getEntry(nfxm)) / diff);
+ gradientAtTrustRegionCenter.setEntry(nfxm, (gradientAtTrustRegionCenter.getEntry(nfxm) * stepb - tmp * stepa) / diff);
+ if (stepa * stepb < ZERO && f < fAtInterpolationPoints.getEntry(nfm - n)) {
+ fAtInterpolationPoints.setEntry(nfm, fAtInterpolationPoints.getEntry(nfm - n));
+ fAtInterpolationPoints.setEntry(nfm - n, f);
+ if (trustRegionCenterInterpolationPointIndex == nfm) {
+ trustRegionCenterInterpolationPointIndex = nfm - n;
+ }
+ interpolationPoints.setEntry(nfm - n, nfxm, stepb);
+ interpolationPoints.setEntry(nfm, nfxm, stepa);
+ }
+ bMatrix.setEntry(0, nfxm, -(stepa + stepb) / (stepa * stepb));
+ bMatrix.setEntry(nfm, nfxm, -HALF / interpolationPoints.getEntry(nfm - n, nfxm));
+ bMatrix.setEntry(nfm - n, nfxm,
+ -bMatrix.getEntry(0, nfxm) - bMatrix.getEntry(nfm, nfxm));
+ zMatrix.setEntry(0, nfxm, FastMath.sqrt(TWO) / (stepa * stepb));
+ zMatrix.setEntry(nfm, nfxm, FastMath.sqrt(HALF) / rhosq);
+ // zMatrix.setEntry(nfm, nfxm, FastMath.sqrt(HALF) * recip); // XXX "testAckley" and "testDiffPow" fail.
+ zMatrix.setEntry(nfm - n, nfxm,
+ -zMatrix.getEntry(0, nfxm) - zMatrix.getEntry(nfm, nfxm));
+ }
+
+ // Set the off-diagonal second derivatives of the Lagrange functions and
+ // the initial quadratic model.
+
+ } else {
+ zMatrix.setEntry(0, nfxm, recip);
+ zMatrix.setEntry(nfm, nfxm, recip);
+ zMatrix.setEntry(ipt, nfxm, -recip);
+ zMatrix.setEntry(jpt, nfxm, -recip);
+
+ final int ih = ipt * (ipt - 1) / 2 + jpt - 1;
+ final double tmp = interpolationPoints.getEntry(nfm, ipt - 1) * interpolationPoints.getEntry(nfm, jpt - 1);
+ modelSecondDerivativesValues.setEntry(ih, (fbeg - fAtInterpolationPoints.getEntry(ipt) - fAtInterpolationPoints.getEntry(jpt) + f) / tmp);
+// throw new PathIsExploredException(); // XXX
+ }
+ } while (getEvaluations() < npt);
+ } // prelim
+
+
+ // ----------------------------------------------------------------------------------------
+
+ /**
+ * A version of the truncated conjugate gradient is applied. If a line
+ * search is restricted by a constraint, then the procedure is restarted,
+ * the values of the variables that are at their bounds being fixed. If
+ * the trust region boundary is reached, then further changes may be made
+ * to D, each one being in the two dimensional space that is spanned
+ * by the current D and the gradient of Q at XOPT+D, staying on the trust
+ * region boundary. Termination occurs when the reduction in Q seems to
+ * be close to the greatest reduction that can be achieved.
+ * The arguments N, NPT, XPT, XOPT, GOPT, HQ, PQ, SL and SU have the same
+ * meanings as the corresponding arguments of BOBYQB.
+ * DELTA is the trust region radius for the present calculation, which
+ * seeks a small value of the quadratic model within distance DELTA of
+ * XOPT subject to the bounds on the variables.
+ * XNEW will be set to a new vector of variables that is approximately
+ * the one that minimizes the quadratic model within the trust region
+ * subject to the SL and SU constraints on the variables. It satisfies
+ * as equations the bounds that become active during the calculation.
+ * D is the calculated trial step from XOPT, generated iteratively from an
+ * initial value of zero. Thus XNEW is XOPT+D after the final iteration.
+ * GNEW holds the gradient of the quadratic model at XOPT+D. It is updated
+ * when D is updated.
+ * xbdi.get( is a working space vector. For I=1,2,...,N, the element xbdi.get((I) is
+ * set to -1.0, 0.0, or 1.0, the value being nonzero if and only if the
+ * I-th variable has become fixed at a bound, the bound being SL(I) or
+ * SU(I) in the case xbdi.get((I)=-1.0 or xbdi.get((I)=1.0, respectively. This
+ * information is accumulated during the construction of XNEW.
+ * The arrays S, HS and HRED are also used for working space. They hold the
+ * current search direction, and the changes in the gradient of Q along S
+ * and the reduced D, respectively, where the reduced D is the same as D,
+ * except that the components of the fixed variables are zero.
+ * DSQ will be set to the square of the length of XNEW-XOPT.
+ * CRVMIN is set to zero if D reaches the trust region boundary. Otherwise
+ * it is set to the least curvature of H that occurs in the conjugate
+ * gradient searches that are not restricted by any constraints. The
+ * value CRVMIN=-1.0D0 is set, however, if all of these searches are
+ * constrained.
+ * @param delta
+ * @param gnew
+ * @param xbdi
+ * @param s
+ * @param hs
+ * @param hred
+ */
+ private double[] trsbox(
+ double delta,
+ ArrayRealVector gnew,
+ ArrayRealVector xbdi,
+ ArrayRealVector s,
+ ArrayRealVector hs,
+ ArrayRealVector hred
+ ) {
+ printMethod(); // XXX
+
+ final int n = currentBest.getDimension();
+ final int npt = numberOfInterpolationPoints;
+
+ double dsq = Double.NaN;
+ double crvmin = Double.NaN;
+
+ // Local variables
+ double ds;
+ int iu;
+ double dhd, dhs, cth, shs, sth, ssq, beta=0, sdec, blen;
+ int iact = -1;
+ int nact = 0;
+ double angt = 0, qred;
+ int isav;
+ double temp = 0, xsav = 0, xsum = 0, angbd = 0, dredg = 0, sredg = 0;
+ int iterc;
+ double resid = 0, delsq = 0, ggsav = 0, tempa = 0, tempb = 0,
+ redmax = 0, dredsq = 0, redsav = 0, gredsq = 0, rednew = 0;
+ int itcsav = 0;
+ double rdprev = 0, rdnext = 0, stplen = 0, stepsq = 0;
+ int itermax = 0;
+
+ // Set some constants.
+
+ // Function Body
+
+ // The sign of GOPT(I) gives the sign of the change to the I-th variable
+ // that will reduce Q from its value at XOPT. Thus xbdi.get((I) shows whether
+ // or not to fix the I-th variable at one of its bounds initially, with
+ // NACT being set to the number of fixed variables. D and GNEW are also
+ // set for the first iteration. DELSQ is the upper bound on the sum of
+ // squares of the free variables. QRED is the reduction in Q so far.
+
+ iterc = 0;
+ nact = 0;
+ for (int i = 0; i < n; i++) {
+ xbdi.setEntry(i, ZERO);
+ if (trustRegionCenterOffset.getEntry(i) <= lowerDifference.getEntry(i)) {
+ if (gradientAtTrustRegionCenter.getEntry(i) >= ZERO) {
+ xbdi.setEntry(i, MINUS_ONE);
+ }
+ } else if (trustRegionCenterOffset.getEntry(i) >= upperDifference.getEntry(i) &&
+ gradientAtTrustRegionCenter.getEntry(i) <= ZERO) {
+ xbdi.setEntry(i, ONE);
+ }
+ if (xbdi.getEntry(i) != ZERO) {
+ ++nact;
+ }
+ trialStepPoint.setEntry(i, ZERO);
+ gnew.setEntry(i, gradientAtTrustRegionCenter.getEntry(i));
+ }
+ delsq = delta * delta;
+ qred = ZERO;
+ crvmin = MINUS_ONE;
+
+ // Set the next search direction of the conjugate gradient method. It is
+ // the steepest descent direction initially and when the iterations are
+ // restarted because a variable has just been fixed by a bound, and of
+ // course the components of the fixed variables are zero. ITERMAX is an
+ // upper bound on the indices of the conjugate gradient iterations.
+
+ int state = 20;
+ for(;;) {
+ switch (state) {
+ case 20: {
+ printState(20); // XXX
+ beta = ZERO;
+ }
+ case 30: {
+ printState(30); // XXX
+ stepsq = ZERO;
+ for (int i = 0; i < n; i++) {
+ if (xbdi.getEntry(i) != ZERO) {
+ s.setEntry(i, ZERO);
+ } else if (beta == ZERO) {
+ s.setEntry(i, -gnew.getEntry(i));
+ } else {
+ s.setEntry(i, beta * s.getEntry(i) - gnew.getEntry(i));
+ }
+ // Computing 2nd power
+ final double d1 = s.getEntry(i);
+ stepsq += d1 * d1;
+ }
+ if (stepsq == ZERO) {
+ state = 190; break;
+ }
+ if (beta == ZERO) {
+ gredsq = stepsq;
+ itermax = iterc + n - nact;
+ }
+ if (gredsq * delsq <= qred * 1e-4 * qred) {
+ state = 190; break;
+ }
+
+ // Multiply the search direction by the second derivative matrix of Q and
+ // calculate some scalars for the choice of steplength. Then set BLEN to
+ // the length of the the step to the trust region boundary and STPLEN to
+ // the steplength, ignoring the simple bounds.
+
+ state = 210; break;
+ }
+ case 50: {
+ printState(50); // XXX
+ resid = delsq;
+ ds = ZERO;
+ shs = ZERO;
+ for (int i = 0; i < n; i++) {
+ if (xbdi.getEntry(i) == ZERO) {
+ // Computing 2nd power
+ final double d1 = trialStepPoint.getEntry(i);
+ resid -= d1 * d1;
+ ds += s.getEntry(i) * trialStepPoint.getEntry(i);
+ shs += s.getEntry(i) * hs.getEntry(i);
+ }
+ }
+ if (resid <= ZERO) {
+ state = 90; break;
+ }
+ temp = FastMath.sqrt(stepsq * resid + ds * ds);
+ if (ds < ZERO) {
+ blen = (temp - ds) / stepsq;
+ } else {
+ blen = resid / (temp + ds);
+ }
+ stplen = blen;
+ if (shs > ZERO) {
+ // Computing MIN
+ stplen = FastMath.min(blen, gredsq / shs);
+ }
+
+ // Reduce STPLEN if necessary in order to preserve the simple bounds,
+ // letting IACT be the index of the new constrained variable.
+
+ iact = -1;
+ for (int i = 0; i < n; i++) {
+ if (s.getEntry(i) != ZERO) {
+ xsum = trustRegionCenterOffset.getEntry(i) + trialStepPoint.getEntry(i);
+ if (s.getEntry(i) > ZERO) {
+ temp = (upperDifference.getEntry(i) - xsum) / s.getEntry(i);
+ } else {
+ temp = (lowerDifference.getEntry(i) - xsum) / s.getEntry(i);
+ }
+ if (temp < stplen) {
+ stplen = temp;
+ iact = i;
+ }
+ }
+ }
+
+ // Update CRVMIN, GNEW and D. Set SDEC to the decrease that occurs in Q.
+
+ sdec = ZERO;
+ if (stplen > ZERO) {
+ ++iterc;
+ temp = shs / stepsq;
+ if (iact == -1 && temp > ZERO) {
+ crvmin = FastMath.min(crvmin,temp);
+ if (crvmin == MINUS_ONE) {
+ crvmin = temp;
+ }
+ }
+ ggsav = gredsq;
+ gredsq = ZERO;
+ for (int i = 0; i < n; i++) {
+ gnew.setEntry(i, gnew.getEntry(i) + stplen * hs.getEntry(i));
+ if (xbdi.getEntry(i) == ZERO) {
+ // Computing 2nd power
+ final double d1 = gnew.getEntry(i);
+ gredsq += d1 * d1;
+ }
+ trialStepPoint.setEntry(i, trialStepPoint.getEntry(i) + stplen * s.getEntry(i));
+ }
+ // Computing MAX
+ final double d1 = stplen * (ggsav - HALF * stplen * shs);
+ sdec = FastMath.max(d1, ZERO);
+ qred += sdec;
+ }
+
+ // Restart the conjugate gradient method if it has hit a new bound.
+
+ if (iact >= 0) {
+ ++nact;
+ xbdi.setEntry(iact, ONE);
+ if (s.getEntry(iact) < ZERO) {
+ xbdi.setEntry(iact, MINUS_ONE);
+ }
+ // Computing 2nd power
+ final double d1 = trialStepPoint.getEntry(iact);
+ delsq -= d1 * d1;
+ if (delsq <= ZERO) {
+ state = 190; break;
+ }
+ state = 20; break;
+ }
+
+ // If STPLEN is less than BLEN, then either apply another conjugate
+ // gradient iteration or RETURN.
+
+ if (stplen < blen) {
+ if (iterc == itermax) {
+ state = 190; break;
+ }
+ if (sdec <= qred * .01) {
+ state = 190; break;
+ }
+ beta = gredsq / ggsav;
+ state = 30; break;
+ }
+ }
+ case 90: {
+ printState(90); // XXX
+ crvmin = ZERO;
+
+ // Prepare for the alternative iteration by calculating some scalars
+ // and by multiplying the reduced D by the second derivative matrix of
+ // Q, where S holds the reduced D in the call of GGMULT.
+
+ }
+ case 100: {
+ printState(100); // XXX
+ if (nact >= n - 1) {
+ state = 190; break;
+ }
+ dredsq = ZERO;
+ dredg = ZERO;
+ gredsq = ZERO;
+ for (int i = 0; i < n; i++) {
+ if (xbdi.getEntry(i) == ZERO) {
+ // Computing 2nd power
+ double d1 = trialStepPoint.getEntry(i);
+ dredsq += d1 * d1;
+ dredg += trialStepPoint.getEntry(i) * gnew.getEntry(i);
+ // Computing 2nd power
+ d1 = gnew.getEntry(i);
+ gredsq += d1 * d1;
+ s.setEntry(i, trialStepPoint.getEntry(i));
+ } else {
+ s.setEntry(i, ZERO);
+ }
+ }
+ itcsav = iterc;
+ state = 210; break;
+ // Let the search direction S be a linear combination of the reduced D
+ // and the reduced G that is orthogonal to the reduced D.
+ }
+ case 120: {
+ printState(120); // XXX
+ ++iterc;
+ temp = gredsq * dredsq - dredg * dredg;
+ if (temp <= qred * 1e-4 * qred) {
+ state = 190; break;
+ }
+ temp = FastMath.sqrt(temp);
+ for (int i = 0; i < n; i++) {
+ if (xbdi.getEntry(i) == ZERO) {
+ s.setEntry(i, (dredg * trialStepPoint.getEntry(i) - dredsq * gnew.getEntry(i)) / temp);
+ } else {
+ s.setEntry(i, ZERO);
+ }
+ }
+ sredg = -temp;
+
+ // By considering the simple bounds on the variables, calculate an upper
+ // bound on the tangent of half the angle of the alternative iteration,
+ // namely ANGBD, except that, if already a free variable has reached a
+ // bound, there is a branch back to label 100 after fixing that variable.
+
+ angbd = ONE;
+ iact = -1;
+ for (int i = 0; i < n; i++) {
+ if (xbdi.getEntry(i) == ZERO) {
+ tempa = trustRegionCenterOffset.getEntry(i) + trialStepPoint.getEntry(i) - lowerDifference.getEntry(i);
+ tempb = upperDifference.getEntry(i) - trustRegionCenterOffset.getEntry(i) - trialStepPoint.getEntry(i);
+ if (tempa <= ZERO) {
+ ++nact;
+ xbdi.setEntry(i, MINUS_ONE);
+ state = 100; break;
+ } else if (tempb <= ZERO) {
+ ++nact;
+ xbdi.setEntry(i, ONE);
+ state = 100; break;
+ }
+ // Computing 2nd power
+ double d1 = trialStepPoint.getEntry(i);
+ // Computing 2nd power
+ double d2 = s.getEntry(i);
+ ssq = d1 * d1 + d2 * d2;
+ // Computing 2nd power
+ d1 = trustRegionCenterOffset.getEntry(i) - lowerDifference.getEntry(i);
+ temp = ssq - d1 * d1;
+ if (temp > ZERO) {
+ temp = FastMath.sqrt(temp) - s.getEntry(i);
+ if (angbd * temp > tempa) {
+ angbd = tempa / temp;
+ iact = i;
+ xsav = MINUS_ONE;
+ }
+ }
+ // Computing 2nd power
+ d1 = upperDifference.getEntry(i) - trustRegionCenterOffset.getEntry(i);
+ temp = ssq - d1 * d1;
+ if (temp > ZERO) {
+ temp = FastMath.sqrt(temp) + s.getEntry(i);
+ if (angbd * temp > tempb) {
+ angbd = tempb / temp;
+ iact = i;
+ xsav = ONE;
+ }
+ }
+ }
+ }
+
+ // Calculate HHD and some curvatures for the alternative iteration.
+
+ state = 210; break;
+ }
+ case 150: {
+ printState(150); // XXX
+ shs = ZERO;
+ dhs = ZERO;
+ dhd = ZERO;
+ for (int i = 0; i < n; i++) {
+ if (xbdi.getEntry(i) == ZERO) {
+ shs += s.getEntry(i) * hs.getEntry(i);
+ dhs += trialStepPoint.getEntry(i) * hs.getEntry(i);
+ dhd += trialStepPoint.getEntry(i) * hred.getEntry(i);
+ }
+ }
+
+ // Seek the greatest reduction in Q for a range of equally spaced values
+ // of ANGT in [0,ANGBD], where ANGT is the tangent of half the angle of
+ // the alternative iteration.
+
+ redmax = ZERO;
+ isav = -1;
+ redsav = ZERO;
+ iu = (int) (angbd * 17. + 3.1);
+ for (int i = 0; i < iu; i++) {
+ angt = angbd * i / iu;
+ sth = (angt + angt) / (ONE + angt * angt);
+ temp = shs + angt * (angt * dhd - dhs - dhs);
+ rednew = sth * (angt * dredg - sredg - HALF * sth * temp);
+ if (rednew > redmax) {
+ redmax = rednew;
+ isav = i;
+ rdprev = redsav;
+ } else if (i == isav + 1) {
+ rdnext = rednew;
+ }
+ redsav = rednew;
+ }
+
+ // Return if the reduction is zero. Otherwise, set the sine and cosine
+ // of the angle of the alternative iteration, and calculate SDEC.
+
+ if (isav < 0) {
+ state = 190; break;
+ }
+ if (isav < iu) {
+ temp = (rdnext - rdprev) / (redmax + redmax - rdprev - rdnext);
+ angt = angbd * (isav + HALF * temp) / iu;
+ }
+ cth = (ONE - angt * angt) / (ONE + angt * angt);
+ sth = (angt + angt) / (ONE + angt * angt);
+ temp = shs + angt * (angt * dhd - dhs - dhs);
+ sdec = sth * (angt * dredg - sredg - HALF * sth * temp);
+ if (sdec <= ZERO) {
+ state = 190; break;
+ }
+
+ // Update GNEW, D and HRED. If the angle of the alternative iteration
+ // is restricted by a bound on a free variable, that variable is fixed
+ // at the bound.
+
+ dredg = ZERO;
+ gredsq = ZERO;
+ for (int i = 0; i < n; i++) {
+ gnew.setEntry(i, gnew.getEntry(i) + (cth - ONE) * hred.getEntry(i) + sth * hs.getEntry(i));
+ if (xbdi.getEntry(i) == ZERO) {
+ trialStepPoint.setEntry(i, cth * trialStepPoint.getEntry(i) + sth * s.getEntry(i));
+ dredg += trialStepPoint.getEntry(i) * gnew.getEntry(i);
+ // Computing 2nd power
+ final double d1 = gnew.getEntry(i);
+ gredsq += d1 * d1;
+ }
+ hred.setEntry(i, cth * hred.getEntry(i) + sth * hs.getEntry(i));
+ }
+ qred += sdec;
+ if (iact >= 0 && isav == iu) {
+ ++nact;
+ xbdi.setEntry(iact, xsav);
+ state = 100; break;
+ }
+
+ // If SDEC is sufficiently small, then RETURN after setting XNEW to
+ // XOPT+D, giving careful attention to the bounds.
+
+ if (sdec > qred * .01) {
+ state = 120; break;
+ }
+ }
+ case 190: {
+ printState(190); // XXX
+ dsq = ZERO;
+ for (int i = 0; i < n; i++) {
+ // Computing MAX
+ // Computing MIN
+ final double min = FastMath.min(trustRegionCenterOffset.getEntry(i) + trialStepPoint.getEntry(i),
+ upperDifference.getEntry(i));
+ newPoint.setEntry(i, FastMath.max(min, lowerDifference.getEntry(i)));
+ if (xbdi.getEntry(i) == MINUS_ONE) {
+ newPoint.setEntry(i, lowerDifference.getEntry(i));
+ }
+ if (xbdi.getEntry(i) == ONE) {
+ newPoint.setEntry(i, upperDifference.getEntry(i));
+ }
+ trialStepPoint.setEntry(i, newPoint.getEntry(i) - trustRegionCenterOffset.getEntry(i));
+ // Computing 2nd power
+ final double d1 = trialStepPoint.getEntry(i);
+ dsq += d1 * d1;
+ }
+ return new double[] { dsq, crvmin };
+ // The following instructions multiply the current S-vector by the second
+ // derivative matrix of the quadratic model, putting the product in HS.
+ // They are reached from three different parts of the software above and
+ // they can be regarded as an external subroutine.
+ }
+ case 210: {
+ printState(210); // XXX
+ int ih = 0;
+ for (int j = 0; j < n; j++) {
+ hs.setEntry(j, ZERO);
+ for (int i = 0; i <= j; i++) {
+ if (i < j) {
+ hs.setEntry(j, hs.getEntry(j) + modelSecondDerivativesValues.getEntry(ih) * s.getEntry(i));
+ }
+ hs.setEntry(i, hs.getEntry(i) + modelSecondDerivativesValues.getEntry(ih) * s.getEntry(j));
+ ih++;
+ }
+ }
+ final RealVector tmp = interpolationPoints.operate(s).ebeMultiply(modelSecondDerivativesParameters);
+ for (int k = 0; k < npt; k++) {
+ if (modelSecondDerivativesParameters.getEntry(k) != ZERO) {
+ for (int i = 0; i < n; i++) {
+ hs.setEntry(i, hs.getEntry(i) + tmp.getEntry(k) * interpolationPoints.getEntry(k, i));
+ }
+ }
+ }
+ if (crvmin != ZERO) {
+ state = 50; break;
+ }
+ if (iterc > itcsav) {
+ state = 150; break;
+ }
+ for (int i = 0; i < n; i++) {
+ hred.setEntry(i, hs.getEntry(i));
+ }
+ state = 120; break;
+ }
+ default: {
+ throw new MathIllegalStateException(LocalizedFormats.SIMPLE_MESSAGE, "trsbox");
+ }}
+ }
+ } // trsbox
+
+ // ----------------------------------------------------------------------------------------
+
+ /**
+ * The arrays BMAT and ZMAT are updated, as required by the new position
+ * of the interpolation point that has the index KNEW. The vector VLAG has
+ * N+NPT components, set on entry to the first NPT and last N components
+ * of the product Hw in equation (4.11) of the Powell (2006) paper on
+ * NEWUOA. Further, BETA is set on entry to the value of the parameter
+ * with that name, and DENOM is set to the denominator of the updating
+ * formula. Elements of ZMAT may be treated as zero if their moduli are
+ * at most ZTEST. The first NDIM elements of W are used for working space.
+ * @param beta
+ * @param denom
+ * @param knew
+ */
+ private void update(
+ double beta,
+ double denom,
+ int knew
+ ) {
+ printMethod(); // XXX
+
+ final int n = currentBest.getDimension();
+ final int npt = numberOfInterpolationPoints;
+ final int nptm = npt - n - 1;
+
+ // XXX Should probably be split into two arrays.
+ final ArrayRealVector work = new ArrayRealVector(npt + n);
+
+ double ztest = ZERO;
+ for (int k = 0; k < npt; k++) {
+ for (int j = 0; j < nptm; j++) {
+ // Computing MAX
+ ztest = FastMath.max(ztest, FastMath.abs(zMatrix.getEntry(k, j)));
+ }
+ }
+ ztest *= 1e-20;
+
+ // Apply the rotations that put zeros in the KNEW-th row of ZMAT.
+
+ for (int j = 1; j < nptm; j++) {
+ final double d1 = zMatrix.getEntry(knew, j);
+ if (FastMath.abs(d1) > ztest) {
+ // Computing 2nd power
+ final double d2 = zMatrix.getEntry(knew, 0);
+ // Computing 2nd power
+ final double d3 = zMatrix.getEntry(knew, j);
+ final double d4 = FastMath.sqrt(d2 * d2 + d3 * d3);
+ final double d5 = zMatrix.getEntry(knew, 0) / d4;
+ final double d6 = zMatrix.getEntry(knew, j) / d4;
+ for (int i = 0; i < npt; i++) {
+ final double d7 = d5 * zMatrix.getEntry(i, 0) + d6 * zMatrix.getEntry(i, j);
+ zMatrix.setEntry(i, j, d5 * zMatrix.getEntry(i, j) - d6 * zMatrix.getEntry(i, 0));
+ zMatrix.setEntry(i, 0, d7);
+ }
+ }
+ zMatrix.setEntry(knew, j, ZERO);
+ }
+
+ // Put the first NPT components of the KNEW-th column of HLAG into W,
+ // and calculate the parameters of the updating formula.
+
+ for (int i = 0; i < npt; i++) {
+ work.setEntry(i, zMatrix.getEntry(knew, 0) * zMatrix.getEntry(i, 0));
+ }
+ final double alpha = work.getEntry(knew);
+ final double tau = lagrangeValuesAtNewPoint.getEntry(knew);
+ lagrangeValuesAtNewPoint.setEntry(knew, lagrangeValuesAtNewPoint.getEntry(knew) - ONE);
+
+ // Complete the updating of ZMAT.
+
+ final double sqrtDenom = FastMath.sqrt(denom);
+ final double d1 = tau / sqrtDenom;
+ final double d2 = zMatrix.getEntry(knew, 0) / sqrtDenom;
+ for (int i = 0; i < npt; i++) {
+ zMatrix.setEntry(i, 0,
+ d1 * zMatrix.getEntry(i, 0) - d2 * lagrangeValuesAtNewPoint.getEntry(i));
+ }
+
+ // Finally, update the matrix BMAT.
+
+ for (int j = 0; j < n; j++) {
+ final int jp = npt + j;
+ work.setEntry(jp, bMatrix.getEntry(knew, j));
+ final double d3 = (alpha * lagrangeValuesAtNewPoint.getEntry(jp) - tau * work.getEntry(jp)) / denom;
+ final double d4 = (-beta * work.getEntry(jp) - tau * lagrangeValuesAtNewPoint.getEntry(jp)) / denom;
+ for (int i = 0; i <= jp; i++) {
+ bMatrix.setEntry(i, j,
+ bMatrix.getEntry(i, j) + d3 * lagrangeValuesAtNewPoint.getEntry(i) + d4 * work.getEntry(i));
+ if (i >= npt) {
+ bMatrix.setEntry(jp, (i - npt), bMatrix.getEntry(i, j));
+ }
+ }
+ }
+ } // update
+
+ /**
+ * Performs validity checks.
+ *
+ * @param lowerBound Lower bounds (constraints) of the objective variables.
+ * @param upperBound Upperer bounds (constraints) of the objective variables.
+ */
+ private void setup(double[] lowerBound,
+ double[] upperBound) {
+ printMethod(); // XXX
+
+ double[] init = getStartPoint();
+ final int dimension = init.length;
+
+ // Check problem dimension.
+ if (dimension < MINIMUM_PROBLEM_DIMENSION) {
+ throw new NumberIsTooSmallException(dimension, MINIMUM_PROBLEM_DIMENSION, true);
+ }
+ // Check number of interpolation points.
+ final int[] nPointsInterval = { dimension + 2, (dimension + 2) * (dimension + 1) / 2 };
+ if (numberOfInterpolationPoints < nPointsInterval[0] ||
+ numberOfInterpolationPoints > nPointsInterval[1]) {
+ throw new OutOfRangeException(LocalizedFormats.NUMBER_OF_INTERPOLATION_POINTS,
+ numberOfInterpolationPoints,
+ nPointsInterval[0],
+ nPointsInterval[1]);
+ }
+
+ // Initialize bound differences.
+ boundDifference = new double[dimension];
+
+ double requiredMinDiff = 2 * initialTrustRegionRadius;
+ double minDiff = Double.POSITIVE_INFINITY;
+ for (int i = 0; i < dimension; i++) {
+ boundDifference[i] = upperBound[i] - lowerBound[i];
+ minDiff = FastMath.min(minDiff, boundDifference[i]);
+ }
+ if (minDiff < requiredMinDiff) {
+ initialTrustRegionRadius = minDiff / 3.0;
+ }
+
+ // Initialize the data structures used by the "bobyqa" method.
+ bMatrix = new Array2DRowRealMatrix(dimension + numberOfInterpolationPoints,
+ dimension);
+ zMatrix = new Array2DRowRealMatrix(numberOfInterpolationPoints,
+ numberOfInterpolationPoints - dimension - 1);
+ interpolationPoints = new Array2DRowRealMatrix(numberOfInterpolationPoints,
+ dimension);
+ originShift = new ArrayRealVector(dimension);
+ fAtInterpolationPoints = new ArrayRealVector(numberOfInterpolationPoints);
+ trustRegionCenterOffset = new ArrayRealVector(dimension);
+ gradientAtTrustRegionCenter = new ArrayRealVector(dimension);
+ lowerDifference = new ArrayRealVector(dimension);
+ upperDifference = new ArrayRealVector(dimension);
+ modelSecondDerivativesParameters = new ArrayRealVector(numberOfInterpolationPoints);
+ newPoint = new ArrayRealVector(dimension);
+ alternativeNewPoint = new ArrayRealVector(dimension);
+ trialStepPoint = new ArrayRealVector(dimension);
+ lagrangeValuesAtNewPoint = new ArrayRealVector(dimension + numberOfInterpolationPoints);
+ modelSecondDerivativesValues = new ArrayRealVector(dimension * (dimension + 1) / 2);
+ }
+
+ // XXX utility for figuring out call sequence.
+ private static String caller(int n) {
+ final Throwable t = new Throwable();
+ final StackTraceElement[] elements = t.getStackTrace();
+ final StackTraceElement e = elements[n];
+ return e.getMethodName() + " (at line " + e.getLineNumber() + ")";
+ }
+ // XXX utility for figuring out call sequence.
+ private static void printState(int s) {
+ // System.out.println(caller(2) + ": state " + s);
+ }
+ // XXX utility for figuring out call sequence.
+ private static void printMethod() {
+ // System.out.println(caller(2));
+ }
+
+ /**
+ * Marker for code paths that are not explored with the current unit tests.
+ * If the path becomes explored, it should just be removed from the code.
+ */
+ private static class PathIsExploredException extends RuntimeException {
+ /** Serializable UID. */
+ private static final long serialVersionUID = 745350979634801853L;
+
+ /** Message string. */
+ private static final String PATH_IS_EXPLORED
+ = "If this exception is thrown, just remove it from the code";
+
+ PathIsExploredException() {
+ super(PATH_IS_EXPLORED + " " + BOBYQAOptimizer.caller(3));
+ }
+ }
+}
+//CHECKSTYLE: resume all
diff --git a/src/main/java/org/apache/commons/math3/optim/nonlinear/scalar/noderiv/CMAESOptimizer.java b/src/main/java/org/apache/commons/math3/optim/nonlinear/scalar/noderiv/CMAESOptimizer.java
new file mode 100644
index 0000000..13566be
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/optim/nonlinear/scalar/noderiv/CMAESOptimizer.java
@@ -0,0 +1,1354 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math3.optim.nonlinear.scalar.noderiv;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+import org.apache.commons.math3.exception.DimensionMismatchException;
+import org.apache.commons.math3.exception.NotPositiveException;
+import org.apache.commons.math3.exception.NotStrictlyPositiveException;
+import org.apache.commons.math3.exception.OutOfRangeException;
+import org.apache.commons.math3.exception.TooManyEvaluationsException;
+import org.apache.commons.math3.linear.Array2DRowRealMatrix;
+import org.apache.commons.math3.linear.EigenDecomposition;
+import org.apache.commons.math3.linear.MatrixUtils;
+import org.apache.commons.math3.linear.RealMatrix;
+import org.apache.commons.math3.optim.ConvergenceChecker;
+import org.apache.commons.math3.optim.OptimizationData;
+import org.apache.commons.math3.optim.nonlinear.scalar.GoalType;
+import org.apache.commons.math3.optim.PointValuePair;
+import org.apache.commons.math3.optim.nonlinear.scalar.MultivariateOptimizer;
+import org.apache.commons.math3.random.RandomGenerator;
+import org.apache.commons.math3.util.FastMath;
+import org.apache.commons.math3.util.MathArrays;
+
+/**
+ * An implementation of the active Covariance Matrix Adaptation Evolution Strategy (CMA-ES)
+ * for non-linear, non-convex, non-smooth, global function minimization.
+ * <p>
+ * The CMA-Evolution Strategy (CMA-ES) is a reliable stochastic optimization method
+ * which should be applied if derivative-based methods, e.g. quasi-Newton BFGS or
+ * conjugate gradient, fail due to a rugged search landscape (e.g. noise, local
+ * optima, outlier, etc.) of the objective function. Like a
+ * quasi-Newton method, the CMA-ES learns and applies a variable metric
+ * on the underlying search space. Unlike a quasi-Newton method, the
+ * CMA-ES neither estimates nor uses gradients, making it considerably more
+ * reliable in terms of finding a good, or even close to optimal, solution.
+ * <p>
+ * In general, on smooth objective functions the CMA-ES is roughly ten times
+ * slower than BFGS (counting objective function evaluations, no gradients provided).
+ * For up to <math>N=10</math> variables also the derivative-free simplex
+ * direct search method (Nelder and Mead) can be faster, but it is
+ * far less reliable than CMA-ES.
+ * <p>
+ * The CMA-ES is particularly well suited for non-separable
+ * and/or badly conditioned problems. To observe the advantage of CMA compared
+ * to a conventional evolution strategy, it will usually take about
+ * <math>30 N</math> function evaluations. On difficult problems the complete
+ * optimization (a single run) is expected to take <em>roughly</em> between
+ * <math>30 N</math> and <math>300 N<sup>2</sup></math>
+ * function evaluations.
+ * <p>
+ * This implementation is translated and adapted from the Matlab version
+ * of the CMA-ES algorithm as implemented in module {@code cmaes.m} version 3.51.
+ * <p>
+ * For more information, please refer to the following links:
+ * <ul>
+ * <li><a href="http://www.lri.fr/~hansen/cmaes.m">Matlab code</a></li>
+ * <li><a href="http://www.lri.fr/~hansen/cmaesintro.html">Introduction to CMA-ES</a></li>
+ * <li><a href="http://en.wikipedia.org/wiki/CMA-ES">Wikipedia</a></li>
+ * </ul>
+ *
+ * @since 3.0
+ */
+public class CMAESOptimizer
+ extends MultivariateOptimizer {
+ // global search parameters
+ /**
+ * Population size, offspring number. The primary strategy parameter to play
+ * with, which can be increased from its default value. Increasing the
+ * population size improves global search properties in exchange to speed.
+ * Speed decreases, as a rule, at most linearly with increasing population
+ * size. It is advisable to begin with the default small population size.
+ */
+ private int lambda; // population size
+ /**
+ * Covariance update mechanism, default is active CMA. isActiveCMA = true
+ * turns on "active CMA" with a negative update of the covariance matrix and
+ * checks for positive definiteness. OPTS.CMA.active = 2 does not check for
+ * pos. def. and is numerically faster. Active CMA usually speeds up the
+ * adaptation.
+ */
+ private final boolean isActiveCMA;
+ /**
+ * Determines how often a new random offspring is generated in case it is
+ * not feasible / beyond the defined limits, default is 0.
+ */
+ private final int checkFeasableCount;
+ /**
+ * @see Sigma
+ */
+ private double[] inputSigma;
+ /** Number of objective variables/problem dimension */
+ private int dimension;
+ /**
+ * Defines the number of initial iterations, where the covariance matrix
+ * remains diagonal and the algorithm has internally linear time complexity.
+ * diagonalOnly = 1 means keeping the covariance matrix always diagonal and
+ * this setting also exhibits linear space complexity. This can be
+ * particularly useful for dimension > 100.
+ * @see <a href="http://hal.archives-ouvertes.fr/inria-00287367/en">A Simple Modification in CMA-ES</a>
+ */
+ private int diagonalOnly;
+ /** Number of objective variables/problem dimension */
+ private boolean isMinimize = true;
+ /** Indicates whether statistic data is collected. */
+ private final boolean generateStatistics;
+
+ // termination criteria
+ /** Maximal number of iterations allowed. */
+ private final int maxIterations;
+ /** Limit for fitness value. */
+ private final double stopFitness;
+ /** Stop if x-changes larger stopTolUpX. */
+ private double stopTolUpX;
+ /** Stop if x-change smaller stopTolX. */
+ private double stopTolX;
+ /** Stop if fun-changes smaller stopTolFun. */
+ private double stopTolFun;
+ /** Stop if back fun-changes smaller stopTolHistFun. */
+ private double stopTolHistFun;
+
+ // selection strategy parameters
+ /** Number of parents/points for recombination. */
+ private int mu; //
+ /** log(mu + 0.5), stored for efficiency. */
+ private double logMu2;
+ /** Array for weighted recombination. */
+ private RealMatrix weights;
+ /** Variance-effectiveness of sum w_i x_i. */
+ private double mueff; //
+
+ // dynamic strategy parameters and constants
+ /** Overall standard deviation - search volume. */
+ private double sigma;
+ /** Cumulation constant. */
+ private double cc;
+ /** Cumulation constant for step-size. */
+ private double cs;
+ /** Damping for step-size. */
+ private double damps;
+ /** Learning rate for rank-one update. */
+ private double ccov1;
+ /** Learning rate for rank-mu update' */
+ private double ccovmu;
+ /** Expectation of ||N(0,I)|| == norm(randn(N,1)). */
+ private double chiN;
+ /** Learning rate for rank-one update - diagonalOnly */
+ private double ccov1Sep;
+ /** Learning rate for rank-mu update - diagonalOnly */
+ private double ccovmuSep;
+
+ // CMA internal values - updated each generation
+ /** Objective variables. */
+ private RealMatrix xmean;
+ /** Evolution path. */
+ private RealMatrix pc;
+ /** Evolution path for sigma. */
+ private RealMatrix ps;
+ /** Norm of ps, stored for efficiency. */
+ private double normps;
+ /** Coordinate system. */
+ private RealMatrix B;
+ /** Scaling. */
+ private RealMatrix D;
+ /** B*D, stored for efficiency. */
+ private RealMatrix BD;
+ /** Diagonal of sqrt(D), stored for efficiency. */
+ private RealMatrix diagD;
+ /** Covariance matrix. */
+ private RealMatrix C;
+ /** Diagonal of C, used for diagonalOnly. */
+ private RealMatrix diagC;
+ /** Number of iterations already performed. */
+ private int iterations;
+
+ /** History queue of best values. */
+ private double[] fitnessHistory;
+ /** Size of history queue of best values. */
+ private int historySize;
+
+ /** Random generator. */
+ private final RandomGenerator random;
+
+ /** History of sigma values. */
+ private final List<Double> statisticsSigmaHistory = new ArrayList<Double>();
+ /** History of mean matrix. */
+ private final List<RealMatrix> statisticsMeanHistory = new ArrayList<RealMatrix>();
+ /** History of fitness values. */
+ private final List<Double> statisticsFitnessHistory = new ArrayList<Double>();
+ /** History of D matrix. */
+ private final List<RealMatrix> statisticsDHistory = new ArrayList<RealMatrix>();
+
+ /**
+ * @param maxIterations Maximal number of iterations.
+ * @param stopFitness Whether to stop if objective function value is smaller than
+ * {@code stopFitness}.
+ * @param isActiveCMA Chooses the covariance matrix update method.
+ * @param diagonalOnly Number of initial iterations, where the covariance matrix
+ * remains diagonal.
+ * @param checkFeasableCount Determines how often new random objective variables are
+ * generated in case they are out of bounds.
+ * @param random Random generator.
+ * @param generateStatistics Whether statistic data is collected.
+ * @param checker Convergence checker.
+ *
+ * @since 3.1
+ */
+ public CMAESOptimizer(int maxIterations,
+ double stopFitness,
+ boolean isActiveCMA,
+ int diagonalOnly,
+ int checkFeasableCount,
+ RandomGenerator random,
+ boolean generateStatistics,
+ ConvergenceChecker<PointValuePair> checker) {
+ super(checker);
+ this.maxIterations = maxIterations;
+ this.stopFitness = stopFitness;
+ this.isActiveCMA = isActiveCMA;
+ this.diagonalOnly = diagonalOnly;
+ this.checkFeasableCount = checkFeasableCount;
+ this.random = random;
+ this.generateStatistics = generateStatistics;
+ }
+
+ /**
+ * @return History of sigma values.
+ */
+ public List<Double> getStatisticsSigmaHistory() {
+ return statisticsSigmaHistory;
+ }
+
+ /**
+ * @return History of mean matrix.
+ */
+ public List<RealMatrix> getStatisticsMeanHistory() {
+ return statisticsMeanHistory;
+ }
+
+ /**
+ * @return History of fitness values.
+ */
+ public List<Double> getStatisticsFitnessHistory() {
+ return statisticsFitnessHistory;
+ }
+
+ /**
+ * @return History of D matrix.
+ */
+ public List<RealMatrix> getStatisticsDHistory() {
+ return statisticsDHistory;
+ }
+
+ /**
+ * Input sigma values.
+ * They define the initial coordinate-wise standard deviations for
+ * sampling new search points around the initial guess.
+ * It is suggested to set them to the estimated distance from the
+ * initial to the desired optimum.
+ * Small values induce the search to be more local (and very small
+ * values are more likely to find a local optimum close to the initial
+ * guess).
+ * Too small values might however lead to early termination.
+ */
+ public static class Sigma implements OptimizationData {
+ /** Sigma values. */
+ private final double[] sigma;
+
+ /**
+ * @param s Sigma values.
+ * @throws NotPositiveException if any of the array entries is smaller
+ * than zero.
+ */
+ public Sigma(double[] s)
+ throws NotPositiveException {
+ for (int i = 0; i < s.length; i++) {
+ if (s[i] < 0) {
+ throw new NotPositiveException(s[i]);
+ }
+ }
+
+ sigma = s.clone();
+ }
+
+ /**
+ * @return the sigma values.
+ */
+ public double[] getSigma() {
+ return sigma.clone();
+ }
+ }
+
+ /**
+ * Population size.
+ * The number of offspring is the primary strategy parameter.
+ * In the absence of better clues, a good default could be an
+ * integer close to {@code 4 + 3 ln(n)}, where {@code n} is the
+ * number of optimized parameters.
+ * Increasing the population size improves global search properties
+ * at the expense of speed (which in general decreases at most
+ * linearly with increasing population size).
+ */
+ public static class PopulationSize implements OptimizationData {
+ /** Population size. */
+ private final int lambda;
+
+ /**
+ * @param size Population size.
+ * @throws NotStrictlyPositiveException if {@code size <= 0}.
+ */
+ public PopulationSize(int size)
+ throws NotStrictlyPositiveException {
+ if (size <= 0) {
+ throw new NotStrictlyPositiveException(size);
+ }
+ lambda = size;
+ }
+
+ /**
+ * @return the population size.
+ */
+ public int getPopulationSize() {
+ return lambda;
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @param optData Optimization data. In addition to those documented in
+ * {@link MultivariateOptimizer#parseOptimizationData(OptimizationData[])
+ * MultivariateOptimizer}, this method will register the following data:
+ * <ul>
+ * <li>{@link Sigma}</li>
+ * <li>{@link PopulationSize}</li>
+ * </ul>
+ * @return {@inheritDoc}
+ * @throws TooManyEvaluationsException if the maximal number of
+ * evaluations is exceeded.
+ * @throws DimensionMismatchException if the initial guess, target, and weight
+ * arguments have inconsistent dimensions.
+ */
+ @Override
+ public PointValuePair optimize(OptimizationData... optData)
+ throws TooManyEvaluationsException,
+ DimensionMismatchException {
+ // Set up base class and perform computation.
+ return super.optimize(optData);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ protected PointValuePair doOptimize() {
+ // -------------------- Initialization --------------------------------
+ isMinimize = getGoalType().equals(GoalType.MINIMIZE);
+ final FitnessFunction fitfun = new FitnessFunction();
+ final double[] guess = getStartPoint();
+ // number of objective variables/problem dimension
+ dimension = guess.length;
+ initializeCMA(guess);
+ iterations = 0;
+ ValuePenaltyPair valuePenalty = fitfun.value(guess);
+ double bestValue = valuePenalty.value+valuePenalty.penalty;
+ push(fitnessHistory, bestValue);
+ PointValuePair optimum
+ = new PointValuePair(getStartPoint(),
+ isMinimize ? bestValue : -bestValue);
+ PointValuePair lastResult = null;
+
+ // -------------------- Generation Loop --------------------------------
+
+ generationLoop:
+ for (iterations = 1; iterations <= maxIterations; iterations++) {
+ incrementIterationCount();
+
+ // Generate and evaluate lambda offspring
+ final RealMatrix arz = randn1(dimension, lambda);
+ final RealMatrix arx = zeros(dimension, lambda);
+ final double[] fitness = new double[lambda];
+ final ValuePenaltyPair[] valuePenaltyPairs = new ValuePenaltyPair[lambda];
+ // generate random offspring
+ for (int k = 0; k < lambda; k++) {
+ RealMatrix arxk = null;
+ for (int i = 0; i < checkFeasableCount + 1; i++) {
+ if (diagonalOnly <= 0) {
+ arxk = xmean.add(BD.multiply(arz.getColumnMatrix(k))
+ .scalarMultiply(sigma)); // m + sig * Normal(0,C)
+ } else {
+ arxk = xmean.add(times(diagD,arz.getColumnMatrix(k))
+ .scalarMultiply(sigma));
+ }
+ if (i >= checkFeasableCount ||
+ fitfun.isFeasible(arxk.getColumn(0))) {
+ break;
+ }
+ // regenerate random arguments for row
+ arz.setColumn(k, randn(dimension));
+ }
+ copyColumn(arxk, 0, arx, k);
+ try {
+ valuePenaltyPairs[k] = fitfun.value(arx.getColumn(k)); // compute fitness
+ } catch (TooManyEvaluationsException e) {
+ break generationLoop;
+ }
+ }
+
+ // Compute fitnesses by adding value and penalty after scaling by value range.
+ double valueRange = valueRange(valuePenaltyPairs);
+ for (int iValue=0;iValue<valuePenaltyPairs.length;iValue++) {
+ fitness[iValue] = valuePenaltyPairs[iValue].value + valuePenaltyPairs[iValue].penalty*valueRange;
+ }
+
+ // Sort by fitness and compute weighted mean into xmean
+ final int[] arindex = sortedIndices(fitness);
+ // Calculate new xmean, this is selection and recombination
+ final RealMatrix xold = xmean; // for speed up of Eq. (2) and (3)
+ final RealMatrix bestArx = selectColumns(arx, MathArrays.copyOf(arindex, mu));
+ xmean = bestArx.multiply(weights);
+ final RealMatrix bestArz = selectColumns(arz, MathArrays.copyOf(arindex, mu));
+ final RealMatrix zmean = bestArz.multiply(weights);
+ final boolean hsig = updateEvolutionPaths(zmean, xold);
+ if (diagonalOnly <= 0) {
+ updateCovariance(hsig, bestArx, arz, arindex, xold);
+ } else {
+ updateCovarianceDiagonalOnly(hsig, bestArz);
+ }
+ // Adapt step size sigma - Eq. (5)
+ sigma *= FastMath.exp(FastMath.min(1, (normps/chiN - 1) * cs / damps));
+ final double bestFitness = fitness[arindex[0]];
+ final double worstFitness = fitness[arindex[arindex.length - 1]];
+ if (bestValue > bestFitness) {
+ bestValue = bestFitness;
+ lastResult = optimum;
+ optimum = new PointValuePair(fitfun.repair(bestArx.getColumn(0)),
+ isMinimize ? bestFitness : -bestFitness);
+ if (getConvergenceChecker() != null && lastResult != null &&
+ getConvergenceChecker().converged(iterations, optimum, lastResult)) {
+ break generationLoop;
+ }
+ }
+ // handle termination criteria
+ // Break, if fitness is good enough
+ if (stopFitness != 0 && bestFitness < (isMinimize ? stopFitness : -stopFitness)) {
+ break generationLoop;
+ }
+ final double[] sqrtDiagC = sqrt(diagC).getColumn(0);
+ final double[] pcCol = pc.getColumn(0);
+ for (int i = 0; i < dimension; i++) {
+ if (sigma * FastMath.max(FastMath.abs(pcCol[i]), sqrtDiagC[i]) > stopTolX) {
+ break;
+ }
+ if (i >= dimension - 1) {
+ break generationLoop;
+ }
+ }
+ for (int i = 0; i < dimension; i++) {
+ if (sigma * sqrtDiagC[i] > stopTolUpX) {
+ break generationLoop;
+ }
+ }
+ final double historyBest = min(fitnessHistory);
+ final double historyWorst = max(fitnessHistory);
+ if (iterations > 2 &&
+ FastMath.max(historyWorst, worstFitness) -
+ FastMath.min(historyBest, bestFitness) < stopTolFun) {
+ break generationLoop;
+ }
+ if (iterations > fitnessHistory.length &&
+ historyWorst - historyBest < stopTolHistFun) {
+ break generationLoop;
+ }
+ // condition number of the covariance matrix exceeds 1e14
+ if (max(diagD) / min(diagD) > 1e7) {
+ break generationLoop;
+ }
+ // user defined termination
+ if (getConvergenceChecker() != null) {
+ final PointValuePair current
+ = new PointValuePair(bestArx.getColumn(0),
+ isMinimize ? bestFitness : -bestFitness);
+ if (lastResult != null &&
+ getConvergenceChecker().converged(iterations, current, lastResult)) {
+ break generationLoop;
+ }
+ lastResult = current;
+ }
+ // Adjust step size in case of equal function values (flat fitness)
+ if (bestValue == fitness[arindex[(int)(0.1+lambda/4.)]]) {
+ sigma *= FastMath.exp(0.2 + cs / damps);
+ }
+ if (iterations > 2 && FastMath.max(historyWorst, bestFitness) -
+ FastMath.min(historyBest, bestFitness) == 0) {
+ sigma *= FastMath.exp(0.2 + cs / damps);
+ }
+ // store best in history
+ push(fitnessHistory,bestFitness);
+ if (generateStatistics) {
+ statisticsSigmaHistory.add(sigma);
+ statisticsFitnessHistory.add(bestFitness);
+ statisticsMeanHistory.add(xmean.transpose());
+ statisticsDHistory.add(diagD.transpose().scalarMultiply(1E5));
+ }
+ }
+ return optimum;
+ }
+
+ /**
+ * Scans the list of (required and optional) optimization data that
+ * characterize the problem.
+ *
+ * @param optData Optimization data. The following data will be looked for:
+ * <ul>
+ * <li>{@link Sigma}</li>
+ * <li>{@link PopulationSize}</li>
+ * </ul>
+ */
+ @Override
+ protected void parseOptimizationData(OptimizationData... optData) {
+ // Allow base class to register its own data.
+ super.parseOptimizationData(optData);
+
+ // The existing values (as set by the previous call) are reused if
+ // not provided in the argument list.
+ for (OptimizationData data : optData) {
+ if (data instanceof Sigma) {
+ inputSigma = ((Sigma) data).getSigma();
+ continue;
+ }
+ if (data instanceof PopulationSize) {
+ lambda = ((PopulationSize) data).getPopulationSize();
+ continue;
+ }
+ }
+
+ checkParameters();
+ }
+
+ /**
+ * Checks dimensions and values of boundaries and inputSigma if defined.
+ */
+ private void checkParameters() {
+ final double[] init = getStartPoint();
+ final double[] lB = getLowerBound();
+ final double[] uB = getUpperBound();
+
+ if (inputSigma != null) {
+ if (inputSigma.length != init.length) {
+ throw new DimensionMismatchException(inputSigma.length, init.length);
+ }
+ for (int i = 0; i < init.length; i++) {
+ if (inputSigma[i] > uB[i] - lB[i]) {
+ throw new OutOfRangeException(inputSigma[i], 0, uB[i] - lB[i]);
+ }
+ }
+ }
+ }
+
+ /**
+ * Initialization of the dynamic search parameters
+ *
+ * @param guess Initial guess for the arguments of the fitness function.
+ */
+ private void initializeCMA(double[] guess) {
+ if (lambda <= 0) {
+ throw new NotStrictlyPositiveException(lambda);
+ }
+ // initialize sigma
+ final double[][] sigmaArray = new double[guess.length][1];
+ for (int i = 0; i < guess.length; i++) {
+ sigmaArray[i][0] = inputSigma[i];
+ }
+ final RealMatrix insigma = new Array2DRowRealMatrix(sigmaArray, false);
+ sigma = max(insigma); // overall standard deviation
+
+ // initialize termination criteria
+ stopTolUpX = 1e3 * max(insigma);
+ stopTolX = 1e-11 * max(insigma);
+ stopTolFun = 1e-12;
+ stopTolHistFun = 1e-13;
+
+ // initialize selection strategy parameters
+ mu = lambda / 2; // number of parents/points for recombination
+ logMu2 = FastMath.log(mu + 0.5);
+ weights = log(sequence(1, mu, 1)).scalarMultiply(-1).scalarAdd(logMu2);
+ double sumw = 0;
+ double sumwq = 0;
+ for (int i = 0; i < mu; i++) {
+ double w = weights.getEntry(i, 0);
+ sumw += w;
+ sumwq += w * w;
+ }
+ weights = weights.scalarMultiply(1 / sumw);
+ mueff = sumw * sumw / sumwq; // variance-effectiveness of sum w_i x_i
+
+ // initialize dynamic strategy parameters and constants
+ cc = (4 + mueff / dimension) /
+ (dimension + 4 + 2 * mueff / dimension);
+ cs = (mueff + 2) / (dimension + mueff + 3.);
+ damps = (1 + 2 * FastMath.max(0, FastMath.sqrt((mueff - 1) /
+ (dimension + 1)) - 1)) *
+ FastMath.max(0.3,
+ 1 - dimension / (1e-6 + maxIterations)) + cs; // minor increment
+ ccov1 = 2 / ((dimension + 1.3) * (dimension + 1.3) + mueff);
+ ccovmu = FastMath.min(1 - ccov1, 2 * (mueff - 2 + 1 / mueff) /
+ ((dimension + 2) * (dimension + 2) + mueff));
+ ccov1Sep = FastMath.min(1, ccov1 * (dimension + 1.5) / 3);
+ ccovmuSep = FastMath.min(1 - ccov1, ccovmu * (dimension + 1.5) / 3);
+ chiN = FastMath.sqrt(dimension) *
+ (1 - 1 / ((double) 4 * dimension) + 1 / ((double) 21 * dimension * dimension));
+ // intialize CMA internal values - updated each generation
+ xmean = MatrixUtils.createColumnRealMatrix(guess); // objective variables
+ diagD = insigma.scalarMultiply(1 / sigma);
+ diagC = square(diagD);
+ pc = zeros(dimension, 1); // evolution paths for C and sigma
+ ps = zeros(dimension, 1); // B defines the coordinate system
+ normps = ps.getFrobeniusNorm();
+
+ B = eye(dimension, dimension);
+ D = ones(dimension, 1); // diagonal D defines the scaling
+ BD = times(B, repmat(diagD.transpose(), dimension, 1));
+ C = B.multiply(diag(square(D)).multiply(B.transpose())); // covariance
+ historySize = 10 + (int) (3 * 10 * dimension / (double) lambda);
+ fitnessHistory = new double[historySize]; // history of fitness values
+ for (int i = 0; i < historySize; i++) {
+ fitnessHistory[i] = Double.MAX_VALUE;
+ }
+ }
+
+ /**
+ * Update of the evolution paths ps and pc.
+ *
+ * @param zmean Weighted row matrix of the gaussian random numbers generating
+ * the current offspring.
+ * @param xold xmean matrix of the previous generation.
+ * @return hsig flag indicating a small correction.
+ */
+ private boolean updateEvolutionPaths(RealMatrix zmean, RealMatrix xold) {
+ ps = ps.scalarMultiply(1 - cs).add(
+ B.multiply(zmean).scalarMultiply(
+ FastMath.sqrt(cs * (2 - cs) * mueff)));
+ normps = ps.getFrobeniusNorm();
+ final boolean hsig = normps /
+ FastMath.sqrt(1 - FastMath.pow(1 - cs, 2 * iterations)) /
+ chiN < 1.4 + 2 / ((double) dimension + 1);
+ pc = pc.scalarMultiply(1 - cc);
+ if (hsig) {
+ pc = pc.add(xmean.subtract(xold).scalarMultiply(FastMath.sqrt(cc * (2 - cc) * mueff) / sigma));
+ }
+ return hsig;
+ }
+
+ /**
+ * Update of the covariance matrix C for diagonalOnly > 0
+ *
+ * @param hsig Flag indicating a small correction.
+ * @param bestArz Fitness-sorted matrix of the gaussian random values of the
+ * current offspring.
+ */
+ private void updateCovarianceDiagonalOnly(boolean hsig,
+ final RealMatrix bestArz) {
+ // minor correction if hsig==false
+ double oldFac = hsig ? 0 : ccov1Sep * cc * (2 - cc);
+ oldFac += 1 - ccov1Sep - ccovmuSep;
+ diagC = diagC.scalarMultiply(oldFac) // regard old matrix
+ .add(square(pc).scalarMultiply(ccov1Sep)) // plus rank one update
+ .add((times(diagC, square(bestArz).multiply(weights))) // plus rank mu update
+ .scalarMultiply(ccovmuSep));
+ diagD = sqrt(diagC); // replaces eig(C)
+ if (diagonalOnly > 1 &&
+ iterations > diagonalOnly) {
+ // full covariance matrix from now on
+ diagonalOnly = 0;
+ B = eye(dimension, dimension);
+ BD = diag(diagD);
+ C = diag(diagC);
+ }
+ }
+
+ /**
+ * Update of the covariance matrix C.
+ *
+ * @param hsig Flag indicating a small correction.
+ * @param bestArx Fitness-sorted matrix of the argument vectors producing the
+ * current offspring.
+ * @param arz Unsorted matrix containing the gaussian random values of the
+ * current offspring.
+ * @param arindex Indices indicating the fitness-order of the current offspring.
+ * @param xold xmean matrix of the previous generation.
+ */
+ private void updateCovariance(boolean hsig, final RealMatrix bestArx,
+ final RealMatrix arz, final int[] arindex,
+ final RealMatrix xold) {
+ double negccov = 0;
+ if (ccov1 + ccovmu > 0) {
+ final RealMatrix arpos = bestArx.subtract(repmat(xold, 1, mu))
+ .scalarMultiply(1 / sigma); // mu difference vectors
+ final RealMatrix roneu = pc.multiply(pc.transpose())
+ .scalarMultiply(ccov1); // rank one update
+ // minor correction if hsig==false
+ double oldFac = hsig ? 0 : ccov1 * cc * (2 - cc);
+ oldFac += 1 - ccov1 - ccovmu;
+ if (isActiveCMA) {
+ // Adapt covariance matrix C active CMA
+ negccov = (1 - ccovmu) * 0.25 * mueff /
+ (FastMath.pow(dimension + 2, 1.5) + 2 * mueff);
+ // keep at least 0.66 in all directions, small popsize are most
+ // critical
+ final double negminresidualvariance = 0.66;
+ // where to make up for the variance loss
+ final double negalphaold = 0.5;
+ // prepare vectors, compute negative updating matrix Cneg
+ final int[] arReverseIndex = reverse(arindex);
+ RealMatrix arzneg = selectColumns(arz, MathArrays.copyOf(arReverseIndex, mu));
+ RealMatrix arnorms = sqrt(sumRows(square(arzneg)));
+ final int[] idxnorms = sortedIndices(arnorms.getRow(0));
+ final RealMatrix arnormsSorted = selectColumns(arnorms, idxnorms);
+ final int[] idxReverse = reverse(idxnorms);
+ final RealMatrix arnormsReverse = selectColumns(arnorms, idxReverse);
+ arnorms = divide(arnormsReverse, arnormsSorted);
+ final int[] idxInv = inverse(idxnorms);
+ final RealMatrix arnormsInv = selectColumns(arnorms, idxInv);
+ // check and set learning rate negccov
+ final double negcovMax = (1 - negminresidualvariance) /
+ square(arnormsInv).multiply(weights).getEntry(0, 0);
+ if (negccov > negcovMax) {
+ negccov = negcovMax;
+ }
+ arzneg = times(arzneg, repmat(arnormsInv, dimension, 1));
+ final RealMatrix artmp = BD.multiply(arzneg);
+ final RealMatrix Cneg = artmp.multiply(diag(weights)).multiply(artmp.transpose());
+ oldFac += negalphaold * negccov;
+ C = C.scalarMultiply(oldFac)
+ .add(roneu) // regard old matrix
+ .add(arpos.scalarMultiply( // plus rank one update
+ ccovmu + (1 - negalphaold) * negccov) // plus rank mu update
+ .multiply(times(repmat(weights, 1, dimension),
+ arpos.transpose())))
+ .subtract(Cneg.scalarMultiply(negccov));
+ } else {
+ // Adapt covariance matrix C - nonactive
+ C = C.scalarMultiply(oldFac) // regard old matrix
+ .add(roneu) // plus rank one update
+ .add(arpos.scalarMultiply(ccovmu) // plus rank mu update
+ .multiply(times(repmat(weights, 1, dimension),
+ arpos.transpose())));
+ }
+ }
+ updateBD(negccov);
+ }
+
+ /**
+ * Update B and D from C.
+ *
+ * @param negccov Negative covariance factor.
+ */
+ private void updateBD(double negccov) {
+ if (ccov1 + ccovmu + negccov > 0 &&
+ (iterations % 1. / (ccov1 + ccovmu + negccov) / dimension / 10.) < 1) {
+ // to achieve O(N^2)
+ C = triu(C, 0).add(triu(C, 1).transpose());
+ // enforce symmetry to prevent complex numbers
+ final EigenDecomposition eig = new EigenDecomposition(C);
+ B = eig.getV(); // eigen decomposition, B==normalized eigenvectors
+ D = eig.getD();
+ diagD = diag(D);
+ if (min(diagD) <= 0) {
+ for (int i = 0; i < dimension; i++) {
+ if (diagD.getEntry(i, 0) < 0) {
+ diagD.setEntry(i, 0, 0);
+ }
+ }
+ final double tfac = max(diagD) / 1e14;
+ C = C.add(eye(dimension, dimension).scalarMultiply(tfac));
+ diagD = diagD.add(ones(dimension, 1).scalarMultiply(tfac));
+ }
+ if (max(diagD) > 1e14 * min(diagD)) {
+ final double tfac = max(diagD) / 1e14 - min(diagD);
+ C = C.add(eye(dimension, dimension).scalarMultiply(tfac));
+ diagD = diagD.add(ones(dimension, 1).scalarMultiply(tfac));
+ }
+ diagC = diag(C);
+ diagD = sqrt(diagD); // D contains standard deviations now
+ BD = times(B, repmat(diagD.transpose(), dimension, 1)); // O(n^2)
+ }
+ }
+
+ /**
+ * Pushes the current best fitness value in a history queue.
+ *
+ * @param vals History queue.
+ * @param val Current best fitness value.
+ */
+ private static void push(double[] vals, double val) {
+ for (int i = vals.length-1; i > 0; i--) {
+ vals[i] = vals[i-1];
+ }
+ vals[0] = val;
+ }
+
+ /**
+ * Sorts fitness values.
+ *
+ * @param doubles Array of values to be sorted.
+ * @return a sorted array of indices pointing into doubles.
+ */
+ private int[] sortedIndices(final double[] doubles) {
+ final DoubleIndex[] dis = new DoubleIndex[doubles.length];
+ for (int i = 0; i < doubles.length; i++) {
+ dis[i] = new DoubleIndex(doubles[i], i);
+ }
+ Arrays.sort(dis);
+ final int[] indices = new int[doubles.length];
+ for (int i = 0; i < doubles.length; i++) {
+ indices[i] = dis[i].index;
+ }
+ return indices;
+ }
+ /**
+ * Get range of values.
+ *
+ * @param vpPairs Array of valuePenaltyPairs to get range from.
+ * @return a double equal to maximum value minus minimum value.
+ */
+ private double valueRange(final ValuePenaltyPair[] vpPairs) {
+ double max = Double.NEGATIVE_INFINITY;
+ double min = Double.MAX_VALUE;
+ for (ValuePenaltyPair vpPair:vpPairs) {
+ if (vpPair.value > max) {
+ max = vpPair.value;
+ }
+ if (vpPair.value < min) {
+ min = vpPair.value;
+ }
+ }
+ return max-min;
+ }
+
+ /**
+ * Used to sort fitness values. Sorting is always in lower value first
+ * order.
+ */
+ private static class DoubleIndex implements Comparable<DoubleIndex> {
+ /** Value to compare. */
+ private final double value;
+ /** Index into sorted array. */
+ private final int index;
+
+ /**
+ * @param value Value to compare.
+ * @param index Index into sorted array.
+ */
+ DoubleIndex(double value, int index) {
+ this.value = value;
+ this.index = index;
+ }
+
+ /** {@inheritDoc} */
+ public int compareTo(DoubleIndex o) {
+ return Double.compare(value, o.value);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public boolean equals(Object other) {
+
+ if (this == other) {
+ return true;
+ }
+
+ if (other instanceof DoubleIndex) {
+ return Double.compare(value, ((DoubleIndex) other).value) == 0;
+ }
+
+ return false;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public int hashCode() {
+ long bits = Double.doubleToLongBits(value);
+ return (int) ((1438542 ^ (bits >>> 32) ^ bits) & 0xffffffff);
+ }
+ }
+ /**
+ * Stores the value and penalty (for repair of out of bounds point).
+ */
+ private static class ValuePenaltyPair {
+ /** Objective function value. */
+ private double value;
+ /** Penalty value for repair of out out of bounds points. */
+ private double penalty;
+
+ /**
+ * @param value Function value.
+ * @param penalty Out-of-bounds penalty.
+ */
+ ValuePenaltyPair(final double value, final double penalty) {
+ this.value = value;
+ this.penalty = penalty;
+ }
+ }
+
+
+ /**
+ * Normalizes fitness values to the range [0,1]. Adds a penalty to the
+ * fitness value if out of range.
+ */
+ private class FitnessFunction {
+ /**
+ * Flag indicating whether the objective variables are forced into their
+ * bounds if defined
+ */
+ private final boolean isRepairMode;
+
+ /** Simple constructor.
+ */
+ FitnessFunction() {
+ isRepairMode = true;
+ }
+
+ /**
+ * @param point Normalized objective variables.
+ * @return the objective value + penalty for violated bounds.
+ */
+ public ValuePenaltyPair value(final double[] point) {
+ double value;
+ double penalty=0.0;
+ if (isRepairMode) {
+ double[] repaired = repair(point);
+ value = CMAESOptimizer.this.computeObjectiveValue(repaired);
+ penalty = penalty(point, repaired);
+ } else {
+ value = CMAESOptimizer.this.computeObjectiveValue(point);
+ }
+ value = isMinimize ? value : -value;
+ penalty = isMinimize ? penalty : -penalty;
+ return new ValuePenaltyPair(value,penalty);
+ }
+
+ /**
+ * @param x Normalized objective variables.
+ * @return {@code true} if in bounds.
+ */
+ public boolean isFeasible(final double[] x) {
+ final double[] lB = CMAESOptimizer.this.getLowerBound();
+ final double[] uB = CMAESOptimizer.this.getUpperBound();
+
+ for (int i = 0; i < x.length; i++) {
+ if (x[i] < lB[i]) {
+ return false;
+ }
+ if (x[i] > uB[i]) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ /**
+ * @param x Normalized objective variables.
+ * @return the repaired (i.e. all in bounds) objective variables.
+ */
+ private double[] repair(final double[] x) {
+ final double[] lB = CMAESOptimizer.this.getLowerBound();
+ final double[] uB = CMAESOptimizer.this.getUpperBound();
+
+ final double[] repaired = new double[x.length];
+ for (int i = 0; i < x.length; i++) {
+ if (x[i] < lB[i]) {
+ repaired[i] = lB[i];
+ } else if (x[i] > uB[i]) {
+ repaired[i] = uB[i];
+ } else {
+ repaired[i] = x[i];
+ }
+ }
+ return repaired;
+ }
+
+ /**
+ * @param x Normalized objective variables.
+ * @param repaired Repaired objective variables.
+ * @return Penalty value according to the violation of the bounds.
+ */
+ private double penalty(final double[] x, final double[] repaired) {
+ double penalty = 0;
+ for (int i = 0; i < x.length; i++) {
+ double diff = FastMath.abs(x[i] - repaired[i]);
+ penalty += diff;
+ }
+ return isMinimize ? penalty : -penalty;
+ }
+ }
+
+ // -----Matrix utility functions similar to the Matlab build in functions------
+
+ /**
+ * @param m Input matrix
+ * @return Matrix representing the element-wise logarithm of m.
+ */
+ private static RealMatrix log(final RealMatrix m) {
+ final double[][] d = new double[m.getRowDimension()][m.getColumnDimension()];
+ for (int r = 0; r < m.getRowDimension(); r++) {
+ for (int c = 0; c < m.getColumnDimension(); c++) {
+ d[r][c] = FastMath.log(m.getEntry(r, c));
+ }
+ }
+ return new Array2DRowRealMatrix(d, false);
+ }
+
+ /**
+ * @param m Input matrix.
+ * @return Matrix representing the element-wise square root of m.
+ */
+ private static RealMatrix sqrt(final RealMatrix m) {
+ final double[][] d = new double[m.getRowDimension()][m.getColumnDimension()];
+ for (int r = 0; r < m.getRowDimension(); r++) {
+ for (int c = 0; c < m.getColumnDimension(); c++) {
+ d[r][c] = FastMath.sqrt(m.getEntry(r, c));
+ }
+ }
+ return new Array2DRowRealMatrix(d, false);
+ }
+
+ /**
+ * @param m Input matrix.
+ * @return Matrix representing the element-wise square of m.
+ */
+ private static RealMatrix square(final RealMatrix m) {
+ final double[][] d = new double[m.getRowDimension()][m.getColumnDimension()];
+ for (int r = 0; r < m.getRowDimension(); r++) {
+ for (int c = 0; c < m.getColumnDimension(); c++) {
+ double e = m.getEntry(r, c);
+ d[r][c] = e * e;
+ }
+ }
+ return new Array2DRowRealMatrix(d, false);
+ }
+
+ /**
+ * @param m Input matrix 1.
+ * @param n Input matrix 2.
+ * @return the matrix where the elements of m and n are element-wise multiplied.
+ */
+ private static RealMatrix times(final RealMatrix m, final RealMatrix n) {
+ final double[][] d = new double[m.getRowDimension()][m.getColumnDimension()];
+ for (int r = 0; r < m.getRowDimension(); r++) {
+ for (int c = 0; c < m.getColumnDimension(); c++) {
+ d[r][c] = m.getEntry(r, c) * n.getEntry(r, c);
+ }
+ }
+ return new Array2DRowRealMatrix(d, false);
+ }
+
+ /**
+ * @param m Input matrix 1.
+ * @param n Input matrix 2.
+ * @return Matrix where the elements of m and n are element-wise divided.
+ */
+ private static RealMatrix divide(final RealMatrix m, final RealMatrix n) {
+ final double[][] d = new double[m.getRowDimension()][m.getColumnDimension()];
+ for (int r = 0; r < m.getRowDimension(); r++) {
+ for (int c = 0; c < m.getColumnDimension(); c++) {
+ d[r][c] = m.getEntry(r, c) / n.getEntry(r, c);
+ }
+ }
+ return new Array2DRowRealMatrix(d, false);
+ }
+
+ /**
+ * @param m Input matrix.
+ * @param cols Columns to select.
+ * @return Matrix representing the selected columns.
+ */
+ private static RealMatrix selectColumns(final RealMatrix m, final int[] cols) {
+ final double[][] d = new double[m.getRowDimension()][cols.length];
+ for (int r = 0; r < m.getRowDimension(); r++) {
+ for (int c = 0; c < cols.length; c++) {
+ d[r][c] = m.getEntry(r, cols[c]);
+ }
+ }
+ return new Array2DRowRealMatrix(d, false);
+ }
+
+ /**
+ * @param m Input matrix.
+ * @param k Diagonal position.
+ * @return Upper triangular part of matrix.
+ */
+ private static RealMatrix triu(final RealMatrix m, int k) {
+ final double[][] d = new double[m.getRowDimension()][m.getColumnDimension()];
+ for (int r = 0; r < m.getRowDimension(); r++) {
+ for (int c = 0; c < m.getColumnDimension(); c++) {
+ d[r][c] = r <= c - k ? m.getEntry(r, c) : 0;
+ }
+ }
+ return new Array2DRowRealMatrix(d, false);
+ }
+
+ /**
+ * @param m Input matrix.
+ * @return Row matrix representing the sums of the rows.
+ */
+ private static RealMatrix sumRows(final RealMatrix m) {
+ final double[][] d = new double[1][m.getColumnDimension()];
+ for (int c = 0; c < m.getColumnDimension(); c++) {
+ double sum = 0;
+ for (int r = 0; r < m.getRowDimension(); r++) {
+ sum += m.getEntry(r, c);
+ }
+ d[0][c] = sum;
+ }
+ return new Array2DRowRealMatrix(d, false);
+ }
+
+ /**
+ * @param m Input matrix.
+ * @return the diagonal n-by-n matrix if m is a column matrix or the column
+ * matrix representing the diagonal if m is a n-by-n matrix.
+ */
+ private static RealMatrix diag(final RealMatrix m) {
+ if (m.getColumnDimension() == 1) {
+ final double[][] d = new double[m.getRowDimension()][m.getRowDimension()];
+ for (int i = 0; i < m.getRowDimension(); i++) {
+ d[i][i] = m.getEntry(i, 0);
+ }
+ return new Array2DRowRealMatrix(d, false);
+ } else {
+ final double[][] d = new double[m.getRowDimension()][1];
+ for (int i = 0; i < m.getColumnDimension(); i++) {
+ d[i][0] = m.getEntry(i, i);
+ }
+ return new Array2DRowRealMatrix(d, false);
+ }
+ }
+
+ /**
+ * Copies a column from m1 to m2.
+ *
+ * @param m1 Source matrix.
+ * @param col1 Source column.
+ * @param m2 Target matrix.
+ * @param col2 Target column.
+ */
+ private static void copyColumn(final RealMatrix m1, int col1,
+ RealMatrix m2, int col2) {
+ for (int i = 0; i < m1.getRowDimension(); i++) {
+ m2.setEntry(i, col2, m1.getEntry(i, col1));
+ }
+ }
+
+ /**
+ * @param n Number of rows.
+ * @param m Number of columns.
+ * @return n-by-m matrix filled with 1.
+ */
+ private static RealMatrix ones(int n, int m) {
+ final double[][] d = new double[n][m];
+ for (int r = 0; r < n; r++) {
+ Arrays.fill(d[r], 1);
+ }
+ return new Array2DRowRealMatrix(d, false);
+ }
+
+ /**
+ * @param n Number of rows.
+ * @param m Number of columns.
+ * @return n-by-m matrix of 0 values out of diagonal, and 1 values on
+ * the diagonal.
+ */
+ private static RealMatrix eye(int n, int m) {
+ final double[][] d = new double[n][m];
+ for (int r = 0; r < n; r++) {
+ if (r < m) {
+ d[r][r] = 1;
+ }
+ }
+ return new Array2DRowRealMatrix(d, false);
+ }
+
+ /**
+ * @param n Number of rows.
+ * @param m Number of columns.
+ * @return n-by-m matrix of zero values.
+ */
+ private static RealMatrix zeros(int n, int m) {
+ return new Array2DRowRealMatrix(n, m);
+ }
+
+ /**
+ * @param mat Input matrix.
+ * @param n Number of row replicates.
+ * @param m Number of column replicates.
+ * @return a matrix which replicates the input matrix in both directions.
+ */
+ private static RealMatrix repmat(final RealMatrix mat, int n, int m) {
+ final int rd = mat.getRowDimension();
+ final int cd = mat.getColumnDimension();
+ final double[][] d = new double[n * rd][m * cd];
+ for (int r = 0; r < n * rd; r++) {
+ for (int c = 0; c < m * cd; c++) {
+ d[r][c] = mat.getEntry(r % rd, c % cd);
+ }
+ }
+ return new Array2DRowRealMatrix(d, false);
+ }
+
+ /**
+ * @param start Start value.
+ * @param end End value.
+ * @param step Step size.
+ * @return a sequence as column matrix.
+ */
+ private static RealMatrix sequence(double start, double end, double step) {
+ final int size = (int) ((end - start) / step + 1);
+ final double[][] d = new double[size][1];
+ double value = start;
+ for (int r = 0; r < size; r++) {
+ d[r][0] = value;
+ value += step;
+ }
+ return new Array2DRowRealMatrix(d, false);
+ }
+
+ /**
+ * @param m Input matrix.
+ * @return the maximum of the matrix element values.
+ */
+ private static double max(final RealMatrix m) {
+ double max = -Double.MAX_VALUE;
+ for (int r = 0; r < m.getRowDimension(); r++) {
+ for (int c = 0; c < m.getColumnDimension(); c++) {
+ double e = m.getEntry(r, c);
+ if (max < e) {
+ max = e;
+ }
+ }
+ }
+ return max;
+ }
+
+ /**
+ * @param m Input matrix.
+ * @return the minimum of the matrix element values.
+ */
+ private static double min(final RealMatrix m) {
+ double min = Double.MAX_VALUE;
+ for (int r = 0; r < m.getRowDimension(); r++) {
+ for (int c = 0; c < m.getColumnDimension(); c++) {
+ double e = m.getEntry(r, c);
+ if (min > e) {
+ min = e;
+ }
+ }
+ }
+ return min;
+ }
+
+ /**
+ * @param m Input array.
+ * @return the maximum of the array values.
+ */
+ private static double max(final double[] m) {
+ double max = -Double.MAX_VALUE;
+ for (int r = 0; r < m.length; r++) {
+ if (max < m[r]) {
+ max = m[r];
+ }
+ }
+ return max;
+ }
+
+ /**
+ * @param m Input array.
+ * @return the minimum of the array values.
+ */
+ private static double min(final double[] m) {
+ double min = Double.MAX_VALUE;
+ for (int r = 0; r < m.length; r++) {
+ if (min > m[r]) {
+ min = m[r];
+ }
+ }
+ return min;
+ }
+
+ /**
+ * @param indices Input index array.
+ * @return the inverse of the mapping defined by indices.
+ */
+ private static int[] inverse(final int[] indices) {
+ final int[] inverse = new int[indices.length];
+ for (int i = 0; i < indices.length; i++) {
+ inverse[indices[i]] = i;
+ }
+ return inverse;
+ }
+
+ /**
+ * @param indices Input index array.
+ * @return the indices in inverse order (last is first).
+ */
+ private static int[] reverse(final int[] indices) {
+ final int[] reverse = new int[indices.length];
+ for (int i = 0; i < indices.length; i++) {
+ reverse[i] = indices[indices.length - i - 1];
+ }
+ return reverse;
+ }
+
+ /**
+ * @param size Length of random array.
+ * @return an array of Gaussian random numbers.
+ */
+ private double[] randn(int size) {
+ final double[] randn = new double[size];
+ for (int i = 0; i < size; i++) {
+ randn[i] = random.nextGaussian();
+ }
+ return randn;
+ }
+
+ /**
+ * @param size Number of rows.
+ * @param popSize Population size.
+ * @return a 2-dimensional matrix of Gaussian random numbers.
+ */
+ private RealMatrix randn1(int size, int popSize) {
+ final double[][] d = new double[size][popSize];
+ for (int r = 0; r < size; r++) {
+ for (int c = 0; c < popSize; c++) {
+ d[r][c] = random.nextGaussian();
+ }
+ }
+ return new Array2DRowRealMatrix(d, false);
+ }
+}
diff --git a/src/main/java/org/apache/commons/math3/optim/nonlinear/scalar/noderiv/MultiDirectionalSimplex.java b/src/main/java/org/apache/commons/math3/optim/nonlinear/scalar/noderiv/MultiDirectionalSimplex.java
new file mode 100644
index 0000000..7ee3acf
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/optim/nonlinear/scalar/noderiv/MultiDirectionalSimplex.java
@@ -0,0 +1,215 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math3.optim.nonlinear.scalar.noderiv;
+
+import java.util.Comparator;
+
+import org.apache.commons.math3.analysis.MultivariateFunction;
+import org.apache.commons.math3.optim.PointValuePair;
+
+/**
+ * This class implements the multi-directional direct search method.
+ *
+ * @since 3.0
+ */
+public class MultiDirectionalSimplex extends AbstractSimplex {
+ /** Default value for {@link #khi}: {@value}. */
+ private static final double DEFAULT_KHI = 2;
+ /** Default value for {@link #gamma}: {@value}. */
+ private static final double DEFAULT_GAMMA = 0.5;
+ /** Expansion coefficient. */
+ private final double khi;
+ /** Contraction coefficient. */
+ private final double gamma;
+
+ /**
+ * Build a multi-directional simplex with default coefficients.
+ * The default values are 2.0 for khi and 0.5 for gamma.
+ *
+ * @param n Dimension of the simplex.
+ */
+ public MultiDirectionalSimplex(final int n) {
+ this(n, 1d);
+ }
+
+ /**
+ * Build a multi-directional simplex with default coefficients.
+ * The default values are 2.0 for khi and 0.5 for gamma.
+ *
+ * @param n Dimension of the simplex.
+ * @param sideLength Length of the sides of the default (hypercube)
+ * simplex. See {@link AbstractSimplex#AbstractSimplex(int,double)}.
+ */
+ public MultiDirectionalSimplex(final int n, double sideLength) {
+ this(n, sideLength, DEFAULT_KHI, DEFAULT_GAMMA);
+ }
+
+ /**
+ * Build a multi-directional simplex with specified coefficients.
+ *
+ * @param n Dimension of the simplex. See
+ * {@link AbstractSimplex#AbstractSimplex(int,double)}.
+ * @param khi Expansion coefficient.
+ * @param gamma Contraction coefficient.
+ */
+ public MultiDirectionalSimplex(final int n,
+ final double khi, final double gamma) {
+ this(n, 1d, khi, gamma);
+ }
+
+ /**
+ * Build a multi-directional simplex with specified coefficients.
+ *
+ * @param n Dimension of the simplex. See
+ * {@link AbstractSimplex#AbstractSimplex(int,double)}.
+ * @param sideLength Length of the sides of the default (hypercube)
+ * simplex. See {@link AbstractSimplex#AbstractSimplex(int,double)}.
+ * @param khi Expansion coefficient.
+ * @param gamma Contraction coefficient.
+ */
+ public MultiDirectionalSimplex(final int n, double sideLength,
+ final double khi, final double gamma) {
+ super(n, sideLength);
+
+ this.khi = khi;
+ this.gamma = gamma;
+ }
+
+ /**
+ * Build a multi-directional simplex with default coefficients.
+ * The default values are 2.0 for khi and 0.5 for gamma.
+ *
+ * @param steps Steps along the canonical axes representing box edges.
+ * They may be negative but not zero. See
+ */
+ public MultiDirectionalSimplex(final double[] steps) {
+ this(steps, DEFAULT_KHI, DEFAULT_GAMMA);
+ }
+
+ /**
+ * Build a multi-directional simplex with specified coefficients.
+ *
+ * @param steps Steps along the canonical axes representing box edges.
+ * They may be negative but not zero. See
+ * {@link AbstractSimplex#AbstractSimplex(double[])}.
+ * @param khi Expansion coefficient.
+ * @param gamma Contraction coefficient.
+ */
+ public MultiDirectionalSimplex(final double[] steps,
+ final double khi, final double gamma) {
+ super(steps);
+
+ this.khi = khi;
+ this.gamma = gamma;
+ }
+
+ /**
+ * Build a multi-directional simplex with default coefficients.
+ * The default values are 2.0 for khi and 0.5 for gamma.
+ *
+ * @param referenceSimplex Reference simplex. See
+ * {@link AbstractSimplex#AbstractSimplex(double[][])}.
+ */
+ public MultiDirectionalSimplex(final double[][] referenceSimplex) {
+ this(referenceSimplex, DEFAULT_KHI, DEFAULT_GAMMA);
+ }
+
+ /**
+ * Build a multi-directional simplex with specified coefficients.
+ *
+ * @param referenceSimplex Reference simplex. See
+ * {@link AbstractSimplex#AbstractSimplex(double[][])}.
+ * @param khi Expansion coefficient.
+ * @param gamma Contraction coefficient.
+ * @throws org.apache.commons.math3.exception.NotStrictlyPositiveException
+ * if the reference simplex does not contain at least one point.
+ * @throws org.apache.commons.math3.exception.DimensionMismatchException
+ * if there is a dimension mismatch in the reference simplex.
+ */
+ public MultiDirectionalSimplex(final double[][] referenceSimplex,
+ final double khi, final double gamma) {
+ super(referenceSimplex);
+
+ this.khi = khi;
+ this.gamma = gamma;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void iterate(final MultivariateFunction evaluationFunction,
+ final Comparator<PointValuePair> comparator) {
+ // Save the original simplex.
+ final PointValuePair[] original = getPoints();
+ final PointValuePair best = original[0];
+
+ // Perform a reflection step.
+ final PointValuePair reflected = evaluateNewSimplex(evaluationFunction,
+ original, 1, comparator);
+ if (comparator.compare(reflected, best) < 0) {
+ // Compute the expanded simplex.
+ final PointValuePair[] reflectedSimplex = getPoints();
+ final PointValuePair expanded = evaluateNewSimplex(evaluationFunction,
+ original, khi, comparator);
+ if (comparator.compare(reflected, expanded) <= 0) {
+ // Keep the reflected simplex.
+ setPoints(reflectedSimplex);
+ }
+ // Keep the expanded simplex.
+ return;
+ }
+
+ // Compute the contracted simplex.
+ evaluateNewSimplex(evaluationFunction, original, gamma, comparator);
+
+ }
+
+ /**
+ * Compute and evaluate a new simplex.
+ *
+ * @param evaluationFunction Evaluation function.
+ * @param original Original simplex (to be preserved).
+ * @param coeff Linear coefficient.
+ * @param comparator Comparator to use to sort simplex vertices from best
+ * to poorest.
+ * @return the best point in the transformed simplex.
+ * @throws org.apache.commons.math3.exception.TooManyEvaluationsException
+ * if the maximal number of evaluations is exceeded.
+ */
+ private PointValuePair evaluateNewSimplex(final MultivariateFunction evaluationFunction,
+ final PointValuePair[] original,
+ final double coeff,
+ final Comparator<PointValuePair> comparator) {
+ final double[] xSmallest = original[0].getPointRef();
+ // Perform a linear transformation on all the simplex points,
+ // except the first one.
+ setPoint(0, original[0]);
+ final int dim = getDimension();
+ for (int i = 1; i < getSize(); i++) {
+ final double[] xOriginal = original[i].getPointRef();
+ final double[] xTransformed = new double[dim];
+ for (int j = 0; j < dim; j++) {
+ xTransformed[j] = xSmallest[j] + coeff * (xSmallest[j] - xOriginal[j]);
+ }
+ setPoint(i, new PointValuePair(xTransformed, Double.NaN, false));
+ }
+
+ // Evaluate the simplex.
+ evaluate(evaluationFunction, comparator);
+
+ return getPoint(0);
+ }
+}
diff --git a/src/main/java/org/apache/commons/math3/optim/nonlinear/scalar/noderiv/NelderMeadSimplex.java b/src/main/java/org/apache/commons/math3/optim/nonlinear/scalar/noderiv/NelderMeadSimplex.java
new file mode 100644
index 0000000..f7015ed
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/optim/nonlinear/scalar/noderiv/NelderMeadSimplex.java
@@ -0,0 +1,280 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math3.optim.nonlinear.scalar.noderiv;
+
+import java.util.Comparator;
+
+import org.apache.commons.math3.optim.PointValuePair;
+import org.apache.commons.math3.analysis.MultivariateFunction;
+
+/**
+ * This class implements the Nelder-Mead simplex algorithm.
+ *
+ * @since 3.0
+ */
+public class NelderMeadSimplex extends AbstractSimplex {
+ /** Default value for {@link #rho}: {@value}. */
+ private static final double DEFAULT_RHO = 1;
+ /** Default value for {@link #khi}: {@value}. */
+ private static final double DEFAULT_KHI = 2;
+ /** Default value for {@link #gamma}: {@value}. */
+ private static final double DEFAULT_GAMMA = 0.5;
+ /** Default value for {@link #sigma}: {@value}. */
+ private static final double DEFAULT_SIGMA = 0.5;
+ /** Reflection coefficient. */
+ private final double rho;
+ /** Expansion coefficient. */
+ private final double khi;
+ /** Contraction coefficient. */
+ private final double gamma;
+ /** Shrinkage coefficient. */
+ private final double sigma;
+
+ /**
+ * Build a Nelder-Mead simplex with default coefficients.
+ * The default coefficients are 1.0 for rho, 2.0 for khi and 0.5
+ * for both gamma and sigma.
+ *
+ * @param n Dimension of the simplex.
+ */
+ public NelderMeadSimplex(final int n) {
+ this(n, 1d);
+ }
+
+ /**
+ * Build a Nelder-Mead simplex with default coefficients.
+ * The default coefficients are 1.0 for rho, 2.0 for khi and 0.5
+ * for both gamma and sigma.
+ *
+ * @param n Dimension of the simplex.
+ * @param sideLength Length of the sides of the default (hypercube)
+ * simplex. See {@link AbstractSimplex#AbstractSimplex(int,double)}.
+ */
+ public NelderMeadSimplex(final int n, double sideLength) {
+ this(n, sideLength,
+ DEFAULT_RHO, DEFAULT_KHI, DEFAULT_GAMMA, DEFAULT_SIGMA);
+ }
+
+ /**
+ * Build a Nelder-Mead simplex with specified coefficients.
+ *
+ * @param n Dimension of the simplex. See
+ * {@link AbstractSimplex#AbstractSimplex(int,double)}.
+ * @param sideLength Length of the sides of the default (hypercube)
+ * simplex. See {@link AbstractSimplex#AbstractSimplex(int,double)}.
+ * @param rho Reflection coefficient.
+ * @param khi Expansion coefficient.
+ * @param gamma Contraction coefficient.
+ * @param sigma Shrinkage coefficient.
+ */
+ public NelderMeadSimplex(final int n, double sideLength,
+ final double rho, final double khi,
+ final double gamma, final double sigma) {
+ super(n, sideLength);
+
+ this.rho = rho;
+ this.khi = khi;
+ this.gamma = gamma;
+ this.sigma = sigma;
+ }
+
+ /**
+ * Build a Nelder-Mead simplex with specified coefficients.
+ *
+ * @param n Dimension of the simplex. See
+ * {@link AbstractSimplex#AbstractSimplex(int)}.
+ * @param rho Reflection coefficient.
+ * @param khi Expansion coefficient.
+ * @param gamma Contraction coefficient.
+ * @param sigma Shrinkage coefficient.
+ */
+ public NelderMeadSimplex(final int n,
+ final double rho, final double khi,
+ final double gamma, final double sigma) {
+ this(n, 1d, rho, khi, gamma, sigma);
+ }
+
+ /**
+ * Build a Nelder-Mead simplex with default coefficients.
+ * The default coefficients are 1.0 for rho, 2.0 for khi and 0.5
+ * for both gamma and sigma.
+ *
+ * @param steps Steps along the canonical axes representing box edges.
+ * They may be negative but not zero. See
+ */
+ public NelderMeadSimplex(final double[] steps) {
+ this(steps, DEFAULT_RHO, DEFAULT_KHI, DEFAULT_GAMMA, DEFAULT_SIGMA);
+ }
+
+ /**
+ * Build a Nelder-Mead simplex with specified coefficients.
+ *
+ * @param steps Steps along the canonical axes representing box edges.
+ * They may be negative but not zero. See
+ * {@link AbstractSimplex#AbstractSimplex(double[])}.
+ * @param rho Reflection coefficient.
+ * @param khi Expansion coefficient.
+ * @param gamma Contraction coefficient.
+ * @param sigma Shrinkage coefficient.
+ * @throws IllegalArgumentException if one of the steps is zero.
+ */
+ public NelderMeadSimplex(final double[] steps,
+ final double rho, final double khi,
+ final double gamma, final double sigma) {
+ super(steps);
+
+ this.rho = rho;
+ this.khi = khi;
+ this.gamma = gamma;
+ this.sigma = sigma;
+ }
+
+ /**
+ * Build a Nelder-Mead simplex with default coefficients.
+ * The default coefficients are 1.0 for rho, 2.0 for khi and 0.5
+ * for both gamma and sigma.
+ *
+ * @param referenceSimplex Reference simplex. See
+ * {@link AbstractSimplex#AbstractSimplex(double[][])}.
+ */
+ public NelderMeadSimplex(final double[][] referenceSimplex) {
+ this(referenceSimplex, DEFAULT_RHO, DEFAULT_KHI, DEFAULT_GAMMA, DEFAULT_SIGMA);
+ }
+
+ /**
+ * Build a Nelder-Mead simplex with specified coefficients.
+ *
+ * @param referenceSimplex Reference simplex. See
+ * {@link AbstractSimplex#AbstractSimplex(double[][])}.
+ * @param rho Reflection coefficient.
+ * @param khi Expansion coefficient.
+ * @param gamma Contraction coefficient.
+ * @param sigma Shrinkage coefficient.
+ * @throws org.apache.commons.math3.exception.NotStrictlyPositiveException
+ * if the reference simplex does not contain at least one point.
+ * @throws org.apache.commons.math3.exception.DimensionMismatchException
+ * if there is a dimension mismatch in the reference simplex.
+ */
+ public NelderMeadSimplex(final double[][] referenceSimplex,
+ final double rho, final double khi,
+ final double gamma, final double sigma) {
+ super(referenceSimplex);
+
+ this.rho = rho;
+ this.khi = khi;
+ this.gamma = gamma;
+ this.sigma = sigma;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void iterate(final MultivariateFunction evaluationFunction,
+ final Comparator<PointValuePair> comparator) {
+ // The simplex has n + 1 points if dimension is n.
+ final int n = getDimension();
+
+ // Interesting values.
+ final PointValuePair best = getPoint(0);
+ final PointValuePair secondBest = getPoint(n - 1);
+ final PointValuePair worst = getPoint(n);
+ final double[] xWorst = worst.getPointRef();
+
+ // Compute the centroid of the best vertices (dismissing the worst
+ // point at index n).
+ final double[] centroid = new double[n];
+ for (int i = 0; i < n; i++) {
+ final double[] x = getPoint(i).getPointRef();
+ for (int j = 0; j < n; j++) {
+ centroid[j] += x[j];
+ }
+ }
+ final double scaling = 1.0 / n;
+ for (int j = 0; j < n; j++) {
+ centroid[j] *= scaling;
+ }
+
+ // compute the reflection point
+ final double[] xR = new double[n];
+ for (int j = 0; j < n; j++) {
+ xR[j] = centroid[j] + rho * (centroid[j] - xWorst[j]);
+ }
+ final PointValuePair reflected
+ = new PointValuePair(xR, evaluationFunction.value(xR), false);
+
+ if (comparator.compare(best, reflected) <= 0 &&
+ comparator.compare(reflected, secondBest) < 0) {
+ // Accept the reflected point.
+ replaceWorstPoint(reflected, comparator);
+ } else if (comparator.compare(reflected, best) < 0) {
+ // Compute the expansion point.
+ final double[] xE = new double[n];
+ for (int j = 0; j < n; j++) {
+ xE[j] = centroid[j] + khi * (xR[j] - centroid[j]);
+ }
+ final PointValuePair expanded
+ = new PointValuePair(xE, evaluationFunction.value(xE), false);
+
+ if (comparator.compare(expanded, reflected) < 0) {
+ // Accept the expansion point.
+ replaceWorstPoint(expanded, comparator);
+ } else {
+ // Accept the reflected point.
+ replaceWorstPoint(reflected, comparator);
+ }
+ } else {
+ if (comparator.compare(reflected, worst) < 0) {
+ // Perform an outside contraction.
+ final double[] xC = new double[n];
+ for (int j = 0; j < n; j++) {
+ xC[j] = centroid[j] + gamma * (xR[j] - centroid[j]);
+ }
+ final PointValuePair outContracted
+ = new PointValuePair(xC, evaluationFunction.value(xC), false);
+ if (comparator.compare(outContracted, reflected) <= 0) {
+ // Accept the contraction point.
+ replaceWorstPoint(outContracted, comparator);
+ return;
+ }
+ } else {
+ // Perform an inside contraction.
+ final double[] xC = new double[n];
+ for (int j = 0; j < n; j++) {
+ xC[j] = centroid[j] - gamma * (centroid[j] - xWorst[j]);
+ }
+ final PointValuePair inContracted
+ = new PointValuePair(xC, evaluationFunction.value(xC), false);
+
+ if (comparator.compare(inContracted, worst) < 0) {
+ // Accept the contraction point.
+ replaceWorstPoint(inContracted, comparator);
+ return;
+ }
+ }
+
+ // Perform a shrink.
+ final double[] xSmallest = getPoint(0).getPointRef();
+ for (int i = 1; i <= n; i++) {
+ final double[] x = getPoint(i).getPoint();
+ for (int j = 0; j < n; j++) {
+ x[j] = xSmallest[j] + sigma * (x[j] - xSmallest[j]);
+ }
+ setPoint(i, new PointValuePair(x, Double.NaN, false));
+ }
+ evaluate(evaluationFunction, comparator);
+ }
+ }
+}
diff --git a/src/main/java/org/apache/commons/math3/optim/nonlinear/scalar/noderiv/PowellOptimizer.java b/src/main/java/org/apache/commons/math3/optim/nonlinear/scalar/noderiv/PowellOptimizer.java
new file mode 100644
index 0000000..afa8426
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/optim/nonlinear/scalar/noderiv/PowellOptimizer.java
@@ -0,0 +1,299 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math3.optim.nonlinear.scalar.noderiv;
+
+import org.apache.commons.math3.util.FastMath;
+import org.apache.commons.math3.util.MathArrays;
+import org.apache.commons.math3.exception.NumberIsTooSmallException;
+import org.apache.commons.math3.exception.NotStrictlyPositiveException;
+import org.apache.commons.math3.exception.MathUnsupportedOperationException;
+import org.apache.commons.math3.exception.util.LocalizedFormats;
+import org.apache.commons.math3.optim.nonlinear.scalar.GoalType;
+import org.apache.commons.math3.optim.PointValuePair;
+import org.apache.commons.math3.optim.ConvergenceChecker;
+import org.apache.commons.math3.optim.nonlinear.scalar.MultivariateOptimizer;
+import org.apache.commons.math3.optim.nonlinear.scalar.LineSearch;
+import org.apache.commons.math3.optim.univariate.UnivariatePointValuePair;
+
+/**
+ * Powell's algorithm.
+ * This code is translated and adapted from the Python version of this
+ * algorithm (as implemented in module {@code optimize.py} v0.5 of
+ * <em>SciPy</em>).
+ * <br/>
+ * The default stopping criterion is based on the differences of the
+ * function value between two successive iterations. It is however possible
+ * to define a custom convergence checker that might terminate the algorithm
+ * earlier.
+ * <br/>
+ * Line search is performed by the {@link LineSearch} class.
+ * <br/>
+ * Constraints are not supported: the call to
+ * {@link #optimize(OptimizationData[]) optimize} will throw
+ * {@link MathUnsupportedOperationException} if bounds are passed to it.
+ * In order to impose simple constraints, the objective function must be
+ * wrapped in an adapter like
+ * {@link org.apache.commons.math3.optim.nonlinear.scalar.MultivariateFunctionMappingAdapter
+ * MultivariateFunctionMappingAdapter} or
+ * {@link org.apache.commons.math3.optim.nonlinear.scalar.MultivariateFunctionPenaltyAdapter
+ * MultivariateFunctionPenaltyAdapter}.
+ *
+ * @since 2.2
+ */
+public class PowellOptimizer
+ extends MultivariateOptimizer {
+ /**
+ * Minimum relative tolerance.
+ */
+ private static final double MIN_RELATIVE_TOLERANCE = 2 * FastMath.ulp(1d);
+ /**
+ * Relative threshold.
+ */
+ private final double relativeThreshold;
+ /**
+ * Absolute threshold.
+ */
+ private final double absoluteThreshold;
+ /**
+ * Line search.
+ */
+ private final LineSearch line;
+
+ /**
+ * This constructor allows to specify a user-defined convergence checker,
+ * in addition to the parameters that control the default convergence
+ * checking procedure.
+ * <br/>
+ * The internal line search tolerances are set to the square-root of their
+ * corresponding value in the multivariate optimizer.
+ *
+ * @param rel Relative threshold.
+ * @param abs Absolute threshold.
+ * @param checker Convergence checker.
+ * @throws NotStrictlyPositiveException if {@code abs <= 0}.
+ * @throws NumberIsTooSmallException if {@code rel < 2 * Math.ulp(1d)}.
+ */
+ public PowellOptimizer(double rel,
+ double abs,
+ ConvergenceChecker<PointValuePair> checker) {
+ this(rel, abs, FastMath.sqrt(rel), FastMath.sqrt(abs), checker);
+ }
+
+ /**
+ * This constructor allows to specify a user-defined convergence checker,
+ * in addition to the parameters that control the default convergence
+ * checking procedure and the line search tolerances.
+ *
+ * @param rel Relative threshold for this optimizer.
+ * @param abs Absolute threshold for this optimizer.
+ * @param lineRel Relative threshold for the internal line search optimizer.
+ * @param lineAbs Absolute threshold for the internal line search optimizer.
+ * @param checker Convergence checker.
+ * @throws NotStrictlyPositiveException if {@code abs <= 0}.
+ * @throws NumberIsTooSmallException if {@code rel < 2 * Math.ulp(1d)}.
+ */
+ public PowellOptimizer(double rel,
+ double abs,
+ double lineRel,
+ double lineAbs,
+ ConvergenceChecker<PointValuePair> checker) {
+ super(checker);
+
+ if (rel < MIN_RELATIVE_TOLERANCE) {
+ throw new NumberIsTooSmallException(rel, MIN_RELATIVE_TOLERANCE, true);
+ }
+ if (abs <= 0) {
+ throw new NotStrictlyPositiveException(abs);
+ }
+ relativeThreshold = rel;
+ absoluteThreshold = abs;
+
+ // Create the line search optimizer.
+ line = new LineSearch(this,
+ lineRel,
+ lineAbs,
+ 1d);
+ }
+
+ /**
+ * The parameters control the default convergence checking procedure.
+ * <br/>
+ * The internal line search tolerances are set to the square-root of their
+ * corresponding value in the multivariate optimizer.
+ *
+ * @param rel Relative threshold.
+ * @param abs Absolute threshold.
+ * @throws NotStrictlyPositiveException if {@code abs <= 0}.
+ * @throws NumberIsTooSmallException if {@code rel < 2 * Math.ulp(1d)}.
+ */
+ public PowellOptimizer(double rel,
+ double abs) {
+ this(rel, abs, null);
+ }
+
+ /**
+ * Builds an instance with the default convergence checking procedure.
+ *
+ * @param rel Relative threshold.
+ * @param abs Absolute threshold.
+ * @param lineRel Relative threshold for the internal line search optimizer.
+ * @param lineAbs Absolute threshold for the internal line search optimizer.
+ * @throws NotStrictlyPositiveException if {@code abs <= 0}.
+ * @throws NumberIsTooSmallException if {@code rel < 2 * Math.ulp(1d)}.
+ */
+ public PowellOptimizer(double rel,
+ double abs,
+ double lineRel,
+ double lineAbs) {
+ this(rel, abs, lineRel, lineAbs, null);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ protected PointValuePair doOptimize() {
+ checkParameters();
+
+ final GoalType goal = getGoalType();
+ final double[] guess = getStartPoint();
+ final int n = guess.length;
+
+ final double[][] direc = new double[n][n];
+ for (int i = 0; i < n; i++) {
+ direc[i][i] = 1;
+ }
+
+ final ConvergenceChecker<PointValuePair> checker
+ = getConvergenceChecker();
+
+ double[] x = guess;
+ double fVal = computeObjectiveValue(x);
+ double[] x1 = x.clone();
+ while (true) {
+ incrementIterationCount();
+
+ double fX = fVal;
+ double fX2 = 0;
+ double delta = 0;
+ int bigInd = 0;
+ double alphaMin = 0;
+
+ for (int i = 0; i < n; i++) {
+ final double[] d = MathArrays.copyOf(direc[i]);
+
+ fX2 = fVal;
+
+ final UnivariatePointValuePair optimum = line.search(x, d);
+ fVal = optimum.getValue();
+ alphaMin = optimum.getPoint();
+ final double[][] result = newPointAndDirection(x, d, alphaMin);
+ x = result[0];
+
+ if ((fX2 - fVal) > delta) {
+ delta = fX2 - fVal;
+ bigInd = i;
+ }
+ }
+
+ // Default convergence check.
+ boolean stop = 2 * (fX - fVal) <=
+ (relativeThreshold * (FastMath.abs(fX) + FastMath.abs(fVal)) +
+ absoluteThreshold);
+
+ final PointValuePair previous = new PointValuePair(x1, fX);
+ final PointValuePair current = new PointValuePair(x, fVal);
+ if (!stop && checker != null) { // User-defined stopping criteria.
+ stop = checker.converged(getIterations(), previous, current);
+ }
+ if (stop) {
+ if (goal == GoalType.MINIMIZE) {
+ return (fVal < fX) ? current : previous;
+ } else {
+ return (fVal > fX) ? current : previous;
+ }
+ }
+
+ final double[] d = new double[n];
+ final double[] x2 = new double[n];
+ for (int i = 0; i < n; i++) {
+ d[i] = x[i] - x1[i];
+ x2[i] = 2 * x[i] - x1[i];
+ }
+
+ x1 = x.clone();
+ fX2 = computeObjectiveValue(x2);
+
+ if (fX > fX2) {
+ double t = 2 * (fX + fX2 - 2 * fVal);
+ double temp = fX - fVal - delta;
+ t *= temp * temp;
+ temp = fX - fX2;
+ t -= delta * temp * temp;
+
+ if (t < 0.0) {
+ final UnivariatePointValuePair optimum = line.search(x, d);
+ fVal = optimum.getValue();
+ alphaMin = optimum.getPoint();
+ final double[][] result = newPointAndDirection(x, d, alphaMin);
+ x = result[0];
+
+ final int lastInd = n - 1;
+ direc[bigInd] = direc[lastInd];
+ direc[lastInd] = result[1];
+ }
+ }
+ }
+ }
+
+ /**
+ * Compute a new point (in the original space) and a new direction
+ * vector, resulting from the line search.
+ *
+ * @param p Point used in the line search.
+ * @param d Direction used in the line search.
+ * @param optimum Optimum found by the line search.
+ * @return a 2-element array containing the new point (at index 0) and
+ * the new direction (at index 1).
+ */
+ private double[][] newPointAndDirection(double[] p,
+ double[] d,
+ double optimum) {
+ final int n = p.length;
+ final double[] nP = new double[n];
+ final double[] nD = new double[n];
+ for (int i = 0; i < n; i++) {
+ nD[i] = d[i] * optimum;
+ nP[i] = p[i] + nD[i];
+ }
+
+ final double[][] result = new double[2][];
+ result[0] = nP;
+ result[1] = nD;
+
+ return result;
+ }
+
+ /**
+ * @throws MathUnsupportedOperationException if bounds were passed to the
+ * {@link #optimize(OptimizationData[]) optimize} method.
+ */
+ private void checkParameters() {
+ if (getLowerBound() != null ||
+ getUpperBound() != null) {
+ throw new MathUnsupportedOperationException(LocalizedFormats.CONSTRAINT);
+ }
+ }
+}
diff --git a/src/main/java/org/apache/commons/math3/optim/nonlinear/scalar/noderiv/SimplexOptimizer.java b/src/main/java/org/apache/commons/math3/optim/nonlinear/scalar/noderiv/SimplexOptimizer.java
new file mode 100644
index 0000000..4bb6b64
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/optim/nonlinear/scalar/noderiv/SimplexOptimizer.java
@@ -0,0 +1,222 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math3.optim.nonlinear.scalar.noderiv;
+
+import java.util.Comparator;
+import org.apache.commons.math3.analysis.MultivariateFunction;
+import org.apache.commons.math3.exception.NullArgumentException;
+import org.apache.commons.math3.exception.MathUnsupportedOperationException;
+import org.apache.commons.math3.exception.util.LocalizedFormats;
+import org.apache.commons.math3.optim.nonlinear.scalar.GoalType;
+import org.apache.commons.math3.optim.ConvergenceChecker;
+import org.apache.commons.math3.optim.PointValuePair;
+import org.apache.commons.math3.optim.SimpleValueChecker;
+import org.apache.commons.math3.optim.OptimizationData;
+import org.apache.commons.math3.optim.nonlinear.scalar.MultivariateOptimizer;
+
+/**
+ * This class implements simplex-based direct search optimization.
+ *
+ * <p>
+ * Direct search methods only use objective function values, they do
+ * not need derivatives and don't either try to compute approximation
+ * of the derivatives. According to a 1996 paper by Margaret H. Wright
+ * (<a href="http://cm.bell-labs.com/cm/cs/doc/96/4-02.ps.gz">Direct
+ * Search Methods: Once Scorned, Now Respectable</a>), they are used
+ * when either the computation of the derivative is impossible (noisy
+ * functions, unpredictable discontinuities) or difficult (complexity,
+ * computation cost). In the first cases, rather than an optimum, a
+ * <em>not too bad</em> point is desired. In the latter cases, an
+ * optimum is desired but cannot be reasonably found. In all cases
+ * direct search methods can be useful.
+ * </p>
+ * <p>
+ * Simplex-based direct search methods are based on comparison of
+ * the objective function values at the vertices of a simplex (which is a
+ * set of n+1 points in dimension n) that is updated by the algorithms
+ * steps.
+ * <p>
+ * <p>
+ * The simplex update procedure ({@link NelderMeadSimplex} or
+ * {@link MultiDirectionalSimplex}) must be passed to the
+ * {@code optimize} method.
+ * </p>
+ * <p>
+ * Each call to {@code optimize} will re-use the start configuration of
+ * the current simplex and move it such that its first vertex is at the
+ * provided start point of the optimization.
+ * If the {@code optimize} method is called to solve a different problem
+ * and the number of parameters change, the simplex must be re-initialized
+ * to one with the appropriate dimensions.
+ * </p>
+ * <p>
+ * Convergence is checked by providing the <em>worst</em> points of
+ * previous and current simplex to the convergence checker, not the best
+ * ones.
+ * </p>
+ * <p>
+ * This simplex optimizer implementation does not directly support constrained
+ * optimization with simple bounds; so, for such optimizations, either a more
+ * dedicated algorithm must be used like
+ * {@link CMAESOptimizer} or {@link BOBYQAOptimizer}, or the objective
+ * function must be wrapped in an adapter like
+ * {@link org.apache.commons.math3.optim.nonlinear.scalar.MultivariateFunctionMappingAdapter
+ * MultivariateFunctionMappingAdapter} or
+ * {@link org.apache.commons.math3.optim.nonlinear.scalar.MultivariateFunctionPenaltyAdapter
+ * MultivariateFunctionPenaltyAdapter}.
+ * <br/>
+ * The call to {@link #optimize(OptimizationData[]) optimize} will throw
+ * {@link MathUnsupportedOperationException} if bounds are passed to it.
+ * </p>
+ *
+ * @since 3.0
+ */
+public class SimplexOptimizer extends MultivariateOptimizer {
+ /** Simplex update rule. */
+ private AbstractSimplex simplex;
+
+ /**
+ * @param checker Convergence checker.
+ */
+ public SimplexOptimizer(ConvergenceChecker<PointValuePair> checker) {
+ super(checker);
+ }
+
+ /**
+ * @param rel Relative threshold.
+ * @param abs Absolute threshold.
+ */
+ public SimplexOptimizer(double rel, double abs) {
+ this(new SimpleValueChecker(rel, abs));
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @param optData Optimization data. In addition to those documented in
+ * {@link MultivariateOptimizer#parseOptimizationData(OptimizationData[])
+ * MultivariateOptimizer}, this method will register the following data:
+ * <ul>
+ * <li>{@link AbstractSimplex}</li>
+ * </ul>
+ * @return {@inheritDoc}
+ */
+ @Override
+ public PointValuePair optimize(OptimizationData... optData) {
+ // Set up base class and perform computation.
+ return super.optimize(optData);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ protected PointValuePair doOptimize() {
+ checkParameters();
+
+ // Indirect call to "computeObjectiveValue" in order to update the
+ // evaluations counter.
+ final MultivariateFunction evalFunc
+ = new MultivariateFunction() {
+ /** {@inheritDoc} */
+ public double value(double[] point) {
+ return computeObjectiveValue(point);
+ }
+ };
+
+ final boolean isMinim = getGoalType() == GoalType.MINIMIZE;
+ final Comparator<PointValuePair> comparator
+ = new Comparator<PointValuePair>() {
+ /** {@inheritDoc} */
+ public int compare(final PointValuePair o1,
+ final PointValuePair o2) {
+ final double v1 = o1.getValue();
+ final double v2 = o2.getValue();
+ return isMinim ? Double.compare(v1, v2) : Double.compare(v2, v1);
+ }
+ };
+
+ // Initialize search.
+ simplex.build(getStartPoint());
+ simplex.evaluate(evalFunc, comparator);
+
+ PointValuePair[] previous = null;
+ int iteration = 0;
+ final ConvergenceChecker<PointValuePair> checker = getConvergenceChecker();
+ while (true) {
+ if (getIterations() > 0) {
+ boolean converged = true;
+ for (int i = 0; i < simplex.getSize(); i++) {
+ PointValuePair prev = previous[i];
+ converged = converged &&
+ checker.converged(iteration, prev, simplex.getPoint(i));
+ }
+ if (converged) {
+ // We have found an optimum.
+ return simplex.getPoint(0);
+ }
+ }
+
+ // We still need to search.
+ previous = simplex.getPoints();
+ simplex.iterate(evalFunc, comparator);
+
+ incrementIterationCount();
+ }
+ }
+
+ /**
+ * Scans the list of (required and optional) optimization data that
+ * characterize the problem.
+ *
+ * @param optData Optimization data.
+ * The following data will be looked for:
+ * <ul>
+ * <li>{@link AbstractSimplex}</li>
+ * </ul>
+ */
+ @Override
+ protected void parseOptimizationData(OptimizationData... optData) {
+ // Allow base class to register its own data.
+ super.parseOptimizationData(optData);
+
+ // The existing values (as set by the previous call) are reused if
+ // not provided in the argument list.
+ for (OptimizationData data : optData) {
+ if (data instanceof AbstractSimplex) {
+ simplex = (AbstractSimplex) data;
+ // If more data must be parsed, this statement _must_ be
+ // changed to "continue".
+ break;
+ }
+ }
+ }
+
+ /**
+ * @throws MathUnsupportedOperationException if bounds were passed to the
+ * {@link #optimize(OptimizationData[]) optimize} method.
+ * @throws NullArgumentException if no initial simplex was passed to the
+ * {@link #optimize(OptimizationData[]) optimize} method.
+ */
+ private void checkParameters() {
+ if (simplex == null) {
+ throw new NullArgumentException();
+ }
+ if (getLowerBound() != null ||
+ getUpperBound() != null) {
+ throw new MathUnsupportedOperationException(LocalizedFormats.CONSTRAINT);
+ }
+ }
+}
diff --git a/src/main/java/org/apache/commons/math3/optim/nonlinear/scalar/noderiv/package-info.java b/src/main/java/org/apache/commons/math3/optim/nonlinear/scalar/noderiv/package-info.java
new file mode 100644
index 0000000..4afeb50
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/optim/nonlinear/scalar/noderiv/package-info.java
@@ -0,0 +1,21 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * This package provides optimization algorithms that do not require derivatives.
+ */
+package org.apache.commons.math3.optim.nonlinear.scalar.noderiv;
diff --git a/src/main/java/org/apache/commons/math3/optim/nonlinear/scalar/package-info.java b/src/main/java/org/apache/commons/math3/optim/nonlinear/scalar/package-info.java
new file mode 100644
index 0000000..d65533a
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/optim/nonlinear/scalar/package-info.java
@@ -0,0 +1,21 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * Algorithms for optimizing a scalar function.
+ */
+package org.apache.commons.math3.optim.nonlinear.scalar;
diff --git a/src/main/java/org/apache/commons/math3/optim/nonlinear/vector/JacobianMultivariateVectorOptimizer.java b/src/main/java/org/apache/commons/math3/optim/nonlinear/vector/JacobianMultivariateVectorOptimizer.java
new file mode 100644
index 0000000..52372c8
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/optim/nonlinear/vector/JacobianMultivariateVectorOptimizer.java
@@ -0,0 +1,116 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math3.optim.nonlinear.vector;
+
+import org.apache.commons.math3.analysis.MultivariateMatrixFunction;
+import org.apache.commons.math3.optim.ConvergenceChecker;
+import org.apache.commons.math3.optim.OptimizationData;
+import org.apache.commons.math3.optim.PointVectorValuePair;
+import org.apache.commons.math3.exception.TooManyEvaluationsException;
+import org.apache.commons.math3.exception.DimensionMismatchException;
+
+/**
+ * Base class for implementing optimizers for multivariate vector
+ * differentiable functions.
+ * It contains boiler-plate code for dealing with Jacobian evaluation.
+ * It assumes that the rows of the Jacobian matrix iterate on the model
+ * functions while the columns iterate on the parameters; thus, the numbers
+ * of rows is equal to the dimension of the {@link Target} while the
+ * number of columns is equal to the dimension of the
+ * {@link org.apache.commons.math3.optim.InitialGuess InitialGuess}.
+ *
+ * @since 3.1
+ * @deprecated All classes and interfaces in this package are deprecated.
+ * The optimizers that were provided here were moved to the
+ * {@link org.apache.commons.math3.fitting.leastsquares} package
+ * (cf. MATH-1008).
+ */
+@Deprecated
+public abstract class JacobianMultivariateVectorOptimizer
+ extends MultivariateVectorOptimizer {
+ /**
+ * Jacobian of the model function.
+ */
+ private MultivariateMatrixFunction jacobian;
+
+ /**
+ * @param checker Convergence checker.
+ */
+ protected JacobianMultivariateVectorOptimizer(ConvergenceChecker<PointVectorValuePair> checker) {
+ super(checker);
+ }
+
+ /**
+ * Computes the Jacobian matrix.
+ *
+ * @param params Point at which the Jacobian must be evaluated.
+ * @return the Jacobian at the specified point.
+ */
+ protected double[][] computeJacobian(final double[] params) {
+ return jacobian.value(params);
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @param optData Optimization data. In addition to those documented in
+ * {@link MultivariateVectorOptimizer#optimize(OptimizationData...)}
+ * MultivariateOptimizer}, this method will register the following data:
+ * <ul>
+ * <li>{@link ModelFunctionJacobian}</li>
+ * </ul>
+ * @return {@inheritDoc}
+ * @throws TooManyEvaluationsException if the maximal number of
+ * evaluations is exceeded.
+ * @throws DimensionMismatchException if the initial guess, target, and weight
+ * arguments have inconsistent dimensions.
+ */
+ @Override
+ public PointVectorValuePair optimize(OptimizationData... optData)
+ throws TooManyEvaluationsException,
+ DimensionMismatchException {
+ // Set up base class and perform computation.
+ return super.optimize(optData);
+ }
+
+ /**
+ * Scans the list of (required and optional) optimization data that
+ * characterize the problem.
+ *
+ * @param optData Optimization data.
+ * The following data will be looked for:
+ * <ul>
+ * <li>{@link ModelFunctionJacobian}</li>
+ * </ul>
+ */
+ @Override
+ protected void parseOptimizationData(OptimizationData... optData) {
+ // Allow base class to register its own data.
+ super.parseOptimizationData(optData);
+
+ // The existing values (as set by the previous call) are reused if
+ // not provided in the argument list.
+ for (OptimizationData data : optData) {
+ if (data instanceof ModelFunctionJacobian) {
+ jacobian = ((ModelFunctionJacobian) data).getModelFunctionJacobian();
+ // If more data must be parsed, this statement _must_ be
+ // changed to "continue".
+ break;
+ }
+ }
+ }
+}
diff --git a/src/main/java/org/apache/commons/math3/optim/nonlinear/vector/ModelFunction.java b/src/main/java/org/apache/commons/math3/optim/nonlinear/vector/ModelFunction.java
new file mode 100644
index 0000000..73de7d6
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/optim/nonlinear/vector/ModelFunction.java
@@ -0,0 +1,51 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math3.optim.nonlinear.vector;
+
+import org.apache.commons.math3.analysis.MultivariateVectorFunction;
+import org.apache.commons.math3.optim.OptimizationData;
+
+/**
+ * Model (vector) function to be optimized.
+ *
+ * @since 3.1
+ * @deprecated All classes and interfaces in this package are deprecated.
+ * The optimizers that were provided here were moved to the
+ * {@link org.apache.commons.math3.fitting.leastsquares} package
+ * (cf. MATH-1008).
+ */
+@Deprecated
+public class ModelFunction implements OptimizationData {
+ /** Function to be optimized. */
+ private final MultivariateVectorFunction model;
+
+ /**
+ * @param m Model function to be optimized.
+ */
+ public ModelFunction(MultivariateVectorFunction m) {
+ model = m;
+ }
+
+ /**
+ * Gets the model function to be optimized.
+ *
+ * @return the model function.
+ */
+ public MultivariateVectorFunction getModelFunction() {
+ return model;
+ }
+}
diff --git a/src/main/java/org/apache/commons/math3/optim/nonlinear/vector/ModelFunctionJacobian.java b/src/main/java/org/apache/commons/math3/optim/nonlinear/vector/ModelFunctionJacobian.java
new file mode 100644
index 0000000..72ea4ae
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/optim/nonlinear/vector/ModelFunctionJacobian.java
@@ -0,0 +1,51 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math3.optim.nonlinear.vector;
+
+import org.apache.commons.math3.analysis.MultivariateMatrixFunction;
+import org.apache.commons.math3.optim.OptimizationData;
+
+/**
+ * Jacobian of the model (vector) function to be optimized.
+ *
+ * @since 3.1
+ * @deprecated All classes and interfaces in this package are deprecated.
+ * The optimizers that were provided here were moved to the
+ * {@link org.apache.commons.math3.fitting.leastsquares} package
+ * (cf. MATH-1008).
+ */
+@Deprecated
+public class ModelFunctionJacobian implements OptimizationData {
+ /** Function to be optimized. */
+ private final MultivariateMatrixFunction jacobian;
+
+ /**
+ * @param j Jacobian of the model function to be optimized.
+ */
+ public ModelFunctionJacobian(MultivariateMatrixFunction j) {
+ jacobian = j;
+ }
+
+ /**
+ * Gets the Jacobian of the model function to be optimized.
+ *
+ * @return the model function Jacobian.
+ */
+ public MultivariateMatrixFunction getModelFunctionJacobian() {
+ return jacobian;
+ }
+}
diff --git a/src/main/java/org/apache/commons/math3/optim/nonlinear/vector/MultiStartMultivariateVectorOptimizer.java b/src/main/java/org/apache/commons/math3/optim/nonlinear/vector/MultiStartMultivariateVectorOptimizer.java
new file mode 100644
index 0000000..2cebf79
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/optim/nonlinear/vector/MultiStartMultivariateVectorOptimizer.java
@@ -0,0 +1,124 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math3.optim.nonlinear.vector;
+
+import java.util.Collections;
+import java.util.List;
+import java.util.ArrayList;
+import java.util.Comparator;
+import org.apache.commons.math3.exception.NotStrictlyPositiveException;
+import org.apache.commons.math3.exception.NullArgumentException;
+import org.apache.commons.math3.linear.RealMatrix;
+import org.apache.commons.math3.linear.RealVector;
+import org.apache.commons.math3.linear.ArrayRealVector;
+import org.apache.commons.math3.random.RandomVectorGenerator;
+import org.apache.commons.math3.optim.BaseMultiStartMultivariateOptimizer;
+import org.apache.commons.math3.optim.PointVectorValuePair;
+
+/**
+ * Multi-start optimizer for a (vector) model function.
+ *
+ * This class wraps an optimizer in order to use it several times in
+ * turn with different starting points (trying to avoid being trapped
+ * in a local extremum when looking for a global one).
+ *
+ * @since 3.0
+ */
+@Deprecated
+public class MultiStartMultivariateVectorOptimizer
+ extends BaseMultiStartMultivariateOptimizer<PointVectorValuePair> {
+ /** Underlying optimizer. */
+ private final MultivariateVectorOptimizer optimizer;
+ /** Found optima. */
+ private final List<PointVectorValuePair> optima = new ArrayList<PointVectorValuePair>();
+
+ /**
+ * Create a multi-start optimizer from a single-start optimizer.
+ *
+ * @param optimizer Single-start optimizer to wrap.
+ * @param starts Number of starts to perform.
+ * If {@code starts == 1}, the result will be same as if {@code optimizer}
+ * is called directly.
+ * @param generator Random vector generator to use for restarts.
+ * @throws NullArgumentException if {@code optimizer} or {@code generator}
+ * is {@code null}.
+ * @throws NotStrictlyPositiveException if {@code starts < 1}.
+ */
+ public MultiStartMultivariateVectorOptimizer(final MultivariateVectorOptimizer optimizer,
+ final int starts,
+ final RandomVectorGenerator generator)
+ throws NullArgumentException,
+ NotStrictlyPositiveException {
+ super(optimizer, starts, generator);
+ this.optimizer = optimizer;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public PointVectorValuePair[] getOptima() {
+ Collections.sort(optima, getPairComparator());
+ return optima.toArray(new PointVectorValuePair[0]);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ protected void store(PointVectorValuePair optimum) {
+ optima.add(optimum);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ protected void clear() {
+ optima.clear();
+ }
+
+ /**
+ * @return a comparator for sorting the optima.
+ */
+ private Comparator<PointVectorValuePair> getPairComparator() {
+ return new Comparator<PointVectorValuePair>() {
+ /** Observed value to be matched. */
+ private final RealVector target = new ArrayRealVector(optimizer.getTarget(), false);
+ /** Observations weights. */
+ private final RealMatrix weight = optimizer.getWeight();
+
+ /** {@inheritDoc} */
+ public int compare(final PointVectorValuePair o1,
+ final PointVectorValuePair o2) {
+ if (o1 == null) {
+ return (o2 == null) ? 0 : 1;
+ } else if (o2 == null) {
+ return -1;
+ }
+ return Double.compare(weightedResidual(o1),
+ weightedResidual(o2));
+ }
+
+ private double weightedResidual(final PointVectorValuePair pv) {
+ final RealVector v = new ArrayRealVector(pv.getValueRef(), false);
+ final RealVector r = target.subtract(v);
+ return r.dotProduct(weight.operate(r));
+ }
+ };
+ }
+}
diff --git a/src/main/java/org/apache/commons/math3/optim/nonlinear/vector/MultivariateVectorOptimizer.java b/src/main/java/org/apache/commons/math3/optim/nonlinear/vector/MultivariateVectorOptimizer.java
new file mode 100644
index 0000000..c79defa
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/optim/nonlinear/vector/MultivariateVectorOptimizer.java
@@ -0,0 +1,167 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math3.optim.nonlinear.vector;
+
+import org.apache.commons.math3.exception.TooManyEvaluationsException;
+import org.apache.commons.math3.exception.DimensionMismatchException;
+import org.apache.commons.math3.analysis.MultivariateVectorFunction;
+import org.apache.commons.math3.optim.OptimizationData;
+import org.apache.commons.math3.optim.BaseMultivariateOptimizer;
+import org.apache.commons.math3.optim.ConvergenceChecker;
+import org.apache.commons.math3.optim.PointVectorValuePair;
+import org.apache.commons.math3.linear.RealMatrix;
+
+/**
+ * Base class for a multivariate vector function optimizer.
+ *
+ * @since 3.1
+ */
+@Deprecated
+public abstract class MultivariateVectorOptimizer
+ extends BaseMultivariateOptimizer<PointVectorValuePair> {
+ /** Target values for the model function at optimum. */
+ private double[] target;
+ /** Weight matrix. */
+ private RealMatrix weightMatrix;
+ /** Model function. */
+ private MultivariateVectorFunction model;
+
+ /**
+ * @param checker Convergence checker.
+ */
+ protected MultivariateVectorOptimizer(ConvergenceChecker<PointVectorValuePair> checker) {
+ super(checker);
+ }
+
+ /**
+ * Computes the objective function value.
+ * This method <em>must</em> be called by subclasses to enforce the
+ * evaluation counter limit.
+ *
+ * @param params Point at which the objective function must be evaluated.
+ * @return the objective function value at the specified point.
+ * @throws TooManyEvaluationsException if the maximal number of evaluations
+ * (of the model vector function) is exceeded.
+ */
+ protected double[] computeObjectiveValue(double[] params) {
+ super.incrementEvaluationCount();
+ return model.value(params);
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @param optData Optimization data. In addition to those documented in
+ * {@link BaseMultivariateOptimizer#parseOptimizationData(OptimizationData[])
+ * BaseMultivariateOptimizer}, this method will register the following data:
+ * <ul>
+ * <li>{@link Target}</li>
+ * <li>{@link Weight}</li>
+ * <li>{@link ModelFunction}</li>
+ * </ul>
+ * @return {@inheritDoc}
+ * @throws TooManyEvaluationsException if the maximal number of
+ * evaluations is exceeded.
+ * @throws DimensionMismatchException if the initial guess, target, and weight
+ * arguments have inconsistent dimensions.
+ */
+ @Override
+ public PointVectorValuePair optimize(OptimizationData... optData)
+ throws TooManyEvaluationsException,
+ DimensionMismatchException {
+ // Set up base class and perform computation.
+ return super.optimize(optData);
+ }
+
+ /**
+ * Gets the weight matrix of the observations.
+ *
+ * @return the weight matrix.
+ */
+ public RealMatrix getWeight() {
+ return weightMatrix.copy();
+ }
+ /**
+ * Gets the observed values to be matched by the objective vector
+ * function.
+ *
+ * @return the target values.
+ */
+ public double[] getTarget() {
+ return target.clone();
+ }
+
+ /**
+ * Gets the number of observed values.
+ *
+ * @return the length of the target vector.
+ */
+ public int getTargetSize() {
+ return target.length;
+ }
+
+ /**
+ * Scans the list of (required and optional) optimization data that
+ * characterize the problem.
+ *
+ * @param optData Optimization data. The following data will be looked for:
+ * <ul>
+ * <li>{@link Target}</li>
+ * <li>{@link Weight}</li>
+ * <li>{@link ModelFunction}</li>
+ * </ul>
+ */
+ @Override
+ protected void parseOptimizationData(OptimizationData... optData) {
+ // Allow base class to register its own data.
+ super.parseOptimizationData(optData);
+
+ // The existing values (as set by the previous call) are reused if
+ // not provided in the argument list.
+ for (OptimizationData data : optData) {
+ if (data instanceof ModelFunction) {
+ model = ((ModelFunction) data).getModelFunction();
+ continue;
+ }
+ if (data instanceof Target) {
+ target = ((Target) data).getTarget();
+ continue;
+ }
+ if (data instanceof Weight) {
+ weightMatrix = ((Weight) data).getWeight();
+ continue;
+ }
+ }
+
+ // Check input consistency.
+ checkParameters();
+ }
+
+ /**
+ * Check parameters consistency.
+ *
+ * @throws DimensionMismatchException if {@link #target} and
+ * {@link #weightMatrix} have inconsistent dimensions.
+ */
+ private void checkParameters() {
+ if (target.length != weightMatrix.getColumnDimension()) {
+ throw new DimensionMismatchException(target.length,
+ weightMatrix.getColumnDimension());
+ }
+ }
+}
diff --git a/src/main/java/org/apache/commons/math3/optim/nonlinear/vector/Target.java b/src/main/java/org/apache/commons/math3/optim/nonlinear/vector/Target.java
new file mode 100644
index 0000000..cd387d5
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/optim/nonlinear/vector/Target.java
@@ -0,0 +1,54 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math3.optim.nonlinear.vector;
+
+import org.apache.commons.math3.optim.OptimizationData;
+
+/**
+ * Target of the optimization procedure.
+ * They are the values which the objective vector function must reproduce
+ * When the parameters of the model have been optimized.
+ * <br/>
+ * Immutable class.
+ *
+ * @since 3.1
+ * @deprecated All classes and interfaces in this package are deprecated.
+ * The optimizers that were provided here were moved to the
+ * {@link org.apache.commons.math3.fitting.leastsquares} package
+ * (cf. MATH-1008).
+ */
+@Deprecated
+public class Target implements OptimizationData {
+ /** Target values (of the objective vector function). */
+ private final double[] target;
+
+ /**
+ * @param observations Target values.
+ */
+ public Target(double[] observations) {
+ target = observations.clone();
+ }
+
+ /**
+ * Gets the initial guess.
+ *
+ * @return the initial guess.
+ */
+ public double[] getTarget() {
+ return target.clone();
+ }
+}
diff --git a/src/main/java/org/apache/commons/math3/optim/nonlinear/vector/Weight.java b/src/main/java/org/apache/commons/math3/optim/nonlinear/vector/Weight.java
new file mode 100644
index 0000000..4d51cd7
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/optim/nonlinear/vector/Weight.java
@@ -0,0 +1,71 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math3.optim.nonlinear.vector;
+
+import org.apache.commons.math3.optim.OptimizationData;
+import org.apache.commons.math3.linear.RealMatrix;
+import org.apache.commons.math3.linear.DiagonalMatrix;
+import org.apache.commons.math3.linear.NonSquareMatrixException;
+
+/**
+ * Weight matrix of the residuals between model and observations.
+ * <br/>
+ * Immutable class.
+ *
+ * @since 3.1
+ * @deprecated All classes and interfaces in this package are deprecated.
+ * The optimizers that were provided here were moved to the
+ * {@link org.apache.commons.math3.fitting.leastsquares} package
+ * (cf. MATH-1008).
+ */
+@Deprecated
+public class Weight implements OptimizationData {
+ /** Weight matrix. */
+ private final RealMatrix weightMatrix;
+
+ /**
+ * Creates a diagonal weight matrix.
+ *
+ * @param weight List of the values of the diagonal.
+ */
+ public Weight(double[] weight) {
+ weightMatrix = new DiagonalMatrix(weight);
+ }
+
+ /**
+ * @param weight Weight matrix.
+ * @throws NonSquareMatrixException if the argument is not
+ * a square matrix.
+ */
+ public Weight(RealMatrix weight) {
+ if (weight.getColumnDimension() != weight.getRowDimension()) {
+ throw new NonSquareMatrixException(weight.getColumnDimension(),
+ weight.getRowDimension());
+ }
+
+ weightMatrix = weight.copy();
+ }
+
+ /**
+ * Gets the initial guess.
+ *
+ * @return the initial guess.
+ */
+ public RealMatrix getWeight() {
+ return weightMatrix.copy();
+ }
+}
diff --git a/src/main/java/org/apache/commons/math3/optim/nonlinear/vector/jacobian/AbstractLeastSquaresOptimizer.java b/src/main/java/org/apache/commons/math3/optim/nonlinear/vector/jacobian/AbstractLeastSquaresOptimizer.java
new file mode 100644
index 0000000..67682eb
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/optim/nonlinear/vector/jacobian/AbstractLeastSquaresOptimizer.java
@@ -0,0 +1,281 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math3.optim.nonlinear.vector.jacobian;
+
+import org.apache.commons.math3.exception.DimensionMismatchException;
+import org.apache.commons.math3.exception.TooManyEvaluationsException;
+import org.apache.commons.math3.linear.ArrayRealVector;
+import org.apache.commons.math3.linear.RealMatrix;
+import org.apache.commons.math3.linear.DiagonalMatrix;
+import org.apache.commons.math3.linear.DecompositionSolver;
+import org.apache.commons.math3.linear.MatrixUtils;
+import org.apache.commons.math3.linear.QRDecomposition;
+import org.apache.commons.math3.linear.EigenDecomposition;
+import org.apache.commons.math3.optim.OptimizationData;
+import org.apache.commons.math3.optim.ConvergenceChecker;
+import org.apache.commons.math3.optim.PointVectorValuePair;
+import org.apache.commons.math3.optim.nonlinear.vector.Weight;
+import org.apache.commons.math3.optim.nonlinear.vector.JacobianMultivariateVectorOptimizer;
+import org.apache.commons.math3.util.FastMath;
+
+/**
+ * Base class for implementing least-squares optimizers.
+ * It provides methods for error estimation.
+ *
+ * @since 3.1
+ * @deprecated All classes and interfaces in this package are deprecated.
+ * The optimizers that were provided here were moved to the
+ * {@link org.apache.commons.math3.fitting.leastsquares} package
+ * (cf. MATH-1008).
+ */
+@Deprecated
+public abstract class AbstractLeastSquaresOptimizer
+ extends JacobianMultivariateVectorOptimizer {
+ /** Square-root of the weight matrix. */
+ private RealMatrix weightMatrixSqrt;
+ /** Cost value (square root of the sum of the residuals). */
+ private double cost;
+
+ /**
+ * @param checker Convergence checker.
+ */
+ protected AbstractLeastSquaresOptimizer(ConvergenceChecker<PointVectorValuePair> checker) {
+ super(checker);
+ }
+
+ /**
+ * Computes the weighted Jacobian matrix.
+ *
+ * @param params Model parameters at which to compute the Jacobian.
+ * @return the weighted Jacobian: W<sup>1/2</sup> J.
+ * @throws DimensionMismatchException if the Jacobian dimension does not
+ * match problem dimension.
+ */
+ protected RealMatrix computeWeightedJacobian(double[] params) {
+ return weightMatrixSqrt.multiply(MatrixUtils.createRealMatrix(computeJacobian(params)));
+ }
+
+ /**
+ * Computes the cost.
+ *
+ * @param residuals Residuals.
+ * @return the cost.
+ * @see #computeResiduals(double[])
+ */
+ protected double computeCost(double[] residuals) {
+ final ArrayRealVector r = new ArrayRealVector(residuals);
+ return FastMath.sqrt(r.dotProduct(getWeight().operate(r)));
+ }
+
+ /**
+ * Gets the root-mean-square (RMS) value.
+ *
+ * The RMS the root of the arithmetic mean of the square of all weighted
+ * residuals.
+ * This is related to the criterion that is minimized by the optimizer
+ * as follows: If <em>c</em> if the criterion, and <em>n</em> is the
+ * number of measurements, then the RMS is <em>sqrt (c/n)</em>.
+ *
+ * @return the RMS value.
+ */
+ public double getRMS() {
+ return FastMath.sqrt(getChiSquare() / getTargetSize());
+ }
+
+ /**
+ * Get a Chi-Square-like value assuming the N residuals follow N
+ * distinct normal distributions centered on 0 and whose variances are
+ * the reciprocal of the weights.
+ * @return chi-square value
+ */
+ public double getChiSquare() {
+ return cost * cost;
+ }
+
+ /**
+ * Gets the square-root of the weight matrix.
+ *
+ * @return the square-root of the weight matrix.
+ */
+ public RealMatrix getWeightSquareRoot() {
+ return weightMatrixSqrt.copy();
+ }
+
+ /**
+ * Sets the cost.
+ *
+ * @param cost Cost value.
+ */
+ protected void setCost(double cost) {
+ this.cost = cost;
+ }
+
+ /**
+ * Get the covariance matrix of the optimized parameters.
+ * <br/>
+ * Note that this operation involves the inversion of the
+ * <code>J<sup>T</sup>J</code> matrix, where {@code J} is the
+ * Jacobian matrix.
+ * The {@code threshold} parameter is a way for the caller to specify
+ * that the result of this computation should be considered meaningless,
+ * and thus trigger an exception.
+ *
+ * @param params Model parameters.
+ * @param threshold Singularity threshold.
+ * @return the covariance matrix.
+ * @throws org.apache.commons.math3.linear.SingularMatrixException
+ * if the covariance matrix cannot be computed (singular problem).
+ */
+ public double[][] computeCovariances(double[] params,
+ double threshold) {
+ // Set up the Jacobian.
+ final RealMatrix j = computeWeightedJacobian(params);
+
+ // Compute transpose(J)J.
+ final RealMatrix jTj = j.transpose().multiply(j);
+
+ // Compute the covariances matrix.
+ final DecompositionSolver solver
+ = new QRDecomposition(jTj, threshold).getSolver();
+ return solver.getInverse().getData();
+ }
+
+ /**
+ * Computes an estimate of the standard deviation of the parameters. The
+ * returned values are the square root of the diagonal coefficients of the
+ * covariance matrix, {@code sd(a[i]) ~= sqrt(C[i][i])}, where {@code a[i]}
+ * is the optimized value of the {@code i}-th parameter, and {@code C} is
+ * the covariance matrix.
+ *
+ * @param params Model parameters.
+ * @param covarianceSingularityThreshold Singularity threshold (see
+ * {@link #computeCovariances(double[],double) computeCovariances}).
+ * @return an estimate of the standard deviation of the optimized parameters
+ * @throws org.apache.commons.math3.linear.SingularMatrixException
+ * if the covariance matrix cannot be computed.
+ */
+ public double[] computeSigma(double[] params,
+ double covarianceSingularityThreshold) {
+ final int nC = params.length;
+ final double[] sig = new double[nC];
+ final double[][] cov = computeCovariances(params, covarianceSingularityThreshold);
+ for (int i = 0; i < nC; ++i) {
+ sig[i] = FastMath.sqrt(cov[i][i]);
+ }
+ return sig;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @param optData Optimization data. In addition to those documented in
+ * {@link JacobianMultivariateVectorOptimizer#parseOptimizationData(OptimizationData[])
+ * JacobianMultivariateVectorOptimizer}, this method will register the following data:
+ * <ul>
+ * <li>{@link org.apache.commons.math3.optim.nonlinear.vector.Weight}</li>
+ * </ul>
+ * @return {@inheritDoc}
+ * @throws TooManyEvaluationsException if the maximal number of
+ * evaluations is exceeded.
+ * @throws DimensionMismatchException if the initial guess, target, and weight
+ * arguments have inconsistent dimensions.
+ */
+ @Override
+ public PointVectorValuePair optimize(OptimizationData... optData)
+ throws TooManyEvaluationsException {
+ // Set up base class and perform computation.
+ return super.optimize(optData);
+ }
+
+ /**
+ * Computes the residuals.
+ * The residual is the difference between the observed (target)
+ * values and the model (objective function) value.
+ * There is one residual for each element of the vector-valued
+ * function.
+ *
+ * @param objectiveValue Value of the the objective function. This is
+ * the value returned from a call to
+ * {@link #computeObjectiveValue(double[]) computeObjectiveValue}
+ * (whose array argument contains the model parameters).
+ * @return the residuals.
+ * @throws DimensionMismatchException if {@code params} has a wrong
+ * length.
+ */
+ protected double[] computeResiduals(double[] objectiveValue) {
+ final double[] target = getTarget();
+ if (objectiveValue.length != target.length) {
+ throw new DimensionMismatchException(target.length,
+ objectiveValue.length);
+ }
+
+ final double[] residuals = new double[target.length];
+ for (int i = 0; i < target.length; i++) {
+ residuals[i] = target[i] - objectiveValue[i];
+ }
+
+ return residuals;
+ }
+
+ /**
+ * Scans the list of (required and optional) optimization data that
+ * characterize the problem.
+ * If the weight matrix is specified, the {@link #weightMatrixSqrt}
+ * field is recomputed.
+ *
+ * @param optData Optimization data. The following data will be looked for:
+ * <ul>
+ * <li>{@link Weight}</li>
+ * </ul>
+ */
+ @Override
+ protected void parseOptimizationData(OptimizationData... optData) {
+ // Allow base class to register its own data.
+ super.parseOptimizationData(optData);
+
+ // The existing values (as set by the previous call) are reused if
+ // not provided in the argument list.
+ for (OptimizationData data : optData) {
+ if (data instanceof Weight) {
+ weightMatrixSqrt = squareRoot(((Weight) data).getWeight());
+ // If more data must be parsed, this statement _must_ be
+ // changed to "continue".
+ break;
+ }
+ }
+ }
+
+ /**
+ * Computes the square-root of the weight matrix.
+ *
+ * @param m Symmetric, positive-definite (weight) matrix.
+ * @return the square-root of the weight matrix.
+ */
+ private RealMatrix squareRoot(RealMatrix m) {
+ if (m instanceof DiagonalMatrix) {
+ final int dim = m.getRowDimension();
+ final RealMatrix sqrtM = new DiagonalMatrix(dim);
+ for (int i = 0; i < dim; i++) {
+ sqrtM.setEntry(i, i, FastMath.sqrt(m.getEntry(i, i)));
+ }
+ return sqrtM;
+ } else {
+ final EigenDecomposition dec = new EigenDecomposition(m);
+ return dec.getSquareRoot();
+ }
+ }
+}
diff --git a/src/main/java/org/apache/commons/math3/optim/nonlinear/vector/jacobian/GaussNewtonOptimizer.java b/src/main/java/org/apache/commons/math3/optim/nonlinear/vector/jacobian/GaussNewtonOptimizer.java
new file mode 100644
index 0000000..0668475
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/optim/nonlinear/vector/jacobian/GaussNewtonOptimizer.java
@@ -0,0 +1,183 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math3.optim.nonlinear.vector.jacobian;
+
+import org.apache.commons.math3.exception.ConvergenceException;
+import org.apache.commons.math3.exception.NullArgumentException;
+import org.apache.commons.math3.exception.MathInternalError;
+import org.apache.commons.math3.exception.MathUnsupportedOperationException;
+import org.apache.commons.math3.exception.util.LocalizedFormats;
+import org.apache.commons.math3.linear.ArrayRealVector;
+import org.apache.commons.math3.linear.BlockRealMatrix;
+import org.apache.commons.math3.linear.DecompositionSolver;
+import org.apache.commons.math3.linear.LUDecomposition;
+import org.apache.commons.math3.linear.QRDecomposition;
+import org.apache.commons.math3.linear.RealMatrix;
+import org.apache.commons.math3.linear.SingularMatrixException;
+import org.apache.commons.math3.optim.ConvergenceChecker;
+import org.apache.commons.math3.optim.PointVectorValuePair;
+
+/**
+ * Gauss-Newton least-squares solver.
+ * <br/>
+ * Constraints are not supported: the call to
+ * {@link #optimize(OptimizationData[]) optimize} will throw
+ * {@link MathUnsupportedOperationException} if bounds are passed to it.
+ *
+ * <p>
+ * This class solve a least-square problem by solving the normal equations
+ * of the linearized problem at each iteration. Either LU decomposition or
+ * QR decomposition can be used to solve the normal equations. LU decomposition
+ * is faster but QR decomposition is more robust for difficult problems.
+ * </p>
+ *
+ * @since 2.0
+ * @deprecated All classes and interfaces in this package are deprecated.
+ * The optimizers that were provided here were moved to the
+ * {@link org.apache.commons.math3.fitting.leastsquares} package
+ * (cf. MATH-1008).
+ */
+@Deprecated
+public class GaussNewtonOptimizer extends AbstractLeastSquaresOptimizer {
+ /** Indicator for using LU decomposition. */
+ private final boolean useLU;
+
+ /**
+ * Simple constructor with default settings.
+ * The normal equations will be solved using LU decomposition.
+ *
+ * @param checker Convergence checker.
+ */
+ public GaussNewtonOptimizer(ConvergenceChecker<PointVectorValuePair> checker) {
+ this(true, checker);
+ }
+
+ /**
+ * @param useLU If {@code true}, the normal equations will be solved
+ * using LU decomposition, otherwise they will be solved using QR
+ * decomposition.
+ * @param checker Convergence checker.
+ */
+ public GaussNewtonOptimizer(final boolean useLU,
+ ConvergenceChecker<PointVectorValuePair> checker) {
+ super(checker);
+ this.useLU = useLU;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public PointVectorValuePair doOptimize() {
+ checkParameters();
+
+ final ConvergenceChecker<PointVectorValuePair> checker
+ = getConvergenceChecker();
+
+ // Computation will be useless without a checker (see "for-loop").
+ if (checker == null) {
+ throw new NullArgumentException();
+ }
+
+ final double[] targetValues = getTarget();
+ final int nR = targetValues.length; // Number of observed data.
+
+ final RealMatrix weightMatrix = getWeight();
+ // Diagonal of the weight matrix.
+ final double[] residualsWeights = new double[nR];
+ for (int i = 0; i < nR; i++) {
+ residualsWeights[i] = weightMatrix.getEntry(i, i);
+ }
+
+ final double[] currentPoint = getStartPoint();
+ final int nC = currentPoint.length;
+
+ // iterate until convergence is reached
+ PointVectorValuePair current = null;
+ for (boolean converged = false; !converged;) {
+ incrementIterationCount();
+
+ // evaluate the objective function and its jacobian
+ PointVectorValuePair previous = current;
+ // Value of the objective function at "currentPoint".
+ final double[] currentObjective = computeObjectiveValue(currentPoint);
+ final double[] currentResiduals = computeResiduals(currentObjective);
+ final RealMatrix weightedJacobian = computeWeightedJacobian(currentPoint);
+ current = new PointVectorValuePair(currentPoint, currentObjective);
+
+ // build the linear problem
+ final double[] b = new double[nC];
+ final double[][] a = new double[nC][nC];
+ for (int i = 0; i < nR; ++i) {
+
+ final double[] grad = weightedJacobian.getRow(i);
+ final double weight = residualsWeights[i];
+ final double residual = currentResiduals[i];
+
+ // compute the normal equation
+ final double wr = weight * residual;
+ for (int j = 0; j < nC; ++j) {
+ b[j] += wr * grad[j];
+ }
+
+ // build the contribution matrix for measurement i
+ for (int k = 0; k < nC; ++k) {
+ double[] ak = a[k];
+ double wgk = weight * grad[k];
+ for (int l = 0; l < nC; ++l) {
+ ak[l] += wgk * grad[l];
+ }
+ }
+ }
+
+ // Check convergence.
+ if (previous != null) {
+ converged = checker.converged(getIterations(), previous, current);
+ if (converged) {
+ setCost(computeCost(currentResiduals));
+ return current;
+ }
+ }
+
+ try {
+ // solve the linearized least squares problem
+ RealMatrix mA = new BlockRealMatrix(a);
+ DecompositionSolver solver = useLU ?
+ new LUDecomposition(mA).getSolver() :
+ new QRDecomposition(mA).getSolver();
+ final double[] dX = solver.solve(new ArrayRealVector(b, false)).toArray();
+ // update the estimated parameters
+ for (int i = 0; i < nC; ++i) {
+ currentPoint[i] += dX[i];
+ }
+ } catch (SingularMatrixException e) {
+ throw new ConvergenceException(LocalizedFormats.UNABLE_TO_SOLVE_SINGULAR_PROBLEM);
+ }
+ }
+ // Must never happen.
+ throw new MathInternalError();
+ }
+
+ /**
+ * @throws MathUnsupportedOperationException if bounds were passed to the
+ * {@link #optimize(OptimizationData[]) optimize} method.
+ */
+ private void checkParameters() {
+ if (getLowerBound() != null ||
+ getUpperBound() != null) {
+ throw new MathUnsupportedOperationException(LocalizedFormats.CONSTRAINT);
+ }
+ }
+}
diff --git a/src/main/java/org/apache/commons/math3/optim/nonlinear/vector/jacobian/LevenbergMarquardtOptimizer.java b/src/main/java/org/apache/commons/math3/optim/nonlinear/vector/jacobian/LevenbergMarquardtOptimizer.java
new file mode 100644
index 0000000..05be0d0
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/optim/nonlinear/vector/jacobian/LevenbergMarquardtOptimizer.java
@@ -0,0 +1,961 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math3.optim.nonlinear.vector.jacobian;
+
+import java.util.Arrays;
+import org.apache.commons.math3.exception.ConvergenceException;
+import org.apache.commons.math3.exception.MathUnsupportedOperationException;
+import org.apache.commons.math3.exception.util.LocalizedFormats;
+import org.apache.commons.math3.optim.PointVectorValuePair;
+import org.apache.commons.math3.optim.ConvergenceChecker;
+import org.apache.commons.math3.linear.RealMatrix;
+import org.apache.commons.math3.util.Precision;
+import org.apache.commons.math3.util.FastMath;
+
+
+/**
+ * This class solves a least-squares problem using the Levenberg-Marquardt
+ * algorithm.
+ * <br/>
+ * Constraints are not supported: the call to
+ * {@link #optimize(OptimizationData[]) optimize} will throw
+ * {@link MathUnsupportedOperationException} if bounds are passed to it.
+ *
+ * <p>This implementation <em>should</em> work even for over-determined systems
+ * (i.e. systems having more point than equations). Over-determined systems
+ * are solved by ignoring the point which have the smallest impact according
+ * to their jacobian column norm. Only the rank of the matrix and some loop bounds
+ * are changed to implement this.</p>
+ *
+ * <p>The resolution engine is a simple translation of the MINPACK <a
+ * href="http://www.netlib.org/minpack/lmder.f">lmder</a> routine with minor
+ * changes. The changes include the over-determined resolution, the use of
+ * inherited convergence checker and the Q.R. decomposition which has been
+ * rewritten following the algorithm described in the
+ * P. Lascaux and R. Theodor book <i>Analyse num&eacute;rique matricielle
+ * appliqu&eacute;e &agrave; l'art de l'ing&eacute;nieur</i>, Masson 1986.</p>
+ * <p>The authors of the original fortran version are:
+ * <ul>
+ * <li>Argonne National Laboratory. MINPACK project. March 1980</li>
+ * <li>Burton S. Garbow</li>
+ * <li>Kenneth E. Hillstrom</li>
+ * <li>Jorge J. More</li>
+ * </ul>
+ * The redistribution policy for MINPACK is available <a
+ * href="http://www.netlib.org/minpack/disclaimer">here</a>, for convenience, it
+ * is reproduced below.</p>
+ *
+ * <table border="0" width="80%" cellpadding="10" align="center" bgcolor="#E0E0E0">
+ * <tr><td>
+ * Minpack Copyright Notice (1999) University of Chicago.
+ * All rights reserved
+ * </td></tr>
+ * <tr><td>
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * <ol>
+ * <li>Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.</li>
+ * <li>Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided
+ * with the distribution.</li>
+ * <li>The end-user documentation included with the redistribution, if any,
+ * must include the following acknowledgment:
+ * <code>This product includes software developed by the University of
+ * Chicago, as Operator of Argonne National Laboratory.</code>
+ * Alternately, this acknowledgment may appear in the software itself,
+ * if and wherever such third-party acknowledgments normally appear.</li>
+ * <li><strong>WARRANTY DISCLAIMER. THE SOFTWARE IS SUPPLIED "AS IS"
+ * WITHOUT WARRANTY OF ANY KIND. THE COPYRIGHT HOLDER, THE
+ * UNITED STATES, THE UNITED STATES DEPARTMENT OF ENERGY, AND
+ * THEIR EMPLOYEES: (1) DISCLAIM ANY WARRANTIES, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO ANY IMPLIED WARRANTIES
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, TITLE
+ * OR NON-INFRINGEMENT, (2) DO NOT ASSUME ANY LEGAL LIABILITY
+ * OR RESPONSIBILITY FOR THE ACCURACY, COMPLETENESS, OR
+ * USEFULNESS OF THE SOFTWARE, (3) DO NOT REPRESENT THAT USE OF
+ * THE SOFTWARE WOULD NOT INFRINGE PRIVATELY OWNED RIGHTS, (4)
+ * DO NOT WARRANT THAT THE SOFTWARE WILL FUNCTION
+ * UNINTERRUPTED, THAT IT IS ERROR-FREE OR THAT ANY ERRORS WILL
+ * BE CORRECTED.</strong></li>
+ * <li><strong>LIMITATION OF LIABILITY. IN NO EVENT WILL THE COPYRIGHT
+ * HOLDER, THE UNITED STATES, THE UNITED STATES DEPARTMENT OF
+ * ENERGY, OR THEIR EMPLOYEES: BE LIABLE FOR ANY INDIRECT,
+ * INCIDENTAL, CONSEQUENTIAL, SPECIAL OR PUNITIVE DAMAGES OF
+ * ANY KIND OR NATURE, INCLUDING BUT NOT LIMITED TO LOSS OF
+ * PROFITS OR LOSS OF DATA, FOR ANY REASON WHATSOEVER, WHETHER
+ * SUCH LIABILITY IS ASSERTED ON THE BASIS OF CONTRACT, TORT
+ * (INCLUDING NEGLIGENCE OR STRICT LIABILITY), OR OTHERWISE,
+ * EVEN IF ANY OF SAID PARTIES HAS BEEN WARNED OF THE
+ * POSSIBILITY OF SUCH LOSS OR DAMAGES.</strong></li>
+ * <ol></td></tr>
+ * </table>
+ *
+ * @since 2.0
+ * @deprecated All classes and interfaces in this package are deprecated.
+ * The optimizers that were provided here were moved to the
+ * {@link org.apache.commons.math3.fitting.leastsquares} package
+ * (cf. MATH-1008).
+ */
+@Deprecated
+public class LevenbergMarquardtOptimizer
+ extends AbstractLeastSquaresOptimizer {
+ /** Twice the "epsilon machine". */
+ private static final double TWO_EPS = 2 * Precision.EPSILON;
+ /** Number of solved point. */
+ private int solvedCols;
+ /** Diagonal elements of the R matrix in the Q.R. decomposition. */
+ private double[] diagR;
+ /** Norms of the columns of the jacobian matrix. */
+ private double[] jacNorm;
+ /** Coefficients of the Householder transforms vectors. */
+ private double[] beta;
+ /** Columns permutation array. */
+ private int[] permutation;
+ /** Rank of the jacobian matrix. */
+ private int rank;
+ /** Levenberg-Marquardt parameter. */
+ private double lmPar;
+ /** Parameters evolution direction associated with lmPar. */
+ private double[] lmDir;
+ /** Positive input variable used in determining the initial step bound. */
+ private final double initialStepBoundFactor;
+ /** Desired relative error in the sum of squares. */
+ private final double costRelativeTolerance;
+ /** Desired relative error in the approximate solution parameters. */
+ private final double parRelativeTolerance;
+ /** Desired max cosine on the orthogonality between the function vector
+ * and the columns of the jacobian. */
+ private final double orthoTolerance;
+ /** Threshold for QR ranking. */
+ private final double qrRankingThreshold;
+ /** Weighted residuals. */
+ private double[] weightedResidual;
+ /** Weighted Jacobian. */
+ private double[][] weightedJacobian;
+
+ /**
+ * Build an optimizer for least squares problems with default values
+ * for all the tuning parameters (see the {@link
+ * #LevenbergMarquardtOptimizer(double,double,double,double,double)
+ * other contructor}.
+ * The default values for the algorithm settings are:
+ * <ul>
+ * <li>Initial step bound factor: 100</li>
+ * <li>Cost relative tolerance: 1e-10</li>
+ * <li>Parameters relative tolerance: 1e-10</li>
+ * <li>Orthogonality tolerance: 1e-10</li>
+ * <li>QR ranking threshold: {@link Precision#SAFE_MIN}</li>
+ * </ul>
+ */
+ public LevenbergMarquardtOptimizer() {
+ this(100, 1e-10, 1e-10, 1e-10, Precision.SAFE_MIN);
+ }
+
+ /**
+ * Constructor that allows the specification of a custom convergence
+ * checker.
+ * Note that all the usual convergence checks will be <em>disabled</em>.
+ * The default values for the algorithm settings are:
+ * <ul>
+ * <li>Initial step bound factor: 100</li>
+ * <li>Cost relative tolerance: 1e-10</li>
+ * <li>Parameters relative tolerance: 1e-10</li>
+ * <li>Orthogonality tolerance: 1e-10</li>
+ * <li>QR ranking threshold: {@link Precision#SAFE_MIN}</li>
+ * </ul>
+ *
+ * @param checker Convergence checker.
+ */
+ public LevenbergMarquardtOptimizer(ConvergenceChecker<PointVectorValuePair> checker) {
+ this(100, checker, 1e-10, 1e-10, 1e-10, Precision.SAFE_MIN);
+ }
+
+ /**
+ * Constructor that allows the specification of a custom convergence
+ * checker, in addition to the standard ones.
+ *
+ * @param initialStepBoundFactor Positive input variable used in
+ * determining the initial step bound. This bound is set to the
+ * product of initialStepBoundFactor and the euclidean norm of
+ * {@code diag * x} if non-zero, or else to {@code initialStepBoundFactor}
+ * itself. In most cases factor should lie in the interval
+ * {@code (0.1, 100.0)}. {@code 100} is a generally recommended value.
+ * @param checker Convergence checker.
+ * @param costRelativeTolerance Desired relative error in the sum of
+ * squares.
+ * @param parRelativeTolerance Desired relative error in the approximate
+ * solution parameters.
+ * @param orthoTolerance Desired max cosine on the orthogonality between
+ * the function vector and the columns of the Jacobian.
+ * @param threshold Desired threshold for QR ranking. If the squared norm
+ * of a column vector is smaller or equal to this threshold during QR
+ * decomposition, it is considered to be a zero vector and hence the rank
+ * of the matrix is reduced.
+ */
+ public LevenbergMarquardtOptimizer(double initialStepBoundFactor,
+ ConvergenceChecker<PointVectorValuePair> checker,
+ double costRelativeTolerance,
+ double parRelativeTolerance,
+ double orthoTolerance,
+ double threshold) {
+ super(checker);
+ this.initialStepBoundFactor = initialStepBoundFactor;
+ this.costRelativeTolerance = costRelativeTolerance;
+ this.parRelativeTolerance = parRelativeTolerance;
+ this.orthoTolerance = orthoTolerance;
+ this.qrRankingThreshold = threshold;
+ }
+
+ /**
+ * Build an optimizer for least squares problems with default values
+ * for some of the tuning parameters (see the {@link
+ * #LevenbergMarquardtOptimizer(double,double,double,double,double)
+ * other contructor}.
+ * The default values for the algorithm settings are:
+ * <ul>
+ * <li>Initial step bound factor}: 100</li>
+ * <li>QR ranking threshold}: {@link Precision#SAFE_MIN}</li>
+ * </ul>
+ *
+ * @param costRelativeTolerance Desired relative error in the sum of
+ * squares.
+ * @param parRelativeTolerance Desired relative error in the approximate
+ * solution parameters.
+ * @param orthoTolerance Desired max cosine on the orthogonality between
+ * the function vector and the columns of the Jacobian.
+ */
+ public LevenbergMarquardtOptimizer(double costRelativeTolerance,
+ double parRelativeTolerance,
+ double orthoTolerance) {
+ this(100,
+ costRelativeTolerance, parRelativeTolerance, orthoTolerance,
+ Precision.SAFE_MIN);
+ }
+
+ /**
+ * The arguments control the behaviour of the default convergence checking
+ * procedure.
+ * Additional criteria can defined through the setting of a {@link
+ * ConvergenceChecker}.
+ *
+ * @param initialStepBoundFactor Positive input variable used in
+ * determining the initial step bound. This bound is set to the
+ * product of initialStepBoundFactor and the euclidean norm of
+ * {@code diag * x} if non-zero, or else to {@code initialStepBoundFactor}
+ * itself. In most cases factor should lie in the interval
+ * {@code (0.1, 100.0)}. {@code 100} is a generally recommended value.
+ * @param costRelativeTolerance Desired relative error in the sum of
+ * squares.
+ * @param parRelativeTolerance Desired relative error in the approximate
+ * solution parameters.
+ * @param orthoTolerance Desired max cosine on the orthogonality between
+ * the function vector and the columns of the Jacobian.
+ * @param threshold Desired threshold for QR ranking. If the squared norm
+ * of a column vector is smaller or equal to this threshold during QR
+ * decomposition, it is considered to be a zero vector and hence the rank
+ * of the matrix is reduced.
+ */
+ public LevenbergMarquardtOptimizer(double initialStepBoundFactor,
+ double costRelativeTolerance,
+ double parRelativeTolerance,
+ double orthoTolerance,
+ double threshold) {
+ super(null); // No custom convergence criterion.
+ this.initialStepBoundFactor = initialStepBoundFactor;
+ this.costRelativeTolerance = costRelativeTolerance;
+ this.parRelativeTolerance = parRelativeTolerance;
+ this.orthoTolerance = orthoTolerance;
+ this.qrRankingThreshold = threshold;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ protected PointVectorValuePair doOptimize() {
+ checkParameters();
+
+ final int nR = getTarget().length; // Number of observed data.
+ final double[] currentPoint = getStartPoint();
+ final int nC = currentPoint.length; // Number of parameters.
+
+ // arrays shared with the other private methods
+ solvedCols = FastMath.min(nR, nC);
+ diagR = new double[nC];
+ jacNorm = new double[nC];
+ beta = new double[nC];
+ permutation = new int[nC];
+ lmDir = new double[nC];
+
+ // local point
+ double delta = 0;
+ double xNorm = 0;
+ double[] diag = new double[nC];
+ double[] oldX = new double[nC];
+ double[] oldRes = new double[nR];
+ double[] oldObj = new double[nR];
+ double[] qtf = new double[nR];
+ double[] work1 = new double[nC];
+ double[] work2 = new double[nC];
+ double[] work3 = new double[nC];
+
+ final RealMatrix weightMatrixSqrt = getWeightSquareRoot();
+
+ // Evaluate the function at the starting point and calculate its norm.
+ double[] currentObjective = computeObjectiveValue(currentPoint);
+ double[] currentResiduals = computeResiduals(currentObjective);
+ PointVectorValuePair current = new PointVectorValuePair(currentPoint, currentObjective);
+ double currentCost = computeCost(currentResiduals);
+
+ // Outer loop.
+ lmPar = 0;
+ boolean firstIteration = true;
+ final ConvergenceChecker<PointVectorValuePair> checker = getConvergenceChecker();
+ while (true) {
+ incrementIterationCount();
+
+ final PointVectorValuePair previous = current;
+
+ // QR decomposition of the jacobian matrix
+ qrDecomposition(computeWeightedJacobian(currentPoint));
+
+ weightedResidual = weightMatrixSqrt.operate(currentResiduals);
+ for (int i = 0; i < nR; i++) {
+ qtf[i] = weightedResidual[i];
+ }
+
+ // compute Qt.res
+ qTy(qtf);
+
+ // now we don't need Q anymore,
+ // so let jacobian contain the R matrix with its diagonal elements
+ for (int k = 0; k < solvedCols; ++k) {
+ int pk = permutation[k];
+ weightedJacobian[k][pk] = diagR[pk];
+ }
+
+ if (firstIteration) {
+ // scale the point according to the norms of the columns
+ // of the initial jacobian
+ xNorm = 0;
+ for (int k = 0; k < nC; ++k) {
+ double dk = jacNorm[k];
+ if (dk == 0) {
+ dk = 1.0;
+ }
+ double xk = dk * currentPoint[k];
+ xNorm += xk * xk;
+ diag[k] = dk;
+ }
+ xNorm = FastMath.sqrt(xNorm);
+
+ // initialize the step bound delta
+ delta = (xNorm == 0) ? initialStepBoundFactor : (initialStepBoundFactor * xNorm);
+ }
+
+ // check orthogonality between function vector and jacobian columns
+ double maxCosine = 0;
+ if (currentCost != 0) {
+ for (int j = 0; j < solvedCols; ++j) {
+ int pj = permutation[j];
+ double s = jacNorm[pj];
+ if (s != 0) {
+ double sum = 0;
+ for (int i = 0; i <= j; ++i) {
+ sum += weightedJacobian[i][pj] * qtf[i];
+ }
+ maxCosine = FastMath.max(maxCosine, FastMath.abs(sum) / (s * currentCost));
+ }
+ }
+ }
+ if (maxCosine <= orthoTolerance) {
+ // Convergence has been reached.
+ setCost(currentCost);
+ return current;
+ }
+
+ // rescale if necessary
+ for (int j = 0; j < nC; ++j) {
+ diag[j] = FastMath.max(diag[j], jacNorm[j]);
+ }
+
+ // Inner loop.
+ for (double ratio = 0; ratio < 1.0e-4;) {
+
+ // save the state
+ for (int j = 0; j < solvedCols; ++j) {
+ int pj = permutation[j];
+ oldX[pj] = currentPoint[pj];
+ }
+ final double previousCost = currentCost;
+ double[] tmpVec = weightedResidual;
+ weightedResidual = oldRes;
+ oldRes = tmpVec;
+ tmpVec = currentObjective;
+ currentObjective = oldObj;
+ oldObj = tmpVec;
+
+ // determine the Levenberg-Marquardt parameter
+ determineLMParameter(qtf, delta, diag, work1, work2, work3);
+
+ // compute the new point and the norm of the evolution direction
+ double lmNorm = 0;
+ for (int j = 0; j < solvedCols; ++j) {
+ int pj = permutation[j];
+ lmDir[pj] = -lmDir[pj];
+ currentPoint[pj] = oldX[pj] + lmDir[pj];
+ double s = diag[pj] * lmDir[pj];
+ lmNorm += s * s;
+ }
+ lmNorm = FastMath.sqrt(lmNorm);
+ // on the first iteration, adjust the initial step bound.
+ if (firstIteration) {
+ delta = FastMath.min(delta, lmNorm);
+ }
+
+ // Evaluate the function at x + p and calculate its norm.
+ currentObjective = computeObjectiveValue(currentPoint);
+ currentResiduals = computeResiduals(currentObjective);
+ current = new PointVectorValuePair(currentPoint, currentObjective);
+ currentCost = computeCost(currentResiduals);
+
+ // compute the scaled actual reduction
+ double actRed = -1.0;
+ if (0.1 * currentCost < previousCost) {
+ double r = currentCost / previousCost;
+ actRed = 1.0 - r * r;
+ }
+
+ // compute the scaled predicted reduction
+ // and the scaled directional derivative
+ for (int j = 0; j < solvedCols; ++j) {
+ int pj = permutation[j];
+ double dirJ = lmDir[pj];
+ work1[j] = 0;
+ for (int i = 0; i <= j; ++i) {
+ work1[i] += weightedJacobian[i][pj] * dirJ;
+ }
+ }
+ double coeff1 = 0;
+ for (int j = 0; j < solvedCols; ++j) {
+ coeff1 += work1[j] * work1[j];
+ }
+ double pc2 = previousCost * previousCost;
+ coeff1 /= pc2;
+ double coeff2 = lmPar * lmNorm * lmNorm / pc2;
+ double preRed = coeff1 + 2 * coeff2;
+ double dirDer = -(coeff1 + coeff2);
+
+ // ratio of the actual to the predicted reduction
+ ratio = (preRed == 0) ? 0 : (actRed / preRed);
+
+ // update the step bound
+ if (ratio <= 0.25) {
+ double tmp =
+ (actRed < 0) ? (0.5 * dirDer / (dirDer + 0.5 * actRed)) : 0.5;
+ if ((0.1 * currentCost >= previousCost) || (tmp < 0.1)) {
+ tmp = 0.1;
+ }
+ delta = tmp * FastMath.min(delta, 10.0 * lmNorm);
+ lmPar /= tmp;
+ } else if ((lmPar == 0) || (ratio >= 0.75)) {
+ delta = 2 * lmNorm;
+ lmPar *= 0.5;
+ }
+
+ // test for successful iteration.
+ if (ratio >= 1.0e-4) {
+ // successful iteration, update the norm
+ firstIteration = false;
+ xNorm = 0;
+ for (int k = 0; k < nC; ++k) {
+ double xK = diag[k] * currentPoint[k];
+ xNorm += xK * xK;
+ }
+ xNorm = FastMath.sqrt(xNorm);
+
+ // tests for convergence.
+ if (checker != null && checker.converged(getIterations(), previous, current)) {
+ setCost(currentCost);
+ return current;
+ }
+ } else {
+ // failed iteration, reset the previous values
+ currentCost = previousCost;
+ for (int j = 0; j < solvedCols; ++j) {
+ int pj = permutation[j];
+ currentPoint[pj] = oldX[pj];
+ }
+ tmpVec = weightedResidual;
+ weightedResidual = oldRes;
+ oldRes = tmpVec;
+ tmpVec = currentObjective;
+ currentObjective = oldObj;
+ oldObj = tmpVec;
+ // Reset "current" to previous values.
+ current = new PointVectorValuePair(currentPoint, currentObjective);
+ }
+
+ // Default convergence criteria.
+ if ((FastMath.abs(actRed) <= costRelativeTolerance &&
+ preRed <= costRelativeTolerance &&
+ ratio <= 2.0) ||
+ delta <= parRelativeTolerance * xNorm) {
+ setCost(currentCost);
+ return current;
+ }
+
+ // tests for termination and stringent tolerances
+ if (FastMath.abs(actRed) <= TWO_EPS &&
+ preRed <= TWO_EPS &&
+ ratio <= 2.0) {
+ throw new ConvergenceException(LocalizedFormats.TOO_SMALL_COST_RELATIVE_TOLERANCE,
+ costRelativeTolerance);
+ } else if (delta <= TWO_EPS * xNorm) {
+ throw new ConvergenceException(LocalizedFormats.TOO_SMALL_PARAMETERS_RELATIVE_TOLERANCE,
+ parRelativeTolerance);
+ } else if (maxCosine <= TWO_EPS) {
+ throw new ConvergenceException(LocalizedFormats.TOO_SMALL_ORTHOGONALITY_TOLERANCE,
+ orthoTolerance);
+ }
+ }
+ }
+ }
+
+ /**
+ * Determine the Levenberg-Marquardt parameter.
+ * <p>This implementation is a translation in Java of the MINPACK
+ * <a href="http://www.netlib.org/minpack/lmpar.f">lmpar</a>
+ * routine.</p>
+ * <p>This method sets the lmPar and lmDir attributes.</p>
+ * <p>The authors of the original fortran function are:</p>
+ * <ul>
+ * <li>Argonne National Laboratory. MINPACK project. March 1980</li>
+ * <li>Burton S. Garbow</li>
+ * <li>Kenneth E. Hillstrom</li>
+ * <li>Jorge J. More</li>
+ * </ul>
+ * <p>Luc Maisonobe did the Java translation.</p>
+ *
+ * @param qy array containing qTy
+ * @param delta upper bound on the euclidean norm of diagR * lmDir
+ * @param diag diagonal matrix
+ * @param work1 work array
+ * @param work2 work array
+ * @param work3 work array
+ */
+ private void determineLMParameter(double[] qy, double delta, double[] diag,
+ double[] work1, double[] work2, double[] work3) {
+ final int nC = weightedJacobian[0].length;
+
+ // compute and store in x the gauss-newton direction, if the
+ // jacobian is rank-deficient, obtain a least squares solution
+ for (int j = 0; j < rank; ++j) {
+ lmDir[permutation[j]] = qy[j];
+ }
+ for (int j = rank; j < nC; ++j) {
+ lmDir[permutation[j]] = 0;
+ }
+ for (int k = rank - 1; k >= 0; --k) {
+ int pk = permutation[k];
+ double ypk = lmDir[pk] / diagR[pk];
+ for (int i = 0; i < k; ++i) {
+ lmDir[permutation[i]] -= ypk * weightedJacobian[i][pk];
+ }
+ lmDir[pk] = ypk;
+ }
+
+ // evaluate the function at the origin, and test
+ // for acceptance of the Gauss-Newton direction
+ double dxNorm = 0;
+ for (int j = 0; j < solvedCols; ++j) {
+ int pj = permutation[j];
+ double s = diag[pj] * lmDir[pj];
+ work1[pj] = s;
+ dxNorm += s * s;
+ }
+ dxNorm = FastMath.sqrt(dxNorm);
+ double fp = dxNorm - delta;
+ if (fp <= 0.1 * delta) {
+ lmPar = 0;
+ return;
+ }
+
+ // if the jacobian is not rank deficient, the Newton step provides
+ // a lower bound, parl, for the zero of the function,
+ // otherwise set this bound to zero
+ double sum2;
+ double parl = 0;
+ if (rank == solvedCols) {
+ for (int j = 0; j < solvedCols; ++j) {
+ int pj = permutation[j];
+ work1[pj] *= diag[pj] / dxNorm;
+ }
+ sum2 = 0;
+ for (int j = 0; j < solvedCols; ++j) {
+ int pj = permutation[j];
+ double sum = 0;
+ for (int i = 0; i < j; ++i) {
+ sum += weightedJacobian[i][pj] * work1[permutation[i]];
+ }
+ double s = (work1[pj] - sum) / diagR[pj];
+ work1[pj] = s;
+ sum2 += s * s;
+ }
+ parl = fp / (delta * sum2);
+ }
+
+ // calculate an upper bound, paru, for the zero of the function
+ sum2 = 0;
+ for (int j = 0; j < solvedCols; ++j) {
+ int pj = permutation[j];
+ double sum = 0;
+ for (int i = 0; i <= j; ++i) {
+ sum += weightedJacobian[i][pj] * qy[i];
+ }
+ sum /= diag[pj];
+ sum2 += sum * sum;
+ }
+ double gNorm = FastMath.sqrt(sum2);
+ double paru = gNorm / delta;
+ if (paru == 0) {
+ paru = Precision.SAFE_MIN / FastMath.min(delta, 0.1);
+ }
+
+ // if the input par lies outside of the interval (parl,paru),
+ // set par to the closer endpoint
+ lmPar = FastMath.min(paru, FastMath.max(lmPar, parl));
+ if (lmPar == 0) {
+ lmPar = gNorm / dxNorm;
+ }
+
+ for (int countdown = 10; countdown >= 0; --countdown) {
+
+ // evaluate the function at the current value of lmPar
+ if (lmPar == 0) {
+ lmPar = FastMath.max(Precision.SAFE_MIN, 0.001 * paru);
+ }
+ double sPar = FastMath.sqrt(lmPar);
+ for (int j = 0; j < solvedCols; ++j) {
+ int pj = permutation[j];
+ work1[pj] = sPar * diag[pj];
+ }
+ determineLMDirection(qy, work1, work2, work3);
+
+ dxNorm = 0;
+ for (int j = 0; j < solvedCols; ++j) {
+ int pj = permutation[j];
+ double s = diag[pj] * lmDir[pj];
+ work3[pj] = s;
+ dxNorm += s * s;
+ }
+ dxNorm = FastMath.sqrt(dxNorm);
+ double previousFP = fp;
+ fp = dxNorm - delta;
+
+ // if the function is small enough, accept the current value
+ // of lmPar, also test for the exceptional cases where parl is zero
+ if ((FastMath.abs(fp) <= 0.1 * delta) ||
+ ((parl == 0) && (fp <= previousFP) && (previousFP < 0))) {
+ return;
+ }
+
+ // compute the Newton correction
+ for (int j = 0; j < solvedCols; ++j) {
+ int pj = permutation[j];
+ work1[pj] = work3[pj] * diag[pj] / dxNorm;
+ }
+ for (int j = 0; j < solvedCols; ++j) {
+ int pj = permutation[j];
+ work1[pj] /= work2[j];
+ double tmp = work1[pj];
+ for (int i = j + 1; i < solvedCols; ++i) {
+ work1[permutation[i]] -= weightedJacobian[i][pj] * tmp;
+ }
+ }
+ sum2 = 0;
+ for (int j = 0; j < solvedCols; ++j) {
+ double s = work1[permutation[j]];
+ sum2 += s * s;
+ }
+ double correction = fp / (delta * sum2);
+
+ // depending on the sign of the function, update parl or paru.
+ if (fp > 0) {
+ parl = FastMath.max(parl, lmPar);
+ } else if (fp < 0) {
+ paru = FastMath.min(paru, lmPar);
+ }
+
+ // compute an improved estimate for lmPar
+ lmPar = FastMath.max(parl, lmPar + correction);
+
+ }
+ }
+
+ /**
+ * Solve a*x = b and d*x = 0 in the least squares sense.
+ * <p>This implementation is a translation in Java of the MINPACK
+ * <a href="http://www.netlib.org/minpack/qrsolv.f">qrsolv</a>
+ * routine.</p>
+ * <p>This method sets the lmDir and lmDiag attributes.</p>
+ * <p>The authors of the original fortran function are:</p>
+ * <ul>
+ * <li>Argonne National Laboratory. MINPACK project. March 1980</li>
+ * <li>Burton S. Garbow</li>
+ * <li>Kenneth E. Hillstrom</li>
+ * <li>Jorge J. More</li>
+ * </ul>
+ * <p>Luc Maisonobe did the Java translation.</p>
+ *
+ * @param qy array containing qTy
+ * @param diag diagonal matrix
+ * @param lmDiag diagonal elements associated with lmDir
+ * @param work work array
+ */
+ private void determineLMDirection(double[] qy, double[] diag,
+ double[] lmDiag, double[] work) {
+
+ // copy R and Qty to preserve input and initialize s
+ // in particular, save the diagonal elements of R in lmDir
+ for (int j = 0; j < solvedCols; ++j) {
+ int pj = permutation[j];
+ for (int i = j + 1; i < solvedCols; ++i) {
+ weightedJacobian[i][pj] = weightedJacobian[j][permutation[i]];
+ }
+ lmDir[j] = diagR[pj];
+ work[j] = qy[j];
+ }
+
+ // eliminate the diagonal matrix d using a Givens rotation
+ for (int j = 0; j < solvedCols; ++j) {
+
+ // prepare the row of d to be eliminated, locating the
+ // diagonal element using p from the Q.R. factorization
+ int pj = permutation[j];
+ double dpj = diag[pj];
+ if (dpj != 0) {
+ Arrays.fill(lmDiag, j + 1, lmDiag.length, 0);
+ }
+ lmDiag[j] = dpj;
+
+ // the transformations to eliminate the row of d
+ // modify only a single element of Qty
+ // beyond the first n, which is initially zero.
+ double qtbpj = 0;
+ for (int k = j; k < solvedCols; ++k) {
+ int pk = permutation[k];
+
+ // determine a Givens rotation which eliminates the
+ // appropriate element in the current row of d
+ if (lmDiag[k] != 0) {
+
+ final double sin;
+ final double cos;
+ double rkk = weightedJacobian[k][pk];
+ if (FastMath.abs(rkk) < FastMath.abs(lmDiag[k])) {
+ final double cotan = rkk / lmDiag[k];
+ sin = 1.0 / FastMath.sqrt(1.0 + cotan * cotan);
+ cos = sin * cotan;
+ } else {
+ final double tan = lmDiag[k] / rkk;
+ cos = 1.0 / FastMath.sqrt(1.0 + tan * tan);
+ sin = cos * tan;
+ }
+
+ // compute the modified diagonal element of R and
+ // the modified element of (Qty,0)
+ weightedJacobian[k][pk] = cos * rkk + sin * lmDiag[k];
+ final double temp = cos * work[k] + sin * qtbpj;
+ qtbpj = -sin * work[k] + cos * qtbpj;
+ work[k] = temp;
+
+ // accumulate the tranformation in the row of s
+ for (int i = k + 1; i < solvedCols; ++i) {
+ double rik = weightedJacobian[i][pk];
+ final double temp2 = cos * rik + sin * lmDiag[i];
+ lmDiag[i] = -sin * rik + cos * lmDiag[i];
+ weightedJacobian[i][pk] = temp2;
+ }
+ }
+ }
+
+ // store the diagonal element of s and restore
+ // the corresponding diagonal element of R
+ lmDiag[j] = weightedJacobian[j][permutation[j]];
+ weightedJacobian[j][permutation[j]] = lmDir[j];
+ }
+
+ // solve the triangular system for z, if the system is
+ // singular, then obtain a least squares solution
+ int nSing = solvedCols;
+ for (int j = 0; j < solvedCols; ++j) {
+ if ((lmDiag[j] == 0) && (nSing == solvedCols)) {
+ nSing = j;
+ }
+ if (nSing < solvedCols) {
+ work[j] = 0;
+ }
+ }
+ if (nSing > 0) {
+ for (int j = nSing - 1; j >= 0; --j) {
+ int pj = permutation[j];
+ double sum = 0;
+ for (int i = j + 1; i < nSing; ++i) {
+ sum += weightedJacobian[i][pj] * work[i];
+ }
+ work[j] = (work[j] - sum) / lmDiag[j];
+ }
+ }
+
+ // permute the components of z back to components of lmDir
+ for (int j = 0; j < lmDir.length; ++j) {
+ lmDir[permutation[j]] = work[j];
+ }
+ }
+
+ /**
+ * Decompose a matrix A as A.P = Q.R using Householder transforms.
+ * <p>As suggested in the P. Lascaux and R. Theodor book
+ * <i>Analyse num&eacute;rique matricielle appliqu&eacute;e &agrave;
+ * l'art de l'ing&eacute;nieur</i> (Masson, 1986), instead of representing
+ * the Householder transforms with u<sub>k</sub> unit vectors such that:
+ * <pre>
+ * H<sub>k</sub> = I - 2u<sub>k</sub>.u<sub>k</sub><sup>t</sup>
+ * </pre>
+ * we use <sub>k</sub> non-unit vectors such that:
+ * <pre>
+ * H<sub>k</sub> = I - beta<sub>k</sub>v<sub>k</sub>.v<sub>k</sub><sup>t</sup>
+ * </pre>
+ * where v<sub>k</sub> = a<sub>k</sub> - alpha<sub>k</sub> e<sub>k</sub>.
+ * The beta<sub>k</sub> coefficients are provided upon exit as recomputing
+ * them from the v<sub>k</sub> vectors would be costly.</p>
+ * <p>This decomposition handles rank deficient cases since the tranformations
+ * are performed in non-increasing columns norms order thanks to columns
+ * pivoting. The diagonal elements of the R matrix are therefore also in
+ * non-increasing absolute values order.</p>
+ *
+ * @param jacobian Weighted Jacobian matrix at the current point.
+ * @exception ConvergenceException if the decomposition cannot be performed
+ */
+ private void qrDecomposition(RealMatrix jacobian) throws ConvergenceException {
+ // Code in this class assumes that the weighted Jacobian is -(W^(1/2) J),
+ // hence the multiplication by -1.
+ weightedJacobian = jacobian.scalarMultiply(-1).getData();
+
+ final int nR = weightedJacobian.length;
+ final int nC = weightedJacobian[0].length;
+
+ // initializations
+ for (int k = 0; k < nC; ++k) {
+ permutation[k] = k;
+ double norm2 = 0;
+ for (int i = 0; i < nR; ++i) {
+ double akk = weightedJacobian[i][k];
+ norm2 += akk * akk;
+ }
+ jacNorm[k] = FastMath.sqrt(norm2);
+ }
+
+ // transform the matrix column after column
+ for (int k = 0; k < nC; ++k) {
+
+ // select the column with the greatest norm on active components
+ int nextColumn = -1;
+ double ak2 = Double.NEGATIVE_INFINITY;
+ for (int i = k; i < nC; ++i) {
+ double norm2 = 0;
+ for (int j = k; j < nR; ++j) {
+ double aki = weightedJacobian[j][permutation[i]];
+ norm2 += aki * aki;
+ }
+ if (Double.isInfinite(norm2) || Double.isNaN(norm2)) {
+ throw new ConvergenceException(LocalizedFormats.UNABLE_TO_PERFORM_QR_DECOMPOSITION_ON_JACOBIAN,
+ nR, nC);
+ }
+ if (norm2 > ak2) {
+ nextColumn = i;
+ ak2 = norm2;
+ }
+ }
+ if (ak2 <= qrRankingThreshold) {
+ rank = k;
+ return;
+ }
+ int pk = permutation[nextColumn];
+ permutation[nextColumn] = permutation[k];
+ permutation[k] = pk;
+
+ // choose alpha such that Hk.u = alpha ek
+ double akk = weightedJacobian[k][pk];
+ double alpha = (akk > 0) ? -FastMath.sqrt(ak2) : FastMath.sqrt(ak2);
+ double betak = 1.0 / (ak2 - akk * alpha);
+ beta[pk] = betak;
+
+ // transform the current column
+ diagR[pk] = alpha;
+ weightedJacobian[k][pk] -= alpha;
+
+ // transform the remaining columns
+ for (int dk = nC - 1 - k; dk > 0; --dk) {
+ double gamma = 0;
+ for (int j = k; j < nR; ++j) {
+ gamma += weightedJacobian[j][pk] * weightedJacobian[j][permutation[k + dk]];
+ }
+ gamma *= betak;
+ for (int j = k; j < nR; ++j) {
+ weightedJacobian[j][permutation[k + dk]] -= gamma * weightedJacobian[j][pk];
+ }
+ }
+ }
+ rank = solvedCols;
+ }
+
+ /**
+ * Compute the product Qt.y for some Q.R. decomposition.
+ *
+ * @param y vector to multiply (will be overwritten with the result)
+ */
+ private void qTy(double[] y) {
+ final int nR = weightedJacobian.length;
+ final int nC = weightedJacobian[0].length;
+
+ for (int k = 0; k < nC; ++k) {
+ int pk = permutation[k];
+ double gamma = 0;
+ for (int i = k; i < nR; ++i) {
+ gamma += weightedJacobian[i][pk] * y[i];
+ }
+ gamma *= beta[pk];
+ for (int i = k; i < nR; ++i) {
+ y[i] -= gamma * weightedJacobian[i][pk];
+ }
+ }
+ }
+
+ /**
+ * @throws MathUnsupportedOperationException if bounds were passed to the
+ * {@link #optimize(OptimizationData[]) optimize} method.
+ */
+ private void checkParameters() {
+ if (getLowerBound() != null ||
+ getUpperBound() != null) {
+ throw new MathUnsupportedOperationException(LocalizedFormats.CONSTRAINT);
+ }
+ }
+}
diff --git a/src/main/java/org/apache/commons/math3/optim/nonlinear/vector/jacobian/package-info.java b/src/main/java/org/apache/commons/math3/optim/nonlinear/vector/jacobian/package-info.java
new file mode 100644
index 0000000..4c844ba
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/optim/nonlinear/vector/jacobian/package-info.java
@@ -0,0 +1,26 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * This package provides optimization algorithms that require derivatives.
+ *
+ * @deprecated All classes and interfaces in this package are deprecated.
+ * The optimizers that were provided here were moved to the
+ * {@link org.apache.commons.math3.fitting.leastsquares} package
+ * (cf. MATH-1008).
+ */
+package org.apache.commons.math3.optim.nonlinear.vector.jacobian;
diff --git a/src/main/java/org/apache/commons/math3/optim/nonlinear/vector/package-info.java b/src/main/java/org/apache/commons/math3/optim/nonlinear/vector/package-info.java
new file mode 100644
index 0000000..439fc3c
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/optim/nonlinear/vector/package-info.java
@@ -0,0 +1,26 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * Algorithms for optimizing a vector function.
+ *
+ * @deprecated All classes and interfaces in this package are deprecated.
+ * The optimizers that were provided here were moved to the
+ * {@link org.apache.commons.math3.fitting.leastsquares} package
+ * (cf. MATH-1008).
+ */
+package org.apache.commons.math3.optim.nonlinear.vector;
diff --git a/src/main/java/org/apache/commons/math3/optim/package-info.java b/src/main/java/org/apache/commons/math3/optim/package-info.java
new file mode 100644
index 0000000..e2f3c9f
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/optim/package-info.java
@@ -0,0 +1,60 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * Generally, optimizers are algorithms that will either {@link
+ * org.apache.commons.math3.optim.nonlinear.scalar.GoalType#MINIMIZE minimize} or {@link
+ * org.apache.commons.math3.optim.nonlinear.scalar.GoalType#MAXIMIZE maximize} a scalar function,
+ * called the {@link org.apache.commons.math3.optim.nonlinear.scalar.ObjectiveFunction <em>objective
+ * function</em>}. <br>
+ * For some scalar objective functions the gradient can be computed (analytically or numerically).
+ * Algorithms that use this knowledge are defined in the {@link
+ * org.apache.commons.math3.optim.nonlinear.scalar.gradient} package. The algorithms that do not
+ * need this additional information are located in the {@link
+ * org.apache.commons.math3.optim.nonlinear.scalar.noderiv} package.
+ *
+ * <p>Some problems are solved more efficiently by algorithms that, instead of an objective
+ * function, need access to a {@link org.apache.commons.math3.optim.nonlinear.vector.ModelFunction
+ * <em>model function</em>}: such a model predicts a set of values which the algorithm tries to
+ * match with a set of given {@link org.apache.commons.math3.optim.nonlinear.vector.Target target
+ * values}. Those algorithms are located in the {@link
+ * org.apache.commons.math3.optim.nonlinear.vector} package. <br>
+ * Algorithms that also require the {@link
+ * org.apache.commons.math3.optim.nonlinear.vector.ModelFunctionJacobian Jacobian matrix of the
+ * model} are located in the {@link org.apache.commons.math3.optim.nonlinear.vector.jacobian}
+ * package. <br>
+ * The {@link org.apache.commons.math3.optim.nonlinear.vector.jacobian.AbstractLeastSquaresOptimizer
+ * non-linear least-squares optimizers} are a specialization of the the latter, that minimize the
+ * distance (called <em>cost</em> or <em>&chi;<sup>2</sup></em>) between model and observations.
+ * <br>
+ * For cases where the Jacobian cannot be provided, a utility class will {@link
+ * org.apache.commons.math3.optim.nonlinear.scalar.LeastSquaresConverter convert} a (vector) model
+ * into a (scalar) objective function.
+ *
+ * <p>This package provides common functionality for the optimization algorithms. Abstract classes
+ * ({@link org.apache.commons.math3.optim.BaseOptimizer} and {@link
+ * org.apache.commons.math3.optim.BaseMultivariateOptimizer}) contain boiler-plate code for storing
+ * {@link org.apache.commons.math3.optim.MaxEval evaluations} and {@link
+ * org.apache.commons.math3.optim.MaxIter iterations} counters and a user-defined {@link
+ * org.apache.commons.math3.optim.ConvergenceChecker convergence checker}.
+ *
+ * <p>For each of the optimizer types, there is a special implementation that wraps an optimizer
+ * instance and provides a "multi-start" feature: it calls the underlying optimizer several times
+ * with different starting points and returns the best optimum found, or all optima if so desired.
+ * This could be useful to avoid being trapped in a local extremum.
+ */
+package org.apache.commons.math3.optim;
diff --git a/src/main/java/org/apache/commons/math3/optim/univariate/BracketFinder.java b/src/main/java/org/apache/commons/math3/optim/univariate/BracketFinder.java
new file mode 100644
index 0000000..6d42c0d
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/optim/univariate/BracketFinder.java
@@ -0,0 +1,290 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math3.optim.univariate;
+
+import org.apache.commons.math3.util.FastMath;
+import org.apache.commons.math3.util.IntegerSequence;
+import org.apache.commons.math3.exception.NotStrictlyPositiveException;
+import org.apache.commons.math3.exception.TooManyEvaluationsException;
+import org.apache.commons.math3.exception.MaxCountExceededException;
+import org.apache.commons.math3.analysis.UnivariateFunction;
+import org.apache.commons.math3.optim.nonlinear.scalar.GoalType;
+
+/**
+ * Provide an interval that brackets a local optimum of a function.
+ * This code is based on a Python implementation (from <em>SciPy</em>,
+ * module {@code optimize.py} v0.5).
+ *
+ * @since 2.2
+ */
+public class BracketFinder {
+ /** Tolerance to avoid division by zero. */
+ private static final double EPS_MIN = 1e-21;
+ /**
+ * Golden section.
+ */
+ private static final double GOLD = 1.618034;
+ /**
+ * Factor for expanding the interval.
+ */
+ private final double growLimit;
+ /**
+ * Counter for function evaluations.
+ */
+ private IntegerSequence.Incrementor evaluations;
+ /**
+ * Lower bound of the bracket.
+ */
+ private double lo;
+ /**
+ * Higher bound of the bracket.
+ */
+ private double hi;
+ /**
+ * Point inside the bracket.
+ */
+ private double mid;
+ /**
+ * Function value at {@link #lo}.
+ */
+ private double fLo;
+ /**
+ * Function value at {@link #hi}.
+ */
+ private double fHi;
+ /**
+ * Function value at {@link #mid}.
+ */
+ private double fMid;
+
+ /**
+ * Constructor with default values {@code 100, 500} (see the
+ * {@link #BracketFinder(double,int) other constructor}).
+ */
+ public BracketFinder() {
+ this(100, 500);
+ }
+
+ /**
+ * Create a bracketing interval finder.
+ *
+ * @param growLimit Expanding factor.
+ * @param maxEvaluations Maximum number of evaluations allowed for finding
+ * a bracketing interval.
+ */
+ public BracketFinder(double growLimit,
+ int maxEvaluations) {
+ if (growLimit <= 0) {
+ throw new NotStrictlyPositiveException(growLimit);
+ }
+ if (maxEvaluations <= 0) {
+ throw new NotStrictlyPositiveException(maxEvaluations);
+ }
+
+ this.growLimit = growLimit;
+ evaluations = IntegerSequence.Incrementor.create().withMaximalCount(maxEvaluations);
+ }
+
+ /**
+ * Search new points that bracket a local optimum of the function.
+ *
+ * @param func Function whose optimum should be bracketed.
+ * @param goal {@link GoalType Goal type}.
+ * @param xA Initial point.
+ * @param xB Initial point.
+ * @throws TooManyEvaluationsException if the maximum number of evaluations
+ * is exceeded.
+ */
+ public void search(UnivariateFunction func,
+ GoalType goal,
+ double xA,
+ double xB) {
+ evaluations = evaluations.withStart(0);
+ final boolean isMinim = goal == GoalType.MINIMIZE;
+
+ double fA = eval(func, xA);
+ double fB = eval(func, xB);
+ if (isMinim ?
+ fA < fB :
+ fA > fB) {
+
+ double tmp = xA;
+ xA = xB;
+ xB = tmp;
+
+ tmp = fA;
+ fA = fB;
+ fB = tmp;
+ }
+
+ double xC = xB + GOLD * (xB - xA);
+ double fC = eval(func, xC);
+
+ while (isMinim ? fC < fB : fC > fB) {
+ double tmp1 = (xB - xA) * (fB - fC);
+ double tmp2 = (xB - xC) * (fB - fA);
+
+ double val = tmp2 - tmp1;
+ double denom = FastMath.abs(val) < EPS_MIN ? 2 * EPS_MIN : 2 * val;
+
+ double w = xB - ((xB - xC) * tmp2 - (xB - xA) * tmp1) / denom;
+ double wLim = xB + growLimit * (xC - xB);
+
+ double fW;
+ if ((w - xC) * (xB - w) > 0) {
+ fW = eval(func, w);
+ if (isMinim ?
+ fW < fC :
+ fW > fC) {
+ xA = xB;
+ xB = w;
+ fA = fB;
+ fB = fW;
+ break;
+ } else if (isMinim ?
+ fW > fB :
+ fW < fB) {
+ xC = w;
+ fC = fW;
+ break;
+ }
+ w = xC + GOLD * (xC - xB);
+ fW = eval(func, w);
+ } else if ((w - wLim) * (wLim - xC) >= 0) {
+ w = wLim;
+ fW = eval(func, w);
+ } else if ((w - wLim) * (xC - w) > 0) {
+ fW = eval(func, w);
+ if (isMinim ?
+ fW < fC :
+ fW > fC) {
+ xB = xC;
+ xC = w;
+ w = xC + GOLD * (xC - xB);
+ fB = fC;
+ fC =fW;
+ fW = eval(func, w);
+ }
+ } else {
+ w = xC + GOLD * (xC - xB);
+ fW = eval(func, w);
+ }
+
+ xA = xB;
+ fA = fB;
+ xB = xC;
+ fB = fC;
+ xC = w;
+ fC = fW;
+ }
+
+ lo = xA;
+ fLo = fA;
+ mid = xB;
+ fMid = fB;
+ hi = xC;
+ fHi = fC;
+
+ if (lo > hi) {
+ double tmp = lo;
+ lo = hi;
+ hi = tmp;
+
+ tmp = fLo;
+ fLo = fHi;
+ fHi = tmp;
+ }
+ }
+
+ /**
+ * @return the number of evalutations.
+ */
+ public int getMaxEvaluations() {
+ return evaluations.getMaximalCount();
+ }
+
+ /**
+ * @return the number of evalutations.
+ */
+ public int getEvaluations() {
+ return evaluations.getCount();
+ }
+
+ /**
+ * @return the lower bound of the bracket.
+ * @see #getFLo()
+ */
+ public double getLo() {
+ return lo;
+ }
+
+ /**
+ * Get function value at {@link #getLo()}.
+ * @return function value at {@link #getLo()}
+ */
+ public double getFLo() {
+ return fLo;
+ }
+
+ /**
+ * @return the higher bound of the bracket.
+ * @see #getFHi()
+ */
+ public double getHi() {
+ return hi;
+ }
+
+ /**
+ * Get function value at {@link #getHi()}.
+ * @return function value at {@link #getHi()}
+ */
+ public double getFHi() {
+ return fHi;
+ }
+
+ /**
+ * @return a point in the middle of the bracket.
+ * @see #getFMid()
+ */
+ public double getMid() {
+ return mid;
+ }
+
+ /**
+ * Get function value at {@link #getMid()}.
+ * @return function value at {@link #getMid()}
+ */
+ public double getFMid() {
+ return fMid;
+ }
+
+ /**
+ * @param f Function.
+ * @param x Argument.
+ * @return {@code f(x)}
+ * @throws TooManyEvaluationsException if the maximal number of evaluations is
+ * exceeded.
+ */
+ private double eval(UnivariateFunction f, double x) {
+ try {
+ evaluations.increment();
+ } catch (MaxCountExceededException e) {
+ throw new TooManyEvaluationsException(e.getMax());
+ }
+ return f.value(x);
+ }
+}
diff --git a/src/main/java/org/apache/commons/math3/optim/univariate/BrentOptimizer.java b/src/main/java/org/apache/commons/math3/optim/univariate/BrentOptimizer.java
new file mode 100644
index 0000000..d783405
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/optim/univariate/BrentOptimizer.java
@@ -0,0 +1,314 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math3.optim.univariate;
+
+import org.apache.commons.math3.util.Precision;
+import org.apache.commons.math3.util.FastMath;
+import org.apache.commons.math3.exception.NumberIsTooSmallException;
+import org.apache.commons.math3.exception.NotStrictlyPositiveException;
+import org.apache.commons.math3.optim.ConvergenceChecker;
+import org.apache.commons.math3.optim.nonlinear.scalar.GoalType;
+
+/**
+ * For a function defined on some interval {@code (lo, hi)}, this class
+ * finds an approximation {@code x} to the point at which the function
+ * attains its minimum.
+ * It implements Richard Brent's algorithm (from his book "Algorithms for
+ * Minimization without Derivatives", p. 79) for finding minima of real
+ * univariate functions.
+ * <br/>
+ * This code is an adaptation, partly based on the Python code from SciPy
+ * (module "optimize.py" v0.5); the original algorithm is also modified
+ * <ul>
+ * <li>to use an initial guess provided by the user,</li>
+ * <li>to ensure that the best point encountered is the one returned.</li>
+ * </ul>
+ *
+ * @since 2.0
+ */
+public class BrentOptimizer extends UnivariateOptimizer {
+ /**
+ * Golden section.
+ */
+ private static final double GOLDEN_SECTION = 0.5 * (3 - FastMath.sqrt(5));
+ /**
+ * Minimum relative tolerance.
+ */
+ private static final double MIN_RELATIVE_TOLERANCE = 2 * FastMath.ulp(1d);
+ /**
+ * Relative threshold.
+ */
+ private final double relativeThreshold;
+ /**
+ * Absolute threshold.
+ */
+ private final double absoluteThreshold;
+
+ /**
+ * The arguments are used implement the original stopping criterion
+ * of Brent's algorithm.
+ * {@code abs} and {@code rel} define a tolerance
+ * {@code tol = rel |x| + abs}. {@code rel} should be no smaller than
+ * <em>2 macheps</em> and preferably not much less than <em>sqrt(macheps)</em>,
+ * where <em>macheps</em> is the relative machine precision. {@code abs} must
+ * be positive.
+ *
+ * @param rel Relative threshold.
+ * @param abs Absolute threshold.
+ * @param checker Additional, user-defined, convergence checking
+ * procedure.
+ * @throws NotStrictlyPositiveException if {@code abs <= 0}.
+ * @throws NumberIsTooSmallException if {@code rel < 2 * Math.ulp(1d)}.
+ */
+ public BrentOptimizer(double rel,
+ double abs,
+ ConvergenceChecker<UnivariatePointValuePair> checker) {
+ super(checker);
+
+ if (rel < MIN_RELATIVE_TOLERANCE) {
+ throw new NumberIsTooSmallException(rel, MIN_RELATIVE_TOLERANCE, true);
+ }
+ if (abs <= 0) {
+ throw new NotStrictlyPositiveException(abs);
+ }
+
+ relativeThreshold = rel;
+ absoluteThreshold = abs;
+ }
+
+ /**
+ * The arguments are used for implementing the original stopping criterion
+ * of Brent's algorithm.
+ * {@code abs} and {@code rel} define a tolerance
+ * {@code tol = rel |x| + abs}. {@code rel} should be no smaller than
+ * <em>2 macheps</em> and preferably not much less than <em>sqrt(macheps)</em>,
+ * where <em>macheps</em> is the relative machine precision. {@code abs} must
+ * be positive.
+ *
+ * @param rel Relative threshold.
+ * @param abs Absolute threshold.
+ * @throws NotStrictlyPositiveException if {@code abs <= 0}.
+ * @throws NumberIsTooSmallException if {@code rel < 2 * Math.ulp(1d)}.
+ */
+ public BrentOptimizer(double rel,
+ double abs) {
+ this(rel, abs, null);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ protected UnivariatePointValuePair doOptimize() {
+ final boolean isMinim = getGoalType() == GoalType.MINIMIZE;
+ final double lo = getMin();
+ final double mid = getStartValue();
+ final double hi = getMax();
+
+ // Optional additional convergence criteria.
+ final ConvergenceChecker<UnivariatePointValuePair> checker
+ = getConvergenceChecker();
+
+ double a;
+ double b;
+ if (lo < hi) {
+ a = lo;
+ b = hi;
+ } else {
+ a = hi;
+ b = lo;
+ }
+
+ double x = mid;
+ double v = x;
+ double w = x;
+ double d = 0;
+ double e = 0;
+ double fx = computeObjectiveValue(x);
+ if (!isMinim) {
+ fx = -fx;
+ }
+ double fv = fx;
+ double fw = fx;
+
+ UnivariatePointValuePair previous = null;
+ UnivariatePointValuePair current
+ = new UnivariatePointValuePair(x, isMinim ? fx : -fx);
+ // Best point encountered so far (which is the initial guess).
+ UnivariatePointValuePair best = current;
+
+ while (true) {
+ final double m = 0.5 * (a + b);
+ final double tol1 = relativeThreshold * FastMath.abs(x) + absoluteThreshold;
+ final double tol2 = 2 * tol1;
+
+ // Default stopping criterion.
+ final boolean stop = FastMath.abs(x - m) <= tol2 - 0.5 * (b - a);
+ if (!stop) {
+ double p = 0;
+ double q = 0;
+ double r = 0;
+ double u = 0;
+
+ if (FastMath.abs(e) > tol1) { // Fit parabola.
+ r = (x - w) * (fx - fv);
+ q = (x - v) * (fx - fw);
+ p = (x - v) * q - (x - w) * r;
+ q = 2 * (q - r);
+
+ if (q > 0) {
+ p = -p;
+ } else {
+ q = -q;
+ }
+
+ r = e;
+ e = d;
+
+ if (p > q * (a - x) &&
+ p < q * (b - x) &&
+ FastMath.abs(p) < FastMath.abs(0.5 * q * r)) {
+ // Parabolic interpolation step.
+ d = p / q;
+ u = x + d;
+
+ // f must not be evaluated too close to a or b.
+ if (u - a < tol2 || b - u < tol2) {
+ if (x <= m) {
+ d = tol1;
+ } else {
+ d = -tol1;
+ }
+ }
+ } else {
+ // Golden section step.
+ if (x < m) {
+ e = b - x;
+ } else {
+ e = a - x;
+ }
+ d = GOLDEN_SECTION * e;
+ }
+ } else {
+ // Golden section step.
+ if (x < m) {
+ e = b - x;
+ } else {
+ e = a - x;
+ }
+ d = GOLDEN_SECTION * e;
+ }
+
+ // Update by at least "tol1".
+ if (FastMath.abs(d) < tol1) {
+ if (d >= 0) {
+ u = x + tol1;
+ } else {
+ u = x - tol1;
+ }
+ } else {
+ u = x + d;
+ }
+
+ double fu = computeObjectiveValue(u);
+ if (!isMinim) {
+ fu = -fu;
+ }
+
+ // User-defined convergence checker.
+ previous = current;
+ current = new UnivariatePointValuePair(u, isMinim ? fu : -fu);
+ best = best(best,
+ best(previous,
+ current,
+ isMinim),
+ isMinim);
+
+ if (checker != null && checker.converged(getIterations(), previous, current)) {
+ return best;
+ }
+
+ // Update a, b, v, w and x.
+ if (fu <= fx) {
+ if (u < x) {
+ b = x;
+ } else {
+ a = x;
+ }
+ v = w;
+ fv = fw;
+ w = x;
+ fw = fx;
+ x = u;
+ fx = fu;
+ } else {
+ if (u < x) {
+ a = u;
+ } else {
+ b = u;
+ }
+ if (fu <= fw ||
+ Precision.equals(w, x)) {
+ v = w;
+ fv = fw;
+ w = u;
+ fw = fu;
+ } else if (fu <= fv ||
+ Precision.equals(v, x) ||
+ Precision.equals(v, w)) {
+ v = u;
+ fv = fu;
+ }
+ }
+ } else { // Default termination (Brent's criterion).
+ return best(best,
+ best(previous,
+ current,
+ isMinim),
+ isMinim);
+ }
+
+ incrementIterationCount();
+ }
+ }
+
+ /**
+ * Selects the best of two points.
+ *
+ * @param a Point and value.
+ * @param b Point and value.
+ * @param isMinim {@code true} if the selected point must be the one with
+ * the lowest value.
+ * @return the best point, or {@code null} if {@code a} and {@code b} are
+ * both {@code null}. When {@code a} and {@code b} have the same function
+ * value, {@code a} is returned.
+ */
+ private UnivariatePointValuePair best(UnivariatePointValuePair a,
+ UnivariatePointValuePair b,
+ boolean isMinim) {
+ if (a == null) {
+ return b;
+ }
+ if (b == null) {
+ return a;
+ }
+
+ if (isMinim) {
+ return a.getValue() <= b.getValue() ? a : b;
+ } else {
+ return a.getValue() >= b.getValue() ? a : b;
+ }
+ }
+}
diff --git a/src/main/java/org/apache/commons/math3/optim/univariate/MultiStartUnivariateOptimizer.java b/src/main/java/org/apache/commons/math3/optim/univariate/MultiStartUnivariateOptimizer.java
new file mode 100644
index 0000000..d12ec97
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/optim/univariate/MultiStartUnivariateOptimizer.java
@@ -0,0 +1,228 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math3.optim.univariate;
+
+import java.util.Arrays;
+import java.util.Comparator;
+import org.apache.commons.math3.exception.MathIllegalStateException;
+import org.apache.commons.math3.exception.NotStrictlyPositiveException;
+import org.apache.commons.math3.exception.util.LocalizedFormats;
+import org.apache.commons.math3.random.RandomGenerator;
+import org.apache.commons.math3.optim.MaxEval;
+import org.apache.commons.math3.optim.nonlinear.scalar.GoalType;
+import org.apache.commons.math3.optim.OptimizationData;
+
+/**
+ * Special implementation of the {@link UnivariateOptimizer} interface
+ * adding multi-start features to an existing optimizer.
+ * <br/>
+ * This class wraps an optimizer in order to use it several times in
+ * turn with different starting points (trying to avoid being trapped
+ * in a local extremum when looking for a global one).
+ *
+ * @since 3.0
+ */
+public class MultiStartUnivariateOptimizer
+ extends UnivariateOptimizer {
+ /** Underlying classical optimizer. */
+ private final UnivariateOptimizer optimizer;
+ /** Number of evaluations already performed for all starts. */
+ private int totalEvaluations;
+ /** Number of starts to go. */
+ private int starts;
+ /** Random generator for multi-start. */
+ private RandomGenerator generator;
+ /** Found optima. */
+ private UnivariatePointValuePair[] optima;
+ /** Optimization data. */
+ private OptimizationData[] optimData;
+ /**
+ * Location in {@link #optimData} where the updated maximum
+ * number of evaluations will be stored.
+ */
+ private int maxEvalIndex = -1;
+ /**
+ * Location in {@link #optimData} where the updated start value
+ * will be stored.
+ */
+ private int searchIntervalIndex = -1;
+
+ /**
+ * Create a multi-start optimizer from a single-start optimizer.
+ *
+ * @param optimizer Single-start optimizer to wrap.
+ * @param starts Number of starts to perform. If {@code starts == 1},
+ * the {@code optimize} methods will return the same solution as
+ * {@code optimizer} would.
+ * @param generator Random generator to use for restarts.
+ * @throws NotStrictlyPositiveException if {@code starts < 1}.
+ */
+ public MultiStartUnivariateOptimizer(final UnivariateOptimizer optimizer,
+ final int starts,
+ final RandomGenerator generator) {
+ super(optimizer.getConvergenceChecker());
+
+ if (starts < 1) {
+ throw new NotStrictlyPositiveException(starts);
+ }
+
+ this.optimizer = optimizer;
+ this.starts = starts;
+ this.generator = generator;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public int getEvaluations() {
+ return totalEvaluations;
+ }
+
+ /**
+ * Gets all the optima found during the last call to {@code optimize}.
+ * The optimizer stores all the optima found during a set of
+ * restarts. The {@code optimize} method returns the best point only.
+ * This method returns all the points found at the end of each starts,
+ * including the best one already returned by the {@code optimize} method.
+ * <br/>
+ * The returned array as one element for each start as specified
+ * in the constructor. It is ordered with the results from the
+ * runs that did converge first, sorted from best to worst
+ * objective value (i.e in ascending order if minimizing and in
+ * descending order if maximizing), followed by {@code null} elements
+ * corresponding to the runs that did not converge. This means all
+ * elements will be {@code null} if the {@code optimize} method did throw
+ * an exception.
+ * This also means that if the first element is not {@code null}, it is
+ * the best point found across all starts.
+ *
+ * @return an array containing the optima.
+ * @throws MathIllegalStateException if {@link #optimize(OptimizationData[])
+ * optimize} has not been called.
+ */
+ public UnivariatePointValuePair[] getOptima() {
+ if (optima == null) {
+ throw new MathIllegalStateException(LocalizedFormats.NO_OPTIMUM_COMPUTED_YET);
+ }
+ return optima.clone();
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @throws MathIllegalStateException if {@code optData} does not contain an
+ * instance of {@link MaxEval} or {@link SearchInterval}.
+ */
+ @Override
+ public UnivariatePointValuePair optimize(OptimizationData... optData) {
+ // Store arguments in order to pass them to the internal optimizer.
+ optimData = optData;
+ // Set up base class and perform computations.
+ return super.optimize(optData);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ protected UnivariatePointValuePair doOptimize() {
+ // Remove all instances of "MaxEval" and "SearchInterval" from the
+ // array that will be passed to the internal optimizer.
+ // The former is to enforce smaller numbers of allowed evaluations
+ // (according to how many have been used up already), and the latter
+ // to impose a different start value for each start.
+ for (int i = 0; i < optimData.length; i++) {
+ if (optimData[i] instanceof MaxEval) {
+ optimData[i] = null;
+ maxEvalIndex = i;
+ continue;
+ }
+ if (optimData[i] instanceof SearchInterval) {
+ optimData[i] = null;
+ searchIntervalIndex = i;
+ continue;
+ }
+ }
+ if (maxEvalIndex == -1) {
+ throw new MathIllegalStateException();
+ }
+ if (searchIntervalIndex == -1) {
+ throw new MathIllegalStateException();
+ }
+
+ RuntimeException lastException = null;
+ optima = new UnivariatePointValuePair[starts];
+ totalEvaluations = 0;
+
+ final int maxEval = getMaxEvaluations();
+ final double min = getMin();
+ final double max = getMax();
+ final double startValue = getStartValue();
+
+ // Multi-start loop.
+ for (int i = 0; i < starts; i++) {
+ // CHECKSTYLE: stop IllegalCatch
+ try {
+ // Decrease number of allowed evaluations.
+ optimData[maxEvalIndex] = new MaxEval(maxEval - totalEvaluations);
+ // New start value.
+ final double s = (i == 0) ?
+ startValue :
+ min + generator.nextDouble() * (max - min);
+ optimData[searchIntervalIndex] = new SearchInterval(min, max, s);
+ // Optimize.
+ optima[i] = optimizer.optimize(optimData);
+ } catch (RuntimeException mue) {
+ lastException = mue;
+ optima[i] = null;
+ }
+ // CHECKSTYLE: resume IllegalCatch
+
+ totalEvaluations += optimizer.getEvaluations();
+ }
+
+ sortPairs(getGoalType());
+
+ if (optima[0] == null) {
+ throw lastException; // Cannot be null if starts >= 1.
+ }
+
+ // Return the point with the best objective function value.
+ return optima[0];
+ }
+
+ /**
+ * Sort the optima from best to worst, followed by {@code null} elements.
+ *
+ * @param goal Goal type.
+ */
+ private void sortPairs(final GoalType goal) {
+ Arrays.sort(optima, new Comparator<UnivariatePointValuePair>() {
+ /** {@inheritDoc} */
+ public int compare(final UnivariatePointValuePair o1,
+ final UnivariatePointValuePair o2) {
+ if (o1 == null) {
+ return (o2 == null) ? 0 : 1;
+ } else if (o2 == null) {
+ return -1;
+ }
+ final double v1 = o1.getValue();
+ final double v2 = o2.getValue();
+ return (goal == GoalType.MINIMIZE) ?
+ Double.compare(v1, v2) : Double.compare(v2, v1);
+ }
+ });
+ }
+}
diff --git a/src/main/java/org/apache/commons/math3/optim/univariate/SearchInterval.java b/src/main/java/org/apache/commons/math3/optim/univariate/SearchInterval.java
new file mode 100644
index 0000000..fa80e64
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/optim/univariate/SearchInterval.java
@@ -0,0 +1,95 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math3.optim.univariate;
+
+import org.apache.commons.math3.optim.OptimizationData;
+import org.apache.commons.math3.exception.NumberIsTooLargeException;
+import org.apache.commons.math3.exception.OutOfRangeException;
+
+/**
+ * Search interval and (optional) start value.
+ * <br/>
+ * Immutable class.
+ *
+ * @since 3.1
+ */
+public class SearchInterval implements OptimizationData {
+ /** Lower bound. */
+ private final double lower;
+ /** Upper bound. */
+ private final double upper;
+ /** Start value. */
+ private final double start;
+
+ /**
+ * @param lo Lower bound.
+ * @param hi Upper bound.
+ * @param init Start value.
+ * @throws NumberIsTooLargeException if {@code lo >= hi}.
+ * @throws OutOfRangeException if {@code init < lo} or {@code init > hi}.
+ */
+ public SearchInterval(double lo,
+ double hi,
+ double init) {
+ if (lo >= hi) {
+ throw new NumberIsTooLargeException(lo, hi, false);
+ }
+ if (init < lo ||
+ init > hi) {
+ throw new OutOfRangeException(init, lo, hi);
+ }
+
+ lower = lo;
+ upper = hi;
+ start = init;
+ }
+
+ /**
+ * @param lo Lower bound.
+ * @param hi Upper bound.
+ * @throws NumberIsTooLargeException if {@code lo >= hi}.
+ */
+ public SearchInterval(double lo,
+ double hi) {
+ this(lo, hi, 0.5 * (lo + hi));
+ }
+
+ /**
+ * Gets the lower bound.
+ *
+ * @return the lower bound.
+ */
+ public double getMin() {
+ return lower;
+ }
+ /**
+ * Gets the upper bound.
+ *
+ * @return the upper bound.
+ */
+ public double getMax() {
+ return upper;
+ }
+ /**
+ * Gets the start value.
+ *
+ * @return the start value.
+ */
+ public double getStartValue() {
+ return start;
+ }
+}
diff --git a/src/main/java/org/apache/commons/math3/optim/univariate/SimpleUnivariateValueChecker.java b/src/main/java/org/apache/commons/math3/optim/univariate/SimpleUnivariateValueChecker.java
new file mode 100644
index 0000000..58cc521
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/optim/univariate/SimpleUnivariateValueChecker.java
@@ -0,0 +1,127 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math3.optim.univariate;
+
+import org.apache.commons.math3.util.FastMath;
+import org.apache.commons.math3.exception.NotStrictlyPositiveException;
+import org.apache.commons.math3.optim.AbstractConvergenceChecker;
+
+/**
+ * Simple implementation of the
+ * {@link org.apache.commons.math3.optimization.ConvergenceChecker} interface
+ * that uses only objective function values.
+ *
+ * Convergence is considered to have been reached if either the relative
+ * difference between the objective function values is smaller than a
+ * threshold or if either the absolute difference between the objective
+ * function values is smaller than another threshold.
+ * <br/>
+ * The {@link #converged(int,UnivariatePointValuePair,UnivariatePointValuePair)
+ * converged} method will also return {@code true} if the number of iterations
+ * has been set (see {@link #SimpleUnivariateValueChecker(double,double,int)
+ * this constructor}).
+ *
+ * @since 3.1
+ */
+public class SimpleUnivariateValueChecker
+ extends AbstractConvergenceChecker<UnivariatePointValuePair> {
+ /**
+ * If {@link #maxIterationCount} is set to this value, the number of
+ * iterations will never cause
+ * {@link #converged(int,UnivariatePointValuePair,UnivariatePointValuePair)}
+ * to return {@code true}.
+ */
+ private static final int ITERATION_CHECK_DISABLED = -1;
+ /**
+ * Number of iterations after which the
+ * {@link #converged(int,UnivariatePointValuePair,UnivariatePointValuePair)}
+ * method will return true (unless the check is disabled).
+ */
+ private final int maxIterationCount;
+
+ /** Build an instance with specified thresholds.
+ *
+ * In order to perform only relative checks, the absolute tolerance
+ * must be set to a negative value. In order to perform only absolute
+ * checks, the relative tolerance must be set to a negative value.
+ *
+ * @param relativeThreshold relative tolerance threshold
+ * @param absoluteThreshold absolute tolerance threshold
+ */
+ public SimpleUnivariateValueChecker(final double relativeThreshold,
+ final double absoluteThreshold) {
+ super(relativeThreshold, absoluteThreshold);
+ maxIterationCount = ITERATION_CHECK_DISABLED;
+ }
+
+ /**
+ * Builds an instance with specified thresholds.
+ *
+ * In order to perform only relative checks, the absolute tolerance
+ * must be set to a negative value. In order to perform only absolute
+ * checks, the relative tolerance must be set to a negative value.
+ *
+ * @param relativeThreshold relative tolerance threshold
+ * @param absoluteThreshold absolute tolerance threshold
+ * @param maxIter Maximum iteration count.
+ * @throws NotStrictlyPositiveException if {@code maxIter <= 0}.
+ *
+ * @since 3.1
+ */
+ public SimpleUnivariateValueChecker(final double relativeThreshold,
+ final double absoluteThreshold,
+ final int maxIter) {
+ super(relativeThreshold, absoluteThreshold);
+
+ if (maxIter <= 0) {
+ throw new NotStrictlyPositiveException(maxIter);
+ }
+ maxIterationCount = maxIter;
+ }
+
+ /**
+ * Check if the optimization algorithm has converged considering the
+ * last two points.
+ * This method may be called several time from the same algorithm
+ * iteration with different points. This can be detected by checking the
+ * iteration number at each call if needed. Each time this method is
+ * called, the previous and current point correspond to points with the
+ * same role at each iteration, so they can be compared. As an example,
+ * simplex-based algorithms call this method for all points of the simplex,
+ * not only for the best or worst ones.
+ *
+ * @param iteration Index of current iteration
+ * @param previous Best point in the previous iteration.
+ * @param current Best point in the current iteration.
+ * @return {@code true} if the algorithm has converged.
+ */
+ @Override
+ public boolean converged(final int iteration,
+ final UnivariatePointValuePair previous,
+ final UnivariatePointValuePair current) {
+ if (maxIterationCount != ITERATION_CHECK_DISABLED && iteration >= maxIterationCount) {
+ return true;
+ }
+
+ final double p = previous.getValue();
+ final double c = current.getValue();
+ final double difference = FastMath.abs(p - c);
+ final double size = FastMath.max(FastMath.abs(p), FastMath.abs(c));
+ return difference <= size * getRelativeThreshold() ||
+ difference <= getAbsoluteThreshold();
+ }
+}
diff --git a/src/main/java/org/apache/commons/math3/optim/univariate/UnivariateObjectiveFunction.java b/src/main/java/org/apache/commons/math3/optim/univariate/UnivariateObjectiveFunction.java
new file mode 100644
index 0000000..ad06d84
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/optim/univariate/UnivariateObjectiveFunction.java
@@ -0,0 +1,46 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math3.optim.univariate;
+
+import org.apache.commons.math3.analysis.UnivariateFunction;
+import org.apache.commons.math3.optim.OptimizationData;
+
+/**
+ * Scalar function to be optimized.
+ *
+ * @since 3.1
+ */
+public class UnivariateObjectiveFunction implements OptimizationData {
+ /** Function to be optimized. */
+ private final UnivariateFunction function;
+
+ /**
+ * @param f Function to be optimized.
+ */
+ public UnivariateObjectiveFunction(UnivariateFunction f) {
+ function = f;
+ }
+
+ /**
+ * Gets the function to be optimized.
+ *
+ * @return the objective function.
+ */
+ public UnivariateFunction getObjectiveFunction() {
+ return function;
+ }
+}
diff --git a/src/main/java/org/apache/commons/math3/optim/univariate/UnivariateOptimizer.java b/src/main/java/org/apache/commons/math3/optim/univariate/UnivariateOptimizer.java
new file mode 100644
index 0000000..a7512c1
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/optim/univariate/UnivariateOptimizer.java
@@ -0,0 +1,151 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math3.optim.univariate;
+
+import org.apache.commons.math3.analysis.UnivariateFunction;
+import org.apache.commons.math3.optim.BaseOptimizer;
+import org.apache.commons.math3.optim.OptimizationData;
+import org.apache.commons.math3.optim.nonlinear.scalar.GoalType;
+import org.apache.commons.math3.optim.ConvergenceChecker;
+import org.apache.commons.math3.exception.TooManyEvaluationsException;
+
+/**
+ * Base class for a univariate scalar function optimizer.
+ *
+ * @since 3.1
+ */
+public abstract class UnivariateOptimizer
+ extends BaseOptimizer<UnivariatePointValuePair> {
+ /** Objective function. */
+ private UnivariateFunction function;
+ /** Type of optimization. */
+ private GoalType goal;
+ /** Initial guess. */
+ private double start;
+ /** Lower bound. */
+ private double min;
+ /** Upper bound. */
+ private double max;
+
+ /**
+ * @param checker Convergence checker.
+ */
+ protected UnivariateOptimizer(ConvergenceChecker<UnivariatePointValuePair> checker) {
+ super(checker);
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @param optData Optimization data. In addition to those documented in
+ * {@link BaseOptimizer#parseOptimizationData(OptimizationData[])
+ * BaseOptimizer}, this method will register the following data:
+ * <ul>
+ * <li>{@link GoalType}</li>
+ * <li>{@link SearchInterval}</li>
+ * <li>{@link UnivariateObjectiveFunction}</li>
+ * </ul>
+ * @return {@inheritDoc}
+ * @throws TooManyEvaluationsException if the maximal number of
+ * evaluations is exceeded.
+ */
+ @Override
+ public UnivariatePointValuePair optimize(OptimizationData... optData)
+ throws TooManyEvaluationsException {
+ // Perform computation.
+ return super.optimize(optData);
+ }
+
+ /**
+ * @return the optimization type.
+ */
+ public GoalType getGoalType() {
+ return goal;
+ }
+
+ /**
+ * Scans the list of (required and optional) optimization data that
+ * characterize the problem.
+ *
+ * @param optData Optimization data.
+ * The following data will be looked for:
+ * <ul>
+ * <li>{@link GoalType}</li>
+ * <li>{@link SearchInterval}</li>
+ * <li>{@link UnivariateObjectiveFunction}</li>
+ * </ul>
+ */
+ @Override
+ protected void parseOptimizationData(OptimizationData... optData) {
+ // Allow base class to register its own data.
+ super.parseOptimizationData(optData);
+
+ // The existing values (as set by the previous call) are reused if
+ // not provided in the argument list.
+ for (OptimizationData data : optData) {
+ if (data instanceof SearchInterval) {
+ final SearchInterval interval = (SearchInterval) data;
+ min = interval.getMin();
+ max = interval.getMax();
+ start = interval.getStartValue();
+ continue;
+ }
+ if (data instanceof UnivariateObjectiveFunction) {
+ function = ((UnivariateObjectiveFunction) data).getObjectiveFunction();
+ continue;
+ }
+ if (data instanceof GoalType) {
+ goal = (GoalType) data;
+ continue;
+ }
+ }
+ }
+
+ /**
+ * @return the initial guess.
+ */
+ public double getStartValue() {
+ return start;
+ }
+ /**
+ * @return the lower bounds.
+ */
+ public double getMin() {
+ return min;
+ }
+ /**
+ * @return the upper bounds.
+ */
+ public double getMax() {
+ return max;
+ }
+
+ /**
+ * Computes the objective function value.
+ * This method <em>must</em> be called by subclasses to enforce the
+ * evaluation counter limit.
+ *
+ * @param x Point at which the objective function must be evaluated.
+ * @return the objective function value at the specified point.
+ * @throws TooManyEvaluationsException if the maximal number of
+ * evaluations is exceeded.
+ */
+ protected double computeObjectiveValue(double x) {
+ super.incrementEvaluationCount();
+ return function.value(x);
+ }
+}
diff --git a/src/main/java/org/apache/commons/math3/optim/univariate/UnivariatePointValuePair.java b/src/main/java/org/apache/commons/math3/optim/univariate/UnivariatePointValuePair.java
new file mode 100644
index 0000000..6b2b51a
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/optim/univariate/UnivariatePointValuePair.java
@@ -0,0 +1,66 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math3.optim.univariate;
+
+import java.io.Serializable;
+
+/**
+ * This class holds a point and the value of an objective function at this
+ * point.
+ * This is a simple immutable container.
+ *
+ * @since 3.0
+ */
+public class UnivariatePointValuePair implements Serializable {
+ /** Serializable version identifier. */
+ private static final long serialVersionUID = 1003888396256744753L;
+ /** Point. */
+ private final double point;
+ /** Value of the objective function at the point. */
+ private final double value;
+
+ /**
+ * Build a point/objective function value pair.
+ *
+ * @param point Point.
+ * @param value Value of an objective function at the point
+ */
+ public UnivariatePointValuePair(final double point,
+ final double value) {
+ this.point = point;
+ this.value = value;
+ }
+
+ /**
+ * Get the point.
+ *
+ * @return the point.
+ */
+ public double getPoint() {
+ return point;
+ }
+
+ /**
+ * Get the value of the objective function.
+ *
+ * @return the stored value of the objective function.
+ */
+ public double getValue() {
+ return value;
+ }
+}
diff --git a/src/main/java/org/apache/commons/math3/optim/univariate/package-info.java b/src/main/java/org/apache/commons/math3/optim/univariate/package-info.java
new file mode 100644
index 0000000..2273bab
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/optim/univariate/package-info.java
@@ -0,0 +1,21 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * One-dimensional optimization algorithms.
+ */
+package org.apache.commons.math3.optim.univariate;
diff --git a/src/main/java/org/apache/commons/math3/optimization/AbstractConvergenceChecker.java b/src/main/java/org/apache/commons/math3/optimization/AbstractConvergenceChecker.java
new file mode 100644
index 0000000..98248ce
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/optimization/AbstractConvergenceChecker.java
@@ -0,0 +1,95 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math3.optimization;
+
+import org.apache.commons.math3.util.Precision;
+
+/**
+ * Base class for all convergence checker implementations.
+ *
+ * @param <PAIR> Type of (point, value) pair.
+ * @deprecated As of 3.1 (to be removed in 4.0).
+ * @since 3.0
+ */
+@Deprecated
+public abstract class AbstractConvergenceChecker<PAIR> implements ConvergenceChecker<PAIR> {
+ /**
+ * Default relative threshold.
+ *
+ * @deprecated in 3.1 (to be removed in 4.0) because this value is too small to be useful as a
+ * default (cf. MATH-798).
+ */
+ @Deprecated private static final double DEFAULT_RELATIVE_THRESHOLD = 100 * Precision.EPSILON;
+
+ /**
+ * Default absolute threshold.
+ *
+ * @deprecated in 3.1 (to be removed in 4.0) because this value is too small to be useful as a
+ * default (cf. MATH-798).
+ */
+ @Deprecated private static final double DEFAULT_ABSOLUTE_THRESHOLD = 100 * Precision.SAFE_MIN;
+
+ /** Relative tolerance threshold. */
+ private final double relativeThreshold;
+
+ /** Absolute tolerance threshold. */
+ private final double absoluteThreshold;
+
+ /**
+ * Build an instance with default thresholds.
+ *
+ * @deprecated in 3.1 (to be removed in 4.0). Convergence thresholds are problem-dependent. As
+ * this class is intended for users who want to set their own convergence criterion instead
+ * of relying on an algorithm's default procedure, they should also set the thresholds
+ * appropriately (cf. MATH-798).
+ */
+ @Deprecated
+ public AbstractConvergenceChecker() {
+ this.relativeThreshold = DEFAULT_RELATIVE_THRESHOLD;
+ this.absoluteThreshold = DEFAULT_ABSOLUTE_THRESHOLD;
+ }
+
+ /**
+ * Build an instance with a specified thresholds.
+ *
+ * @param relativeThreshold relative tolerance threshold
+ * @param absoluteThreshold absolute tolerance threshold
+ */
+ public AbstractConvergenceChecker(
+ final double relativeThreshold, final double absoluteThreshold) {
+ this.relativeThreshold = relativeThreshold;
+ this.absoluteThreshold = absoluteThreshold;
+ }
+
+ /**
+ * @return the relative threshold.
+ */
+ public double getRelativeThreshold() {
+ return relativeThreshold;
+ }
+
+ /**
+ * @return the absolute threshold.
+ */
+ public double getAbsoluteThreshold() {
+ return absoluteThreshold;
+ }
+
+ /** {@inheritDoc} */
+ public abstract boolean converged(int iteration, PAIR previous, PAIR current);
+}
diff --git a/src/main/java/org/apache/commons/math3/optimization/BaseMultivariateMultiStartOptimizer.java b/src/main/java/org/apache/commons/math3/optimization/BaseMultivariateMultiStartOptimizer.java
new file mode 100644
index 0000000..2c4aa17
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/optimization/BaseMultivariateMultiStartOptimizer.java
@@ -0,0 +1,192 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math3.optimization;
+
+import org.apache.commons.math3.analysis.MultivariateFunction;
+import org.apache.commons.math3.exception.MathIllegalStateException;
+import org.apache.commons.math3.exception.NotStrictlyPositiveException;
+import org.apache.commons.math3.exception.NullArgumentException;
+import org.apache.commons.math3.exception.util.LocalizedFormats;
+import org.apache.commons.math3.random.RandomVectorGenerator;
+
+import java.util.Arrays;
+import java.util.Comparator;
+
+/**
+ * Base class for all implementations of a multi-start optimizer.
+ *
+ * <p>This interface is mainly intended to enforce the internal coherence of Commons-Math. Users of
+ * the API are advised to base their code on {@link MultivariateMultiStartOptimizer} or on {@link
+ * DifferentiableMultivariateMultiStartOptimizer}.
+ *
+ * @param <FUNC> Type of the objective function to be optimized.
+ * @deprecated As of 3.1 (to be removed in 4.0).
+ * @since 3.0
+ */
+@Deprecated
+public class BaseMultivariateMultiStartOptimizer<FUNC extends MultivariateFunction>
+ implements BaseMultivariateOptimizer<FUNC> {
+ /** Underlying classical optimizer. */
+ private final BaseMultivariateOptimizer<FUNC> optimizer;
+
+ /** Maximal number of evaluations allowed. */
+ private int maxEvaluations;
+
+ /** Number of evaluations already performed for all starts. */
+ private int totalEvaluations;
+
+ /** Number of starts to go. */
+ private int starts;
+
+ /** Random generator for multi-start. */
+ private RandomVectorGenerator generator;
+
+ /** Found optima. */
+ private PointValuePair[] optima;
+
+ /**
+ * Create a multi-start optimizer from a single-start optimizer.
+ *
+ * @param optimizer Single-start optimizer to wrap.
+ * @param starts Number of starts to perform. If {@code starts == 1}, the {@link
+ * #optimize(int,MultivariateFunction,GoalType,double[]) optimize} will return the same
+ * solution as {@code optimizer} would.
+ * @param generator Random vector generator to use for restarts.
+ * @throws NullArgumentException if {@code optimizer} or {@code generator} is {@code null}.
+ * @throws NotStrictlyPositiveException if {@code starts < 1}.
+ */
+ protected BaseMultivariateMultiStartOptimizer(
+ final BaseMultivariateOptimizer<FUNC> optimizer,
+ final int starts,
+ final RandomVectorGenerator generator) {
+ if (optimizer == null || generator == null) {
+ throw new NullArgumentException();
+ }
+ if (starts < 1) {
+ throw new NotStrictlyPositiveException(starts);
+ }
+
+ this.optimizer = optimizer;
+ this.starts = starts;
+ this.generator = generator;
+ }
+
+ /**
+ * Get all the optima found during the last call to {@link
+ * #optimize(int,MultivariateFunction,GoalType,double[]) optimize}. The optimizer stores all the
+ * optima found during a set of restarts. The {@link
+ * #optimize(int,MultivariateFunction,GoalType,double[]) optimize} method returns the best point
+ * only. This method returns all the points found at the end of each starts, including the best
+ * one already returned by the {@link #optimize(int,MultivariateFunction,GoalType,double[])
+ * optimize} method. <br>
+ * The returned array as one element for each start as specified in the constructor. It is
+ * ordered with the results from the runs that did converge first, sorted from best to worst
+ * objective value (i.e in ascending order if minimizing and in descending order if maximizing),
+ * followed by and null elements corresponding to the runs that did not converge. This means all
+ * elements will be null if the {@link #optimize(int,MultivariateFunction,GoalType,double[])
+ * optimize} method did throw an exception. This also means that if the first element is not
+ * {@code null}, it is the best point found across all starts.
+ *
+ * @return an array containing the optima.
+ * @throws MathIllegalStateException if {@link
+ * #optimize(int,MultivariateFunction,GoalType,double[]) optimize} has not been called.
+ */
+ public PointValuePair[] getOptima() {
+ if (optima == null) {
+ throw new MathIllegalStateException(LocalizedFormats.NO_OPTIMUM_COMPUTED_YET);
+ }
+ return optima.clone();
+ }
+
+ /** {@inheritDoc} */
+ public int getMaxEvaluations() {
+ return maxEvaluations;
+ }
+
+ /** {@inheritDoc} */
+ public int getEvaluations() {
+ return totalEvaluations;
+ }
+
+ /** {@inheritDoc} */
+ public ConvergenceChecker<PointValuePair> getConvergenceChecker() {
+ return optimizer.getConvergenceChecker();
+ }
+
+ /** {@inheritDoc} */
+ public PointValuePair optimize(
+ int maxEval, final FUNC f, final GoalType goal, double[] startPoint) {
+ maxEvaluations = maxEval;
+ RuntimeException lastException = null;
+ optima = new PointValuePair[starts];
+ totalEvaluations = 0;
+
+ // Multi-start loop.
+ for (int i = 0; i < starts; ++i) {
+ // CHECKSTYLE: stop IllegalCatch
+ try {
+ optima[i] =
+ optimizer.optimize(
+ maxEval - totalEvaluations,
+ f,
+ goal,
+ i == 0 ? startPoint : generator.nextVector());
+ } catch (RuntimeException mue) {
+ lastException = mue;
+ optima[i] = null;
+ }
+ // CHECKSTYLE: resume IllegalCatch
+
+ totalEvaluations += optimizer.getEvaluations();
+ }
+
+ sortPairs(goal);
+
+ if (optima[0] == null) {
+ throw lastException; // cannot be null if starts >=1
+ }
+
+ // Return the found point given the best objective function value.
+ return optima[0];
+ }
+
+ /**
+ * Sort the optima from best to worst, followed by {@code null} elements.
+ *
+ * @param goal Goal type.
+ */
+ private void sortPairs(final GoalType goal) {
+ Arrays.sort(
+ optima,
+ new Comparator<PointValuePair>() {
+ /** {@inheritDoc} */
+ public int compare(final PointValuePair o1, final PointValuePair o2) {
+ if (o1 == null) {
+ return (o2 == null) ? 0 : 1;
+ } else if (o2 == null) {
+ return -1;
+ }
+ final double v1 = o1.getValue();
+ final double v2 = o2.getValue();
+ return (goal == GoalType.MINIMIZE)
+ ? Double.compare(v1, v2)
+ : Double.compare(v2, v1);
+ }
+ });
+ }
+}
diff --git a/src/main/java/org/apache/commons/math3/optimization/BaseMultivariateOptimizer.java b/src/main/java/org/apache/commons/math3/optimization/BaseMultivariateOptimizer.java
new file mode 100644
index 0000000..2db1401
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/optimization/BaseMultivariateOptimizer.java
@@ -0,0 +1,60 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math3.optimization;
+
+import org.apache.commons.math3.analysis.MultivariateFunction;
+
+/**
+ * This interface is mainly intended to enforce the internal coherence of Commons-FastMath. Users of
+ * the API are advised to base their code on the following interfaces:
+ *
+ * <ul>
+ * <li>{@link org.apache.commons.math3.optimization.MultivariateOptimizer}
+ * <li>{@link org.apache.commons.math3.optimization.MultivariateDifferentiableOptimizer}
+ * </ul>
+ *
+ * @param <FUNC> Type of the objective function to be optimized.
+ * @deprecated As of 3.1 (to be removed in 4.0).
+ * @since 3.0
+ */
+@Deprecated
+public interface BaseMultivariateOptimizer<FUNC extends MultivariateFunction>
+ extends BaseOptimizer<PointValuePair> {
+ /**
+ * Optimize an objective function.
+ *
+ * @param f Objective function.
+ * @param goalType Type of optimization goal: either {@link GoalType#MAXIMIZE} or {@link
+ * GoalType#MINIMIZE}.
+ * @param startPoint Start point for optimization.
+ * @param maxEval Maximum number of function evaluations.
+ * @return the point/value pair giving the optimal value for objective function.
+ * @throws org.apache.commons.math3.exception.DimensionMismatchException if the start point
+ * dimension is wrong.
+ * @throws org.apache.commons.math3.exception.TooManyEvaluationsException if the maximal number
+ * of evaluations is exceeded.
+ * @throws org.apache.commons.math3.exception.NullArgumentException if any argument is {@code
+ * null}.
+ * @deprecated As of 3.1. In 4.0, it will be replaced by the declaration corresponding to this
+ * {@link
+ * org.apache.commons.math3.optimization.direct.BaseAbstractMultivariateOptimizer#optimize(int,MultivariateFunction,GoalType,OptimizationData[])
+ * method}.
+ */
+ @Deprecated
+ PointValuePair optimize(int maxEval, FUNC f, GoalType goalType, double[] startPoint);
+}
diff --git a/src/main/java/org/apache/commons/math3/optimization/BaseMultivariateSimpleBoundsOptimizer.java b/src/main/java/org/apache/commons/math3/optimization/BaseMultivariateSimpleBoundsOptimizer.java
new file mode 100644
index 0000000..4a5a901
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/optimization/BaseMultivariateSimpleBoundsOptimizer.java
@@ -0,0 +1,67 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math3.optimization;
+
+import org.apache.commons.math3.analysis.MultivariateFunction;
+
+/**
+ * This interface is mainly intended to enforce the internal coherence of Commons-FastMath. Users of
+ * the API are advised to base their code on the following interfaces:
+ *
+ * <ul>
+ * <li>{@link org.apache.commons.math3.optimization.MultivariateOptimizer}
+ * <li>{@link org.apache.commons.math3.optimization.MultivariateDifferentiableOptimizer}
+ * </ul>
+ *
+ * @param <FUNC> Type of the objective function to be optimized.
+ * @deprecated As of 3.1 (to be removed in 4.0).
+ * @since 3.0
+ */
+@Deprecated
+public interface BaseMultivariateSimpleBoundsOptimizer<FUNC extends MultivariateFunction>
+ extends BaseMultivariateOptimizer<FUNC> {
+ /**
+ * Optimize an objective function.
+ *
+ * @param f Objective function.
+ * @param goalType Type of optimization goal: either {@link GoalType#MAXIMIZE} or {@link
+ * GoalType#MINIMIZE}.
+ * @param startPoint Start point for optimization.
+ * @param maxEval Maximum number of function evaluations.
+ * @param lowerBound Lower bound for each of the parameters.
+ * @param upperBound Upper bound for each of the parameters.
+ * @return the point/value pair giving the optimal value for objective function.
+ * @throws org.apache.commons.math3.exception.DimensionMismatchException if the array sizes are
+ * wrong.
+ * @throws org.apache.commons.math3.exception.TooManyEvaluationsException if the maximal number
+ * of evaluations is exceeded.
+ * @throws org.apache.commons.math3.exception.NullArgumentException if {@code f}, {@code
+ * goalType} or {@code startPoint} is {@code null}.
+ * @throws org.apache.commons.math3.exception.NumberIsTooSmallException if any of the initial
+ * values is less than its lower bound.
+ * @throws org.apache.commons.math3.exception.NumberIsTooLargeException if any of the initial
+ * values is greater than its upper bound.
+ */
+ PointValuePair optimize(
+ int maxEval,
+ FUNC f,
+ GoalType goalType,
+ double[] startPoint,
+ double[] lowerBound,
+ double[] upperBound);
+}
diff --git a/src/main/java/org/apache/commons/math3/optimization/BaseMultivariateVectorMultiStartOptimizer.java b/src/main/java/org/apache/commons/math3/optimization/BaseMultivariateVectorMultiStartOptimizer.java
new file mode 100644
index 0000000..7b6523a
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/optimization/BaseMultivariateVectorMultiStartOptimizer.java
@@ -0,0 +1,207 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math3.optimization;
+
+import org.apache.commons.math3.analysis.MultivariateVectorFunction;
+import org.apache.commons.math3.exception.ConvergenceException;
+import org.apache.commons.math3.exception.MathIllegalStateException;
+import org.apache.commons.math3.exception.NotStrictlyPositiveException;
+import org.apache.commons.math3.exception.NullArgumentException;
+import org.apache.commons.math3.exception.util.LocalizedFormats;
+import org.apache.commons.math3.random.RandomVectorGenerator;
+
+import java.util.Arrays;
+import java.util.Comparator;
+
+/**
+ * Base class for all implementations of a multi-start optimizer.
+ *
+ * <p>This interface is mainly intended to enforce the internal coherence of Commons-Math. Users of
+ * the API are advised to base their code on {@link
+ * DifferentiableMultivariateVectorMultiStartOptimizer}.
+ *
+ * @param <FUNC> Type of the objective function to be optimized.
+ * @deprecated As of 3.1 (to be removed in 4.0).
+ * @since 3.0
+ */
+@Deprecated
+public class BaseMultivariateVectorMultiStartOptimizer<FUNC extends MultivariateVectorFunction>
+ implements BaseMultivariateVectorOptimizer<FUNC> {
+ /** Underlying classical optimizer. */
+ private final BaseMultivariateVectorOptimizer<FUNC> optimizer;
+
+ /** Maximal number of evaluations allowed. */
+ private int maxEvaluations;
+
+ /** Number of evaluations already performed for all starts. */
+ private int totalEvaluations;
+
+ /** Number of starts to go. */
+ private int starts;
+
+ /** Random generator for multi-start. */
+ private RandomVectorGenerator generator;
+
+ /** Found optima. */
+ private PointVectorValuePair[] optima;
+
+ /**
+ * Create a multi-start optimizer from a single-start optimizer.
+ *
+ * @param optimizer Single-start optimizer to wrap.
+ * @param starts Number of starts to perform. If {@code starts == 1}, the {@link
+ * #optimize(int,MultivariateVectorFunction,double[],double[],double[]) optimize} will
+ * return the same solution as {@code optimizer} would.
+ * @param generator Random vector generator to use for restarts.
+ * @throws NullArgumentException if {@code optimizer} or {@code generator} is {@code null}.
+ * @throws NotStrictlyPositiveException if {@code starts < 1}.
+ */
+ protected BaseMultivariateVectorMultiStartOptimizer(
+ final BaseMultivariateVectorOptimizer<FUNC> optimizer,
+ final int starts,
+ final RandomVectorGenerator generator) {
+ if (optimizer == null || generator == null) {
+ throw new NullArgumentException();
+ }
+ if (starts < 1) {
+ throw new NotStrictlyPositiveException(starts);
+ }
+
+ this.optimizer = optimizer;
+ this.starts = starts;
+ this.generator = generator;
+ }
+
+ /**
+ * Get all the optima found during the last call to {@link
+ * #optimize(int,MultivariateVectorFunction,double[],double[],double[]) optimize}. The optimizer
+ * stores all the optima found during a set of restarts. The {@link
+ * #optimize(int,MultivariateVectorFunction,double[],double[],double[]) optimize} method returns
+ * the best point only. This method returns all the points found at the end of each starts,
+ * including the best one already returned by the {@link
+ * #optimize(int,MultivariateVectorFunction,double[],double[],double[]) optimize} method. <br>
+ * The returned array as one element for each start as specified in the constructor. It is
+ * ordered with the results from the runs that did converge first, sorted from best to worst
+ * objective value (i.e. in ascending order if minimizing and in descending order if
+ * maximizing), followed by and null elements corresponding to the runs that did not converge.
+ * This means all elements will be null if the {@link
+ * #optimize(int,MultivariateVectorFunction,double[],double[],double[]) optimize} method did
+ * throw a {@link ConvergenceException}). This also means that if the first element is not
+ * {@code null}, it is the best point found across all starts.
+ *
+ * @return array containing the optima
+ * @throws MathIllegalStateException if {@link
+ * #optimize(int,MultivariateVectorFunction,double[],double[],double[]) optimize} has not
+ * been called.
+ */
+ public PointVectorValuePair[] getOptima() {
+ if (optima == null) {
+ throw new MathIllegalStateException(LocalizedFormats.NO_OPTIMUM_COMPUTED_YET);
+ }
+ return optima.clone();
+ }
+
+ /** {@inheritDoc} */
+ public int getMaxEvaluations() {
+ return maxEvaluations;
+ }
+
+ /** {@inheritDoc} */
+ public int getEvaluations() {
+ return totalEvaluations;
+ }
+
+ /** {@inheritDoc} */
+ public ConvergenceChecker<PointVectorValuePair> getConvergenceChecker() {
+ return optimizer.getConvergenceChecker();
+ }
+
+ /** {@inheritDoc} */
+ public PointVectorValuePair optimize(
+ int maxEval, final FUNC f, double[] target, double[] weights, double[] startPoint) {
+ maxEvaluations = maxEval;
+ RuntimeException lastException = null;
+ optima = new PointVectorValuePair[starts];
+ totalEvaluations = 0;
+
+ // Multi-start loop.
+ for (int i = 0; i < starts; ++i) {
+
+ // CHECKSTYLE: stop IllegalCatch
+ try {
+ optima[i] =
+ optimizer.optimize(
+ maxEval - totalEvaluations,
+ f,
+ target,
+ weights,
+ i == 0 ? startPoint : generator.nextVector());
+ } catch (ConvergenceException oe) {
+ optima[i] = null;
+ } catch (RuntimeException mue) {
+ lastException = mue;
+ optima[i] = null;
+ }
+ // CHECKSTYLE: resume IllegalCatch
+
+ totalEvaluations += optimizer.getEvaluations();
+ }
+
+ sortPairs(target, weights);
+
+ if (optima[0] == null) {
+ throw lastException; // cannot be null if starts >=1
+ }
+
+ // Return the found point given the best objective function value.
+ return optima[0];
+ }
+
+ /**
+ * Sort the optima from best to worst, followed by {@code null} elements.
+ *
+ * @param target Target value for the objective functions at optimum.
+ * @param weights Weights for the least-squares cost computation.
+ */
+ private void sortPairs(final double[] target, final double[] weights) {
+ Arrays.sort(
+ optima,
+ new Comparator<PointVectorValuePair>() {
+ /** {@inheritDoc} */
+ public int compare(
+ final PointVectorValuePair o1, final PointVectorValuePair o2) {
+ if (o1 == null) {
+ return (o2 == null) ? 0 : 1;
+ } else if (o2 == null) {
+ return -1;
+ }
+ return Double.compare(weightedResidual(o1), weightedResidual(o2));
+ }
+
+ private double weightedResidual(final PointVectorValuePair pv) {
+ final double[] value = pv.getValueRef();
+ double sum = 0;
+ for (int i = 0; i < value.length; ++i) {
+ final double ri = value[i] - target[i];
+ sum += weights[i] * ri * ri;
+ }
+ return sum;
+ }
+ });
+ }
+}
diff --git a/src/main/java/org/apache/commons/math3/optimization/BaseMultivariateVectorOptimizer.java b/src/main/java/org/apache/commons/math3/optimization/BaseMultivariateVectorOptimizer.java
new file mode 100644
index 0000000..b8a386f
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/optimization/BaseMultivariateVectorOptimizer.java
@@ -0,0 +1,62 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math3.optimization;
+
+import org.apache.commons.math3.analysis.MultivariateVectorFunction;
+
+/**
+ * This interface is mainly intended to enforce the internal coherence of Commons-Math. Users of the
+ * API are advised to base their code on the following interfaces:
+ *
+ * <ul>
+ * <li>{@link org.apache.commons.math3.optimization.DifferentiableMultivariateVectorOptimizer}
+ * </ul>
+ *
+ * @param <FUNC> Type of the objective function to be optimized.
+ * @deprecated As of 3.1 (to be removed in 4.0).
+ * @since 3.0
+ */
+@Deprecated
+public interface BaseMultivariateVectorOptimizer<FUNC extends MultivariateVectorFunction>
+ extends BaseOptimizer<PointVectorValuePair> {
+ /**
+ * Optimize an objective function. Optimization is considered to be a weighted least-squares
+ * minimization. The cost function to be minimized is <code>
+ * &sum;weight<sub>i</sub>(objective<sub>i</sub> - target<sub>i</sub>)<sup>2</sup></code>
+ *
+ * @param f Objective function.
+ * @param target Target value for the objective functions at optimum.
+ * @param weight Weights for the least squares cost computation.
+ * @param startPoint Start point for optimization.
+ * @return the point/value pair giving the optimal value for objective function.
+ * @param maxEval Maximum number of function evaluations.
+ * @throws org.apache.commons.math3.exception.DimensionMismatchException if the start point
+ * dimension is wrong.
+ * @throws org.apache.commons.math3.exception.TooManyEvaluationsException if the maximal number
+ * of evaluations is exceeded.
+ * @throws org.apache.commons.math3.exception.NullArgumentException if any argument is {@code
+ * null}.
+ * @deprecated As of 3.1. In 4.0, this will be replaced by the declaration corresponding to this
+ * {@link
+ * org.apache.commons.math3.optimization.direct.BaseAbstractMultivariateVectorOptimizer#optimize(int,MultivariateVectorFunction,OptimizationData[])
+ * method}.
+ */
+ @Deprecated
+ PointVectorValuePair optimize(
+ int maxEval, FUNC f, double[] target, double[] weight, double[] startPoint);
+}
diff --git a/src/main/java/org/apache/commons/math3/optimization/BaseOptimizer.java b/src/main/java/org/apache/commons/math3/optimization/BaseOptimizer.java
new file mode 100644
index 0000000..b9db4bd
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/optimization/BaseOptimizer.java
@@ -0,0 +1,59 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math3.optimization;
+
+/**
+ * This interface is mainly intended to enforce the internal coherence of Commons-Math. Users of the
+ * API are advised to base their code on the following interfaces:
+ *
+ * <ul>
+ * <li>{@link org.apache.commons.math3.optimization.MultivariateOptimizer}
+ * <li>{@link org.apache.commons.math3.optimization.MultivariateDifferentiableOptimizer}
+ * <li>{@link org.apache.commons.math3.optimization.MultivariateDifferentiableVectorOptimizer}
+ * <li>{@link org.apache.commons.math3.optimization.univariate.UnivariateOptimizer}
+ * </ul>
+ *
+ * @param <PAIR> Type of the point/objective pair.
+ * @deprecated As of 3.1 (to be removed in 4.0).
+ * @since 3.0
+ */
+@Deprecated
+public interface BaseOptimizer<PAIR> {
+ /**
+ * Get the maximal number of function evaluations.
+ *
+ * @return the maximal number of function evaluations.
+ */
+ int getMaxEvaluations();
+
+ /**
+ * Get the number of evaluations of the objective function. The number of evaluations
+ * corresponds to the last call to the {@code optimize} method. It is 0 if the method has not
+ * been called yet.
+ *
+ * @return the number of evaluations of the objective function.
+ */
+ int getEvaluations();
+
+ /**
+ * Get the convergence checker.
+ *
+ * @return the object used to check for convergence.
+ */
+ ConvergenceChecker<PAIR> getConvergenceChecker();
+}
diff --git a/src/main/java/org/apache/commons/math3/optimization/ConvergenceChecker.java b/src/main/java/org/apache/commons/math3/optimization/ConvergenceChecker.java
new file mode 100644
index 0000000..ee43b95
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/optimization/ConvergenceChecker.java
@@ -0,0 +1,50 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math3.optimization;
+
+/**
+ * This interface specifies how to check if an optimization algorithm has converged. <br>
+ * Deciding if convergence has been reached is a problem-dependent issue. The user should provide a
+ * class implementing this interface to allow the optimization algorithm to stop its search
+ * according to the problem at hand. <br>
+ * For convenience, three implementations that fit simple needs are already provided: {@link
+ * SimpleValueChecker}, {@link SimpleVectorValueChecker} and {@link SimplePointChecker}. The first
+ * two consider that convergence is reached when the objective function value does not change much
+ * anymore, it does not use the point set at all. The third one considers that convergence is
+ * reached when the input point set does not change much anymore, it does not use objective function
+ * value at all.
+ *
+ * @param <PAIR> Type of the (point, objective value) pair.
+ * @see org.apache.commons.math3.optimization.SimplePointChecker
+ * @see org.apache.commons.math3.optimization.SimpleValueChecker
+ * @see org.apache.commons.math3.optimization.SimpleVectorValueChecker
+ * @deprecated As of 3.1 (to be removed in 4.0).
+ * @since 3.0
+ */
+@Deprecated
+public interface ConvergenceChecker<PAIR> {
+ /**
+ * Check if the optimization algorithm has converged.
+ *
+ * @param iteration Current iteration.
+ * @param previous Best point in the previous iteration.
+ * @param current Best point in the current iteration.
+ * @return {@code true} if the algorithm is considered to have converged.
+ */
+ boolean converged(int iteration, PAIR previous, PAIR current);
+}
diff --git a/src/main/java/org/apache/commons/math3/optimization/DifferentiableMultivariateMultiStartOptimizer.java b/src/main/java/org/apache/commons/math3/optimization/DifferentiableMultivariateMultiStartOptimizer.java
new file mode 100644
index 0000000..ae2a48e
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/optimization/DifferentiableMultivariateMultiStartOptimizer.java
@@ -0,0 +1,51 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math3.optimization;
+
+import org.apache.commons.math3.analysis.DifferentiableMultivariateFunction;
+import org.apache.commons.math3.random.RandomVectorGenerator;
+
+/**
+ * Special implementation of the {@link DifferentiableMultivariateOptimizer} interface adding
+ * multi-start features to an existing optimizer.
+ *
+ * <p>This class wraps a classical optimizer to use it several times in turn with different starting
+ * points in order to avoid being trapped into a local extremum when looking for a global one.
+ *
+ * @deprecated As of 3.1 (to be removed in 4.0).
+ * @since 2.0
+ */
+@Deprecated
+public class DifferentiableMultivariateMultiStartOptimizer
+ extends BaseMultivariateMultiStartOptimizer<DifferentiableMultivariateFunction>
+ implements DifferentiableMultivariateOptimizer {
+ /**
+ * Create a multi-start optimizer from a single-start optimizer.
+ *
+ * @param optimizer Single-start optimizer to wrap.
+ * @param starts Number of starts to perform (including the first one), multi-start is disabled
+ * if value is less than or equal to 1.
+ * @param generator Random vector generator to use for restarts.
+ */
+ public DifferentiableMultivariateMultiStartOptimizer(
+ final DifferentiableMultivariateOptimizer optimizer,
+ final int starts,
+ final RandomVectorGenerator generator) {
+ super(optimizer, starts, generator);
+ }
+}
diff --git a/src/main/java/org/apache/commons/math3/optimization/DifferentiableMultivariateOptimizer.java b/src/main/java/org/apache/commons/math3/optimization/DifferentiableMultivariateOptimizer.java
new file mode 100644
index 0000000..51e9f26
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/optimization/DifferentiableMultivariateOptimizer.java
@@ -0,0 +1,34 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math3.optimization;
+
+import org.apache.commons.math3.analysis.DifferentiableMultivariateFunction;
+
+/**
+ * This interface represents an optimization algorithm for {@link DifferentiableMultivariateFunction
+ * scalar differentiable objective functions}. Optimization algorithms find the input point set that
+ * either {@link GoalType maximize or minimize} an objective function.
+ *
+ * @see MultivariateOptimizer
+ * @see DifferentiableMultivariateVectorOptimizer
+ * @deprecated As of 3.1 (to be removed in 4.0).
+ * @since 2.0
+ */
+@Deprecated
+public interface DifferentiableMultivariateOptimizer
+ extends BaseMultivariateOptimizer<DifferentiableMultivariateFunction> {}
diff --git a/src/main/java/org/apache/commons/math3/optimization/DifferentiableMultivariateVectorMultiStartOptimizer.java b/src/main/java/org/apache/commons/math3/optimization/DifferentiableMultivariateVectorMultiStartOptimizer.java
new file mode 100644
index 0000000..2ad2bbf
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/optimization/DifferentiableMultivariateVectorMultiStartOptimizer.java
@@ -0,0 +1,51 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math3.optimization;
+
+import org.apache.commons.math3.analysis.DifferentiableMultivariateVectorFunction;
+import org.apache.commons.math3.random.RandomVectorGenerator;
+
+/**
+ * Special implementation of the {@link DifferentiableMultivariateVectorOptimizer} interface addind
+ * multi-start features to an existing optimizer.
+ *
+ * <p>This class wraps a classical optimizer to use it several times in turn with different starting
+ * points in order to avoid being trapped into a local extremum when looking for a global one.
+ *
+ * @deprecated As of 3.1 (to be removed in 4.0).
+ * @since 2.0
+ */
+@Deprecated
+public class DifferentiableMultivariateVectorMultiStartOptimizer
+ extends BaseMultivariateVectorMultiStartOptimizer<DifferentiableMultivariateVectorFunction>
+ implements DifferentiableMultivariateVectorOptimizer {
+ /**
+ * Create a multi-start optimizer from a single-start optimizer.
+ *
+ * @param optimizer Single-start optimizer to wrap.
+ * @param starts Number of starts to perform (including the first one), multi-start is disabled
+ * if value is less than or equal to 1.
+ * @param generator Random vector generator to use for restarts.
+ */
+ public DifferentiableMultivariateVectorMultiStartOptimizer(
+ final DifferentiableMultivariateVectorOptimizer optimizer,
+ final int starts,
+ final RandomVectorGenerator generator) {
+ super(optimizer, starts, generator);
+ }
+}
diff --git a/src/main/java/org/apache/commons/math3/optimization/DifferentiableMultivariateVectorOptimizer.java b/src/main/java/org/apache/commons/math3/optimization/DifferentiableMultivariateVectorOptimizer.java
new file mode 100644
index 0000000..6c697f8
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/optimization/DifferentiableMultivariateVectorOptimizer.java
@@ -0,0 +1,31 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math3.optimization;
+
+import org.apache.commons.math3.analysis.DifferentiableMultivariateVectorFunction;
+
+/**
+ * This interface represents an optimization algorithm for {@link
+ * DifferentiableMultivariateVectorFunction vectorial differentiable objective functions}.
+ *
+ * @deprecated As of 3.1 (to be removed in 4.0).
+ * @since 3.0
+ */
+@Deprecated
+public interface DifferentiableMultivariateVectorOptimizer
+ extends BaseMultivariateVectorOptimizer<DifferentiableMultivariateVectorFunction> {}
diff --git a/src/main/java/org/apache/commons/math3/optimization/GoalType.java b/src/main/java/org/apache/commons/math3/optimization/GoalType.java
new file mode 100644
index 0000000..f43a350
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/optimization/GoalType.java
@@ -0,0 +1,36 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math3.optimization;
+
+import java.io.Serializable;
+
+/**
+ * Goal type for an optimization problem.
+ *
+ * @deprecated As of 3.1 (to be removed in 4.0).
+ * @since 2.0
+ */
+@Deprecated
+public enum GoalType implements Serializable {
+
+ /** Maximization goal. */
+ MAXIMIZE,
+
+ /** Minimization goal. */
+ MINIMIZE
+}
diff --git a/src/main/java/org/apache/commons/math3/optimization/InitialGuess.java b/src/main/java/org/apache/commons/math3/optimization/InitialGuess.java
new file mode 100644
index 0000000..d61e129
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/optimization/InitialGuess.java
@@ -0,0 +1,47 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math3.optimization;
+
+/**
+ * Starting point (first guess) of the optimization procedure. <br>
+ * Immutable class.
+ *
+ * @deprecated As of 3.1 (to be removed in 4.0).
+ * @since 3.1
+ */
+@Deprecated
+public class InitialGuess implements OptimizationData {
+ /** Initial guess. */
+ private final double[] init;
+
+ /**
+ * @param startPoint Initial guess.
+ */
+ public InitialGuess(double[] startPoint) {
+ init = startPoint.clone();
+ }
+
+ /**
+ * Gets the initial guess.
+ *
+ * @return the initial guess.
+ */
+ public double[] getInitialGuess() {
+ return init.clone();
+ }
+}
diff --git a/src/main/java/org/apache/commons/math3/optimization/LeastSquaresConverter.java b/src/main/java/org/apache/commons/math3/optimization/LeastSquaresConverter.java
new file mode 100644
index 0000000..5ee9754
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/optimization/LeastSquaresConverter.java
@@ -0,0 +1,181 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math3.optimization;
+
+import org.apache.commons.math3.analysis.MultivariateFunction;
+import org.apache.commons.math3.analysis.MultivariateVectorFunction;
+import org.apache.commons.math3.exception.DimensionMismatchException;
+import org.apache.commons.math3.linear.RealMatrix;
+
+/**
+ * This class converts {@link MultivariateVectorFunction vectorial objective functions} to {@link
+ * MultivariateFunction scalar objective functions} when the goal is to minimize them.
+ *
+ * <p>This class is mostly used when the vectorial objective function represents a theoretical
+ * result computed from a point set applied to a model and the models point must be adjusted to fit
+ * the theoretical result to some reference observations. The observations may be obtained for
+ * example from physical measurements whether the model is built from theoretical considerations.
+ *
+ * <p>This class computes a possibly weighted squared sum of the residuals, which is a scalar value.
+ * The residuals are the difference between the theoretical model (i.e. the output of the vectorial
+ * objective function) and the observations. The class implements the {@link MultivariateFunction}
+ * interface and can therefore be minimized by any optimizer supporting scalar objectives
+ * functions.This is one way to perform a least square estimation. There are other ways to do this
+ * without using this converter, as some optimization algorithms directly support vectorial
+ * objective functions.
+ *
+ * <p>This class support combination of residuals with or without weights and correlations.
+ *
+ * @see MultivariateFunction
+ * @see MultivariateVectorFunction
+ * @deprecated As of 3.1 (to be removed in 4.0).
+ * @since 2.0
+ */
+@Deprecated
+public class LeastSquaresConverter implements MultivariateFunction {
+
+ /** Underlying vectorial function. */
+ private final MultivariateVectorFunction function;
+
+ /** Observations to be compared to objective function to compute residuals. */
+ private final double[] observations;
+
+ /** Optional weights for the residuals. */
+ private final double[] weights;
+
+ /** Optional scaling matrix (weight and correlations) for the residuals. */
+ private final RealMatrix scale;
+
+ /**
+ * Build a simple converter for uncorrelated residuals with the same weight.
+ *
+ * @param function vectorial residuals function to wrap
+ * @param observations observations to be compared to objective function to compute residuals
+ */
+ public LeastSquaresConverter(
+ final MultivariateVectorFunction function, final double[] observations) {
+ this.function = function;
+ this.observations = observations.clone();
+ this.weights = null;
+ this.scale = null;
+ }
+
+ /**
+ * Build a simple converter for uncorrelated residuals with the specific weights.
+ *
+ * <p>The scalar objective function value is computed as:
+ *
+ * <pre>
+ * objective = &sum;weight<sub>i</sub>(observation<sub>i</sub>-objective<sub>i</sub>)<sup>2</sup>
+ * </pre>
+ *
+ * <p>Weights can be used for example to combine residuals with different standard deviations.
+ * As an example, consider a residuals array in which even elements are angular measurements in
+ * degrees with a 0.01&deg; standard deviation and odd elements are distance measurements in
+ * meters with a 15m standard deviation. In this case, the weights array should be initialized
+ * with value 1.0/(0.01<sup>2</sup>) in the even elements and 1.0/(15.0<sup>2</sup>) in the odd
+ * elements (i.e. reciprocals of variances).
+ *
+ * <p>The array computed by the objective function, the observations array and the weights array
+ * must have consistent sizes or a {@link DimensionMismatchException} will be triggered while
+ * computing the scalar objective.
+ *
+ * @param function vectorial residuals function to wrap
+ * @param observations observations to be compared to objective function to compute residuals
+ * @param weights weights to apply to the residuals
+ * @exception DimensionMismatchException if the observations vector and the weights vector
+ * dimensions do not match (objective function dimension is checked only when the {@link
+ * #value(double[])} method is called)
+ */
+ public LeastSquaresConverter(
+ final MultivariateVectorFunction function,
+ final double[] observations,
+ final double[] weights) {
+ if (observations.length != weights.length) {
+ throw new DimensionMismatchException(observations.length, weights.length);
+ }
+ this.function = function;
+ this.observations = observations.clone();
+ this.weights = weights.clone();
+ this.scale = null;
+ }
+
+ /**
+ * Build a simple converter for correlated residuals with the specific weights.
+ *
+ * <p>The scalar objective function value is computed as:
+ *
+ * <pre>
+ * objective = y<sup>T</sup>y with y = scale&times;(observation-objective)
+ * </pre>
+ *
+ * <p>The array computed by the objective function, the observations array and the the scaling
+ * matrix must have consistent sizes or a {@link DimensionMismatchException} will be triggered
+ * while computing the scalar objective.
+ *
+ * @param function vectorial residuals function to wrap
+ * @param observations observations to be compared to objective function to compute residuals
+ * @param scale scaling matrix
+ * @throws DimensionMismatchException if the observations vector and the scale matrix dimensions
+ * do not match (objective function dimension is checked only when the {@link
+ * #value(double[])} method is called)
+ */
+ public LeastSquaresConverter(
+ final MultivariateVectorFunction function,
+ final double[] observations,
+ final RealMatrix scale) {
+ if (observations.length != scale.getColumnDimension()) {
+ throw new DimensionMismatchException(observations.length, scale.getColumnDimension());
+ }
+ this.function = function;
+ this.observations = observations.clone();
+ this.weights = null;
+ this.scale = scale.copy();
+ }
+
+ /** {@inheritDoc} */
+ public double value(final double[] point) {
+ // compute residuals
+ final double[] residuals = function.value(point);
+ if (residuals.length != observations.length) {
+ throw new DimensionMismatchException(residuals.length, observations.length);
+ }
+ for (int i = 0; i < residuals.length; ++i) {
+ residuals[i] -= observations[i];
+ }
+
+ // compute sum of squares
+ double sumSquares = 0;
+ if (weights != null) {
+ for (int i = 0; i < residuals.length; ++i) {
+ final double ri = residuals[i];
+ sumSquares += weights[i] * ri * ri;
+ }
+ } else if (scale != null) {
+ for (final double yi : scale.operate(residuals)) {
+ sumSquares += yi * yi;
+ }
+ } else {
+ for (final double ri : residuals) {
+ sumSquares += ri * ri;
+ }
+ }
+
+ return sumSquares;
+ }
+}
diff --git a/src/main/java/org/apache/commons/math3/optimization/MultivariateDifferentiableMultiStartOptimizer.java b/src/main/java/org/apache/commons/math3/optimization/MultivariateDifferentiableMultiStartOptimizer.java
new file mode 100644
index 0000000..e883805
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/optimization/MultivariateDifferentiableMultiStartOptimizer.java
@@ -0,0 +1,51 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math3.optimization;
+
+import org.apache.commons.math3.analysis.differentiation.MultivariateDifferentiableFunction;
+import org.apache.commons.math3.random.RandomVectorGenerator;
+
+/**
+ * Special implementation of the {@link MultivariateDifferentiableOptimizer} interface adding
+ * multi-start features to an existing optimizer.
+ *
+ * <p>This class wraps a classical optimizer to use it several times in turn with different starting
+ * points in order to avoid being trapped into a local extremum when looking for a global one.
+ *
+ * @deprecated As of 3.1 (to be removed in 4.0).
+ * @since 3.1
+ */
+@Deprecated
+public class MultivariateDifferentiableMultiStartOptimizer
+ extends BaseMultivariateMultiStartOptimizer<MultivariateDifferentiableFunction>
+ implements MultivariateDifferentiableOptimizer {
+ /**
+ * Create a multi-start optimizer from a single-start optimizer.
+ *
+ * @param optimizer Single-start optimizer to wrap.
+ * @param starts Number of starts to perform (including the first one), multi-start is disabled
+ * if value is less than or equal to 1.
+ * @param generator Random vector generator to use for restarts.
+ */
+ public MultivariateDifferentiableMultiStartOptimizer(
+ final MultivariateDifferentiableOptimizer optimizer,
+ final int starts,
+ final RandomVectorGenerator generator) {
+ super(optimizer, starts, generator);
+ }
+}
diff --git a/src/main/java/org/apache/commons/math3/optimization/MultivariateDifferentiableOptimizer.java b/src/main/java/org/apache/commons/math3/optimization/MultivariateDifferentiableOptimizer.java
new file mode 100644
index 0000000..a2cf840
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/optimization/MultivariateDifferentiableOptimizer.java
@@ -0,0 +1,34 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math3.optimization;
+
+import org.apache.commons.math3.analysis.differentiation.MultivariateDifferentiableFunction;
+
+/**
+ * This interface represents an optimization algorithm for {@link MultivariateDifferentiableFunction
+ * scalar differentiable objective functions}. Optimization algorithms find the input point set that
+ * either {@link GoalType maximize or minimize} an objective function.
+ *
+ * @see MultivariateOptimizer
+ * @see MultivariateDifferentiableVectorOptimizer
+ * @deprecated As of 3.1 (to be removed in 4.0).
+ * @since 3.1
+ */
+@Deprecated
+public interface MultivariateDifferentiableOptimizer
+ extends BaseMultivariateOptimizer<MultivariateDifferentiableFunction> {}
diff --git a/src/main/java/org/apache/commons/math3/optimization/MultivariateDifferentiableVectorMultiStartOptimizer.java b/src/main/java/org/apache/commons/math3/optimization/MultivariateDifferentiableVectorMultiStartOptimizer.java
new file mode 100644
index 0000000..36432cd
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/optimization/MultivariateDifferentiableVectorMultiStartOptimizer.java
@@ -0,0 +1,51 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math3.optimization;
+
+import org.apache.commons.math3.analysis.differentiation.MultivariateDifferentiableVectorFunction;
+import org.apache.commons.math3.random.RandomVectorGenerator;
+
+/**
+ * Special implementation of the {@link MultivariateDifferentiableVectorOptimizer} interface adding
+ * multi-start features to an existing optimizer.
+ *
+ * <p>This class wraps a classical optimizer to use it several times in turn with different starting
+ * points in order to avoid being trapped into a local extremum when looking for a global one.
+ *
+ * @deprecated As of 3.1 (to be removed in 4.0).
+ * @since 3.1
+ */
+@Deprecated
+public class MultivariateDifferentiableVectorMultiStartOptimizer
+ extends BaseMultivariateVectorMultiStartOptimizer<MultivariateDifferentiableVectorFunction>
+ implements MultivariateDifferentiableVectorOptimizer {
+ /**
+ * Create a multi-start optimizer from a single-start optimizer.
+ *
+ * @param optimizer Single-start optimizer to wrap.
+ * @param starts Number of starts to perform (including the first one), multi-start is disabled
+ * if value is less than or equal to 1.
+ * @param generator Random vector generator to use for restarts.
+ */
+ public MultivariateDifferentiableVectorMultiStartOptimizer(
+ final MultivariateDifferentiableVectorOptimizer optimizer,
+ final int starts,
+ final RandomVectorGenerator generator) {
+ super(optimizer, starts, generator);
+ }
+}
diff --git a/src/main/java/org/apache/commons/math3/optimization/MultivariateDifferentiableVectorOptimizer.java b/src/main/java/org/apache/commons/math3/optimization/MultivariateDifferentiableVectorOptimizer.java
new file mode 100644
index 0000000..65868db
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/optimization/MultivariateDifferentiableVectorOptimizer.java
@@ -0,0 +1,31 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math3.optimization;
+
+import org.apache.commons.math3.analysis.differentiation.MultivariateDifferentiableVectorFunction;
+
+/**
+ * This interface represents an optimization algorithm for {@link
+ * MultivariateDifferentiableVectorFunction differentiable vectorial objective functions}.
+ *
+ * @deprecated As of 3.1 (to be removed in 4.0).
+ * @since 3.1
+ */
+@Deprecated
+public interface MultivariateDifferentiableVectorOptimizer
+ extends BaseMultivariateVectorOptimizer<MultivariateDifferentiableVectorFunction> {}
diff --git a/src/main/java/org/apache/commons/math3/optimization/MultivariateMultiStartOptimizer.java b/src/main/java/org/apache/commons/math3/optimization/MultivariateMultiStartOptimizer.java
new file mode 100644
index 0000000..24726c4
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/optimization/MultivariateMultiStartOptimizer.java
@@ -0,0 +1,51 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math3.optimization;
+
+import org.apache.commons.math3.analysis.MultivariateFunction;
+import org.apache.commons.math3.random.RandomVectorGenerator;
+
+/**
+ * Special implementation of the {@link MultivariateOptimizer} interface adding multi-start features
+ * to an existing optimizer.
+ *
+ * <p>This class wraps a classical optimizer to use it several times in turn with different starting
+ * points in order to avoid being trapped into a local extremum when looking for a global one.
+ *
+ * @deprecated As of 3.1 (to be removed in 4.0).
+ * @since 2.0
+ */
+@Deprecated
+public class MultivariateMultiStartOptimizer
+ extends BaseMultivariateMultiStartOptimizer<MultivariateFunction>
+ implements MultivariateOptimizer {
+ /**
+ * Create a multi-start optimizer from a single-start optimizer.
+ *
+ * @param optimizer Single-start optimizer to wrap.
+ * @param starts Number of starts to perform (including the first one), multi-start is disabled
+ * if value is less than or equal to 1.
+ * @param generator Random vector generator to use for restarts.
+ */
+ public MultivariateMultiStartOptimizer(
+ final MultivariateOptimizer optimizer,
+ final int starts,
+ final RandomVectorGenerator generator) {
+ super(optimizer, starts, generator);
+ }
+}
diff --git a/src/main/java/org/apache/commons/math3/optimization/MultivariateOptimizer.java b/src/main/java/org/apache/commons/math3/optimization/MultivariateOptimizer.java
new file mode 100644
index 0000000..e90443c
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/optimization/MultivariateOptimizer.java
@@ -0,0 +1,35 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math3.optimization;
+
+import org.apache.commons.math3.analysis.MultivariateFunction;
+
+/**
+ * This interface represents an optimization algorithm for {@link MultivariateFunction scalar
+ * objective functions}.
+ *
+ * <p>Optimization algorithms find the input point set that either {@link GoalType maximize or
+ * minimize} an objective function.
+ *
+ * @see MultivariateDifferentiableOptimizer
+ * @see MultivariateDifferentiableVectorOptimizer
+ * @deprecated As of 3.1 (to be removed in 4.0).
+ * @since 2.0
+ */
+@Deprecated
+public interface MultivariateOptimizer extends BaseMultivariateOptimizer<MultivariateFunction> {}
diff --git a/src/main/java/org/apache/commons/math3/optimization/OptimizationData.java b/src/main/java/org/apache/commons/math3/optimization/OptimizationData.java
new file mode 100644
index 0000000..2faaa30
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/optimization/OptimizationData.java
@@ -0,0 +1,28 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math3.optimization;
+
+/**
+ * Marker interface. Implementations will provide functionality (optional or required) needed by the
+ * optimizers, and those will need to check the actual type of the arguments and perform the
+ * appropriate cast in order to access the data they need.
+ *
+ * @deprecated As of 3.1 (to be removed in 4.0).
+ * @since 3.1
+ */
+@Deprecated
+public interface OptimizationData {}
diff --git a/src/main/java/org/apache/commons/math3/optimization/PointValuePair.java b/src/main/java/org/apache/commons/math3/optimization/PointValuePair.java
new file mode 100644
index 0000000..cb4e0bd
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/optimization/PointValuePair.java
@@ -0,0 +1,120 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math3.optimization;
+
+import org.apache.commons.math3.util.Pair;
+
+import java.io.Serializable;
+
+/**
+ * This class holds a point and the value of an objective function at that point.
+ *
+ * @see PointVectorValuePair
+ * @see org.apache.commons.math3.analysis.MultivariateFunction
+ * @deprecated As of 3.1 (to be removed in 4.0).
+ * @since 3.0
+ */
+@Deprecated
+public class PointValuePair extends Pair<double[], Double> implements Serializable {
+
+ /** Serializable UID. */
+ private static final long serialVersionUID = 20120513L;
+
+ /**
+ * Builds a point/objective function value pair.
+ *
+ * @param point Point coordinates. This instance will store a copy of the array, not the array
+ * passed as argument.
+ * @param value Value of the objective function at the point.
+ */
+ public PointValuePair(final double[] point, final double value) {
+ this(point, value, true);
+ }
+
+ /**
+ * Builds a point/objective function value pair.
+ *
+ * @param point Point coordinates.
+ * @param value Value of the objective function at the point.
+ * @param copyArray if {@code true}, the input array will be copied, otherwise it will be
+ * referenced.
+ */
+ public PointValuePair(final double[] point, final double value, final boolean copyArray) {
+ super(copyArray ? ((point == null) ? null : point.clone()) : point, value);
+ }
+
+ /**
+ * Gets the point.
+ *
+ * @return a copy of the stored point.
+ */
+ public double[] getPoint() {
+ final double[] p = getKey();
+ return p == null ? null : p.clone();
+ }
+
+ /**
+ * Gets a reference to the point.
+ *
+ * @return a reference to the internal array storing the point.
+ */
+ public double[] getPointRef() {
+ return getKey();
+ }
+
+ /**
+ * Replace the instance with a data transfer object for serialization.
+ *
+ * @return data transfer object that will be serialized
+ */
+ private Object writeReplace() {
+ return new DataTransferObject(getKey(), getValue());
+ }
+
+ /** Internal class used only for serialization. */
+ private static class DataTransferObject implements Serializable {
+ /** Serializable UID. */
+ private static final long serialVersionUID = 20120513L;
+
+ /** Point coordinates. @Serial */
+ private final double[] point;
+
+ /** Value of the objective function at the point. @Serial */
+ private final double value;
+
+ /**
+ * Simple constructor.
+ *
+ * @param point Point coordinates.
+ * @param value Value of the objective function at the point.
+ */
+ DataTransferObject(final double[] point, final double value) {
+ this.point = point.clone();
+ this.value = value;
+ }
+
+ /**
+ * Replace the deserialized data transfer object with a {@link PointValuePair}.
+ *
+ * @return replacement {@link PointValuePair}
+ */
+ private Object readResolve() {
+ return new PointValuePair(point, value, false);
+ }
+ }
+}
diff --git a/src/main/java/org/apache/commons/math3/optimization/PointVectorValuePair.java b/src/main/java/org/apache/commons/math3/optimization/PointVectorValuePair.java
new file mode 100644
index 0000000..bf1bf61
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/optimization/PointVectorValuePair.java
@@ -0,0 +1,143 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math3.optimization;
+
+import org.apache.commons.math3.util.Pair;
+
+import java.io.Serializable;
+
+/**
+ * This class holds a point and the vectorial value of an objective function at that point.
+ *
+ * @see PointValuePair
+ * @see org.apache.commons.math3.analysis.MultivariateVectorFunction
+ * @deprecated As of 3.1 (to be removed in 4.0).
+ * @since 3.0
+ */
+@Deprecated
+public class PointVectorValuePair extends Pair<double[], double[]> implements Serializable {
+
+ /** Serializable UID. */
+ private static final long serialVersionUID = 20120513L;
+
+ /**
+ * Builds a point/objective function value pair.
+ *
+ * @param point Point coordinates. This instance will store a copy of the array, not the array
+ * passed as argument.
+ * @param value Value of the objective function at the point.
+ */
+ public PointVectorValuePair(final double[] point, final double[] value) {
+ this(point, value, true);
+ }
+
+ /**
+ * Build a point/objective function value pair.
+ *
+ * @param point Point coordinates.
+ * @param value Value of the objective function at the point.
+ * @param copyArray if {@code true}, the input arrays will be copied, otherwise they will be
+ * referenced.
+ */
+ public PointVectorValuePair(
+ final double[] point, final double[] value, final boolean copyArray) {
+ super(
+ copyArray ? ((point == null) ? null : point.clone()) : point,
+ copyArray ? ((value == null) ? null : value.clone()) : value);
+ }
+
+ /**
+ * Gets the point.
+ *
+ * @return a copy of the stored point.
+ */
+ public double[] getPoint() {
+ final double[] p = getKey();
+ return p == null ? null : p.clone();
+ }
+
+ /**
+ * Gets a reference to the point.
+ *
+ * @return a reference to the internal array storing the point.
+ */
+ public double[] getPointRef() {
+ return getKey();
+ }
+
+ /**
+ * Gets the value of the objective function.
+ *
+ * @return a copy of the stored value of the objective function.
+ */
+ @Override
+ public double[] getValue() {
+ final double[] v = super.getValue();
+ return v == null ? null : v.clone();
+ }
+
+ /**
+ * Gets a reference to the value of the objective function.
+ *
+ * @return a reference to the internal array storing the value of the objective function.
+ */
+ public double[] getValueRef() {
+ return super.getValue();
+ }
+
+ /**
+ * Replace the instance with a data transfer object for serialization.
+ *
+ * @return data transfer object that will be serialized
+ */
+ private Object writeReplace() {
+ return new DataTransferObject(getKey(), getValue());
+ }
+
+ /** Internal class used only for serialization. */
+ private static class DataTransferObject implements Serializable {
+ /** Serializable UID. */
+ private static final long serialVersionUID = 20120513L;
+
+ /** Point coordinates. @Serial */
+ private final double[] point;
+
+ /** Value of the objective function at the point. @Serial */
+ private final double[] value;
+
+ /**
+ * Simple constructor.
+ *
+ * @param point Point coordinates.
+ * @param value Value of the objective function at the point.
+ */
+ DataTransferObject(final double[] point, final double[] value) {
+ this.point = point.clone();
+ this.value = value.clone();
+ }
+
+ /**
+ * Replace the deserialized data transfer object with a {@link PointValuePair}.
+ *
+ * @return replacement {@link PointValuePair}
+ */
+ private Object readResolve() {
+ return new PointVectorValuePair(point, value, false);
+ }
+ }
+}
diff --git a/src/main/java/org/apache/commons/math3/optimization/SimpleBounds.java b/src/main/java/org/apache/commons/math3/optimization/SimpleBounds.java
new file mode 100644
index 0000000..ebda3ca
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/optimization/SimpleBounds.java
@@ -0,0 +1,62 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math3.optimization;
+
+/**
+ * Simple optimization constraints: lower and upper bounds. The valid range of the parameters is an
+ * interval that can be infinite (in one or both directions). <br>
+ * Immutable class.
+ *
+ * @deprecated As of 3.1 (to be removed in 4.0).
+ * @since 3.1
+ */
+@Deprecated
+public class SimpleBounds implements OptimizationData {
+ /** Lower bounds. */
+ private final double[] lower;
+
+ /** Upper bounds. */
+ private final double[] upper;
+
+ /**
+ * @param lB Lower bounds.
+ * @param uB Upper bounds.
+ */
+ public SimpleBounds(double[] lB, double[] uB) {
+ lower = lB.clone();
+ upper = uB.clone();
+ }
+
+ /**
+ * Gets the lower bounds.
+ *
+ * @return the initial guess.
+ */
+ public double[] getLower() {
+ return lower.clone();
+ }
+
+ /**
+ * Gets the lower bounds.
+ *
+ * @return the initial guess.
+ */
+ public double[] getUpper() {
+ return upper.clone();
+ }
+}
diff --git a/src/main/java/org/apache/commons/math3/optimization/SimplePointChecker.java b/src/main/java/org/apache/commons/math3/optimization/SimplePointChecker.java
new file mode 100644
index 0000000..50ee2c5
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/optimization/SimplePointChecker.java
@@ -0,0 +1,130 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math3.optimization;
+
+import org.apache.commons.math3.exception.NotStrictlyPositiveException;
+import org.apache.commons.math3.util.FastMath;
+import org.apache.commons.math3.util.Pair;
+
+/**
+ * Simple implementation of the {@link ConvergenceChecker} interface using only point coordinates.
+ *
+ * <p>Convergence is considered to have been reached if either the relative difference between each
+ * point coordinate are smaller than a threshold or if either the absolute difference between the
+ * point coordinates are smaller than another threshold. <br>
+ * The {@link #converged(int,Pair,Pair) converged} method will also return {@code true} if the
+ * number of iterations has been set (see {@link #SimplePointChecker(double,double,int) this
+ * constructor}).
+ *
+ * @param <PAIR> Type of the (point, value) pair. The type of the "value" part of the pair (not used
+ * by this class).
+ * @deprecated As of 3.1 (to be removed in 4.0).
+ * @since 3.0
+ */
+@Deprecated
+public class SimplePointChecker<PAIR extends Pair<double[], ? extends Object>>
+ extends AbstractConvergenceChecker<PAIR> {
+ /**
+ * If {@link #maxIterationCount} is set to this value, the number of iterations will never cause
+ * {@link #converged(int, Pair, Pair)} to return {@code true}.
+ */
+ private static final int ITERATION_CHECK_DISABLED = -1;
+
+ /**
+ * Number of iterations after which the {@link #converged(int, Pair, Pair)} method will return
+ * true (unless the check is disabled).
+ */
+ private final int maxIterationCount;
+
+ /**
+ * Build an instance with default threshold.
+ *
+ * @deprecated See {@link AbstractConvergenceChecker#AbstractConvergenceChecker()}
+ */
+ @Deprecated
+ public SimplePointChecker() {
+ maxIterationCount = ITERATION_CHECK_DISABLED;
+ }
+
+ /**
+ * Build an instance with specified thresholds. In order to perform only relative checks, the
+ * absolute tolerance must be set to a negative value. In order to perform only absolute checks,
+ * the relative tolerance must be set to a negative value.
+ *
+ * @param relativeThreshold relative tolerance threshold
+ * @param absoluteThreshold absolute tolerance threshold
+ */
+ public SimplePointChecker(final double relativeThreshold, final double absoluteThreshold) {
+ super(relativeThreshold, absoluteThreshold);
+ maxIterationCount = ITERATION_CHECK_DISABLED;
+ }
+
+ /**
+ * Builds an instance with specified thresholds. In order to perform only relative checks, the
+ * absolute tolerance must be set to a negative value. In order to perform only absolute checks,
+ * the relative tolerance must be set to a negative value.
+ *
+ * @param relativeThreshold Relative tolerance threshold.
+ * @param absoluteThreshold Absolute tolerance threshold.
+ * @param maxIter Maximum iteration count.
+ * @throws NotStrictlyPositiveException if {@code maxIter <= 0}.
+ * @since 3.1
+ */
+ public SimplePointChecker(
+ final double relativeThreshold, final double absoluteThreshold, final int maxIter) {
+ super(relativeThreshold, absoluteThreshold);
+
+ if (maxIter <= 0) {
+ throw new NotStrictlyPositiveException(maxIter);
+ }
+ maxIterationCount = maxIter;
+ }
+
+ /**
+ * Check if the optimization algorithm has converged considering the last two points. This
+ * method may be called several times from the same algorithm iteration with different points.
+ * This can be detected by checking the iteration number at each call if needed. Each time this
+ * method is called, the previous and current point correspond to points with the same role at
+ * each iteration, so they can be compared. As an example, simplex-based algorithms call this
+ * method for all points of the simplex, not only for the best or worst ones.
+ *
+ * @param iteration Index of current iteration
+ * @param previous Best point in the previous iteration.
+ * @param current Best point in the current iteration.
+ * @return {@code true} if the arguments satify the convergence criterion.
+ */
+ @Override
+ public boolean converged(final int iteration, final PAIR previous, final PAIR current) {
+ if (maxIterationCount != ITERATION_CHECK_DISABLED && iteration >= maxIterationCount) {
+ return true;
+ }
+
+ final double[] p = previous.getKey();
+ final double[] c = current.getKey();
+ for (int i = 0; i < p.length; ++i) {
+ final double pi = p[i];
+ final double ci = c[i];
+ final double difference = FastMath.abs(pi - ci);
+ final double size = FastMath.max(FastMath.abs(pi), FastMath.abs(ci));
+ if (difference > size * getRelativeThreshold() && difference > getAbsoluteThreshold()) {
+ return false;
+ }
+ }
+ return true;
+ }
+}
diff --git a/src/main/java/org/apache/commons/math3/optimization/SimpleValueChecker.java b/src/main/java/org/apache/commons/math3/optimization/SimpleValueChecker.java
new file mode 100644
index 0000000..6f7a2c0
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/optimization/SimpleValueChecker.java
@@ -0,0 +1,125 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math3.optimization;
+
+import org.apache.commons.math3.exception.NotStrictlyPositiveException;
+import org.apache.commons.math3.util.FastMath;
+
+/**
+ * Simple implementation of the {@link ConvergenceChecker} interface using only objective function
+ * values.
+ *
+ * <p>Convergence is considered to have been reached if either the relative difference between the
+ * objective function values is smaller than a threshold or if either the absolute difference
+ * between the objective function values is smaller than another threshold. <br>
+ * The {@link #converged(int,PointValuePair,PointValuePair) converged} method will also return
+ * {@code true} if the number of iterations has been set (see {@link
+ * #SimpleValueChecker(double,double,int) this constructor}).
+ *
+ * @deprecated As of 3.1 (to be removed in 4.0).
+ * @since 3.0
+ */
+@Deprecated
+public class SimpleValueChecker extends AbstractConvergenceChecker<PointValuePair> {
+ /**
+ * If {@link #maxIterationCount} is set to this value, the number of iterations will never cause
+ * {@link #converged(int,PointValuePair,PointValuePair)} to return {@code true}.
+ */
+ private static final int ITERATION_CHECK_DISABLED = -1;
+
+ /**
+ * Number of iterations after which the {@link #converged(int,PointValuePair,PointValuePair)}
+ * method will return true (unless the check is disabled).
+ */
+ private final int maxIterationCount;
+
+ /**
+ * Build an instance with default thresholds.
+ *
+ * @deprecated See {@link AbstractConvergenceChecker#AbstractConvergenceChecker()}
+ */
+ @Deprecated
+ public SimpleValueChecker() {
+ maxIterationCount = ITERATION_CHECK_DISABLED;
+ }
+
+ /**
+ * Build an instance with specified thresholds.
+ *
+ * <p>In order to perform only relative checks, the absolute tolerance must be set to a negative
+ * value. In order to perform only absolute checks, the relative tolerance must be set to a
+ * negative value.
+ *
+ * @param relativeThreshold relative tolerance threshold
+ * @param absoluteThreshold absolute tolerance threshold
+ */
+ public SimpleValueChecker(final double relativeThreshold, final double absoluteThreshold) {
+ super(relativeThreshold, absoluteThreshold);
+ maxIterationCount = ITERATION_CHECK_DISABLED;
+ }
+
+ /**
+ * Builds an instance with specified thresholds.
+ *
+ * <p>In order to perform only relative checks, the absolute tolerance must be set to a negative
+ * value. In order to perform only absolute checks, the relative tolerance must be set to a
+ * negative value.
+ *
+ * @param relativeThreshold relative tolerance threshold
+ * @param absoluteThreshold absolute tolerance threshold
+ * @param maxIter Maximum iteration count.
+ * @throws NotStrictlyPositiveException if {@code maxIter <= 0}.
+ * @since 3.1
+ */
+ public SimpleValueChecker(
+ final double relativeThreshold, final double absoluteThreshold, final int maxIter) {
+ super(relativeThreshold, absoluteThreshold);
+
+ if (maxIter <= 0) {
+ throw new NotStrictlyPositiveException(maxIter);
+ }
+ maxIterationCount = maxIter;
+ }
+
+ /**
+ * Check if the optimization algorithm has converged considering the last two points. This
+ * method may be called several time from the same algorithm iteration with different points.
+ * This can be detected by checking the iteration number at each call if needed. Each time this
+ * method is called, the previous and current point correspond to points with the same role at
+ * each iteration, so they can be compared. As an example, simplex-based algorithms call this
+ * method for all points of the simplex, not only for the best or worst ones.
+ *
+ * @param iteration Index of current iteration
+ * @param previous Best point in the previous iteration.
+ * @param current Best point in the current iteration.
+ * @return {@code true} if the algorithm has converged.
+ */
+ @Override
+ public boolean converged(
+ final int iteration, final PointValuePair previous, final PointValuePair current) {
+ if (maxIterationCount != ITERATION_CHECK_DISABLED && iteration >= maxIterationCount) {
+ return true;
+ }
+
+ final double p = previous.getValue();
+ final double c = current.getValue();
+ final double difference = FastMath.abs(p - c);
+ final double size = FastMath.max(FastMath.abs(p), FastMath.abs(c));
+ return difference <= size * getRelativeThreshold() || difference <= getAbsoluteThreshold();
+ }
+}
diff --git a/src/main/java/org/apache/commons/math3/optimization/SimpleVectorValueChecker.java b/src/main/java/org/apache/commons/math3/optimization/SimpleVectorValueChecker.java
new file mode 100644
index 0000000..4ddb93d
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/optimization/SimpleVectorValueChecker.java
@@ -0,0 +1,137 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math3.optimization;
+
+import org.apache.commons.math3.exception.NotStrictlyPositiveException;
+import org.apache.commons.math3.util.FastMath;
+
+/**
+ * Simple implementation of the {@link ConvergenceChecker} interface using only objective function
+ * values.
+ *
+ * <p>Convergence is considered to have been reached if either the relative difference between the
+ * objective function values is smaller than a threshold or if either the absolute difference
+ * between the objective function values is smaller than another threshold for all vectors elements.
+ * <br>
+ * The {@link #converged(int,PointVectorValuePair,PointVectorValuePair) converged} method will also
+ * return {@code true} if the number of iterations has been set (see {@link
+ * #SimpleVectorValueChecker(double,double,int) this constructor}).
+ *
+ * @deprecated As of 3.1 (to be removed in 4.0).
+ * @since 3.0
+ */
+@Deprecated
+public class SimpleVectorValueChecker extends AbstractConvergenceChecker<PointVectorValuePair> {
+ /**
+ * If {@link #maxIterationCount} is set to this value, the number of iterations will never cause
+ * {@link #converged(int,PointVectorValuePair,PointVectorValuePair)} to return {@code true}.
+ */
+ private static final int ITERATION_CHECK_DISABLED = -1;
+
+ /**
+ * Number of iterations after which the {@link
+ * #converged(int,PointVectorValuePair,PointVectorValuePair)} method will return true (unless
+ * the check is disabled).
+ */
+ private final int maxIterationCount;
+
+ /**
+ * Build an instance with default thresholds.
+ *
+ * @deprecated See {@link AbstractConvergenceChecker#AbstractConvergenceChecker()}
+ */
+ @Deprecated
+ public SimpleVectorValueChecker() {
+ maxIterationCount = ITERATION_CHECK_DISABLED;
+ }
+
+ /**
+ * Build an instance with specified thresholds.
+ *
+ * <p>In order to perform only relative checks, the absolute tolerance must be set to a negative
+ * value. In order to perform only absolute checks, the relative tolerance must be set to a
+ * negative value.
+ *
+ * @param relativeThreshold relative tolerance threshold
+ * @param absoluteThreshold absolute tolerance threshold
+ */
+ public SimpleVectorValueChecker(
+ final double relativeThreshold, final double absoluteThreshold) {
+ super(relativeThreshold, absoluteThreshold);
+ maxIterationCount = ITERATION_CHECK_DISABLED;
+ }
+
+ /**
+ * Builds an instance with specified tolerance thresholds and iteration count.
+ *
+ * <p>In order to perform only relative checks, the absolute tolerance must be set to a negative
+ * value. In order to perform only absolute checks, the relative tolerance must be set to a
+ * negative value.
+ *
+ * @param relativeThreshold Relative tolerance threshold.
+ * @param absoluteThreshold Absolute tolerance threshold.
+ * @param maxIter Maximum iteration count.
+ * @throws NotStrictlyPositiveException if {@code maxIter <= 0}.
+ * @since 3.1
+ */
+ public SimpleVectorValueChecker(
+ final double relativeThreshold, final double absoluteThreshold, final int maxIter) {
+ super(relativeThreshold, absoluteThreshold);
+
+ if (maxIter <= 0) {
+ throw new NotStrictlyPositiveException(maxIter);
+ }
+ maxIterationCount = maxIter;
+ }
+
+ /**
+ * Check if the optimization algorithm has converged considering the last two points. This
+ * method may be called several times from the same algorithm iteration with different points.
+ * This can be detected by checking the iteration number at each call if needed. Each time this
+ * method is called, the previous and current point correspond to points with the same role at
+ * each iteration, so they can be compared. As an example, simplex-based algorithms call this
+ * method for all points of the simplex, not only for the best or worst ones.
+ *
+ * @param iteration Index of current iteration
+ * @param previous Best point in the previous iteration.
+ * @param current Best point in the current iteration.
+ * @return {@code true} if the arguments satify the convergence criterion.
+ */
+ @Override
+ public boolean converged(
+ final int iteration,
+ final PointVectorValuePair previous,
+ final PointVectorValuePair current) {
+ if (maxIterationCount != ITERATION_CHECK_DISABLED && iteration >= maxIterationCount) {
+ return true;
+ }
+
+ final double[] p = previous.getValueRef();
+ final double[] c = current.getValueRef();
+ for (int i = 0; i < p.length; ++i) {
+ final double pi = p[i];
+ final double ci = c[i];
+ final double difference = FastMath.abs(pi - ci);
+ final double size = FastMath.max(FastMath.abs(pi), FastMath.abs(ci));
+ if (difference > size * getRelativeThreshold() && difference > getAbsoluteThreshold()) {
+ return false;
+ }
+ }
+ return true;
+ }
+}
diff --git a/src/main/java/org/apache/commons/math3/optimization/Target.java b/src/main/java/org/apache/commons/math3/optimization/Target.java
new file mode 100644
index 0000000..43940ac
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/optimization/Target.java
@@ -0,0 +1,48 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math3.optimization;
+
+/**
+ * Target of the optimization procedure. They are the values which the objective vector function
+ * must reproduce When the parameters of the model have been optimized. <br>
+ * Immutable class.
+ *
+ * @deprecated As of 3.1 (to be removed in 4.0).
+ * @since 3.1
+ */
+@Deprecated
+public class Target implements OptimizationData {
+ /** Target values (of the objective vector function). */
+ private final double[] target;
+
+ /**
+ * @param observations Target values.
+ */
+ public Target(double[] observations) {
+ target = observations.clone();
+ }
+
+ /**
+ * Gets the initial guess.
+ *
+ * @return the initial guess.
+ */
+ public double[] getTarget() {
+ return target.clone();
+ }
+}
diff --git a/src/main/java/org/apache/commons/math3/optimization/Weight.java b/src/main/java/org/apache/commons/math3/optimization/Weight.java
new file mode 100644
index 0000000..83226b5
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/optimization/Weight.java
@@ -0,0 +1,66 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math3.optimization;
+
+import org.apache.commons.math3.linear.DiagonalMatrix;
+import org.apache.commons.math3.linear.NonSquareMatrixException;
+import org.apache.commons.math3.linear.RealMatrix;
+
+/**
+ * Weight matrix of the residuals between model and observations. <br>
+ * Immutable class.
+ *
+ * @deprecated As of 3.1 (to be removed in 4.0).
+ * @since 3.1
+ */
+@Deprecated
+public class Weight implements OptimizationData {
+ /** Weight matrix. */
+ private final RealMatrix weightMatrix;
+
+ /**
+ * Creates a diagonal weight matrix.
+ *
+ * @param weight List of the values of the diagonal.
+ */
+ public Weight(double[] weight) {
+ weightMatrix = new DiagonalMatrix(weight);
+ }
+
+ /**
+ * @param weight Weight matrix.
+ * @throws NonSquareMatrixException if the argument is not a square matrix.
+ */
+ public Weight(RealMatrix weight) {
+ if (weight.getColumnDimension() != weight.getRowDimension()) {
+ throw new NonSquareMatrixException(
+ weight.getColumnDimension(), weight.getRowDimension());
+ }
+
+ weightMatrix = weight.copy();
+ }
+
+ /**
+ * Gets the initial guess.
+ *
+ * @return the initial guess.
+ */
+ public RealMatrix getWeight() {
+ return weightMatrix.copy();
+ }
+}
diff --git a/src/main/java/org/apache/commons/math3/optimization/direct/AbstractSimplex.java b/src/main/java/org/apache/commons/math3/optimization/direct/AbstractSimplex.java
new file mode 100644
index 0000000..b229cd1
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/optimization/direct/AbstractSimplex.java
@@ -0,0 +1,347 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math3.optimization.direct;
+
+import java.util.Arrays;
+import java.util.Comparator;
+
+import org.apache.commons.math3.analysis.MultivariateFunction;
+import org.apache.commons.math3.exception.NotStrictlyPositiveException;
+import org.apache.commons.math3.exception.DimensionMismatchException;
+import org.apache.commons.math3.exception.ZeroException;
+import org.apache.commons.math3.exception.OutOfRangeException;
+import org.apache.commons.math3.exception.NullArgumentException;
+import org.apache.commons.math3.exception.MathIllegalArgumentException;
+import org.apache.commons.math3.exception.util.LocalizedFormats;
+import org.apache.commons.math3.optimization.PointValuePair;
+import org.apache.commons.math3.optimization.OptimizationData;
+
+/**
+ * This class implements the simplex concept.
+ * It is intended to be used in conjunction with {@link SimplexOptimizer}.
+ * <br/>
+ * The initial configuration of the simplex is set by the constructors
+ * {@link #AbstractSimplex(double[])} or {@link #AbstractSimplex(double[][])}.
+ * The other {@link #AbstractSimplex(int) constructor} will set all steps
+ * to 1, thus building a default configuration from a unit hypercube.
+ * <br/>
+ * Users <em>must</em> call the {@link #build(double[]) build} method in order
+ * to create the data structure that will be acted on by the other methods of
+ * this class.
+ *
+ * @see SimplexOptimizer
+ * @deprecated As of 3.1 (to be removed in 4.0).
+ * @since 3.0
+ */
+@Deprecated
+public abstract class AbstractSimplex implements OptimizationData {
+ /** Simplex. */
+ private PointValuePair[] simplex;
+ /** Start simplex configuration. */
+ private double[][] startConfiguration;
+ /** Simplex dimension (must be equal to {@code simplex.length - 1}). */
+ private final int dimension;
+
+ /**
+ * Build a unit hypercube simplex.
+ *
+ * @param n Dimension of the simplex.
+ */
+ protected AbstractSimplex(int n) {
+ this(n, 1d);
+ }
+
+ /**
+ * Build a hypercube simplex with the given side length.
+ *
+ * @param n Dimension of the simplex.
+ * @param sideLength Length of the sides of the hypercube.
+ */
+ protected AbstractSimplex(int n,
+ double sideLength) {
+ this(createHypercubeSteps(n, sideLength));
+ }
+
+ /**
+ * The start configuration for simplex is built from a box parallel to
+ * the canonical axes of the space. The simplex is the subset of vertices
+ * of a box parallel to the canonical axes. It is built as the path followed
+ * while traveling from one vertex of the box to the diagonally opposite
+ * vertex moving only along the box edges. The first vertex of the box will
+ * be located at the start point of the optimization.
+ * As an example, in dimension 3 a simplex has 4 vertices. Setting the
+ * steps to (1, 10, 2) and the start point to (1, 1, 1) would imply the
+ * start simplex would be: { (1, 1, 1), (2, 1, 1), (2, 11, 1), (2, 11, 3) }.
+ * The first vertex would be set to the start point at (1, 1, 1) and the
+ * last vertex would be set to the diagonally opposite vertex at (2, 11, 3).
+ *
+ * @param steps Steps along the canonical axes representing box edges. They
+ * may be negative but not zero.
+ * @throws NullArgumentException if {@code steps} is {@code null}.
+ * @throws ZeroException if one of the steps is zero.
+ */
+ protected AbstractSimplex(final double[] steps) {
+ if (steps == null) {
+ throw new NullArgumentException();
+ }
+ if (steps.length == 0) {
+ throw new ZeroException();
+ }
+ dimension = steps.length;
+
+ // Only the relative position of the n final vertices with respect
+ // to the first one are stored.
+ startConfiguration = new double[dimension][dimension];
+ for (int i = 0; i < dimension; i++) {
+ final double[] vertexI = startConfiguration[i];
+ for (int j = 0; j < i + 1; j++) {
+ if (steps[j] == 0) {
+ throw new ZeroException(LocalizedFormats.EQUAL_VERTICES_IN_SIMPLEX);
+ }
+ System.arraycopy(steps, 0, vertexI, 0, j + 1);
+ }
+ }
+ }
+
+ /**
+ * The real initial simplex will be set up by moving the reference
+ * simplex such that its first point is located at the start point of the
+ * optimization.
+ *
+ * @param referenceSimplex Reference simplex.
+ * @throws NotStrictlyPositiveException if the reference simplex does not
+ * contain at least one point.
+ * @throws DimensionMismatchException if there is a dimension mismatch
+ * in the reference simplex.
+ * @throws IllegalArgumentException if one of its vertices is duplicated.
+ */
+ protected AbstractSimplex(final double[][] referenceSimplex) {
+ if (referenceSimplex.length <= 0) {
+ throw new NotStrictlyPositiveException(LocalizedFormats.SIMPLEX_NEED_ONE_POINT,
+ referenceSimplex.length);
+ }
+ dimension = referenceSimplex.length - 1;
+
+ // Only the relative position of the n final vertices with respect
+ // to the first one are stored.
+ startConfiguration = new double[dimension][dimension];
+ final double[] ref0 = referenceSimplex[0];
+
+ // Loop over vertices.
+ for (int i = 0; i < referenceSimplex.length; i++) {
+ final double[] refI = referenceSimplex[i];
+
+ // Safety checks.
+ if (refI.length != dimension) {
+ throw new DimensionMismatchException(refI.length, dimension);
+ }
+ for (int j = 0; j < i; j++) {
+ final double[] refJ = referenceSimplex[j];
+ boolean allEquals = true;
+ for (int k = 0; k < dimension; k++) {
+ if (refI[k] != refJ[k]) {
+ allEquals = false;
+ break;
+ }
+ }
+ if (allEquals) {
+ throw new MathIllegalArgumentException(LocalizedFormats.EQUAL_VERTICES_IN_SIMPLEX,
+ i, j);
+ }
+ }
+
+ // Store vertex i position relative to vertex 0 position.
+ if (i > 0) {
+ final double[] confI = startConfiguration[i - 1];
+ for (int k = 0; k < dimension; k++) {
+ confI[k] = refI[k] - ref0[k];
+ }
+ }
+ }
+ }
+
+ /**
+ * Get simplex dimension.
+ *
+ * @return the dimension of the simplex.
+ */
+ public int getDimension() {
+ return dimension;
+ }
+
+ /**
+ * Get simplex size.
+ * After calling the {@link #build(double[]) build} method, this method will
+ * will be equivalent to {@code getDimension() + 1}.
+ *
+ * @return the size of the simplex.
+ */
+ public int getSize() {
+ return simplex.length;
+ }
+
+ /**
+ * Compute the next simplex of the algorithm.
+ *
+ * @param evaluationFunction Evaluation function.
+ * @param comparator Comparator to use to sort simplex vertices from best
+ * to worst.
+ * @throws org.apache.commons.math3.exception.TooManyEvaluationsException
+ * if the algorithm fails to converge.
+ */
+ public abstract void iterate(final MultivariateFunction evaluationFunction,
+ final Comparator<PointValuePair> comparator);
+
+ /**
+ * Build an initial simplex.
+ *
+ * @param startPoint First point of the simplex.
+ * @throws DimensionMismatchException if the start point does not match
+ * simplex dimension.
+ */
+ public void build(final double[] startPoint) {
+ if (dimension != startPoint.length) {
+ throw new DimensionMismatchException(dimension, startPoint.length);
+ }
+
+ // Set first vertex.
+ simplex = new PointValuePair[dimension + 1];
+ simplex[0] = new PointValuePair(startPoint, Double.NaN);
+
+ // Set remaining vertices.
+ for (int i = 0; i < dimension; i++) {
+ final double[] confI = startConfiguration[i];
+ final double[] vertexI = new double[dimension];
+ for (int k = 0; k < dimension; k++) {
+ vertexI[k] = startPoint[k] + confI[k];
+ }
+ simplex[i + 1] = new PointValuePair(vertexI, Double.NaN);
+ }
+ }
+
+ /**
+ * Evaluate all the non-evaluated points of the simplex.
+ *
+ * @param evaluationFunction Evaluation function.
+ * @param comparator Comparator to use to sort simplex vertices from best to worst.
+ * @throws org.apache.commons.math3.exception.TooManyEvaluationsException
+ * if the maximal number of evaluations is exceeded.
+ */
+ public void evaluate(final MultivariateFunction evaluationFunction,
+ final Comparator<PointValuePair> comparator) {
+ // Evaluate the objective function at all non-evaluated simplex points.
+ for (int i = 0; i < simplex.length; i++) {
+ final PointValuePair vertex = simplex[i];
+ final double[] point = vertex.getPointRef();
+ if (Double.isNaN(vertex.getValue())) {
+ simplex[i] = new PointValuePair(point, evaluationFunction.value(point), false);
+ }
+ }
+
+ // Sort the simplex from best to worst.
+ Arrays.sort(simplex, comparator);
+ }
+
+ /**
+ * Replace the worst point of the simplex by a new point.
+ *
+ * @param pointValuePair Point to insert.
+ * @param comparator Comparator to use for sorting the simplex vertices
+ * from best to worst.
+ */
+ protected void replaceWorstPoint(PointValuePair pointValuePair,
+ final Comparator<PointValuePair> comparator) {
+ for (int i = 0; i < dimension; i++) {
+ if (comparator.compare(simplex[i], pointValuePair) > 0) {
+ PointValuePair tmp = simplex[i];
+ simplex[i] = pointValuePair;
+ pointValuePair = tmp;
+ }
+ }
+ simplex[dimension] = pointValuePair;
+ }
+
+ /**
+ * Get the points of the simplex.
+ *
+ * @return all the simplex points.
+ */
+ public PointValuePair[] getPoints() {
+ final PointValuePair[] copy = new PointValuePair[simplex.length];
+ System.arraycopy(simplex, 0, copy, 0, simplex.length);
+ return copy;
+ }
+
+ /**
+ * Get the simplex point stored at the requested {@code index}.
+ *
+ * @param index Location.
+ * @return the point at location {@code index}.
+ */
+ public PointValuePair getPoint(int index) {
+ if (index < 0 ||
+ index >= simplex.length) {
+ throw new OutOfRangeException(index, 0, simplex.length - 1);
+ }
+ return simplex[index];
+ }
+
+ /**
+ * Store a new point at location {@code index}.
+ * Note that no deep-copy of {@code point} is performed.
+ *
+ * @param index Location.
+ * @param point New value.
+ */
+ protected void setPoint(int index, PointValuePair point) {
+ if (index < 0 ||
+ index >= simplex.length) {
+ throw new OutOfRangeException(index, 0, simplex.length - 1);
+ }
+ simplex[index] = point;
+ }
+
+ /**
+ * Replace all points.
+ * Note that no deep-copy of {@code points} is performed.
+ *
+ * @param points New Points.
+ */
+ protected void setPoints(PointValuePair[] points) {
+ if (points.length != simplex.length) {
+ throw new DimensionMismatchException(points.length, simplex.length);
+ }
+ simplex = points;
+ }
+
+ /**
+ * Create steps for a unit hypercube.
+ *
+ * @param n Dimension of the hypercube.
+ * @param sideLength Length of the sides of the hypercube.
+ * @return the steps.
+ */
+ private static double[] createHypercubeSteps(int n,
+ double sideLength) {
+ final double[] steps = new double[n];
+ for (int i = 0; i < n; i++) {
+ steps[i] = sideLength;
+ }
+ return steps;
+ }
+}
diff --git a/src/main/java/org/apache/commons/math3/optimization/direct/BOBYQAOptimizer.java b/src/main/java/org/apache/commons/math3/optimization/direct/BOBYQAOptimizer.java
new file mode 100644
index 0000000..78d2d2c
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/optimization/direct/BOBYQAOptimizer.java
@@ -0,0 +1,2480 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+// CHECKSTYLE: stop all
+package org.apache.commons.math3.optimization.direct;
+
+import org.apache.commons.math3.analysis.MultivariateFunction;
+import org.apache.commons.math3.exception.MathIllegalStateException;
+import org.apache.commons.math3.exception.NumberIsTooSmallException;
+import org.apache.commons.math3.exception.OutOfRangeException;
+import org.apache.commons.math3.exception.util.LocalizedFormats;
+import org.apache.commons.math3.linear.Array2DRowRealMatrix;
+import org.apache.commons.math3.linear.ArrayRealVector;
+import org.apache.commons.math3.linear.RealVector;
+import org.apache.commons.math3.optimization.GoalType;
+import org.apache.commons.math3.optimization.PointValuePair;
+import org.apache.commons.math3.optimization.MultivariateOptimizer;
+import org.apache.commons.math3.util.FastMath;
+
+/**
+ * Powell's BOBYQA algorithm. This implementation is translated and
+ * adapted from the Fortran version available
+ * <a href="http://plato.asu.edu/ftp/other_software/bobyqa.zip">here</a>.
+ * See <a href="http://www.optimization-online.org/DB_HTML/2010/05/2616.html">
+ * this paper</a> for an introduction.
+ * <br/>
+ * BOBYQA is particularly well suited for high dimensional problems
+ * where derivatives are not available. In most cases it outperforms the
+ * {@link PowellOptimizer} significantly. Stochastic algorithms like
+ * {@link CMAESOptimizer} succeed more often than BOBYQA, but are more
+ * expensive. BOBYQA could also be considered as a replacement of any
+ * derivative-based optimizer when the derivatives are approximated by
+ * finite differences.
+ *
+ * @deprecated As of 3.1 (to be removed in 4.0).
+ * @since 3.0
+ */
+@Deprecated
+public class BOBYQAOptimizer
+ extends BaseAbstractMultivariateSimpleBoundsOptimizer<MultivariateFunction>
+ implements MultivariateOptimizer {
+ /** Minimum dimension of the problem: {@value} */
+ public static final int MINIMUM_PROBLEM_DIMENSION = 2;
+ /** Default value for {@link #initialTrustRegionRadius}: {@value} . */
+ public static final double DEFAULT_INITIAL_RADIUS = 10.0;
+ /** Default value for {@link #stoppingTrustRegionRadius}: {@value} . */
+ public static final double DEFAULT_STOPPING_RADIUS = 1E-8;
+ /** Constant 0. */
+ private static final double ZERO = 0d;
+ /** Constant 1. */
+ private static final double ONE = 1d;
+ /** Constant 2. */
+ private static final double TWO = 2d;
+ /** Constant 10. */
+ private static final double TEN = 10d;
+ /** Constant 16. */
+ private static final double SIXTEEN = 16d;
+ /** Constant 250. */
+ private static final double TWO_HUNDRED_FIFTY = 250d;
+ /** Constant -1. */
+ private static final double MINUS_ONE = -ONE;
+ /** Constant 1/2. */
+ private static final double HALF = ONE / 2;
+ /** Constant 1/4. */
+ private static final double ONE_OVER_FOUR = ONE / 4;
+ /** Constant 1/8. */
+ private static final double ONE_OVER_EIGHT = ONE / 8;
+ /** Constant 1/10. */
+ private static final double ONE_OVER_TEN = ONE / 10;
+ /** Constant 1/1000. */
+ private static final double ONE_OVER_A_THOUSAND = ONE / 1000;
+
+ /**
+ * numberOfInterpolationPoints XXX
+ */
+ private final int numberOfInterpolationPoints;
+ /**
+ * initialTrustRegionRadius XXX
+ */
+ private double initialTrustRegionRadius;
+ /**
+ * stoppingTrustRegionRadius XXX
+ */
+ private final double stoppingTrustRegionRadius;
+ /** Goal type (minimize or maximize). */
+ private boolean isMinimize;
+ /**
+ * Current best values for the variables to be optimized.
+ * The vector will be changed in-place to contain the values of the least
+ * calculated objective function values.
+ */
+ private ArrayRealVector currentBest;
+ /** Differences between the upper and lower bounds. */
+ private double[] boundDifference;
+ /**
+ * Index of the interpolation point at the trust region center.
+ */
+ private int trustRegionCenterInterpolationPointIndex;
+ /**
+ * Last <em>n</em> columns of matrix H (where <em>n</em> is the dimension
+ * of the problem).
+ * XXX "bmat" in the original code.
+ */
+ private Array2DRowRealMatrix bMatrix;
+ /**
+ * Factorization of the leading <em>npt</em> square submatrix of H, this
+ * factorization being Z Z<sup>T</sup>, which provides both the correct
+ * rank and positive semi-definiteness.
+ * XXX "zmat" in the original code.
+ */
+ private Array2DRowRealMatrix zMatrix;
+ /**
+ * Coordinates of the interpolation points relative to {@link #originShift}.
+ * XXX "xpt" in the original code.
+ */
+ private Array2DRowRealMatrix interpolationPoints;
+ /**
+ * Shift of origin that should reduce the contributions from rounding
+ * errors to values of the model and Lagrange functions.
+ * XXX "xbase" in the original code.
+ */
+ private ArrayRealVector originShift;
+ /**
+ * Values of the objective function at the interpolation points.
+ * XXX "fval" in the original code.
+ */
+ private ArrayRealVector fAtInterpolationPoints;
+ /**
+ * Displacement from {@link #originShift} of the trust region center.
+ * XXX "xopt" in the original code.
+ */
+ private ArrayRealVector trustRegionCenterOffset;
+ /**
+ * Gradient of the quadratic model at {@link #originShift} +
+ * {@link #trustRegionCenterOffset}.
+ * XXX "gopt" in the original code.
+ */
+ private ArrayRealVector gradientAtTrustRegionCenter;
+ /**
+ * Differences {@link #getLowerBound()} - {@link #originShift}.
+ * All the components of every {@link #trustRegionCenterOffset} are going
+ * to satisfy the bounds<br/>
+ * {@link #getLowerBound() lowerBound}<sub>i</sub> &le;
+ * {@link #trustRegionCenterOffset}<sub>i</sub>,<br/>
+ * with appropriate equalities when {@link #trustRegionCenterOffset} is
+ * on a constraint boundary.
+ * XXX "sl" in the original code.
+ */
+ private ArrayRealVector lowerDifference;
+ /**
+ * Differences {@link #getUpperBound()} - {@link #originShift}
+ * All the components of every {@link #trustRegionCenterOffset} are going
+ * to satisfy the bounds<br/>
+ * {@link #trustRegionCenterOffset}<sub>i</sub> &le;
+ * {@link #getUpperBound() upperBound}<sub>i</sub>,<br/>
+ * with appropriate equalities when {@link #trustRegionCenterOffset} is
+ * on a constraint boundary.
+ * XXX "su" in the original code.
+ */
+ private ArrayRealVector upperDifference;
+ /**
+ * Parameters of the implicit second derivatives of the quadratic model.
+ * XXX "pq" in the original code.
+ */
+ private ArrayRealVector modelSecondDerivativesParameters;
+ /**
+ * Point chosen by function {@link #trsbox(double,ArrayRealVector,
+ * ArrayRealVector, ArrayRealVector,ArrayRealVector,ArrayRealVector) trsbox}
+ * or {@link #altmov(int,double) altmov}.
+ * Usually {@link #originShift} + {@link #newPoint} is the vector of
+ * variables for the next evaluation of the objective function.
+ * It also satisfies the constraints indicated in {@link #lowerDifference}
+ * and {@link #upperDifference}.
+ * XXX "xnew" in the original code.
+ */
+ private ArrayRealVector newPoint;
+ /**
+ * Alternative to {@link #newPoint}, chosen by
+ * {@link #altmov(int,double) altmov}.
+ * It may replace {@link #newPoint} in order to increase the denominator
+ * in the {@link #update(double, double, int) updating procedure}.
+ * XXX "xalt" in the original code.
+ */
+ private ArrayRealVector alternativeNewPoint;
+ /**
+ * Trial step from {@link #trustRegionCenterOffset} which is usually
+ * {@link #newPoint} - {@link #trustRegionCenterOffset}.
+ * XXX "d__" in the original code.
+ */
+ private ArrayRealVector trialStepPoint;
+ /**
+ * Values of the Lagrange functions at a new point.
+ * XXX "vlag" in the original code.
+ */
+ private ArrayRealVector lagrangeValuesAtNewPoint;
+ /**
+ * Explicit second derivatives of the quadratic model.
+ * XXX "hq" in the original code.
+ */
+ private ArrayRealVector modelSecondDerivativesValues;
+
+ /**
+ * @param numberOfInterpolationPoints Number of interpolation conditions.
+ * For a problem of dimension {@code n}, its value must be in the interval
+ * {@code [n+2, (n+1)(n+2)/2]}.
+ * Choices that exceed {@code 2n+1} are not recommended.
+ */
+ public BOBYQAOptimizer(int numberOfInterpolationPoints) {
+ this(numberOfInterpolationPoints,
+ DEFAULT_INITIAL_RADIUS,
+ DEFAULT_STOPPING_RADIUS);
+ }
+
+ /**
+ * @param numberOfInterpolationPoints Number of interpolation conditions.
+ * For a problem of dimension {@code n}, its value must be in the interval
+ * {@code [n+2, (n+1)(n+2)/2]}.
+ * Choices that exceed {@code 2n+1} are not recommended.
+ * @param initialTrustRegionRadius Initial trust region radius.
+ * @param stoppingTrustRegionRadius Stopping trust region radius.
+ */
+ public BOBYQAOptimizer(int numberOfInterpolationPoints,
+ double initialTrustRegionRadius,
+ double stoppingTrustRegionRadius) {
+ super(null); // No custom convergence criterion.
+ this.numberOfInterpolationPoints = numberOfInterpolationPoints;
+ this.initialTrustRegionRadius = initialTrustRegionRadius;
+ this.stoppingTrustRegionRadius = stoppingTrustRegionRadius;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ protected PointValuePair doOptimize() {
+ final double[] lowerBound = getLowerBound();
+ final double[] upperBound = getUpperBound();
+
+ // Validity checks.
+ setup(lowerBound, upperBound);
+
+ isMinimize = (getGoalType() == GoalType.MINIMIZE);
+ currentBest = new ArrayRealVector(getStartPoint());
+
+ final double value = bobyqa(lowerBound, upperBound);
+
+ return new PointValuePair(currentBest.getDataRef(),
+ isMinimize ? value : -value);
+ }
+
+ /**
+ * This subroutine seeks the least value of a function of many variables,
+ * by applying a trust region method that forms quadratic models by
+ * interpolation. There is usually some freedom in the interpolation
+ * conditions, which is taken up by minimizing the Frobenius norm of
+ * the change to the second derivative of the model, beginning with the
+ * zero matrix. The values of the variables are constrained by upper and
+ * lower bounds. The arguments of the subroutine are as follows.
+ *
+ * N must be set to the number of variables and must be at least two.
+ * NPT is the number of interpolation conditions. Its value must be in
+ * the interval [N+2,(N+1)(N+2)/2]. Choices that exceed 2*N+1 are not
+ * recommended.
+ * Initial values of the variables must be set in X(1),X(2),...,X(N). They
+ * will be changed to the values that give the least calculated F.
+ * For I=1,2,...,N, XL(I) and XU(I) must provide the lower and upper
+ * bounds, respectively, on X(I). The construction of quadratic models
+ * requires XL(I) to be strictly less than XU(I) for each I. Further,
+ * the contribution to a model from changes to the I-th variable is
+ * damaged severely by rounding errors if XU(I)-XL(I) is too small.
+ * RHOBEG and RHOEND must be set to the initial and final values of a trust
+ * region radius, so both must be positive with RHOEND no greater than
+ * RHOBEG. Typically, RHOBEG should be about one tenth of the greatest
+ * expected change to a variable, while RHOEND should indicate the
+ * accuracy that is required in the final values of the variables. An
+ * error return occurs if any of the differences XU(I)-XL(I), I=1,...,N,
+ * is less than 2*RHOBEG.
+ * MAXFUN must be set to an upper bound on the number of calls of CALFUN.
+ * The array W will be used for working space. Its length must be at least
+ * (NPT+5)*(NPT+N)+3*N*(N+5)/2.
+ *
+ * @param lowerBound Lower bounds.
+ * @param upperBound Upper bounds.
+ * @return the value of the objective at the optimum.
+ */
+ private double bobyqa(double[] lowerBound,
+ double[] upperBound) {
+ printMethod(); // XXX
+
+ final int n = currentBest.getDimension();
+
+ // Return if there is insufficient space between the bounds. Modify the
+ // initial X if necessary in order to avoid conflicts between the bounds
+ // and the construction of the first quadratic model. The lower and upper
+ // bounds on moves from the updated X are set now, in the ISL and ISU
+ // partitions of W, in order to provide useful and exact information about
+ // components of X that become within distance RHOBEG from their bounds.
+
+ for (int j = 0; j < n; j++) {
+ final double boundDiff = boundDifference[j];
+ lowerDifference.setEntry(j, lowerBound[j] - currentBest.getEntry(j));
+ upperDifference.setEntry(j, upperBound[j] - currentBest.getEntry(j));
+ if (lowerDifference.getEntry(j) >= -initialTrustRegionRadius) {
+ if (lowerDifference.getEntry(j) >= ZERO) {
+ currentBest.setEntry(j, lowerBound[j]);
+ lowerDifference.setEntry(j, ZERO);
+ upperDifference.setEntry(j, boundDiff);
+ } else {
+ currentBest.setEntry(j, lowerBound[j] + initialTrustRegionRadius);
+ lowerDifference.setEntry(j, -initialTrustRegionRadius);
+ // Computing MAX
+ final double deltaOne = upperBound[j] - currentBest.getEntry(j);
+ upperDifference.setEntry(j, FastMath.max(deltaOne, initialTrustRegionRadius));
+ }
+ } else if (upperDifference.getEntry(j) <= initialTrustRegionRadius) {
+ if (upperDifference.getEntry(j) <= ZERO) {
+ currentBest.setEntry(j, upperBound[j]);
+ lowerDifference.setEntry(j, -boundDiff);
+ upperDifference.setEntry(j, ZERO);
+ } else {
+ currentBest.setEntry(j, upperBound[j] - initialTrustRegionRadius);
+ // Computing MIN
+ final double deltaOne = lowerBound[j] - currentBest.getEntry(j);
+ final double deltaTwo = -initialTrustRegionRadius;
+ lowerDifference.setEntry(j, FastMath.min(deltaOne, deltaTwo));
+ upperDifference.setEntry(j, initialTrustRegionRadius);
+ }
+ }
+ }
+
+ // Make the call of BOBYQB.
+
+ return bobyqb(lowerBound, upperBound);
+ } // bobyqa
+
+ // ----------------------------------------------------------------------------------------
+
+ /**
+ * The arguments N, NPT, X, XL, XU, RHOBEG, RHOEND, IPRINT and MAXFUN
+ * are identical to the corresponding arguments in SUBROUTINE BOBYQA.
+ * XBASE holds a shift of origin that should reduce the contributions
+ * from rounding errors to values of the model and Lagrange functions.
+ * XPT is a two-dimensional array that holds the coordinates of the
+ * interpolation points relative to XBASE.
+ * FVAL holds the values of F at the interpolation points.
+ * XOPT is set to the displacement from XBASE of the trust region centre.
+ * GOPT holds the gradient of the quadratic model at XBASE+XOPT.
+ * HQ holds the explicit second derivatives of the quadratic model.
+ * PQ contains the parameters of the implicit second derivatives of the
+ * quadratic model.
+ * BMAT holds the last N columns of H.
+ * ZMAT holds the factorization of the leading NPT by NPT submatrix of H,
+ * this factorization being ZMAT times ZMAT^T, which provides both the
+ * correct rank and positive semi-definiteness.
+ * NDIM is the first dimension of BMAT and has the value NPT+N.
+ * SL and SU hold the differences XL-XBASE and XU-XBASE, respectively.
+ * All the components of every XOPT are going to satisfy the bounds
+ * SL(I) .LEQ. XOPT(I) .LEQ. SU(I), with appropriate equalities when
+ * XOPT is on a constraint boundary.
+ * XNEW is chosen by SUBROUTINE TRSBOX or ALTMOV. Usually XBASE+XNEW is the
+ * vector of variables for the next call of CALFUN. XNEW also satisfies
+ * the SL and SU constraints in the way that has just been mentioned.
+ * XALT is an alternative to XNEW, chosen by ALTMOV, that may replace XNEW
+ * in order to increase the denominator in the updating of UPDATE.
+ * D is reserved for a trial step from XOPT, which is usually XNEW-XOPT.
+ * VLAG contains the values of the Lagrange functions at a new point X.
+ * They are part of a product that requires VLAG to be of length NDIM.
+ * W is a one-dimensional array that is used for working space. Its length
+ * must be at least 3*NDIM = 3*(NPT+N).
+ *
+ * @param lowerBound Lower bounds.
+ * @param upperBound Upper bounds.
+ * @return the value of the objective at the optimum.
+ */
+ private double bobyqb(double[] lowerBound,
+ double[] upperBound) {
+ printMethod(); // XXX
+
+ final int n = currentBest.getDimension();
+ final int npt = numberOfInterpolationPoints;
+ final int np = n + 1;
+ final int nptm = npt - np;
+ final int nh = n * np / 2;
+
+ final ArrayRealVector work1 = new ArrayRealVector(n);
+ final ArrayRealVector work2 = new ArrayRealVector(npt);
+ final ArrayRealVector work3 = new ArrayRealVector(npt);
+
+ double cauchy = Double.NaN;
+ double alpha = Double.NaN;
+ double dsq = Double.NaN;
+ double crvmin = Double.NaN;
+
+ // Set some constants.
+ // Parameter adjustments
+
+ // Function Body
+
+ // The call of PRELIM sets the elements of XBASE, XPT, FVAL, GOPT, HQ, PQ,
+ // BMAT and ZMAT for the first iteration, with the corresponding values of
+ // of NF and KOPT, which are the number of calls of CALFUN so far and the
+ // index of the interpolation point at the trust region centre. Then the
+ // initial XOPT is set too. The branch to label 720 occurs if MAXFUN is
+ // less than NPT. GOPT will be updated if KOPT is different from KBASE.
+
+ trustRegionCenterInterpolationPointIndex = 0;
+
+ prelim(lowerBound, upperBound);
+ double xoptsq = ZERO;
+ for (int i = 0; i < n; i++) {
+ trustRegionCenterOffset.setEntry(i, interpolationPoints.getEntry(trustRegionCenterInterpolationPointIndex, i));
+ // Computing 2nd power
+ final double deltaOne = trustRegionCenterOffset.getEntry(i);
+ xoptsq += deltaOne * deltaOne;
+ }
+ double fsave = fAtInterpolationPoints.getEntry(0);
+ final int kbase = 0;
+
+ // Complete the settings that are required for the iterative procedure.
+
+ int ntrits = 0;
+ int itest = 0;
+ int knew = 0;
+ int nfsav = getEvaluations();
+ double rho = initialTrustRegionRadius;
+ double delta = rho;
+ double diffa = ZERO;
+ double diffb = ZERO;
+ double diffc = ZERO;
+ double f = ZERO;
+ double beta = ZERO;
+ double adelt = ZERO;
+ double denom = ZERO;
+ double ratio = ZERO;
+ double dnorm = ZERO;
+ double scaden = ZERO;
+ double biglsq = ZERO;
+ double distsq = ZERO;
+
+ // Update GOPT if necessary before the first iteration and after each
+ // call of RESCUE that makes a call of CALFUN.
+
+ int state = 20;
+ for(;;) {
+ switch (state) {
+ case 20: {
+ printState(20); // XXX
+ if (trustRegionCenterInterpolationPointIndex != kbase) {
+ int ih = 0;
+ for (int j = 0; j < n; j++) {
+ for (int i = 0; i <= j; i++) {
+ if (i < j) {
+ gradientAtTrustRegionCenter.setEntry(j, gradientAtTrustRegionCenter.getEntry(j) + modelSecondDerivativesValues.getEntry(ih) * trustRegionCenterOffset.getEntry(i));
+ }
+ gradientAtTrustRegionCenter.setEntry(i, gradientAtTrustRegionCenter.getEntry(i) + modelSecondDerivativesValues.getEntry(ih) * trustRegionCenterOffset.getEntry(j));
+ ih++;
+ }
+ }
+ if (getEvaluations() > npt) {
+ for (int k = 0; k < npt; k++) {
+ double temp = ZERO;
+ for (int j = 0; j < n; j++) {
+ temp += interpolationPoints.getEntry(k, j) * trustRegionCenterOffset.getEntry(j);
+ }
+ temp *= modelSecondDerivativesParameters.getEntry(k);
+ for (int i = 0; i < n; i++) {
+ gradientAtTrustRegionCenter.setEntry(i, gradientAtTrustRegionCenter.getEntry(i) + temp * interpolationPoints.getEntry(k, i));
+ }
+ }
+ // throw new PathIsExploredException(); // XXX
+ }
+ }
+
+ // Generate the next point in the trust region that provides a small value
+ // of the quadratic model subject to the constraints on the variables.
+ // The int NTRITS is set to the number "trust region" iterations that
+ // have occurred since the last "alternative" iteration. If the length
+ // of XNEW-XOPT is less than HALF*RHO, however, then there is a branch to
+ // label 650 or 680 with NTRITS=-1, instead of calculating F at XNEW.
+
+ }
+ case 60: {
+ printState(60); // XXX
+ final ArrayRealVector gnew = new ArrayRealVector(n);
+ final ArrayRealVector xbdi = new ArrayRealVector(n);
+ final ArrayRealVector s = new ArrayRealVector(n);
+ final ArrayRealVector hs = new ArrayRealVector(n);
+ final ArrayRealVector hred = new ArrayRealVector(n);
+
+ final double[] dsqCrvmin = trsbox(delta, gnew, xbdi, s,
+ hs, hred);
+ dsq = dsqCrvmin[0];
+ crvmin = dsqCrvmin[1];
+
+ // Computing MIN
+ double deltaOne = delta;
+ double deltaTwo = FastMath.sqrt(dsq);
+ dnorm = FastMath.min(deltaOne, deltaTwo);
+ if (dnorm < HALF * rho) {
+ ntrits = -1;
+ // Computing 2nd power
+ deltaOne = TEN * rho;
+ distsq = deltaOne * deltaOne;
+ if (getEvaluations() <= nfsav + 2) {
+ state = 650; break;
+ }
+
+ // The following choice between labels 650 and 680 depends on whether or
+ // not our work with the current RHO seems to be complete. Either RHO is
+ // decreased or termination occurs if the errors in the quadratic model at
+ // the last three interpolation points compare favourably with predictions
+ // of likely improvements to the model within distance HALF*RHO of XOPT.
+
+ // Computing MAX
+ deltaOne = FastMath.max(diffa, diffb);
+ final double errbig = FastMath.max(deltaOne, diffc);
+ final double frhosq = rho * ONE_OVER_EIGHT * rho;
+ if (crvmin > ZERO &&
+ errbig > frhosq * crvmin) {
+ state = 650; break;
+ }
+ final double bdtol = errbig / rho;
+ for (int j = 0; j < n; j++) {
+ double bdtest = bdtol;
+ if (newPoint.getEntry(j) == lowerDifference.getEntry(j)) {
+ bdtest = work1.getEntry(j);
+ }
+ if (newPoint.getEntry(j) == upperDifference.getEntry(j)) {
+ bdtest = -work1.getEntry(j);
+ }
+ if (bdtest < bdtol) {
+ double curv = modelSecondDerivativesValues.getEntry((j + j * j) / 2);
+ for (int k = 0; k < npt; k++) {
+ // Computing 2nd power
+ final double d1 = interpolationPoints.getEntry(k, j);
+ curv += modelSecondDerivativesParameters.getEntry(k) * (d1 * d1);
+ }
+ bdtest += HALF * curv * rho;
+ if (bdtest < bdtol) {
+ state = 650; break;
+ }
+ // throw new PathIsExploredException(); // XXX
+ }
+ }
+ state = 680; break;
+ }
+ ++ntrits;
+
+ // Severe cancellation is likely to occur if XOPT is too far from XBASE.
+ // If the following test holds, then XBASE is shifted so that XOPT becomes
+ // zero. The appropriate changes are made to BMAT and to the second
+ // derivatives of the current model, beginning with the changes to BMAT
+ // that do not depend on ZMAT. VLAG is used temporarily for working space.
+
+ }
+ case 90: {
+ printState(90); // XXX
+ if (dsq <= xoptsq * ONE_OVER_A_THOUSAND) {
+ final double fracsq = xoptsq * ONE_OVER_FOUR;
+ double sumpq = ZERO;
+ // final RealVector sumVector
+ // = new ArrayRealVector(npt, -HALF * xoptsq).add(interpolationPoints.operate(trustRegionCenter));
+ for (int k = 0; k < npt; k++) {
+ sumpq += modelSecondDerivativesParameters.getEntry(k);
+ double sum = -HALF * xoptsq;
+ for (int i = 0; i < n; i++) {
+ sum += interpolationPoints.getEntry(k, i) * trustRegionCenterOffset.getEntry(i);
+ }
+ // sum = sumVector.getEntry(k); // XXX "testAckley" and "testDiffPow" fail.
+ work2.setEntry(k, sum);
+ final double temp = fracsq - HALF * sum;
+ for (int i = 0; i < n; i++) {
+ work1.setEntry(i, bMatrix.getEntry(k, i));
+ lagrangeValuesAtNewPoint.setEntry(i, sum * interpolationPoints.getEntry(k, i) + temp * trustRegionCenterOffset.getEntry(i));
+ final int ip = npt + i;
+ for (int j = 0; j <= i; j++) {
+ bMatrix.setEntry(ip, j,
+ bMatrix.getEntry(ip, j)
+ + work1.getEntry(i) * lagrangeValuesAtNewPoint.getEntry(j)
+ + lagrangeValuesAtNewPoint.getEntry(i) * work1.getEntry(j));
+ }
+ }
+ }
+
+ // Then the revisions of BMAT that depend on ZMAT are calculated.
+
+ for (int m = 0; m < nptm; m++) {
+ double sumz = ZERO;
+ double sumw = ZERO;
+ for (int k = 0; k < npt; k++) {
+ sumz += zMatrix.getEntry(k, m);
+ lagrangeValuesAtNewPoint.setEntry(k, work2.getEntry(k) * zMatrix.getEntry(k, m));
+ sumw += lagrangeValuesAtNewPoint.getEntry(k);
+ }
+ for (int j = 0; j < n; j++) {
+ double sum = (fracsq * sumz - HALF * sumw) * trustRegionCenterOffset.getEntry(j);
+ for (int k = 0; k < npt; k++) {
+ sum += lagrangeValuesAtNewPoint.getEntry(k) * interpolationPoints.getEntry(k, j);
+ }
+ work1.setEntry(j, sum);
+ for (int k = 0; k < npt; k++) {
+ bMatrix.setEntry(k, j,
+ bMatrix.getEntry(k, j)
+ + sum * zMatrix.getEntry(k, m));
+ }
+ }
+ for (int i = 0; i < n; i++) {
+ final int ip = i + npt;
+ final double temp = work1.getEntry(i);
+ for (int j = 0; j <= i; j++) {
+ bMatrix.setEntry(ip, j,
+ bMatrix.getEntry(ip, j)
+ + temp * work1.getEntry(j));
+ }
+ }
+ }
+
+ // The following instructions complete the shift, including the changes
+ // to the second derivative parameters of the quadratic model.
+
+ int ih = 0;
+ for (int j = 0; j < n; j++) {
+ work1.setEntry(j, -HALF * sumpq * trustRegionCenterOffset.getEntry(j));
+ for (int k = 0; k < npt; k++) {
+ work1.setEntry(j, work1.getEntry(j) + modelSecondDerivativesParameters.getEntry(k) * interpolationPoints.getEntry(k, j));
+ interpolationPoints.setEntry(k, j, interpolationPoints.getEntry(k, j) - trustRegionCenterOffset.getEntry(j));
+ }
+ for (int i = 0; i <= j; i++) {
+ modelSecondDerivativesValues.setEntry(ih,
+ modelSecondDerivativesValues.getEntry(ih)
+ + work1.getEntry(i) * trustRegionCenterOffset.getEntry(j)
+ + trustRegionCenterOffset.getEntry(i) * work1.getEntry(j));
+ bMatrix.setEntry(npt + i, j, bMatrix.getEntry(npt + j, i));
+ ih++;
+ }
+ }
+ for (int i = 0; i < n; i++) {
+ originShift.setEntry(i, originShift.getEntry(i) + trustRegionCenterOffset.getEntry(i));
+ newPoint.setEntry(i, newPoint.getEntry(i) - trustRegionCenterOffset.getEntry(i));
+ lowerDifference.setEntry(i, lowerDifference.getEntry(i) - trustRegionCenterOffset.getEntry(i));
+ upperDifference.setEntry(i, upperDifference.getEntry(i) - trustRegionCenterOffset.getEntry(i));
+ trustRegionCenterOffset.setEntry(i, ZERO);
+ }
+ xoptsq = ZERO;
+ }
+ if (ntrits == 0) {
+ state = 210; break;
+ }
+ state = 230; break;
+
+ // XBASE is also moved to XOPT by a call of RESCUE. This calculation is
+ // more expensive than the previous shift, because new matrices BMAT and
+ // ZMAT are generated from scratch, which may include the replacement of
+ // interpolation points whose positions seem to be causing near linear
+ // dependence in the interpolation conditions. Therefore RESCUE is called
+ // only if rounding errors have reduced by at least a factor of two the
+ // denominator of the formula for updating the H matrix. It provides a
+ // useful safeguard, but is not invoked in most applications of BOBYQA.
+
+ }
+ case 210: {
+ printState(210); // XXX
+ // Pick two alternative vectors of variables, relative to XBASE, that
+ // are suitable as new positions of the KNEW-th interpolation point.
+ // Firstly, XNEW is set to the point on a line through XOPT and another
+ // interpolation point that minimizes the predicted value of the next
+ // denominator, subject to ||XNEW - XOPT|| .LEQ. ADELT and to the SL
+ // and SU bounds. Secondly, XALT is set to the best feasible point on
+ // a constrained version of the Cauchy step of the KNEW-th Lagrange
+ // function, the corresponding value of the square of this function
+ // being returned in CAUCHY. The choice between these alternatives is
+ // going to be made when the denominator is calculated.
+
+ final double[] alphaCauchy = altmov(knew, adelt);
+ alpha = alphaCauchy[0];
+ cauchy = alphaCauchy[1];
+
+ for (int i = 0; i < n; i++) {
+ trialStepPoint.setEntry(i, newPoint.getEntry(i) - trustRegionCenterOffset.getEntry(i));
+ }
+
+ // Calculate VLAG and BETA for the current choice of D. The scalar
+ // product of D with XPT(K,.) is going to be held in W(NPT+K) for
+ // use when VQUAD is calculated.
+
+ }
+ case 230: {
+ printState(230); // XXX
+ for (int k = 0; k < npt; k++) {
+ double suma = ZERO;
+ double sumb = ZERO;
+ double sum = ZERO;
+ for (int j = 0; j < n; j++) {
+ suma += interpolationPoints.getEntry(k, j) * trialStepPoint.getEntry(j);
+ sumb += interpolationPoints.getEntry(k, j) * trustRegionCenterOffset.getEntry(j);
+ sum += bMatrix.getEntry(k, j) * trialStepPoint.getEntry(j);
+ }
+ work3.setEntry(k, suma * (HALF * suma + sumb));
+ lagrangeValuesAtNewPoint.setEntry(k, sum);
+ work2.setEntry(k, suma);
+ }
+ beta = ZERO;
+ for (int m = 0; m < nptm; m++) {
+ double sum = ZERO;
+ for (int k = 0; k < npt; k++) {
+ sum += zMatrix.getEntry(k, m) * work3.getEntry(k);
+ }
+ beta -= sum * sum;
+ for (int k = 0; k < npt; k++) {
+ lagrangeValuesAtNewPoint.setEntry(k, lagrangeValuesAtNewPoint.getEntry(k) + sum * zMatrix.getEntry(k, m));
+ }
+ }
+ dsq = ZERO;
+ double bsum = ZERO;
+ double dx = ZERO;
+ for (int j = 0; j < n; j++) {
+ // Computing 2nd power
+ final double d1 = trialStepPoint.getEntry(j);
+ dsq += d1 * d1;
+ double sum = ZERO;
+ for (int k = 0; k < npt; k++) {
+ sum += work3.getEntry(k) * bMatrix.getEntry(k, j);
+ }
+ bsum += sum * trialStepPoint.getEntry(j);
+ final int jp = npt + j;
+ for (int i = 0; i < n; i++) {
+ sum += bMatrix.getEntry(jp, i) * trialStepPoint.getEntry(i);
+ }
+ lagrangeValuesAtNewPoint.setEntry(jp, sum);
+ bsum += sum * trialStepPoint.getEntry(j);
+ dx += trialStepPoint.getEntry(j) * trustRegionCenterOffset.getEntry(j);
+ }
+
+ beta = dx * dx + dsq * (xoptsq + dx + dx + HALF * dsq) + beta - bsum; // Original
+ // beta += dx * dx + dsq * (xoptsq + dx + dx + HALF * dsq) - bsum; // XXX "testAckley" and "testDiffPow" fail.
+ // beta = dx * dx + dsq * (xoptsq + 2 * dx + HALF * dsq) + beta - bsum; // XXX "testDiffPow" fails.
+
+ lagrangeValuesAtNewPoint.setEntry(trustRegionCenterInterpolationPointIndex,
+ lagrangeValuesAtNewPoint.getEntry(trustRegionCenterInterpolationPointIndex) + ONE);
+
+ // If NTRITS is zero, the denominator may be increased by replacing
+ // the step D of ALTMOV by a Cauchy step. Then RESCUE may be called if
+ // rounding errors have damaged the chosen denominator.
+
+ if (ntrits == 0) {
+ // Computing 2nd power
+ final double d1 = lagrangeValuesAtNewPoint.getEntry(knew);
+ denom = d1 * d1 + alpha * beta;
+ if (denom < cauchy && cauchy > ZERO) {
+ for (int i = 0; i < n; i++) {
+ newPoint.setEntry(i, alternativeNewPoint.getEntry(i));
+ trialStepPoint.setEntry(i, newPoint.getEntry(i) - trustRegionCenterOffset.getEntry(i));
+ }
+ cauchy = ZERO; // XXX Useful statement?
+ state = 230; break;
+ }
+ // Alternatively, if NTRITS is positive, then set KNEW to the index of
+ // the next interpolation point to be deleted to make room for a trust
+ // region step. Again RESCUE may be called if rounding errors have damaged_
+ // the chosen denominator, which is the reason for attempting to select
+ // KNEW before calculating the next value of the objective function.
+
+ } else {
+ final double delsq = delta * delta;
+ scaden = ZERO;
+ biglsq = ZERO;
+ knew = 0;
+ for (int k = 0; k < npt; k++) {
+ if (k == trustRegionCenterInterpolationPointIndex) {
+ continue;
+ }
+ double hdiag = ZERO;
+ for (int m = 0; m < nptm; m++) {
+ // Computing 2nd power
+ final double d1 = zMatrix.getEntry(k, m);
+ hdiag += d1 * d1;
+ }
+ // Computing 2nd power
+ final double d2 = lagrangeValuesAtNewPoint.getEntry(k);
+ final double den = beta * hdiag + d2 * d2;
+ distsq = ZERO;
+ for (int j = 0; j < n; j++) {
+ // Computing 2nd power
+ final double d3 = interpolationPoints.getEntry(k, j) - trustRegionCenterOffset.getEntry(j);
+ distsq += d3 * d3;
+ }
+ // Computing MAX
+ // Computing 2nd power
+ final double d4 = distsq / delsq;
+ final double temp = FastMath.max(ONE, d4 * d4);
+ if (temp * den > scaden) {
+ scaden = temp * den;
+ knew = k;
+ denom = den;
+ }
+ // Computing MAX
+ // Computing 2nd power
+ final double d5 = lagrangeValuesAtNewPoint.getEntry(k);
+ biglsq = FastMath.max(biglsq, temp * (d5 * d5));
+ }
+ }
+
+ // Put the variables for the next calculation of the objective function
+ // in XNEW, with any adjustments for the bounds.
+
+ // Calculate the value of the objective function at XBASE+XNEW, unless
+ // the limit on the number of calculations of F has been reached.
+
+ }
+ case 360: {
+ printState(360); // XXX
+ for (int i = 0; i < n; i++) {
+ // Computing MIN
+ // Computing MAX
+ final double d3 = lowerBound[i];
+ final double d4 = originShift.getEntry(i) + newPoint.getEntry(i);
+ final double d1 = FastMath.max(d3, d4);
+ final double d2 = upperBound[i];
+ currentBest.setEntry(i, FastMath.min(d1, d2));
+ if (newPoint.getEntry(i) == lowerDifference.getEntry(i)) {
+ currentBest.setEntry(i, lowerBound[i]);
+ }
+ if (newPoint.getEntry(i) == upperDifference.getEntry(i)) {
+ currentBest.setEntry(i, upperBound[i]);
+ }
+ }
+
+ f = computeObjectiveValue(currentBest.toArray());
+
+ if (!isMinimize) {
+ f = -f;
+ }
+ if (ntrits == -1) {
+ fsave = f;
+ state = 720; break;
+ }
+
+ // Use the quadratic model to predict the change in F due to the step D,
+ // and set DIFF to the error of this prediction.
+
+ final double fopt = fAtInterpolationPoints.getEntry(trustRegionCenterInterpolationPointIndex);
+ double vquad = ZERO;
+ int ih = 0;
+ for (int j = 0; j < n; j++) {
+ vquad += trialStepPoint.getEntry(j) * gradientAtTrustRegionCenter.getEntry(j);
+ for (int i = 0; i <= j; i++) {
+ double temp = trialStepPoint.getEntry(i) * trialStepPoint.getEntry(j);
+ if (i == j) {
+ temp *= HALF;
+ }
+ vquad += modelSecondDerivativesValues.getEntry(ih) * temp;
+ ih++;
+ }
+ }
+ for (int k = 0; k < npt; k++) {
+ // Computing 2nd power
+ final double d1 = work2.getEntry(k);
+ final double d2 = d1 * d1; // "d1" must be squared first to prevent test failures.
+ vquad += HALF * modelSecondDerivativesParameters.getEntry(k) * d2;
+ }
+ final double diff = f - fopt - vquad;
+ diffc = diffb;
+ diffb = diffa;
+ diffa = FastMath.abs(diff);
+ if (dnorm > rho) {
+ nfsav = getEvaluations();
+ }
+
+ // Pick the next value of DELTA after a trust region step.
+
+ if (ntrits > 0) {
+ if (vquad >= ZERO) {
+ throw new MathIllegalStateException(LocalizedFormats.TRUST_REGION_STEP_FAILED, vquad);
+ }
+ ratio = (f - fopt) / vquad;
+ final double hDelta = HALF * delta;
+ if (ratio <= ONE_OVER_TEN) {
+ // Computing MIN
+ delta = FastMath.min(hDelta, dnorm);
+ } else if (ratio <= .7) {
+ // Computing MAX
+ delta = FastMath.max(hDelta, dnorm);
+ } else {
+ // Computing MAX
+ delta = FastMath.max(hDelta, 2 * dnorm);
+ }
+ if (delta <= rho * 1.5) {
+ delta = rho;
+ }
+
+ // Recalculate KNEW and DENOM if the new F is less than FOPT.
+
+ if (f < fopt) {
+ final int ksav = knew;
+ final double densav = denom;
+ final double delsq = delta * delta;
+ scaden = ZERO;
+ biglsq = ZERO;
+ knew = 0;
+ for (int k = 0; k < npt; k++) {
+ double hdiag = ZERO;
+ for (int m = 0; m < nptm; m++) {
+ // Computing 2nd power
+ final double d1 = zMatrix.getEntry(k, m);
+ hdiag += d1 * d1;
+ }
+ // Computing 2nd power
+ final double d1 = lagrangeValuesAtNewPoint.getEntry(k);
+ final double den = beta * hdiag + d1 * d1;
+ distsq = ZERO;
+ for (int j = 0; j < n; j++) {
+ // Computing 2nd power
+ final double d2 = interpolationPoints.getEntry(k, j) - newPoint.getEntry(j);
+ distsq += d2 * d2;
+ }
+ // Computing MAX
+ // Computing 2nd power
+ final double d3 = distsq / delsq;
+ final double temp = FastMath.max(ONE, d3 * d3);
+ if (temp * den > scaden) {
+ scaden = temp * den;
+ knew = k;
+ denom = den;
+ }
+ // Computing MAX
+ // Computing 2nd power
+ final double d4 = lagrangeValuesAtNewPoint.getEntry(k);
+ final double d5 = temp * (d4 * d4);
+ biglsq = FastMath.max(biglsq, d5);
+ }
+ if (scaden <= HALF * biglsq) {
+ knew = ksav;
+ denom = densav;
+ }
+ }
+ }
+
+ // Update BMAT and ZMAT, so that the KNEW-th interpolation point can be
+ // moved. Also update the second derivative terms of the model.
+
+ update(beta, denom, knew);
+
+ ih = 0;
+ final double pqold = modelSecondDerivativesParameters.getEntry(knew);
+ modelSecondDerivativesParameters.setEntry(knew, ZERO);
+ for (int i = 0; i < n; i++) {
+ final double temp = pqold * interpolationPoints.getEntry(knew, i);
+ for (int j = 0; j <= i; j++) {
+ modelSecondDerivativesValues.setEntry(ih, modelSecondDerivativesValues.getEntry(ih) + temp * interpolationPoints.getEntry(knew, j));
+ ih++;
+ }
+ }
+ for (int m = 0; m < nptm; m++) {
+ final double temp = diff * zMatrix.getEntry(knew, m);
+ for (int k = 0; k < npt; k++) {
+ modelSecondDerivativesParameters.setEntry(k, modelSecondDerivativesParameters.getEntry(k) + temp * zMatrix.getEntry(k, m));
+ }
+ }
+
+ // Include the new interpolation point, and make the changes to GOPT at
+ // the old XOPT that are caused by the updating of the quadratic model.
+
+ fAtInterpolationPoints.setEntry(knew, f);
+ for (int i = 0; i < n; i++) {
+ interpolationPoints.setEntry(knew, i, newPoint.getEntry(i));
+ work1.setEntry(i, bMatrix.getEntry(knew, i));
+ }
+ for (int k = 0; k < npt; k++) {
+ double suma = ZERO;
+ for (int m = 0; m < nptm; m++) {
+ suma += zMatrix.getEntry(knew, m) * zMatrix.getEntry(k, m);
+ }
+ double sumb = ZERO;
+ for (int j = 0; j < n; j++) {
+ sumb += interpolationPoints.getEntry(k, j) * trustRegionCenterOffset.getEntry(j);
+ }
+ final double temp = suma * sumb;
+ for (int i = 0; i < n; i++) {
+ work1.setEntry(i, work1.getEntry(i) + temp * interpolationPoints.getEntry(k, i));
+ }
+ }
+ for (int i = 0; i < n; i++) {
+ gradientAtTrustRegionCenter.setEntry(i, gradientAtTrustRegionCenter.getEntry(i) + diff * work1.getEntry(i));
+ }
+
+ // Update XOPT, GOPT and KOPT if the new calculated F is less than FOPT.
+
+ if (f < fopt) {
+ trustRegionCenterInterpolationPointIndex = knew;
+ xoptsq = ZERO;
+ ih = 0;
+ for (int j = 0; j < n; j++) {
+ trustRegionCenterOffset.setEntry(j, newPoint.getEntry(j));
+ // Computing 2nd power
+ final double d1 = trustRegionCenterOffset.getEntry(j);
+ xoptsq += d1 * d1;
+ for (int i = 0; i <= j; i++) {
+ if (i < j) {
+ gradientAtTrustRegionCenter.setEntry(j, gradientAtTrustRegionCenter.getEntry(j) + modelSecondDerivativesValues.getEntry(ih) * trialStepPoint.getEntry(i));
+ }
+ gradientAtTrustRegionCenter.setEntry(i, gradientAtTrustRegionCenter.getEntry(i) + modelSecondDerivativesValues.getEntry(ih) * trialStepPoint.getEntry(j));
+ ih++;
+ }
+ }
+ for (int k = 0; k < npt; k++) {
+ double temp = ZERO;
+ for (int j = 0; j < n; j++) {
+ temp += interpolationPoints.getEntry(k, j) * trialStepPoint.getEntry(j);
+ }
+ temp *= modelSecondDerivativesParameters.getEntry(k);
+ for (int i = 0; i < n; i++) {
+ gradientAtTrustRegionCenter.setEntry(i, gradientAtTrustRegionCenter.getEntry(i) + temp * interpolationPoints.getEntry(k, i));
+ }
+ }
+ }
+
+ // Calculate the parameters of the least Frobenius norm interpolant to
+ // the current data, the gradient of this interpolant at XOPT being put
+ // into VLAG(NPT+I), I=1,2,...,N.
+
+ if (ntrits > 0) {
+ for (int k = 0; k < npt; k++) {
+ lagrangeValuesAtNewPoint.setEntry(k, fAtInterpolationPoints.getEntry(k) - fAtInterpolationPoints.getEntry(trustRegionCenterInterpolationPointIndex));
+ work3.setEntry(k, ZERO);
+ }
+ for (int j = 0; j < nptm; j++) {
+ double sum = ZERO;
+ for (int k = 0; k < npt; k++) {
+ sum += zMatrix.getEntry(k, j) * lagrangeValuesAtNewPoint.getEntry(k);
+ }
+ for (int k = 0; k < npt; k++) {
+ work3.setEntry(k, work3.getEntry(k) + sum * zMatrix.getEntry(k, j));
+ }
+ }
+ for (int k = 0; k < npt; k++) {
+ double sum = ZERO;
+ for (int j = 0; j < n; j++) {
+ sum += interpolationPoints.getEntry(k, j) * trustRegionCenterOffset.getEntry(j);
+ }
+ work2.setEntry(k, work3.getEntry(k));
+ work3.setEntry(k, sum * work3.getEntry(k));
+ }
+ double gqsq = ZERO;
+ double gisq = ZERO;
+ for (int i = 0; i < n; i++) {
+ double sum = ZERO;
+ for (int k = 0; k < npt; k++) {
+ sum += bMatrix.getEntry(k, i) *
+ lagrangeValuesAtNewPoint.getEntry(k) + interpolationPoints.getEntry(k, i) * work3.getEntry(k);
+ }
+ if (trustRegionCenterOffset.getEntry(i) == lowerDifference.getEntry(i)) {
+ // Computing MIN
+ // Computing 2nd power
+ final double d1 = FastMath.min(ZERO, gradientAtTrustRegionCenter.getEntry(i));
+ gqsq += d1 * d1;
+ // Computing 2nd power
+ final double d2 = FastMath.min(ZERO, sum);
+ gisq += d2 * d2;
+ } else if (trustRegionCenterOffset.getEntry(i) == upperDifference.getEntry(i)) {
+ // Computing MAX
+ // Computing 2nd power
+ final double d1 = FastMath.max(ZERO, gradientAtTrustRegionCenter.getEntry(i));
+ gqsq += d1 * d1;
+ // Computing 2nd power
+ final double d2 = FastMath.max(ZERO, sum);
+ gisq += d2 * d2;
+ } else {
+ // Computing 2nd power
+ final double d1 = gradientAtTrustRegionCenter.getEntry(i);
+ gqsq += d1 * d1;
+ gisq += sum * sum;
+ }
+ lagrangeValuesAtNewPoint.setEntry(npt + i, sum);
+ }
+
+ // Test whether to replace the new quadratic model by the least Frobenius
+ // norm interpolant, making the replacement if the test is satisfied.
+
+ ++itest;
+ if (gqsq < TEN * gisq) {
+ itest = 0;
+ }
+ if (itest >= 3) {
+ for (int i = 0, max = FastMath.max(npt, nh); i < max; i++) {
+ if (i < n) {
+ gradientAtTrustRegionCenter.setEntry(i, lagrangeValuesAtNewPoint.getEntry(npt + i));
+ }
+ if (i < npt) {
+ modelSecondDerivativesParameters.setEntry(i, work2.getEntry(i));
+ }
+ if (i < nh) {
+ modelSecondDerivativesValues.setEntry(i, ZERO);
+ }
+ itest = 0;
+ }
+ }
+ }
+
+ // If a trust region step has provided a sufficient decrease in F, then
+ // branch for another trust region calculation. The case NTRITS=0 occurs
+ // when the new interpolation point was reached by an alternative step.
+
+ if (ntrits == 0) {
+ state = 60; break;
+ }
+ if (f <= fopt + ONE_OVER_TEN * vquad) {
+ state = 60; break;
+ }
+
+ // Alternatively, find out if the interpolation points are close enough
+ // to the best point so far.
+
+ // Computing MAX
+ // Computing 2nd power
+ final double d1 = TWO * delta;
+ // Computing 2nd power
+ final double d2 = TEN * rho;
+ distsq = FastMath.max(d1 * d1, d2 * d2);
+ }
+ case 650: {
+ printState(650); // XXX
+ knew = -1;
+ for (int k = 0; k < npt; k++) {
+ double sum = ZERO;
+ for (int j = 0; j < n; j++) {
+ // Computing 2nd power
+ final double d1 = interpolationPoints.getEntry(k, j) - trustRegionCenterOffset.getEntry(j);
+ sum += d1 * d1;
+ }
+ if (sum > distsq) {
+ knew = k;
+ distsq = sum;
+ }
+ }
+
+ // If KNEW is positive, then ALTMOV finds alternative new positions for
+ // the KNEW-th interpolation point within distance ADELT of XOPT. It is
+ // reached via label 90. Otherwise, there is a branch to label 60 for
+ // another trust region iteration, unless the calculations with the
+ // current RHO are complete.
+
+ if (knew >= 0) {
+ final double dist = FastMath.sqrt(distsq);
+ if (ntrits == -1) {
+ // Computing MIN
+ delta = FastMath.min(ONE_OVER_TEN * delta, HALF * dist);
+ if (delta <= rho * 1.5) {
+ delta = rho;
+ }
+ }
+ ntrits = 0;
+ // Computing MAX
+ // Computing MIN
+ final double d1 = FastMath.min(ONE_OVER_TEN * dist, delta);
+ adelt = FastMath.max(d1, rho);
+ dsq = adelt * adelt;
+ state = 90; break;
+ }
+ if (ntrits == -1) {
+ state = 680; break;
+ }
+ if (ratio > ZERO) {
+ state = 60; break;
+ }
+ if (FastMath.max(delta, dnorm) > rho) {
+ state = 60; break;
+ }
+
+ // The calculations with the current value of RHO are complete. Pick the
+ // next values of RHO and DELTA.
+ }
+ case 680: {
+ printState(680); // XXX
+ if (rho > stoppingTrustRegionRadius) {
+ delta = HALF * rho;
+ ratio = rho / stoppingTrustRegionRadius;
+ if (ratio <= SIXTEEN) {
+ rho = stoppingTrustRegionRadius;
+ } else if (ratio <= TWO_HUNDRED_FIFTY) {
+ rho = FastMath.sqrt(ratio) * stoppingTrustRegionRadius;
+ } else {
+ rho *= ONE_OVER_TEN;
+ }
+ delta = FastMath.max(delta, rho);
+ ntrits = 0;
+ nfsav = getEvaluations();
+ state = 60; break;
+ }
+
+ // Return from the calculation, after another Newton-Raphson step, if
+ // it is too short to have been tried before.
+
+ if (ntrits == -1) {
+ state = 360; break;
+ }
+ }
+ case 720: {
+ printState(720); // XXX
+ if (fAtInterpolationPoints.getEntry(trustRegionCenterInterpolationPointIndex) <= fsave) {
+ for (int i = 0; i < n; i++) {
+ // Computing MIN
+ // Computing MAX
+ final double d3 = lowerBound[i];
+ final double d4 = originShift.getEntry(i) + trustRegionCenterOffset.getEntry(i);
+ final double d1 = FastMath.max(d3, d4);
+ final double d2 = upperBound[i];
+ currentBest.setEntry(i, FastMath.min(d1, d2));
+ if (trustRegionCenterOffset.getEntry(i) == lowerDifference.getEntry(i)) {
+ currentBest.setEntry(i, lowerBound[i]);
+ }
+ if (trustRegionCenterOffset.getEntry(i) == upperDifference.getEntry(i)) {
+ currentBest.setEntry(i, upperBound[i]);
+ }
+ }
+ f = fAtInterpolationPoints.getEntry(trustRegionCenterInterpolationPointIndex);
+ }
+ return f;
+ }
+ default: {
+ throw new MathIllegalStateException(LocalizedFormats.SIMPLE_MESSAGE, "bobyqb");
+ }}}
+ } // bobyqb
+
+ // ----------------------------------------------------------------------------------------
+
+ /**
+ * The arguments N, NPT, XPT, XOPT, BMAT, ZMAT, NDIM, SL and SU all have
+ * the same meanings as the corresponding arguments of BOBYQB.
+ * KOPT is the index of the optimal interpolation point.
+ * KNEW is the index of the interpolation point that is going to be moved.
+ * ADELT is the current trust region bound.
+ * XNEW will be set to a suitable new position for the interpolation point
+ * XPT(KNEW,.). Specifically, it satisfies the SL, SU and trust region
+ * bounds and it should provide a large denominator in the next call of
+ * UPDATE. The step XNEW-XOPT from XOPT is restricted to moves along the
+ * straight lines through XOPT and another interpolation point.
+ * XALT also provides a large value of the modulus of the KNEW-th Lagrange
+ * function subject to the constraints that have been mentioned, its main
+ * difference from XNEW being that XALT-XOPT is a constrained version of
+ * the Cauchy step within the trust region. An exception is that XALT is
+ * not calculated if all components of GLAG (see below) are zero.
+ * ALPHA will be set to the KNEW-th diagonal element of the H matrix.
+ * CAUCHY will be set to the square of the KNEW-th Lagrange function at
+ * the step XALT-XOPT from XOPT for the vector XALT that is returned,
+ * except that CAUCHY is set to zero if XALT is not calculated.
+ * GLAG is a working space vector of length N for the gradient of the
+ * KNEW-th Lagrange function at XOPT.
+ * HCOL is a working space vector of length NPT for the second derivative
+ * coefficients of the KNEW-th Lagrange function.
+ * W is a working space vector of length 2N that is going to hold the
+ * constrained Cauchy step from XOPT of the Lagrange function, followed
+ * by the downhill version of XALT when the uphill step is calculated.
+ *
+ * Set the first NPT components of W to the leading elements of the
+ * KNEW-th column of the H matrix.
+ * @param knew
+ * @param adelt
+ */
+ private double[] altmov(
+ int knew,
+ double adelt
+ ) {
+ printMethod(); // XXX
+
+ final int n = currentBest.getDimension();
+ final int npt = numberOfInterpolationPoints;
+
+ final ArrayRealVector glag = new ArrayRealVector(n);
+ final ArrayRealVector hcol = new ArrayRealVector(npt);
+
+ final ArrayRealVector work1 = new ArrayRealVector(n);
+ final ArrayRealVector work2 = new ArrayRealVector(n);
+
+ for (int k = 0; k < npt; k++) {
+ hcol.setEntry(k, ZERO);
+ }
+ for (int j = 0, max = npt - n - 1; j < max; j++) {
+ final double tmp = zMatrix.getEntry(knew, j);
+ for (int k = 0; k < npt; k++) {
+ hcol.setEntry(k, hcol.getEntry(k) + tmp * zMatrix.getEntry(k, j));
+ }
+ }
+ final double alpha = hcol.getEntry(knew);
+ final double ha = HALF * alpha;
+
+ // Calculate the gradient of the KNEW-th Lagrange function at XOPT.
+
+ for (int i = 0; i < n; i++) {
+ glag.setEntry(i, bMatrix.getEntry(knew, i));
+ }
+ for (int k = 0; k < npt; k++) {
+ double tmp = ZERO;
+ for (int j = 0; j < n; j++) {
+ tmp += interpolationPoints.getEntry(k, j) * trustRegionCenterOffset.getEntry(j);
+ }
+ tmp *= hcol.getEntry(k);
+ for (int i = 0; i < n; i++) {
+ glag.setEntry(i, glag.getEntry(i) + tmp * interpolationPoints.getEntry(k, i));
+ }
+ }
+
+ // Search for a large denominator along the straight lines through XOPT
+ // and another interpolation point. SLBD and SUBD will be lower and upper
+ // bounds on the step along each of these lines in turn. PREDSQ will be
+ // set to the square of the predicted denominator for each line. PRESAV
+ // will be set to the largest admissible value of PREDSQ that occurs.
+
+ double presav = ZERO;
+ double step = Double.NaN;
+ int ksav = 0;
+ int ibdsav = 0;
+ double stpsav = 0;
+ for (int k = 0; k < npt; k++) {
+ if (k == trustRegionCenterInterpolationPointIndex) {
+ continue;
+ }
+ double dderiv = ZERO;
+ double distsq = ZERO;
+ for (int i = 0; i < n; i++) {
+ final double tmp = interpolationPoints.getEntry(k, i) - trustRegionCenterOffset.getEntry(i);
+ dderiv += glag.getEntry(i) * tmp;
+ distsq += tmp * tmp;
+ }
+ double subd = adelt / FastMath.sqrt(distsq);
+ double slbd = -subd;
+ int ilbd = 0;
+ int iubd = 0;
+ final double sumin = FastMath.min(ONE, subd);
+
+ // Revise SLBD and SUBD if necessary because of the bounds in SL and SU.
+
+ for (int i = 0; i < n; i++) {
+ final double tmp = interpolationPoints.getEntry(k, i) - trustRegionCenterOffset.getEntry(i);
+ if (tmp > ZERO) {
+ if (slbd * tmp < lowerDifference.getEntry(i) - trustRegionCenterOffset.getEntry(i)) {
+ slbd = (lowerDifference.getEntry(i) - trustRegionCenterOffset.getEntry(i)) / tmp;
+ ilbd = -i - 1;
+ }
+ if (subd * tmp > upperDifference.getEntry(i) - trustRegionCenterOffset.getEntry(i)) {
+ // Computing MAX
+ subd = FastMath.max(sumin,
+ (upperDifference.getEntry(i) - trustRegionCenterOffset.getEntry(i)) / tmp);
+ iubd = i + 1;
+ }
+ } else if (tmp < ZERO) {
+ if (slbd * tmp > upperDifference.getEntry(i) - trustRegionCenterOffset.getEntry(i)) {
+ slbd = (upperDifference.getEntry(i) - trustRegionCenterOffset.getEntry(i)) / tmp;
+ ilbd = i + 1;
+ }
+ if (subd * tmp < lowerDifference.getEntry(i) - trustRegionCenterOffset.getEntry(i)) {
+ // Computing MAX
+ subd = FastMath.max(sumin,
+ (lowerDifference.getEntry(i) - trustRegionCenterOffset.getEntry(i)) / tmp);
+ iubd = -i - 1;
+ }
+ }
+ }
+
+ // Seek a large modulus of the KNEW-th Lagrange function when the index
+ // of the other interpolation point on the line through XOPT is KNEW.
+
+ step = slbd;
+ int isbd = ilbd;
+ double vlag = Double.NaN;
+ if (k == knew) {
+ final double diff = dderiv - ONE;
+ vlag = slbd * (dderiv - slbd * diff);
+ final double d1 = subd * (dderiv - subd * diff);
+ if (FastMath.abs(d1) > FastMath.abs(vlag)) {
+ step = subd;
+ vlag = d1;
+ isbd = iubd;
+ }
+ final double d2 = HALF * dderiv;
+ final double d3 = d2 - diff * slbd;
+ final double d4 = d2 - diff * subd;
+ if (d3 * d4 < ZERO) {
+ final double d5 = d2 * d2 / diff;
+ if (FastMath.abs(d5) > FastMath.abs(vlag)) {
+ step = d2 / diff;
+ vlag = d5;
+ isbd = 0;
+ }
+ }
+
+ // Search along each of the other lines through XOPT and another point.
+
+ } else {
+ vlag = slbd * (ONE - slbd);
+ final double tmp = subd * (ONE - subd);
+ if (FastMath.abs(tmp) > FastMath.abs(vlag)) {
+ step = subd;
+ vlag = tmp;
+ isbd = iubd;
+ }
+ if (subd > HALF && FastMath.abs(vlag) < ONE_OVER_FOUR) {
+ step = HALF;
+ vlag = ONE_OVER_FOUR;
+ isbd = 0;
+ }
+ vlag *= dderiv;
+ }
+
+ // Calculate PREDSQ for the current line search and maintain PRESAV.
+
+ final double tmp = step * (ONE - step) * distsq;
+ final double predsq = vlag * vlag * (vlag * vlag + ha * tmp * tmp);
+ if (predsq > presav) {
+ presav = predsq;
+ ksav = k;
+ stpsav = step;
+ ibdsav = isbd;
+ }
+ }
+
+ // Construct XNEW in a way that satisfies the bound constraints exactly.
+
+ for (int i = 0; i < n; i++) {
+ final double tmp = trustRegionCenterOffset.getEntry(i) + stpsav * (interpolationPoints.getEntry(ksav, i) - trustRegionCenterOffset.getEntry(i));
+ newPoint.setEntry(i, FastMath.max(lowerDifference.getEntry(i),
+ FastMath.min(upperDifference.getEntry(i), tmp)));
+ }
+ if (ibdsav < 0) {
+ newPoint.setEntry(-ibdsav - 1, lowerDifference.getEntry(-ibdsav - 1));
+ }
+ if (ibdsav > 0) {
+ newPoint.setEntry(ibdsav - 1, upperDifference.getEntry(ibdsav - 1));
+ }
+
+ // Prepare for the iterative method that assembles the constrained Cauchy
+ // step in W. The sum of squares of the fixed components of W is formed in
+ // WFIXSQ, and the free components of W are set to BIGSTP.
+
+ final double bigstp = adelt + adelt;
+ int iflag = 0;
+ double cauchy = Double.NaN;
+ double csave = ZERO;
+ while (true) {
+ double wfixsq = ZERO;
+ double ggfree = ZERO;
+ for (int i = 0; i < n; i++) {
+ final double glagValue = glag.getEntry(i);
+ work1.setEntry(i, ZERO);
+ if (FastMath.min(trustRegionCenterOffset.getEntry(i) - lowerDifference.getEntry(i), glagValue) > ZERO ||
+ FastMath.max(trustRegionCenterOffset.getEntry(i) - upperDifference.getEntry(i), glagValue) < ZERO) {
+ work1.setEntry(i, bigstp);
+ // Computing 2nd power
+ ggfree += glagValue * glagValue;
+ }
+ }
+ if (ggfree == ZERO) {
+ return new double[] { alpha, ZERO };
+ }
+
+ // Investigate whether more components of W can be fixed.
+ final double tmp1 = adelt * adelt - wfixsq;
+ if (tmp1 > ZERO) {
+ step = FastMath.sqrt(tmp1 / ggfree);
+ ggfree = ZERO;
+ for (int i = 0; i < n; i++) {
+ if (work1.getEntry(i) == bigstp) {
+ final double tmp2 = trustRegionCenterOffset.getEntry(i) - step * glag.getEntry(i);
+ if (tmp2 <= lowerDifference.getEntry(i)) {
+ work1.setEntry(i, lowerDifference.getEntry(i) - trustRegionCenterOffset.getEntry(i));
+ // Computing 2nd power
+ final double d1 = work1.getEntry(i);
+ wfixsq += d1 * d1;
+ } else if (tmp2 >= upperDifference.getEntry(i)) {
+ work1.setEntry(i, upperDifference.getEntry(i) - trustRegionCenterOffset.getEntry(i));
+ // Computing 2nd power
+ final double d1 = work1.getEntry(i);
+ wfixsq += d1 * d1;
+ } else {
+ // Computing 2nd power
+ final double d1 = glag.getEntry(i);
+ ggfree += d1 * d1;
+ }
+ }
+ }
+ }
+
+ // Set the remaining free components of W and all components of XALT,
+ // except that W may be scaled later.
+
+ double gw = ZERO;
+ for (int i = 0; i < n; i++) {
+ final double glagValue = glag.getEntry(i);
+ if (work1.getEntry(i) == bigstp) {
+ work1.setEntry(i, -step * glagValue);
+ final double min = FastMath.min(upperDifference.getEntry(i),
+ trustRegionCenterOffset.getEntry(i) + work1.getEntry(i));
+ alternativeNewPoint.setEntry(i, FastMath.max(lowerDifference.getEntry(i), min));
+ } else if (work1.getEntry(i) == ZERO) {
+ alternativeNewPoint.setEntry(i, trustRegionCenterOffset.getEntry(i));
+ } else if (glagValue > ZERO) {
+ alternativeNewPoint.setEntry(i, lowerDifference.getEntry(i));
+ } else {
+ alternativeNewPoint.setEntry(i, upperDifference.getEntry(i));
+ }
+ gw += glagValue * work1.getEntry(i);
+ }
+
+ // Set CURV to the curvature of the KNEW-th Lagrange function along W.
+ // Scale W by a factor less than one if that can reduce the modulus of
+ // the Lagrange function at XOPT+W. Set CAUCHY to the final value of
+ // the square of this function.
+
+ double curv = ZERO;
+ for (int k = 0; k < npt; k++) {
+ double tmp = ZERO;
+ for (int j = 0; j < n; j++) {
+ tmp += interpolationPoints.getEntry(k, j) * work1.getEntry(j);
+ }
+ curv += hcol.getEntry(k) * tmp * tmp;
+ }
+ if (iflag == 1) {
+ curv = -curv;
+ }
+ if (curv > -gw &&
+ curv < -gw * (ONE + FastMath.sqrt(TWO))) {
+ final double scale = -gw / curv;
+ for (int i = 0; i < n; i++) {
+ final double tmp = trustRegionCenterOffset.getEntry(i) + scale * work1.getEntry(i);
+ alternativeNewPoint.setEntry(i, FastMath.max(lowerDifference.getEntry(i),
+ FastMath.min(upperDifference.getEntry(i), tmp)));
+ }
+ // Computing 2nd power
+ final double d1 = HALF * gw * scale;
+ cauchy = d1 * d1;
+ } else {
+ // Computing 2nd power
+ final double d1 = gw + HALF * curv;
+ cauchy = d1 * d1;
+ }
+
+ // If IFLAG is zero, then XALT is calculated as before after reversing
+ // the sign of GLAG. Thus two XALT vectors become available. The one that
+ // is chosen is the one that gives the larger value of CAUCHY.
+
+ if (iflag == 0) {
+ for (int i = 0; i < n; i++) {
+ glag.setEntry(i, -glag.getEntry(i));
+ work2.setEntry(i, alternativeNewPoint.getEntry(i));
+ }
+ csave = cauchy;
+ iflag = 1;
+ } else {
+ break;
+ }
+ }
+ if (csave > cauchy) {
+ for (int i = 0; i < n; i++) {
+ alternativeNewPoint.setEntry(i, work2.getEntry(i));
+ }
+ cauchy = csave;
+ }
+
+ return new double[] { alpha, cauchy };
+ } // altmov
+
+ // ----------------------------------------------------------------------------------------
+
+ /**
+ * SUBROUTINE PRELIM sets the elements of XBASE, XPT, FVAL, GOPT, HQ, PQ,
+ * BMAT and ZMAT for the first iteration, and it maintains the values of
+ * NF and KOPT. The vector X is also changed by PRELIM.
+ *
+ * The arguments N, NPT, X, XL, XU, RHOBEG, IPRINT and MAXFUN are the
+ * same as the corresponding arguments in SUBROUTINE BOBYQA.
+ * The arguments XBASE, XPT, FVAL, HQ, PQ, BMAT, ZMAT, NDIM, SL and SU
+ * are the same as the corresponding arguments in BOBYQB, the elements
+ * of SL and SU being set in BOBYQA.
+ * GOPT is usually the gradient of the quadratic model at XOPT+XBASE, but
+ * it is set by PRELIM to the gradient of the quadratic model at XBASE.
+ * If XOPT is nonzero, BOBYQB will change it to its usual value later.
+ * NF is maintaned as the number of calls of CALFUN so far.
+ * KOPT will be such that the least calculated value of F so far is at
+ * the point XPT(KOPT,.)+XBASE in the space of the variables.
+ *
+ * @param lowerBound Lower bounds.
+ * @param upperBound Upper bounds.
+ */
+ private void prelim(double[] lowerBound,
+ double[] upperBound) {
+ printMethod(); // XXX
+
+ final int n = currentBest.getDimension();
+ final int npt = numberOfInterpolationPoints;
+ final int ndim = bMatrix.getRowDimension();
+
+ final double rhosq = initialTrustRegionRadius * initialTrustRegionRadius;
+ final double recip = 1d / rhosq;
+ final int np = n + 1;
+
+ // Set XBASE to the initial vector of variables, and set the initial
+ // elements of XPT, BMAT, HQ, PQ and ZMAT to zero.
+
+ for (int j = 0; j < n; j++) {
+ originShift.setEntry(j, currentBest.getEntry(j));
+ for (int k = 0; k < npt; k++) {
+ interpolationPoints.setEntry(k, j, ZERO);
+ }
+ for (int i = 0; i < ndim; i++) {
+ bMatrix.setEntry(i, j, ZERO);
+ }
+ }
+ for (int i = 0, max = n * np / 2; i < max; i++) {
+ modelSecondDerivativesValues.setEntry(i, ZERO);
+ }
+ for (int k = 0; k < npt; k++) {
+ modelSecondDerivativesParameters.setEntry(k, ZERO);
+ for (int j = 0, max = npt - np; j < max; j++) {
+ zMatrix.setEntry(k, j, ZERO);
+ }
+ }
+
+ // Begin the initialization procedure. NF becomes one more than the number
+ // of function values so far. The coordinates of the displacement of the
+ // next initial interpolation point from XBASE are set in XPT(NF+1,.).
+
+ int ipt = 0;
+ int jpt = 0;
+ double fbeg = Double.NaN;
+ do {
+ final int nfm = getEvaluations();
+ final int nfx = nfm - n;
+ final int nfmm = nfm - 1;
+ final int nfxm = nfx - 1;
+ double stepa = 0;
+ double stepb = 0;
+ if (nfm <= 2 * n) {
+ if (nfm >= 1 &&
+ nfm <= n) {
+ stepa = initialTrustRegionRadius;
+ if (upperDifference.getEntry(nfmm) == ZERO) {
+ stepa = -stepa;
+ // throw new PathIsExploredException(); // XXX
+ }
+ interpolationPoints.setEntry(nfm, nfmm, stepa);
+ } else if (nfm > n) {
+ stepa = interpolationPoints.getEntry(nfx, nfxm);
+ stepb = -initialTrustRegionRadius;
+ if (lowerDifference.getEntry(nfxm) == ZERO) {
+ stepb = FastMath.min(TWO * initialTrustRegionRadius, upperDifference.getEntry(nfxm));
+ // throw new PathIsExploredException(); // XXX
+ }
+ if (upperDifference.getEntry(nfxm) == ZERO) {
+ stepb = FastMath.max(-TWO * initialTrustRegionRadius, lowerDifference.getEntry(nfxm));
+ // throw new PathIsExploredException(); // XXX
+ }
+ interpolationPoints.setEntry(nfm, nfxm, stepb);
+ }
+ } else {
+ final int tmp1 = (nfm - np) / n;
+ jpt = nfm - tmp1 * n - n;
+ ipt = jpt + tmp1;
+ if (ipt > n) {
+ final int tmp2 = jpt;
+ jpt = ipt - n;
+ ipt = tmp2;
+// throw new PathIsExploredException(); // XXX
+ }
+ final int iptMinus1 = ipt - 1;
+ final int jptMinus1 = jpt - 1;
+ interpolationPoints.setEntry(nfm, iptMinus1, interpolationPoints.getEntry(ipt, iptMinus1));
+ interpolationPoints.setEntry(nfm, jptMinus1, interpolationPoints.getEntry(jpt, jptMinus1));
+ }
+
+ // Calculate the next value of F. The least function value so far and
+ // its index are required.
+
+ for (int j = 0; j < n; j++) {
+ currentBest.setEntry(j, FastMath.min(FastMath.max(lowerBound[j],
+ originShift.getEntry(j) + interpolationPoints.getEntry(nfm, j)),
+ upperBound[j]));
+ if (interpolationPoints.getEntry(nfm, j) == lowerDifference.getEntry(j)) {
+ currentBest.setEntry(j, lowerBound[j]);
+ }
+ if (interpolationPoints.getEntry(nfm, j) == upperDifference.getEntry(j)) {
+ currentBest.setEntry(j, upperBound[j]);
+ }
+ }
+
+ final double objectiveValue = computeObjectiveValue(currentBest.toArray());
+ final double f = isMinimize ? objectiveValue : -objectiveValue;
+ final int numEval = getEvaluations(); // nfm + 1
+ fAtInterpolationPoints.setEntry(nfm, f);
+
+ if (numEval == 1) {
+ fbeg = f;
+ trustRegionCenterInterpolationPointIndex = 0;
+ } else if (f < fAtInterpolationPoints.getEntry(trustRegionCenterInterpolationPointIndex)) {
+ trustRegionCenterInterpolationPointIndex = nfm;
+ }
+
+ // Set the nonzero initial elements of BMAT and the quadratic model in the
+ // cases when NF is at most 2*N+1. If NF exceeds N+1, then the positions
+ // of the NF-th and (NF-N)-th interpolation points may be switched, in
+ // order that the function value at the first of them contributes to the
+ // off-diagonal second derivative terms of the initial quadratic model.
+
+ if (numEval <= 2 * n + 1) {
+ if (numEval >= 2 &&
+ numEval <= n + 1) {
+ gradientAtTrustRegionCenter.setEntry(nfmm, (f - fbeg) / stepa);
+ if (npt < numEval + n) {
+ final double oneOverStepA = ONE / stepa;
+ bMatrix.setEntry(0, nfmm, -oneOverStepA);
+ bMatrix.setEntry(nfm, nfmm, oneOverStepA);
+ bMatrix.setEntry(npt + nfmm, nfmm, -HALF * rhosq);
+ // throw new PathIsExploredException(); // XXX
+ }
+ } else if (numEval >= n + 2) {
+ final int ih = nfx * (nfx + 1) / 2 - 1;
+ final double tmp = (f - fbeg) / stepb;
+ final double diff = stepb - stepa;
+ modelSecondDerivativesValues.setEntry(ih, TWO * (tmp - gradientAtTrustRegionCenter.getEntry(nfxm)) / diff);
+ gradientAtTrustRegionCenter.setEntry(nfxm, (gradientAtTrustRegionCenter.getEntry(nfxm) * stepb - tmp * stepa) / diff);
+ if (stepa * stepb < ZERO && f < fAtInterpolationPoints.getEntry(nfm - n)) {
+ fAtInterpolationPoints.setEntry(nfm, fAtInterpolationPoints.getEntry(nfm - n));
+ fAtInterpolationPoints.setEntry(nfm - n, f);
+ if (trustRegionCenterInterpolationPointIndex == nfm) {
+ trustRegionCenterInterpolationPointIndex = nfm - n;
+ }
+ interpolationPoints.setEntry(nfm - n, nfxm, stepb);
+ interpolationPoints.setEntry(nfm, nfxm, stepa);
+ }
+ bMatrix.setEntry(0, nfxm, -(stepa + stepb) / (stepa * stepb));
+ bMatrix.setEntry(nfm, nfxm, -HALF / interpolationPoints.getEntry(nfm - n, nfxm));
+ bMatrix.setEntry(nfm - n, nfxm,
+ -bMatrix.getEntry(0, nfxm) - bMatrix.getEntry(nfm, nfxm));
+ zMatrix.setEntry(0, nfxm, FastMath.sqrt(TWO) / (stepa * stepb));
+ zMatrix.setEntry(nfm, nfxm, FastMath.sqrt(HALF) / rhosq);
+ // zMatrix.setEntry(nfm, nfxm, FastMath.sqrt(HALF) * recip); // XXX "testAckley" and "testDiffPow" fail.
+ zMatrix.setEntry(nfm - n, nfxm,
+ -zMatrix.getEntry(0, nfxm) - zMatrix.getEntry(nfm, nfxm));
+ }
+
+ // Set the off-diagonal second derivatives of the Lagrange functions and
+ // the initial quadratic model.
+
+ } else {
+ zMatrix.setEntry(0, nfxm, recip);
+ zMatrix.setEntry(nfm, nfxm, recip);
+ zMatrix.setEntry(ipt, nfxm, -recip);
+ zMatrix.setEntry(jpt, nfxm, -recip);
+
+ final int ih = ipt * (ipt - 1) / 2 + jpt - 1;
+ final double tmp = interpolationPoints.getEntry(nfm, ipt - 1) * interpolationPoints.getEntry(nfm, jpt - 1);
+ modelSecondDerivativesValues.setEntry(ih, (fbeg - fAtInterpolationPoints.getEntry(ipt) - fAtInterpolationPoints.getEntry(jpt) + f) / tmp);
+// throw new PathIsExploredException(); // XXX
+ }
+ } while (getEvaluations() < npt);
+ } // prelim
+
+
+ // ----------------------------------------------------------------------------------------
+
+ /**
+ * A version of the truncated conjugate gradient is applied. If a line
+ * search is restricted by a constraint, then the procedure is restarted,
+ * the values of the variables that are at their bounds being fixed. If
+ * the trust region boundary is reached, then further changes may be made
+ * to D, each one being in the two dimensional space that is spanned
+ * by the current D and the gradient of Q at XOPT+D, staying on the trust
+ * region boundary. Termination occurs when the reduction in Q seems to
+ * be close to the greatest reduction that can be achieved.
+ * The arguments N, NPT, XPT, XOPT, GOPT, HQ, PQ, SL and SU have the same
+ * meanings as the corresponding arguments of BOBYQB.
+ * DELTA is the trust region radius for the present calculation, which
+ * seeks a small value of the quadratic model within distance DELTA of
+ * XOPT subject to the bounds on the variables.
+ * XNEW will be set to a new vector of variables that is approximately
+ * the one that minimizes the quadratic model within the trust region
+ * subject to the SL and SU constraints on the variables. It satisfies
+ * as equations the bounds that become active during the calculation.
+ * D is the calculated trial step from XOPT, generated iteratively from an
+ * initial value of zero. Thus XNEW is XOPT+D after the final iteration.
+ * GNEW holds the gradient of the quadratic model at XOPT+D. It is updated
+ * when D is updated.
+ * xbdi.get( is a working space vector. For I=1,2,...,N, the element xbdi.get((I) is
+ * set to -1.0, 0.0, or 1.0, the value being nonzero if and only if the
+ * I-th variable has become fixed at a bound, the bound being SL(I) or
+ * SU(I) in the case xbdi.get((I)=-1.0 or xbdi.get((I)=1.0, respectively. This
+ * information is accumulated during the construction of XNEW.
+ * The arrays S, HS and HRED are also used for working space. They hold the
+ * current search direction, and the changes in the gradient of Q along S
+ * and the reduced D, respectively, where the reduced D is the same as D,
+ * except that the components of the fixed variables are zero.
+ * DSQ will be set to the square of the length of XNEW-XOPT.
+ * CRVMIN is set to zero if D reaches the trust region boundary. Otherwise
+ * it is set to the least curvature of H that occurs in the conjugate
+ * gradient searches that are not restricted by any constraints. The
+ * value CRVMIN=-1.0D0 is set, however, if all of these searches are
+ * constrained.
+ * @param delta
+ * @param gnew
+ * @param xbdi
+ * @param s
+ * @param hs
+ * @param hred
+ */
+ private double[] trsbox(
+ double delta,
+ ArrayRealVector gnew,
+ ArrayRealVector xbdi,
+ ArrayRealVector s,
+ ArrayRealVector hs,
+ ArrayRealVector hred
+ ) {
+ printMethod(); // XXX
+
+ final int n = currentBest.getDimension();
+ final int npt = numberOfInterpolationPoints;
+
+ double dsq = Double.NaN;
+ double crvmin = Double.NaN;
+
+ // Local variables
+ double ds;
+ int iu;
+ double dhd, dhs, cth, shs, sth, ssq, beta=0, sdec, blen;
+ int iact = -1;
+ int nact = 0;
+ double angt = 0, qred;
+ int isav;
+ double temp = 0, xsav = 0, xsum = 0, angbd = 0, dredg = 0, sredg = 0;
+ int iterc;
+ double resid = 0, delsq = 0, ggsav = 0, tempa = 0, tempb = 0,
+ redmax = 0, dredsq = 0, redsav = 0, gredsq = 0, rednew = 0;
+ int itcsav = 0;
+ double rdprev = 0, rdnext = 0, stplen = 0, stepsq = 0;
+ int itermax = 0;
+
+ // Set some constants.
+
+ // Function Body
+
+ // The sign of GOPT(I) gives the sign of the change to the I-th variable
+ // that will reduce Q from its value at XOPT. Thus xbdi.get((I) shows whether
+ // or not to fix the I-th variable at one of its bounds initially, with
+ // NACT being set to the number of fixed variables. D and GNEW are also
+ // set for the first iteration. DELSQ is the upper bound on the sum of
+ // squares of the free variables. QRED is the reduction in Q so far.
+
+ iterc = 0;
+ nact = 0;
+ for (int i = 0; i < n; i++) {
+ xbdi.setEntry(i, ZERO);
+ if (trustRegionCenterOffset.getEntry(i) <= lowerDifference.getEntry(i)) {
+ if (gradientAtTrustRegionCenter.getEntry(i) >= ZERO) {
+ xbdi.setEntry(i, MINUS_ONE);
+ }
+ } else if (trustRegionCenterOffset.getEntry(i) >= upperDifference.getEntry(i) &&
+ gradientAtTrustRegionCenter.getEntry(i) <= ZERO) {
+ xbdi.setEntry(i, ONE);
+ }
+ if (xbdi.getEntry(i) != ZERO) {
+ ++nact;
+ }
+ trialStepPoint.setEntry(i, ZERO);
+ gnew.setEntry(i, gradientAtTrustRegionCenter.getEntry(i));
+ }
+ delsq = delta * delta;
+ qred = ZERO;
+ crvmin = MINUS_ONE;
+
+ // Set the next search direction of the conjugate gradient method. It is
+ // the steepest descent direction initially and when the iterations are
+ // restarted because a variable has just been fixed by a bound, and of
+ // course the components of the fixed variables are zero. ITERMAX is an
+ // upper bound on the indices of the conjugate gradient iterations.
+
+ int state = 20;
+ for(;;) {
+ switch (state) {
+ case 20: {
+ printState(20); // XXX
+ beta = ZERO;
+ }
+ case 30: {
+ printState(30); // XXX
+ stepsq = ZERO;
+ for (int i = 0; i < n; i++) {
+ if (xbdi.getEntry(i) != ZERO) {
+ s.setEntry(i, ZERO);
+ } else if (beta == ZERO) {
+ s.setEntry(i, -gnew.getEntry(i));
+ } else {
+ s.setEntry(i, beta * s.getEntry(i) - gnew.getEntry(i));
+ }
+ // Computing 2nd power
+ final double d1 = s.getEntry(i);
+ stepsq += d1 * d1;
+ }
+ if (stepsq == ZERO) {
+ state = 190; break;
+ }
+ if (beta == ZERO) {
+ gredsq = stepsq;
+ itermax = iterc + n - nact;
+ }
+ if (gredsq * delsq <= qred * 1e-4 * qred) {
+ state = 190; break;
+ }
+
+ // Multiply the search direction by the second derivative matrix of Q and
+ // calculate some scalars for the choice of steplength. Then set BLEN to
+ // the length of the the step to the trust region boundary and STPLEN to
+ // the steplength, ignoring the simple bounds.
+
+ state = 210; break;
+ }
+ case 50: {
+ printState(50); // XXX
+ resid = delsq;
+ ds = ZERO;
+ shs = ZERO;
+ for (int i = 0; i < n; i++) {
+ if (xbdi.getEntry(i) == ZERO) {
+ // Computing 2nd power
+ final double d1 = trialStepPoint.getEntry(i);
+ resid -= d1 * d1;
+ ds += s.getEntry(i) * trialStepPoint.getEntry(i);
+ shs += s.getEntry(i) * hs.getEntry(i);
+ }
+ }
+ if (resid <= ZERO) {
+ state = 90; break;
+ }
+ temp = FastMath.sqrt(stepsq * resid + ds * ds);
+ if (ds < ZERO) {
+ blen = (temp - ds) / stepsq;
+ } else {
+ blen = resid / (temp + ds);
+ }
+ stplen = blen;
+ if (shs > ZERO) {
+ // Computing MIN
+ stplen = FastMath.min(blen, gredsq / shs);
+ }
+
+ // Reduce STPLEN if necessary in order to preserve the simple bounds,
+ // letting IACT be the index of the new constrained variable.
+
+ iact = -1;
+ for (int i = 0; i < n; i++) {
+ if (s.getEntry(i) != ZERO) {
+ xsum = trustRegionCenterOffset.getEntry(i) + trialStepPoint.getEntry(i);
+ if (s.getEntry(i) > ZERO) {
+ temp = (upperDifference.getEntry(i) - xsum) / s.getEntry(i);
+ } else {
+ temp = (lowerDifference.getEntry(i) - xsum) / s.getEntry(i);
+ }
+ if (temp < stplen) {
+ stplen = temp;
+ iact = i;
+ }
+ }
+ }
+
+ // Update CRVMIN, GNEW and D. Set SDEC to the decrease that occurs in Q.
+
+ sdec = ZERO;
+ if (stplen > ZERO) {
+ ++iterc;
+ temp = shs / stepsq;
+ if (iact == -1 && temp > ZERO) {
+ crvmin = FastMath.min(crvmin,temp);
+ if (crvmin == MINUS_ONE) {
+ crvmin = temp;
+ }
+ }
+ ggsav = gredsq;
+ gredsq = ZERO;
+ for (int i = 0; i < n; i++) {
+ gnew.setEntry(i, gnew.getEntry(i) + stplen * hs.getEntry(i));
+ if (xbdi.getEntry(i) == ZERO) {
+ // Computing 2nd power
+ final double d1 = gnew.getEntry(i);
+ gredsq += d1 * d1;
+ }
+ trialStepPoint.setEntry(i, trialStepPoint.getEntry(i) + stplen * s.getEntry(i));
+ }
+ // Computing MAX
+ final double d1 = stplen * (ggsav - HALF * stplen * shs);
+ sdec = FastMath.max(d1, ZERO);
+ qred += sdec;
+ }
+
+ // Restart the conjugate gradient method if it has hit a new bound.
+
+ if (iact >= 0) {
+ ++nact;
+ xbdi.setEntry(iact, ONE);
+ if (s.getEntry(iact) < ZERO) {
+ xbdi.setEntry(iact, MINUS_ONE);
+ }
+ // Computing 2nd power
+ final double d1 = trialStepPoint.getEntry(iact);
+ delsq -= d1 * d1;
+ if (delsq <= ZERO) {
+ state = 190; break;
+ }
+ state = 20; break;
+ }
+
+ // If STPLEN is less than BLEN, then either apply another conjugate
+ // gradient iteration or RETURN.
+
+ if (stplen < blen) {
+ if (iterc == itermax) {
+ state = 190; break;
+ }
+ if (sdec <= qred * .01) {
+ state = 190; break;
+ }
+ beta = gredsq / ggsav;
+ state = 30; break;
+ }
+ }
+ case 90: {
+ printState(90); // XXX
+ crvmin = ZERO;
+
+ // Prepare for the alternative iteration by calculating some scalars
+ // and by multiplying the reduced D by the second derivative matrix of
+ // Q, where S holds the reduced D in the call of GGMULT.
+
+ }
+ case 100: {
+ printState(100); // XXX
+ if (nact >= n - 1) {
+ state = 190; break;
+ }
+ dredsq = ZERO;
+ dredg = ZERO;
+ gredsq = ZERO;
+ for (int i = 0; i < n; i++) {
+ if (xbdi.getEntry(i) == ZERO) {
+ // Computing 2nd power
+ double d1 = trialStepPoint.getEntry(i);
+ dredsq += d1 * d1;
+ dredg += trialStepPoint.getEntry(i) * gnew.getEntry(i);
+ // Computing 2nd power
+ d1 = gnew.getEntry(i);
+ gredsq += d1 * d1;
+ s.setEntry(i, trialStepPoint.getEntry(i));
+ } else {
+ s.setEntry(i, ZERO);
+ }
+ }
+ itcsav = iterc;
+ state = 210; break;
+ // Let the search direction S be a linear combination of the reduced D
+ // and the reduced G that is orthogonal to the reduced D.
+ }
+ case 120: {
+ printState(120); // XXX
+ ++iterc;
+ temp = gredsq * dredsq - dredg * dredg;
+ if (temp <= qred * 1e-4 * qred) {
+ state = 190; break;
+ }
+ temp = FastMath.sqrt(temp);
+ for (int i = 0; i < n; i++) {
+ if (xbdi.getEntry(i) == ZERO) {
+ s.setEntry(i, (dredg * trialStepPoint.getEntry(i) - dredsq * gnew.getEntry(i)) / temp);
+ } else {
+ s.setEntry(i, ZERO);
+ }
+ }
+ sredg = -temp;
+
+ // By considering the simple bounds on the variables, calculate an upper
+ // bound on the tangent of half the angle of the alternative iteration,
+ // namely ANGBD, except that, if already a free variable has reached a
+ // bound, there is a branch back to label 100 after fixing that variable.
+
+ angbd = ONE;
+ iact = -1;
+ for (int i = 0; i < n; i++) {
+ if (xbdi.getEntry(i) == ZERO) {
+ tempa = trustRegionCenterOffset.getEntry(i) + trialStepPoint.getEntry(i) - lowerDifference.getEntry(i);
+ tempb = upperDifference.getEntry(i) - trustRegionCenterOffset.getEntry(i) - trialStepPoint.getEntry(i);
+ if (tempa <= ZERO) {
+ ++nact;
+ xbdi.setEntry(i, MINUS_ONE);
+ state = 100; break;
+ } else if (tempb <= ZERO) {
+ ++nact;
+ xbdi.setEntry(i, ONE);
+ state = 100; break;
+ }
+ // Computing 2nd power
+ double d1 = trialStepPoint.getEntry(i);
+ // Computing 2nd power
+ double d2 = s.getEntry(i);
+ ssq = d1 * d1 + d2 * d2;
+ // Computing 2nd power
+ d1 = trustRegionCenterOffset.getEntry(i) - lowerDifference.getEntry(i);
+ temp = ssq - d1 * d1;
+ if (temp > ZERO) {
+ temp = FastMath.sqrt(temp) - s.getEntry(i);
+ if (angbd * temp > tempa) {
+ angbd = tempa / temp;
+ iact = i;
+ xsav = MINUS_ONE;
+ }
+ }
+ // Computing 2nd power
+ d1 = upperDifference.getEntry(i) - trustRegionCenterOffset.getEntry(i);
+ temp = ssq - d1 * d1;
+ if (temp > ZERO) {
+ temp = FastMath.sqrt(temp) + s.getEntry(i);
+ if (angbd * temp > tempb) {
+ angbd = tempb / temp;
+ iact = i;
+ xsav = ONE;
+ }
+ }
+ }
+ }
+
+ // Calculate HHD and some curvatures for the alternative iteration.
+
+ state = 210; break;
+ }
+ case 150: {
+ printState(150); // XXX
+ shs = ZERO;
+ dhs = ZERO;
+ dhd = ZERO;
+ for (int i = 0; i < n; i++) {
+ if (xbdi.getEntry(i) == ZERO) {
+ shs += s.getEntry(i) * hs.getEntry(i);
+ dhs += trialStepPoint.getEntry(i) * hs.getEntry(i);
+ dhd += trialStepPoint.getEntry(i) * hred.getEntry(i);
+ }
+ }
+
+ // Seek the greatest reduction in Q for a range of equally spaced values
+ // of ANGT in [0,ANGBD], where ANGT is the tangent of half the angle of
+ // the alternative iteration.
+
+ redmax = ZERO;
+ isav = -1;
+ redsav = ZERO;
+ iu = (int) (angbd * 17. + 3.1);
+ for (int i = 0; i < iu; i++) {
+ angt = angbd * i / iu;
+ sth = (angt + angt) / (ONE + angt * angt);
+ temp = shs + angt * (angt * dhd - dhs - dhs);
+ rednew = sth * (angt * dredg - sredg - HALF * sth * temp);
+ if (rednew > redmax) {
+ redmax = rednew;
+ isav = i;
+ rdprev = redsav;
+ } else if (i == isav + 1) {
+ rdnext = rednew;
+ }
+ redsav = rednew;
+ }
+
+ // Return if the reduction is zero. Otherwise, set the sine and cosine
+ // of the angle of the alternative iteration, and calculate SDEC.
+
+ if (isav < 0) {
+ state = 190; break;
+ }
+ if (isav < iu) {
+ temp = (rdnext - rdprev) / (redmax + redmax - rdprev - rdnext);
+ angt = angbd * (isav + HALF * temp) / iu;
+ }
+ cth = (ONE - angt * angt) / (ONE + angt * angt);
+ sth = (angt + angt) / (ONE + angt * angt);
+ temp = shs + angt * (angt * dhd - dhs - dhs);
+ sdec = sth * (angt * dredg - sredg - HALF * sth * temp);
+ if (sdec <= ZERO) {
+ state = 190; break;
+ }
+
+ // Update GNEW, D and HRED. If the angle of the alternative iteration
+ // is restricted by a bound on a free variable, that variable is fixed
+ // at the bound.
+
+ dredg = ZERO;
+ gredsq = ZERO;
+ for (int i = 0; i < n; i++) {
+ gnew.setEntry(i, gnew.getEntry(i) + (cth - ONE) * hred.getEntry(i) + sth * hs.getEntry(i));
+ if (xbdi.getEntry(i) == ZERO) {
+ trialStepPoint.setEntry(i, cth * trialStepPoint.getEntry(i) + sth * s.getEntry(i));
+ dredg += trialStepPoint.getEntry(i) * gnew.getEntry(i);
+ // Computing 2nd power
+ final double d1 = gnew.getEntry(i);
+ gredsq += d1 * d1;
+ }
+ hred.setEntry(i, cth * hred.getEntry(i) + sth * hs.getEntry(i));
+ }
+ qred += sdec;
+ if (iact >= 0 && isav == iu) {
+ ++nact;
+ xbdi.setEntry(iact, xsav);
+ state = 100; break;
+ }
+
+ // If SDEC is sufficiently small, then RETURN after setting XNEW to
+ // XOPT+D, giving careful attention to the bounds.
+
+ if (sdec > qred * .01) {
+ state = 120; break;
+ }
+ }
+ case 190: {
+ printState(190); // XXX
+ dsq = ZERO;
+ for (int i = 0; i < n; i++) {
+ // Computing MAX
+ // Computing MIN
+ final double min = FastMath.min(trustRegionCenterOffset.getEntry(i) + trialStepPoint.getEntry(i),
+ upperDifference.getEntry(i));
+ newPoint.setEntry(i, FastMath.max(min, lowerDifference.getEntry(i)));
+ if (xbdi.getEntry(i) == MINUS_ONE) {
+ newPoint.setEntry(i, lowerDifference.getEntry(i));
+ }
+ if (xbdi.getEntry(i) == ONE) {
+ newPoint.setEntry(i, upperDifference.getEntry(i));
+ }
+ trialStepPoint.setEntry(i, newPoint.getEntry(i) - trustRegionCenterOffset.getEntry(i));
+ // Computing 2nd power
+ final double d1 = trialStepPoint.getEntry(i);
+ dsq += d1 * d1;
+ }
+ return new double[] { dsq, crvmin };
+ // The following instructions multiply the current S-vector by the second
+ // derivative matrix of the quadratic model, putting the product in HS.
+ // They are reached from three different parts of the software above and
+ // they can be regarded as an external subroutine.
+ }
+ case 210: {
+ printState(210); // XXX
+ int ih = 0;
+ for (int j = 0; j < n; j++) {
+ hs.setEntry(j, ZERO);
+ for (int i = 0; i <= j; i++) {
+ if (i < j) {
+ hs.setEntry(j, hs.getEntry(j) + modelSecondDerivativesValues.getEntry(ih) * s.getEntry(i));
+ }
+ hs.setEntry(i, hs.getEntry(i) + modelSecondDerivativesValues.getEntry(ih) * s.getEntry(j));
+ ih++;
+ }
+ }
+ final RealVector tmp = interpolationPoints.operate(s).ebeMultiply(modelSecondDerivativesParameters);
+ for (int k = 0; k < npt; k++) {
+ if (modelSecondDerivativesParameters.getEntry(k) != ZERO) {
+ for (int i = 0; i < n; i++) {
+ hs.setEntry(i, hs.getEntry(i) + tmp.getEntry(k) * interpolationPoints.getEntry(k, i));
+ }
+ }
+ }
+ if (crvmin != ZERO) {
+ state = 50; break;
+ }
+ if (iterc > itcsav) {
+ state = 150; break;
+ }
+ for (int i = 0; i < n; i++) {
+ hred.setEntry(i, hs.getEntry(i));
+ }
+ state = 120; break;
+ }
+ default: {
+ throw new MathIllegalStateException(LocalizedFormats.SIMPLE_MESSAGE, "trsbox");
+ }}
+ }
+ } // trsbox
+
+ // ----------------------------------------------------------------------------------------
+
+ /**
+ * The arrays BMAT and ZMAT are updated, as required by the new position
+ * of the interpolation point that has the index KNEW. The vector VLAG has
+ * N+NPT components, set on entry to the first NPT and last N components
+ * of the product Hw in equation (4.11) of the Powell (2006) paper on
+ * NEWUOA. Further, BETA is set on entry to the value of the parameter
+ * with that name, and DENOM is set to the denominator of the updating
+ * formula. Elements of ZMAT may be treated as zero if their moduli are
+ * at most ZTEST. The first NDIM elements of W are used for working space.
+ * @param beta
+ * @param denom
+ * @param knew
+ */
+ private void update(
+ double beta,
+ double denom,
+ int knew
+ ) {
+ printMethod(); // XXX
+
+ final int n = currentBest.getDimension();
+ final int npt = numberOfInterpolationPoints;
+ final int nptm = npt - n - 1;
+
+ // XXX Should probably be split into two arrays.
+ final ArrayRealVector work = new ArrayRealVector(npt + n);
+
+ double ztest = ZERO;
+ for (int k = 0; k < npt; k++) {
+ for (int j = 0; j < nptm; j++) {
+ // Computing MAX
+ ztest = FastMath.max(ztest, FastMath.abs(zMatrix.getEntry(k, j)));
+ }
+ }
+ ztest *= 1e-20;
+
+ // Apply the rotations that put zeros in the KNEW-th row of ZMAT.
+
+ for (int j = 1; j < nptm; j++) {
+ final double d1 = zMatrix.getEntry(knew, j);
+ if (FastMath.abs(d1) > ztest) {
+ // Computing 2nd power
+ final double d2 = zMatrix.getEntry(knew, 0);
+ // Computing 2nd power
+ final double d3 = zMatrix.getEntry(knew, j);
+ final double d4 = FastMath.sqrt(d2 * d2 + d3 * d3);
+ final double d5 = zMatrix.getEntry(knew, 0) / d4;
+ final double d6 = zMatrix.getEntry(knew, j) / d4;
+ for (int i = 0; i < npt; i++) {
+ final double d7 = d5 * zMatrix.getEntry(i, 0) + d6 * zMatrix.getEntry(i, j);
+ zMatrix.setEntry(i, j, d5 * zMatrix.getEntry(i, j) - d6 * zMatrix.getEntry(i, 0));
+ zMatrix.setEntry(i, 0, d7);
+ }
+ }
+ zMatrix.setEntry(knew, j, ZERO);
+ }
+
+ // Put the first NPT components of the KNEW-th column of HLAG into W,
+ // and calculate the parameters of the updating formula.
+
+ for (int i = 0; i < npt; i++) {
+ work.setEntry(i, zMatrix.getEntry(knew, 0) * zMatrix.getEntry(i, 0));
+ }
+ final double alpha = work.getEntry(knew);
+ final double tau = lagrangeValuesAtNewPoint.getEntry(knew);
+ lagrangeValuesAtNewPoint.setEntry(knew, lagrangeValuesAtNewPoint.getEntry(knew) - ONE);
+
+ // Complete the updating of ZMAT.
+
+ final double sqrtDenom = FastMath.sqrt(denom);
+ final double d1 = tau / sqrtDenom;
+ final double d2 = zMatrix.getEntry(knew, 0) / sqrtDenom;
+ for (int i = 0; i < npt; i++) {
+ zMatrix.setEntry(i, 0,
+ d1 * zMatrix.getEntry(i, 0) - d2 * lagrangeValuesAtNewPoint.getEntry(i));
+ }
+
+ // Finally, update the matrix BMAT.
+
+ for (int j = 0; j < n; j++) {
+ final int jp = npt + j;
+ work.setEntry(jp, bMatrix.getEntry(knew, j));
+ final double d3 = (alpha * lagrangeValuesAtNewPoint.getEntry(jp) - tau * work.getEntry(jp)) / denom;
+ final double d4 = (-beta * work.getEntry(jp) - tau * lagrangeValuesAtNewPoint.getEntry(jp)) / denom;
+ for (int i = 0; i <= jp; i++) {
+ bMatrix.setEntry(i, j,
+ bMatrix.getEntry(i, j) + d3 * lagrangeValuesAtNewPoint.getEntry(i) + d4 * work.getEntry(i));
+ if (i >= npt) {
+ bMatrix.setEntry(jp, (i - npt), bMatrix.getEntry(i, j));
+ }
+ }
+ }
+ } // update
+
+ /**
+ * Performs validity checks.
+ *
+ * @param lowerBound Lower bounds (constraints) of the objective variables.
+ * @param upperBound Upperer bounds (constraints) of the objective variables.
+ */
+ private void setup(double[] lowerBound,
+ double[] upperBound) {
+ printMethod(); // XXX
+
+ double[] init = getStartPoint();
+ final int dimension = init.length;
+
+ // Check problem dimension.
+ if (dimension < MINIMUM_PROBLEM_DIMENSION) {
+ throw new NumberIsTooSmallException(dimension, MINIMUM_PROBLEM_DIMENSION, true);
+ }
+ // Check number of interpolation points.
+ final int[] nPointsInterval = { dimension + 2, (dimension + 2) * (dimension + 1) / 2 };
+ if (numberOfInterpolationPoints < nPointsInterval[0] ||
+ numberOfInterpolationPoints > nPointsInterval[1]) {
+ throw new OutOfRangeException(LocalizedFormats.NUMBER_OF_INTERPOLATION_POINTS,
+ numberOfInterpolationPoints,
+ nPointsInterval[0],
+ nPointsInterval[1]);
+ }
+
+ // Initialize bound differences.
+ boundDifference = new double[dimension];
+
+ double requiredMinDiff = 2 * initialTrustRegionRadius;
+ double minDiff = Double.POSITIVE_INFINITY;
+ for (int i = 0; i < dimension; i++) {
+ boundDifference[i] = upperBound[i] - lowerBound[i];
+ minDiff = FastMath.min(minDiff, boundDifference[i]);
+ }
+ if (minDiff < requiredMinDiff) {
+ initialTrustRegionRadius = minDiff / 3.0;
+ }
+
+ // Initialize the data structures used by the "bobyqa" method.
+ bMatrix = new Array2DRowRealMatrix(dimension + numberOfInterpolationPoints,
+ dimension);
+ zMatrix = new Array2DRowRealMatrix(numberOfInterpolationPoints,
+ numberOfInterpolationPoints - dimension - 1);
+ interpolationPoints = new Array2DRowRealMatrix(numberOfInterpolationPoints,
+ dimension);
+ originShift = new ArrayRealVector(dimension);
+ fAtInterpolationPoints = new ArrayRealVector(numberOfInterpolationPoints);
+ trustRegionCenterOffset = new ArrayRealVector(dimension);
+ gradientAtTrustRegionCenter = new ArrayRealVector(dimension);
+ lowerDifference = new ArrayRealVector(dimension);
+ upperDifference = new ArrayRealVector(dimension);
+ modelSecondDerivativesParameters = new ArrayRealVector(numberOfInterpolationPoints);
+ newPoint = new ArrayRealVector(dimension);
+ alternativeNewPoint = new ArrayRealVector(dimension);
+ trialStepPoint = new ArrayRealVector(dimension);
+ lagrangeValuesAtNewPoint = new ArrayRealVector(dimension + numberOfInterpolationPoints);
+ modelSecondDerivativesValues = new ArrayRealVector(dimension * (dimension + 1) / 2);
+ }
+
+ // XXX utility for figuring out call sequence.
+ private static String caller(int n) {
+ final Throwable t = new Throwable();
+ final StackTraceElement[] elements = t.getStackTrace();
+ final StackTraceElement e = elements[n];
+ return e.getMethodName() + " (at line " + e.getLineNumber() + ")";
+ }
+ // XXX utility for figuring out call sequence.
+ private static void printState(int s) {
+ // System.out.println(caller(2) + ": state " + s);
+ }
+ // XXX utility for figuring out call sequence.
+ private static void printMethod() {
+ // System.out.println(caller(2));
+ }
+
+ /**
+ * Marker for code paths that are not explored with the current unit tests.
+ * If the path becomes explored, it should just be removed from the code.
+ */
+ private static class PathIsExploredException extends RuntimeException {
+ /** Serializable UID. */
+ private static final long serialVersionUID = 745350979634801853L;
+
+ /** Message string. */
+ private static final String PATH_IS_EXPLORED
+ = "If this exception is thrown, just remove it from the code";
+
+ PathIsExploredException() {
+ super(PATH_IS_EXPLORED + " " + BOBYQAOptimizer.caller(3));
+ }
+ }
+}
+//CHECKSTYLE: resume all
diff --git a/src/main/java/org/apache/commons/math3/optimization/direct/BaseAbstractMultivariateOptimizer.java b/src/main/java/org/apache/commons/math3/optimization/direct/BaseAbstractMultivariateOptimizer.java
new file mode 100644
index 0000000..d148d8c
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/optimization/direct/BaseAbstractMultivariateOptimizer.java
@@ -0,0 +1,318 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math3.optimization.direct;
+
+import org.apache.commons.math3.util.Incrementor;
+import org.apache.commons.math3.exception.MaxCountExceededException;
+import org.apache.commons.math3.exception.TooManyEvaluationsException;
+import org.apache.commons.math3.analysis.MultivariateFunction;
+import org.apache.commons.math3.optimization.BaseMultivariateOptimizer;
+import org.apache.commons.math3.optimization.OptimizationData;
+import org.apache.commons.math3.optimization.GoalType;
+import org.apache.commons.math3.optimization.InitialGuess;
+import org.apache.commons.math3.optimization.SimpleBounds;
+import org.apache.commons.math3.optimization.ConvergenceChecker;
+import org.apache.commons.math3.optimization.PointValuePair;
+import org.apache.commons.math3.optimization.SimpleValueChecker;
+import org.apache.commons.math3.exception.DimensionMismatchException;
+import org.apache.commons.math3.exception.NumberIsTooSmallException;
+import org.apache.commons.math3.exception.NumberIsTooLargeException;
+
+/**
+ * Base class for implementing optimizers for multivariate scalar functions.
+ * This base class handles the boiler-plate methods associated to thresholds,
+ * evaluations counting, initial guess and simple bounds settings.
+ *
+ * @param <FUNC> Type of the objective function to be optimized.
+ *
+ * @deprecated As of 3.1 (to be removed in 4.0).
+ * @since 2.2
+ */
+@Deprecated
+public abstract class BaseAbstractMultivariateOptimizer<FUNC extends MultivariateFunction>
+ implements BaseMultivariateOptimizer<FUNC> {
+ /** Evaluations counter. */
+ protected final Incrementor evaluations = new Incrementor();
+ /** Convergence checker. */
+ private ConvergenceChecker<PointValuePair> checker;
+ /** Type of optimization. */
+ private GoalType goal;
+ /** Initial guess. */
+ private double[] start;
+ /** Lower bounds. */
+ private double[] lowerBound;
+ /** Upper bounds. */
+ private double[] upperBound;
+ /** Objective function. */
+ private MultivariateFunction function;
+
+ /**
+ * Simple constructor with default settings.
+ * The convergence check is set to a {@link SimpleValueChecker}.
+ * @deprecated See {@link SimpleValueChecker#SimpleValueChecker()}
+ */
+ @Deprecated
+ protected BaseAbstractMultivariateOptimizer() {
+ this(new SimpleValueChecker());
+ }
+ /**
+ * @param checker Convergence checker.
+ */
+ protected BaseAbstractMultivariateOptimizer(ConvergenceChecker<PointValuePair> checker) {
+ this.checker = checker;
+ }
+
+ /** {@inheritDoc} */
+ public int getMaxEvaluations() {
+ return evaluations.getMaximalCount();
+ }
+
+ /** {@inheritDoc} */
+ public int getEvaluations() {
+ return evaluations.getCount();
+ }
+
+ /** {@inheritDoc} */
+ public ConvergenceChecker<PointValuePair> getConvergenceChecker() {
+ return checker;
+ }
+
+ /**
+ * Compute the objective function value.
+ *
+ * @param point Point at which the objective function must be evaluated.
+ * @return the objective function value at the specified point.
+ * @throws TooManyEvaluationsException if the maximal number of
+ * evaluations is exceeded.
+ */
+ protected double computeObjectiveValue(double[] point) {
+ try {
+ evaluations.incrementCount();
+ } catch (MaxCountExceededException e) {
+ throw new TooManyEvaluationsException(e.getMax());
+ }
+ return function.value(point);
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @deprecated As of 3.1. Please use
+ * {@link #optimize(int,MultivariateFunction,GoalType,OptimizationData[])}
+ * instead.
+ */
+ @Deprecated
+ public PointValuePair optimize(int maxEval, FUNC f, GoalType goalType,
+ double[] startPoint) {
+ return optimizeInternal(maxEval, f, goalType, new InitialGuess(startPoint));
+ }
+
+ /**
+ * Optimize an objective function.
+ *
+ * @param maxEval Allowed number of evaluations of the objective function.
+ * @param f Objective function.
+ * @param goalType Optimization type.
+ * @param optData Optimization data. The following data will be looked for:
+ * <ul>
+ * <li>{@link InitialGuess}</li>
+ * <li>{@link SimpleBounds}</li>
+ * </ul>
+ * @return the point/value pair giving the optimal value of the objective
+ * function.
+ * @since 3.1
+ */
+ public PointValuePair optimize(int maxEval,
+ FUNC f,
+ GoalType goalType,
+ OptimizationData... optData) {
+ return optimizeInternal(maxEval, f, goalType, optData);
+ }
+
+ /**
+ * Optimize an objective function.
+ *
+ * @param f Objective function.
+ * @param goalType Type of optimization goal: either
+ * {@link GoalType#MAXIMIZE} or {@link GoalType#MINIMIZE}.
+ * @param startPoint Start point for optimization.
+ * @param maxEval Maximum number of function evaluations.
+ * @return the point/value pair giving the optimal value for objective
+ * function.
+ * @throws org.apache.commons.math3.exception.DimensionMismatchException
+ * if the start point dimension is wrong.
+ * @throws org.apache.commons.math3.exception.TooManyEvaluationsException
+ * if the maximal number of evaluations is exceeded.
+ * @throws org.apache.commons.math3.exception.NullArgumentException if
+ * any argument is {@code null}.
+ * @deprecated As of 3.1. Please use
+ * {@link #optimize(int,MultivariateFunction,GoalType,OptimizationData[])}
+ * instead.
+ */
+ @Deprecated
+ protected PointValuePair optimizeInternal(int maxEval, FUNC f, GoalType goalType,
+ double[] startPoint) {
+ return optimizeInternal(maxEval, f, goalType, new InitialGuess(startPoint));
+ }
+
+ /**
+ * Optimize an objective function.
+ *
+ * @param maxEval Allowed number of evaluations of the objective function.
+ * @param f Objective function.
+ * @param goalType Optimization type.
+ * @param optData Optimization data. The following data will be looked for:
+ * <ul>
+ * <li>{@link InitialGuess}</li>
+ * <li>{@link SimpleBounds}</li>
+ * </ul>
+ * @return the point/value pair giving the optimal value of the objective
+ * function.
+ * @throws TooManyEvaluationsException if the maximal number of
+ * evaluations is exceeded.
+ * @since 3.1
+ */
+ protected PointValuePair optimizeInternal(int maxEval,
+ FUNC f,
+ GoalType goalType,
+ OptimizationData... optData)
+ throws TooManyEvaluationsException {
+ // Set internal state.
+ evaluations.setMaximalCount(maxEval);
+ evaluations.resetCount();
+ function = f;
+ goal = goalType;
+ // Retrieve other settings.
+ parseOptimizationData(optData);
+ // Check input consistency.
+ checkParameters();
+ // Perform computation.
+ return doOptimize();
+ }
+
+ /**
+ * Scans the list of (required and optional) optimization data that
+ * characterize the problem.
+ *
+ * @param optData Optimization data. The following data will be looked for:
+ * <ul>
+ * <li>{@link InitialGuess}</li>
+ * <li>{@link SimpleBounds}</li>
+ * </ul>
+ */
+ private void parseOptimizationData(OptimizationData... optData) {
+ // The existing values (as set by the previous call) are reused if
+ // not provided in the argument list.
+ for (OptimizationData data : optData) {
+ if (data instanceof InitialGuess) {
+ start = ((InitialGuess) data).getInitialGuess();
+ continue;
+ }
+ if (data instanceof SimpleBounds) {
+ final SimpleBounds bounds = (SimpleBounds) data;
+ lowerBound = bounds.getLower();
+ upperBound = bounds.getUpper();
+ continue;
+ }
+ }
+ }
+
+ /**
+ * @return the optimization type.
+ */
+ public GoalType getGoalType() {
+ return goal;
+ }
+
+ /**
+ * @return the initial guess.
+ */
+ public double[] getStartPoint() {
+ return start == null ? null : start.clone();
+ }
+ /**
+ * @return the lower bounds.
+ * @since 3.1
+ */
+ public double[] getLowerBound() {
+ return lowerBound == null ? null : lowerBound.clone();
+ }
+ /**
+ * @return the upper bounds.
+ * @since 3.1
+ */
+ public double[] getUpperBound() {
+ return upperBound == null ? null : upperBound.clone();
+ }
+
+ /**
+ * Perform the bulk of the optimization algorithm.
+ *
+ * @return the point/value pair giving the optimal value of the
+ * objective function.
+ */
+ protected abstract PointValuePair doOptimize();
+
+ /**
+ * Check parameters consistency.
+ */
+ private void checkParameters() {
+ if (start != null) {
+ final int dim = start.length;
+ if (lowerBound != null) {
+ if (lowerBound.length != dim) {
+ throw new DimensionMismatchException(lowerBound.length, dim);
+ }
+ for (int i = 0; i < dim; i++) {
+ final double v = start[i];
+ final double lo = lowerBound[i];
+ if (v < lo) {
+ throw new NumberIsTooSmallException(v, lo, true);
+ }
+ }
+ }
+ if (upperBound != null) {
+ if (upperBound.length != dim) {
+ throw new DimensionMismatchException(upperBound.length, dim);
+ }
+ for (int i = 0; i < dim; i++) {
+ final double v = start[i];
+ final double hi = upperBound[i];
+ if (v > hi) {
+ throw new NumberIsTooLargeException(v, hi, true);
+ }
+ }
+ }
+
+ // If the bounds were not specified, the allowed interval is
+ // assumed to be [-inf, +inf].
+ if (lowerBound == null) {
+ lowerBound = new double[dim];
+ for (int i = 0; i < dim; i++) {
+ lowerBound[i] = Double.NEGATIVE_INFINITY;
+ }
+ }
+ if (upperBound == null) {
+ upperBound = new double[dim];
+ for (int i = 0; i < dim; i++) {
+ upperBound[i] = Double.POSITIVE_INFINITY;
+ }
+ }
+ }
+ }
+}
diff --git a/src/main/java/org/apache/commons/math3/optimization/direct/BaseAbstractMultivariateSimpleBoundsOptimizer.java b/src/main/java/org/apache/commons/math3/optimization/direct/BaseAbstractMultivariateSimpleBoundsOptimizer.java
new file mode 100644
index 0000000..67a4296
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/optimization/direct/BaseAbstractMultivariateSimpleBoundsOptimizer.java
@@ -0,0 +1,82 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math3.optimization.direct;
+
+import org.apache.commons.math3.analysis.MultivariateFunction;
+import org.apache.commons.math3.optimization.BaseMultivariateOptimizer;
+import org.apache.commons.math3.optimization.BaseMultivariateSimpleBoundsOptimizer;
+import org.apache.commons.math3.optimization.GoalType;
+import org.apache.commons.math3.optimization.InitialGuess;
+import org.apache.commons.math3.optimization.SimpleBounds;
+import org.apache.commons.math3.optimization.PointValuePair;
+import org.apache.commons.math3.optimization.ConvergenceChecker;
+
+/**
+ * Base class for implementing optimizers for multivariate scalar functions,
+ * subject to simple bounds: The valid range of the parameters is an interval.
+ * The interval can possibly be infinite (in one or both directions).
+ * This base class handles the boiler-plate methods associated to thresholds
+ * settings, iterations and evaluations counting.
+ *
+ * @param <FUNC> Type of the objective function to be optimized.
+ *
+ * @deprecated As of 3.1 (to be removed in 4.0).
+ * @since 3.0
+ * @deprecated As of 3.1 since the {@link BaseAbstractMultivariateOptimizer
+ * base class} contains similar functionality.
+ */
+@Deprecated
+public abstract class BaseAbstractMultivariateSimpleBoundsOptimizer<FUNC extends MultivariateFunction>
+ extends BaseAbstractMultivariateOptimizer<FUNC>
+ implements BaseMultivariateOptimizer<FUNC>,
+ BaseMultivariateSimpleBoundsOptimizer<FUNC> {
+ /**
+ * Simple constructor with default settings.
+ * The convergence checker is set to a
+ * {@link org.apache.commons.math3.optimization.SimpleValueChecker}.
+ *
+ * @see BaseAbstractMultivariateOptimizer#BaseAbstractMultivariateOptimizer()
+ * @deprecated See {@link org.apache.commons.math3.optimization.SimpleValueChecker#SimpleValueChecker()}
+ */
+ @Deprecated
+ protected BaseAbstractMultivariateSimpleBoundsOptimizer() {}
+
+ /**
+ * @param checker Convergence checker.
+ */
+ protected BaseAbstractMultivariateSimpleBoundsOptimizer(ConvergenceChecker<PointValuePair> checker) {
+ super(checker);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public PointValuePair optimize(int maxEval, FUNC f, GoalType goalType,
+ double[] startPoint) {
+ return super.optimizeInternal(maxEval, f, goalType,
+ new InitialGuess(startPoint));
+ }
+
+ /** {@inheritDoc} */
+ public PointValuePair optimize(int maxEval, FUNC f, GoalType goalType,
+ double[] startPoint,
+ double[] lower, double[] upper) {
+ return super.optimizeInternal(maxEval, f, goalType,
+ new InitialGuess(startPoint),
+ new SimpleBounds(lower, upper));
+ }
+}
diff --git a/src/main/java/org/apache/commons/math3/optimization/direct/BaseAbstractMultivariateVectorOptimizer.java b/src/main/java/org/apache/commons/math3/optimization/direct/BaseAbstractMultivariateVectorOptimizer.java
new file mode 100644
index 0000000..e070632
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/optimization/direct/BaseAbstractMultivariateVectorOptimizer.java
@@ -0,0 +1,370 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math3.optimization.direct;
+
+import org.apache.commons.math3.util.Incrementor;
+import org.apache.commons.math3.exception.MaxCountExceededException;
+import org.apache.commons.math3.exception.TooManyEvaluationsException;
+import org.apache.commons.math3.exception.DimensionMismatchException;
+import org.apache.commons.math3.exception.NullArgumentException;
+import org.apache.commons.math3.analysis.MultivariateVectorFunction;
+import org.apache.commons.math3.optimization.OptimizationData;
+import org.apache.commons.math3.optimization.InitialGuess;
+import org.apache.commons.math3.optimization.Target;
+import org.apache.commons.math3.optimization.Weight;
+import org.apache.commons.math3.optimization.BaseMultivariateVectorOptimizer;
+import org.apache.commons.math3.optimization.ConvergenceChecker;
+import org.apache.commons.math3.optimization.PointVectorValuePair;
+import org.apache.commons.math3.optimization.SimpleVectorValueChecker;
+import org.apache.commons.math3.linear.RealMatrix;
+
+/**
+ * Base class for implementing optimizers for multivariate scalar functions.
+ * This base class handles the boiler-plate methods associated to thresholds
+ * settings, iterations and evaluations counting.
+ *
+ * @param <FUNC> the type of the objective function to be optimized
+ *
+ * @deprecated As of 3.1 (to be removed in 4.0).
+ * @since 3.0
+ */
+@Deprecated
+public abstract class BaseAbstractMultivariateVectorOptimizer<FUNC extends MultivariateVectorFunction>
+ implements BaseMultivariateVectorOptimizer<FUNC> {
+ /** Evaluations counter. */
+ protected final Incrementor evaluations = new Incrementor();
+ /** Convergence checker. */
+ private ConvergenceChecker<PointVectorValuePair> checker;
+ /** Target value for the objective functions at optimum. */
+ private double[] target;
+ /** Weight matrix. */
+ private RealMatrix weightMatrix;
+ /** Weight for the least squares cost computation.
+ * @deprecated
+ */
+ @Deprecated
+ private double[] weight;
+ /** Initial guess. */
+ private double[] start;
+ /** Objective function. */
+ private FUNC function;
+
+ /**
+ * Simple constructor with default settings.
+ * The convergence check is set to a {@link SimpleVectorValueChecker}.
+ * @deprecated See {@link SimpleVectorValueChecker#SimpleVectorValueChecker()}
+ */
+ @Deprecated
+ protected BaseAbstractMultivariateVectorOptimizer() {
+ this(new SimpleVectorValueChecker());
+ }
+ /**
+ * @param checker Convergence checker.
+ */
+ protected BaseAbstractMultivariateVectorOptimizer(ConvergenceChecker<PointVectorValuePair> checker) {
+ this.checker = checker;
+ }
+
+ /** {@inheritDoc} */
+ public int getMaxEvaluations() {
+ return evaluations.getMaximalCount();
+ }
+
+ /** {@inheritDoc} */
+ public int getEvaluations() {
+ return evaluations.getCount();
+ }
+
+ /** {@inheritDoc} */
+ public ConvergenceChecker<PointVectorValuePair> getConvergenceChecker() {
+ return checker;
+ }
+
+ /**
+ * Compute the objective function value.
+ *
+ * @param point Point at which the objective function must be evaluated.
+ * @return the objective function value at the specified point.
+ * @throws TooManyEvaluationsException if the maximal number of evaluations is
+ * exceeded.
+ */
+ protected double[] computeObjectiveValue(double[] point) {
+ try {
+ evaluations.incrementCount();
+ } catch (MaxCountExceededException e) {
+ throw new TooManyEvaluationsException(e.getMax());
+ }
+ return function.value(point);
+ }
+
+ /** {@inheritDoc}
+ *
+ * @deprecated As of 3.1. Please use
+ * {@link #optimize(int,MultivariateVectorFunction,OptimizationData[])}
+ * instead.
+ */
+ @Deprecated
+ public PointVectorValuePair optimize(int maxEval, FUNC f, double[] t, double[] w,
+ double[] startPoint) {
+ return optimizeInternal(maxEval, f, t, w, startPoint);
+ }
+
+ /**
+ * Optimize an objective function.
+ *
+ * @param maxEval Allowed number of evaluations of the objective function.
+ * @param f Objective function.
+ * @param optData Optimization data. The following data will be looked for:
+ * <ul>
+ * <li>{@link Target}</li>
+ * <li>{@link Weight}</li>
+ * <li>{@link InitialGuess}</li>
+ * </ul>
+ * @return the point/value pair giving the optimal value of the objective
+ * function.
+ * @throws TooManyEvaluationsException if the maximal number of
+ * evaluations is exceeded.
+ * @throws DimensionMismatchException if the initial guess, target, and weight
+ * arguments have inconsistent dimensions.
+ *
+ * @since 3.1
+ */
+ protected PointVectorValuePair optimize(int maxEval,
+ FUNC f,
+ OptimizationData... optData)
+ throws TooManyEvaluationsException,
+ DimensionMismatchException {
+ return optimizeInternal(maxEval, f, optData);
+ }
+
+ /**
+ * Optimize an objective function.
+ * Optimization is considered to be a weighted least-squares minimization.
+ * The cost function to be minimized is
+ * <code>&sum;weight<sub>i</sub>(objective<sub>i</sub> - target<sub>i</sub>)<sup>2</sup></code>
+ *
+ * @param f Objective function.
+ * @param t Target value for the objective functions at optimum.
+ * @param w Weights for the least squares cost computation.
+ * @param startPoint Start point for optimization.
+ * @return the point/value pair giving the optimal value for objective
+ * function.
+ * @param maxEval Maximum number of function evaluations.
+ * @throws org.apache.commons.math3.exception.DimensionMismatchException
+ * if the start point dimension is wrong.
+ * @throws org.apache.commons.math3.exception.TooManyEvaluationsException
+ * if the maximal number of evaluations is exceeded.
+ * @throws org.apache.commons.math3.exception.NullArgumentException if
+ * any argument is {@code null}.
+ * @deprecated As of 3.1. Please use
+ * {@link #optimizeInternal(int,MultivariateVectorFunction,OptimizationData[])}
+ * instead.
+ */
+ @Deprecated
+ protected PointVectorValuePair optimizeInternal(final int maxEval, final FUNC f,
+ final double[] t, final double[] w,
+ final double[] startPoint) {
+ // Checks.
+ if (f == null) {
+ throw new NullArgumentException();
+ }
+ if (t == null) {
+ throw new NullArgumentException();
+ }
+ if (w == null) {
+ throw new NullArgumentException();
+ }
+ if (startPoint == null) {
+ throw new NullArgumentException();
+ }
+ if (t.length != w.length) {
+ throw new DimensionMismatchException(t.length, w.length);
+ }
+
+ return optimizeInternal(maxEval, f,
+ new Target(t),
+ new Weight(w),
+ new InitialGuess(startPoint));
+ }
+
+ /**
+ * Optimize an objective function.
+ *
+ * @param maxEval Allowed number of evaluations of the objective function.
+ * @param f Objective function.
+ * @param optData Optimization data. The following data will be looked for:
+ * <ul>
+ * <li>{@link Target}</li>
+ * <li>{@link Weight}</li>
+ * <li>{@link InitialGuess}</li>
+ * </ul>
+ * @return the point/value pair giving the optimal value of the objective
+ * function.
+ * @throws TooManyEvaluationsException if the maximal number of
+ * evaluations is exceeded.
+ * @throws DimensionMismatchException if the initial guess, target, and weight
+ * arguments have inconsistent dimensions.
+ *
+ * @since 3.1
+ */
+ protected PointVectorValuePair optimizeInternal(int maxEval,
+ FUNC f,
+ OptimizationData... optData)
+ throws TooManyEvaluationsException,
+ DimensionMismatchException {
+ // Set internal state.
+ evaluations.setMaximalCount(maxEval);
+ evaluations.resetCount();
+ function = f;
+ // Retrieve other settings.
+ parseOptimizationData(optData);
+ // Check input consistency.
+ checkParameters();
+ // Allow subclasses to reset their own internal state.
+ setUp();
+ // Perform computation.
+ return doOptimize();
+ }
+
+ /**
+ * Gets the initial values of the optimized parameters.
+ *
+ * @return the initial guess.
+ */
+ public double[] getStartPoint() {
+ return start.clone();
+ }
+
+ /**
+ * Gets the weight matrix of the observations.
+ *
+ * @return the weight matrix.
+ * @since 3.1
+ */
+ public RealMatrix getWeight() {
+ return weightMatrix.copy();
+ }
+ /**
+ * Gets the observed values to be matched by the objective vector
+ * function.
+ *
+ * @return the target values.
+ * @since 3.1
+ */
+ public double[] getTarget() {
+ return target.clone();
+ }
+
+ /**
+ * Gets the objective vector function.
+ * Note that this access bypasses the evaluation counter.
+ *
+ * @return the objective vector function.
+ * @since 3.1
+ */
+ protected FUNC getObjectiveFunction() {
+ return function;
+ }
+
+ /**
+ * Perform the bulk of the optimization algorithm.
+ *
+ * @return the point/value pair giving the optimal value for the
+ * objective function.
+ */
+ protected abstract PointVectorValuePair doOptimize();
+
+ /**
+ * @return a reference to the {@link #target array}.
+ * @deprecated As of 3.1.
+ */
+ @Deprecated
+ protected double[] getTargetRef() {
+ return target;
+ }
+ /**
+ * @return a reference to the {@link #weight array}.
+ * @deprecated As of 3.1.
+ */
+ @Deprecated
+ protected double[] getWeightRef() {
+ return weight;
+ }
+
+ /**
+ * Method which a subclass <em>must</em> override whenever its internal
+ * state depend on the {@link OptimizationData input} parsed by this base
+ * class.
+ * It will be called after the parsing step performed in the
+ * {@link #optimize(int,MultivariateVectorFunction,OptimizationData[])
+ * optimize} method and just before {@link #doOptimize()}.
+ *
+ * @since 3.1
+ */
+ protected void setUp() {
+ // XXX Temporary code until the new internal data is used everywhere.
+ final int dim = target.length;
+ weight = new double[dim];
+ for (int i = 0; i < dim; i++) {
+ weight[i] = weightMatrix.getEntry(i, i);
+ }
+ }
+
+ /**
+ * Scans the list of (required and optional) optimization data that
+ * characterize the problem.
+ *
+ * @param optData Optimization data. The following data will be looked for:
+ * <ul>
+ * <li>{@link Target}</li>
+ * <li>{@link Weight}</li>
+ * <li>{@link InitialGuess}</li>
+ * </ul>
+ */
+ private void parseOptimizationData(OptimizationData... optData) {
+ // The existing values (as set by the previous call) are reused if
+ // not provided in the argument list.
+ for (OptimizationData data : optData) {
+ if (data instanceof Target) {
+ target = ((Target) data).getTarget();
+ continue;
+ }
+ if (data instanceof Weight) {
+ weightMatrix = ((Weight) data).getWeight();
+ continue;
+ }
+ if (data instanceof InitialGuess) {
+ start = ((InitialGuess) data).getInitialGuess();
+ continue;
+ }
+ }
+ }
+
+ /**
+ * Check parameters consistency.
+ *
+ * @throws DimensionMismatchException if {@link #target} and
+ * {@link #weightMatrix} have inconsistent dimensions.
+ */
+ private void checkParameters() {
+ if (target.length != weightMatrix.getColumnDimension()) {
+ throw new DimensionMismatchException(target.length,
+ weightMatrix.getColumnDimension());
+ }
+ }
+}
diff --git a/src/main/java/org/apache/commons/math3/optimization/direct/CMAESOptimizer.java b/src/main/java/org/apache/commons/math3/optimization/direct/CMAESOptimizer.java
new file mode 100644
index 0000000..388a6f7
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/optimization/direct/CMAESOptimizer.java
@@ -0,0 +1,1441 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math3.optimization.direct;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+import org.apache.commons.math3.analysis.MultivariateFunction;
+import org.apache.commons.math3.exception.DimensionMismatchException;
+import org.apache.commons.math3.exception.NotPositiveException;
+import org.apache.commons.math3.exception.NotStrictlyPositiveException;
+import org.apache.commons.math3.exception.OutOfRangeException;
+import org.apache.commons.math3.exception.TooManyEvaluationsException;
+import org.apache.commons.math3.linear.Array2DRowRealMatrix;
+import org.apache.commons.math3.linear.EigenDecomposition;
+import org.apache.commons.math3.linear.MatrixUtils;
+import org.apache.commons.math3.linear.RealMatrix;
+import org.apache.commons.math3.optimization.ConvergenceChecker;
+import org.apache.commons.math3.optimization.OptimizationData;
+import org.apache.commons.math3.optimization.GoalType;
+import org.apache.commons.math3.optimization.MultivariateOptimizer;
+import org.apache.commons.math3.optimization.PointValuePair;
+import org.apache.commons.math3.optimization.SimpleValueChecker;
+import org.apache.commons.math3.random.MersenneTwister;
+import org.apache.commons.math3.random.RandomGenerator;
+import org.apache.commons.math3.util.FastMath;
+import org.apache.commons.math3.util.MathArrays;
+
+/**
+ * <p>An implementation of the active Covariance Matrix Adaptation Evolution Strategy (CMA-ES)
+ * for non-linear, non-convex, non-smooth, global function minimization.
+ * The CMA-Evolution Strategy (CMA-ES) is a reliable stochastic optimization method
+ * which should be applied if derivative-based methods, e.g. quasi-Newton BFGS or
+ * conjugate gradient, fail due to a rugged search landscape (e.g. noise, local
+ * optima, outlier, etc.) of the objective function. Like a
+ * quasi-Newton method, the CMA-ES learns and applies a variable metric
+ * on the underlying search space. Unlike a quasi-Newton method, the
+ * CMA-ES neither estimates nor uses gradients, making it considerably more
+ * reliable in terms of finding a good, or even close to optimal, solution.</p>
+ *
+ * <p>In general, on smooth objective functions the CMA-ES is roughly ten times
+ * slower than BFGS (counting objective function evaluations, no gradients provided).
+ * For up to <math>N=10</math> variables also the derivative-free simplex
+ * direct search method (Nelder and Mead) can be faster, but it is
+ * far less reliable than CMA-ES.</p>
+ *
+ * <p>The CMA-ES is particularly well suited for non-separable
+ * and/or badly conditioned problems. To observe the advantage of CMA compared
+ * to a conventional evolution strategy, it will usually take about
+ * <math>30 N</math> function evaluations. On difficult problems the complete
+ * optimization (a single run) is expected to take <em>roughly</em> between
+ * <math>30 N</math> and <math>300 N<sup>2</sup></math>
+ * function evaluations.</p>
+ *
+ * <p>This implementation is translated and adapted from the Matlab version
+ * of the CMA-ES algorithm as implemented in module {@code cmaes.m} version 3.51.</p>
+ *
+ * For more information, please refer to the following links:
+ * <ul>
+ * <li><a href="http://www.lri.fr/~hansen/cmaes.m">Matlab code</a></li>
+ * <li><a href="http://www.lri.fr/~hansen/cmaesintro.html">Introduction to CMA-ES</a></li>
+ * <li><a href="http://en.wikipedia.org/wiki/CMA-ES">Wikipedia</a></li>
+ * </ul>
+ *
+ * @deprecated As of 3.1 (to be removed in 4.0).
+ * @since 3.0
+ */
+@Deprecated
+public class CMAESOptimizer
+ extends BaseAbstractMultivariateSimpleBoundsOptimizer<MultivariateFunction>
+ implements MultivariateOptimizer {
+ /** Default value for {@link #checkFeasableCount}: {@value}. */
+ public static final int DEFAULT_CHECKFEASABLECOUNT = 0;
+ /** Default value for {@link #stopFitness}: {@value}. */
+ public static final double DEFAULT_STOPFITNESS = 0;
+ /** Default value for {@link #isActiveCMA}: {@value}. */
+ public static final boolean DEFAULT_ISACTIVECMA = true;
+ /** Default value for {@link #maxIterations}: {@value}. */
+ public static final int DEFAULT_MAXITERATIONS = 30000;
+ /** Default value for {@link #diagonalOnly}: {@value}. */
+ public static final int DEFAULT_DIAGONALONLY = 0;
+ /** Default value for {@link #random}. */
+ public static final RandomGenerator DEFAULT_RANDOMGENERATOR = new MersenneTwister();
+
+ // global search parameters
+ /**
+ * Population size, offspring number. The primary strategy parameter to play
+ * with, which can be increased from its default value. Increasing the
+ * population size improves global search properties in exchange to speed.
+ * Speed decreases, as a rule, at most linearly with increasing population
+ * size. It is advisable to begin with the default small population size.
+ */
+ private int lambda; // population size
+ /**
+ * Covariance update mechanism, default is active CMA. isActiveCMA = true
+ * turns on "active CMA" with a negative update of the covariance matrix and
+ * checks for positive definiteness. OPTS.CMA.active = 2 does not check for
+ * pos. def. and is numerically faster. Active CMA usually speeds up the
+ * adaptation.
+ */
+ private boolean isActiveCMA;
+ /**
+ * Determines how often a new random offspring is generated in case it is
+ * not feasible / beyond the defined limits, default is 0.
+ */
+ private int checkFeasableCount;
+ /**
+ * @see Sigma
+ */
+ private double[] inputSigma;
+ /** Number of objective variables/problem dimension */
+ private int dimension;
+ /**
+ * Defines the number of initial iterations, where the covariance matrix
+ * remains diagonal and the algorithm has internally linear time complexity.
+ * diagonalOnly = 1 means keeping the covariance matrix always diagonal and
+ * this setting also exhibits linear space complexity. This can be
+ * particularly useful for dimension > 100.
+ * @see <a href="http://hal.archives-ouvertes.fr/inria-00287367/en">A Simple Modification in CMA-ES</a>
+ */
+ private int diagonalOnly = 0;
+ /** Number of objective variables/problem dimension */
+ private boolean isMinimize = true;
+ /** Indicates whether statistic data is collected. */
+ private boolean generateStatistics = false;
+
+ // termination criteria
+ /** Maximal number of iterations allowed. */
+ private int maxIterations;
+ /** Limit for fitness value. */
+ private double stopFitness;
+ /** Stop if x-changes larger stopTolUpX. */
+ private double stopTolUpX;
+ /** Stop if x-change smaller stopTolX. */
+ private double stopTolX;
+ /** Stop if fun-changes smaller stopTolFun. */
+ private double stopTolFun;
+ /** Stop if back fun-changes smaller stopTolHistFun. */
+ private double stopTolHistFun;
+
+ // selection strategy parameters
+ /** Number of parents/points for recombination. */
+ private int mu; //
+ /** log(mu + 0.5), stored for efficiency. */
+ private double logMu2;
+ /** Array for weighted recombination. */
+ private RealMatrix weights;
+ /** Variance-effectiveness of sum w_i x_i. */
+ private double mueff; //
+
+ // dynamic strategy parameters and constants
+ /** Overall standard deviation - search volume. */
+ private double sigma;
+ /** Cumulation constant. */
+ private double cc;
+ /** Cumulation constant for step-size. */
+ private double cs;
+ /** Damping for step-size. */
+ private double damps;
+ /** Learning rate for rank-one update. */
+ private double ccov1;
+ /** Learning rate for rank-mu update' */
+ private double ccovmu;
+ /** Expectation of ||N(0,I)|| == norm(randn(N,1)). */
+ private double chiN;
+ /** Learning rate for rank-one update - diagonalOnly */
+ private double ccov1Sep;
+ /** Learning rate for rank-mu update - diagonalOnly */
+ private double ccovmuSep;
+
+ // CMA internal values - updated each generation
+ /** Objective variables. */
+ private RealMatrix xmean;
+ /** Evolution path. */
+ private RealMatrix pc;
+ /** Evolution path for sigma. */
+ private RealMatrix ps;
+ /** Norm of ps, stored for efficiency. */
+ private double normps;
+ /** Coordinate system. */
+ private RealMatrix B;
+ /** Scaling. */
+ private RealMatrix D;
+ /** B*D, stored for efficiency. */
+ private RealMatrix BD;
+ /** Diagonal of sqrt(D), stored for efficiency. */
+ private RealMatrix diagD;
+ /** Covariance matrix. */
+ private RealMatrix C;
+ /** Diagonal of C, used for diagonalOnly. */
+ private RealMatrix diagC;
+ /** Number of iterations already performed. */
+ private int iterations;
+
+ /** History queue of best values. */
+ private double[] fitnessHistory;
+ /** Size of history queue of best values. */
+ private int historySize;
+
+ /** Random generator. */
+ private RandomGenerator random;
+
+ /** History of sigma values. */
+ private List<Double> statisticsSigmaHistory = new ArrayList<Double>();
+ /** History of mean matrix. */
+ private List<RealMatrix> statisticsMeanHistory = new ArrayList<RealMatrix>();
+ /** History of fitness values. */
+ private List<Double> statisticsFitnessHistory = new ArrayList<Double>();
+ /** History of D matrix. */
+ private List<RealMatrix> statisticsDHistory = new ArrayList<RealMatrix>();
+
+ /**
+ * Default constructor, uses default parameters
+ *
+ * @deprecated As of version 3.1: Parameter {@code lambda} must be
+ * passed with the call to {@link #optimize(int,MultivariateFunction,GoalType,OptimizationData[])
+ * optimize} (whereas in the current code it is set to an undocumented value).
+ */
+ @Deprecated
+ public CMAESOptimizer() {
+ this(0);
+ }
+
+ /**
+ * @param lambda Population size.
+ * @deprecated As of version 3.1: Parameter {@code lambda} must be
+ * passed with the call to {@link #optimize(int,MultivariateFunction,GoalType,OptimizationData[])
+ * optimize} (whereas in the current code it is set to an undocumented value)..
+ */
+ @Deprecated
+ public CMAESOptimizer(int lambda) {
+ this(lambda, null, DEFAULT_MAXITERATIONS, DEFAULT_STOPFITNESS,
+ DEFAULT_ISACTIVECMA, DEFAULT_DIAGONALONLY,
+ DEFAULT_CHECKFEASABLECOUNT, DEFAULT_RANDOMGENERATOR,
+ false, null);
+ }
+
+ /**
+ * @param lambda Population size.
+ * @param inputSigma Initial standard deviations to sample new points
+ * around the initial guess.
+ * @deprecated As of version 3.1: Parameters {@code lambda} and {@code inputSigma} must be
+ * passed with the call to {@link #optimize(int,MultivariateFunction,GoalType,OptimizationData[])
+ * optimize}.
+ */
+ @Deprecated
+ public CMAESOptimizer(int lambda, double[] inputSigma) {
+ this(lambda, inputSigma, DEFAULT_MAXITERATIONS, DEFAULT_STOPFITNESS,
+ DEFAULT_ISACTIVECMA, DEFAULT_DIAGONALONLY,
+ DEFAULT_CHECKFEASABLECOUNT, DEFAULT_RANDOMGENERATOR, false);
+ }
+
+ /**
+ * @param lambda Population size.
+ * @param inputSigma Initial standard deviations to sample new points
+ * around the initial guess.
+ * @param maxIterations Maximal number of iterations.
+ * @param stopFitness Whether to stop if objective function value is smaller than
+ * {@code stopFitness}.
+ * @param isActiveCMA Chooses the covariance matrix update method.
+ * @param diagonalOnly Number of initial iterations, where the covariance matrix
+ * remains diagonal.
+ * @param checkFeasableCount Determines how often new random objective variables are
+ * generated in case they are out of bounds.
+ * @param random Random generator.
+ * @param generateStatistics Whether statistic data is collected.
+ * @deprecated See {@link SimpleValueChecker#SimpleValueChecker()}
+ */
+ @Deprecated
+ public CMAESOptimizer(int lambda, double[] inputSigma,
+ int maxIterations, double stopFitness,
+ boolean isActiveCMA, int diagonalOnly, int checkFeasableCount,
+ RandomGenerator random, boolean generateStatistics) {
+ this(lambda, inputSigma, maxIterations, stopFitness, isActiveCMA,
+ diagonalOnly, checkFeasableCount, random, generateStatistics,
+ new SimpleValueChecker());
+ }
+
+ /**
+ * @param lambda Population size.
+ * @param inputSigma Initial standard deviations to sample new points
+ * around the initial guess.
+ * @param maxIterations Maximal number of iterations.
+ * @param stopFitness Whether to stop if objective function value is smaller than
+ * {@code stopFitness}.
+ * @param isActiveCMA Chooses the covariance matrix update method.
+ * @param diagonalOnly Number of initial iterations, where the covariance matrix
+ * remains diagonal.
+ * @param checkFeasableCount Determines how often new random objective variables are
+ * generated in case they are out of bounds.
+ * @param random Random generator.
+ * @param generateStatistics Whether statistic data is collected.
+ * @param checker Convergence checker.
+ * @deprecated As of version 3.1: Parameters {@code lambda} and {@code inputSigma} must be
+ * passed with the call to {@link #optimize(int,MultivariateFunction,GoalType,OptimizationData[])
+ * optimize}.
+ */
+ @Deprecated
+ public CMAESOptimizer(int lambda, double[] inputSigma,
+ int maxIterations, double stopFitness,
+ boolean isActiveCMA, int diagonalOnly, int checkFeasableCount,
+ RandomGenerator random, boolean generateStatistics,
+ ConvergenceChecker<PointValuePair> checker) {
+ super(checker);
+ this.lambda = lambda;
+ this.inputSigma = inputSigma == null ? null : (double[]) inputSigma.clone();
+ this.maxIterations = maxIterations;
+ this.stopFitness = stopFitness;
+ this.isActiveCMA = isActiveCMA;
+ this.diagonalOnly = diagonalOnly;
+ this.checkFeasableCount = checkFeasableCount;
+ this.random = random;
+ this.generateStatistics = generateStatistics;
+ }
+
+ /**
+ * @param maxIterations Maximal number of iterations.
+ * @param stopFitness Whether to stop if objective function value is smaller than
+ * {@code stopFitness}.
+ * @param isActiveCMA Chooses the covariance matrix update method.
+ * @param diagonalOnly Number of initial iterations, where the covariance matrix
+ * remains diagonal.
+ * @param checkFeasableCount Determines how often new random objective variables are
+ * generated in case they are out of bounds.
+ * @param random Random generator.
+ * @param generateStatistics Whether statistic data is collected.
+ * @param checker Convergence checker.
+ *
+ * @since 3.1
+ */
+ public CMAESOptimizer(int maxIterations,
+ double stopFitness,
+ boolean isActiveCMA,
+ int diagonalOnly,
+ int checkFeasableCount,
+ RandomGenerator random,
+ boolean generateStatistics,
+ ConvergenceChecker<PointValuePair> checker) {
+ super(checker);
+ this.maxIterations = maxIterations;
+ this.stopFitness = stopFitness;
+ this.isActiveCMA = isActiveCMA;
+ this.diagonalOnly = diagonalOnly;
+ this.checkFeasableCount = checkFeasableCount;
+ this.random = random;
+ this.generateStatistics = generateStatistics;
+ }
+
+ /**
+ * @return History of sigma values.
+ */
+ public List<Double> getStatisticsSigmaHistory() {
+ return statisticsSigmaHistory;
+ }
+
+ /**
+ * @return History of mean matrix.
+ */
+ public List<RealMatrix> getStatisticsMeanHistory() {
+ return statisticsMeanHistory;
+ }
+
+ /**
+ * @return History of fitness values.
+ */
+ public List<Double> getStatisticsFitnessHistory() {
+ return statisticsFitnessHistory;
+ }
+
+ /**
+ * @return History of D matrix.
+ */
+ public List<RealMatrix> getStatisticsDHistory() {
+ return statisticsDHistory;
+ }
+
+ /**
+ * Input sigma values.
+ * They define the initial coordinate-wise standard deviations for
+ * sampling new search points around the initial guess.
+ * It is suggested to set them to the estimated distance from the
+ * initial to the desired optimum.
+ * Small values induce the search to be more local (and very small
+ * values are more likely to find a local optimum close to the initial
+ * guess).
+ * Too small values might however lead to early termination.
+ * @since 3.1
+ */
+ public static class Sigma implements OptimizationData {
+ /** Sigma values. */
+ private final double[] sigma;
+
+ /**
+ * @param s Sigma values.
+ * @throws NotPositiveException if any of the array entries is smaller
+ * than zero.
+ */
+ public Sigma(double[] s)
+ throws NotPositiveException {
+ for (int i = 0; i < s.length; i++) {
+ if (s[i] < 0) {
+ throw new NotPositiveException(s[i]);
+ }
+ }
+
+ sigma = s.clone();
+ }
+
+ /**
+ * @return the sigma values.
+ */
+ public double[] getSigma() {
+ return sigma.clone();
+ }
+ }
+
+ /**
+ * Population size.
+ * The number of offspring is the primary strategy parameter.
+ * In the absence of better clues, a good default could be an
+ * integer close to {@code 4 + 3 ln(n)}, where {@code n} is the
+ * number of optimized parameters.
+ * Increasing the population size improves global search properties
+ * at the expense of speed (which in general decreases at most
+ * linearly with increasing population size).
+ * @since 3.1
+ */
+ public static class PopulationSize implements OptimizationData {
+ /** Population size. */
+ private final int lambda;
+
+ /**
+ * @param size Population size.
+ * @throws NotStrictlyPositiveException if {@code size <= 0}.
+ */
+ public PopulationSize(int size)
+ throws NotStrictlyPositiveException {
+ if (size <= 0) {
+ throw new NotStrictlyPositiveException(size);
+ }
+ lambda = size;
+ }
+
+ /**
+ * @return the population size.
+ */
+ public int getPopulationSize() {
+ return lambda;
+ }
+ }
+
+ /**
+ * Optimize an objective function.
+ *
+ * @param maxEval Allowed number of evaluations of the objective function.
+ * @param f Objective function.
+ * @param goalType Optimization type.
+ * @param optData Optimization data. The following data will be looked for:
+ * <ul>
+ * <li>{@link org.apache.commons.math3.optimization.InitialGuess InitialGuess}</li>
+ * <li>{@link Sigma}</li>
+ * <li>{@link PopulationSize}</li>
+ * </ul>
+ * @return the point/value pair giving the optimal value for objective
+ * function.
+ */
+ @Override
+ protected PointValuePair optimizeInternal(int maxEval, MultivariateFunction f,
+ GoalType goalType,
+ OptimizationData... optData) {
+ // Scan "optData" for the input specific to this optimizer.
+ parseOptimizationData(optData);
+
+ // The parent's method will retrieve the common parameters from
+ // "optData" and call "doOptimize".
+ return super.optimizeInternal(maxEval, f, goalType, optData);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ protected PointValuePair doOptimize() {
+ checkParameters();
+ // -------------------- Initialization --------------------------------
+ isMinimize = getGoalType().equals(GoalType.MINIMIZE);
+ final FitnessFunction fitfun = new FitnessFunction();
+ final double[] guess = getStartPoint();
+ // number of objective variables/problem dimension
+ dimension = guess.length;
+ initializeCMA(guess);
+ iterations = 0;
+ double bestValue = fitfun.value(guess);
+ push(fitnessHistory, bestValue);
+ PointValuePair optimum = new PointValuePair(getStartPoint(),
+ isMinimize ? bestValue : -bestValue);
+ PointValuePair lastResult = null;
+
+ // -------------------- Generation Loop --------------------------------
+
+ generationLoop:
+ for (iterations = 1; iterations <= maxIterations; iterations++) {
+ // Generate and evaluate lambda offspring
+ final RealMatrix arz = randn1(dimension, lambda);
+ final RealMatrix arx = zeros(dimension, lambda);
+ final double[] fitness = new double[lambda];
+ // generate random offspring
+ for (int k = 0; k < lambda; k++) {
+ RealMatrix arxk = null;
+ for (int i = 0; i < checkFeasableCount + 1; i++) {
+ if (diagonalOnly <= 0) {
+ arxk = xmean.add(BD.multiply(arz.getColumnMatrix(k))
+ .scalarMultiply(sigma)); // m + sig * Normal(0,C)
+ } else {
+ arxk = xmean.add(times(diagD,arz.getColumnMatrix(k))
+ .scalarMultiply(sigma));
+ }
+ if (i >= checkFeasableCount ||
+ fitfun.isFeasible(arxk.getColumn(0))) {
+ break;
+ }
+ // regenerate random arguments for row
+ arz.setColumn(k, randn(dimension));
+ }
+ copyColumn(arxk, 0, arx, k);
+ try {
+ fitness[k] = fitfun.value(arx.getColumn(k)); // compute fitness
+ } catch (TooManyEvaluationsException e) {
+ break generationLoop;
+ }
+ }
+ // Sort by fitness and compute weighted mean into xmean
+ final int[] arindex = sortedIndices(fitness);
+ // Calculate new xmean, this is selection and recombination
+ final RealMatrix xold = xmean; // for speed up of Eq. (2) and (3)
+ final RealMatrix bestArx = selectColumns(arx, MathArrays.copyOf(arindex, mu));
+ xmean = bestArx.multiply(weights);
+ final RealMatrix bestArz = selectColumns(arz, MathArrays.copyOf(arindex, mu));
+ final RealMatrix zmean = bestArz.multiply(weights);
+ final boolean hsig = updateEvolutionPaths(zmean, xold);
+ if (diagonalOnly <= 0) {
+ updateCovariance(hsig, bestArx, arz, arindex, xold);
+ } else {
+ updateCovarianceDiagonalOnly(hsig, bestArz);
+ }
+ // Adapt step size sigma - Eq. (5)
+ sigma *= FastMath.exp(FastMath.min(1, (normps/chiN - 1) * cs / damps));
+ final double bestFitness = fitness[arindex[0]];
+ final double worstFitness = fitness[arindex[arindex.length - 1]];
+ if (bestValue > bestFitness) {
+ bestValue = bestFitness;
+ lastResult = optimum;
+ optimum = new PointValuePair(fitfun.repair(bestArx.getColumn(0)),
+ isMinimize ? bestFitness : -bestFitness);
+ if (getConvergenceChecker() != null && lastResult != null &&
+ getConvergenceChecker().converged(iterations, optimum, lastResult)) {
+ break generationLoop;
+ }
+ }
+ // handle termination criteria
+ // Break, if fitness is good enough
+ if (stopFitness != 0 && bestFitness < (isMinimize ? stopFitness : -stopFitness)) {
+ break generationLoop;
+ }
+ final double[] sqrtDiagC = sqrt(diagC).getColumn(0);
+ final double[] pcCol = pc.getColumn(0);
+ for (int i = 0; i < dimension; i++) {
+ if (sigma * FastMath.max(FastMath.abs(pcCol[i]), sqrtDiagC[i]) > stopTolX) {
+ break;
+ }
+ if (i >= dimension - 1) {
+ break generationLoop;
+ }
+ }
+ for (int i = 0; i < dimension; i++) {
+ if (sigma * sqrtDiagC[i] > stopTolUpX) {
+ break generationLoop;
+ }
+ }
+ final double historyBest = min(fitnessHistory);
+ final double historyWorst = max(fitnessHistory);
+ if (iterations > 2 &&
+ FastMath.max(historyWorst, worstFitness) -
+ FastMath.min(historyBest, bestFitness) < stopTolFun) {
+ break generationLoop;
+ }
+ if (iterations > fitnessHistory.length &&
+ historyWorst-historyBest < stopTolHistFun) {
+ break generationLoop;
+ }
+ // condition number of the covariance matrix exceeds 1e14
+ if (max(diagD)/min(diagD) > 1e7) {
+ break generationLoop;
+ }
+ // user defined termination
+ if (getConvergenceChecker() != null) {
+ final PointValuePair current
+ = new PointValuePair(bestArx.getColumn(0),
+ isMinimize ? bestFitness : -bestFitness);
+ if (lastResult != null &&
+ getConvergenceChecker().converged(iterations, current, lastResult)) {
+ break generationLoop;
+ }
+ lastResult = current;
+ }
+ // Adjust step size in case of equal function values (flat fitness)
+ if (bestValue == fitness[arindex[(int)(0.1+lambda/4.)]]) {
+ sigma *= FastMath.exp(0.2 + cs / damps);
+ }
+ if (iterations > 2 && FastMath.max(historyWorst, bestFitness) -
+ FastMath.min(historyBest, bestFitness) == 0) {
+ sigma *= FastMath.exp(0.2 + cs / damps);
+ }
+ // store best in history
+ push(fitnessHistory,bestFitness);
+ fitfun.setValueRange(worstFitness-bestFitness);
+ if (generateStatistics) {
+ statisticsSigmaHistory.add(sigma);
+ statisticsFitnessHistory.add(bestFitness);
+ statisticsMeanHistory.add(xmean.transpose());
+ statisticsDHistory.add(diagD.transpose().scalarMultiply(1E5));
+ }
+ }
+ return optimum;
+ }
+
+ /**
+ * Scans the list of (required and optional) optimization data that
+ * characterize the problem.
+ *
+ * @param optData Optimization data. The following data will be looked for:
+ * <ul>
+ * <li>{@link Sigma}</li>
+ * <li>{@link PopulationSize}</li>
+ * </ul>
+ */
+ private void parseOptimizationData(OptimizationData... optData) {
+ // The existing values (as set by the previous call) are reused if
+ // not provided in the argument list.
+ for (OptimizationData data : optData) {
+ if (data instanceof Sigma) {
+ inputSigma = ((Sigma) data).getSigma();
+ continue;
+ }
+ if (data instanceof PopulationSize) {
+ lambda = ((PopulationSize) data).getPopulationSize();
+ continue;
+ }
+ }
+ }
+
+ /**
+ * Checks dimensions and values of boundaries and inputSigma if defined.
+ */
+ private void checkParameters() {
+ final double[] init = getStartPoint();
+ final double[] lB = getLowerBound();
+ final double[] uB = getUpperBound();
+
+ if (inputSigma != null) {
+ if (inputSigma.length != init.length) {
+ throw new DimensionMismatchException(inputSigma.length, init.length);
+ }
+ for (int i = 0; i < init.length; i++) {
+ if (inputSigma[i] < 0) {
+ // XXX Remove this block in 4.0 (check performed in "Sigma" class).
+ throw new NotPositiveException(inputSigma[i]);
+ }
+ if (inputSigma[i] > uB[i] - lB[i]) {
+ throw new OutOfRangeException(inputSigma[i], 0, uB[i] - lB[i]);
+ }
+ }
+ }
+ }
+
+ /**
+ * Initialization of the dynamic search parameters
+ *
+ * @param guess Initial guess for the arguments of the fitness function.
+ */
+ private void initializeCMA(double[] guess) {
+ if (lambda <= 0) {
+ // XXX Line below to replace the current one in 4.0 (MATH-879).
+ // throw new NotStrictlyPositiveException(lambda);
+ lambda = 4 + (int) (3 * FastMath.log(dimension));
+ }
+ // initialize sigma
+ final double[][] sigmaArray = new double[guess.length][1];
+ for (int i = 0; i < guess.length; i++) {
+ // XXX Line below to replace the current one in 4.0 (MATH-868).
+ // sigmaArray[i][0] = inputSigma[i];
+ sigmaArray[i][0] = inputSigma == null ? 0.3 : inputSigma[i];
+ }
+ final RealMatrix insigma = new Array2DRowRealMatrix(sigmaArray, false);
+ sigma = max(insigma); // overall standard deviation
+
+ // initialize termination criteria
+ stopTolUpX = 1e3 * max(insigma);
+ stopTolX = 1e-11 * max(insigma);
+ stopTolFun = 1e-12;
+ stopTolHistFun = 1e-13;
+
+ // initialize selection strategy parameters
+ mu = lambda / 2; // number of parents/points for recombination
+ logMu2 = FastMath.log(mu + 0.5);
+ weights = log(sequence(1, mu, 1)).scalarMultiply(-1).scalarAdd(logMu2);
+ double sumw = 0;
+ double sumwq = 0;
+ for (int i = 0; i < mu; i++) {
+ double w = weights.getEntry(i, 0);
+ sumw += w;
+ sumwq += w * w;
+ }
+ weights = weights.scalarMultiply(1 / sumw);
+ mueff = sumw * sumw / sumwq; // variance-effectiveness of sum w_i x_i
+
+ // initialize dynamic strategy parameters and constants
+ cc = (4 + mueff / dimension) /
+ (dimension + 4 + 2 * mueff / dimension);
+ cs = (mueff + 2) / (dimension + mueff + 3.);
+ damps = (1 + 2 * FastMath.max(0, FastMath.sqrt((mueff - 1) /
+ (dimension + 1)) - 1)) *
+ FastMath.max(0.3,
+ 1 - dimension / (1e-6 + maxIterations)) + cs; // minor increment
+ ccov1 = 2 / ((dimension + 1.3) * (dimension + 1.3) + mueff);
+ ccovmu = FastMath.min(1 - ccov1, 2 * (mueff - 2 + 1 / mueff) /
+ ((dimension + 2) * (dimension + 2) + mueff));
+ ccov1Sep = FastMath.min(1, ccov1 * (dimension + 1.5) / 3);
+ ccovmuSep = FastMath.min(1 - ccov1, ccovmu * (dimension + 1.5) / 3);
+ chiN = FastMath.sqrt(dimension) *
+ (1 - 1 / ((double) 4 * dimension) + 1 / ((double) 21 * dimension * dimension));
+ // intialize CMA internal values - updated each generation
+ xmean = MatrixUtils.createColumnRealMatrix(guess); // objective variables
+ diagD = insigma.scalarMultiply(1 / sigma);
+ diagC = square(diagD);
+ pc = zeros(dimension, 1); // evolution paths for C and sigma
+ ps = zeros(dimension, 1); // B defines the coordinate system
+ normps = ps.getFrobeniusNorm();
+
+ B = eye(dimension, dimension);
+ D = ones(dimension, 1); // diagonal D defines the scaling
+ BD = times(B, repmat(diagD.transpose(), dimension, 1));
+ C = B.multiply(diag(square(D)).multiply(B.transpose())); // covariance
+ historySize = 10 + (int) (3 * 10 * dimension / (double) lambda);
+ fitnessHistory = new double[historySize]; // history of fitness values
+ for (int i = 0; i < historySize; i++) {
+ fitnessHistory[i] = Double.MAX_VALUE;
+ }
+ }
+
+ /**
+ * Update of the evolution paths ps and pc.
+ *
+ * @param zmean Weighted row matrix of the gaussian random numbers generating
+ * the current offspring.
+ * @param xold xmean matrix of the previous generation.
+ * @return hsig flag indicating a small correction.
+ */
+ private boolean updateEvolutionPaths(RealMatrix zmean, RealMatrix xold) {
+ ps = ps.scalarMultiply(1 - cs).add(
+ B.multiply(zmean).scalarMultiply(FastMath.sqrt(cs * (2 - cs) * mueff)));
+ normps = ps.getFrobeniusNorm();
+ final boolean hsig = normps /
+ FastMath.sqrt(1 - FastMath.pow(1 - cs, 2 * iterations)) /
+ chiN < 1.4 + 2 / ((double) dimension + 1);
+ pc = pc.scalarMultiply(1 - cc);
+ if (hsig) {
+ pc = pc.add(xmean.subtract(xold).scalarMultiply(FastMath.sqrt(cc * (2 - cc) * mueff) / sigma));
+ }
+ return hsig;
+ }
+
+ /**
+ * Update of the covariance matrix C for diagonalOnly > 0
+ *
+ * @param hsig Flag indicating a small correction.
+ * @param bestArz Fitness-sorted matrix of the gaussian random values of the
+ * current offspring.
+ */
+ private void updateCovarianceDiagonalOnly(boolean hsig,
+ final RealMatrix bestArz) {
+ // minor correction if hsig==false
+ double oldFac = hsig ? 0 : ccov1Sep * cc * (2 - cc);
+ oldFac += 1 - ccov1Sep - ccovmuSep;
+ diagC = diagC.scalarMultiply(oldFac) // regard old matrix
+ .add(square(pc).scalarMultiply(ccov1Sep)) // plus rank one update
+ .add((times(diagC, square(bestArz).multiply(weights))) // plus rank mu update
+ .scalarMultiply(ccovmuSep));
+ diagD = sqrt(diagC); // replaces eig(C)
+ if (diagonalOnly > 1 &&
+ iterations > diagonalOnly) {
+ // full covariance matrix from now on
+ diagonalOnly = 0;
+ B = eye(dimension, dimension);
+ BD = diag(diagD);
+ C = diag(diagC);
+ }
+ }
+
+ /**
+ * Update of the covariance matrix C.
+ *
+ * @param hsig Flag indicating a small correction.
+ * @param bestArx Fitness-sorted matrix of the argument vectors producing the
+ * current offspring.
+ * @param arz Unsorted matrix containing the gaussian random values of the
+ * current offspring.
+ * @param arindex Indices indicating the fitness-order of the current offspring.
+ * @param xold xmean matrix of the previous generation.
+ */
+ private void updateCovariance(boolean hsig, final RealMatrix bestArx,
+ final RealMatrix arz, final int[] arindex,
+ final RealMatrix xold) {
+ double negccov = 0;
+ if (ccov1 + ccovmu > 0) {
+ final RealMatrix arpos = bestArx.subtract(repmat(xold, 1, mu))
+ .scalarMultiply(1 / sigma); // mu difference vectors
+ final RealMatrix roneu = pc.multiply(pc.transpose())
+ .scalarMultiply(ccov1); // rank one update
+ // minor correction if hsig==false
+ double oldFac = hsig ? 0 : ccov1 * cc * (2 - cc);
+ oldFac += 1 - ccov1 - ccovmu;
+ if (isActiveCMA) {
+ // Adapt covariance matrix C active CMA
+ negccov = (1 - ccovmu) * 0.25 * mueff / (FastMath.pow(dimension + 2, 1.5) + 2 * mueff);
+ // keep at least 0.66 in all directions, small popsize are most
+ // critical
+ final double negminresidualvariance = 0.66;
+ // where to make up for the variance loss
+ final double negalphaold = 0.5;
+ // prepare vectors, compute negative updating matrix Cneg
+ final int[] arReverseIndex = reverse(arindex);
+ RealMatrix arzneg = selectColumns(arz, MathArrays.copyOf(arReverseIndex, mu));
+ RealMatrix arnorms = sqrt(sumRows(square(arzneg)));
+ final int[] idxnorms = sortedIndices(arnorms.getRow(0));
+ final RealMatrix arnormsSorted = selectColumns(arnorms, idxnorms);
+ final int[] idxReverse = reverse(idxnorms);
+ final RealMatrix arnormsReverse = selectColumns(arnorms, idxReverse);
+ arnorms = divide(arnormsReverse, arnormsSorted);
+ final int[] idxInv = inverse(idxnorms);
+ final RealMatrix arnormsInv = selectColumns(arnorms, idxInv);
+ // check and set learning rate negccov
+ final double negcovMax = (1 - negminresidualvariance) /
+ square(arnormsInv).multiply(weights).getEntry(0, 0);
+ if (negccov > negcovMax) {
+ negccov = negcovMax;
+ }
+ arzneg = times(arzneg, repmat(arnormsInv, dimension, 1));
+ final RealMatrix artmp = BD.multiply(arzneg);
+ final RealMatrix Cneg = artmp.multiply(diag(weights)).multiply(artmp.transpose());
+ oldFac += negalphaold * negccov;
+ C = C.scalarMultiply(oldFac)
+ .add(roneu) // regard old matrix
+ .add(arpos.scalarMultiply( // plus rank one update
+ ccovmu + (1 - negalphaold) * negccov) // plus rank mu update
+ .multiply(times(repmat(weights, 1, dimension),
+ arpos.transpose())))
+ .subtract(Cneg.scalarMultiply(negccov));
+ } else {
+ // Adapt covariance matrix C - nonactive
+ C = C.scalarMultiply(oldFac) // regard old matrix
+ .add(roneu) // plus rank one update
+ .add(arpos.scalarMultiply(ccovmu) // plus rank mu update
+ .multiply(times(repmat(weights, 1, dimension),
+ arpos.transpose())));
+ }
+ }
+ updateBD(negccov);
+ }
+
+ /**
+ * Update B and D from C.
+ *
+ * @param negccov Negative covariance factor.
+ */
+ private void updateBD(double negccov) {
+ if (ccov1 + ccovmu + negccov > 0 &&
+ (iterations % 1. / (ccov1 + ccovmu + negccov) / dimension / 10.) < 1) {
+ // to achieve O(N^2)
+ C = triu(C, 0).add(triu(C, 1).transpose());
+ // enforce symmetry to prevent complex numbers
+ final EigenDecomposition eig = new EigenDecomposition(C);
+ B = eig.getV(); // eigen decomposition, B==normalized eigenvectors
+ D = eig.getD();
+ diagD = diag(D);
+ if (min(diagD) <= 0) {
+ for (int i = 0; i < dimension; i++) {
+ if (diagD.getEntry(i, 0) < 0) {
+ diagD.setEntry(i, 0, 0);
+ }
+ }
+ final double tfac = max(diagD) / 1e14;
+ C = C.add(eye(dimension, dimension).scalarMultiply(tfac));
+ diagD = diagD.add(ones(dimension, 1).scalarMultiply(tfac));
+ }
+ if (max(diagD) > 1e14 * min(diagD)) {
+ final double tfac = max(diagD) / 1e14 - min(diagD);
+ C = C.add(eye(dimension, dimension).scalarMultiply(tfac));
+ diagD = diagD.add(ones(dimension, 1).scalarMultiply(tfac));
+ }
+ diagC = diag(C);
+ diagD = sqrt(diagD); // D contains standard deviations now
+ BD = times(B, repmat(diagD.transpose(), dimension, 1)); // O(n^2)
+ }
+ }
+
+ /**
+ * Pushes the current best fitness value in a history queue.
+ *
+ * @param vals History queue.
+ * @param val Current best fitness value.
+ */
+ private static void push(double[] vals, double val) {
+ for (int i = vals.length-1; i > 0; i--) {
+ vals[i] = vals[i-1];
+ }
+ vals[0] = val;
+ }
+
+ /**
+ * Sorts fitness values.
+ *
+ * @param doubles Array of values to be sorted.
+ * @return a sorted array of indices pointing into doubles.
+ */
+ private int[] sortedIndices(final double[] doubles) {
+ final DoubleIndex[] dis = new DoubleIndex[doubles.length];
+ for (int i = 0; i < doubles.length; i++) {
+ dis[i] = new DoubleIndex(doubles[i], i);
+ }
+ Arrays.sort(dis);
+ final int[] indices = new int[doubles.length];
+ for (int i = 0; i < doubles.length; i++) {
+ indices[i] = dis[i].index;
+ }
+ return indices;
+ }
+
+ /**
+ * Used to sort fitness values. Sorting is always in lower value first
+ * order.
+ */
+ private static class DoubleIndex implements Comparable<DoubleIndex> {
+ /** Value to compare. */
+ private final double value;
+ /** Index into sorted array. */
+ private final int index;
+
+ /**
+ * @param value Value to compare.
+ * @param index Index into sorted array.
+ */
+ DoubleIndex(double value, int index) {
+ this.value = value;
+ this.index = index;
+ }
+
+ /** {@inheritDoc} */
+ public int compareTo(DoubleIndex o) {
+ return Double.compare(value, o.value);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public boolean equals(Object other) {
+
+ if (this == other) {
+ return true;
+ }
+
+ if (other instanceof DoubleIndex) {
+ return Double.compare(value, ((DoubleIndex) other).value) == 0;
+ }
+
+ return false;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public int hashCode() {
+ long bits = Double.doubleToLongBits(value);
+ return (int) ((1438542 ^ (bits >>> 32) ^ bits) & 0xffffffff);
+ }
+ }
+
+ /**
+ * Normalizes fitness values to the range [0,1]. Adds a penalty to the
+ * fitness value if out of range. The penalty is adjusted by calling
+ * setValueRange().
+ */
+ private class FitnessFunction {
+ /** Determines the penalty for boundary violations */
+ private double valueRange;
+ /**
+ * Flag indicating whether the objective variables are forced into their
+ * bounds if defined
+ */
+ private final boolean isRepairMode;
+
+ /** Simple constructor.
+ */
+ FitnessFunction() {
+ valueRange = 1;
+ isRepairMode = true;
+ }
+
+ /**
+ * @param point Normalized objective variables.
+ * @return the objective value + penalty for violated bounds.
+ */
+ public double value(final double[] point) {
+ double value;
+ if (isRepairMode) {
+ double[] repaired = repair(point);
+ value = CMAESOptimizer.this.computeObjectiveValue(repaired) +
+ penalty(point, repaired);
+ } else {
+ value = CMAESOptimizer.this.computeObjectiveValue(point);
+ }
+ return isMinimize ? value : -value;
+ }
+
+ /**
+ * @param x Normalized objective variables.
+ * @return {@code true} if in bounds.
+ */
+ public boolean isFeasible(final double[] x) {
+ final double[] lB = CMAESOptimizer.this.getLowerBound();
+ final double[] uB = CMAESOptimizer.this.getUpperBound();
+
+ for (int i = 0; i < x.length; i++) {
+ if (x[i] < lB[i]) {
+ return false;
+ }
+ if (x[i] > uB[i]) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ /**
+ * @param valueRange Adjusts the penalty computation.
+ */
+ public void setValueRange(double valueRange) {
+ this.valueRange = valueRange;
+ }
+
+ /**
+ * @param x Normalized objective variables.
+ * @return the repaired (i.e. all in bounds) objective variables.
+ */
+ private double[] repair(final double[] x) {
+ final double[] lB = CMAESOptimizer.this.getLowerBound();
+ final double[] uB = CMAESOptimizer.this.getUpperBound();
+
+ final double[] repaired = new double[x.length];
+ for (int i = 0; i < x.length; i++) {
+ if (x[i] < lB[i]) {
+ repaired[i] = lB[i];
+ } else if (x[i] > uB[i]) {
+ repaired[i] = uB[i];
+ } else {
+ repaired[i] = x[i];
+ }
+ }
+ return repaired;
+ }
+
+ /**
+ * @param x Normalized objective variables.
+ * @param repaired Repaired objective variables.
+ * @return Penalty value according to the violation of the bounds.
+ */
+ private double penalty(final double[] x, final double[] repaired) {
+ double penalty = 0;
+ for (int i = 0; i < x.length; i++) {
+ double diff = FastMath.abs(x[i] - repaired[i]);
+ penalty += diff * valueRange;
+ }
+ return isMinimize ? penalty : -penalty;
+ }
+ }
+
+ // -----Matrix utility functions similar to the Matlab build in functions------
+
+ /**
+ * @param m Input matrix
+ * @return Matrix representing the element-wise logarithm of m.
+ */
+ private static RealMatrix log(final RealMatrix m) {
+ final double[][] d = new double[m.getRowDimension()][m.getColumnDimension()];
+ for (int r = 0; r < m.getRowDimension(); r++) {
+ for (int c = 0; c < m.getColumnDimension(); c++) {
+ d[r][c] = FastMath.log(m.getEntry(r, c));
+ }
+ }
+ return new Array2DRowRealMatrix(d, false);
+ }
+
+ /**
+ * @param m Input matrix.
+ * @return Matrix representing the element-wise square root of m.
+ */
+ private static RealMatrix sqrt(final RealMatrix m) {
+ final double[][] d = new double[m.getRowDimension()][m.getColumnDimension()];
+ for (int r = 0; r < m.getRowDimension(); r++) {
+ for (int c = 0; c < m.getColumnDimension(); c++) {
+ d[r][c] = FastMath.sqrt(m.getEntry(r, c));
+ }
+ }
+ return new Array2DRowRealMatrix(d, false);
+ }
+
+ /**
+ * @param m Input matrix.
+ * @return Matrix representing the element-wise square of m.
+ */
+ private static RealMatrix square(final RealMatrix m) {
+ final double[][] d = new double[m.getRowDimension()][m.getColumnDimension()];
+ for (int r = 0; r < m.getRowDimension(); r++) {
+ for (int c = 0; c < m.getColumnDimension(); c++) {
+ double e = m.getEntry(r, c);
+ d[r][c] = e * e;
+ }
+ }
+ return new Array2DRowRealMatrix(d, false);
+ }
+
+ /**
+ * @param m Input matrix 1.
+ * @param n Input matrix 2.
+ * @return the matrix where the elements of m and n are element-wise multiplied.
+ */
+ private static RealMatrix times(final RealMatrix m, final RealMatrix n) {
+ final double[][] d = new double[m.getRowDimension()][m.getColumnDimension()];
+ for (int r = 0; r < m.getRowDimension(); r++) {
+ for (int c = 0; c < m.getColumnDimension(); c++) {
+ d[r][c] = m.getEntry(r, c) * n.getEntry(r, c);
+ }
+ }
+ return new Array2DRowRealMatrix(d, false);
+ }
+
+ /**
+ * @param m Input matrix 1.
+ * @param n Input matrix 2.
+ * @return Matrix where the elements of m and n are element-wise divided.
+ */
+ private static RealMatrix divide(final RealMatrix m, final RealMatrix n) {
+ final double[][] d = new double[m.getRowDimension()][m.getColumnDimension()];
+ for (int r = 0; r < m.getRowDimension(); r++) {
+ for (int c = 0; c < m.getColumnDimension(); c++) {
+ d[r][c] = m.getEntry(r, c) / n.getEntry(r, c);
+ }
+ }
+ return new Array2DRowRealMatrix(d, false);
+ }
+
+ /**
+ * @param m Input matrix.
+ * @param cols Columns to select.
+ * @return Matrix representing the selected columns.
+ */
+ private static RealMatrix selectColumns(final RealMatrix m, final int[] cols) {
+ final double[][] d = new double[m.getRowDimension()][cols.length];
+ for (int r = 0; r < m.getRowDimension(); r++) {
+ for (int c = 0; c < cols.length; c++) {
+ d[r][c] = m.getEntry(r, cols[c]);
+ }
+ }
+ return new Array2DRowRealMatrix(d, false);
+ }
+
+ /**
+ * @param m Input matrix.
+ * @param k Diagonal position.
+ * @return Upper triangular part of matrix.
+ */
+ private static RealMatrix triu(final RealMatrix m, int k) {
+ final double[][] d = new double[m.getRowDimension()][m.getColumnDimension()];
+ for (int r = 0; r < m.getRowDimension(); r++) {
+ for (int c = 0; c < m.getColumnDimension(); c++) {
+ d[r][c] = r <= c - k ? m.getEntry(r, c) : 0;
+ }
+ }
+ return new Array2DRowRealMatrix(d, false);
+ }
+
+ /**
+ * @param m Input matrix.
+ * @return Row matrix representing the sums of the rows.
+ */
+ private static RealMatrix sumRows(final RealMatrix m) {
+ final double[][] d = new double[1][m.getColumnDimension()];
+ for (int c = 0; c < m.getColumnDimension(); c++) {
+ double sum = 0;
+ for (int r = 0; r < m.getRowDimension(); r++) {
+ sum += m.getEntry(r, c);
+ }
+ d[0][c] = sum;
+ }
+ return new Array2DRowRealMatrix(d, false);
+ }
+
+ /**
+ * @param m Input matrix.
+ * @return the diagonal n-by-n matrix if m is a column matrix or the column
+ * matrix representing the diagonal if m is a n-by-n matrix.
+ */
+ private static RealMatrix diag(final RealMatrix m) {
+ if (m.getColumnDimension() == 1) {
+ final double[][] d = new double[m.getRowDimension()][m.getRowDimension()];
+ for (int i = 0; i < m.getRowDimension(); i++) {
+ d[i][i] = m.getEntry(i, 0);
+ }
+ return new Array2DRowRealMatrix(d, false);
+ } else {
+ final double[][] d = new double[m.getRowDimension()][1];
+ for (int i = 0; i < m.getColumnDimension(); i++) {
+ d[i][0] = m.getEntry(i, i);
+ }
+ return new Array2DRowRealMatrix(d, false);
+ }
+ }
+
+ /**
+ * Copies a column from m1 to m2.
+ *
+ * @param m1 Source matrix.
+ * @param col1 Source column.
+ * @param m2 Target matrix.
+ * @param col2 Target column.
+ */
+ private static void copyColumn(final RealMatrix m1, int col1,
+ RealMatrix m2, int col2) {
+ for (int i = 0; i < m1.getRowDimension(); i++) {
+ m2.setEntry(i, col2, m1.getEntry(i, col1));
+ }
+ }
+
+ /**
+ * @param n Number of rows.
+ * @param m Number of columns.
+ * @return n-by-m matrix filled with 1.
+ */
+ private static RealMatrix ones(int n, int m) {
+ final double[][] d = new double[n][m];
+ for (int r = 0; r < n; r++) {
+ Arrays.fill(d[r], 1);
+ }
+ return new Array2DRowRealMatrix(d, false);
+ }
+
+ /**
+ * @param n Number of rows.
+ * @param m Number of columns.
+ * @return n-by-m matrix of 0 values out of diagonal, and 1 values on
+ * the diagonal.
+ */
+ private static RealMatrix eye(int n, int m) {
+ final double[][] d = new double[n][m];
+ for (int r = 0; r < n; r++) {
+ if (r < m) {
+ d[r][r] = 1;
+ }
+ }
+ return new Array2DRowRealMatrix(d, false);
+ }
+
+ /**
+ * @param n Number of rows.
+ * @param m Number of columns.
+ * @return n-by-m matrix of zero values.
+ */
+ private static RealMatrix zeros(int n, int m) {
+ return new Array2DRowRealMatrix(n, m);
+ }
+
+ /**
+ * @param mat Input matrix.
+ * @param n Number of row replicates.
+ * @param m Number of column replicates.
+ * @return a matrix which replicates the input matrix in both directions.
+ */
+ private static RealMatrix repmat(final RealMatrix mat, int n, int m) {
+ final int rd = mat.getRowDimension();
+ final int cd = mat.getColumnDimension();
+ final double[][] d = new double[n * rd][m * cd];
+ for (int r = 0; r < n * rd; r++) {
+ for (int c = 0; c < m * cd; c++) {
+ d[r][c] = mat.getEntry(r % rd, c % cd);
+ }
+ }
+ return new Array2DRowRealMatrix(d, false);
+ }
+
+ /**
+ * @param start Start value.
+ * @param end End value.
+ * @param step Step size.
+ * @return a sequence as column matrix.
+ */
+ private static RealMatrix sequence(double start, double end, double step) {
+ final int size = (int) ((end - start) / step + 1);
+ final double[][] d = new double[size][1];
+ double value = start;
+ for (int r = 0; r < size; r++) {
+ d[r][0] = value;
+ value += step;
+ }
+ return new Array2DRowRealMatrix(d, false);
+ }
+
+ /**
+ * @param m Input matrix.
+ * @return the maximum of the matrix element values.
+ */
+ private static double max(final RealMatrix m) {
+ double max = -Double.MAX_VALUE;
+ for (int r = 0; r < m.getRowDimension(); r++) {
+ for (int c = 0; c < m.getColumnDimension(); c++) {
+ double e = m.getEntry(r, c);
+ if (max < e) {
+ max = e;
+ }
+ }
+ }
+ return max;
+ }
+
+ /**
+ * @param m Input matrix.
+ * @return the minimum of the matrix element values.
+ */
+ private static double min(final RealMatrix m) {
+ double min = Double.MAX_VALUE;
+ for (int r = 0; r < m.getRowDimension(); r++) {
+ for (int c = 0; c < m.getColumnDimension(); c++) {
+ double e = m.getEntry(r, c);
+ if (min > e) {
+ min = e;
+ }
+ }
+ }
+ return min;
+ }
+
+ /**
+ * @param m Input array.
+ * @return the maximum of the array values.
+ */
+ private static double max(final double[] m) {
+ double max = -Double.MAX_VALUE;
+ for (int r = 0; r < m.length; r++) {
+ if (max < m[r]) {
+ max = m[r];
+ }
+ }
+ return max;
+ }
+
+ /**
+ * @param m Input array.
+ * @return the minimum of the array values.
+ */
+ private static double min(final double[] m) {
+ double min = Double.MAX_VALUE;
+ for (int r = 0; r < m.length; r++) {
+ if (min > m[r]) {
+ min = m[r];
+ }
+ }
+ return min;
+ }
+
+ /**
+ * @param indices Input index array.
+ * @return the inverse of the mapping defined by indices.
+ */
+ private static int[] inverse(final int[] indices) {
+ final int[] inverse = new int[indices.length];
+ for (int i = 0; i < indices.length; i++) {
+ inverse[indices[i]] = i;
+ }
+ return inverse;
+ }
+
+ /**
+ * @param indices Input index array.
+ * @return the indices in inverse order (last is first).
+ */
+ private static int[] reverse(final int[] indices) {
+ final int[] reverse = new int[indices.length];
+ for (int i = 0; i < indices.length; i++) {
+ reverse[i] = indices[indices.length - i - 1];
+ }
+ return reverse;
+ }
+
+ /**
+ * @param size Length of random array.
+ * @return an array of Gaussian random numbers.
+ */
+ private double[] randn(int size) {
+ final double[] randn = new double[size];
+ for (int i = 0; i < size; i++) {
+ randn[i] = random.nextGaussian();
+ }
+ return randn;
+ }
+
+ /**
+ * @param size Number of rows.
+ * @param popSize Population size.
+ * @return a 2-dimensional matrix of Gaussian random numbers.
+ */
+ private RealMatrix randn1(int size, int popSize) {
+ final double[][] d = new double[size][popSize];
+ for (int r = 0; r < size; r++) {
+ for (int c = 0; c < popSize; c++) {
+ d[r][c] = random.nextGaussian();
+ }
+ }
+ return new Array2DRowRealMatrix(d, false);
+ }
+}
diff --git a/src/main/java/org/apache/commons/math3/optimization/direct/MultiDirectionalSimplex.java b/src/main/java/org/apache/commons/math3/optimization/direct/MultiDirectionalSimplex.java
new file mode 100644
index 0000000..c06bf96
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/optimization/direct/MultiDirectionalSimplex.java
@@ -0,0 +1,218 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math3.optimization.direct;
+
+import java.util.Comparator;
+
+import org.apache.commons.math3.analysis.MultivariateFunction;
+import org.apache.commons.math3.optimization.PointValuePair;
+
+/**
+ * This class implements the multi-directional direct search method.
+ *
+ * @deprecated As of 3.1 (to be removed in 4.0).
+ * @since 3.0
+ */
+@Deprecated
+public class MultiDirectionalSimplex extends AbstractSimplex {
+ /** Default value for {@link #khi}: {@value}. */
+ private static final double DEFAULT_KHI = 2;
+ /** Default value for {@link #gamma}: {@value}. */
+ private static final double DEFAULT_GAMMA = 0.5;
+ /** Expansion coefficient. */
+ private final double khi;
+ /** Contraction coefficient. */
+ private final double gamma;
+
+ /**
+ * Build a multi-directional simplex with default coefficients.
+ * The default values are 2.0 for khi and 0.5 for gamma.
+ *
+ * @param n Dimension of the simplex.
+ */
+ public MultiDirectionalSimplex(final int n) {
+ this(n, 1d);
+ }
+
+ /**
+ * Build a multi-directional simplex with default coefficients.
+ * The default values are 2.0 for khi and 0.5 for gamma.
+ *
+ * @param n Dimension of the simplex.
+ * @param sideLength Length of the sides of the default (hypercube)
+ * simplex. See {@link AbstractSimplex#AbstractSimplex(int,double)}.
+ */
+ public MultiDirectionalSimplex(final int n, double sideLength) {
+ this(n, sideLength, DEFAULT_KHI, DEFAULT_GAMMA);
+ }
+
+ /**
+ * Build a multi-directional simplex with specified coefficients.
+ *
+ * @param n Dimension of the simplex. See
+ * {@link AbstractSimplex#AbstractSimplex(int,double)}.
+ * @param khi Expansion coefficient.
+ * @param gamma Contraction coefficient.
+ */
+ public MultiDirectionalSimplex(final int n,
+ final double khi, final double gamma) {
+ this(n, 1d, khi, gamma);
+ }
+
+ /**
+ * Build a multi-directional simplex with specified coefficients.
+ *
+ * @param n Dimension of the simplex. See
+ * {@link AbstractSimplex#AbstractSimplex(int,double)}.
+ * @param sideLength Length of the sides of the default (hypercube)
+ * simplex. See {@link AbstractSimplex#AbstractSimplex(int,double)}.
+ * @param khi Expansion coefficient.
+ * @param gamma Contraction coefficient.
+ */
+ public MultiDirectionalSimplex(final int n, double sideLength,
+ final double khi, final double gamma) {
+ super(n, sideLength);
+
+ this.khi = khi;
+ this.gamma = gamma;
+ }
+
+ /**
+ * Build a multi-directional simplex with default coefficients.
+ * The default values are 2.0 for khi and 0.5 for gamma.
+ *
+ * @param steps Steps along the canonical axes representing box edges.
+ * They may be negative but not zero. See
+ */
+ public MultiDirectionalSimplex(final double[] steps) {
+ this(steps, DEFAULT_KHI, DEFAULT_GAMMA);
+ }
+
+ /**
+ * Build a multi-directional simplex with specified coefficients.
+ *
+ * @param steps Steps along the canonical axes representing box edges.
+ * They may be negative but not zero. See
+ * {@link AbstractSimplex#AbstractSimplex(double[])}.
+ * @param khi Expansion coefficient.
+ * @param gamma Contraction coefficient.
+ */
+ public MultiDirectionalSimplex(final double[] steps,
+ final double khi, final double gamma) {
+ super(steps);
+
+ this.khi = khi;
+ this.gamma = gamma;
+ }
+
+ /**
+ * Build a multi-directional simplex with default coefficients.
+ * The default values are 2.0 for khi and 0.5 for gamma.
+ *
+ * @param referenceSimplex Reference simplex. See
+ * {@link AbstractSimplex#AbstractSimplex(double[][])}.
+ */
+ public MultiDirectionalSimplex(final double[][] referenceSimplex) {
+ this(referenceSimplex, DEFAULT_KHI, DEFAULT_GAMMA);
+ }
+
+ /**
+ * Build a multi-directional simplex with specified coefficients.
+ *
+ * @param referenceSimplex Reference simplex. See
+ * {@link AbstractSimplex#AbstractSimplex(double[][])}.
+ * @param khi Expansion coefficient.
+ * @param gamma Contraction coefficient.
+ * @throws org.apache.commons.math3.exception.NotStrictlyPositiveException
+ * if the reference simplex does not contain at least one point.
+ * @throws org.apache.commons.math3.exception.DimensionMismatchException
+ * if there is a dimension mismatch in the reference simplex.
+ */
+ public MultiDirectionalSimplex(final double[][] referenceSimplex,
+ final double khi, final double gamma) {
+ super(referenceSimplex);
+
+ this.khi = khi;
+ this.gamma = gamma;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void iterate(final MultivariateFunction evaluationFunction,
+ final Comparator<PointValuePair> comparator) {
+ // Save the original simplex.
+ final PointValuePair[] original = getPoints();
+ final PointValuePair best = original[0];
+
+ // Perform a reflection step.
+ final PointValuePair reflected = evaluateNewSimplex(evaluationFunction,
+ original, 1, comparator);
+ if (comparator.compare(reflected, best) < 0) {
+ // Compute the expanded simplex.
+ final PointValuePair[] reflectedSimplex = getPoints();
+ final PointValuePair expanded = evaluateNewSimplex(evaluationFunction,
+ original, khi, comparator);
+ if (comparator.compare(reflected, expanded) <= 0) {
+ // Keep the reflected simplex.
+ setPoints(reflectedSimplex);
+ }
+ // Keep the expanded simplex.
+ return;
+ }
+
+ // Compute the contracted simplex.
+ evaluateNewSimplex(evaluationFunction, original, gamma, comparator);
+
+ }
+
+ /**
+ * Compute and evaluate a new simplex.
+ *
+ * @param evaluationFunction Evaluation function.
+ * @param original Original simplex (to be preserved).
+ * @param coeff Linear coefficient.
+ * @param comparator Comparator to use to sort simplex vertices from best
+ * to poorest.
+ * @return the best point in the transformed simplex.
+ * @throws org.apache.commons.math3.exception.TooManyEvaluationsException
+ * if the maximal number of evaluations is exceeded.
+ */
+ private PointValuePair evaluateNewSimplex(final MultivariateFunction evaluationFunction,
+ final PointValuePair[] original,
+ final double coeff,
+ final Comparator<PointValuePair> comparator) {
+ final double[] xSmallest = original[0].getPointRef();
+ // Perform a linear transformation on all the simplex points,
+ // except the first one.
+ setPoint(0, original[0]);
+ final int dim = getDimension();
+ for (int i = 1; i < getSize(); i++) {
+ final double[] xOriginal = original[i].getPointRef();
+ final double[] xTransformed = new double[dim];
+ for (int j = 0; j < dim; j++) {
+ xTransformed[j] = xSmallest[j] + coeff * (xSmallest[j] - xOriginal[j]);
+ }
+ setPoint(i, new PointValuePair(xTransformed, Double.NaN, false));
+ }
+
+ // Evaluate the simplex.
+ evaluate(evaluationFunction, comparator);
+
+ return getPoint(0);
+ }
+}
diff --git a/src/main/java/org/apache/commons/math3/optimization/direct/MultivariateFunctionMappingAdapter.java b/src/main/java/org/apache/commons/math3/optimization/direct/MultivariateFunctionMappingAdapter.java
new file mode 100644
index 0000000..32f2a2c
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/optimization/direct/MultivariateFunctionMappingAdapter.java
@@ -0,0 +1,301 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math3.optimization.direct;
+
+import org.apache.commons.math3.analysis.MultivariateFunction;
+import org.apache.commons.math3.analysis.UnivariateFunction;
+import org.apache.commons.math3.analysis.function.Logit;
+import org.apache.commons.math3.analysis.function.Sigmoid;
+import org.apache.commons.math3.exception.DimensionMismatchException;
+import org.apache.commons.math3.exception.NumberIsTooSmallException;
+import org.apache.commons.math3.util.FastMath;
+import org.apache.commons.math3.util.MathUtils;
+
+/**
+ * <p>Adapter for mapping bounded {@link MultivariateFunction} to unbounded ones.</p>
+ *
+ * <p>
+ * This adapter can be used to wrap functions subject to simple bounds on
+ * parameters so they can be used by optimizers that do <em>not</em> directly
+ * support simple bounds.
+ * </p>
+ * <p>
+ * The principle is that the user function that will be wrapped will see its
+ * parameters bounded as required, i.e when its {@code value} method is called
+ * with argument array {@code point}, the elements array will fulfill requirement
+ * {@code lower[i] <= point[i] <= upper[i]} for all i. Some of the components
+ * may be unbounded or bounded only on one side if the corresponding bound is
+ * set to an infinite value. The optimizer will not manage the user function by
+ * itself, but it will handle this adapter and it is this adapter that will take
+ * care the bounds are fulfilled. The adapter {@link #value(double[])} method will
+ * be called by the optimizer with unbound parameters, and the adapter will map
+ * the unbounded value to the bounded range using appropriate functions like
+ * {@link Sigmoid} for double bounded elements for example.
+ * </p>
+ * <p>
+ * As the optimizer sees only unbounded parameters, it should be noted that the
+ * start point or simplex expected by the optimizer should be unbounded, so the
+ * user is responsible for converting his bounded point to unbounded by calling
+ * {@link #boundedToUnbounded(double[])} before providing them to the optimizer.
+ * For the same reason, the point returned by the {@link
+ * org.apache.commons.math3.optimization.BaseMultivariateOptimizer#optimize(int,
+ * MultivariateFunction, org.apache.commons.math3.optimization.GoalType, double[])}
+ * method is unbounded. So to convert this point to bounded, users must call
+ * {@link #unboundedToBounded(double[])} by themselves!</p>
+ * <p>
+ * This adapter is only a poor man solution to simple bounds optimization constraints
+ * that can be used with simple optimizers like {@link SimplexOptimizer} with {@link
+ * NelderMeadSimplex} or {@link MultiDirectionalSimplex}. A better solution is to use
+ * an optimizer that directly supports simple bounds like {@link CMAESOptimizer} or
+ * {@link BOBYQAOptimizer}. One caveat of this poor man solution is that behavior near
+ * the bounds may be numerically unstable as bounds are mapped from infinite values.
+ * Another caveat is that convergence values are evaluated by the optimizer with respect
+ * to unbounded variables, so there will be scales differences when converted to bounded
+ * variables.
+ * </p>
+ *
+ * @see MultivariateFunctionPenaltyAdapter
+ *
+ * @deprecated As of 3.1 (to be removed in 4.0).
+ * @since 3.0
+ */
+
+@Deprecated
+public class MultivariateFunctionMappingAdapter implements MultivariateFunction {
+
+ /** Underlying bounded function. */
+ private final MultivariateFunction bounded;
+
+ /** Mapping functions. */
+ private final Mapper[] mappers;
+
+ /** Simple constructor.
+ * @param bounded bounded function
+ * @param lower lower bounds for each element of the input parameters array
+ * (some elements may be set to {@code Double.NEGATIVE_INFINITY} for
+ * unbounded values)
+ * @param upper upper bounds for each element of the input parameters array
+ * (some elements may be set to {@code Double.POSITIVE_INFINITY} for
+ * unbounded values)
+ * @exception DimensionMismatchException if lower and upper bounds are not
+ * consistent, either according to dimension or to values
+ */
+ public MultivariateFunctionMappingAdapter(final MultivariateFunction bounded,
+ final double[] lower, final double[] upper) {
+
+ // safety checks
+ MathUtils.checkNotNull(lower);
+ MathUtils.checkNotNull(upper);
+ if (lower.length != upper.length) {
+ throw new DimensionMismatchException(lower.length, upper.length);
+ }
+ for (int i = 0; i < lower.length; ++i) {
+ // note the following test is written in such a way it also fails for NaN
+ if (!(upper[i] >= lower[i])) {
+ throw new NumberIsTooSmallException(upper[i], lower[i], true);
+ }
+ }
+
+ this.bounded = bounded;
+ this.mappers = new Mapper[lower.length];
+ for (int i = 0; i < mappers.length; ++i) {
+ if (Double.isInfinite(lower[i])) {
+ if (Double.isInfinite(upper[i])) {
+ // element is unbounded, no transformation is needed
+ mappers[i] = new NoBoundsMapper();
+ } else {
+ // element is simple-bounded on the upper side
+ mappers[i] = new UpperBoundMapper(upper[i]);
+ }
+ } else {
+ if (Double.isInfinite(upper[i])) {
+ // element is simple-bounded on the lower side
+ mappers[i] = new LowerBoundMapper(lower[i]);
+ } else {
+ // element is double-bounded
+ mappers[i] = new LowerUpperBoundMapper(lower[i], upper[i]);
+ }
+ }
+ }
+
+ }
+
+ /** Map an array from unbounded to bounded.
+ * @param point unbounded value
+ * @return bounded value
+ */
+ public double[] unboundedToBounded(double[] point) {
+
+ // map unbounded input point to bounded point
+ final double[] mapped = new double[mappers.length];
+ for (int i = 0; i < mappers.length; ++i) {
+ mapped[i] = mappers[i].unboundedToBounded(point[i]);
+ }
+
+ return mapped;
+
+ }
+
+ /** Map an array from bounded to unbounded.
+ * @param point bounded value
+ * @return unbounded value
+ */
+ public double[] boundedToUnbounded(double[] point) {
+
+ // map bounded input point to unbounded point
+ final double[] mapped = new double[mappers.length];
+ for (int i = 0; i < mappers.length; ++i) {
+ mapped[i] = mappers[i].boundedToUnbounded(point[i]);
+ }
+
+ return mapped;
+
+ }
+
+ /** Compute the underlying function value from an unbounded point.
+ * <p>
+ * This method simply bounds the unbounded point using the mappings
+ * set up at construction and calls the underlying function using
+ * the bounded point.
+ * </p>
+ * @param point unbounded value
+ * @return underlying function value
+ * @see #unboundedToBounded(double[])
+ */
+ public double value(double[] point) {
+ return bounded.value(unboundedToBounded(point));
+ }
+
+ /** Mapping interface. */
+ private interface Mapper {
+
+ /** Map a value from unbounded to bounded.
+ * @param y unbounded value
+ * @return bounded value
+ */
+ double unboundedToBounded(double y);
+
+ /** Map a value from bounded to unbounded.
+ * @param x bounded value
+ * @return unbounded value
+ */
+ double boundedToUnbounded(double x);
+
+ }
+
+ /** Local class for no bounds mapping. */
+ private static class NoBoundsMapper implements Mapper {
+
+ /** Simple constructor.
+ */
+ NoBoundsMapper() {
+ }
+
+ /** {@inheritDoc} */
+ public double unboundedToBounded(final double y) {
+ return y;
+ }
+
+ /** {@inheritDoc} */
+ public double boundedToUnbounded(final double x) {
+ return x;
+ }
+
+ }
+
+ /** Local class for lower bounds mapping. */
+ private static class LowerBoundMapper implements Mapper {
+
+ /** Low bound. */
+ private final double lower;
+
+ /** Simple constructor.
+ * @param lower lower bound
+ */
+ LowerBoundMapper(final double lower) {
+ this.lower = lower;
+ }
+
+ /** {@inheritDoc} */
+ public double unboundedToBounded(final double y) {
+ return lower + FastMath.exp(y);
+ }
+
+ /** {@inheritDoc} */
+ public double boundedToUnbounded(final double x) {
+ return FastMath.log(x - lower);
+ }
+
+ }
+
+ /** Local class for upper bounds mapping. */
+ private static class UpperBoundMapper implements Mapper {
+
+ /** Upper bound. */
+ private final double upper;
+
+ /** Simple constructor.
+ * @param upper upper bound
+ */
+ UpperBoundMapper(final double upper) {
+ this.upper = upper;
+ }
+
+ /** {@inheritDoc} */
+ public double unboundedToBounded(final double y) {
+ return upper - FastMath.exp(-y);
+ }
+
+ /** {@inheritDoc} */
+ public double boundedToUnbounded(final double x) {
+ return -FastMath.log(upper - x);
+ }
+
+ }
+
+ /** Local class for lower and bounds mapping. */
+ private static class LowerUpperBoundMapper implements Mapper {
+
+ /** Function from unbounded to bounded. */
+ private final UnivariateFunction boundingFunction;
+
+ /** Function from bounded to unbounded. */
+ private final UnivariateFunction unboundingFunction;
+
+ /** Simple constructor.
+ * @param lower lower bound
+ * @param upper upper bound
+ */
+ LowerUpperBoundMapper(final double lower, final double upper) {
+ boundingFunction = new Sigmoid(lower, upper);
+ unboundingFunction = new Logit(lower, upper);
+ }
+
+ /** {@inheritDoc} */
+ public double unboundedToBounded(final double y) {
+ return boundingFunction.value(y);
+ }
+
+ /** {@inheritDoc} */
+ public double boundedToUnbounded(final double x) {
+ return unboundingFunction.value(x);
+ }
+
+ }
+
+}
diff --git a/src/main/java/org/apache/commons/math3/optimization/direct/MultivariateFunctionPenaltyAdapter.java b/src/main/java/org/apache/commons/math3/optimization/direct/MultivariateFunctionPenaltyAdapter.java
new file mode 100644
index 0000000..4946487
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/optimization/direct/MultivariateFunctionPenaltyAdapter.java
@@ -0,0 +1,190 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math3.optimization.direct;
+
+import org.apache.commons.math3.analysis.MultivariateFunction;
+import org.apache.commons.math3.exception.DimensionMismatchException;
+import org.apache.commons.math3.exception.NumberIsTooSmallException;
+import org.apache.commons.math3.util.FastMath;
+import org.apache.commons.math3.util.MathUtils;
+
+/**
+ * <p>Adapter extending bounded {@link MultivariateFunction} to an unbouded
+ * domain using a penalty function.</p>
+ *
+ * <p>
+ * This adapter can be used to wrap functions subject to simple bounds on
+ * parameters so they can be used by optimizers that do <em>not</em> directly
+ * support simple bounds.
+ * </p>
+ * <p>
+ * The principle is that the user function that will be wrapped will see its
+ * parameters bounded as required, i.e when its {@code value} method is called
+ * with argument array {@code point}, the elements array will fulfill requirement
+ * {@code lower[i] <= point[i] <= upper[i]} for all i. Some of the components
+ * may be unbounded or bounded only on one side if the corresponding bound is
+ * set to an infinite value. The optimizer will not manage the user function by
+ * itself, but it will handle this adapter and it is this adapter that will take
+ * care the bounds are fulfilled. The adapter {@link #value(double[])} method will
+ * be called by the optimizer with unbound parameters, and the adapter will check
+ * if the parameters is within range or not. If it is in range, then the underlying
+ * user function will be called, and if it is not the value of a penalty function
+ * will be returned instead.
+ * </p>
+ * <p>
+ * This adapter is only a poor man solution to simple bounds optimization constraints
+ * that can be used with simple optimizers like {@link SimplexOptimizer} with {@link
+ * NelderMeadSimplex} or {@link MultiDirectionalSimplex}. A better solution is to use
+ * an optimizer that directly supports simple bounds like {@link CMAESOptimizer} or
+ * {@link BOBYQAOptimizer}. One caveat of this poor man solution is that if start point
+ * or start simplex is completely outside of the allowed range, only the penalty function
+ * is used, and the optimizer may converge without ever entering the range.
+ * </p>
+ *
+ * @see MultivariateFunctionMappingAdapter
+ *
+ * @deprecated As of 3.1 (to be removed in 4.0).
+ * @since 3.0
+ */
+
+@Deprecated
+public class MultivariateFunctionPenaltyAdapter implements MultivariateFunction {
+
+ /** Underlying bounded function. */
+ private final MultivariateFunction bounded;
+
+ /** Lower bounds. */
+ private final double[] lower;
+
+ /** Upper bounds. */
+ private final double[] upper;
+
+ /** Penalty offset. */
+ private final double offset;
+
+ /** Penalty scales. */
+ private final double[] scale;
+
+ /** Simple constructor.
+ * <p>
+ * When the optimizer provided points are out of range, the value of the
+ * penalty function will be used instead of the value of the underlying
+ * function. In order for this penalty to be effective in rejecting this
+ * point during the optimization process, the penalty function value should
+ * be defined with care. This value is computed as:
+ * <pre>
+ * penalty(point) = offset + &sum;<sub>i</sub>[scale[i] * &radic;|point[i]-boundary[i]|]
+ * </pre>
+ * where indices i correspond to all the components that violates their boundaries.
+ * </p>
+ * <p>
+ * So when attempting a function minimization, offset should be larger than
+ * the maximum expected value of the underlying function and scale components
+ * should all be positive. When attempting a function maximization, offset
+ * should be lesser than the minimum expected value of the underlying function
+ * and scale components should all be negative.
+ * minimization, and lesser than the minimum expected value of the underlying
+ * function when attempting maximization.
+ * </p>
+ * <p>
+ * These choices for the penalty function have two properties. First, all out
+ * of range points will return a function value that is worse than the value
+ * returned by any in range point. Second, the penalty is worse for large
+ * boundaries violation than for small violations, so the optimizer has an hint
+ * about the direction in which it should search for acceptable points.
+ * </p>
+ * @param bounded bounded function
+ * @param lower lower bounds for each element of the input parameters array
+ * (some elements may be set to {@code Double.NEGATIVE_INFINITY} for
+ * unbounded values)
+ * @param upper upper bounds for each element of the input parameters array
+ * (some elements may be set to {@code Double.POSITIVE_INFINITY} for
+ * unbounded values)
+ * @param offset base offset of the penalty function
+ * @param scale scale of the penalty function
+ * @exception DimensionMismatchException if lower bounds, upper bounds and
+ * scales are not consistent, either according to dimension or to bounadary
+ * values
+ */
+ public MultivariateFunctionPenaltyAdapter(final MultivariateFunction bounded,
+ final double[] lower, final double[] upper,
+ final double offset, final double[] scale) {
+
+ // safety checks
+ MathUtils.checkNotNull(lower);
+ MathUtils.checkNotNull(upper);
+ MathUtils.checkNotNull(scale);
+ if (lower.length != upper.length) {
+ throw new DimensionMismatchException(lower.length, upper.length);
+ }
+ if (lower.length != scale.length) {
+ throw new DimensionMismatchException(lower.length, scale.length);
+ }
+ for (int i = 0; i < lower.length; ++i) {
+ // note the following test is written in such a way it also fails for NaN
+ if (!(upper[i] >= lower[i])) {
+ throw new NumberIsTooSmallException(upper[i], lower[i], true);
+ }
+ }
+
+ this.bounded = bounded;
+ this.lower = lower.clone();
+ this.upper = upper.clone();
+ this.offset = offset;
+ this.scale = scale.clone();
+
+ }
+
+ /** Compute the underlying function value from an unbounded point.
+ * <p>
+ * This method simply returns the value of the underlying function
+ * if the unbounded point already fulfills the bounds, and compute
+ * a replacement value using the offset and scale if bounds are
+ * violated, without calling the function at all.
+ * </p>
+ * @param point unbounded point
+ * @return either underlying function value or penalty function value
+ */
+ public double value(double[] point) {
+
+ for (int i = 0; i < scale.length; ++i) {
+ if ((point[i] < lower[i]) || (point[i] > upper[i])) {
+ // bound violation starting at this component
+ double sum = 0;
+ for (int j = i; j < scale.length; ++j) {
+ final double overshoot;
+ if (point[j] < lower[j]) {
+ overshoot = scale[j] * (lower[j] - point[j]);
+ } else if (point[j] > upper[j]) {
+ overshoot = scale[j] * (point[j] - upper[j]);
+ } else {
+ overshoot = 0;
+ }
+ sum += FastMath.sqrt(overshoot);
+ }
+ return offset + sum;
+ }
+ }
+
+ // all boundaries are fulfilled, we are in the expected
+ // domain of the underlying function
+ return bounded.value(point);
+
+ }
+
+}
diff --git a/src/main/java/org/apache/commons/math3/optimization/direct/NelderMeadSimplex.java b/src/main/java/org/apache/commons/math3/optimization/direct/NelderMeadSimplex.java
new file mode 100644
index 0000000..a17586b
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/optimization/direct/NelderMeadSimplex.java
@@ -0,0 +1,283 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math3.optimization.direct;
+
+import java.util.Comparator;
+
+import org.apache.commons.math3.optimization.PointValuePair;
+import org.apache.commons.math3.analysis.MultivariateFunction;
+
+/**
+ * This class implements the Nelder-Mead simplex algorithm.
+ *
+ * @deprecated As of 3.1 (to be removed in 4.0).
+ * @since 3.0
+ */
+@Deprecated
+public class NelderMeadSimplex extends AbstractSimplex {
+ /** Default value for {@link #rho}: {@value}. */
+ private static final double DEFAULT_RHO = 1;
+ /** Default value for {@link #khi}: {@value}. */
+ private static final double DEFAULT_KHI = 2;
+ /** Default value for {@link #gamma}: {@value}. */
+ private static final double DEFAULT_GAMMA = 0.5;
+ /** Default value for {@link #sigma}: {@value}. */
+ private static final double DEFAULT_SIGMA = 0.5;
+ /** Reflection coefficient. */
+ private final double rho;
+ /** Expansion coefficient. */
+ private final double khi;
+ /** Contraction coefficient. */
+ private final double gamma;
+ /** Shrinkage coefficient. */
+ private final double sigma;
+
+ /**
+ * Build a Nelder-Mead simplex with default coefficients.
+ * The default coefficients are 1.0 for rho, 2.0 for khi and 0.5
+ * for both gamma and sigma.
+ *
+ * @param n Dimension of the simplex.
+ */
+ public NelderMeadSimplex(final int n) {
+ this(n, 1d);
+ }
+
+ /**
+ * Build a Nelder-Mead simplex with default coefficients.
+ * The default coefficients are 1.0 for rho, 2.0 for khi and 0.5
+ * for both gamma and sigma.
+ *
+ * @param n Dimension of the simplex.
+ * @param sideLength Length of the sides of the default (hypercube)
+ * simplex. See {@link AbstractSimplex#AbstractSimplex(int,double)}.
+ */
+ public NelderMeadSimplex(final int n, double sideLength) {
+ this(n, sideLength,
+ DEFAULT_RHO, DEFAULT_KHI, DEFAULT_GAMMA, DEFAULT_SIGMA);
+ }
+
+ /**
+ * Build a Nelder-Mead simplex with specified coefficients.
+ *
+ * @param n Dimension of the simplex. See
+ * {@link AbstractSimplex#AbstractSimplex(int,double)}.
+ * @param sideLength Length of the sides of the default (hypercube)
+ * simplex. See {@link AbstractSimplex#AbstractSimplex(int,double)}.
+ * @param rho Reflection coefficient.
+ * @param khi Expansion coefficient.
+ * @param gamma Contraction coefficient.
+ * @param sigma Shrinkage coefficient.
+ */
+ public NelderMeadSimplex(final int n, double sideLength,
+ final double rho, final double khi,
+ final double gamma, final double sigma) {
+ super(n, sideLength);
+
+ this.rho = rho;
+ this.khi = khi;
+ this.gamma = gamma;
+ this.sigma = sigma;
+ }
+
+ /**
+ * Build a Nelder-Mead simplex with specified coefficients.
+ *
+ * @param n Dimension of the simplex. See
+ * {@link AbstractSimplex#AbstractSimplex(int)}.
+ * @param rho Reflection coefficient.
+ * @param khi Expansion coefficient.
+ * @param gamma Contraction coefficient.
+ * @param sigma Shrinkage coefficient.
+ */
+ public NelderMeadSimplex(final int n,
+ final double rho, final double khi,
+ final double gamma, final double sigma) {
+ this(n, 1d, rho, khi, gamma, sigma);
+ }
+
+ /**
+ * Build a Nelder-Mead simplex with default coefficients.
+ * The default coefficients are 1.0 for rho, 2.0 for khi and 0.5
+ * for both gamma and sigma.
+ *
+ * @param steps Steps along the canonical axes representing box edges.
+ * They may be negative but not zero. See
+ */
+ public NelderMeadSimplex(final double[] steps) {
+ this(steps, DEFAULT_RHO, DEFAULT_KHI, DEFAULT_GAMMA, DEFAULT_SIGMA);
+ }
+
+ /**
+ * Build a Nelder-Mead simplex with specified coefficients.
+ *
+ * @param steps Steps along the canonical axes representing box edges.
+ * They may be negative but not zero. See
+ * {@link AbstractSimplex#AbstractSimplex(double[])}.
+ * @param rho Reflection coefficient.
+ * @param khi Expansion coefficient.
+ * @param gamma Contraction coefficient.
+ * @param sigma Shrinkage coefficient.
+ * @throws IllegalArgumentException if one of the steps is zero.
+ */
+ public NelderMeadSimplex(final double[] steps,
+ final double rho, final double khi,
+ final double gamma, final double sigma) {
+ super(steps);
+
+ this.rho = rho;
+ this.khi = khi;
+ this.gamma = gamma;
+ this.sigma = sigma;
+ }
+
+ /**
+ * Build a Nelder-Mead simplex with default coefficients.
+ * The default coefficients are 1.0 for rho, 2.0 for khi and 0.5
+ * for both gamma and sigma.
+ *
+ * @param referenceSimplex Reference simplex. See
+ * {@link AbstractSimplex#AbstractSimplex(double[][])}.
+ */
+ public NelderMeadSimplex(final double[][] referenceSimplex) {
+ this(referenceSimplex, DEFAULT_RHO, DEFAULT_KHI, DEFAULT_GAMMA, DEFAULT_SIGMA);
+ }
+
+ /**
+ * Build a Nelder-Mead simplex with specified coefficients.
+ *
+ * @param referenceSimplex Reference simplex. See
+ * {@link AbstractSimplex#AbstractSimplex(double[][])}.
+ * @param rho Reflection coefficient.
+ * @param khi Expansion coefficient.
+ * @param gamma Contraction coefficient.
+ * @param sigma Shrinkage coefficient.
+ * @throws org.apache.commons.math3.exception.NotStrictlyPositiveException
+ * if the reference simplex does not contain at least one point.
+ * @throws org.apache.commons.math3.exception.DimensionMismatchException
+ * if there is a dimension mismatch in the reference simplex.
+ */
+ public NelderMeadSimplex(final double[][] referenceSimplex,
+ final double rho, final double khi,
+ final double gamma, final double sigma) {
+ super(referenceSimplex);
+
+ this.rho = rho;
+ this.khi = khi;
+ this.gamma = gamma;
+ this.sigma = sigma;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void iterate(final MultivariateFunction evaluationFunction,
+ final Comparator<PointValuePair> comparator) {
+ // The simplex has n + 1 points if dimension is n.
+ final int n = getDimension();
+
+ // Interesting values.
+ final PointValuePair best = getPoint(0);
+ final PointValuePair secondBest = getPoint(n - 1);
+ final PointValuePair worst = getPoint(n);
+ final double[] xWorst = worst.getPointRef();
+
+ // Compute the centroid of the best vertices (dismissing the worst
+ // point at index n).
+ final double[] centroid = new double[n];
+ for (int i = 0; i < n; i++) {
+ final double[] x = getPoint(i).getPointRef();
+ for (int j = 0; j < n; j++) {
+ centroid[j] += x[j];
+ }
+ }
+ final double scaling = 1.0 / n;
+ for (int j = 0; j < n; j++) {
+ centroid[j] *= scaling;
+ }
+
+ // compute the reflection point
+ final double[] xR = new double[n];
+ for (int j = 0; j < n; j++) {
+ xR[j] = centroid[j] + rho * (centroid[j] - xWorst[j]);
+ }
+ final PointValuePair reflected
+ = new PointValuePair(xR, evaluationFunction.value(xR), false);
+
+ if (comparator.compare(best, reflected) <= 0 &&
+ comparator.compare(reflected, secondBest) < 0) {
+ // Accept the reflected point.
+ replaceWorstPoint(reflected, comparator);
+ } else if (comparator.compare(reflected, best) < 0) {
+ // Compute the expansion point.
+ final double[] xE = new double[n];
+ for (int j = 0; j < n; j++) {
+ xE[j] = centroid[j] + khi * (xR[j] - centroid[j]);
+ }
+ final PointValuePair expanded
+ = new PointValuePair(xE, evaluationFunction.value(xE), false);
+
+ if (comparator.compare(expanded, reflected) < 0) {
+ // Accept the expansion point.
+ replaceWorstPoint(expanded, comparator);
+ } else {
+ // Accept the reflected point.
+ replaceWorstPoint(reflected, comparator);
+ }
+ } else {
+ if (comparator.compare(reflected, worst) < 0) {
+ // Perform an outside contraction.
+ final double[] xC = new double[n];
+ for (int j = 0; j < n; j++) {
+ xC[j] = centroid[j] + gamma * (xR[j] - centroid[j]);
+ }
+ final PointValuePair outContracted
+ = new PointValuePair(xC, evaluationFunction.value(xC), false);
+ if (comparator.compare(outContracted, reflected) <= 0) {
+ // Accept the contraction point.
+ replaceWorstPoint(outContracted, comparator);
+ return;
+ }
+ } else {
+ // Perform an inside contraction.
+ final double[] xC = new double[n];
+ for (int j = 0; j < n; j++) {
+ xC[j] = centroid[j] - gamma * (centroid[j] - xWorst[j]);
+ }
+ final PointValuePair inContracted
+ = new PointValuePair(xC, evaluationFunction.value(xC), false);
+
+ if (comparator.compare(inContracted, worst) < 0) {
+ // Accept the contraction point.
+ replaceWorstPoint(inContracted, comparator);
+ return;
+ }
+ }
+
+ // Perform a shrink.
+ final double[] xSmallest = getPoint(0).getPointRef();
+ for (int i = 1; i <= n; i++) {
+ final double[] x = getPoint(i).getPoint();
+ for (int j = 0; j < n; j++) {
+ x[j] = xSmallest[j] + sigma * (x[j] - xSmallest[j]);
+ }
+ setPoint(i, new PointValuePair(x, Double.NaN, false));
+ }
+ evaluate(evaluationFunction, comparator);
+ }
+ }
+}
diff --git a/src/main/java/org/apache/commons/math3/optimization/direct/PowellOptimizer.java b/src/main/java/org/apache/commons/math3/optimization/direct/PowellOptimizer.java
new file mode 100644
index 0000000..8f5dd2b
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/optimization/direct/PowellOptimizer.java
@@ -0,0 +1,353 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math3.optimization.direct;
+
+import org.apache.commons.math3.util.FastMath;
+import org.apache.commons.math3.util.MathArrays;
+import org.apache.commons.math3.analysis.UnivariateFunction;
+import org.apache.commons.math3.analysis.MultivariateFunction;
+import org.apache.commons.math3.exception.NumberIsTooSmallException;
+import org.apache.commons.math3.exception.NotStrictlyPositiveException;
+import org.apache.commons.math3.optimization.GoalType;
+import org.apache.commons.math3.optimization.PointValuePair;
+import org.apache.commons.math3.optimization.ConvergenceChecker;
+import org.apache.commons.math3.optimization.MultivariateOptimizer;
+import org.apache.commons.math3.optimization.univariate.BracketFinder;
+import org.apache.commons.math3.optimization.univariate.BrentOptimizer;
+import org.apache.commons.math3.optimization.univariate.UnivariatePointValuePair;
+import org.apache.commons.math3.optimization.univariate.SimpleUnivariateValueChecker;
+
+/**
+ * Powell algorithm.
+ * This code is translated and adapted from the Python version of this
+ * algorithm (as implemented in module {@code optimize.py} v0.5 of
+ * <em>SciPy</em>).
+ * <br/>
+ * The default stopping criterion is based on the differences of the
+ * function value between two successive iterations. It is however possible
+ * to define a custom convergence checker that might terminate the algorithm
+ * earlier.
+ * <br/>
+ * The internal line search optimizer is a {@link BrentOptimizer} with a
+ * convergence checker set to {@link SimpleUnivariateValueChecker}.
+ *
+ * @deprecated As of 3.1 (to be removed in 4.0).
+ * @since 2.2
+ */
+@Deprecated
+public class PowellOptimizer
+ extends BaseAbstractMultivariateOptimizer<MultivariateFunction>
+ implements MultivariateOptimizer {
+ /**
+ * Minimum relative tolerance.
+ */
+ private static final double MIN_RELATIVE_TOLERANCE = 2 * FastMath.ulp(1d);
+ /**
+ * Relative threshold.
+ */
+ private final double relativeThreshold;
+ /**
+ * Absolute threshold.
+ */
+ private final double absoluteThreshold;
+ /**
+ * Line search.
+ */
+ private final LineSearch line;
+
+ /**
+ * This constructor allows to specify a user-defined convergence checker,
+ * in addition to the parameters that control the default convergence
+ * checking procedure.
+ * <br/>
+ * The internal line search tolerances are set to the square-root of their
+ * corresponding value in the multivariate optimizer.
+ *
+ * @param rel Relative threshold.
+ * @param abs Absolute threshold.
+ * @param checker Convergence checker.
+ * @throws NotStrictlyPositiveException if {@code abs <= 0}.
+ * @throws NumberIsTooSmallException if {@code rel < 2 * Math.ulp(1d)}.
+ */
+ public PowellOptimizer(double rel,
+ double abs,
+ ConvergenceChecker<PointValuePair> checker) {
+ this(rel, abs, FastMath.sqrt(rel), FastMath.sqrt(abs), checker);
+ }
+
+ /**
+ * This constructor allows to specify a user-defined convergence checker,
+ * in addition to the parameters that control the default convergence
+ * checking procedure and the line search tolerances.
+ *
+ * @param rel Relative threshold for this optimizer.
+ * @param abs Absolute threshold for this optimizer.
+ * @param lineRel Relative threshold for the internal line search optimizer.
+ * @param lineAbs Absolute threshold for the internal line search optimizer.
+ * @param checker Convergence checker.
+ * @throws NotStrictlyPositiveException if {@code abs <= 0}.
+ * @throws NumberIsTooSmallException if {@code rel < 2 * Math.ulp(1d)}.
+ */
+ public PowellOptimizer(double rel,
+ double abs,
+ double lineRel,
+ double lineAbs,
+ ConvergenceChecker<PointValuePair> checker) {
+ super(checker);
+
+ if (rel < MIN_RELATIVE_TOLERANCE) {
+ throw new NumberIsTooSmallException(rel, MIN_RELATIVE_TOLERANCE, true);
+ }
+ if (abs <= 0) {
+ throw new NotStrictlyPositiveException(abs);
+ }
+ relativeThreshold = rel;
+ absoluteThreshold = abs;
+
+ // Create the line search optimizer.
+ line = new LineSearch(lineRel,
+ lineAbs);
+ }
+
+ /**
+ * The parameters control the default convergence checking procedure.
+ * <br/>
+ * The internal line search tolerances are set to the square-root of their
+ * corresponding value in the multivariate optimizer.
+ *
+ * @param rel Relative threshold.
+ * @param abs Absolute threshold.
+ * @throws NotStrictlyPositiveException if {@code abs <= 0}.
+ * @throws NumberIsTooSmallException if {@code rel < 2 * Math.ulp(1d)}.
+ */
+ public PowellOptimizer(double rel,
+ double abs) {
+ this(rel, abs, null);
+ }
+
+ /**
+ * Builds an instance with the default convergence checking procedure.
+ *
+ * @param rel Relative threshold.
+ * @param abs Absolute threshold.
+ * @param lineRel Relative threshold for the internal line search optimizer.
+ * @param lineAbs Absolute threshold for the internal line search optimizer.
+ * @throws NotStrictlyPositiveException if {@code abs <= 0}.
+ * @throws NumberIsTooSmallException if {@code rel < 2 * Math.ulp(1d)}.
+ * @since 3.1
+ */
+ public PowellOptimizer(double rel,
+ double abs,
+ double lineRel,
+ double lineAbs) {
+ this(rel, abs, lineRel, lineAbs, null);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ protected PointValuePair doOptimize() {
+ final GoalType goal = getGoalType();
+ final double[] guess = getStartPoint();
+ final int n = guess.length;
+
+ final double[][] direc = new double[n][n];
+ for (int i = 0; i < n; i++) {
+ direc[i][i] = 1;
+ }
+
+ final ConvergenceChecker<PointValuePair> checker
+ = getConvergenceChecker();
+
+ double[] x = guess;
+ double fVal = computeObjectiveValue(x);
+ double[] x1 = x.clone();
+ int iter = 0;
+ while (true) {
+ ++iter;
+
+ double fX = fVal;
+ double fX2 = 0;
+ double delta = 0;
+ int bigInd = 0;
+ double alphaMin = 0;
+
+ for (int i = 0; i < n; i++) {
+ final double[] d = MathArrays.copyOf(direc[i]);
+
+ fX2 = fVal;
+
+ final UnivariatePointValuePair optimum = line.search(x, d);
+ fVal = optimum.getValue();
+ alphaMin = optimum.getPoint();
+ final double[][] result = newPointAndDirection(x, d, alphaMin);
+ x = result[0];
+
+ if ((fX2 - fVal) > delta) {
+ delta = fX2 - fVal;
+ bigInd = i;
+ }
+ }
+
+ // Default convergence check.
+ boolean stop = 2 * (fX - fVal) <=
+ (relativeThreshold * (FastMath.abs(fX) + FastMath.abs(fVal)) +
+ absoluteThreshold);
+
+ final PointValuePair previous = new PointValuePair(x1, fX);
+ final PointValuePair current = new PointValuePair(x, fVal);
+ if (!stop && checker != null) {
+ stop = checker.converged(iter, previous, current);
+ }
+ if (stop) {
+ if (goal == GoalType.MINIMIZE) {
+ return (fVal < fX) ? current : previous;
+ } else {
+ return (fVal > fX) ? current : previous;
+ }
+ }
+
+ final double[] d = new double[n];
+ final double[] x2 = new double[n];
+ for (int i = 0; i < n; i++) {
+ d[i] = x[i] - x1[i];
+ x2[i] = 2 * x[i] - x1[i];
+ }
+
+ x1 = x.clone();
+ fX2 = computeObjectiveValue(x2);
+
+ if (fX > fX2) {
+ double t = 2 * (fX + fX2 - 2 * fVal);
+ double temp = fX - fVal - delta;
+ t *= temp * temp;
+ temp = fX - fX2;
+ t -= delta * temp * temp;
+
+ if (t < 0.0) {
+ final UnivariatePointValuePair optimum = line.search(x, d);
+ fVal = optimum.getValue();
+ alphaMin = optimum.getPoint();
+ final double[][] result = newPointAndDirection(x, d, alphaMin);
+ x = result[0];
+
+ final int lastInd = n - 1;
+ direc[bigInd] = direc[lastInd];
+ direc[lastInd] = result[1];
+ }
+ }
+ }
+ }
+
+ /**
+ * Compute a new point (in the original space) and a new direction
+ * vector, resulting from the line search.
+ *
+ * @param p Point used in the line search.
+ * @param d Direction used in the line search.
+ * @param optimum Optimum found by the line search.
+ * @return a 2-element array containing the new point (at index 0) and
+ * the new direction (at index 1).
+ */
+ private double[][] newPointAndDirection(double[] p,
+ double[] d,
+ double optimum) {
+ final int n = p.length;
+ final double[] nP = new double[n];
+ final double[] nD = new double[n];
+ for (int i = 0; i < n; i++) {
+ nD[i] = d[i] * optimum;
+ nP[i] = p[i] + nD[i];
+ }
+
+ final double[][] result = new double[2][];
+ result[0] = nP;
+ result[1] = nD;
+
+ return result;
+ }
+
+ /**
+ * Class for finding the minimum of the objective function along a given
+ * direction.
+ */
+ private class LineSearch extends BrentOptimizer {
+ /**
+ * Value that will pass the precondition check for {@link BrentOptimizer}
+ * but will not pass the convergence check, so that the custom checker
+ * will always decide when to stop the line search.
+ */
+ private static final double REL_TOL_UNUSED = 1e-15;
+ /**
+ * Value that will pass the precondition check for {@link BrentOptimizer}
+ * but will not pass the convergence check, so that the custom checker
+ * will always decide when to stop the line search.
+ */
+ private static final double ABS_TOL_UNUSED = Double.MIN_VALUE;
+ /**
+ * Automatic bracketing.
+ */
+ private final BracketFinder bracket = new BracketFinder();
+
+ /**
+ * The "BrentOptimizer" default stopping criterion uses the tolerances
+ * to check the domain (point) values, not the function values.
+ * We thus create a custom checker to use function values.
+ *
+ * @param rel Relative threshold.
+ * @param abs Absolute threshold.
+ */
+ LineSearch(double rel,
+ double abs) {
+ super(REL_TOL_UNUSED,
+ ABS_TOL_UNUSED,
+ new SimpleUnivariateValueChecker(rel, abs));
+ }
+
+ /**
+ * Find the minimum of the function {@code f(p + alpha * d)}.
+ *
+ * @param p Starting point.
+ * @param d Search direction.
+ * @return the optimum.
+ * @throws org.apache.commons.math3.exception.TooManyEvaluationsException
+ * if the number of evaluations is exceeded.
+ */
+ public UnivariatePointValuePair search(final double[] p, final double[] d) {
+ final int n = p.length;
+ final UnivariateFunction f = new UnivariateFunction() {
+ /** {@inheritDoc} */
+ public double value(double alpha) {
+ final double[] x = new double[n];
+ for (int i = 0; i < n; i++) {
+ x[i] = p[i] + alpha * d[i];
+ }
+ final double obj = PowellOptimizer.this.computeObjectiveValue(x);
+ return obj;
+ }
+ };
+
+ final GoalType goal = PowellOptimizer.this.getGoalType();
+ bracket.search(f, goal, 0, 1);
+ // Passing "MAX_VALUE" as a dummy value because it is the enclosing
+ // class that counts the number of evaluations (and will eventually
+ // generate the exception).
+ return optimize(Integer.MAX_VALUE, f, goal,
+ bracket.getLo(), bracket.getHi(), bracket.getMid());
+ }
+ }
+}
diff --git a/src/main/java/org/apache/commons/math3/optimization/direct/SimplexOptimizer.java b/src/main/java/org/apache/commons/math3/optimization/direct/SimplexOptimizer.java
new file mode 100644
index 0000000..8136704
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/optimization/direct/SimplexOptimizer.java
@@ -0,0 +1,235 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math3.optimization.direct;
+
+import java.util.Comparator;
+
+import org.apache.commons.math3.analysis.MultivariateFunction;
+import org.apache.commons.math3.exception.NullArgumentException;
+import org.apache.commons.math3.optimization.GoalType;
+import org.apache.commons.math3.optimization.ConvergenceChecker;
+import org.apache.commons.math3.optimization.PointValuePair;
+import org.apache.commons.math3.optimization.SimpleValueChecker;
+import org.apache.commons.math3.optimization.MultivariateOptimizer;
+import org.apache.commons.math3.optimization.OptimizationData;
+
+/**
+ * This class implements simplex-based direct search optimization.
+ *
+ * <p>
+ * Direct search methods only use objective function values, they do
+ * not need derivatives and don't either try to compute approximation
+ * of the derivatives. According to a 1996 paper by Margaret H. Wright
+ * (<a href="http://cm.bell-labs.com/cm/cs/doc/96/4-02.ps.gz">Direct
+ * Search Methods: Once Scorned, Now Respectable</a>), they are used
+ * when either the computation of the derivative is impossible (noisy
+ * functions, unpredictable discontinuities) or difficult (complexity,
+ * computation cost). In the first cases, rather than an optimum, a
+ * <em>not too bad</em> point is desired. In the latter cases, an
+ * optimum is desired but cannot be reasonably found. In all cases
+ * direct search methods can be useful.
+ * </p>
+ * <p>
+ * Simplex-based direct search methods are based on comparison of
+ * the objective function values at the vertices of a simplex (which is a
+ * set of n+1 points in dimension n) that is updated by the algorithms
+ * steps.
+ * <p>
+ * <p>
+ * The {@link #setSimplex(AbstractSimplex) setSimplex} method <em>must</em>
+ * be called prior to calling the {@code optimize} method.
+ * </p>
+ * <p>
+ * Each call to {@link #optimize(int,MultivariateFunction,GoalType,double[])
+ * optimize} will re-use the start configuration of the current simplex and
+ * move it such that its first vertex is at the provided start point of the
+ * optimization. If the {@code optimize} method is called to solve a different
+ * problem and the number of parameters change, the simplex must be
+ * re-initialized to one with the appropriate dimensions.
+ * </p>
+ * <p>
+ * Convergence is checked by providing the <em>worst</em> points of
+ * previous and current simplex to the convergence checker, not the best
+ * ones.
+ * </p>
+ * <p>
+ * This simplex optimizer implementation does not directly support constrained
+ * optimization with simple bounds, so for such optimizations, either a more
+ * dedicated method must be used like {@link CMAESOptimizer} or {@link
+ * BOBYQAOptimizer}, or the optimized method must be wrapped in an adapter like
+ * {@link MultivariateFunctionMappingAdapter} or {@link
+ * MultivariateFunctionPenaltyAdapter}.
+ * </p>
+ *
+ * @see AbstractSimplex
+ * @see MultivariateFunctionMappingAdapter
+ * @see MultivariateFunctionPenaltyAdapter
+ * @see CMAESOptimizer
+ * @see BOBYQAOptimizer
+ * @deprecated As of 3.1 (to be removed in 4.0).
+ * @since 3.0
+ */
+@SuppressWarnings("boxing") // deprecated anyway
+@Deprecated
+public class SimplexOptimizer
+ extends BaseAbstractMultivariateOptimizer<MultivariateFunction>
+ implements MultivariateOptimizer {
+ /** Simplex. */
+ private AbstractSimplex simplex;
+
+ /**
+ * Constructor using a default {@link SimpleValueChecker convergence
+ * checker}.
+ * @deprecated See {@link SimpleValueChecker#SimpleValueChecker()}
+ */
+ @Deprecated
+ public SimplexOptimizer() {
+ this(new SimpleValueChecker());
+ }
+
+ /**
+ * @param checker Convergence checker.
+ */
+ public SimplexOptimizer(ConvergenceChecker<PointValuePair> checker) {
+ super(checker);
+ }
+
+ /**
+ * @param rel Relative threshold.
+ * @param abs Absolute threshold.
+ */
+ public SimplexOptimizer(double rel, double abs) {
+ this(new SimpleValueChecker(rel, abs));
+ }
+
+ /**
+ * Set the simplex algorithm.
+ *
+ * @param simplex Simplex.
+ * @deprecated As of 3.1. The initial simplex can now be passed as an
+ * argument of the {@link #optimize(int,MultivariateFunction,GoalType,OptimizationData[])}
+ * method.
+ */
+ @Deprecated
+ public void setSimplex(AbstractSimplex simplex) {
+ parseOptimizationData(simplex);
+ }
+
+ /**
+ * Optimize an objective function.
+ *
+ * @param maxEval Allowed number of evaluations of the objective function.
+ * @param f Objective function.
+ * @param goalType Optimization type.
+ * @param optData Optimization data. The following data will be looked for:
+ * <ul>
+ * <li>{@link org.apache.commons.math3.optimization.InitialGuess InitialGuess}</li>
+ * <li>{@link AbstractSimplex}</li>
+ * </ul>
+ * @return the point/value pair giving the optimal value for objective
+ * function.
+ */
+ @Override
+ protected PointValuePair optimizeInternal(int maxEval, MultivariateFunction f,
+ GoalType goalType,
+ OptimizationData... optData) {
+ // Scan "optData" for the input specific to this optimizer.
+ parseOptimizationData(optData);
+
+ // The parent's method will retrieve the common parameters from
+ // "optData" and call "doOptimize".
+ return super.optimizeInternal(maxEval, f, goalType, optData);
+ }
+
+ /**
+ * Scans the list of (required and optional) optimization data that
+ * characterize the problem.
+ *
+ * @param optData Optimization data. The following data will be looked for:
+ * <ul>
+ * <li>{@link AbstractSimplex}</li>
+ * </ul>
+ */
+ private void parseOptimizationData(OptimizationData... optData) {
+ // The existing values (as set by the previous call) are reused if
+ // not provided in the argument list.
+ for (OptimizationData data : optData) {
+ if (data instanceof AbstractSimplex) {
+ simplex = (AbstractSimplex) data;
+ continue;
+ }
+ }
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ protected PointValuePair doOptimize() {
+ if (simplex == null) {
+ throw new NullArgumentException();
+ }
+
+ // Indirect call to "computeObjectiveValue" in order to update the
+ // evaluations counter.
+ final MultivariateFunction evalFunc
+ = new MultivariateFunction() {
+ /** {@inheritDoc} */
+ public double value(double[] point) {
+ return computeObjectiveValue(point);
+ }
+ };
+
+ final boolean isMinim = getGoalType() == GoalType.MINIMIZE;
+ final Comparator<PointValuePair> comparator
+ = new Comparator<PointValuePair>() {
+ /** {@inheritDoc} */
+ public int compare(final PointValuePair o1,
+ final PointValuePair o2) {
+ final double v1 = o1.getValue();
+ final double v2 = o2.getValue();
+ return isMinim ? Double.compare(v1, v2) : Double.compare(v2, v1);
+ }
+ };
+
+ // Initialize search.
+ simplex.build(getStartPoint());
+ simplex.evaluate(evalFunc, comparator);
+
+ PointValuePair[] previous = null;
+ int iteration = 0;
+ final ConvergenceChecker<PointValuePair> checker = getConvergenceChecker();
+ while (true) {
+ if (iteration > 0) {
+ boolean converged = true;
+ for (int i = 0; i < simplex.getSize(); i++) {
+ PointValuePair prev = previous[i];
+ converged = converged &&
+ checker.converged(iteration, prev, simplex.getPoint(i));
+ }
+ if (converged) {
+ // We have found an optimum.
+ return simplex.getPoint(0);
+ }
+ }
+
+ // We still need to search.
+ previous = simplex.getPoints();
+ simplex.iterate(evalFunc, comparator);
+ ++iteration;
+ }
+ }
+}
diff --git a/src/main/java/org/apache/commons/math3/optimization/direct/package-info.java b/src/main/java/org/apache/commons/math3/optimization/direct/package-info.java
new file mode 100644
index 0000000..a587bcf
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/optimization/direct/package-info.java
@@ -0,0 +1,24 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/**
+ *
+ * <p>
+ * This package provides optimization algorithms that don't require derivatives.
+ * </p>
+ *
+ */
+package org.apache.commons.math3.optimization.direct;
diff --git a/src/main/java/org/apache/commons/math3/optimization/fitting/CurveFitter.java b/src/main/java/org/apache/commons/math3/optimization/fitting/CurveFitter.java
new file mode 100644
index 0000000..26e39f5
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/optimization/fitting/CurveFitter.java
@@ -0,0 +1,299 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math3.optimization.fitting;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.apache.commons.math3.analysis.DifferentiableMultivariateVectorFunction;
+import org.apache.commons.math3.analysis.MultivariateMatrixFunction;
+import org.apache.commons.math3.analysis.ParametricUnivariateFunction;
+import org.apache.commons.math3.analysis.differentiation.DerivativeStructure;
+import org.apache.commons.math3.analysis.differentiation.MultivariateDifferentiableVectorFunction;
+import org.apache.commons.math3.optimization.DifferentiableMultivariateVectorOptimizer;
+import org.apache.commons.math3.optimization.MultivariateDifferentiableVectorOptimizer;
+import org.apache.commons.math3.optimization.PointVectorValuePair;
+
+/** Fitter for parametric univariate real functions y = f(x).
+ * <br/>
+ * When a univariate real function y = f(x) does depend on some
+ * unknown parameters p<sub>0</sub>, p<sub>1</sub> ... p<sub>n-1</sub>,
+ * this class can be used to find these parameters. It does this
+ * by <em>fitting</em> the curve so it remains very close to a set of
+ * observed points (x<sub>0</sub>, y<sub>0</sub>), (x<sub>1</sub>,
+ * y<sub>1</sub>) ... (x<sub>k-1</sub>, y<sub>k-1</sub>). This fitting
+ * is done by finding the parameters values that minimizes the objective
+ * function &sum;(y<sub>i</sub>-f(x<sub>i</sub>))<sup>2</sup>. This is
+ * really a least squares problem.
+ *
+ * @param <T> Function to use for the fit.
+ *
+ * @deprecated As of 3.1 (to be removed in 4.0).
+ * @since 2.0
+ */
+@Deprecated
+public class CurveFitter<T extends ParametricUnivariateFunction> {
+
+ /** Optimizer to use for the fitting.
+ * @deprecated as of 3.1 replaced by {@link #optimizer}
+ */
+ @Deprecated
+ private final DifferentiableMultivariateVectorOptimizer oldOptimizer;
+
+ /** Optimizer to use for the fitting. */
+ private final MultivariateDifferentiableVectorOptimizer optimizer;
+
+ /** Observed points. */
+ private final List<WeightedObservedPoint> observations;
+
+ /** Simple constructor.
+ * @param optimizer optimizer to use for the fitting
+ * @deprecated as of 3.1 replaced by {@link #CurveFitter(MultivariateDifferentiableVectorOptimizer)}
+ */
+ @Deprecated
+ public CurveFitter(final DifferentiableMultivariateVectorOptimizer optimizer) {
+ this.oldOptimizer = optimizer;
+ this.optimizer = null;
+ observations = new ArrayList<WeightedObservedPoint>();
+ }
+
+ /** Simple constructor.
+ * @param optimizer optimizer to use for the fitting
+ * @since 3.1
+ */
+ public CurveFitter(final MultivariateDifferentiableVectorOptimizer optimizer) {
+ this.oldOptimizer = null;
+ this.optimizer = optimizer;
+ observations = new ArrayList<WeightedObservedPoint>();
+ }
+
+ /** Add an observed (x,y) point to the sample with unit weight.
+ * <p>Calling this method is equivalent to call
+ * {@code addObservedPoint(1.0, x, y)}.</p>
+ * @param x abscissa of the point
+ * @param y observed value of the point at x, after fitting we should
+ * have f(x) as close as possible to this value
+ * @see #addObservedPoint(double, double, double)
+ * @see #addObservedPoint(WeightedObservedPoint)
+ * @see #getObservations()
+ */
+ public void addObservedPoint(double x, double y) {
+ addObservedPoint(1.0, x, y);
+ }
+
+ /** Add an observed weighted (x,y) point to the sample.
+ * @param weight weight of the observed point in the fit
+ * @param x abscissa of the point
+ * @param y observed value of the point at x, after fitting we should
+ * have f(x) as close as possible to this value
+ * @see #addObservedPoint(double, double)
+ * @see #addObservedPoint(WeightedObservedPoint)
+ * @see #getObservations()
+ */
+ public void addObservedPoint(double weight, double x, double y) {
+ observations.add(new WeightedObservedPoint(weight, x, y));
+ }
+
+ /** Add an observed weighted (x,y) point to the sample.
+ * @param observed observed point to add
+ * @see #addObservedPoint(double, double)
+ * @see #addObservedPoint(double, double, double)
+ * @see #getObservations()
+ */
+ public void addObservedPoint(WeightedObservedPoint observed) {
+ observations.add(observed);
+ }
+
+ /** Get the observed points.
+ * @return observed points
+ * @see #addObservedPoint(double, double)
+ * @see #addObservedPoint(double, double, double)
+ * @see #addObservedPoint(WeightedObservedPoint)
+ */
+ public WeightedObservedPoint[] getObservations() {
+ return observations.toArray(new WeightedObservedPoint[observations.size()]);
+ }
+
+ /**
+ * Remove all observations.
+ */
+ public void clearObservations() {
+ observations.clear();
+ }
+
+ /**
+ * Fit a curve.
+ * This method compute the coefficients of the curve that best
+ * fit the sample of observed points previously given through calls
+ * to the {@link #addObservedPoint(WeightedObservedPoint)
+ * addObservedPoint} method.
+ *
+ * @param f parametric function to fit.
+ * @param initialGuess first guess of the function parameters.
+ * @return the fitted parameters.
+ * @throws org.apache.commons.math3.exception.DimensionMismatchException
+ * if the start point dimension is wrong.
+ */
+ public double[] fit(T f, final double[] initialGuess) {
+ return fit(Integer.MAX_VALUE, f, initialGuess);
+ }
+
+ /**
+ * Fit a curve.
+ * This method compute the coefficients of the curve that best
+ * fit the sample of observed points previously given through calls
+ * to the {@link #addObservedPoint(WeightedObservedPoint)
+ * addObservedPoint} method.
+ *
+ * @param f parametric function to fit.
+ * @param initialGuess first guess of the function parameters.
+ * @param maxEval Maximum number of function evaluations.
+ * @return the fitted parameters.
+ * @throws org.apache.commons.math3.exception.TooManyEvaluationsException
+ * if the number of allowed evaluations is exceeded.
+ * @throws org.apache.commons.math3.exception.DimensionMismatchException
+ * if the start point dimension is wrong.
+ * @since 3.0
+ */
+ public double[] fit(int maxEval, T f,
+ final double[] initialGuess) {
+ // prepare least squares problem
+ double[] target = new double[observations.size()];
+ double[] weights = new double[observations.size()];
+ int i = 0;
+ for (WeightedObservedPoint point : observations) {
+ target[i] = point.getY();
+ weights[i] = point.getWeight();
+ ++i;
+ }
+
+ // perform the fit
+ final PointVectorValuePair optimum;
+ if (optimizer == null) {
+ // to be removed in 4.0
+ optimum = oldOptimizer.optimize(maxEval, new OldTheoreticalValuesFunction(f),
+ target, weights, initialGuess);
+ } else {
+ optimum = optimizer.optimize(maxEval, new TheoreticalValuesFunction(f),
+ target, weights, initialGuess);
+ }
+
+ // extract the coefficients
+ return optimum.getPointRef();
+ }
+
+ /** Vectorial function computing function theoretical values. */
+ @Deprecated
+ private class OldTheoreticalValuesFunction
+ implements DifferentiableMultivariateVectorFunction {
+ /** Function to fit. */
+ private final ParametricUnivariateFunction f;
+
+ /** Simple constructor.
+ * @param f function to fit.
+ */
+ OldTheoreticalValuesFunction(final ParametricUnivariateFunction f) {
+ this.f = f;
+ }
+
+ /** {@inheritDoc} */
+ public MultivariateMatrixFunction jacobian() {
+ return new MultivariateMatrixFunction() {
+ /** {@inheritDoc} */
+ public double[][] value(double[] point) {
+ final double[][] jacobian = new double[observations.size()][];
+
+ int i = 0;
+ for (WeightedObservedPoint observed : observations) {
+ jacobian[i++] = f.gradient(observed.getX(), point);
+ }
+
+ return jacobian;
+ }
+ };
+ }
+
+ /** {@inheritDoc} */
+ public double[] value(double[] point) {
+ // compute the residuals
+ final double[] values = new double[observations.size()];
+ int i = 0;
+ for (WeightedObservedPoint observed : observations) {
+ values[i++] = f.value(observed.getX(), point);
+ }
+
+ return values;
+ }
+ }
+
+ /** Vectorial function computing function theoretical values. */
+ private class TheoreticalValuesFunction implements MultivariateDifferentiableVectorFunction {
+
+ /** Function to fit. */
+ private final ParametricUnivariateFunction f;
+
+ /** Simple constructor.
+ * @param f function to fit.
+ */
+ TheoreticalValuesFunction(final ParametricUnivariateFunction f) {
+ this.f = f;
+ }
+
+ /** {@inheritDoc} */
+ public double[] value(double[] point) {
+ // compute the residuals
+ final double[] values = new double[observations.size()];
+ int i = 0;
+ for (WeightedObservedPoint observed : observations) {
+ values[i++] = f.value(observed.getX(), point);
+ }
+
+ return values;
+ }
+
+ /** {@inheritDoc} */
+ public DerivativeStructure[] value(DerivativeStructure[] point) {
+
+ // extract parameters
+ final double[] parameters = new double[point.length];
+ for (int k = 0; k < point.length; ++k) {
+ parameters[k] = point[k].getValue();
+ }
+
+ // compute the residuals
+ final DerivativeStructure[] values = new DerivativeStructure[observations.size()];
+ int i = 0;
+ for (WeightedObservedPoint observed : observations) {
+
+ // build the DerivativeStructure by adding first the value as a constant
+ // and then adding derivatives
+ DerivativeStructure vi = new DerivativeStructure(point.length, 1, f.value(observed.getX(), parameters));
+ for (int k = 0; k < point.length; ++k) {
+ vi = vi.add(new DerivativeStructure(point.length, 1, k, 0.0));
+ }
+
+ values[i++] = vi;
+
+ }
+
+ return values;
+ }
+
+ }
+
+}
diff --git a/src/main/java/org/apache/commons/math3/optimization/fitting/GaussianFitter.java b/src/main/java/org/apache/commons/math3/optimization/fitting/GaussianFitter.java
new file mode 100644
index 0000000..375f12e
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/optimization/fitting/GaussianFitter.java
@@ -0,0 +1,371 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math3.optimization.fitting;
+
+import java.util.Arrays;
+import java.util.Comparator;
+
+import org.apache.commons.math3.analysis.function.Gaussian;
+import org.apache.commons.math3.exception.NullArgumentException;
+import org.apache.commons.math3.exception.NumberIsTooSmallException;
+import org.apache.commons.math3.exception.OutOfRangeException;
+import org.apache.commons.math3.exception.ZeroException;
+import org.apache.commons.math3.exception.NotStrictlyPositiveException;
+import org.apache.commons.math3.exception.util.LocalizedFormats;
+import org.apache.commons.math3.optimization.DifferentiableMultivariateVectorOptimizer;
+import org.apache.commons.math3.util.FastMath;
+
+/**
+ * Fits points to a {@link
+ * org.apache.commons.math3.analysis.function.Gaussian.Parametric Gaussian} function.
+ * <p>
+ * Usage example:
+ * <pre>
+ * GaussianFitter fitter = new GaussianFitter(
+ * new LevenbergMarquardtOptimizer());
+ * fitter.addObservedPoint(4.0254623, 531026.0);
+ * fitter.addObservedPoint(4.03128248, 984167.0);
+ * fitter.addObservedPoint(4.03839603, 1887233.0);
+ * fitter.addObservedPoint(4.04421621, 2687152.0);
+ * fitter.addObservedPoint(4.05132976, 3461228.0);
+ * fitter.addObservedPoint(4.05326982, 3580526.0);
+ * fitter.addObservedPoint(4.05779662, 3439750.0);
+ * fitter.addObservedPoint(4.0636168, 2877648.0);
+ * fitter.addObservedPoint(4.06943698, 2175960.0);
+ * fitter.addObservedPoint(4.07525716, 1447024.0);
+ * fitter.addObservedPoint(4.08237071, 717104.0);
+ * fitter.addObservedPoint(4.08366408, 620014.0);
+ * double[] parameters = fitter.fit();
+ * </pre>
+ *
+ * @since 2.2
+ * @deprecated As of 3.1 (to be removed in 4.0).
+ */
+@Deprecated
+public class GaussianFitter extends CurveFitter<Gaussian.Parametric> {
+ /**
+ * Constructs an instance using the specified optimizer.
+ *
+ * @param optimizer Optimizer to use for the fitting.
+ */
+ public GaussianFitter(DifferentiableMultivariateVectorOptimizer optimizer) {
+ super(optimizer);
+ }
+
+ /**
+ * Fits a Gaussian function to the observed points.
+ *
+ * @param initialGuess First guess values in the following order:
+ * <ul>
+ * <li>Norm</li>
+ * <li>Mean</li>
+ * <li>Sigma</li>
+ * </ul>
+ * @return the parameters of the Gaussian function that best fits the
+ * observed points (in the same order as above).
+ * @since 3.0
+ */
+ public double[] fit(double[] initialGuess) {
+ final Gaussian.Parametric f = new Gaussian.Parametric() {
+ /** {@inheritDoc} */
+ @Override
+ public double value(double x, double ... p) {
+ double v = Double.POSITIVE_INFINITY;
+ try {
+ v = super.value(x, p);
+ } catch (NotStrictlyPositiveException e) { // NOPMD
+ // Do nothing.
+ }
+ return v;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public double[] gradient(double x, double ... p) {
+ double[] v = { Double.POSITIVE_INFINITY,
+ Double.POSITIVE_INFINITY,
+ Double.POSITIVE_INFINITY };
+ try {
+ v = super.gradient(x, p);
+ } catch (NotStrictlyPositiveException e) { // NOPMD
+ // Do nothing.
+ }
+ return v;
+ }
+ };
+
+ return fit(f, initialGuess);
+ }
+
+ /**
+ * Fits a Gaussian function to the observed points.
+ *
+ * @return the parameters of the Gaussian function that best fits the
+ * observed points (in the same order as above).
+ */
+ public double[] fit() {
+ final double[] guess = (new ParameterGuesser(getObservations())).guess();
+ return fit(guess);
+ }
+
+ /**
+ * Guesses the parameters {@code norm}, {@code mean}, and {@code sigma}
+ * of a {@link org.apache.commons.math3.analysis.function.Gaussian.Parametric}
+ * based on the specified observed points.
+ */
+ public static class ParameterGuesser {
+ /** Normalization factor. */
+ private final double norm;
+ /** Mean. */
+ private final double mean;
+ /** Standard deviation. */
+ private final double sigma;
+
+ /**
+ * Constructs instance with the specified observed points.
+ *
+ * @param observations Observed points from which to guess the
+ * parameters of the Gaussian.
+ * @throws NullArgumentException if {@code observations} is
+ * {@code null}.
+ * @throws NumberIsTooSmallException if there are less than 3
+ * observations.
+ */
+ public ParameterGuesser(WeightedObservedPoint[] observations) {
+ if (observations == null) {
+ throw new NullArgumentException(LocalizedFormats.INPUT_ARRAY);
+ }
+ if (observations.length < 3) {
+ throw new NumberIsTooSmallException(observations.length, 3, true);
+ }
+
+ final WeightedObservedPoint[] sorted = sortObservations(observations);
+ final double[] params = basicGuess(sorted);
+
+ norm = params[0];
+ mean = params[1];
+ sigma = params[2];
+ }
+
+ /**
+ * Gets an estimation of the parameters.
+ *
+ * @return the guessed parameters, in the following order:
+ * <ul>
+ * <li>Normalization factor</li>
+ * <li>Mean</li>
+ * <li>Standard deviation</li>
+ * </ul>
+ */
+ public double[] guess() {
+ return new double[] { norm, mean, sigma };
+ }
+
+ /**
+ * Sort the observations.
+ *
+ * @param unsorted Input observations.
+ * @return the input observations, sorted.
+ */
+ private WeightedObservedPoint[] sortObservations(WeightedObservedPoint[] unsorted) {
+ final WeightedObservedPoint[] observations = unsorted.clone();
+ final Comparator<WeightedObservedPoint> cmp
+ = new Comparator<WeightedObservedPoint>() {
+ /** {@inheritDoc} */
+ public int compare(WeightedObservedPoint p1,
+ WeightedObservedPoint p2) {
+ if (p1 == null && p2 == null) {
+ return 0;
+ }
+ if (p1 == null) {
+ return -1;
+ }
+ if (p2 == null) {
+ return 1;
+ }
+ final int cmpX = Double.compare(p1.getX(), p2.getX());
+ if (cmpX < 0) {
+ return -1;
+ }
+ if (cmpX > 0) {
+ return 1;
+ }
+ final int cmpY = Double.compare(p1.getY(), p2.getY());
+ if (cmpY < 0) {
+ return -1;
+ }
+ if (cmpY > 0) {
+ return 1;
+ }
+ final int cmpW = Double.compare(p1.getWeight(), p2.getWeight());
+ if (cmpW < 0) {
+ return -1;
+ }
+ if (cmpW > 0) {
+ return 1;
+ }
+ return 0;
+ }
+ };
+
+ Arrays.sort(observations, cmp);
+ return observations;
+ }
+
+ /**
+ * Guesses the parameters based on the specified observed points.
+ *
+ * @param points Observed points, sorted.
+ * @return the guessed parameters (normalization factor, mean and
+ * sigma).
+ */
+ private double[] basicGuess(WeightedObservedPoint[] points) {
+ final int maxYIdx = findMaxY(points);
+ final double n = points[maxYIdx].getY();
+ final double m = points[maxYIdx].getX();
+
+ double fwhmApprox;
+ try {
+ final double halfY = n + ((m - n) / 2);
+ final double fwhmX1 = interpolateXAtY(points, maxYIdx, -1, halfY);
+ final double fwhmX2 = interpolateXAtY(points, maxYIdx, 1, halfY);
+ fwhmApprox = fwhmX2 - fwhmX1;
+ } catch (OutOfRangeException e) {
+ // TODO: Exceptions should not be used for flow control.
+ fwhmApprox = points[points.length - 1].getX() - points[0].getX();
+ }
+ final double s = fwhmApprox / (2 * FastMath.sqrt(2 * FastMath.log(2)));
+
+ return new double[] { n, m, s };
+ }
+
+ /**
+ * Finds index of point in specified points with the largest Y.
+ *
+ * @param points Points to search.
+ * @return the index in specified points array.
+ */
+ private int findMaxY(WeightedObservedPoint[] points) {
+ int maxYIdx = 0;
+ for (int i = 1; i < points.length; i++) {
+ if (points[i].getY() > points[maxYIdx].getY()) {
+ maxYIdx = i;
+ }
+ }
+ return maxYIdx;
+ }
+
+ /**
+ * Interpolates using the specified points to determine X at the
+ * specified Y.
+ *
+ * @param points Points to use for interpolation.
+ * @param startIdx Index within points from which to start the search for
+ * interpolation bounds points.
+ * @param idxStep Index step for searching interpolation bounds points.
+ * @param y Y value for which X should be determined.
+ * @return the value of X for the specified Y.
+ * @throws ZeroException if {@code idxStep} is 0.
+ * @throws OutOfRangeException if specified {@code y} is not within the
+ * range of the specified {@code points}.
+ */
+ private double interpolateXAtY(WeightedObservedPoint[] points,
+ int startIdx,
+ int idxStep,
+ double y)
+ throws OutOfRangeException {
+ if (idxStep == 0) {
+ throw new ZeroException();
+ }
+ final WeightedObservedPoint[] twoPoints
+ = getInterpolationPointsForY(points, startIdx, idxStep, y);
+ final WeightedObservedPoint p1 = twoPoints[0];
+ final WeightedObservedPoint p2 = twoPoints[1];
+ if (p1.getY() == y) {
+ return p1.getX();
+ }
+ if (p2.getY() == y) {
+ return p2.getX();
+ }
+ return p1.getX() + (((y - p1.getY()) * (p2.getX() - p1.getX())) /
+ (p2.getY() - p1.getY()));
+ }
+
+ /**
+ * Gets the two bounding interpolation points from the specified points
+ * suitable for determining X at the specified Y.
+ *
+ * @param points Points to use for interpolation.
+ * @param startIdx Index within points from which to start search for
+ * interpolation bounds points.
+ * @param idxStep Index step for search for interpolation bounds points.
+ * @param y Y value for which X should be determined.
+ * @return the array containing two points suitable for determining X at
+ * the specified Y.
+ * @throws ZeroException if {@code idxStep} is 0.
+ * @throws OutOfRangeException if specified {@code y} is not within the
+ * range of the specified {@code points}.
+ */
+ private WeightedObservedPoint[] getInterpolationPointsForY(WeightedObservedPoint[] points,
+ int startIdx,
+ int idxStep,
+ double y)
+ throws OutOfRangeException {
+ if (idxStep == 0) {
+ throw new ZeroException();
+ }
+ for (int i = startIdx;
+ idxStep < 0 ? i + idxStep >= 0 : i + idxStep < points.length;
+ i += idxStep) {
+ final WeightedObservedPoint p1 = points[i];
+ final WeightedObservedPoint p2 = points[i + idxStep];
+ if (isBetween(y, p1.getY(), p2.getY())) {
+ if (idxStep < 0) {
+ return new WeightedObservedPoint[] { p2, p1 };
+ } else {
+ return new WeightedObservedPoint[] { p1, p2 };
+ }
+ }
+ }
+
+ // Boundaries are replaced by dummy values because the raised
+ // exception is caught and the message never displayed.
+ // TODO: Exceptions should not be used for flow control.
+ throw new OutOfRangeException(y,
+ Double.NEGATIVE_INFINITY,
+ Double.POSITIVE_INFINITY);
+ }
+
+ /**
+ * Determines whether a value is between two other values.
+ *
+ * @param value Value to test whether it is between {@code boundary1}
+ * and {@code boundary2}.
+ * @param boundary1 One end of the range.
+ * @param boundary2 Other end of the range.
+ * @return {@code true} if {@code value} is between {@code boundary1} and
+ * {@code boundary2} (inclusive), {@code false} otherwise.
+ */
+ private boolean isBetween(double value,
+ double boundary1,
+ double boundary2) {
+ return (value >= boundary1 && value <= boundary2) ||
+ (value >= boundary2 && value <= boundary1);
+ }
+ }
+}
diff --git a/src/main/java/org/apache/commons/math3/optimization/fitting/HarmonicFitter.java b/src/main/java/org/apache/commons/math3/optimization/fitting/HarmonicFitter.java
new file mode 100644
index 0000000..85c6d18
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/optimization/fitting/HarmonicFitter.java
@@ -0,0 +1,384 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math3.optimization.fitting;
+
+import org.apache.commons.math3.optimization.DifferentiableMultivariateVectorOptimizer;
+import org.apache.commons.math3.analysis.function.HarmonicOscillator;
+import org.apache.commons.math3.exception.ZeroException;
+import org.apache.commons.math3.exception.NumberIsTooSmallException;
+import org.apache.commons.math3.exception.MathIllegalStateException;
+import org.apache.commons.math3.exception.util.LocalizedFormats;
+import org.apache.commons.math3.util.FastMath;
+
+/**
+ * Class that implements a curve fitting specialized for sinusoids.
+ *
+ * Harmonic fitting is a very simple case of curve fitting. The
+ * estimated coefficients are the amplitude a, the pulsation &omega; and
+ * the phase &phi;: <code>f (t) = a cos (&omega; t + &phi;)</code>. They are
+ * searched by a least square estimator initialized with a rough guess
+ * based on integrals.
+ *
+ * @deprecated As of 3.1 (to be removed in 4.0).
+ * @since 2.0
+ */
+@Deprecated
+public class HarmonicFitter extends CurveFitter<HarmonicOscillator.Parametric> {
+ /**
+ * Simple constructor.
+ * @param optimizer Optimizer to use for the fitting.
+ */
+ public HarmonicFitter(final DifferentiableMultivariateVectorOptimizer optimizer) {
+ super(optimizer);
+ }
+
+ /**
+ * Fit an harmonic function to the observed points.
+ *
+ * @param initialGuess First guess values in the following order:
+ * <ul>
+ * <li>Amplitude</li>
+ * <li>Angular frequency</li>
+ * <li>Phase</li>
+ * </ul>
+ * @return the parameters of the harmonic function that best fits the
+ * observed points (in the same order as above).
+ */
+ public double[] fit(double[] initialGuess) {
+ return fit(new HarmonicOscillator.Parametric(), initialGuess);
+ }
+
+ /**
+ * Fit an harmonic function to the observed points.
+ * An initial guess will be automatically computed.
+ *
+ * @return the parameters of the harmonic function that best fits the
+ * observed points (see the other {@link #fit(double[]) fit} method.
+ * @throws NumberIsTooSmallException if the sample is too short for the
+ * the first guess to be computed.
+ * @throws ZeroException if the first guess cannot be computed because
+ * the abscissa range is zero.
+ */
+ public double[] fit() {
+ return fit((new ParameterGuesser(getObservations())).guess());
+ }
+
+ /**
+ * This class guesses harmonic coefficients from a sample.
+ * <p>The algorithm used to guess the coefficients is as follows:</p>
+ *
+ * <p>We know f (t) at some sampling points t<sub>i</sub> and want to find a,
+ * &omega; and &phi; such that f (t) = a cos (&omega; t + &phi;).
+ * </p>
+ *
+ * <p>From the analytical expression, we can compute two primitives :
+ * <pre>
+ * If2 (t) = &int; f<sup>2</sup> = a<sup>2</sup> &times; [t + S (t)] / 2
+ * If'2 (t) = &int; f'<sup>2</sup> = a<sup>2</sup> &omega;<sup>2</sup> &times; [t - S (t)] / 2
+ * where S (t) = sin (2 (&omega; t + &phi;)) / (2 &omega;)
+ * </pre>
+ * </p>
+ *
+ * <p>We can remove S between these expressions :
+ * <pre>
+ * If'2 (t) = a<sup>2</sup> &omega;<sup>2</sup> t - &omega;<sup>2</sup> If2 (t)
+ * </pre>
+ * </p>
+ *
+ * <p>The preceding expression shows that If'2 (t) is a linear
+ * combination of both t and If2 (t): If'2 (t) = A &times; t + B &times; If2 (t)
+ * </p>
+ *
+ * <p>From the primitive, we can deduce the same form for definite
+ * integrals between t<sub>1</sub> and t<sub>i</sub> for each t<sub>i</sub> :
+ * <pre>
+ * If2 (t<sub>i</sub>) - If2 (t<sub>1</sub>) = A &times; (t<sub>i</sub> - t<sub>1</sub>) + B &times; (If2 (t<sub>i</sub>) - If2 (t<sub>1</sub>))
+ * </pre>
+ * </p>
+ *
+ * <p>We can find the coefficients A and B that best fit the sample
+ * to this linear expression by computing the definite integrals for
+ * each sample points.
+ * </p>
+ *
+ * <p>For a bilinear expression z (x<sub>i</sub>, y<sub>i</sub>) = A &times; x<sub>i</sub> + B &times; y<sub>i</sub>, the
+ * coefficients A and B that minimize a least square criterion
+ * &sum; (z<sub>i</sub> - z (x<sub>i</sub>, y<sub>i</sub>))<sup>2</sup> are given by these expressions:</p>
+ * <pre>
+ *
+ * &sum;y<sub>i</sub>y<sub>i</sub> &sum;x<sub>i</sub>z<sub>i</sub> - &sum;x<sub>i</sub>y<sub>i</sub> &sum;y<sub>i</sub>z<sub>i</sub>
+ * A = ------------------------
+ * &sum;x<sub>i</sub>x<sub>i</sub> &sum;y<sub>i</sub>y<sub>i</sub> - &sum;x<sub>i</sub>y<sub>i</sub> &sum;x<sub>i</sub>y<sub>i</sub>
+ *
+ * &sum;x<sub>i</sub>x<sub>i</sub> &sum;y<sub>i</sub>z<sub>i</sub> - &sum;x<sub>i</sub>y<sub>i</sub> &sum;x<sub>i</sub>z<sub>i</sub>
+ * B = ------------------------
+ * &sum;x<sub>i</sub>x<sub>i</sub> &sum;y<sub>i</sub>y<sub>i</sub> - &sum;x<sub>i</sub>y<sub>i</sub> &sum;x<sub>i</sub>y<sub>i</sub>
+ * </pre>
+ * </p>
+ *
+ *
+ * <p>In fact, we can assume both a and &omega; are positive and
+ * compute them directly, knowing that A = a<sup>2</sup> &omega;<sup>2</sup> and that
+ * B = - &omega;<sup>2</sup>. The complete algorithm is therefore:</p>
+ * <pre>
+ *
+ * for each t<sub>i</sub> from t<sub>1</sub> to t<sub>n-1</sub>, compute:
+ * f (t<sub>i</sub>)
+ * f' (t<sub>i</sub>) = (f (t<sub>i+1</sub>) - f(t<sub>i-1</sub>)) / (t<sub>i+1</sub> - t<sub>i-1</sub>)
+ * x<sub>i</sub> = t<sub>i</sub> - t<sub>1</sub>
+ * y<sub>i</sub> = &int; f<sup>2</sup> from t<sub>1</sub> to t<sub>i</sub>
+ * z<sub>i</sub> = &int; f'<sup>2</sup> from t<sub>1</sub> to t<sub>i</sub>
+ * update the sums &sum;x<sub>i</sub>x<sub>i</sub>, &sum;y<sub>i</sub>y<sub>i</sub>, &sum;x<sub>i</sub>y<sub>i</sub>, &sum;x<sub>i</sub>z<sub>i</sub> and &sum;y<sub>i</sub>z<sub>i</sub>
+ * end for
+ *
+ * |--------------------------
+ * \ | &sum;y<sub>i</sub>y<sub>i</sub> &sum;x<sub>i</sub>z<sub>i</sub> - &sum;x<sub>i</sub>y<sub>i</sub> &sum;y<sub>i</sub>z<sub>i</sub>
+ * a = \ | ------------------------
+ * \| &sum;x<sub>i</sub>y<sub>i</sub> &sum;x<sub>i</sub>z<sub>i</sub> - &sum;x<sub>i</sub>x<sub>i</sub> &sum;y<sub>i</sub>z<sub>i</sub>
+ *
+ *
+ * |--------------------------
+ * \ | &sum;x<sub>i</sub>y<sub>i</sub> &sum;x<sub>i</sub>z<sub>i</sub> - &sum;x<sub>i</sub>x<sub>i</sub> &sum;y<sub>i</sub>z<sub>i</sub>
+ * &omega; = \ | ------------------------
+ * \| &sum;x<sub>i</sub>x<sub>i</sub> &sum;y<sub>i</sub>y<sub>i</sub> - &sum;x<sub>i</sub>y<sub>i</sub> &sum;x<sub>i</sub>y<sub>i</sub>
+ *
+ * </pre>
+ * </p>
+ *
+ * <p>Once we know &omega;, we can compute:
+ * <pre>
+ * fc = &omega; f (t) cos (&omega; t) - f' (t) sin (&omega; t)
+ * fs = &omega; f (t) sin (&omega; t) + f' (t) cos (&omega; t)
+ * </pre>
+ * </p>
+ *
+ * <p>It appears that <code>fc = a &omega; cos (&phi;)</code> and
+ * <code>fs = -a &omega; sin (&phi;)</code>, so we can use these
+ * expressions to compute &phi;. The best estimate over the sample is
+ * given by averaging these expressions.
+ * </p>
+ *
+ * <p>Since integrals and means are involved in the preceding
+ * estimations, these operations run in O(n) time, where n is the
+ * number of measurements.</p>
+ */
+ public static class ParameterGuesser {
+ /** Amplitude. */
+ private final double a;
+ /** Angular frequency. */
+ private final double omega;
+ /** Phase. */
+ private final double phi;
+
+ /**
+ * Simple constructor.
+ *
+ * @param observations Sampled observations.
+ * @throws NumberIsTooSmallException if the sample is too short.
+ * @throws ZeroException if the abscissa range is zero.
+ * @throws MathIllegalStateException when the guessing procedure cannot
+ * produce sensible results.
+ */
+ public ParameterGuesser(WeightedObservedPoint[] observations) {
+ if (observations.length < 4) {
+ throw new NumberIsTooSmallException(LocalizedFormats.INSUFFICIENT_OBSERVED_POINTS_IN_SAMPLE,
+ observations.length, 4, true);
+ }
+
+ final WeightedObservedPoint[] sorted = sortObservations(observations);
+
+ final double aOmega[] = guessAOmega(sorted);
+ a = aOmega[0];
+ omega = aOmega[1];
+
+ phi = guessPhi(sorted);
+ }
+
+ /**
+ * Gets an estimation of the parameters.
+ *
+ * @return the guessed parameters, in the following order:
+ * <ul>
+ * <li>Amplitude</li>
+ * <li>Angular frequency</li>
+ * <li>Phase</li>
+ * </ul>
+ */
+ public double[] guess() {
+ return new double[] { a, omega, phi };
+ }
+
+ /**
+ * Sort the observations with respect to the abscissa.
+ *
+ * @param unsorted Input observations.
+ * @return the input observations, sorted.
+ */
+ private WeightedObservedPoint[] sortObservations(WeightedObservedPoint[] unsorted) {
+ final WeightedObservedPoint[] observations = unsorted.clone();
+
+ // Since the samples are almost always already sorted, this
+ // method is implemented as an insertion sort that reorders the
+ // elements in place. Insertion sort is very efficient in this case.
+ WeightedObservedPoint curr = observations[0];
+ for (int j = 1; j < observations.length; ++j) {
+ WeightedObservedPoint prec = curr;
+ curr = observations[j];
+ if (curr.getX() < prec.getX()) {
+ // the current element should be inserted closer to the beginning
+ int i = j - 1;
+ WeightedObservedPoint mI = observations[i];
+ while ((i >= 0) && (curr.getX() < mI.getX())) {
+ observations[i + 1] = mI;
+ if (i-- != 0) {
+ mI = observations[i];
+ }
+ }
+ observations[i + 1] = curr;
+ curr = observations[j];
+ }
+ }
+
+ return observations;
+ }
+
+ /**
+ * Estimate a first guess of the amplitude and angular frequency.
+ * This method assumes that the {@link #sortObservations(WeightedObservedPoint[])} method
+ * has been called previously.
+ *
+ * @param observations Observations, sorted w.r.t. abscissa.
+ * @throws ZeroException if the abscissa range is zero.
+ * @throws MathIllegalStateException when the guessing procedure cannot
+ * produce sensible results.
+ * @return the guessed amplitude (at index 0) and circular frequency
+ * (at index 1).
+ */
+ private double[] guessAOmega(WeightedObservedPoint[] observations) {
+ final double[] aOmega = new double[2];
+
+ // initialize the sums for the linear model between the two integrals
+ double sx2 = 0;
+ double sy2 = 0;
+ double sxy = 0;
+ double sxz = 0;
+ double syz = 0;
+
+ double currentX = observations[0].getX();
+ double currentY = observations[0].getY();
+ double f2Integral = 0;
+ double fPrime2Integral = 0;
+ final double startX = currentX;
+ for (int i = 1; i < observations.length; ++i) {
+ // one step forward
+ final double previousX = currentX;
+ final double previousY = currentY;
+ currentX = observations[i].getX();
+ currentY = observations[i].getY();
+
+ // update the integrals of f<sup>2</sup> and f'<sup>2</sup>
+ // considering a linear model for f (and therefore constant f')
+ final double dx = currentX - previousX;
+ final double dy = currentY - previousY;
+ final double f2StepIntegral =
+ dx * (previousY * previousY + previousY * currentY + currentY * currentY) / 3;
+ final double fPrime2StepIntegral = dy * dy / dx;
+
+ final double x = currentX - startX;
+ f2Integral += f2StepIntegral;
+ fPrime2Integral += fPrime2StepIntegral;
+
+ sx2 += x * x;
+ sy2 += f2Integral * f2Integral;
+ sxy += x * f2Integral;
+ sxz += x * fPrime2Integral;
+ syz += f2Integral * fPrime2Integral;
+ }
+
+ // compute the amplitude and pulsation coefficients
+ double c1 = sy2 * sxz - sxy * syz;
+ double c2 = sxy * sxz - sx2 * syz;
+ double c3 = sx2 * sy2 - sxy * sxy;
+ if ((c1 / c2 < 0) || (c2 / c3 < 0)) {
+ final int last = observations.length - 1;
+ // Range of the observations, assuming that the
+ // observations are sorted.
+ final double xRange = observations[last].getX() - observations[0].getX();
+ if (xRange == 0) {
+ throw new ZeroException();
+ }
+ aOmega[1] = 2 * Math.PI / xRange;
+
+ double yMin = Double.POSITIVE_INFINITY;
+ double yMax = Double.NEGATIVE_INFINITY;
+ for (int i = 1; i < observations.length; ++i) {
+ final double y = observations[i].getY();
+ if (y < yMin) {
+ yMin = y;
+ }
+ if (y > yMax) {
+ yMax = y;
+ }
+ }
+ aOmega[0] = 0.5 * (yMax - yMin);
+ } else {
+ if (c2 == 0) {
+ // In some ill-conditioned cases (cf. MATH-844), the guesser
+ // procedure cannot produce sensible results.
+ throw new MathIllegalStateException(LocalizedFormats.ZERO_DENOMINATOR);
+ }
+
+ aOmega[0] = FastMath.sqrt(c1 / c2);
+ aOmega[1] = FastMath.sqrt(c2 / c3);
+ }
+
+ return aOmega;
+ }
+
+ /**
+ * Estimate a first guess of the phase.
+ *
+ * @param observations Observations, sorted w.r.t. abscissa.
+ * @return the guessed phase.
+ */
+ private double guessPhi(WeightedObservedPoint[] observations) {
+ // initialize the means
+ double fcMean = 0;
+ double fsMean = 0;
+
+ double currentX = observations[0].getX();
+ double currentY = observations[0].getY();
+ for (int i = 1; i < observations.length; ++i) {
+ // one step forward
+ final double previousX = currentX;
+ final double previousY = currentY;
+ currentX = observations[i].getX();
+ currentY = observations[i].getY();
+ final double currentYPrime = (currentY - previousY) / (currentX - previousX);
+
+ double omegaX = omega * currentX;
+ double cosine = FastMath.cos(omegaX);
+ double sine = FastMath.sin(omegaX);
+ fcMean += omega * currentY * cosine - currentYPrime * sine;
+ fsMean += omega * currentY * sine + currentYPrime * cosine;
+ }
+
+ return FastMath.atan2(-fsMean, fcMean);
+ }
+ }
+}
diff --git a/src/main/java/org/apache/commons/math3/optimization/fitting/PolynomialFitter.java b/src/main/java/org/apache/commons/math3/optimization/fitting/PolynomialFitter.java
new file mode 100644
index 0000000..dbefcc2
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/optimization/fitting/PolynomialFitter.java
@@ -0,0 +1,111 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math3.optimization.fitting;
+
+import org.apache.commons.math3.analysis.polynomials.PolynomialFunction;
+import org.apache.commons.math3.optimization.DifferentiableMultivariateVectorOptimizer;
+
+/**
+ * Polynomial fitting is a very simple case of {@link CurveFitter curve fitting}.
+ * The estimated coefficients are the polynomial coefficients (see the
+ * {@link #fit(double[]) fit} method).
+ *
+ * @deprecated As of 3.1 (to be removed in 4.0).
+ * @since 2.0
+ */
+@Deprecated
+public class PolynomialFitter extends CurveFitter<PolynomialFunction.Parametric> {
+ /** Polynomial degree.
+ * @deprecated
+ */
+ @Deprecated
+ private final int degree;
+
+ /**
+ * Simple constructor.
+ * <p>The polynomial fitter built this way are complete polynomials,
+ * ie. a n-degree polynomial has n+1 coefficients.</p>
+ *
+ * @param degree Maximal degree of the polynomial.
+ * @param optimizer Optimizer to use for the fitting.
+ * @deprecated Since 3.1 (to be removed in 4.0). Please use
+ * {@link #PolynomialFitter(DifferentiableMultivariateVectorOptimizer)} instead.
+ */
+ @Deprecated
+ public PolynomialFitter(int degree, final DifferentiableMultivariateVectorOptimizer optimizer) {
+ super(optimizer);
+ this.degree = degree;
+ }
+
+ /**
+ * Simple constructor.
+ *
+ * @param optimizer Optimizer to use for the fitting.
+ * @since 3.1
+ */
+ public PolynomialFitter(DifferentiableMultivariateVectorOptimizer optimizer) {
+ super(optimizer);
+ degree = -1; // To avoid compilation error until the instance variable is removed.
+ }
+
+ /**
+ * Get the polynomial fitting the weighted (x, y) points.
+ *
+ * @return the coefficients of the polynomial that best fits the observed points.
+ * @throws org.apache.commons.math3.exception.ConvergenceException
+ * if the algorithm failed to converge.
+ * @deprecated Since 3.1 (to be removed in 4.0). Please use {@link #fit(double[])} instead.
+ */
+ @Deprecated
+ public double[] fit() {
+ return fit(new PolynomialFunction.Parametric(), new double[degree + 1]);
+ }
+
+ /**
+ * Get the coefficients of the polynomial fitting the weighted data points.
+ * The degree of the fitting polynomial is {@code guess.length - 1}.
+ *
+ * @param guess First guess for the coefficients. They must be sorted in
+ * increasing order of the polynomial's degree.
+ * @param maxEval Maximum number of evaluations of the polynomial.
+ * @return the coefficients of the polynomial that best fits the observed points.
+ * @throws org.apache.commons.math3.exception.TooManyEvaluationsException if
+ * the number of evaluations exceeds {@code maxEval}.
+ * @throws org.apache.commons.math3.exception.ConvergenceException
+ * if the algorithm failed to converge.
+ * @since 3.1
+ */
+ public double[] fit(int maxEval, double[] guess) {
+ return fit(maxEval, new PolynomialFunction.Parametric(), guess);
+ }
+
+ /**
+ * Get the coefficients of the polynomial fitting the weighted data points.
+ * The degree of the fitting polynomial is {@code guess.length - 1}.
+ *
+ * @param guess First guess for the coefficients. They must be sorted in
+ * increasing order of the polynomial's degree.
+ * @return the coefficients of the polynomial that best fits the observed points.
+ * @throws org.apache.commons.math3.exception.ConvergenceException
+ * if the algorithm failed to converge.
+ * @since 3.1
+ */
+ public double[] fit(double[] guess) {
+ return fit(new PolynomialFunction.Parametric(), guess);
+ }
+}
diff --git a/src/main/java/org/apache/commons/math3/optimization/fitting/WeightedObservedPoint.java b/src/main/java/org/apache/commons/math3/optimization/fitting/WeightedObservedPoint.java
new file mode 100644
index 0000000..899a502
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/optimization/fitting/WeightedObservedPoint.java
@@ -0,0 +1,76 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math3.optimization.fitting;
+
+import java.io.Serializable;
+
+/** This class is a simple container for weighted observed point in
+ * {@link CurveFitter curve fitting}.
+ * <p>Instances of this class are guaranteed to be immutable.</p>
+ * @deprecated As of 3.1 (to be removed in 4.0).
+ * @since 2.0
+ */
+@Deprecated
+public class WeightedObservedPoint implements Serializable {
+
+ /** Serializable version id. */
+ private static final long serialVersionUID = 5306874947404636157L;
+
+ /** Weight of the measurement in the fitting process. */
+ private final double weight;
+
+ /** Abscissa of the point. */
+ private final double x;
+
+ /** Observed value of the function at x. */
+ private final double y;
+
+ /** Simple constructor.
+ * @param weight weight of the measurement in the fitting process
+ * @param x abscissa of the measurement
+ * @param y ordinate of the measurement
+ */
+ public WeightedObservedPoint(final double weight, final double x, final double y) {
+ this.weight = weight;
+ this.x = x;
+ this.y = y;
+ }
+
+ /** Get the weight of the measurement in the fitting process.
+ * @return weight of the measurement in the fitting process
+ */
+ public double getWeight() {
+ return weight;
+ }
+
+ /** Get the abscissa of the point.
+ * @return abscissa of the point
+ */
+ public double getX() {
+ return x;
+ }
+
+ /** Get the observed value of the function at x.
+ * @return observed value of the function at x
+ */
+ public double getY() {
+ return y;
+ }
+
+}
+
diff --git a/src/main/java/org/apache/commons/math3/optimization/fitting/package-info.java b/src/main/java/org/apache/commons/math3/optimization/fitting/package-info.java
new file mode 100644
index 0000000..b25e5fd
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/optimization/fitting/package-info.java
@@ -0,0 +1,30 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/**
+ *
+ * This package provides classes to perform curve fitting.
+ *
+ * <p>Curve fitting is a special case of a least squares problem
+ * were the parameters are the coefficients of a function <code>f</code>
+ * whose graph <code>y=f(x)</code> should pass through sample points, and
+ * were the objective function is the squared sum of residuals
+ * <code>f(x<sub>i</sub>)-y<sub>i</sub></code> for observed points
+ * (x<sub>i</sub>, y<sub>i</sub>).</p>
+ *
+ *
+ */
+package org.apache.commons.math3.optimization.fitting;
diff --git a/src/main/java/org/apache/commons/math3/optimization/general/AbstractDifferentiableOptimizer.java b/src/main/java/org/apache/commons/math3/optimization/general/AbstractDifferentiableOptimizer.java
new file mode 100644
index 0000000..d175863
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/optimization/general/AbstractDifferentiableOptimizer.java
@@ -0,0 +1,90 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math3.optimization.general;
+
+import org.apache.commons.math3.analysis.MultivariateVectorFunction;
+import org.apache.commons.math3.analysis.differentiation.GradientFunction;
+import org.apache.commons.math3.analysis.differentiation.MultivariateDifferentiableFunction;
+import org.apache.commons.math3.optimization.ConvergenceChecker;
+import org.apache.commons.math3.optimization.GoalType;
+import org.apache.commons.math3.optimization.OptimizationData;
+import org.apache.commons.math3.optimization.InitialGuess;
+import org.apache.commons.math3.optimization.PointValuePair;
+import org.apache.commons.math3.optimization.direct.BaseAbstractMultivariateOptimizer;
+
+/**
+ * Base class for implementing optimizers for multivariate scalar
+ * differentiable functions.
+ * It contains boiler-plate code for dealing with gradient evaluation.
+ *
+ * @deprecated As of 3.1 (to be removed in 4.0).
+ * @since 3.1
+ */
+@Deprecated
+public abstract class AbstractDifferentiableOptimizer
+ extends BaseAbstractMultivariateOptimizer<MultivariateDifferentiableFunction> {
+ /**
+ * Objective function gradient.
+ */
+ private MultivariateVectorFunction gradient;
+
+ /**
+ * @param checker Convergence checker.
+ */
+ protected AbstractDifferentiableOptimizer(ConvergenceChecker<PointValuePair> checker) {
+ super(checker);
+ }
+
+ /**
+ * Compute the gradient vector.
+ *
+ * @param evaluationPoint Point at which the gradient must be evaluated.
+ * @return the gradient at the specified point.
+ */
+ protected double[] computeObjectiveGradient(final double[] evaluationPoint) {
+ return gradient.value(evaluationPoint);
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @deprecated In 3.1. Please use
+ * {@link #optimizeInternal(int,MultivariateDifferentiableFunction,GoalType,OptimizationData[])}
+ * instead.
+ */
+ @Override@Deprecated
+ protected PointValuePair optimizeInternal(final int maxEval,
+ final MultivariateDifferentiableFunction f,
+ final GoalType goalType,
+ final double[] startPoint) {
+ return optimizeInternal(maxEval, f, goalType, new InitialGuess(startPoint));
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ protected PointValuePair optimizeInternal(final int maxEval,
+ final MultivariateDifferentiableFunction f,
+ final GoalType goalType,
+ final OptimizationData... optData) {
+ // Store optimization problem characteristics.
+ gradient = new GradientFunction(f);
+
+ // Perform optimization.
+ return super.optimizeInternal(maxEval, f, goalType, optData);
+ }
+}
diff --git a/src/main/java/org/apache/commons/math3/optimization/general/AbstractLeastSquaresOptimizer.java b/src/main/java/org/apache/commons/math3/optimization/general/AbstractLeastSquaresOptimizer.java
new file mode 100644
index 0000000..96f7fb2
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/optimization/general/AbstractLeastSquaresOptimizer.java
@@ -0,0 +1,577 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math3.optimization.general;
+
+import org.apache.commons.math3.analysis.DifferentiableMultivariateVectorFunction;
+import org.apache.commons.math3.analysis.FunctionUtils;
+import org.apache.commons.math3.analysis.differentiation.DerivativeStructure;
+import org.apache.commons.math3.analysis.differentiation.MultivariateDifferentiableVectorFunction;
+import org.apache.commons.math3.exception.DimensionMismatchException;
+import org.apache.commons.math3.exception.NumberIsTooSmallException;
+import org.apache.commons.math3.exception.util.LocalizedFormats;
+import org.apache.commons.math3.linear.ArrayRealVector;
+import org.apache.commons.math3.linear.RealMatrix;
+import org.apache.commons.math3.linear.DiagonalMatrix;
+import org.apache.commons.math3.linear.DecompositionSolver;
+import org.apache.commons.math3.linear.MatrixUtils;
+import org.apache.commons.math3.linear.QRDecomposition;
+import org.apache.commons.math3.linear.EigenDecomposition;
+import org.apache.commons.math3.optimization.OptimizationData;
+import org.apache.commons.math3.optimization.InitialGuess;
+import org.apache.commons.math3.optimization.Target;
+import org.apache.commons.math3.optimization.Weight;
+import org.apache.commons.math3.optimization.ConvergenceChecker;
+import org.apache.commons.math3.optimization.DifferentiableMultivariateVectorOptimizer;
+import org.apache.commons.math3.optimization.PointVectorValuePair;
+import org.apache.commons.math3.optimization.direct.BaseAbstractMultivariateVectorOptimizer;
+import org.apache.commons.math3.util.FastMath;
+
+/**
+ * Base class for implementing least squares optimizers.
+ * It handles the boilerplate methods associated to thresholds settings,
+ * Jacobian and error estimation.
+ * <br/>
+ * This class constructs the Jacobian matrix of the function argument in method
+ * {@link BaseAbstractMultivariateVectorOptimizer#optimize(int,
+ * org.apache.commons.math3.analysis.MultivariateVectorFunction,OptimizationData[])
+ * optimize} and assumes that the rows of that matrix iterate on the model
+ * functions while the columns iterate on the parameters; thus, the numbers
+ * of rows is equal to the dimension of the
+ * {@link org.apache.commons.math3.optimization.Target Target} while
+ * the number of columns is equal to the dimension of the
+ * {@link org.apache.commons.math3.optimization.InitialGuess InitialGuess}.
+ *
+ * @deprecated As of 3.1 (to be removed in 4.0).
+ * @since 1.2
+ */
+@Deprecated
+public abstract class AbstractLeastSquaresOptimizer
+ extends BaseAbstractMultivariateVectorOptimizer<DifferentiableMultivariateVectorFunction>
+ implements DifferentiableMultivariateVectorOptimizer {
+ /**
+ * Singularity threshold (cf. {@link #getCovariances(double)}).
+ * @deprecated As of 3.1.
+ */
+ @Deprecated
+ private static final double DEFAULT_SINGULARITY_THRESHOLD = 1e-14;
+ /**
+ * Jacobian matrix of the weighted residuals.
+ * This matrix is in canonical form just after the calls to
+ * {@link #updateJacobian()}, but may be modified by the solver
+ * in the derived class (the {@link LevenbergMarquardtOptimizer
+ * Levenberg-Marquardt optimizer} does this).
+ * @deprecated As of 3.1. To be removed in 4.0. Please use
+ * {@link #computeWeightedJacobian(double[])} instead.
+ */
+ @Deprecated
+ protected double[][] weightedResidualJacobian;
+ /** Number of columns of the jacobian matrix.
+ * @deprecated As of 3.1.
+ */
+ @Deprecated
+ protected int cols;
+ /** Number of rows of the jacobian matrix.
+ * @deprecated As of 3.1.
+ */
+ @Deprecated
+ protected int rows;
+ /** Current point.
+ * @deprecated As of 3.1.
+ */
+ @Deprecated
+ protected double[] point;
+ /** Current objective function value.
+ * @deprecated As of 3.1.
+ */
+ @Deprecated
+ protected double[] objective;
+ /** Weighted residuals
+ * @deprecated As of 3.1.
+ */
+ @Deprecated
+ protected double[] weightedResiduals;
+ /** Cost value (square root of the sum of the residuals).
+ * @deprecated As of 3.1. Field to become "private" in 4.0.
+ * Please use {@link #setCost(double)}.
+ */
+ @Deprecated
+ protected double cost;
+ /** Objective function derivatives. */
+ private MultivariateDifferentiableVectorFunction jF;
+ /** Number of evaluations of the Jacobian. */
+ private int jacobianEvaluations;
+ /** Square-root of the weight matrix. */
+ private RealMatrix weightMatrixSqrt;
+
+ /**
+ * Simple constructor with default settings.
+ * The convergence check is set to a {@link
+ * org.apache.commons.math3.optimization.SimpleVectorValueChecker}.
+ * @deprecated See {@link org.apache.commons.math3.optimization.SimpleValueChecker#SimpleValueChecker()}
+ */
+ @Deprecated
+ protected AbstractLeastSquaresOptimizer() {}
+
+ /**
+ * @param checker Convergence checker.
+ */
+ protected AbstractLeastSquaresOptimizer(ConvergenceChecker<PointVectorValuePair> checker) {
+ super(checker);
+ }
+
+ /**
+ * @return the number of evaluations of the Jacobian function.
+ */
+ public int getJacobianEvaluations() {
+ return jacobianEvaluations;
+ }
+
+ /**
+ * Update the jacobian matrix.
+ *
+ * @throws DimensionMismatchException if the Jacobian dimension does not
+ * match problem dimension.
+ * @deprecated As of 3.1. Please use {@link #computeWeightedJacobian(double[])}
+ * instead.
+ */
+ @Deprecated
+ protected void updateJacobian() {
+ final RealMatrix weightedJacobian = computeWeightedJacobian(point);
+ weightedResidualJacobian = weightedJacobian.scalarMultiply(-1).getData();
+ }
+
+ /**
+ * Computes the Jacobian matrix.
+ *
+ * @param params Model parameters at which to compute the Jacobian.
+ * @return the weighted Jacobian: W<sup>1/2</sup> J.
+ * @throws DimensionMismatchException if the Jacobian dimension does not
+ * match problem dimension.
+ * @since 3.1
+ */
+ protected RealMatrix computeWeightedJacobian(double[] params) {
+ ++jacobianEvaluations;
+
+ final DerivativeStructure[] dsPoint = new DerivativeStructure[params.length];
+ final int nC = params.length;
+ for (int i = 0; i < nC; ++i) {
+ dsPoint[i] = new DerivativeStructure(nC, 1, i, params[i]);
+ }
+ final DerivativeStructure[] dsValue = jF.value(dsPoint);
+ final int nR = getTarget().length;
+ if (dsValue.length != nR) {
+ throw new DimensionMismatchException(dsValue.length, nR);
+ }
+ final double[][] jacobianData = new double[nR][nC];
+ for (int i = 0; i < nR; ++i) {
+ int[] orders = new int[nC];
+ for (int j = 0; j < nC; ++j) {
+ orders[j] = 1;
+ jacobianData[i][j] = dsValue[i].getPartialDerivative(orders);
+ orders[j] = 0;
+ }
+ }
+
+ return weightMatrixSqrt.multiply(MatrixUtils.createRealMatrix(jacobianData));
+ }
+
+ /**
+ * Update the residuals array and cost function value.
+ * @throws DimensionMismatchException if the dimension does not match the
+ * problem dimension.
+ * @throws org.apache.commons.math3.exception.TooManyEvaluationsException
+ * if the maximal number of evaluations is exceeded.
+ * @deprecated As of 3.1. Please use {@link #computeResiduals(double[])},
+ * {@link #computeObjectiveValue(double[])}, {@link #computeCost(double[])}
+ * and {@link #setCost(double)} instead.
+ */
+ @Deprecated
+ protected void updateResidualsAndCost() {
+ objective = computeObjectiveValue(point);
+ final double[] res = computeResiduals(objective);
+
+ // Compute cost.
+ cost = computeCost(res);
+
+ // Compute weighted residuals.
+ final ArrayRealVector residuals = new ArrayRealVector(res);
+ weightedResiduals = weightMatrixSqrt.operate(residuals).toArray();
+ }
+
+ /**
+ * Computes the cost.
+ *
+ * @param residuals Residuals.
+ * @return the cost.
+ * @see #computeResiduals(double[])
+ * @since 3.1
+ */
+ protected double computeCost(double[] residuals) {
+ final ArrayRealVector r = new ArrayRealVector(residuals);
+ return FastMath.sqrt(r.dotProduct(getWeight().operate(r)));
+ }
+
+ /**
+ * Get the Root Mean Square value.
+ * Get the Root Mean Square value, i.e. the root of the arithmetic
+ * mean of the square of all weighted residuals. This is related to the
+ * criterion that is minimized by the optimizer as follows: if
+ * <em>c</em> if the criterion, and <em>n</em> is the number of
+ * measurements, then the RMS is <em>sqrt (c/n)</em>.
+ *
+ * @return RMS value
+ */
+ public double getRMS() {
+ return FastMath.sqrt(getChiSquare() / rows);
+ }
+
+ /**
+ * Get a Chi-Square-like value assuming the N residuals follow N
+ * distinct normal distributions centered on 0 and whose variances are
+ * the reciprocal of the weights.
+ * @return chi-square value
+ */
+ public double getChiSquare() {
+ return cost * cost;
+ }
+
+ /**
+ * Gets the square-root of the weight matrix.
+ *
+ * @return the square-root of the weight matrix.
+ * @since 3.1
+ */
+ public RealMatrix getWeightSquareRoot() {
+ return weightMatrixSqrt.copy();
+ }
+
+ /**
+ * Sets the cost.
+ *
+ * @param cost Cost value.
+ * @since 3.1
+ */
+ protected void setCost(double cost) {
+ this.cost = cost;
+ }
+
+ /**
+ * Get the covariance matrix of the optimized parameters.
+ *
+ * @return the covariance matrix.
+ * @throws org.apache.commons.math3.linear.SingularMatrixException
+ * if the covariance matrix cannot be computed (singular problem).
+ * @see #getCovariances(double)
+ * @deprecated As of 3.1. Please use {@link #computeCovariances(double[],double)}
+ * instead.
+ */
+ @Deprecated
+ public double[][] getCovariances() {
+ return getCovariances(DEFAULT_SINGULARITY_THRESHOLD);
+ }
+
+ /**
+ * Get the covariance matrix of the optimized parameters.
+ * <br/>
+ * Note that this operation involves the inversion of the
+ * <code>J<sup>T</sup>J</code> matrix, where {@code J} is the
+ * Jacobian matrix.
+ * The {@code threshold} parameter is a way for the caller to specify
+ * that the result of this computation should be considered meaningless,
+ * and thus trigger an exception.
+ *
+ * @param threshold Singularity threshold.
+ * @return the covariance matrix.
+ * @throws org.apache.commons.math3.linear.SingularMatrixException
+ * if the covariance matrix cannot be computed (singular problem).
+ * @deprecated As of 3.1. Please use {@link #computeCovariances(double[],double)}
+ * instead.
+ */
+ @Deprecated
+ public double[][] getCovariances(double threshold) {
+ return computeCovariances(point, threshold);
+ }
+
+ /**
+ * Get the covariance matrix of the optimized parameters.
+ * <br/>
+ * Note that this operation involves the inversion of the
+ * <code>J<sup>T</sup>J</code> matrix, where {@code J} is the
+ * Jacobian matrix.
+ * The {@code threshold} parameter is a way for the caller to specify
+ * that the result of this computation should be considered meaningless,
+ * and thus trigger an exception.
+ *
+ * @param params Model parameters.
+ * @param threshold Singularity threshold.
+ * @return the covariance matrix.
+ * @throws org.apache.commons.math3.linear.SingularMatrixException
+ * if the covariance matrix cannot be computed (singular problem).
+ * @since 3.1
+ */
+ public double[][] computeCovariances(double[] params,
+ double threshold) {
+ // Set up the Jacobian.
+ final RealMatrix j = computeWeightedJacobian(params);
+
+ // Compute transpose(J)J.
+ final RealMatrix jTj = j.transpose().multiply(j);
+
+ // Compute the covariances matrix.
+ final DecompositionSolver solver
+ = new QRDecomposition(jTj, threshold).getSolver();
+ return solver.getInverse().getData();
+ }
+
+ /**
+ * <p>
+ * Returns an estimate of the standard deviation of each parameter. The
+ * returned values are the so-called (asymptotic) standard errors on the
+ * parameters, defined as {@code sd(a[i]) = sqrt(S / (n - m) * C[i][i])},
+ * where {@code a[i]} is the optimized value of the {@code i}-th parameter,
+ * {@code S} is the minimized value of the sum of squares objective function
+ * (as returned by {@link #getChiSquare()}), {@code n} is the number of
+ * observations, {@code m} is the number of parameters and {@code C} is the
+ * covariance matrix.
+ * </p>
+ * <p>
+ * See also
+ * <a href="http://en.wikipedia.org/wiki/Least_squares">Wikipedia</a>,
+ * or
+ * <a href="http://mathworld.wolfram.com/LeastSquaresFitting.html">MathWorld</a>,
+ * equations (34) and (35) for a particular case.
+ * </p>
+ *
+ * @return an estimate of the standard deviation of the optimized parameters
+ * @throws org.apache.commons.math3.linear.SingularMatrixException
+ * if the covariance matrix cannot be computed.
+ * @throws NumberIsTooSmallException if the number of degrees of freedom is not
+ * positive, i.e. the number of measurements is less or equal to the number of
+ * parameters.
+ * @deprecated as of version 3.1, {@link #computeSigma(double[],double)} should be used
+ * instead. It should be emphasized that {@code guessParametersErrors} and
+ * {@code computeSigma} are <em>not</em> strictly equivalent.
+ */
+ @Deprecated
+ public double[] guessParametersErrors() {
+ if (rows <= cols) {
+ throw new NumberIsTooSmallException(LocalizedFormats.NO_DEGREES_OF_FREEDOM,
+ rows, cols, false);
+ }
+ double[] errors = new double[cols];
+ final double c = FastMath.sqrt(getChiSquare() / (rows - cols));
+ double[][] covar = computeCovariances(point, 1e-14);
+ for (int i = 0; i < errors.length; ++i) {
+ errors[i] = FastMath.sqrt(covar[i][i]) * c;
+ }
+ return errors;
+ }
+
+ /**
+ * Computes an estimate of the standard deviation of the parameters. The
+ * returned values are the square root of the diagonal coefficients of the
+ * covariance matrix, {@code sd(a[i]) ~= sqrt(C[i][i])}, where {@code a[i]}
+ * is the optimized value of the {@code i}-th parameter, and {@code C} is
+ * the covariance matrix.
+ *
+ * @param params Model parameters.
+ * @param covarianceSingularityThreshold Singularity threshold (see
+ * {@link #computeCovariances(double[],double) computeCovariances}).
+ * @return an estimate of the standard deviation of the optimized parameters
+ * @throws org.apache.commons.math3.linear.SingularMatrixException
+ * if the covariance matrix cannot be computed.
+ * @since 3.1
+ */
+ public double[] computeSigma(double[] params,
+ double covarianceSingularityThreshold) {
+ final int nC = params.length;
+ final double[] sig = new double[nC];
+ final double[][] cov = computeCovariances(params, covarianceSingularityThreshold);
+ for (int i = 0; i < nC; ++i) {
+ sig[i] = FastMath.sqrt(cov[i][i]);
+ }
+ return sig;
+ }
+
+ /** {@inheritDoc}
+ * @deprecated As of 3.1. Please use
+ * {@link BaseAbstractMultivariateVectorOptimizer#optimize(int,
+ * org.apache.commons.math3.analysis.MultivariateVectorFunction,OptimizationData[])
+ * optimize(int,MultivariateDifferentiableVectorFunction,OptimizationData...)}
+ * instead.
+ */
+ @Override
+ @Deprecated
+ public PointVectorValuePair optimize(int maxEval,
+ final DifferentiableMultivariateVectorFunction f,
+ final double[] target, final double[] weights,
+ final double[] startPoint) {
+ return optimizeInternal(maxEval,
+ FunctionUtils.toMultivariateDifferentiableVectorFunction(f),
+ new Target(target),
+ new Weight(weights),
+ new InitialGuess(startPoint));
+ }
+
+ /**
+ * Optimize an objective function.
+ * Optimization is considered to be a weighted least-squares minimization.
+ * The cost function to be minimized is
+ * <code>&sum;weight<sub>i</sub>(objective<sub>i</sub> - target<sub>i</sub>)<sup>2</sup></code>
+ *
+ * @param f Objective function.
+ * @param target Target value for the objective functions at optimum.
+ * @param weights Weights for the least squares cost computation.
+ * @param startPoint Start point for optimization.
+ * @return the point/value pair giving the optimal value for objective
+ * function.
+ * @param maxEval Maximum number of function evaluations.
+ * @throws org.apache.commons.math3.exception.DimensionMismatchException
+ * if the start point dimension is wrong.
+ * @throws org.apache.commons.math3.exception.TooManyEvaluationsException
+ * if the maximal number of evaluations is exceeded.
+ * @throws org.apache.commons.math3.exception.NullArgumentException if
+ * any argument is {@code null}.
+ * @deprecated As of 3.1. Please use
+ * {@link BaseAbstractMultivariateVectorOptimizer#optimize(int,
+ * org.apache.commons.math3.analysis.MultivariateVectorFunction,OptimizationData[])
+ * optimize(int,MultivariateDifferentiableVectorFunction,OptimizationData...)}
+ * instead.
+ */
+ @Deprecated
+ public PointVectorValuePair optimize(final int maxEval,
+ final MultivariateDifferentiableVectorFunction f,
+ final double[] target, final double[] weights,
+ final double[] startPoint) {
+ return optimizeInternal(maxEval, f,
+ new Target(target),
+ new Weight(weights),
+ new InitialGuess(startPoint));
+ }
+
+ /**
+ * Optimize an objective function.
+ * Optimization is considered to be a weighted least-squares minimization.
+ * The cost function to be minimized is
+ * <code>&sum;weight<sub>i</sub>(objective<sub>i</sub> - target<sub>i</sub>)<sup>2</sup></code>
+ *
+ * @param maxEval Allowed number of evaluations of the objective function.
+ * @param f Objective function.
+ * @param optData Optimization data. The following data will be looked for:
+ * <ul>
+ * <li>{@link Target}</li>
+ * <li>{@link Weight}</li>
+ * <li>{@link InitialGuess}</li>
+ * </ul>
+ * @return the point/value pair giving the optimal value of the objective
+ * function.
+ * @throws org.apache.commons.math3.exception.TooManyEvaluationsException if
+ * the maximal number of evaluations is exceeded.
+ * @throws DimensionMismatchException if the target, and weight arguments
+ * have inconsistent dimensions.
+ * @see BaseAbstractMultivariateVectorOptimizer#optimizeInternal(int,
+ * org.apache.commons.math3.analysis.MultivariateVectorFunction,OptimizationData[])
+ * @since 3.1
+ * @deprecated As of 3.1. Override is necessary only until this class's generic
+ * argument is changed to {@code MultivariateDifferentiableVectorFunction}.
+ */
+ @Deprecated
+ protected PointVectorValuePair optimizeInternal(final int maxEval,
+ final MultivariateDifferentiableVectorFunction f,
+ OptimizationData... optData) {
+ // XXX Conversion will be removed when the generic argument of the
+ // base class becomes "MultivariateDifferentiableVectorFunction".
+ return super.optimizeInternal(maxEval, FunctionUtils.toDifferentiableMultivariateVectorFunction(f), optData);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ protected void setUp() {
+ super.setUp();
+
+ // Reset counter.
+ jacobianEvaluations = 0;
+
+ // Square-root of the weight matrix.
+ weightMatrixSqrt = squareRoot(getWeight());
+
+ // Store least squares problem characteristics.
+ // XXX The conversion won't be necessary when the generic argument of
+ // the base class becomes "MultivariateDifferentiableVectorFunction".
+ // XXX "jF" is not strictly necessary anymore but is currently more
+ // efficient than converting the value returned from "getObjectiveFunction()"
+ // every time it is used.
+ jF = FunctionUtils.toMultivariateDifferentiableVectorFunction((DifferentiableMultivariateVectorFunction) getObjectiveFunction());
+
+ // Arrays shared with "private" and "protected" methods.
+ point = getStartPoint();
+ rows = getTarget().length;
+ cols = point.length;
+ }
+
+ /**
+ * Computes the residuals.
+ * The residual is the difference between the observed (target)
+ * values and the model (objective function) value.
+ * There is one residual for each element of the vector-valued
+ * function.
+ *
+ * @param objectiveValue Value of the the objective function. This is
+ * the value returned from a call to
+ * {@link #computeObjectiveValue(double[]) computeObjectiveValue}
+ * (whose array argument contains the model parameters).
+ * @return the residuals.
+ * @throws DimensionMismatchException if {@code params} has a wrong
+ * length.
+ * @since 3.1
+ */
+ protected double[] computeResiduals(double[] objectiveValue) {
+ final double[] target = getTarget();
+ if (objectiveValue.length != target.length) {
+ throw new DimensionMismatchException(target.length,
+ objectiveValue.length);
+ }
+
+ final double[] residuals = new double[target.length];
+ for (int i = 0; i < target.length; i++) {
+ residuals[i] = target[i] - objectiveValue[i];
+ }
+
+ return residuals;
+ }
+
+ /**
+ * Computes the square-root of the weight matrix.
+ *
+ * @param m Symmetric, positive-definite (weight) matrix.
+ * @return the square-root of the weight matrix.
+ */
+ private RealMatrix squareRoot(RealMatrix m) {
+ if (m instanceof DiagonalMatrix) {
+ final int dim = m.getRowDimension();
+ final RealMatrix sqrtM = new DiagonalMatrix(dim);
+ for (int i = 0; i < dim; i++) {
+ sqrtM.setEntry(i, i, FastMath.sqrt(m.getEntry(i, i)));
+ }
+ return sqrtM;
+ } else {
+ final EigenDecomposition dec = new EigenDecomposition(m);
+ return dec.getSquareRoot();
+ }
+ }
+}
diff --git a/src/main/java/org/apache/commons/math3/optimization/general/AbstractScalarDifferentiableOptimizer.java b/src/main/java/org/apache/commons/math3/optimization/general/AbstractScalarDifferentiableOptimizer.java
new file mode 100644
index 0000000..3947c2c
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/optimization/general/AbstractScalarDifferentiableOptimizer.java
@@ -0,0 +1,114 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math3.optimization.general;
+
+import org.apache.commons.math3.analysis.DifferentiableMultivariateFunction;
+import org.apache.commons.math3.analysis.MultivariateVectorFunction;
+import org.apache.commons.math3.analysis.FunctionUtils;
+import org.apache.commons.math3.analysis.differentiation.MultivariateDifferentiableFunction;
+import org.apache.commons.math3.optimization.DifferentiableMultivariateOptimizer;
+import org.apache.commons.math3.optimization.GoalType;
+import org.apache.commons.math3.optimization.ConvergenceChecker;
+import org.apache.commons.math3.optimization.PointValuePair;
+import org.apache.commons.math3.optimization.direct.BaseAbstractMultivariateOptimizer;
+
+/**
+ * Base class for implementing optimizers for multivariate scalar
+ * differentiable functions.
+ * It contains boiler-plate code for dealing with gradient evaluation.
+ *
+ * @deprecated As of 3.1 (to be removed in 4.0).
+ * @since 2.0
+ */
+@Deprecated
+public abstract class AbstractScalarDifferentiableOptimizer
+ extends BaseAbstractMultivariateOptimizer<DifferentiableMultivariateFunction>
+ implements DifferentiableMultivariateOptimizer {
+ /**
+ * Objective function gradient.
+ */
+ private MultivariateVectorFunction gradient;
+
+ /**
+ * Simple constructor with default settings.
+ * The convergence check is set to a
+ * {@link org.apache.commons.math3.optimization.SimpleValueChecker
+ * SimpleValueChecker}.
+ * @deprecated See {@link org.apache.commons.math3.optimization.SimpleValueChecker#SimpleValueChecker()}
+ */
+ @Deprecated
+ protected AbstractScalarDifferentiableOptimizer() {}
+
+ /**
+ * @param checker Convergence checker.
+ */
+ protected AbstractScalarDifferentiableOptimizer(ConvergenceChecker<PointValuePair> checker) {
+ super(checker);
+ }
+
+ /**
+ * Compute the gradient vector.
+ *
+ * @param evaluationPoint Point at which the gradient must be evaluated.
+ * @return the gradient at the specified point.
+ * @throws org.apache.commons.math3.exception.TooManyEvaluationsException
+ * if the allowed number of evaluations is exceeded.
+ */
+ protected double[] computeObjectiveGradient(final double[] evaluationPoint) {
+ return gradient.value(evaluationPoint);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ protected PointValuePair optimizeInternal(int maxEval,
+ final DifferentiableMultivariateFunction f,
+ final GoalType goalType,
+ final double[] startPoint) {
+ // Store optimization problem characteristics.
+ gradient = f.gradient();
+
+ return super.optimizeInternal(maxEval, f, goalType, startPoint);
+ }
+
+ /**
+ * Optimize an objective function.
+ *
+ * @param f Objective function.
+ * @param goalType Type of optimization goal: either
+ * {@link GoalType#MAXIMIZE} or {@link GoalType#MINIMIZE}.
+ * @param startPoint Start point for optimization.
+ * @param maxEval Maximum number of function evaluations.
+ * @return the point/value pair giving the optimal value for objective
+ * function.
+ * @throws org.apache.commons.math3.exception.DimensionMismatchException
+ * if the start point dimension is wrong.
+ * @throws org.apache.commons.math3.exception.TooManyEvaluationsException
+ * if the maximal number of evaluations is exceeded.
+ * @throws org.apache.commons.math3.exception.NullArgumentException if
+ * any argument is {@code null}.
+ */
+ public PointValuePair optimize(final int maxEval,
+ final MultivariateDifferentiableFunction f,
+ final GoalType goalType,
+ final double[] startPoint) {
+ return optimizeInternal(maxEval,
+ FunctionUtils.toDifferentiableMultivariateFunction(f),
+ goalType,
+ startPoint);
+ }
+}
diff --git a/src/main/java/org/apache/commons/math3/optimization/general/ConjugateGradientFormula.java b/src/main/java/org/apache/commons/math3/optimization/general/ConjugateGradientFormula.java
new file mode 100644
index 0000000..5fee40a
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/optimization/general/ConjugateGradientFormula.java
@@ -0,0 +1,50 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math3.optimization.general;
+
+/**
+ * Available choices of update formulas for the &beta; parameter
+ * in {@link NonLinearConjugateGradientOptimizer}.
+ * <p>
+ * The &beta; parameter is used to compute the successive conjugate
+ * search directions. For non-linear conjugate gradients, there are
+ * two formulas to compute &beta;:
+ * <ul>
+ * <li>Fletcher-Reeves formula</li>
+ * <li>Polak-Ribi&egrave;re formula</li>
+ * </ul>
+ * On the one hand, the Fletcher-Reeves formula is guaranteed to converge
+ * if the start point is close enough of the optimum whether the
+ * Polak-Ribi&egrave;re formula may not converge in rare cases. On the
+ * other hand, the Polak-Ribi&egrave;re formula is often faster when it
+ * does converge. Polak-Ribi&egrave;re is often used.
+ * <p>
+ * @see NonLinearConjugateGradientOptimizer
+ * @deprecated As of 3.1 (to be removed in 4.0).
+ * @since 2.0
+ */
+@Deprecated
+public enum ConjugateGradientFormula {
+
+ /** Fletcher-Reeves formula. */
+ FLETCHER_REEVES,
+
+ /** Polak-Ribi&egrave;re formula. */
+ POLAK_RIBIERE
+
+}
diff --git a/src/main/java/org/apache/commons/math3/optimization/general/GaussNewtonOptimizer.java b/src/main/java/org/apache/commons/math3/optimization/general/GaussNewtonOptimizer.java
new file mode 100644
index 0000000..464a0f0
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/optimization/general/GaussNewtonOptimizer.java
@@ -0,0 +1,194 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math3.optimization.general;
+
+import org.apache.commons.math3.exception.ConvergenceException;
+import org.apache.commons.math3.exception.NullArgumentException;
+import org.apache.commons.math3.exception.MathInternalError;
+import org.apache.commons.math3.exception.util.LocalizedFormats;
+import org.apache.commons.math3.linear.ArrayRealVector;
+import org.apache.commons.math3.linear.BlockRealMatrix;
+import org.apache.commons.math3.linear.DecompositionSolver;
+import org.apache.commons.math3.linear.LUDecomposition;
+import org.apache.commons.math3.linear.QRDecomposition;
+import org.apache.commons.math3.linear.RealMatrix;
+import org.apache.commons.math3.linear.SingularMatrixException;
+import org.apache.commons.math3.optimization.ConvergenceChecker;
+import org.apache.commons.math3.optimization.SimpleVectorValueChecker;
+import org.apache.commons.math3.optimization.PointVectorValuePair;
+
+/**
+ * Gauss-Newton least-squares solver.
+ * <p>
+ * This class solve a least-square problem by solving the normal equations
+ * of the linearized problem at each iteration. Either LU decomposition or
+ * QR decomposition can be used to solve the normal equations. LU decomposition
+ * is faster but QR decomposition is more robust for difficult problems.
+ * </p>
+ *
+ * @deprecated As of 3.1 (to be removed in 4.0).
+ * @since 2.0
+ *
+ */
+@Deprecated
+public class GaussNewtonOptimizer extends AbstractLeastSquaresOptimizer {
+ /** Indicator for using LU decomposition. */
+ private final boolean useLU;
+
+ /**
+ * Simple constructor with default settings.
+ * The normal equations will be solved using LU decomposition and the
+ * convergence check is set to a {@link SimpleVectorValueChecker}
+ * with default tolerances.
+ * @deprecated See {@link SimpleVectorValueChecker#SimpleVectorValueChecker()}
+ */
+ @Deprecated
+ public GaussNewtonOptimizer() {
+ this(true);
+ }
+
+ /**
+ * Simple constructor with default settings.
+ * The normal equations will be solved using LU decomposition.
+ *
+ * @param checker Convergence checker.
+ */
+ public GaussNewtonOptimizer(ConvergenceChecker<PointVectorValuePair> checker) {
+ this(true, checker);
+ }
+
+ /**
+ * Simple constructor with default settings.
+ * The convergence check is set to a {@link SimpleVectorValueChecker}
+ * with default tolerances.
+ *
+ * @param useLU If {@code true}, the normal equations will be solved
+ * using LU decomposition, otherwise they will be solved using QR
+ * decomposition.
+ * @deprecated See {@link SimpleVectorValueChecker#SimpleVectorValueChecker()}
+ */
+ @Deprecated
+ public GaussNewtonOptimizer(final boolean useLU) {
+ this(useLU, new SimpleVectorValueChecker());
+ }
+
+ /**
+ * @param useLU If {@code true}, the normal equations will be solved
+ * using LU decomposition, otherwise they will be solved using QR
+ * decomposition.
+ * @param checker Convergence checker.
+ */
+ public GaussNewtonOptimizer(final boolean useLU,
+ ConvergenceChecker<PointVectorValuePair> checker) {
+ super(checker);
+ this.useLU = useLU;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public PointVectorValuePair doOptimize() {
+ final ConvergenceChecker<PointVectorValuePair> checker
+ = getConvergenceChecker();
+
+ // Computation will be useless without a checker (see "for-loop").
+ if (checker == null) {
+ throw new NullArgumentException();
+ }
+
+ final double[] targetValues = getTarget();
+ final int nR = targetValues.length; // Number of observed data.
+
+ final RealMatrix weightMatrix = getWeight();
+ // Diagonal of the weight matrix.
+ final double[] residualsWeights = new double[nR];
+ for (int i = 0; i < nR; i++) {
+ residualsWeights[i] = weightMatrix.getEntry(i, i);
+ }
+
+ final double[] currentPoint = getStartPoint();
+ final int nC = currentPoint.length;
+
+ // iterate until convergence is reached
+ PointVectorValuePair current = null;
+ int iter = 0;
+ for (boolean converged = false; !converged;) {
+ ++iter;
+
+ // evaluate the objective function and its jacobian
+ PointVectorValuePair previous = current;
+ // Value of the objective function at "currentPoint".
+ final double[] currentObjective = computeObjectiveValue(currentPoint);
+ final double[] currentResiduals = computeResiduals(currentObjective);
+ final RealMatrix weightedJacobian = computeWeightedJacobian(currentPoint);
+ current = new PointVectorValuePair(currentPoint, currentObjective);
+
+ // build the linear problem
+ final double[] b = new double[nC];
+ final double[][] a = new double[nC][nC];
+ for (int i = 0; i < nR; ++i) {
+
+ final double[] grad = weightedJacobian.getRow(i);
+ final double weight = residualsWeights[i];
+ final double residual = currentResiduals[i];
+
+ // compute the normal equation
+ final double wr = weight * residual;
+ for (int j = 0; j < nC; ++j) {
+ b[j] += wr * grad[j];
+ }
+
+ // build the contribution matrix for measurement i
+ for (int k = 0; k < nC; ++k) {
+ double[] ak = a[k];
+ double wgk = weight * grad[k];
+ for (int l = 0; l < nC; ++l) {
+ ak[l] += wgk * grad[l];
+ }
+ }
+ }
+
+ try {
+ // solve the linearized least squares problem
+ RealMatrix mA = new BlockRealMatrix(a);
+ DecompositionSolver solver = useLU ?
+ new LUDecomposition(mA).getSolver() :
+ new QRDecomposition(mA).getSolver();
+ final double[] dX = solver.solve(new ArrayRealVector(b, false)).toArray();
+ // update the estimated parameters
+ for (int i = 0; i < nC; ++i) {
+ currentPoint[i] += dX[i];
+ }
+ } catch (SingularMatrixException e) {
+ throw new ConvergenceException(LocalizedFormats.UNABLE_TO_SOLVE_SINGULAR_PROBLEM);
+ }
+
+ // Check convergence.
+ if (previous != null) {
+ converged = checker.converged(iter, previous, current);
+ if (converged) {
+ cost = computeCost(currentResiduals);
+ // Update (deprecated) "point" field.
+ point = current.getPoint();
+ return current;
+ }
+ }
+ }
+ // Must never happen.
+ throw new MathInternalError();
+ }
+}
diff --git a/src/main/java/org/apache/commons/math3/optimization/general/LevenbergMarquardtOptimizer.java b/src/main/java/org/apache/commons/math3/optimization/general/LevenbergMarquardtOptimizer.java
new file mode 100644
index 0000000..a29cafc
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/optimization/general/LevenbergMarquardtOptimizer.java
@@ -0,0 +1,943 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math3.optimization.general;
+
+import java.util.Arrays;
+
+import org.apache.commons.math3.exception.ConvergenceException;
+import org.apache.commons.math3.exception.util.LocalizedFormats;
+import org.apache.commons.math3.optimization.PointVectorValuePair;
+import org.apache.commons.math3.optimization.ConvergenceChecker;
+import org.apache.commons.math3.linear.RealMatrix;
+import org.apache.commons.math3.util.Precision;
+import org.apache.commons.math3.util.FastMath;
+
+
+/**
+ * This class solves a least squares problem using the Levenberg-Marquardt algorithm.
+ *
+ * <p>This implementation <em>should</em> work even for over-determined systems
+ * (i.e. systems having more point than equations). Over-determined systems
+ * are solved by ignoring the point which have the smallest impact according
+ * to their jacobian column norm. Only the rank of the matrix and some loop bounds
+ * are changed to implement this.</p>
+ *
+ * <p>The resolution engine is a simple translation of the MINPACK <a
+ * href="http://www.netlib.org/minpack/lmder.f">lmder</a> routine with minor
+ * changes. The changes include the over-determined resolution, the use of
+ * inherited convergence checker and the Q.R. decomposition which has been
+ * rewritten following the algorithm described in the
+ * P. Lascaux and R. Theodor book <i>Analyse num&eacute;rique matricielle
+ * appliqu&eacute;e &agrave; l'art de l'ing&eacute;nieur</i>, Masson 1986.</p>
+ * <p>The authors of the original fortran version are:
+ * <ul>
+ * <li>Argonne National Laboratory. MINPACK project. March 1980</li>
+ * <li>Burton S. Garbow</li>
+ * <li>Kenneth E. Hillstrom</li>
+ * <li>Jorge J. More</li>
+ * </ul>
+ * The redistribution policy for MINPACK is available <a
+ * href="http://www.netlib.org/minpack/disclaimer">here</a>, for convenience, it
+ * is reproduced below.</p>
+ *
+ * <table border="0" width="80%" cellpadding="10" align="center" bgcolor="#E0E0E0">
+ * <tr><td>
+ * Minpack Copyright Notice (1999) University of Chicago.
+ * All rights reserved
+ * </td></tr>
+ * <tr><td>
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * <ol>
+ * <li>Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.</li>
+ * <li>Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided
+ * with the distribution.</li>
+ * <li>The end-user documentation included with the redistribution, if any,
+ * must include the following acknowledgment:
+ * <code>This product includes software developed by the University of
+ * Chicago, as Operator of Argonne National Laboratory.</code>
+ * Alternately, this acknowledgment may appear in the software itself,
+ * if and wherever such third-party acknowledgments normally appear.</li>
+ * <li><strong>WARRANTY DISCLAIMER. THE SOFTWARE IS SUPPLIED "AS IS"
+ * WITHOUT WARRANTY OF ANY KIND. THE COPYRIGHT HOLDER, THE
+ * UNITED STATES, THE UNITED STATES DEPARTMENT OF ENERGY, AND
+ * THEIR EMPLOYEES: (1) DISCLAIM ANY WARRANTIES, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO ANY IMPLIED WARRANTIES
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, TITLE
+ * OR NON-INFRINGEMENT, (2) DO NOT ASSUME ANY LEGAL LIABILITY
+ * OR RESPONSIBILITY FOR THE ACCURACY, COMPLETENESS, OR
+ * USEFULNESS OF THE SOFTWARE, (3) DO NOT REPRESENT THAT USE OF
+ * THE SOFTWARE WOULD NOT INFRINGE PRIVATELY OWNED RIGHTS, (4)
+ * DO NOT WARRANT THAT THE SOFTWARE WILL FUNCTION
+ * UNINTERRUPTED, THAT IT IS ERROR-FREE OR THAT ANY ERRORS WILL
+ * BE CORRECTED.</strong></li>
+ * <li><strong>LIMITATION OF LIABILITY. IN NO EVENT WILL THE COPYRIGHT
+ * HOLDER, THE UNITED STATES, THE UNITED STATES DEPARTMENT OF
+ * ENERGY, OR THEIR EMPLOYEES: BE LIABLE FOR ANY INDIRECT,
+ * INCIDENTAL, CONSEQUENTIAL, SPECIAL OR PUNITIVE DAMAGES OF
+ * ANY KIND OR NATURE, INCLUDING BUT NOT LIMITED TO LOSS OF
+ * PROFITS OR LOSS OF DATA, FOR ANY REASON WHATSOEVER, WHETHER
+ * SUCH LIABILITY IS ASSERTED ON THE BASIS OF CONTRACT, TORT
+ * (INCLUDING NEGLIGENCE OR STRICT LIABILITY), OR OTHERWISE,
+ * EVEN IF ANY OF SAID PARTIES HAS BEEN WARNED OF THE
+ * POSSIBILITY OF SUCH LOSS OR DAMAGES.</strong></li>
+ * <ol></td></tr>
+ * </table>
+ * @deprecated As of 3.1 (to be removed in 4.0).
+ * @since 2.0
+ *
+ */
+@Deprecated
+public class LevenbergMarquardtOptimizer extends AbstractLeastSquaresOptimizer {
+ /** Number of solved point. */
+ private int solvedCols;
+ /** Diagonal elements of the R matrix in the Q.R. decomposition. */
+ private double[] diagR;
+ /** Norms of the columns of the jacobian matrix. */
+ private double[] jacNorm;
+ /** Coefficients of the Householder transforms vectors. */
+ private double[] beta;
+ /** Columns permutation array. */
+ private int[] permutation;
+ /** Rank of the jacobian matrix. */
+ private int rank;
+ /** Levenberg-Marquardt parameter. */
+ private double lmPar;
+ /** Parameters evolution direction associated with lmPar. */
+ private double[] lmDir;
+ /** Positive input variable used in determining the initial step bound. */
+ private final double initialStepBoundFactor;
+ /** Desired relative error in the sum of squares. */
+ private final double costRelativeTolerance;
+ /** Desired relative error in the approximate solution parameters. */
+ private final double parRelativeTolerance;
+ /** Desired max cosine on the orthogonality between the function vector
+ * and the columns of the jacobian. */
+ private final double orthoTolerance;
+ /** Threshold for QR ranking. */
+ private final double qrRankingThreshold;
+ /** Weighted residuals. */
+ private double[] weightedResidual;
+ /** Weighted Jacobian. */
+ private double[][] weightedJacobian;
+
+ /**
+ * Build an optimizer for least squares problems with default values
+ * for all the tuning parameters (see the {@link
+ * #LevenbergMarquardtOptimizer(double,double,double,double,double)
+ * other contructor}.
+ * The default values for the algorithm settings are:
+ * <ul>
+ * <li>Initial step bound factor: 100</li>
+ * <li>Cost relative tolerance: 1e-10</li>
+ * <li>Parameters relative tolerance: 1e-10</li>
+ * <li>Orthogonality tolerance: 1e-10</li>
+ * <li>QR ranking threshold: {@link Precision#SAFE_MIN}</li>
+ * </ul>
+ */
+ public LevenbergMarquardtOptimizer() {
+ this(100, 1e-10, 1e-10, 1e-10, Precision.SAFE_MIN);
+ }
+
+ /**
+ * Constructor that allows the specification of a custom convergence
+ * checker.
+ * Note that all the usual convergence checks will be <em>disabled</em>.
+ * The default values for the algorithm settings are:
+ * <ul>
+ * <li>Initial step bound factor: 100</li>
+ * <li>Cost relative tolerance: 1e-10</li>
+ * <li>Parameters relative tolerance: 1e-10</li>
+ * <li>Orthogonality tolerance: 1e-10</li>
+ * <li>QR ranking threshold: {@link Precision#SAFE_MIN}</li>
+ * </ul>
+ *
+ * @param checker Convergence checker.
+ */
+ public LevenbergMarquardtOptimizer(ConvergenceChecker<PointVectorValuePair> checker) {
+ this(100, checker, 1e-10, 1e-10, 1e-10, Precision.SAFE_MIN);
+ }
+
+ /**
+ * Constructor that allows the specification of a custom convergence
+ * checker, in addition to the standard ones.
+ *
+ * @param initialStepBoundFactor Positive input variable used in
+ * determining the initial step bound. This bound is set to the
+ * product of initialStepBoundFactor and the euclidean norm of
+ * {@code diag * x} if non-zero, or else to {@code initialStepBoundFactor}
+ * itself. In most cases factor should lie in the interval
+ * {@code (0.1, 100.0)}. {@code 100} is a generally recommended value.
+ * @param checker Convergence checker.
+ * @param costRelativeTolerance Desired relative error in the sum of
+ * squares.
+ * @param parRelativeTolerance Desired relative error in the approximate
+ * solution parameters.
+ * @param orthoTolerance Desired max cosine on the orthogonality between
+ * the function vector and the columns of the Jacobian.
+ * @param threshold Desired threshold for QR ranking. If the squared norm
+ * of a column vector is smaller or equal to this threshold during QR
+ * decomposition, it is considered to be a zero vector and hence the rank
+ * of the matrix is reduced.
+ */
+ public LevenbergMarquardtOptimizer(double initialStepBoundFactor,
+ ConvergenceChecker<PointVectorValuePair> checker,
+ double costRelativeTolerance,
+ double parRelativeTolerance,
+ double orthoTolerance,
+ double threshold) {
+ super(checker);
+ this.initialStepBoundFactor = initialStepBoundFactor;
+ this.costRelativeTolerance = costRelativeTolerance;
+ this.parRelativeTolerance = parRelativeTolerance;
+ this.orthoTolerance = orthoTolerance;
+ this.qrRankingThreshold = threshold;
+ }
+
+ /**
+ * Build an optimizer for least squares problems with default values
+ * for some of the tuning parameters (see the {@link
+ * #LevenbergMarquardtOptimizer(double,double,double,double,double)
+ * other contructor}.
+ * The default values for the algorithm settings are:
+ * <ul>
+ * <li>Initial step bound factor}: 100</li>
+ * <li>QR ranking threshold}: {@link Precision#SAFE_MIN}</li>
+ * </ul>
+ *
+ * @param costRelativeTolerance Desired relative error in the sum of
+ * squares.
+ * @param parRelativeTolerance Desired relative error in the approximate
+ * solution parameters.
+ * @param orthoTolerance Desired max cosine on the orthogonality between
+ * the function vector and the columns of the Jacobian.
+ */
+ public LevenbergMarquardtOptimizer(double costRelativeTolerance,
+ double parRelativeTolerance,
+ double orthoTolerance) {
+ this(100,
+ costRelativeTolerance, parRelativeTolerance, orthoTolerance,
+ Precision.SAFE_MIN);
+ }
+
+ /**
+ * The arguments control the behaviour of the default convergence checking
+ * procedure.
+ * Additional criteria can defined through the setting of a {@link
+ * ConvergenceChecker}.
+ *
+ * @param initialStepBoundFactor Positive input variable used in
+ * determining the initial step bound. This bound is set to the
+ * product of initialStepBoundFactor and the euclidean norm of
+ * {@code diag * x} if non-zero, or else to {@code initialStepBoundFactor}
+ * itself. In most cases factor should lie in the interval
+ * {@code (0.1, 100.0)}. {@code 100} is a generally recommended value.
+ * @param costRelativeTolerance Desired relative error in the sum of
+ * squares.
+ * @param parRelativeTolerance Desired relative error in the approximate
+ * solution parameters.
+ * @param orthoTolerance Desired max cosine on the orthogonality between
+ * the function vector and the columns of the Jacobian.
+ * @param threshold Desired threshold for QR ranking. If the squared norm
+ * of a column vector is smaller or equal to this threshold during QR
+ * decomposition, it is considered to be a zero vector and hence the rank
+ * of the matrix is reduced.
+ */
+ public LevenbergMarquardtOptimizer(double initialStepBoundFactor,
+ double costRelativeTolerance,
+ double parRelativeTolerance,
+ double orthoTolerance,
+ double threshold) {
+ super(null); // No custom convergence criterion.
+ this.initialStepBoundFactor = initialStepBoundFactor;
+ this.costRelativeTolerance = costRelativeTolerance;
+ this.parRelativeTolerance = parRelativeTolerance;
+ this.orthoTolerance = orthoTolerance;
+ this.qrRankingThreshold = threshold;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ protected PointVectorValuePair doOptimize() {
+ final int nR = getTarget().length; // Number of observed data.
+ final double[] currentPoint = getStartPoint();
+ final int nC = currentPoint.length; // Number of parameters.
+
+ // arrays shared with the other private methods
+ solvedCols = FastMath.min(nR, nC);
+ diagR = new double[nC];
+ jacNorm = new double[nC];
+ beta = new double[nC];
+ permutation = new int[nC];
+ lmDir = new double[nC];
+
+ // local point
+ double delta = 0;
+ double xNorm = 0;
+ double[] diag = new double[nC];
+ double[] oldX = new double[nC];
+ double[] oldRes = new double[nR];
+ double[] oldObj = new double[nR];
+ double[] qtf = new double[nR];
+ double[] work1 = new double[nC];
+ double[] work2 = new double[nC];
+ double[] work3 = new double[nC];
+
+ final RealMatrix weightMatrixSqrt = getWeightSquareRoot();
+
+ // Evaluate the function at the starting point and calculate its norm.
+ double[] currentObjective = computeObjectiveValue(currentPoint);
+ double[] currentResiduals = computeResiduals(currentObjective);
+ PointVectorValuePair current = new PointVectorValuePair(currentPoint, currentObjective);
+ double currentCost = computeCost(currentResiduals);
+
+ // Outer loop.
+ lmPar = 0;
+ boolean firstIteration = true;
+ int iter = 0;
+ final ConvergenceChecker<PointVectorValuePair> checker = getConvergenceChecker();
+ while (true) {
+ ++iter;
+ final PointVectorValuePair previous = current;
+
+ // QR decomposition of the jacobian matrix
+ qrDecomposition(computeWeightedJacobian(currentPoint));
+
+ weightedResidual = weightMatrixSqrt.operate(currentResiduals);
+ for (int i = 0; i < nR; i++) {
+ qtf[i] = weightedResidual[i];
+ }
+
+ // compute Qt.res
+ qTy(qtf);
+
+ // now we don't need Q anymore,
+ // so let jacobian contain the R matrix with its diagonal elements
+ for (int k = 0; k < solvedCols; ++k) {
+ int pk = permutation[k];
+ weightedJacobian[k][pk] = diagR[pk];
+ }
+
+ if (firstIteration) {
+ // scale the point according to the norms of the columns
+ // of the initial jacobian
+ xNorm = 0;
+ for (int k = 0; k < nC; ++k) {
+ double dk = jacNorm[k];
+ if (dk == 0) {
+ dk = 1.0;
+ }
+ double xk = dk * currentPoint[k];
+ xNorm += xk * xk;
+ diag[k] = dk;
+ }
+ xNorm = FastMath.sqrt(xNorm);
+
+ // initialize the step bound delta
+ delta = (xNorm == 0) ? initialStepBoundFactor : (initialStepBoundFactor * xNorm);
+ }
+
+ // check orthogonality between function vector and jacobian columns
+ double maxCosine = 0;
+ if (currentCost != 0) {
+ for (int j = 0; j < solvedCols; ++j) {
+ int pj = permutation[j];
+ double s = jacNorm[pj];
+ if (s != 0) {
+ double sum = 0;
+ for (int i = 0; i <= j; ++i) {
+ sum += weightedJacobian[i][pj] * qtf[i];
+ }
+ maxCosine = FastMath.max(maxCosine, FastMath.abs(sum) / (s * currentCost));
+ }
+ }
+ }
+ if (maxCosine <= orthoTolerance) {
+ // Convergence has been reached.
+ setCost(currentCost);
+ // Update (deprecated) "point" field.
+ point = current.getPoint();
+ return current;
+ }
+
+ // rescale if necessary
+ for (int j = 0; j < nC; ++j) {
+ diag[j] = FastMath.max(diag[j], jacNorm[j]);
+ }
+
+ // Inner loop.
+ for (double ratio = 0; ratio < 1.0e-4;) {
+
+ // save the state
+ for (int j = 0; j < solvedCols; ++j) {
+ int pj = permutation[j];
+ oldX[pj] = currentPoint[pj];
+ }
+ final double previousCost = currentCost;
+ double[] tmpVec = weightedResidual;
+ weightedResidual = oldRes;
+ oldRes = tmpVec;
+ tmpVec = currentObjective;
+ currentObjective = oldObj;
+ oldObj = tmpVec;
+
+ // determine the Levenberg-Marquardt parameter
+ determineLMParameter(qtf, delta, diag, work1, work2, work3);
+
+ // compute the new point and the norm of the evolution direction
+ double lmNorm = 0;
+ for (int j = 0; j < solvedCols; ++j) {
+ int pj = permutation[j];
+ lmDir[pj] = -lmDir[pj];
+ currentPoint[pj] = oldX[pj] + lmDir[pj];
+ double s = diag[pj] * lmDir[pj];
+ lmNorm += s * s;
+ }
+ lmNorm = FastMath.sqrt(lmNorm);
+ // on the first iteration, adjust the initial step bound.
+ if (firstIteration) {
+ delta = FastMath.min(delta, lmNorm);
+ }
+
+ // Evaluate the function at x + p and calculate its norm.
+ currentObjective = computeObjectiveValue(currentPoint);
+ currentResiduals = computeResiduals(currentObjective);
+ current = new PointVectorValuePair(currentPoint, currentObjective);
+ currentCost = computeCost(currentResiduals);
+
+ // compute the scaled actual reduction
+ double actRed = -1.0;
+ if (0.1 * currentCost < previousCost) {
+ double r = currentCost / previousCost;
+ actRed = 1.0 - r * r;
+ }
+
+ // compute the scaled predicted reduction
+ // and the scaled directional derivative
+ for (int j = 0; j < solvedCols; ++j) {
+ int pj = permutation[j];
+ double dirJ = lmDir[pj];
+ work1[j] = 0;
+ for (int i = 0; i <= j; ++i) {
+ work1[i] += weightedJacobian[i][pj] * dirJ;
+ }
+ }
+ double coeff1 = 0;
+ for (int j = 0; j < solvedCols; ++j) {
+ coeff1 += work1[j] * work1[j];
+ }
+ double pc2 = previousCost * previousCost;
+ coeff1 /= pc2;
+ double coeff2 = lmPar * lmNorm * lmNorm / pc2;
+ double preRed = coeff1 + 2 * coeff2;
+ double dirDer = -(coeff1 + coeff2);
+
+ // ratio of the actual to the predicted reduction
+ ratio = (preRed == 0) ? 0 : (actRed / preRed);
+
+ // update the step bound
+ if (ratio <= 0.25) {
+ double tmp =
+ (actRed < 0) ? (0.5 * dirDer / (dirDer + 0.5 * actRed)) : 0.5;
+ if ((0.1 * currentCost >= previousCost) || (tmp < 0.1)) {
+ tmp = 0.1;
+ }
+ delta = tmp * FastMath.min(delta, 10.0 * lmNorm);
+ lmPar /= tmp;
+ } else if ((lmPar == 0) || (ratio >= 0.75)) {
+ delta = 2 * lmNorm;
+ lmPar *= 0.5;
+ }
+
+ // test for successful iteration.
+ if (ratio >= 1.0e-4) {
+ // successful iteration, update the norm
+ firstIteration = false;
+ xNorm = 0;
+ for (int k = 0; k < nC; ++k) {
+ double xK = diag[k] * currentPoint[k];
+ xNorm += xK * xK;
+ }
+ xNorm = FastMath.sqrt(xNorm);
+
+ // tests for convergence.
+ if (checker != null && checker.converged(iter, previous, current)) {
+ setCost(currentCost);
+ // Update (deprecated) "point" field.
+ point = current.getPoint();
+ return current;
+ }
+ } else {
+ // failed iteration, reset the previous values
+ currentCost = previousCost;
+ for (int j = 0; j < solvedCols; ++j) {
+ int pj = permutation[j];
+ currentPoint[pj] = oldX[pj];
+ }
+ tmpVec = weightedResidual;
+ weightedResidual = oldRes;
+ oldRes = tmpVec;
+ tmpVec = currentObjective;
+ currentObjective = oldObj;
+ oldObj = tmpVec;
+ // Reset "current" to previous values.
+ current = new PointVectorValuePair(currentPoint, currentObjective);
+ }
+
+ // Default convergence criteria.
+ if ((FastMath.abs(actRed) <= costRelativeTolerance &&
+ preRed <= costRelativeTolerance &&
+ ratio <= 2.0) ||
+ delta <= parRelativeTolerance * xNorm) {
+ setCost(currentCost);
+ // Update (deprecated) "point" field.
+ point = current.getPoint();
+ return current;
+ }
+
+ // tests for termination and stringent tolerances
+ // (2.2204e-16 is the machine epsilon for IEEE754)
+ if ((FastMath.abs(actRed) <= 2.2204e-16) && (preRed <= 2.2204e-16) && (ratio <= 2.0)) {
+ throw new ConvergenceException(LocalizedFormats.TOO_SMALL_COST_RELATIVE_TOLERANCE,
+ costRelativeTolerance);
+ } else if (delta <= 2.2204e-16 * xNorm) {
+ throw new ConvergenceException(LocalizedFormats.TOO_SMALL_PARAMETERS_RELATIVE_TOLERANCE,
+ parRelativeTolerance);
+ } else if (maxCosine <= 2.2204e-16) {
+ throw new ConvergenceException(LocalizedFormats.TOO_SMALL_ORTHOGONALITY_TOLERANCE,
+ orthoTolerance);
+ }
+ }
+ }
+ }
+
+ /**
+ * Determine the Levenberg-Marquardt parameter.
+ * <p>This implementation is a translation in Java of the MINPACK
+ * <a href="http://www.netlib.org/minpack/lmpar.f">lmpar</a>
+ * routine.</p>
+ * <p>This method sets the lmPar and lmDir attributes.</p>
+ * <p>The authors of the original fortran function are:</p>
+ * <ul>
+ * <li>Argonne National Laboratory. MINPACK project. March 1980</li>
+ * <li>Burton S. Garbow</li>
+ * <li>Kenneth E. Hillstrom</li>
+ * <li>Jorge J. More</li>
+ * </ul>
+ * <p>Luc Maisonobe did the Java translation.</p>
+ *
+ * @param qy array containing qTy
+ * @param delta upper bound on the euclidean norm of diagR * lmDir
+ * @param diag diagonal matrix
+ * @param work1 work array
+ * @param work2 work array
+ * @param work3 work array
+ */
+ private void determineLMParameter(double[] qy, double delta, double[] diag,
+ double[] work1, double[] work2, double[] work3) {
+ final int nC = weightedJacobian[0].length;
+
+ // compute and store in x the gauss-newton direction, if the
+ // jacobian is rank-deficient, obtain a least squares solution
+ for (int j = 0; j < rank; ++j) {
+ lmDir[permutation[j]] = qy[j];
+ }
+ for (int j = rank; j < nC; ++j) {
+ lmDir[permutation[j]] = 0;
+ }
+ for (int k = rank - 1; k >= 0; --k) {
+ int pk = permutation[k];
+ double ypk = lmDir[pk] / diagR[pk];
+ for (int i = 0; i < k; ++i) {
+ lmDir[permutation[i]] -= ypk * weightedJacobian[i][pk];
+ }
+ lmDir[pk] = ypk;
+ }
+
+ // evaluate the function at the origin, and test
+ // for acceptance of the Gauss-Newton direction
+ double dxNorm = 0;
+ for (int j = 0; j < solvedCols; ++j) {
+ int pj = permutation[j];
+ double s = diag[pj] * lmDir[pj];
+ work1[pj] = s;
+ dxNorm += s * s;
+ }
+ dxNorm = FastMath.sqrt(dxNorm);
+ double fp = dxNorm - delta;
+ if (fp <= 0.1 * delta) {
+ lmPar = 0;
+ return;
+ }
+
+ // if the jacobian is not rank deficient, the Newton step provides
+ // a lower bound, parl, for the zero of the function,
+ // otherwise set this bound to zero
+ double sum2;
+ double parl = 0;
+ if (rank == solvedCols) {
+ for (int j = 0; j < solvedCols; ++j) {
+ int pj = permutation[j];
+ work1[pj] *= diag[pj] / dxNorm;
+ }
+ sum2 = 0;
+ for (int j = 0; j < solvedCols; ++j) {
+ int pj = permutation[j];
+ double sum = 0;
+ for (int i = 0; i < j; ++i) {
+ sum += weightedJacobian[i][pj] * work1[permutation[i]];
+ }
+ double s = (work1[pj] - sum) / diagR[pj];
+ work1[pj] = s;
+ sum2 += s * s;
+ }
+ parl = fp / (delta * sum2);
+ }
+
+ // calculate an upper bound, paru, for the zero of the function
+ sum2 = 0;
+ for (int j = 0; j < solvedCols; ++j) {
+ int pj = permutation[j];
+ double sum = 0;
+ for (int i = 0; i <= j; ++i) {
+ sum += weightedJacobian[i][pj] * qy[i];
+ }
+ sum /= diag[pj];
+ sum2 += sum * sum;
+ }
+ double gNorm = FastMath.sqrt(sum2);
+ double paru = gNorm / delta;
+ if (paru == 0) {
+ // 2.2251e-308 is the smallest positive real for IEE754
+ paru = 2.2251e-308 / FastMath.min(delta, 0.1);
+ }
+
+ // if the input par lies outside of the interval (parl,paru),
+ // set par to the closer endpoint
+ lmPar = FastMath.min(paru, FastMath.max(lmPar, parl));
+ if (lmPar == 0) {
+ lmPar = gNorm / dxNorm;
+ }
+
+ for (int countdown = 10; countdown >= 0; --countdown) {
+
+ // evaluate the function at the current value of lmPar
+ if (lmPar == 0) {
+ lmPar = FastMath.max(2.2251e-308, 0.001 * paru);
+ }
+ double sPar = FastMath.sqrt(lmPar);
+ for (int j = 0; j < solvedCols; ++j) {
+ int pj = permutation[j];
+ work1[pj] = sPar * diag[pj];
+ }
+ determineLMDirection(qy, work1, work2, work3);
+
+ dxNorm = 0;
+ for (int j = 0; j < solvedCols; ++j) {
+ int pj = permutation[j];
+ double s = diag[pj] * lmDir[pj];
+ work3[pj] = s;
+ dxNorm += s * s;
+ }
+ dxNorm = FastMath.sqrt(dxNorm);
+ double previousFP = fp;
+ fp = dxNorm - delta;
+
+ // if the function is small enough, accept the current value
+ // of lmPar, also test for the exceptional cases where parl is zero
+ if ((FastMath.abs(fp) <= 0.1 * delta) ||
+ ((parl == 0) && (fp <= previousFP) && (previousFP < 0))) {
+ return;
+ }
+
+ // compute the Newton correction
+ for (int j = 0; j < solvedCols; ++j) {
+ int pj = permutation[j];
+ work1[pj] = work3[pj] * diag[pj] / dxNorm;
+ }
+ for (int j = 0; j < solvedCols; ++j) {
+ int pj = permutation[j];
+ work1[pj] /= work2[j];
+ double tmp = work1[pj];
+ for (int i = j + 1; i < solvedCols; ++i) {
+ work1[permutation[i]] -= weightedJacobian[i][pj] * tmp;
+ }
+ }
+ sum2 = 0;
+ for (int j = 0; j < solvedCols; ++j) {
+ double s = work1[permutation[j]];
+ sum2 += s * s;
+ }
+ double correction = fp / (delta * sum2);
+
+ // depending on the sign of the function, update parl or paru.
+ if (fp > 0) {
+ parl = FastMath.max(parl, lmPar);
+ } else if (fp < 0) {
+ paru = FastMath.min(paru, lmPar);
+ }
+
+ // compute an improved estimate for lmPar
+ lmPar = FastMath.max(parl, lmPar + correction);
+
+ }
+ }
+
+ /**
+ * Solve a*x = b and d*x = 0 in the least squares sense.
+ * <p>This implementation is a translation in Java of the MINPACK
+ * <a href="http://www.netlib.org/minpack/qrsolv.f">qrsolv</a>
+ * routine.</p>
+ * <p>This method sets the lmDir and lmDiag attributes.</p>
+ * <p>The authors of the original fortran function are:</p>
+ * <ul>
+ * <li>Argonne National Laboratory. MINPACK project. March 1980</li>
+ * <li>Burton S. Garbow</li>
+ * <li>Kenneth E. Hillstrom</li>
+ * <li>Jorge J. More</li>
+ * </ul>
+ * <p>Luc Maisonobe did the Java translation.</p>
+ *
+ * @param qy array containing qTy
+ * @param diag diagonal matrix
+ * @param lmDiag diagonal elements associated with lmDir
+ * @param work work array
+ */
+ private void determineLMDirection(double[] qy, double[] diag,
+ double[] lmDiag, double[] work) {
+
+ // copy R and Qty to preserve input and initialize s
+ // in particular, save the diagonal elements of R in lmDir
+ for (int j = 0; j < solvedCols; ++j) {
+ int pj = permutation[j];
+ for (int i = j + 1; i < solvedCols; ++i) {
+ weightedJacobian[i][pj] = weightedJacobian[j][permutation[i]];
+ }
+ lmDir[j] = diagR[pj];
+ work[j] = qy[j];
+ }
+
+ // eliminate the diagonal matrix d using a Givens rotation
+ for (int j = 0; j < solvedCols; ++j) {
+
+ // prepare the row of d to be eliminated, locating the
+ // diagonal element using p from the Q.R. factorization
+ int pj = permutation[j];
+ double dpj = diag[pj];
+ if (dpj != 0) {
+ Arrays.fill(lmDiag, j + 1, lmDiag.length, 0);
+ }
+ lmDiag[j] = dpj;
+
+ // the transformations to eliminate the row of d
+ // modify only a single element of Qty
+ // beyond the first n, which is initially zero.
+ double qtbpj = 0;
+ for (int k = j; k < solvedCols; ++k) {
+ int pk = permutation[k];
+
+ // determine a Givens rotation which eliminates the
+ // appropriate element in the current row of d
+ if (lmDiag[k] != 0) {
+
+ final double sin;
+ final double cos;
+ double rkk = weightedJacobian[k][pk];
+ if (FastMath.abs(rkk) < FastMath.abs(lmDiag[k])) {
+ final double cotan = rkk / lmDiag[k];
+ sin = 1.0 / FastMath.sqrt(1.0 + cotan * cotan);
+ cos = sin * cotan;
+ } else {
+ final double tan = lmDiag[k] / rkk;
+ cos = 1.0 / FastMath.sqrt(1.0 + tan * tan);
+ sin = cos * tan;
+ }
+
+ // compute the modified diagonal element of R and
+ // the modified element of (Qty,0)
+ weightedJacobian[k][pk] = cos * rkk + sin * lmDiag[k];
+ final double temp = cos * work[k] + sin * qtbpj;
+ qtbpj = -sin * work[k] + cos * qtbpj;
+ work[k] = temp;
+
+ // accumulate the tranformation in the row of s
+ for (int i = k + 1; i < solvedCols; ++i) {
+ double rik = weightedJacobian[i][pk];
+ final double temp2 = cos * rik + sin * lmDiag[i];
+ lmDiag[i] = -sin * rik + cos * lmDiag[i];
+ weightedJacobian[i][pk] = temp2;
+ }
+ }
+ }
+
+ // store the diagonal element of s and restore
+ // the corresponding diagonal element of R
+ lmDiag[j] = weightedJacobian[j][permutation[j]];
+ weightedJacobian[j][permutation[j]] = lmDir[j];
+ }
+
+ // solve the triangular system for z, if the system is
+ // singular, then obtain a least squares solution
+ int nSing = solvedCols;
+ for (int j = 0; j < solvedCols; ++j) {
+ if ((lmDiag[j] == 0) && (nSing == solvedCols)) {
+ nSing = j;
+ }
+ if (nSing < solvedCols) {
+ work[j] = 0;
+ }
+ }
+ if (nSing > 0) {
+ for (int j = nSing - 1; j >= 0; --j) {
+ int pj = permutation[j];
+ double sum = 0;
+ for (int i = j + 1; i < nSing; ++i) {
+ sum += weightedJacobian[i][pj] * work[i];
+ }
+ work[j] = (work[j] - sum) / lmDiag[j];
+ }
+ }
+
+ // permute the components of z back to components of lmDir
+ for (int j = 0; j < lmDir.length; ++j) {
+ lmDir[permutation[j]] = work[j];
+ }
+ }
+
+ /**
+ * Decompose a matrix A as A.P = Q.R using Householder transforms.
+ * <p>As suggested in the P. Lascaux and R. Theodor book
+ * <i>Analyse num&eacute;rique matricielle appliqu&eacute;e &agrave;
+ * l'art de l'ing&eacute;nieur</i> (Masson, 1986), instead of representing
+ * the Householder transforms with u<sub>k</sub> unit vectors such that:
+ * <pre>
+ * H<sub>k</sub> = I - 2u<sub>k</sub>.u<sub>k</sub><sup>t</sup>
+ * </pre>
+ * we use <sub>k</sub> non-unit vectors such that:
+ * <pre>
+ * H<sub>k</sub> = I - beta<sub>k</sub>v<sub>k</sub>.v<sub>k</sub><sup>t</sup>
+ * </pre>
+ * where v<sub>k</sub> = a<sub>k</sub> - alpha<sub>k</sub> e<sub>k</sub>.
+ * The beta<sub>k</sub> coefficients are provided upon exit as recomputing
+ * them from the v<sub>k</sub> vectors would be costly.</p>
+ * <p>This decomposition handles rank deficient cases since the tranformations
+ * are performed in non-increasing columns norms order thanks to columns
+ * pivoting. The diagonal elements of the R matrix are therefore also in
+ * non-increasing absolute values order.</p>
+ *
+ * @param jacobian Weighted Jacobian matrix at the current point.
+ * @exception ConvergenceException if the decomposition cannot be performed
+ */
+ private void qrDecomposition(RealMatrix jacobian) throws ConvergenceException {
+ // Code in this class assumes that the weighted Jacobian is -(W^(1/2) J),
+ // hence the multiplication by -1.
+ weightedJacobian = jacobian.scalarMultiply(-1).getData();
+
+ final int nR = weightedJacobian.length;
+ final int nC = weightedJacobian[0].length;
+
+ // initializations
+ for (int k = 0; k < nC; ++k) {
+ permutation[k] = k;
+ double norm2 = 0;
+ for (int i = 0; i < nR; ++i) {
+ double akk = weightedJacobian[i][k];
+ norm2 += akk * akk;
+ }
+ jacNorm[k] = FastMath.sqrt(norm2);
+ }
+
+ // transform the matrix column after column
+ for (int k = 0; k < nC; ++k) {
+
+ // select the column with the greatest norm on active components
+ int nextColumn = -1;
+ double ak2 = Double.NEGATIVE_INFINITY;
+ for (int i = k; i < nC; ++i) {
+ double norm2 = 0;
+ for (int j = k; j < nR; ++j) {
+ double aki = weightedJacobian[j][permutation[i]];
+ norm2 += aki * aki;
+ }
+ if (Double.isInfinite(norm2) || Double.isNaN(norm2)) {
+ throw new ConvergenceException(LocalizedFormats.UNABLE_TO_PERFORM_QR_DECOMPOSITION_ON_JACOBIAN,
+ nR, nC);
+ }
+ if (norm2 > ak2) {
+ nextColumn = i;
+ ak2 = norm2;
+ }
+ }
+ if (ak2 <= qrRankingThreshold) {
+ rank = k;
+ return;
+ }
+ int pk = permutation[nextColumn];
+ permutation[nextColumn] = permutation[k];
+ permutation[k] = pk;
+
+ // choose alpha such that Hk.u = alpha ek
+ double akk = weightedJacobian[k][pk];
+ double alpha = (akk > 0) ? -FastMath.sqrt(ak2) : FastMath.sqrt(ak2);
+ double betak = 1.0 / (ak2 - akk * alpha);
+ beta[pk] = betak;
+
+ // transform the current column
+ diagR[pk] = alpha;
+ weightedJacobian[k][pk] -= alpha;
+
+ // transform the remaining columns
+ for (int dk = nC - 1 - k; dk > 0; --dk) {
+ double gamma = 0;
+ for (int j = k; j < nR; ++j) {
+ gamma += weightedJacobian[j][pk] * weightedJacobian[j][permutation[k + dk]];
+ }
+ gamma *= betak;
+ for (int j = k; j < nR; ++j) {
+ weightedJacobian[j][permutation[k + dk]] -= gamma * weightedJacobian[j][pk];
+ }
+ }
+ }
+ rank = solvedCols;
+ }
+
+ /**
+ * Compute the product Qt.y for some Q.R. decomposition.
+ *
+ * @param y vector to multiply (will be overwritten with the result)
+ */
+ private void qTy(double[] y) {
+ final int nR = weightedJacobian.length;
+ final int nC = weightedJacobian[0].length;
+
+ for (int k = 0; k < nC; ++k) {
+ int pk = permutation[k];
+ double gamma = 0;
+ for (int i = k; i < nR; ++i) {
+ gamma += weightedJacobian[i][pk] * y[i];
+ }
+ gamma *= beta[pk];
+ for (int i = k; i < nR; ++i) {
+ y[i] -= gamma * weightedJacobian[i][pk];
+ }
+ }
+ }
+}
diff --git a/src/main/java/org/apache/commons/math3/optimization/general/NonLinearConjugateGradientOptimizer.java b/src/main/java/org/apache/commons/math3/optimization/general/NonLinearConjugateGradientOptimizer.java
new file mode 100644
index 0000000..ee16472
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/optimization/general/NonLinearConjugateGradientOptimizer.java
@@ -0,0 +1,311 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math3.optimization.general;
+
+import org.apache.commons.math3.exception.MathIllegalStateException;
+import org.apache.commons.math3.analysis.UnivariateFunction;
+import org.apache.commons.math3.analysis.solvers.BrentSolver;
+import org.apache.commons.math3.analysis.solvers.UnivariateSolver;
+import org.apache.commons.math3.exception.util.LocalizedFormats;
+import org.apache.commons.math3.optimization.GoalType;
+import org.apache.commons.math3.optimization.PointValuePair;
+import org.apache.commons.math3.optimization.SimpleValueChecker;
+import org.apache.commons.math3.optimization.ConvergenceChecker;
+import org.apache.commons.math3.util.FastMath;
+
+/**
+ * Non-linear conjugate gradient optimizer.
+ * <p>
+ * This class supports both the Fletcher-Reeves and the Polak-Ribi&egrave;re
+ * update formulas for the conjugate search directions. It also supports
+ * optional preconditioning.
+ * </p>
+ *
+ * @deprecated As of 3.1 (to be removed in 4.0).
+ * @since 2.0
+ *
+ */
+@Deprecated
+public class NonLinearConjugateGradientOptimizer
+ extends AbstractScalarDifferentiableOptimizer {
+ /** Update formula for the beta parameter. */
+ private final ConjugateGradientFormula updateFormula;
+ /** Preconditioner (may be null). */
+ private final Preconditioner preconditioner;
+ /** solver to use in the line search (may be null). */
+ private final UnivariateSolver solver;
+ /** Initial step used to bracket the optimum in line search. */
+ private double initialStep;
+ /** Current point. */
+ private double[] point;
+
+ /**
+ * Constructor with default {@link SimpleValueChecker checker},
+ * {@link BrentSolver line search solver} and
+ * {@link IdentityPreconditioner preconditioner}.
+ *
+ * @param updateFormula formula to use for updating the &beta; parameter,
+ * must be one of {@link ConjugateGradientFormula#FLETCHER_REEVES} or {@link
+ * ConjugateGradientFormula#POLAK_RIBIERE}.
+ * @deprecated See {@link SimpleValueChecker#SimpleValueChecker()}
+ */
+ @Deprecated
+ public NonLinearConjugateGradientOptimizer(final ConjugateGradientFormula updateFormula) {
+ this(updateFormula,
+ new SimpleValueChecker());
+ }
+
+ /**
+ * Constructor with default {@link BrentSolver line search solver} and
+ * {@link IdentityPreconditioner preconditioner}.
+ *
+ * @param updateFormula formula to use for updating the &beta; parameter,
+ * must be one of {@link ConjugateGradientFormula#FLETCHER_REEVES} or {@link
+ * ConjugateGradientFormula#POLAK_RIBIERE}.
+ * @param checker Convergence checker.
+ */
+ public NonLinearConjugateGradientOptimizer(final ConjugateGradientFormula updateFormula,
+ ConvergenceChecker<PointValuePair> checker) {
+ this(updateFormula,
+ checker,
+ new BrentSolver(),
+ new IdentityPreconditioner());
+ }
+
+
+ /**
+ * Constructor with default {@link IdentityPreconditioner preconditioner}.
+ *
+ * @param updateFormula formula to use for updating the &beta; parameter,
+ * must be one of {@link ConjugateGradientFormula#FLETCHER_REEVES} or {@link
+ * ConjugateGradientFormula#POLAK_RIBIERE}.
+ * @param checker Convergence checker.
+ * @param lineSearchSolver Solver to use during line search.
+ */
+ public NonLinearConjugateGradientOptimizer(final ConjugateGradientFormula updateFormula,
+ ConvergenceChecker<PointValuePair> checker,
+ final UnivariateSolver lineSearchSolver) {
+ this(updateFormula,
+ checker,
+ lineSearchSolver,
+ new IdentityPreconditioner());
+ }
+
+ /**
+ * @param updateFormula formula to use for updating the &beta; parameter,
+ * must be one of {@link ConjugateGradientFormula#FLETCHER_REEVES} or {@link
+ * ConjugateGradientFormula#POLAK_RIBIERE}.
+ * @param checker Convergence checker.
+ * @param lineSearchSolver Solver to use during line search.
+ * @param preconditioner Preconditioner.
+ */
+ public NonLinearConjugateGradientOptimizer(final ConjugateGradientFormula updateFormula,
+ ConvergenceChecker<PointValuePair> checker,
+ final UnivariateSolver lineSearchSolver,
+ final Preconditioner preconditioner) {
+ super(checker);
+
+ this.updateFormula = updateFormula;
+ solver = lineSearchSolver;
+ this.preconditioner = preconditioner;
+ initialStep = 1.0;
+ }
+
+ /**
+ * Set the initial step used to bracket the optimum in line search.
+ * <p>
+ * The initial step is a factor with respect to the search direction,
+ * which itself is roughly related to the gradient of the function
+ * </p>
+ * @param initialStep initial step used to bracket the optimum in line search,
+ * if a non-positive value is used, the initial step is reset to its
+ * default value of 1.0
+ */
+ public void setInitialStep(final double initialStep) {
+ if (initialStep <= 0) {
+ this.initialStep = 1.0;
+ } else {
+ this.initialStep = initialStep;
+ }
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ protected PointValuePair doOptimize() {
+ final ConvergenceChecker<PointValuePair> checker = getConvergenceChecker();
+ point = getStartPoint();
+ final GoalType goal = getGoalType();
+ final int n = point.length;
+ double[] r = computeObjectiveGradient(point);
+ if (goal == GoalType.MINIMIZE) {
+ for (int i = 0; i < n; ++i) {
+ r[i] = -r[i];
+ }
+ }
+
+ // Initial search direction.
+ double[] steepestDescent = preconditioner.precondition(point, r);
+ double[] searchDirection = steepestDescent.clone();
+
+ double delta = 0;
+ for (int i = 0; i < n; ++i) {
+ delta += r[i] * searchDirection[i];
+ }
+
+ PointValuePair current = null;
+ int iter = 0;
+ int maxEval = getMaxEvaluations();
+ while (true) {
+ ++iter;
+
+ final double objective = computeObjectiveValue(point);
+ PointValuePair previous = current;
+ current = new PointValuePair(point, objective);
+ if (previous != null && checker.converged(iter, previous, current)) {
+ // We have found an optimum.
+ return current;
+ }
+
+ // Find the optimal step in the search direction.
+ final UnivariateFunction lsf = new LineSearchFunction(searchDirection);
+ final double uB = findUpperBound(lsf, 0, initialStep);
+ // XXX Last parameters is set to a value close to zero in order to
+ // work around the divergence problem in the "testCircleFitting"
+ // unit test (see MATH-439).
+ final double step = solver.solve(maxEval, lsf, 0, uB, 1e-15);
+ maxEval -= solver.getEvaluations(); // Subtract used up evaluations.
+
+ // Validate new point.
+ for (int i = 0; i < point.length; ++i) {
+ point[i] += step * searchDirection[i];
+ }
+
+ r = computeObjectiveGradient(point);
+ if (goal == GoalType.MINIMIZE) {
+ for (int i = 0; i < n; ++i) {
+ r[i] = -r[i];
+ }
+ }
+
+ // Compute beta.
+ final double deltaOld = delta;
+ final double[] newSteepestDescent = preconditioner.precondition(point, r);
+ delta = 0;
+ for (int i = 0; i < n; ++i) {
+ delta += r[i] * newSteepestDescent[i];
+ }
+
+ final double beta;
+ if (updateFormula == ConjugateGradientFormula.FLETCHER_REEVES) {
+ beta = delta / deltaOld;
+ } else {
+ double deltaMid = 0;
+ for (int i = 0; i < r.length; ++i) {
+ deltaMid += r[i] * steepestDescent[i];
+ }
+ beta = (delta - deltaMid) / deltaOld;
+ }
+ steepestDescent = newSteepestDescent;
+
+ // Compute conjugate search direction.
+ if (iter % n == 0 ||
+ beta < 0) {
+ // Break conjugation: reset search direction.
+ searchDirection = steepestDescent.clone();
+ } else {
+ // Compute new conjugate search direction.
+ for (int i = 0; i < n; ++i) {
+ searchDirection[i] = steepestDescent[i] + beta * searchDirection[i];
+ }
+ }
+ }
+ }
+
+ /**
+ * Find the upper bound b ensuring bracketing of a root between a and b.
+ *
+ * @param f function whose root must be bracketed.
+ * @param a lower bound of the interval.
+ * @param h initial step to try.
+ * @return b such that f(a) and f(b) have opposite signs.
+ * @throws MathIllegalStateException if no bracket can be found.
+ */
+ private double findUpperBound(final UnivariateFunction f,
+ final double a, final double h) {
+ final double yA = f.value(a);
+ double yB = yA;
+ for (double step = h; step < Double.MAX_VALUE; step *= FastMath.max(2, yA / yB)) {
+ final double b = a + step;
+ yB = f.value(b);
+ if (yA * yB <= 0) {
+ return b;
+ }
+ }
+ throw new MathIllegalStateException(LocalizedFormats.UNABLE_TO_BRACKET_OPTIMUM_IN_LINE_SEARCH);
+ }
+
+ /** Default identity preconditioner. */
+ public static class IdentityPreconditioner implements Preconditioner {
+
+ /** {@inheritDoc} */
+ public double[] precondition(double[] variables, double[] r) {
+ return r.clone();
+ }
+ }
+
+ /** Internal class for line search.
+ * <p>
+ * The function represented by this class is the dot product of
+ * the objective function gradient and the search direction. Its
+ * value is zero when the gradient is orthogonal to the search
+ * direction, i.e. when the objective function value is a local
+ * extremum along the search direction.
+ * </p>
+ */
+ private class LineSearchFunction implements UnivariateFunction {
+ /** Search direction. */
+ private final double[] searchDirection;
+
+ /** Simple constructor.
+ * @param searchDirection search direction
+ */
+ LineSearchFunction(final double[] searchDirection) {
+ this.searchDirection = searchDirection;
+ }
+
+ /** {@inheritDoc} */
+ public double value(double x) {
+ // current point in the search direction
+ final double[] shiftedPoint = point.clone();
+ for (int i = 0; i < shiftedPoint.length; ++i) {
+ shiftedPoint[i] += x * searchDirection[i];
+ }
+
+ // gradient of the objective function
+ final double[] gradient = computeObjectiveGradient(shiftedPoint);
+
+ // dot product with the search direction
+ double dotProduct = 0;
+ for (int i = 0; i < gradient.length; ++i) {
+ dotProduct += gradient[i] * searchDirection[i];
+ }
+
+ return dotProduct;
+ }
+ }
+}
diff --git a/src/main/java/org/apache/commons/math3/optimization/general/Preconditioner.java b/src/main/java/org/apache/commons/math3/optimization/general/Preconditioner.java
new file mode 100644
index 0000000..7142e76
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/optimization/general/Preconditioner.java
@@ -0,0 +1,46 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math3.optimization.general;
+
+/**
+ * This interface represents a preconditioner for differentiable scalar
+ * objective function optimizers.
+ * @deprecated As of 3.1 (to be removed in 4.0).
+ * @since 2.0
+ */
+@Deprecated
+public interface Preconditioner {
+ /**
+ * Precondition a search direction.
+ * <p>
+ * The returned preconditioned search direction must be computed fast or
+ * the algorithm performances will drop drastically. A classical approach
+ * is to compute only the diagonal elements of the hessian and to divide
+ * the raw search direction by these elements if they are all positive.
+ * If at least one of them is negative, it is safer to return a clone of
+ * the raw search direction as if the hessian was the identity matrix. The
+ * rationale for this simplified choice is that a negative diagonal element
+ * means the current point is far from the optimum and preconditioning will
+ * not be efficient anyway in this case.
+ * </p>
+ * @param point current point at which the search direction was computed
+ * @param r raw search direction (i.e. opposite of the gradient)
+ * @return approximation of H<sup>-1</sup>r where H is the objective function hessian
+ */
+ double[] precondition(double[] point, double[] r);
+}
diff --git a/src/main/java/org/apache/commons/math3/optimization/general/package-info.java b/src/main/java/org/apache/commons/math3/optimization/general/package-info.java
new file mode 100644
index 0000000..ba140ce
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/optimization/general/package-info.java
@@ -0,0 +1,22 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/**
+ *
+ * This package provides optimization algorithms that require derivatives.
+ *
+ */
+package org.apache.commons.math3.optimization.general;
diff --git a/src/main/java/org/apache/commons/math3/optimization/linear/AbstractLinearOptimizer.java b/src/main/java/org/apache/commons/math3/optimization/linear/AbstractLinearOptimizer.java
new file mode 100644
index 0000000..921d877
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/optimization/linear/AbstractLinearOptimizer.java
@@ -0,0 +1,162 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math3.optimization.linear;
+
+import java.util.Collection;
+import java.util.Collections;
+
+import org.apache.commons.math3.exception.MathIllegalStateException;
+import org.apache.commons.math3.exception.MaxCountExceededException;
+import org.apache.commons.math3.optimization.GoalType;
+import org.apache.commons.math3.optimization.PointValuePair;
+
+/**
+ * Base class for implementing linear optimizers.
+ * <p>
+ * This base class handles the boilerplate methods associated to thresholds
+ * settings and iterations counters.
+ *
+ * @deprecated As of 3.1 (to be removed in 4.0).
+ * @since 2.0
+ */
+@Deprecated
+public abstract class AbstractLinearOptimizer implements LinearOptimizer {
+
+ /** Default maximal number of iterations allowed. */
+ public static final int DEFAULT_MAX_ITERATIONS = 100;
+
+ /**
+ * Linear objective function.
+ * @since 2.1
+ */
+ private LinearObjectiveFunction function;
+
+ /**
+ * Linear constraints.
+ * @since 2.1
+ */
+ private Collection<LinearConstraint> linearConstraints;
+
+ /**
+ * Type of optimization goal: either {@link GoalType#MAXIMIZE} or {@link GoalType#MINIMIZE}.
+ * @since 2.1
+ */
+ private GoalType goal;
+
+ /**
+ * Whether to restrict the variables to non-negative values.
+ * @since 2.1
+ */
+ private boolean nonNegative;
+
+ /** Maximal number of iterations allowed. */
+ private int maxIterations;
+
+ /** Number of iterations already performed. */
+ private int iterations;
+
+ /**
+ * Simple constructor with default settings.
+ * <p>The maximal number of evaluation is set to its default value.</p>
+ */
+ protected AbstractLinearOptimizer() {
+ setMaxIterations(DEFAULT_MAX_ITERATIONS);
+ }
+
+ /**
+ * @return {@code true} if the variables are restricted to non-negative values.
+ */
+ protected boolean restrictToNonNegative() {
+ return nonNegative;
+ }
+
+ /**
+ * @return the optimization type.
+ */
+ protected GoalType getGoalType() {
+ return goal;
+ }
+
+ /**
+ * @return the optimization type.
+ */
+ protected LinearObjectiveFunction getFunction() {
+ return function;
+ }
+
+ /**
+ * @return the optimization type.
+ */
+ protected Collection<LinearConstraint> getConstraints() {
+ return Collections.unmodifiableCollection(linearConstraints);
+ }
+
+ /** {@inheritDoc} */
+ public void setMaxIterations(int maxIterations) {
+ this.maxIterations = maxIterations;
+ }
+
+ /** {@inheritDoc} */
+ public int getMaxIterations() {
+ return maxIterations;
+ }
+
+ /** {@inheritDoc} */
+ public int getIterations() {
+ return iterations;
+ }
+
+ /**
+ * Increment the iterations counter by 1.
+ * @exception MaxCountExceededException if the maximal number of iterations is exceeded
+ */
+ protected void incrementIterationsCounter()
+ throws MaxCountExceededException {
+ if (++iterations > maxIterations) {
+ throw new MaxCountExceededException(maxIterations);
+ }
+ }
+
+ /** {@inheritDoc} */
+ public PointValuePair optimize(final LinearObjectiveFunction f,
+ final Collection<LinearConstraint> constraints,
+ final GoalType goalType, final boolean restrictToNonNegative)
+ throws MathIllegalStateException {
+
+ // store linear problem characteristics
+ this.function = f;
+ this.linearConstraints = constraints;
+ this.goal = goalType;
+ this.nonNegative = restrictToNonNegative;
+
+ iterations = 0;
+
+ // solve the problem
+ return doOptimize();
+
+ }
+
+ /**
+ * Perform the bulk of optimization algorithm.
+ * @return the point/value pair giving the optimal value for objective function
+ * @exception MathIllegalStateException if no solution fulfilling the constraints
+ * can be found in the allowed number of iterations
+ */
+ protected abstract PointValuePair doOptimize() throws MathIllegalStateException;
+
+}
diff --git a/src/main/java/org/apache/commons/math3/optimization/linear/LinearConstraint.java b/src/main/java/org/apache/commons/math3/optimization/linear/LinearConstraint.java
new file mode 100644
index 0000000..b3d70d4
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/optimization/linear/LinearConstraint.java
@@ -0,0 +1,236 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math3.optimization.linear;
+
+import java.io.IOException;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+import java.io.Serializable;
+
+import org.apache.commons.math3.linear.MatrixUtils;
+import org.apache.commons.math3.linear.RealVector;
+import org.apache.commons.math3.linear.ArrayRealVector;
+
+
+/**
+ * A linear constraint for a linear optimization problem.
+ * <p>
+ * A linear constraint has one of the forms:
+ * <ul>
+ * <li>c<sub>1</sub>x<sub>1</sub> + ... c<sub>n</sub>x<sub>n</sub> = v</li>
+ * <li>c<sub>1</sub>x<sub>1</sub> + ... c<sub>n</sub>x<sub>n</sub> &lt;= v</li>
+ * <li>c<sub>1</sub>x<sub>1</sub> + ... c<sub>n</sub>x<sub>n</sub> >= v</li>
+ * <li>l<sub>1</sub>x<sub>1</sub> + ... l<sub>n</sub>x<sub>n</sub> + l<sub>cst</sub> =
+ * r<sub>1</sub>x<sub>1</sub> + ... r<sub>n</sub>x<sub>n</sub> + r<sub>cst</sub></li>
+ * <li>l<sub>1</sub>x<sub>1</sub> + ... l<sub>n</sub>x<sub>n</sub> + l<sub>cst</sub> &lt;=
+ * r<sub>1</sub>x<sub>1</sub> + ... r<sub>n</sub>x<sub>n</sub> + r<sub>cst</sub></li>
+ * <li>l<sub>1</sub>x<sub>1</sub> + ... l<sub>n</sub>x<sub>n</sub> + l<sub>cst</sub> >=
+ * r<sub>1</sub>x<sub>1</sub> + ... r<sub>n</sub>x<sub>n</sub> + r<sub>cst</sub></li>
+ * </ul>
+ * The c<sub>i</sub>, l<sub>i</sub> or r<sub>i</sub> are the coefficients of the constraints, the x<sub>i</sub>
+ * are the coordinates of the current point and v is the value of the constraint.
+ * </p>
+ * @deprecated As of 3.1 (to be removed in 4.0).
+ * @since 2.0
+ */
+@Deprecated
+public class LinearConstraint implements Serializable {
+
+ /** Serializable version identifier. */
+ private static final long serialVersionUID = -764632794033034092L;
+
+ /** Coefficients of the constraint (left hand side). */
+ private final transient RealVector coefficients;
+
+ /** Relationship between left and right hand sides (=, &lt;=, >=). */
+ private final Relationship relationship;
+
+ /** Value of the constraint (right hand side). */
+ private final double value;
+
+ /**
+ * Build a constraint involving a single linear equation.
+ * <p>
+ * A linear constraint with a single linear equation has one of the forms:
+ * <ul>
+ * <li>c<sub>1</sub>x<sub>1</sub> + ... c<sub>n</sub>x<sub>n</sub> = v</li>
+ * <li>c<sub>1</sub>x<sub>1</sub> + ... c<sub>n</sub>x<sub>n</sub> &lt;= v</li>
+ * <li>c<sub>1</sub>x<sub>1</sub> + ... c<sub>n</sub>x<sub>n</sub> >= v</li>
+ * </ul>
+ * </p>
+ * @param coefficients The coefficients of the constraint (left hand side)
+ * @param relationship The type of (in)equality used in the constraint
+ * @param value The value of the constraint (right hand side)
+ */
+ public LinearConstraint(final double[] coefficients, final Relationship relationship,
+ final double value) {
+ this(new ArrayRealVector(coefficients), relationship, value);
+ }
+
+ /**
+ * Build a constraint involving a single linear equation.
+ * <p>
+ * A linear constraint with a single linear equation has one of the forms:
+ * <ul>
+ * <li>c<sub>1</sub>x<sub>1</sub> + ... c<sub>n</sub>x<sub>n</sub> = v</li>
+ * <li>c<sub>1</sub>x<sub>1</sub> + ... c<sub>n</sub>x<sub>n</sub> &lt;= v</li>
+ * <li>c<sub>1</sub>x<sub>1</sub> + ... c<sub>n</sub>x<sub>n</sub> >= v</li>
+ * </ul>
+ * </p>
+ * @param coefficients The coefficients of the constraint (left hand side)
+ * @param relationship The type of (in)equality used in the constraint
+ * @param value The value of the constraint (right hand side)
+ */
+ public LinearConstraint(final RealVector coefficients, final Relationship relationship,
+ final double value) {
+ this.coefficients = coefficients;
+ this.relationship = relationship;
+ this.value = value;
+ }
+
+ /**
+ * Build a constraint involving two linear equations.
+ * <p>
+ * A linear constraint with two linear equation has one of the forms:
+ * <ul>
+ * <li>l<sub>1</sub>x<sub>1</sub> + ... l<sub>n</sub>x<sub>n</sub> + l<sub>cst</sub> =
+ * r<sub>1</sub>x<sub>1</sub> + ... r<sub>n</sub>x<sub>n</sub> + r<sub>cst</sub></li>
+ * <li>l<sub>1</sub>x<sub>1</sub> + ... l<sub>n</sub>x<sub>n</sub> + l<sub>cst</sub> &lt;=
+ * r<sub>1</sub>x<sub>1</sub> + ... r<sub>n</sub>x<sub>n</sub> + r<sub>cst</sub></li>
+ * <li>l<sub>1</sub>x<sub>1</sub> + ... l<sub>n</sub>x<sub>n</sub> + l<sub>cst</sub> >=
+ * r<sub>1</sub>x<sub>1</sub> + ... r<sub>n</sub>x<sub>n</sub> + r<sub>cst</sub></li>
+ * </ul>
+ * </p>
+ * @param lhsCoefficients The coefficients of the linear expression on the left hand side of the constraint
+ * @param lhsConstant The constant term of the linear expression on the left hand side of the constraint
+ * @param relationship The type of (in)equality used in the constraint
+ * @param rhsCoefficients The coefficients of the linear expression on the right hand side of the constraint
+ * @param rhsConstant The constant term of the linear expression on the right hand side of the constraint
+ */
+ public LinearConstraint(final double[] lhsCoefficients, final double lhsConstant,
+ final Relationship relationship,
+ final double[] rhsCoefficients, final double rhsConstant) {
+ double[] sub = new double[lhsCoefficients.length];
+ for (int i = 0; i < sub.length; ++i) {
+ sub[i] = lhsCoefficients[i] - rhsCoefficients[i];
+ }
+ this.coefficients = new ArrayRealVector(sub, false);
+ this.relationship = relationship;
+ this.value = rhsConstant - lhsConstant;
+ }
+
+ /**
+ * Build a constraint involving two linear equations.
+ * <p>
+ * A linear constraint with two linear equation has one of the forms:
+ * <ul>
+ * <li>l<sub>1</sub>x<sub>1</sub> + ... l<sub>n</sub>x<sub>n</sub> + l<sub>cst</sub> =
+ * r<sub>1</sub>x<sub>1</sub> + ... r<sub>n</sub>x<sub>n</sub> + r<sub>cst</sub></li>
+ * <li>l<sub>1</sub>x<sub>1</sub> + ... l<sub>n</sub>x<sub>n</sub> + l<sub>cst</sub> &lt;=
+ * r<sub>1</sub>x<sub>1</sub> + ... r<sub>n</sub>x<sub>n</sub> + r<sub>cst</sub></li>
+ * <li>l<sub>1</sub>x<sub>1</sub> + ... l<sub>n</sub>x<sub>n</sub> + l<sub>cst</sub> >=
+ * r<sub>1</sub>x<sub>1</sub> + ... r<sub>n</sub>x<sub>n</sub> + r<sub>cst</sub></li>
+ * </ul>
+ * </p>
+ * @param lhsCoefficients The coefficients of the linear expression on the left hand side of the constraint
+ * @param lhsConstant The constant term of the linear expression on the left hand side of the constraint
+ * @param relationship The type of (in)equality used in the constraint
+ * @param rhsCoefficients The coefficients of the linear expression on the right hand side of the constraint
+ * @param rhsConstant The constant term of the linear expression on the right hand side of the constraint
+ */
+ public LinearConstraint(final RealVector lhsCoefficients, final double lhsConstant,
+ final Relationship relationship,
+ final RealVector rhsCoefficients, final double rhsConstant) {
+ this.coefficients = lhsCoefficients.subtract(rhsCoefficients);
+ this.relationship = relationship;
+ this.value = rhsConstant - lhsConstant;
+ }
+
+ /**
+ * Get the coefficients of the constraint (left hand side).
+ * @return coefficients of the constraint (left hand side)
+ */
+ public RealVector getCoefficients() {
+ return coefficients;
+ }
+
+ /**
+ * Get the relationship between left and right hand sides.
+ * @return relationship between left and right hand sides
+ */
+ public Relationship getRelationship() {
+ return relationship;
+ }
+
+ /**
+ * Get the value of the constraint (right hand side).
+ * @return value of the constraint (right hand side)
+ */
+ public double getValue() {
+ return value;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public boolean equals(Object other) {
+
+ if (this == other) {
+ return true;
+ }
+
+ if (other instanceof LinearConstraint) {
+ LinearConstraint rhs = (LinearConstraint) other;
+ return (relationship == rhs.relationship) &&
+ (value == rhs.value) &&
+ coefficients.equals(rhs.coefficients);
+ }
+ return false;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public int hashCode() {
+ return relationship.hashCode() ^
+ Double.valueOf(value).hashCode() ^
+ coefficients.hashCode();
+ }
+
+ /**
+ * Serialize the instance.
+ * @param oos stream where object should be written
+ * @throws IOException if object cannot be written to stream
+ */
+ private void writeObject(ObjectOutputStream oos)
+ throws IOException {
+ oos.defaultWriteObject();
+ MatrixUtils.serializeRealVector(coefficients, oos);
+ }
+
+ /**
+ * Deserialize the instance.
+ * @param ois stream from which the object should be read
+ * @throws ClassNotFoundException if a class in the stream cannot be found
+ * @throws IOException if object cannot be read from the stream
+ */
+ private void readObject(ObjectInputStream ois)
+ throws ClassNotFoundException, IOException {
+ ois.defaultReadObject();
+ MatrixUtils.deserializeRealVector(this, "coefficients", ois);
+ }
+
+}
diff --git a/src/main/java/org/apache/commons/math3/optimization/linear/LinearObjectiveFunction.java b/src/main/java/org/apache/commons/math3/optimization/linear/LinearObjectiveFunction.java
new file mode 100644
index 0000000..824a139
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/optimization/linear/LinearObjectiveFunction.java
@@ -0,0 +1,150 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math3.optimization.linear;
+
+import java.io.IOException;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+import java.io.Serializable;
+
+import org.apache.commons.math3.linear.MatrixUtils;
+import org.apache.commons.math3.linear.RealVector;
+import org.apache.commons.math3.linear.ArrayRealVector;
+
+/**
+ * An objective function for a linear optimization problem.
+ * <p>
+ * A linear objective function has one the form:
+ * <pre>
+ * c<sub>1</sub>x<sub>1</sub> + ... c<sub>n</sub>x<sub>n</sub> + d
+ * </pre>
+ * The c<sub>i</sub> and d are the coefficients of the equation,
+ * the x<sub>i</sub> are the coordinates of the current point.
+ * </p>
+ * @deprecated As of 3.1 (to be removed in 4.0).
+ * @since 2.0
+ */
+@Deprecated
+public class LinearObjectiveFunction implements Serializable {
+
+ /** Serializable version identifier. */
+ private static final long serialVersionUID = -4531815507568396090L;
+
+ /** Coefficients of the constraint (c<sub>i</sub>). */
+ private final transient RealVector coefficients;
+
+ /** Constant term of the linear equation. */
+ private final double constantTerm;
+
+ /**
+ * @param coefficients The coefficients for the linear equation being optimized
+ * @param constantTerm The constant term of the linear equation
+ */
+ public LinearObjectiveFunction(double[] coefficients, double constantTerm) {
+ this(new ArrayRealVector(coefficients), constantTerm);
+ }
+
+ /**
+ * @param coefficients The coefficients for the linear equation being optimized
+ * @param constantTerm The constant term of the linear equation
+ */
+ public LinearObjectiveFunction(RealVector coefficients, double constantTerm) {
+ this.coefficients = coefficients;
+ this.constantTerm = constantTerm;
+ }
+
+ /**
+ * Get the coefficients of the linear equation being optimized.
+ * @return coefficients of the linear equation being optimized
+ */
+ public RealVector getCoefficients() {
+ return coefficients;
+ }
+
+ /**
+ * Get the constant of the linear equation being optimized.
+ * @return constant of the linear equation being optimized
+ */
+ public double getConstantTerm() {
+ return constantTerm;
+ }
+
+ /**
+ * Compute the value of the linear equation at the current point
+ * @param point point at which linear equation must be evaluated
+ * @return value of the linear equation at the current point
+ */
+ public double getValue(final double[] point) {
+ return coefficients.dotProduct(new ArrayRealVector(point, false)) + constantTerm;
+ }
+
+ /**
+ * Compute the value of the linear equation at the current point
+ * @param point point at which linear equation must be evaluated
+ * @return value of the linear equation at the current point
+ */
+ public double getValue(final RealVector point) {
+ return coefficients.dotProduct(point) + constantTerm;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public boolean equals(Object other) {
+
+ if (this == other) {
+ return true;
+ }
+
+ if (other instanceof LinearObjectiveFunction) {
+ LinearObjectiveFunction rhs = (LinearObjectiveFunction) other;
+ return (constantTerm == rhs.constantTerm) && coefficients.equals(rhs.coefficients);
+ }
+
+ return false;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public int hashCode() {
+ return Double.valueOf(constantTerm).hashCode() ^ coefficients.hashCode();
+ }
+
+ /**
+ * Serialize the instance.
+ * @param oos stream where object should be written
+ * @throws IOException if object cannot be written to stream
+ */
+ private void writeObject(ObjectOutputStream oos)
+ throws IOException {
+ oos.defaultWriteObject();
+ MatrixUtils.serializeRealVector(coefficients, oos);
+ }
+
+ /**
+ * Deserialize the instance.
+ * @param ois stream from which the object should be read
+ * @throws ClassNotFoundException if a class in the stream cannot be found
+ * @throws IOException if object cannot be read from the stream
+ */
+ private void readObject(ObjectInputStream ois)
+ throws ClassNotFoundException, IOException {
+ ois.defaultReadObject();
+ MatrixUtils.deserializeRealVector(this, "coefficients", ois);
+ }
+
+}
diff --git a/src/main/java/org/apache/commons/math3/optimization/linear/LinearOptimizer.java b/src/main/java/org/apache/commons/math3/optimization/linear/LinearOptimizer.java
new file mode 100644
index 0000000..610d0cb
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/optimization/linear/LinearOptimizer.java
@@ -0,0 +1,92 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math3.optimization.linear;
+
+import java.util.Collection;
+
+import org.apache.commons.math3.exception.MathIllegalStateException;
+import org.apache.commons.math3.optimization.GoalType;
+import org.apache.commons.math3.optimization.PointValuePair;
+
+/**
+ * This interface represents an optimization algorithm for linear problems.
+ * <p>Optimization algorithms find the input point set that either {@link GoalType
+ * maximize or minimize} an objective function. In the linear case the form of
+ * the function is restricted to
+ * <pre>
+ * c<sub>1</sub>x<sub>1</sub> + ... c<sub>n</sub>x<sub>n</sub> = v
+ * </pre>
+ * and there may be linear constraints too, of one of the forms:
+ * <ul>
+ * <li>c<sub>1</sub>x<sub>1</sub> + ... c<sub>n</sub>x<sub>n</sub> = v</li>
+ * <li>c<sub>1</sub>x<sub>1</sub> + ... c<sub>n</sub>x<sub>n</sub> &lt;= v</li>
+ * <li>c<sub>1</sub>x<sub>1</sub> + ... c<sub>n</sub>x<sub>n</sub> >= v</li>
+ * <li>l<sub>1</sub>x<sub>1</sub> + ... l<sub>n</sub>x<sub>n</sub> + l<sub>cst</sub> =
+ * r<sub>1</sub>x<sub>1</sub> + ... r<sub>n</sub>x<sub>n</sub> + r<sub>cst</sub></li>
+ * <li>l<sub>1</sub>x<sub>1</sub> + ... l<sub>n</sub>x<sub>n</sub> + l<sub>cst</sub> &lt;=
+ * r<sub>1</sub>x<sub>1</sub> + ... r<sub>n</sub>x<sub>n</sub> + r<sub>cst</sub></li>
+ * <li>l<sub>1</sub>x<sub>1</sub> + ... l<sub>n</sub>x<sub>n</sub> + l<sub>cst</sub> >=
+ * r<sub>1</sub>x<sub>1</sub> + ... r<sub>n</sub>x<sub>n</sub> + r<sub>cst</sub></li>
+ * </ul>
+ * where the c<sub>i</sub>, l<sub>i</sub> or r<sub>i</sub> are the coefficients of
+ * the constraints, the x<sub>i</sub> are the coordinates of the current point and
+ * v is the value of the constraint.
+ * </p>
+ * @deprecated As of 3.1 (to be removed in 4.0).
+ * @since 2.0
+ */
+@Deprecated
+public interface LinearOptimizer {
+
+ /**
+ * Set the maximal number of iterations of the algorithm.
+ * @param maxIterations maximal number of function calls
+ */
+ void setMaxIterations(int maxIterations);
+
+ /**
+ * Get the maximal number of iterations of the algorithm.
+ * @return maximal number of iterations
+ */
+ int getMaxIterations();
+
+ /**
+ * Get the number of iterations realized by the algorithm.
+ * <p>
+ * The number of evaluations corresponds to the last call to the
+ * {@link #optimize(LinearObjectiveFunction, Collection, GoalType, boolean) optimize}
+ * method. It is 0 if the method has not been called yet.
+ * </p>
+ * @return number of iterations
+ */
+ int getIterations();
+
+ /**
+ * Optimizes an objective function.
+ * @param f linear objective function
+ * @param constraints linear constraints
+ * @param goalType type of optimization goal: either {@link GoalType#MAXIMIZE} or {@link GoalType#MINIMIZE}
+ * @param restrictToNonNegative whether to restrict the variables to non-negative values
+ * @return point/value pair giving the optimal value for objective function
+ * @exception MathIllegalStateException if no solution fulfilling the constraints
+ * can be found in the allowed number of iterations
+ */
+ PointValuePair optimize(LinearObjectiveFunction f, Collection<LinearConstraint> constraints,
+ GoalType goalType, boolean restrictToNonNegative) throws MathIllegalStateException;
+
+}
diff --git a/src/main/java/org/apache/commons/math3/optimization/linear/NoFeasibleSolutionException.java b/src/main/java/org/apache/commons/math3/optimization/linear/NoFeasibleSolutionException.java
new file mode 100644
index 0000000..c585c3a
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/optimization/linear/NoFeasibleSolutionException.java
@@ -0,0 +1,42 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math3.optimization.linear;
+
+import org.apache.commons.math3.exception.MathIllegalStateException;
+import org.apache.commons.math3.exception.util.LocalizedFormats;
+
+/**
+ * This class represents exceptions thrown by optimizers when no solution fulfills the constraints.
+ *
+ * @deprecated As of 3.1 (to be removed in 4.0).
+ * @since 2.0
+ */
+@Deprecated
+public class NoFeasibleSolutionException extends MathIllegalStateException {
+
+ /** Serializable version identifier. */
+ private static final long serialVersionUID = -3044253632189082760L;
+
+ /**
+ * Simple constructor using a default message.
+ */
+ public NoFeasibleSolutionException() {
+ super(LocalizedFormats.NO_FEASIBLE_SOLUTION);
+ }
+
+}
diff --git a/src/main/java/org/apache/commons/math3/optimization/linear/Relationship.java b/src/main/java/org/apache/commons/math3/optimization/linear/Relationship.java
new file mode 100644
index 0000000..b1ca087
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/optimization/linear/Relationship.java
@@ -0,0 +1,68 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math3.optimization.linear;
+
+/**
+ * Types of relationships between two cells in a Solver {@link LinearConstraint}.
+ * @deprecated As of 3.1 (to be removed in 4.0).
+ * @since 2.0
+ */
+@Deprecated
+public enum Relationship {
+
+ /** Equality relationship. */
+ EQ("="),
+
+ /** Lesser than or equal relationship. */
+ LEQ("<="),
+
+ /** Greater than or equal relationship. */
+ GEQ(">=");
+
+ /** Display string for the relationship. */
+ private final String stringValue;
+
+ /** Simple constructor.
+ * @param stringValue display string for the relationship
+ */
+ Relationship(String stringValue) {
+ this.stringValue = stringValue;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public String toString() {
+ return stringValue;
+ }
+
+ /**
+ * Get the relationship obtained when multiplying all coefficients by -1.
+ * @return relationship obtained when multiplying all coefficients by -1
+ */
+ public Relationship oppositeRelationship() {
+ switch (this) {
+ case LEQ :
+ return GEQ;
+ case GEQ :
+ return LEQ;
+ default :
+ return EQ;
+ }
+ }
+
+}
diff --git a/src/main/java/org/apache/commons/math3/optimization/linear/SimplexSolver.java b/src/main/java/org/apache/commons/math3/optimization/linear/SimplexSolver.java
new file mode 100644
index 0000000..1e5dbda
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/optimization/linear/SimplexSolver.java
@@ -0,0 +1,238 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math3.optimization.linear;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.apache.commons.math3.exception.MaxCountExceededException;
+import org.apache.commons.math3.optimization.PointValuePair;
+import org.apache.commons.math3.util.Precision;
+
+
+/**
+ * Solves a linear problem using the Two-Phase Simplex Method.
+ *
+ * @deprecated As of 3.1 (to be removed in 4.0).
+ * @since 2.0
+ */
+@Deprecated
+public class SimplexSolver extends AbstractLinearOptimizer {
+
+ /** Default amount of error to accept for algorithm convergence. */
+ private static final double DEFAULT_EPSILON = 1.0e-6;
+
+ /** Default amount of error to accept in floating point comparisons (as ulps). */
+ private static final int DEFAULT_ULPS = 10;
+
+ /** Amount of error to accept for algorithm convergence. */
+ private final double epsilon;
+
+ /** Amount of error to accept in floating point comparisons (as ulps). */
+ private final int maxUlps;
+
+ /**
+ * Build a simplex solver with default settings.
+ */
+ public SimplexSolver() {
+ this(DEFAULT_EPSILON, DEFAULT_ULPS);
+ }
+
+ /**
+ * Build a simplex solver with a specified accepted amount of error
+ * @param epsilon the amount of error to accept for algorithm convergence
+ * @param maxUlps amount of error to accept in floating point comparisons
+ */
+ public SimplexSolver(final double epsilon, final int maxUlps) {
+ this.epsilon = epsilon;
+ this.maxUlps = maxUlps;
+ }
+
+ /**
+ * Returns the column with the most negative coefficient in the objective function row.
+ * @param tableau simple tableau for the problem
+ * @return column with the most negative coefficient
+ */
+ private Integer getPivotColumn(SimplexTableau tableau) {
+ double minValue = 0;
+ Integer minPos = null;
+ for (int i = tableau.getNumObjectiveFunctions(); i < tableau.getWidth() - 1; i++) {
+ final double entry = tableau.getEntry(0, i);
+ // check if the entry is strictly smaller than the current minimum
+ // do not use a ulp/epsilon check
+ if (entry < minValue) {
+ minValue = entry;
+ minPos = i;
+ }
+ }
+ return minPos;
+ }
+
+ /**
+ * Returns the row with the minimum ratio as given by the minimum ratio test (MRT).
+ * @param tableau simple tableau for the problem
+ * @param col the column to test the ratio of. See {@link #getPivotColumn(SimplexTableau)}
+ * @return row with the minimum ratio
+ */
+ private Integer getPivotRow(SimplexTableau tableau, final int col) {
+ // create a list of all the rows that tie for the lowest score in the minimum ratio test
+ List<Integer> minRatioPositions = new ArrayList<Integer>();
+ double minRatio = Double.MAX_VALUE;
+ for (int i = tableau.getNumObjectiveFunctions(); i < tableau.getHeight(); i++) {
+ final double rhs = tableau.getEntry(i, tableau.getWidth() - 1);
+ final double entry = tableau.getEntry(i, col);
+
+ if (Precision.compareTo(entry, 0d, maxUlps) > 0) {
+ final double ratio = rhs / entry;
+ // check if the entry is strictly equal to the current min ratio
+ // do not use a ulp/epsilon check
+ final int cmp = Double.compare(ratio, minRatio);
+ if (cmp == 0) {
+ minRatioPositions.add(i);
+ } else if (cmp < 0) {
+ minRatio = ratio;
+ minRatioPositions = new ArrayList<Integer>();
+ minRatioPositions.add(i);
+ }
+ }
+ }
+
+ if (minRatioPositions.size() == 0) {
+ return null;
+ } else if (minRatioPositions.size() > 1) {
+ // there's a degeneracy as indicated by a tie in the minimum ratio test
+
+ // 1. check if there's an artificial variable that can be forced out of the basis
+ if (tableau.getNumArtificialVariables() > 0) {
+ for (Integer row : minRatioPositions) {
+ for (int i = 0; i < tableau.getNumArtificialVariables(); i++) {
+ int column = i + tableau.getArtificialVariableOffset();
+ final double entry = tableau.getEntry(row, column);
+ if (Precision.equals(entry, 1d, maxUlps) && row.equals(tableau.getBasicRow(column))) {
+ return row;
+ }
+ }
+ }
+ }
+
+ // 2. apply Bland's rule to prevent cycling:
+ // take the row for which the corresponding basic variable has the smallest index
+ //
+ // see http://www.stanford.edu/class/msande310/blandrule.pdf
+ // see http://en.wikipedia.org/wiki/Bland%27s_rule (not equivalent to the above paper)
+ //
+ // Additional heuristic: if we did not get a solution after half of maxIterations
+ // revert to the simple case of just returning the top-most row
+ // This heuristic is based on empirical data gathered while investigating MATH-828.
+ if (getIterations() < getMaxIterations() / 2) {
+ Integer minRow = null;
+ int minIndex = tableau.getWidth();
+ final int varStart = tableau.getNumObjectiveFunctions();
+ final int varEnd = tableau.getWidth() - 1;
+ for (Integer row : minRatioPositions) {
+ for (int i = varStart; i < varEnd && !row.equals(minRow); i++) {
+ final Integer basicRow = tableau.getBasicRow(i);
+ if (basicRow != null && basicRow.equals(row) && i < minIndex) {
+ minIndex = i;
+ minRow = row;
+ }
+ }
+ }
+ return minRow;
+ }
+ }
+ return minRatioPositions.get(0);
+ }
+
+ /**
+ * Runs one iteration of the Simplex method on the given model.
+ * @param tableau simple tableau for the problem
+ * @throws MaxCountExceededException if the maximal iteration count has been exceeded
+ * @throws UnboundedSolutionException if the model is found not to have a bounded solution
+ */
+ protected void doIteration(final SimplexTableau tableau)
+ throws MaxCountExceededException, UnboundedSolutionException {
+
+ incrementIterationsCounter();
+
+ Integer pivotCol = getPivotColumn(tableau);
+ Integer pivotRow = getPivotRow(tableau, pivotCol);
+ if (pivotRow == null) {
+ throw new UnboundedSolutionException();
+ }
+
+ // set the pivot element to 1
+ double pivotVal = tableau.getEntry(pivotRow, pivotCol);
+ tableau.divideRow(pivotRow, pivotVal);
+
+ // set the rest of the pivot column to 0
+ for (int i = 0; i < tableau.getHeight(); i++) {
+ if (i != pivotRow) {
+ final double multiplier = tableau.getEntry(i, pivotCol);
+ tableau.subtractRow(i, pivotRow, multiplier);
+ }
+ }
+ }
+
+ /**
+ * Solves Phase 1 of the Simplex method.
+ * @param tableau simple tableau for the problem
+ * @throws MaxCountExceededException if the maximal iteration count has been exceeded
+ * @throws UnboundedSolutionException if the model is found not to have a bounded solution
+ * @throws NoFeasibleSolutionException if there is no feasible solution
+ */
+ protected void solvePhase1(final SimplexTableau tableau)
+ throws MaxCountExceededException, UnboundedSolutionException, NoFeasibleSolutionException {
+
+ // make sure we're in Phase 1
+ if (tableau.getNumArtificialVariables() == 0) {
+ return;
+ }
+
+ while (!tableau.isOptimal()) {
+ doIteration(tableau);
+ }
+
+ // if W is not zero then we have no feasible solution
+ if (!Precision.equals(tableau.getEntry(0, tableau.getRhsOffset()), 0d, epsilon)) {
+ throw new NoFeasibleSolutionException();
+ }
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public PointValuePair doOptimize()
+ throws MaxCountExceededException, UnboundedSolutionException, NoFeasibleSolutionException {
+ final SimplexTableau tableau =
+ new SimplexTableau(getFunction(),
+ getConstraints(),
+ getGoalType(),
+ restrictToNonNegative(),
+ epsilon,
+ maxUlps);
+
+ solvePhase1(tableau);
+ tableau.dropPhase1Objective();
+
+ while (!tableau.isOptimal()) {
+ doIteration(tableau);
+ }
+ return tableau.getSolution();
+ }
+
+}
diff --git a/src/main/java/org/apache/commons/math3/optimization/linear/SimplexTableau.java b/src/main/java/org/apache/commons/math3/optimization/linear/SimplexTableau.java
new file mode 100644
index 0000000..321f8c0
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/optimization/linear/SimplexTableau.java
@@ -0,0 +1,637 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math3.optimization.linear;
+
+import java.io.IOException;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+import java.io.Serializable;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+import java.util.TreeSet;
+
+import org.apache.commons.math3.linear.Array2DRowRealMatrix;
+import org.apache.commons.math3.linear.MatrixUtils;
+import org.apache.commons.math3.linear.RealMatrix;
+import org.apache.commons.math3.linear.RealVector;
+import org.apache.commons.math3.optimization.GoalType;
+import org.apache.commons.math3.optimization.PointValuePair;
+import org.apache.commons.math3.util.FastMath;
+import org.apache.commons.math3.util.Precision;
+
+/**
+ * A tableau for use in the Simplex method.
+ *
+ * <p>
+ * Example:
+ * <pre>
+ * W | Z | x1 | x2 | x- | s1 | s2 | a1 | RHS
+ * ---------------------------------------------------
+ * -1 0 0 0 0 0 0 1 0 &lt;= phase 1 objective
+ * 0 1 -15 -10 0 0 0 0 0 &lt;= phase 2 objective
+ * 0 0 1 0 0 1 0 0 2 &lt;= constraint 1
+ * 0 0 0 1 0 0 1 0 3 &lt;= constraint 2
+ * 0 0 1 1 0 0 0 1 4 &lt;= constraint 3
+ * </pre>
+ * W: Phase 1 objective function</br>
+ * Z: Phase 2 objective function</br>
+ * x1 &amp; x2: Decision variables</br>
+ * x-: Extra decision variable to allow for negative values</br>
+ * s1 &amp; s2: Slack/Surplus variables</br>
+ * a1: Artificial variable</br>
+ * RHS: Right hand side</br>
+ * </p>
+ * @deprecated As of 3.1 (to be removed in 4.0).
+ * @since 2.0
+ */
+@Deprecated
+class SimplexTableau implements Serializable {
+
+ /** Column label for negative vars. */
+ private static final String NEGATIVE_VAR_COLUMN_LABEL = "x-";
+
+ /** Default amount of error to accept in floating point comparisons (as ulps). */
+ private static final int DEFAULT_ULPS = 10;
+
+ /** The cut-off threshold to zero-out entries. */
+ private static final double CUTOFF_THRESHOLD = 1e-12;
+
+ /** Serializable version identifier. */
+ private static final long serialVersionUID = -1369660067587938365L;
+
+ /** Linear objective function. */
+ private final LinearObjectiveFunction f;
+
+ /** Linear constraints. */
+ private final List<LinearConstraint> constraints;
+
+ /** Whether to restrict the variables to non-negative values. */
+ private final boolean restrictToNonNegative;
+
+ /** The variables each column represents */
+ private final List<String> columnLabels = new ArrayList<String>();
+
+ /** Simple tableau. */
+ private transient RealMatrix tableau;
+
+ /** Number of decision variables. */
+ private final int numDecisionVariables;
+
+ /** Number of slack variables. */
+ private final int numSlackVariables;
+
+ /** Number of artificial variables. */
+ private int numArtificialVariables;
+
+ /** Amount of error to accept when checking for optimality. */
+ private final double epsilon;
+
+ /** Amount of error to accept in floating point comparisons. */
+ private final int maxUlps;
+
+ /**
+ * Build a tableau for a linear problem.
+ * @param f linear objective function
+ * @param constraints linear constraints
+ * @param goalType type of optimization goal: either {@link GoalType#MAXIMIZE} or {@link GoalType#MINIMIZE}
+ * @param restrictToNonNegative whether to restrict the variables to non-negative values
+ * @param epsilon amount of error to accept when checking for optimality
+ */
+ SimplexTableau(final LinearObjectiveFunction f,
+ final Collection<LinearConstraint> constraints,
+ final GoalType goalType, final boolean restrictToNonNegative,
+ final double epsilon) {
+ this(f, constraints, goalType, restrictToNonNegative, epsilon, DEFAULT_ULPS);
+ }
+
+ /**
+ * Build a tableau for a linear problem.
+ * @param f linear objective function
+ * @param constraints linear constraints
+ * @param goalType type of optimization goal: either {@link GoalType#MAXIMIZE} or {@link GoalType#MINIMIZE}
+ * @param restrictToNonNegative whether to restrict the variables to non-negative values
+ * @param epsilon amount of error to accept when checking for optimality
+ * @param maxUlps amount of error to accept in floating point comparisons
+ */
+ SimplexTableau(final LinearObjectiveFunction f,
+ final Collection<LinearConstraint> constraints,
+ final GoalType goalType, final boolean restrictToNonNegative,
+ final double epsilon,
+ final int maxUlps) {
+ this.f = f;
+ this.constraints = normalizeConstraints(constraints);
+ this.restrictToNonNegative = restrictToNonNegative;
+ this.epsilon = epsilon;
+ this.maxUlps = maxUlps;
+ this.numDecisionVariables = f.getCoefficients().getDimension() +
+ (restrictToNonNegative ? 0 : 1);
+ this.numSlackVariables = getConstraintTypeCounts(Relationship.LEQ) +
+ getConstraintTypeCounts(Relationship.GEQ);
+ this.numArtificialVariables = getConstraintTypeCounts(Relationship.EQ) +
+ getConstraintTypeCounts(Relationship.GEQ);
+ this.tableau = createTableau(goalType == GoalType.MAXIMIZE);
+ initializeColumnLabels();
+ }
+
+ /**
+ * Initialize the labels for the columns.
+ */
+ protected void initializeColumnLabels() {
+ if (getNumObjectiveFunctions() == 2) {
+ columnLabels.add("W");
+ }
+ columnLabels.add("Z");
+ for (int i = 0; i < getOriginalNumDecisionVariables(); i++) {
+ columnLabels.add("x" + i);
+ }
+ if (!restrictToNonNegative) {
+ columnLabels.add(NEGATIVE_VAR_COLUMN_LABEL);
+ }
+ for (int i = 0; i < getNumSlackVariables(); i++) {
+ columnLabels.add("s" + i);
+ }
+ for (int i = 0; i < getNumArtificialVariables(); i++) {
+ columnLabels.add("a" + i);
+ }
+ columnLabels.add("RHS");
+ }
+
+ /**
+ * Create the tableau by itself.
+ * @param maximize if true, goal is to maximize the objective function
+ * @return created tableau
+ */
+ protected RealMatrix createTableau(final boolean maximize) {
+
+ // create a matrix of the correct size
+ int width = numDecisionVariables + numSlackVariables +
+ numArtificialVariables + getNumObjectiveFunctions() + 1; // + 1 is for RHS
+ int height = constraints.size() + getNumObjectiveFunctions();
+ Array2DRowRealMatrix matrix = new Array2DRowRealMatrix(height, width);
+
+ // initialize the objective function rows
+ if (getNumObjectiveFunctions() == 2) {
+ matrix.setEntry(0, 0, -1);
+ }
+ int zIndex = (getNumObjectiveFunctions() == 1) ? 0 : 1;
+ matrix.setEntry(zIndex, zIndex, maximize ? 1 : -1);
+ RealVector objectiveCoefficients =
+ maximize ? f.getCoefficients().mapMultiply(-1) : f.getCoefficients();
+ copyArray(objectiveCoefficients.toArray(), matrix.getDataRef()[zIndex]);
+ matrix.setEntry(zIndex, width - 1,
+ maximize ? f.getConstantTerm() : -1 * f.getConstantTerm());
+
+ if (!restrictToNonNegative) {
+ matrix.setEntry(zIndex, getSlackVariableOffset() - 1,
+ getInvertedCoefficientSum(objectiveCoefficients));
+ }
+
+ // initialize the constraint rows
+ int slackVar = 0;
+ int artificialVar = 0;
+ for (int i = 0; i < constraints.size(); i++) {
+ LinearConstraint constraint = constraints.get(i);
+ int row = getNumObjectiveFunctions() + i;
+
+ // decision variable coefficients
+ copyArray(constraint.getCoefficients().toArray(), matrix.getDataRef()[row]);
+
+ // x-
+ if (!restrictToNonNegative) {
+ matrix.setEntry(row, getSlackVariableOffset() - 1,
+ getInvertedCoefficientSum(constraint.getCoefficients()));
+ }
+
+ // RHS
+ matrix.setEntry(row, width - 1, constraint.getValue());
+
+ // slack variables
+ if (constraint.getRelationship() == Relationship.LEQ) {
+ matrix.setEntry(row, getSlackVariableOffset() + slackVar++, 1); // slack
+ } else if (constraint.getRelationship() == Relationship.GEQ) {
+ matrix.setEntry(row, getSlackVariableOffset() + slackVar++, -1); // excess
+ }
+
+ // artificial variables
+ if ((constraint.getRelationship() == Relationship.EQ) ||
+ (constraint.getRelationship() == Relationship.GEQ)) {
+ matrix.setEntry(0, getArtificialVariableOffset() + artificialVar, 1);
+ matrix.setEntry(row, getArtificialVariableOffset() + artificialVar++, 1);
+ matrix.setRowVector(0, matrix.getRowVector(0).subtract(matrix.getRowVector(row)));
+ }
+ }
+
+ return matrix;
+ }
+
+ /**
+ * Get new versions of the constraints which have positive right hand sides.
+ * @param originalConstraints original (not normalized) constraints
+ * @return new versions of the constraints
+ */
+ public List<LinearConstraint> normalizeConstraints(Collection<LinearConstraint> originalConstraints) {
+ List<LinearConstraint> normalized = new ArrayList<LinearConstraint>(originalConstraints.size());
+ for (LinearConstraint constraint : originalConstraints) {
+ normalized.add(normalize(constraint));
+ }
+ return normalized;
+ }
+
+ /**
+ * Get a new equation equivalent to this one with a positive right hand side.
+ * @param constraint reference constraint
+ * @return new equation
+ */
+ private LinearConstraint normalize(final LinearConstraint constraint) {
+ if (constraint.getValue() < 0) {
+ return new LinearConstraint(constraint.getCoefficients().mapMultiply(-1),
+ constraint.getRelationship().oppositeRelationship(),
+ -1 * constraint.getValue());
+ }
+ return new LinearConstraint(constraint.getCoefficients(),
+ constraint.getRelationship(), constraint.getValue());
+ }
+
+ /**
+ * Get the number of objective functions in this tableau.
+ * @return 2 for Phase 1. 1 for Phase 2.
+ */
+ protected final int getNumObjectiveFunctions() {
+ return this.numArtificialVariables > 0 ? 2 : 1;
+ }
+
+ /**
+ * Get a count of constraints corresponding to a specified relationship.
+ * @param relationship relationship to count
+ * @return number of constraint with the specified relationship
+ */
+ private int getConstraintTypeCounts(final Relationship relationship) {
+ int count = 0;
+ for (final LinearConstraint constraint : constraints) {
+ if (constraint.getRelationship() == relationship) {
+ ++count;
+ }
+ }
+ return count;
+ }
+
+ /**
+ * Get the -1 times the sum of all coefficients in the given array.
+ * @param coefficients coefficients to sum
+ * @return the -1 times the sum of all coefficients in the given array.
+ */
+ protected static double getInvertedCoefficientSum(final RealVector coefficients) {
+ double sum = 0;
+ for (double coefficient : coefficients.toArray()) {
+ sum -= coefficient;
+ }
+ return sum;
+ }
+
+ /**
+ * Checks whether the given column is basic.
+ * @param col index of the column to check
+ * @return the row that the variable is basic in. null if the column is not basic
+ */
+ protected Integer getBasicRow(final int col) {
+ Integer row = null;
+ for (int i = 0; i < getHeight(); i++) {
+ final double entry = getEntry(i, col);
+ if (Precision.equals(entry, 1d, maxUlps) && (row == null)) {
+ row = i;
+ } else if (!Precision.equals(entry, 0d, maxUlps)) {
+ return null;
+ }
+ }
+ return row;
+ }
+
+ /**
+ * Removes the phase 1 objective function, positive cost non-artificial variables,
+ * and the non-basic artificial variables from this tableau.
+ */
+ protected void dropPhase1Objective() {
+ if (getNumObjectiveFunctions() == 1) {
+ return;
+ }
+
+ Set<Integer> columnsToDrop = new TreeSet<Integer>();
+ columnsToDrop.add(0);
+
+ // positive cost non-artificial variables
+ for (int i = getNumObjectiveFunctions(); i < getArtificialVariableOffset(); i++) {
+ final double entry = tableau.getEntry(0, i);
+ if (Precision.compareTo(entry, 0d, epsilon) > 0) {
+ columnsToDrop.add(i);
+ }
+ }
+
+ // non-basic artificial variables
+ for (int i = 0; i < getNumArtificialVariables(); i++) {
+ int col = i + getArtificialVariableOffset();
+ if (getBasicRow(col) == null) {
+ columnsToDrop.add(col);
+ }
+ }
+
+ double[][] matrix = new double[getHeight() - 1][getWidth() - columnsToDrop.size()];
+ for (int i = 1; i < getHeight(); i++) {
+ int col = 0;
+ for (int j = 0; j < getWidth(); j++) {
+ if (!columnsToDrop.contains(j)) {
+ matrix[i - 1][col++] = tableau.getEntry(i, j);
+ }
+ }
+ }
+
+ // remove the columns in reverse order so the indices are correct
+ Integer[] drop = columnsToDrop.toArray(new Integer[columnsToDrop.size()]);
+ for (int i = drop.length - 1; i >= 0; i--) {
+ columnLabels.remove((int) drop[i]);
+ }
+
+ this.tableau = new Array2DRowRealMatrix(matrix);
+ this.numArtificialVariables = 0;
+ }
+
+ /**
+ * @param src the source array
+ * @param dest the destination array
+ */
+ private void copyArray(final double[] src, final double[] dest) {
+ System.arraycopy(src, 0, dest, getNumObjectiveFunctions(), src.length);
+ }
+
+ /**
+ * Returns whether the problem is at an optimal state.
+ * @return whether the model has been solved
+ */
+ boolean isOptimal() {
+ for (int i = getNumObjectiveFunctions(); i < getWidth() - 1; i++) {
+ final double entry = tableau.getEntry(0, i);
+ if (Precision.compareTo(entry, 0d, epsilon) < 0) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ /**
+ * Get the current solution.
+ * @return current solution
+ */
+ protected PointValuePair getSolution() {
+ int negativeVarColumn = columnLabels.indexOf(NEGATIVE_VAR_COLUMN_LABEL);
+ Integer negativeVarBasicRow = negativeVarColumn > 0 ? getBasicRow(negativeVarColumn) : null;
+ double mostNegative = negativeVarBasicRow == null ? 0 : getEntry(negativeVarBasicRow, getRhsOffset());
+
+ Set<Integer> basicRows = new HashSet<Integer>();
+ double[] coefficients = new double[getOriginalNumDecisionVariables()];
+ for (int i = 0; i < coefficients.length; i++) {
+ int colIndex = columnLabels.indexOf("x" + i);
+ if (colIndex < 0) {
+ coefficients[i] = 0;
+ continue;
+ }
+ Integer basicRow = getBasicRow(colIndex);
+ if (basicRow != null && basicRow == 0) {
+ // if the basic row is found to be the objective function row
+ // set the coefficient to 0 -> this case handles unconstrained
+ // variables that are still part of the objective function
+ coefficients[i] = 0;
+ } else if (basicRows.contains(basicRow)) {
+ // if multiple variables can take a given value
+ // then we choose the first and set the rest equal to 0
+ coefficients[i] = 0 - (restrictToNonNegative ? 0 : mostNegative);
+ } else {
+ basicRows.add(basicRow);
+ coefficients[i] =
+ (basicRow == null ? 0 : getEntry(basicRow, getRhsOffset())) -
+ (restrictToNonNegative ? 0 : mostNegative);
+ }
+ }
+ return new PointValuePair(coefficients, f.getValue(coefficients));
+ }
+
+ /**
+ * Subtracts a multiple of one row from another.
+ * <p>
+ * After application of this operation, the following will hold:
+ * <pre>minuendRow = minuendRow - multiple * subtrahendRow</pre>
+ *
+ * @param dividendRow index of the row
+ * @param divisor value of the divisor
+ */
+ protected void divideRow(final int dividendRow, final double divisor) {
+ for (int j = 0; j < getWidth(); j++) {
+ tableau.setEntry(dividendRow, j, tableau.getEntry(dividendRow, j) / divisor);
+ }
+ }
+
+ /**
+ * Subtracts a multiple of one row from another.
+ * <p>
+ * After application of this operation, the following will hold:
+ * <pre>minuendRow = minuendRow - multiple * subtrahendRow</pre>
+ *
+ * @param minuendRow row index
+ * @param subtrahendRow row index
+ * @param multiple multiplication factor
+ */
+ protected void subtractRow(final int minuendRow, final int subtrahendRow,
+ final double multiple) {
+ for (int i = 0; i < getWidth(); i++) {
+ double result = tableau.getEntry(minuendRow, i) - tableau.getEntry(subtrahendRow, i) * multiple;
+ // cut-off values smaller than the CUTOFF_THRESHOLD, otherwise may lead to numerical instabilities
+ if (FastMath.abs(result) < CUTOFF_THRESHOLD) {
+ result = 0.0;
+ }
+ tableau.setEntry(minuendRow, i, result);
+ }
+ }
+
+ /**
+ * Get the width of the tableau.
+ * @return width of the tableau
+ */
+ protected final int getWidth() {
+ return tableau.getColumnDimension();
+ }
+
+ /**
+ * Get the height of the tableau.
+ * @return height of the tableau
+ */
+ protected final int getHeight() {
+ return tableau.getRowDimension();
+ }
+
+ /**
+ * Get an entry of the tableau.
+ * @param row row index
+ * @param column column index
+ * @return entry at (row, column)
+ */
+ protected final double getEntry(final int row, final int column) {
+ return tableau.getEntry(row, column);
+ }
+
+ /**
+ * Set an entry of the tableau.
+ * @param row row index
+ * @param column column index
+ * @param value for the entry
+ */
+ protected final void setEntry(final int row, final int column,
+ final double value) {
+ tableau.setEntry(row, column, value);
+ }
+
+ /**
+ * Get the offset of the first slack variable.
+ * @return offset of the first slack variable
+ */
+ protected final int getSlackVariableOffset() {
+ return getNumObjectiveFunctions() + numDecisionVariables;
+ }
+
+ /**
+ * Get the offset of the first artificial variable.
+ * @return offset of the first artificial variable
+ */
+ protected final int getArtificialVariableOffset() {
+ return getNumObjectiveFunctions() + numDecisionVariables + numSlackVariables;
+ }
+
+ /**
+ * Get the offset of the right hand side.
+ * @return offset of the right hand side
+ */
+ protected final int getRhsOffset() {
+ return getWidth() - 1;
+ }
+
+ /**
+ * Get the number of decision variables.
+ * <p>
+ * If variables are not restricted to positive values, this will include 1 extra decision variable to represent
+ * the absolute value of the most negative variable.
+ *
+ * @return number of decision variables
+ * @see #getOriginalNumDecisionVariables()
+ */
+ protected final int getNumDecisionVariables() {
+ return numDecisionVariables;
+ }
+
+ /**
+ * Get the original number of decision variables.
+ * @return original number of decision variables
+ * @see #getNumDecisionVariables()
+ */
+ protected final int getOriginalNumDecisionVariables() {
+ return f.getCoefficients().getDimension();
+ }
+
+ /**
+ * Get the number of slack variables.
+ * @return number of slack variables
+ */
+ protected final int getNumSlackVariables() {
+ return numSlackVariables;
+ }
+
+ /**
+ * Get the number of artificial variables.
+ * @return number of artificial variables
+ */
+ protected final int getNumArtificialVariables() {
+ return numArtificialVariables;
+ }
+
+ /**
+ * Get the tableau data.
+ * @return tableau data
+ */
+ protected final double[][] getData() {
+ return tableau.getData();
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public boolean equals(Object other) {
+
+ if (this == other) {
+ return true;
+ }
+
+ if (other instanceof SimplexTableau) {
+ SimplexTableau rhs = (SimplexTableau) other;
+ return (restrictToNonNegative == rhs.restrictToNonNegative) &&
+ (numDecisionVariables == rhs.numDecisionVariables) &&
+ (numSlackVariables == rhs.numSlackVariables) &&
+ (numArtificialVariables == rhs.numArtificialVariables) &&
+ (epsilon == rhs.epsilon) &&
+ (maxUlps == rhs.maxUlps) &&
+ f.equals(rhs.f) &&
+ constraints.equals(rhs.constraints) &&
+ tableau.equals(rhs.tableau);
+ }
+ return false;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public int hashCode() {
+ return Boolean.valueOf(restrictToNonNegative).hashCode() ^
+ numDecisionVariables ^
+ numSlackVariables ^
+ numArtificialVariables ^
+ Double.valueOf(epsilon).hashCode() ^
+ maxUlps ^
+ f.hashCode() ^
+ constraints.hashCode() ^
+ tableau.hashCode();
+ }
+
+ /**
+ * Serialize the instance.
+ * @param oos stream where object should be written
+ * @throws IOException if object cannot be written to stream
+ */
+ private void writeObject(ObjectOutputStream oos)
+ throws IOException {
+ oos.defaultWriteObject();
+ MatrixUtils.serializeRealMatrix(tableau, oos);
+ }
+
+ /**
+ * Deserialize the instance.
+ * @param ois stream from which the object should be read
+ * @throws ClassNotFoundException if a class in the stream cannot be found
+ * @throws IOException if object cannot be read from the stream
+ */
+ private void readObject(ObjectInputStream ois)
+ throws ClassNotFoundException, IOException {
+ ois.defaultReadObject();
+ MatrixUtils.deserializeRealMatrix(this, "tableau", ois);
+ }
+}
diff --git a/src/main/java/org/apache/commons/math3/optimization/linear/UnboundedSolutionException.java b/src/main/java/org/apache/commons/math3/optimization/linear/UnboundedSolutionException.java
new file mode 100644
index 0000000..a8fe77b
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/optimization/linear/UnboundedSolutionException.java
@@ -0,0 +1,42 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math3.optimization.linear;
+
+import org.apache.commons.math3.exception.MathIllegalStateException;
+import org.apache.commons.math3.exception.util.LocalizedFormats;
+
+/**
+ * This class represents exceptions thrown by optimizers when a solution escapes to infinity.
+ *
+ * @deprecated As of 3.1 (to be removed in 4.0).
+ * @since 2.0
+ */
+@Deprecated
+public class UnboundedSolutionException extends MathIllegalStateException {
+
+ /** Serializable version identifier. */
+ private static final long serialVersionUID = 940539497277290619L;
+
+ /**
+ * Simple constructor using a default message.
+ */
+ public UnboundedSolutionException() {
+ super(LocalizedFormats.UNBOUNDED_SOLUTION);
+ }
+
+}
diff --git a/src/main/java/org/apache/commons/math3/optimization/linear/package-info.java b/src/main/java/org/apache/commons/math3/optimization/linear/package-info.java
new file mode 100644
index 0000000..b61b03b
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/optimization/linear/package-info.java
@@ -0,0 +1,22 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/**
+ *
+ * This package provides optimization algorithms for linear constrained problems.
+ *
+ */
+package org.apache.commons.math3.optimization.linear;
diff --git a/src/main/java/org/apache/commons/math3/optimization/package-info.java b/src/main/java/org/apache/commons/math3/optimization/package-info.java
new file mode 100644
index 0000000..2831237
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/optimization/package-info.java
@@ -0,0 +1,74 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/**
+ *
+ *
+ * <h2>All classes and sub-packages of this package are deprecated.</h2>
+ *
+ * <h3>Please use their replacements, to be found under
+ *
+ * <ul>
+ * <li>{@link org.apache.commons.math3.optim}
+ * <li>{@link org.apache.commons.math3.fitting}
+ * </ul>
+ *
+ * </h3>
+ *
+ * <p>This package provides common interfaces for the optimization algorithms provided in
+ * sub-packages. The main interfaces defines optimizers and convergence checkers. The functions that
+ * are optimized by the algorithms provided by this package and its sub-packages are a subset of the
+ * one defined in the <code>analysis</code> package, namely the real and vector valued functions.
+ * These functions are called objective function here. When the goal is to minimize, the functions
+ * are often called cost function, this name is not used in this package.
+ *
+ * <p>Optimizers are the algorithms that will either minimize or maximize, the objective function by
+ * changing its input variables set until an optimal set is found. There are only four interfaces
+ * defining the common behavior of optimizers, one for each supported type of objective function:
+ *
+ * <ul>
+ * <li>{@link org.apache.commons.math3.optimization.univariate.UnivariateOptimizer
+ * UnivariateOptimizer} for {@link org.apache.commons.math3.analysis.UnivariateFunction
+ * univariate real functions}
+ * <li>{@link org.apache.commons.math3.optimization.MultivariateOptimizer MultivariateOptimizer}
+ * for {@link org.apache.commons.math3.analysis.MultivariateFunction multivariate real
+ * functions}
+ * <li>{@link org.apache.commons.math3.optimization.MultivariateDifferentiableOptimizer
+ * MultivariateDifferentiableOptimizer} for {@link
+ * org.apache.commons.math3.analysis.differentiation.MultivariateDifferentiableFunction
+ * multivariate differentiable real functions}
+ * <li>{@link org.apache.commons.math3.optimization.MultivariateDifferentiableVectorOptimizer
+ * MultivariateDifferentiableVectorOptimizer} for {@link
+ * org.apache.commons.math3.analysis.differentiation.MultivariateDifferentiableVectorFunction
+ * multivariate differentiable vectorial functions}
+ * </ul>
+ *
+ * <p>Despite there are only four types of supported optimizers, it is possible to optimize a
+ * transform a {@link org.apache.commons.math3.analysis.MultivariateVectorFunction
+ * non-differentiable multivariate vectorial function} by converting it to a {@link
+ * org.apache.commons.math3.analysis.MultivariateFunction non-differentiable multivariate real
+ * function} thanks to the {@link org.apache.commons.math3.optimization.LeastSquaresConverter
+ * LeastSquaresConverter} helper class. The transformed function can be optimized using any
+ * implementation of the {@link org.apache.commons.math3.optimization.MultivariateOptimizer
+ * MultivariateOptimizer} interface.
+ *
+ * <p>For each of the four types of supported optimizers, there is a special implementation which
+ * wraps a classical optimizer in order to add it a multi-start feature. This feature call the
+ * underlying optimizer several times in sequence with different starting points and returns the
+ * best optimum found or all optima if desired. This is a classical way to prevent being trapped
+ * into a local extremum when looking for a global one.
+ */
+package org.apache.commons.math3.optimization;
diff --git a/src/main/java/org/apache/commons/math3/optimization/univariate/BaseAbstractUnivariateOptimizer.java b/src/main/java/org/apache/commons/math3/optimization/univariate/BaseAbstractUnivariateOptimizer.java
new file mode 100644
index 0000000..fcacd01
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/optimization/univariate/BaseAbstractUnivariateOptimizer.java
@@ -0,0 +1,162 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math3.optimization.univariate;
+
+import org.apache.commons.math3.util.Incrementor;
+import org.apache.commons.math3.exception.MaxCountExceededException;
+import org.apache.commons.math3.exception.TooManyEvaluationsException;
+import org.apache.commons.math3.exception.NullArgumentException;
+import org.apache.commons.math3.analysis.UnivariateFunction;
+import org.apache.commons.math3.optimization.GoalType;
+import org.apache.commons.math3.optimization.ConvergenceChecker;
+
+/**
+ * Provide a default implementation for several functions useful to generic
+ * optimizers.
+ *
+ * @deprecated As of 3.1 (to be removed in 4.0).
+ * @since 2.0
+ */
+@Deprecated
+public abstract class BaseAbstractUnivariateOptimizer
+ implements UnivariateOptimizer {
+ /** Convergence checker. */
+ private final ConvergenceChecker<UnivariatePointValuePair> checker;
+ /** Evaluations counter. */
+ private final Incrementor evaluations = new Incrementor();
+ /** Optimization type */
+ private GoalType goal;
+ /** Lower end of search interval. */
+ private double searchMin;
+ /** Higher end of search interval. */
+ private double searchMax;
+ /** Initial guess . */
+ private double searchStart;
+ /** Function to optimize. */
+ private UnivariateFunction function;
+
+ /**
+ * @param checker Convergence checking procedure.
+ */
+ protected BaseAbstractUnivariateOptimizer(ConvergenceChecker<UnivariatePointValuePair> checker) {
+ this.checker = checker;
+ }
+
+ /** {@inheritDoc} */
+ public int getMaxEvaluations() {
+ return evaluations.getMaximalCount();
+ }
+
+ /** {@inheritDoc} */
+ public int getEvaluations() {
+ return evaluations.getCount();
+ }
+
+ /**
+ * @return the optimization type.
+ */
+ public GoalType getGoalType() {
+ return goal;
+ }
+ /**
+ * @return the lower end of the search interval.
+ */
+ public double getMin() {
+ return searchMin;
+ }
+ /**
+ * @return the higher end of the search interval.
+ */
+ public double getMax() {
+ return searchMax;
+ }
+ /**
+ * @return the initial guess.
+ */
+ public double getStartValue() {
+ return searchStart;
+ }
+
+ /**
+ * Compute the objective function value.
+ *
+ * @param point Point at which the objective function must be evaluated.
+ * @return the objective function value at specified point.
+ * @throws TooManyEvaluationsException if the maximal number of evaluations
+ * is exceeded.
+ */
+ protected double computeObjectiveValue(double point) {
+ try {
+ evaluations.incrementCount();
+ } catch (MaxCountExceededException e) {
+ throw new TooManyEvaluationsException(e.getMax());
+ }
+ return function.value(point);
+ }
+
+ /** {@inheritDoc} */
+ public UnivariatePointValuePair optimize(int maxEval, UnivariateFunction f,
+ GoalType goalType,
+ double min, double max,
+ double startValue) {
+ // Checks.
+ if (f == null) {
+ throw new NullArgumentException();
+ }
+ if (goalType == null) {
+ throw new NullArgumentException();
+ }
+
+ // Reset.
+ searchMin = min;
+ searchMax = max;
+ searchStart = startValue;
+ goal = goalType;
+ function = f;
+ evaluations.setMaximalCount(maxEval);
+ evaluations.resetCount();
+
+ // Perform computation.
+ return doOptimize();
+ }
+
+ /** {@inheritDoc} */
+ public UnivariatePointValuePair optimize(int maxEval,
+ UnivariateFunction f,
+ GoalType goalType,
+ double min, double max){
+ return optimize(maxEval, f, goalType, min, max, min + 0.5 * (max - min));
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public ConvergenceChecker<UnivariatePointValuePair> getConvergenceChecker() {
+ return checker;
+ }
+
+ /**
+ * Method for implementing actual optimization algorithms in derived
+ * classes.
+ *
+ * @return the optimum and its corresponding function value.
+ * @throws TooManyEvaluationsException if the maximal number of evaluations
+ * is exceeded.
+ */
+ protected abstract UnivariatePointValuePair doOptimize();
+}
diff --git a/src/main/java/org/apache/commons/math3/optimization/univariate/BaseUnivariateOptimizer.java b/src/main/java/org/apache/commons/math3/optimization/univariate/BaseUnivariateOptimizer.java
new file mode 100644
index 0000000..fcae6f1
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/optimization/univariate/BaseUnivariateOptimizer.java
@@ -0,0 +1,86 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math3.optimization.univariate;
+
+import org.apache.commons.math3.analysis.UnivariateFunction;
+import org.apache.commons.math3.optimization.BaseOptimizer;
+import org.apache.commons.math3.optimization.GoalType;
+
+/**
+ * This interface is mainly intended to enforce the internal coherence of
+ * Commons-Math. Users of the API are advised to base their code on
+ * the following interfaces:
+ * <ul>
+ * <li>{@link org.apache.commons.math3.optimization.univariate.UnivariateOptimizer}</li>
+ * </ul>
+ *
+ * @param <FUNC> Type of the objective function to be optimized.
+ *
+ * @deprecated As of 3.1 (to be removed in 4.0).
+ * @since 3.0
+ */
+@Deprecated
+public interface BaseUnivariateOptimizer<FUNC extends UnivariateFunction>
+ extends BaseOptimizer<UnivariatePointValuePair> {
+ /**
+ * Find an optimum in the given interval.
+ *
+ * An optimizer may require that the interval brackets a single optimum.
+ *
+ * @param f Function to optimize.
+ * @param goalType Type of optimization goal: either
+ * {@link GoalType#MAXIMIZE} or {@link GoalType#MINIMIZE}.
+ * @param min Lower bound for the interval.
+ * @param max Upper bound for the interval.
+ * @param maxEval Maximum number of function evaluations.
+ * @return a (point, value) pair where the function is optimum.
+ * @throws org.apache.commons.math3.exception.TooManyEvaluationsException
+ * if the maximum evaluation count is exceeded.
+ * @throws org.apache.commons.math3.exception.ConvergenceException
+ * if the optimizer detects a convergence problem.
+ * @throws IllegalArgumentException if {@code min > max} or the endpoints
+ * do not satisfy the requirements specified by the optimizer.
+ */
+ UnivariatePointValuePair optimize(int maxEval, FUNC f, GoalType goalType,
+ double min, double max);
+
+ /**
+ * Find an optimum in the given interval, start at startValue.
+ * An optimizer may require that the interval brackets a single optimum.
+ *
+ * @param f Function to optimize.
+ * @param goalType Type of optimization goal: either
+ * {@link GoalType#MAXIMIZE} or {@link GoalType#MINIMIZE}.
+ * @param min Lower bound for the interval.
+ * @param max Upper bound for the interval.
+ * @param startValue Start value to use.
+ * @param maxEval Maximum number of function evaluations.
+ * @return a (point, value) pair where the function is optimum.
+ * @throws org.apache.commons.math3.exception.TooManyEvaluationsException
+ * if the maximum evaluation count is exceeded.
+ * @throws org.apache.commons.math3.exception.ConvergenceException if the
+ * optimizer detects a convergence problem.
+ * @throws IllegalArgumentException if {@code min > max} or the endpoints
+ * do not satisfy the requirements specified by the optimizer.
+ * @throws org.apache.commons.math3.exception.NullArgumentException if any
+ * argument is {@code null}.
+ */
+ UnivariatePointValuePair optimize(int maxEval, FUNC f, GoalType goalType,
+ double min, double max,
+ double startValue);
+}
diff --git a/src/main/java/org/apache/commons/math3/optimization/univariate/BracketFinder.java b/src/main/java/org/apache/commons/math3/optimization/univariate/BracketFinder.java
new file mode 100644
index 0000000..cd3057f
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/optimization/univariate/BracketFinder.java
@@ -0,0 +1,289 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math3.optimization.univariate;
+
+import org.apache.commons.math3.util.FastMath;
+import org.apache.commons.math3.util.Incrementor;
+import org.apache.commons.math3.exception.NotStrictlyPositiveException;
+import org.apache.commons.math3.exception.TooManyEvaluationsException;
+import org.apache.commons.math3.exception.MaxCountExceededException;
+import org.apache.commons.math3.analysis.UnivariateFunction;
+import org.apache.commons.math3.optimization.GoalType;
+
+/**
+ * Provide an interval that brackets a local optimum of a function.
+ * This code is based on a Python implementation (from <em>SciPy</em>,
+ * module {@code optimize.py} v0.5).
+ *
+ * @deprecated As of 3.1 (to be removed in 4.0).
+ * @since 2.2
+ */
+@Deprecated
+public class BracketFinder {
+ /** Tolerance to avoid division by zero. */
+ private static final double EPS_MIN = 1e-21;
+ /**
+ * Golden section.
+ */
+ private static final double GOLD = 1.618034;
+ /**
+ * Factor for expanding the interval.
+ */
+ private final double growLimit;
+ /**
+ * Counter for function evaluations.
+ */
+ private final Incrementor evaluations = new Incrementor();
+ /**
+ * Lower bound of the bracket.
+ */
+ private double lo;
+ /**
+ * Higher bound of the bracket.
+ */
+ private double hi;
+ /**
+ * Point inside the bracket.
+ */
+ private double mid;
+ /**
+ * Function value at {@link #lo}.
+ */
+ private double fLo;
+ /**
+ * Function value at {@link #hi}.
+ */
+ private double fHi;
+ /**
+ * Function value at {@link #mid}.
+ */
+ private double fMid;
+
+ /**
+ * Constructor with default values {@code 100, 50} (see the
+ * {@link #BracketFinder(double,int) other constructor}).
+ */
+ public BracketFinder() {
+ this(100, 50);
+ }
+
+ /**
+ * Create a bracketing interval finder.
+ *
+ * @param growLimit Expanding factor.
+ * @param maxEvaluations Maximum number of evaluations allowed for finding
+ * a bracketing interval.
+ */
+ public BracketFinder(double growLimit,
+ int maxEvaluations) {
+ if (growLimit <= 0) {
+ throw new NotStrictlyPositiveException(growLimit);
+ }
+ if (maxEvaluations <= 0) {
+ throw new NotStrictlyPositiveException(maxEvaluations);
+ }
+
+ this.growLimit = growLimit;
+ evaluations.setMaximalCount(maxEvaluations);
+ }
+
+ /**
+ * Search new points that bracket a local optimum of the function.
+ *
+ * @param func Function whose optimum should be bracketed.
+ * @param goal {@link GoalType Goal type}.
+ * @param xA Initial point.
+ * @param xB Initial point.
+ * @throws TooManyEvaluationsException if the maximum number of evaluations
+ * is exceeded.
+ */
+ public void search(UnivariateFunction func, GoalType goal, double xA, double xB) {
+ evaluations.resetCount();
+ final boolean isMinim = goal == GoalType.MINIMIZE;
+
+ double fA = eval(func, xA);
+ double fB = eval(func, xB);
+ if (isMinim ?
+ fA < fB :
+ fA > fB) {
+
+ double tmp = xA;
+ xA = xB;
+ xB = tmp;
+
+ tmp = fA;
+ fA = fB;
+ fB = tmp;
+ }
+
+ double xC = xB + GOLD * (xB - xA);
+ double fC = eval(func, xC);
+
+ while (isMinim ? fC < fB : fC > fB) {
+ double tmp1 = (xB - xA) * (fB - fC);
+ double tmp2 = (xB - xC) * (fB - fA);
+
+ double val = tmp2 - tmp1;
+ double denom = FastMath.abs(val) < EPS_MIN ? 2 * EPS_MIN : 2 * val;
+
+ double w = xB - ((xB - xC) * tmp2 - (xB - xA) * tmp1) / denom;
+ double wLim = xB + growLimit * (xC - xB);
+
+ double fW;
+ if ((w - xC) * (xB - w) > 0) {
+ fW = eval(func, w);
+ if (isMinim ?
+ fW < fC :
+ fW > fC) {
+ xA = xB;
+ xB = w;
+ fA = fB;
+ fB = fW;
+ break;
+ } else if (isMinim ?
+ fW > fB :
+ fW < fB) {
+ xC = w;
+ fC = fW;
+ break;
+ }
+ w = xC + GOLD * (xC - xB);
+ fW = eval(func, w);
+ } else if ((w - wLim) * (wLim - xC) >= 0) {
+ w = wLim;
+ fW = eval(func, w);
+ } else if ((w - wLim) * (xC - w) > 0) {
+ fW = eval(func, w);
+ if (isMinim ?
+ fW < fC :
+ fW > fC) {
+ xB = xC;
+ xC = w;
+ w = xC + GOLD * (xC - xB);
+ fB = fC;
+ fC =fW;
+ fW = eval(func, w);
+ }
+ } else {
+ w = xC + GOLD * (xC - xB);
+ fW = eval(func, w);
+ }
+
+ xA = xB;
+ fA = fB;
+ xB = xC;
+ fB = fC;
+ xC = w;
+ fC = fW;
+ }
+
+ lo = xA;
+ fLo = fA;
+ mid = xB;
+ fMid = fB;
+ hi = xC;
+ fHi = fC;
+
+ if (lo > hi) {
+ double tmp = lo;
+ lo = hi;
+ hi = tmp;
+
+ tmp = fLo;
+ fLo = fHi;
+ fHi = tmp;
+ }
+ }
+
+ /**
+ * @return the number of evalutations.
+ */
+ public int getMaxEvaluations() {
+ return evaluations.getMaximalCount();
+ }
+
+ /**
+ * @return the number of evalutations.
+ */
+ public int getEvaluations() {
+ return evaluations.getCount();
+ }
+
+ /**
+ * @return the lower bound of the bracket.
+ * @see #getFLo()
+ */
+ public double getLo() {
+ return lo;
+ }
+
+ /**
+ * Get function value at {@link #getLo()}.
+ * @return function value at {@link #getLo()}
+ */
+ public double getFLo() {
+ return fLo;
+ }
+
+ /**
+ * @return the higher bound of the bracket.
+ * @see #getFHi()
+ */
+ public double getHi() {
+ return hi;
+ }
+
+ /**
+ * Get function value at {@link #getHi()}.
+ * @return function value at {@link #getHi()}
+ */
+ public double getFHi() {
+ return fHi;
+ }
+
+ /**
+ * @return a point in the middle of the bracket.
+ * @see #getFMid()
+ */
+ public double getMid() {
+ return mid;
+ }
+
+ /**
+ * Get function value at {@link #getMid()}.
+ * @return function value at {@link #getMid()}
+ */
+ public double getFMid() {
+ return fMid;
+ }
+
+ /**
+ * @param f Function.
+ * @param x Argument.
+ * @return {@code f(x)}
+ * @throws TooManyEvaluationsException if the maximal number of evaluations is
+ * exceeded.
+ */
+ private double eval(UnivariateFunction f, double x) {
+ try {
+ evaluations.incrementCount();
+ } catch (MaxCountExceededException e) {
+ throw new TooManyEvaluationsException(e.getMax());
+ }
+ return f.value(x);
+ }
+}
diff --git a/src/main/java/org/apache/commons/math3/optimization/univariate/BrentOptimizer.java b/src/main/java/org/apache/commons/math3/optimization/univariate/BrentOptimizer.java
new file mode 100644
index 0000000..763ec99
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/optimization/univariate/BrentOptimizer.java
@@ -0,0 +1,316 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math3.optimization.univariate;
+
+import org.apache.commons.math3.util.Precision;
+import org.apache.commons.math3.util.FastMath;
+import org.apache.commons.math3.exception.NumberIsTooSmallException;
+import org.apache.commons.math3.exception.NotStrictlyPositiveException;
+import org.apache.commons.math3.optimization.ConvergenceChecker;
+import org.apache.commons.math3.optimization.GoalType;
+
+/**
+ * For a function defined on some interval {@code (lo, hi)}, this class
+ * finds an approximation {@code x} to the point at which the function
+ * attains its minimum.
+ * It implements Richard Brent's algorithm (from his book "Algorithms for
+ * Minimization without Derivatives", p. 79) for finding minima of real
+ * univariate functions.
+ * <br/>
+ * This code is an adaptation, partly based on the Python code from SciPy
+ * (module "optimize.py" v0.5); the original algorithm is also modified
+ * <ul>
+ * <li>to use an initial guess provided by the user,</li>
+ * <li>to ensure that the best point encountered is the one returned.</li>
+ * </ul>
+ *
+ * @deprecated As of 3.1 (to be removed in 4.0).
+ * @since 2.0
+ */
+@Deprecated
+public class BrentOptimizer extends BaseAbstractUnivariateOptimizer {
+ /**
+ * Golden section.
+ */
+ private static final double GOLDEN_SECTION = 0.5 * (3 - FastMath.sqrt(5));
+ /**
+ * Minimum relative tolerance.
+ */
+ private static final double MIN_RELATIVE_TOLERANCE = 2 * FastMath.ulp(1d);
+ /**
+ * Relative threshold.
+ */
+ private final double relativeThreshold;
+ /**
+ * Absolute threshold.
+ */
+ private final double absoluteThreshold;
+
+ /**
+ * The arguments are used implement the original stopping criterion
+ * of Brent's algorithm.
+ * {@code abs} and {@code rel} define a tolerance
+ * {@code tol = rel |x| + abs}. {@code rel} should be no smaller than
+ * <em>2 macheps</em> and preferably not much less than <em>sqrt(macheps)</em>,
+ * where <em>macheps</em> is the relative machine precision. {@code abs} must
+ * be positive.
+ *
+ * @param rel Relative threshold.
+ * @param abs Absolute threshold.
+ * @param checker Additional, user-defined, convergence checking
+ * procedure.
+ * @throws NotStrictlyPositiveException if {@code abs <= 0}.
+ * @throws NumberIsTooSmallException if {@code rel < 2 * Math.ulp(1d)}.
+ */
+ public BrentOptimizer(double rel,
+ double abs,
+ ConvergenceChecker<UnivariatePointValuePair> checker) {
+ super(checker);
+
+ if (rel < MIN_RELATIVE_TOLERANCE) {
+ throw new NumberIsTooSmallException(rel, MIN_RELATIVE_TOLERANCE, true);
+ }
+ if (abs <= 0) {
+ throw new NotStrictlyPositiveException(abs);
+ }
+
+ relativeThreshold = rel;
+ absoluteThreshold = abs;
+ }
+
+ /**
+ * The arguments are used for implementing the original stopping criterion
+ * of Brent's algorithm.
+ * {@code abs} and {@code rel} define a tolerance
+ * {@code tol = rel |x| + abs}. {@code rel} should be no smaller than
+ * <em>2 macheps</em> and preferably not much less than <em>sqrt(macheps)</em>,
+ * where <em>macheps</em> is the relative machine precision. {@code abs} must
+ * be positive.
+ *
+ * @param rel Relative threshold.
+ * @param abs Absolute threshold.
+ * @throws NotStrictlyPositiveException if {@code abs <= 0}.
+ * @throws NumberIsTooSmallException if {@code rel < 2 * Math.ulp(1d)}.
+ */
+ public BrentOptimizer(double rel,
+ double abs) {
+ this(rel, abs, null);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ protected UnivariatePointValuePair doOptimize() {
+ final boolean isMinim = getGoalType() == GoalType.MINIMIZE;
+ final double lo = getMin();
+ final double mid = getStartValue();
+ final double hi = getMax();
+
+ // Optional additional convergence criteria.
+ final ConvergenceChecker<UnivariatePointValuePair> checker
+ = getConvergenceChecker();
+
+ double a;
+ double b;
+ if (lo < hi) {
+ a = lo;
+ b = hi;
+ } else {
+ a = hi;
+ b = lo;
+ }
+
+ double x = mid;
+ double v = x;
+ double w = x;
+ double d = 0;
+ double e = 0;
+ double fx = computeObjectiveValue(x);
+ if (!isMinim) {
+ fx = -fx;
+ }
+ double fv = fx;
+ double fw = fx;
+
+ UnivariatePointValuePair previous = null;
+ UnivariatePointValuePair current
+ = new UnivariatePointValuePair(x, isMinim ? fx : -fx);
+ // Best point encountered so far (which is the initial guess).
+ UnivariatePointValuePair best = current;
+
+ int iter = 0;
+ while (true) {
+ final double m = 0.5 * (a + b);
+ final double tol1 = relativeThreshold * FastMath.abs(x) + absoluteThreshold;
+ final double tol2 = 2 * tol1;
+
+ // Default stopping criterion.
+ final boolean stop = FastMath.abs(x - m) <= tol2 - 0.5 * (b - a);
+ if (!stop) {
+ double p = 0;
+ double q = 0;
+ double r = 0;
+ double u = 0;
+
+ if (FastMath.abs(e) > tol1) { // Fit parabola.
+ r = (x - w) * (fx - fv);
+ q = (x - v) * (fx - fw);
+ p = (x - v) * q - (x - w) * r;
+ q = 2 * (q - r);
+
+ if (q > 0) {
+ p = -p;
+ } else {
+ q = -q;
+ }
+
+ r = e;
+ e = d;
+
+ if (p > q * (a - x) &&
+ p < q * (b - x) &&
+ FastMath.abs(p) < FastMath.abs(0.5 * q * r)) {
+ // Parabolic interpolation step.
+ d = p / q;
+ u = x + d;
+
+ // f must not be evaluated too close to a or b.
+ if (u - a < tol2 || b - u < tol2) {
+ if (x <= m) {
+ d = tol1;
+ } else {
+ d = -tol1;
+ }
+ }
+ } else {
+ // Golden section step.
+ if (x < m) {
+ e = b - x;
+ } else {
+ e = a - x;
+ }
+ d = GOLDEN_SECTION * e;
+ }
+ } else {
+ // Golden section step.
+ if (x < m) {
+ e = b - x;
+ } else {
+ e = a - x;
+ }
+ d = GOLDEN_SECTION * e;
+ }
+
+ // Update by at least "tol1".
+ if (FastMath.abs(d) < tol1) {
+ if (d >= 0) {
+ u = x + tol1;
+ } else {
+ u = x - tol1;
+ }
+ } else {
+ u = x + d;
+ }
+
+ double fu = computeObjectiveValue(u);
+ if (!isMinim) {
+ fu = -fu;
+ }
+
+ // User-defined convergence checker.
+ previous = current;
+ current = new UnivariatePointValuePair(u, isMinim ? fu : -fu);
+ best = best(best,
+ best(previous,
+ current,
+ isMinim),
+ isMinim);
+
+ if (checker != null && checker.converged(iter, previous, current)) {
+ return best;
+ }
+
+ // Update a, b, v, w and x.
+ if (fu <= fx) {
+ if (u < x) {
+ b = x;
+ } else {
+ a = x;
+ }
+ v = w;
+ fv = fw;
+ w = x;
+ fw = fx;
+ x = u;
+ fx = fu;
+ } else {
+ if (u < x) {
+ a = u;
+ } else {
+ b = u;
+ }
+ if (fu <= fw ||
+ Precision.equals(w, x)) {
+ v = w;
+ fv = fw;
+ w = u;
+ fw = fu;
+ } else if (fu <= fv ||
+ Precision.equals(v, x) ||
+ Precision.equals(v, w)) {
+ v = u;
+ fv = fu;
+ }
+ }
+ } else { // Default termination (Brent's criterion).
+ return best(best,
+ best(previous,
+ current,
+ isMinim),
+ isMinim);
+ }
+ ++iter;
+ }
+ }
+
+ /**
+ * Selects the best of two points.
+ *
+ * @param a Point and value.
+ * @param b Point and value.
+ * @param isMinim {@code true} if the selected point must be the one with
+ * the lowest value.
+ * @return the best point, or {@code null} if {@code a} and {@code b} are
+ * both {@code null}. When {@code a} and {@code b} have the same function
+ * value, {@code a} is returned.
+ */
+ private UnivariatePointValuePair best(UnivariatePointValuePair a,
+ UnivariatePointValuePair b,
+ boolean isMinim) {
+ if (a == null) {
+ return b;
+ }
+ if (b == null) {
+ return a;
+ }
+
+ if (isMinim) {
+ return a.getValue() <= b.getValue() ? a : b;
+ } else {
+ return a.getValue() >= b.getValue() ? a : b;
+ }
+ }
+}
diff --git a/src/main/java/org/apache/commons/math3/optimization/univariate/SimpleUnivariateValueChecker.java b/src/main/java/org/apache/commons/math3/optimization/univariate/SimpleUnivariateValueChecker.java
new file mode 100644
index 0000000..82c50b6
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/optimization/univariate/SimpleUnivariateValueChecker.java
@@ -0,0 +1,139 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math3.optimization.univariate;
+
+import org.apache.commons.math3.util.FastMath;
+import org.apache.commons.math3.exception.NotStrictlyPositiveException;
+import org.apache.commons.math3.optimization.AbstractConvergenceChecker;
+
+/**
+ * Simple implementation of the
+ * {@link org.apache.commons.math3.optimization.ConvergenceChecker} interface
+ * that uses only objective function values.
+ *
+ * Convergence is considered to have been reached if either the relative
+ * difference between the objective function values is smaller than a
+ * threshold or if either the absolute difference between the objective
+ * function values is smaller than another threshold.
+ * <br/>
+ * The {@link #converged(int,UnivariatePointValuePair,UnivariatePointValuePair)
+ * converged} method will also return {@code true} if the number of iterations
+ * has been set (see {@link #SimpleUnivariateValueChecker(double,double,int)
+ * this constructor}).
+ *
+ * @deprecated As of 3.1 (to be removed in 4.0).
+ * @since 3.1
+ */
+@Deprecated
+public class SimpleUnivariateValueChecker
+ extends AbstractConvergenceChecker<UnivariatePointValuePair> {
+ /**
+ * If {@link #maxIterationCount} is set to this value, the number of
+ * iterations will never cause
+ * {@link #converged(int,UnivariatePointValuePair,UnivariatePointValuePair)}
+ * to return {@code true}.
+ */
+ private static final int ITERATION_CHECK_DISABLED = -1;
+ /**
+ * Number of iterations after which the
+ * {@link #converged(int,UnivariatePointValuePair,UnivariatePointValuePair)}
+ * method will return true (unless the check is disabled).
+ */
+ private final int maxIterationCount;
+
+ /**
+ * Build an instance with default thresholds.
+ * @deprecated See {@link AbstractConvergenceChecker#AbstractConvergenceChecker()}
+ */
+ @Deprecated
+ public SimpleUnivariateValueChecker() {
+ maxIterationCount = ITERATION_CHECK_DISABLED;
+ }
+
+ /** Build an instance with specified thresholds.
+ *
+ * In order to perform only relative checks, the absolute tolerance
+ * must be set to a negative value. In order to perform only absolute
+ * checks, the relative tolerance must be set to a negative value.
+ *
+ * @param relativeThreshold relative tolerance threshold
+ * @param absoluteThreshold absolute tolerance threshold
+ */
+ public SimpleUnivariateValueChecker(final double relativeThreshold,
+ final double absoluteThreshold) {
+ super(relativeThreshold, absoluteThreshold);
+ maxIterationCount = ITERATION_CHECK_DISABLED;
+ }
+
+ /**
+ * Builds an instance with specified thresholds.
+ *
+ * In order to perform only relative checks, the absolute tolerance
+ * must be set to a negative value. In order to perform only absolute
+ * checks, the relative tolerance must be set to a negative value.
+ *
+ * @param relativeThreshold relative tolerance threshold
+ * @param absoluteThreshold absolute tolerance threshold
+ * @param maxIter Maximum iteration count.
+ * @throws NotStrictlyPositiveException if {@code maxIter <= 0}.
+ *
+ * @since 3.1
+ */
+ public SimpleUnivariateValueChecker(final double relativeThreshold,
+ final double absoluteThreshold,
+ final int maxIter) {
+ super(relativeThreshold, absoluteThreshold);
+
+ if (maxIter <= 0) {
+ throw new NotStrictlyPositiveException(maxIter);
+ }
+ maxIterationCount = maxIter;
+ }
+
+ /**
+ * Check if the optimization algorithm has converged considering the
+ * last two points.
+ * This method may be called several time from the same algorithm
+ * iteration with different points. This can be detected by checking the
+ * iteration number at each call if needed. Each time this method is
+ * called, the previous and current point correspond to points with the
+ * same role at each iteration, so they can be compared. As an example,
+ * simplex-based algorithms call this method for all points of the simplex,
+ * not only for the best or worst ones.
+ *
+ * @param iteration Index of current iteration
+ * @param previous Best point in the previous iteration.
+ * @param current Best point in the current iteration.
+ * @return {@code true} if the algorithm has converged.
+ */
+ @Override
+ public boolean converged(final int iteration,
+ final UnivariatePointValuePair previous,
+ final UnivariatePointValuePair current) {
+ if (maxIterationCount != ITERATION_CHECK_DISABLED && iteration >= maxIterationCount) {
+ return true;
+ }
+
+ final double p = previous.getValue();
+ final double c = current.getValue();
+ final double difference = FastMath.abs(p - c);
+ final double size = FastMath.max(FastMath.abs(p), FastMath.abs(c));
+ return difference <= size * getRelativeThreshold() ||
+ difference <= getAbsoluteThreshold();
+ }
+}
diff --git a/src/main/java/org/apache/commons/math3/optimization/univariate/UnivariateMultiStartOptimizer.java b/src/main/java/org/apache/commons/math3/optimization/univariate/UnivariateMultiStartOptimizer.java
new file mode 100644
index 0000000..f63beb2
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/optimization/univariate/UnivariateMultiStartOptimizer.java
@@ -0,0 +1,203 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math3.optimization.univariate;
+
+import java.util.Arrays;
+import java.util.Comparator;
+
+import org.apache.commons.math3.analysis.UnivariateFunction;
+import org.apache.commons.math3.exception.MathIllegalStateException;
+import org.apache.commons.math3.exception.NotStrictlyPositiveException;
+import org.apache.commons.math3.exception.NullArgumentException;
+import org.apache.commons.math3.exception.util.LocalizedFormats;
+import org.apache.commons.math3.random.RandomGenerator;
+import org.apache.commons.math3.optimization.GoalType;
+import org.apache.commons.math3.optimization.ConvergenceChecker;
+
+/**
+ * Special implementation of the {@link UnivariateOptimizer} interface
+ * adding multi-start features to an existing optimizer.
+ *
+ * This class wraps a classical optimizer to use it several times in
+ * turn with different starting points in order to avoid being trapped
+ * into a local extremum when looking for a global one.
+ *
+ * @param <FUNC> Type of the objective function to be optimized.
+ *
+ * @deprecated As of 3.1 (to be removed in 4.0).
+ * @since 3.0
+ */
+@Deprecated
+public class UnivariateMultiStartOptimizer<FUNC extends UnivariateFunction>
+ implements BaseUnivariateOptimizer<FUNC> {
+ /** Underlying classical optimizer. */
+ private final BaseUnivariateOptimizer<FUNC> optimizer;
+ /** Maximal number of evaluations allowed. */
+ private int maxEvaluations;
+ /** Number of evaluations already performed for all starts. */
+ private int totalEvaluations;
+ /** Number of starts to go. */
+ private int starts;
+ /** Random generator for multi-start. */
+ private RandomGenerator generator;
+ /** Found optima. */
+ private UnivariatePointValuePair[] optima;
+
+ /**
+ * Create a multi-start optimizer from a single-start optimizer.
+ *
+ * @param optimizer Single-start optimizer to wrap.
+ * @param starts Number of starts to perform. If {@code starts == 1},
+ * the {@code optimize} methods will return the same solution as
+ * {@code optimizer} would.
+ * @param generator Random generator to use for restarts.
+ * @throws NullArgumentException if {@code optimizer} or {@code generator}
+ * is {@code null}.
+ * @throws NotStrictlyPositiveException if {@code starts < 1}.
+ */
+ public UnivariateMultiStartOptimizer(final BaseUnivariateOptimizer<FUNC> optimizer,
+ final int starts,
+ final RandomGenerator generator) {
+ if (optimizer == null ||
+ generator == null) {
+ throw new NullArgumentException();
+ }
+ if (starts < 1) {
+ throw new NotStrictlyPositiveException(starts);
+ }
+
+ this.optimizer = optimizer;
+ this.starts = starts;
+ this.generator = generator;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public ConvergenceChecker<UnivariatePointValuePair> getConvergenceChecker() {
+ return optimizer.getConvergenceChecker();
+ }
+
+ /** {@inheritDoc} */
+ public int getMaxEvaluations() {
+ return maxEvaluations;
+ }
+
+ /** {@inheritDoc} */
+ public int getEvaluations() {
+ return totalEvaluations;
+ }
+
+ /**
+ * Get all the optima found during the last call to {@link
+ * #optimize(int,UnivariateFunction,GoalType,double,double) optimize}.
+ * The optimizer stores all the optima found during a set of
+ * restarts. The {@link #optimize(int,UnivariateFunction,GoalType,double,double) optimize}
+ * method returns the best point only. This method returns all the points
+ * found at the end of each starts, including the best one already
+ * returned by the {@link #optimize(int,UnivariateFunction,GoalType,double,double) optimize}
+ * method.
+ * <br/>
+ * The returned array as one element for each start as specified
+ * in the constructor. It is ordered with the results from the
+ * runs that did converge first, sorted from best to worst
+ * objective value (i.e in ascending order if minimizing and in
+ * descending order if maximizing), followed by {@code null} elements
+ * corresponding to the runs that did not converge. This means all
+ * elements will be {@code null} if the {@link
+ * #optimize(int,UnivariateFunction,GoalType,double,double) optimize}
+ * method did throw an exception.
+ * This also means that if the first element is not {@code null}, it is
+ * the best point found across all starts.
+ *
+ * @return an array containing the optima.
+ * @throws MathIllegalStateException if {@link
+ * #optimize(int,UnivariateFunction,GoalType,double,double) optimize}
+ * has not been called.
+ */
+ public UnivariatePointValuePair[] getOptima() {
+ if (optima == null) {
+ throw new MathIllegalStateException(LocalizedFormats.NO_OPTIMUM_COMPUTED_YET);
+ }
+ return optima.clone();
+ }
+
+ /** {@inheritDoc} */
+ public UnivariatePointValuePair optimize(int maxEval, final FUNC f,
+ final GoalType goal,
+ final double min, final double max) {
+ return optimize(maxEval, f, goal, min, max, min + 0.5 * (max - min));
+ }
+
+ /** {@inheritDoc} */
+ public UnivariatePointValuePair optimize(int maxEval, final FUNC f,
+ final GoalType goal,
+ final double min, final double max,
+ final double startValue) {
+ RuntimeException lastException = null;
+ optima = new UnivariatePointValuePair[starts];
+ totalEvaluations = 0;
+
+ // Multi-start loop.
+ for (int i = 0; i < starts; ++i) {
+ // CHECKSTYLE: stop IllegalCatch
+ try {
+ final double s = (i == 0) ? startValue : min + generator.nextDouble() * (max - min);
+ optima[i] = optimizer.optimize(maxEval - totalEvaluations, f, goal, min, max, s);
+ } catch (RuntimeException mue) {
+ lastException = mue;
+ optima[i] = null;
+ }
+ // CHECKSTYLE: resume IllegalCatch
+
+ totalEvaluations += optimizer.getEvaluations();
+ }
+
+ sortPairs(goal);
+
+ if (optima[0] == null) {
+ throw lastException; // cannot be null if starts >=1
+ }
+
+ // Return the point with the best objective function value.
+ return optima[0];
+ }
+
+ /**
+ * Sort the optima from best to worst, followed by {@code null} elements.
+ *
+ * @param goal Goal type.
+ */
+ private void sortPairs(final GoalType goal) {
+ Arrays.sort(optima, new Comparator<UnivariatePointValuePair>() {
+ /** {@inheritDoc} */
+ public int compare(final UnivariatePointValuePair o1,
+ final UnivariatePointValuePair o2) {
+ if (o1 == null) {
+ return (o2 == null) ? 0 : 1;
+ } else if (o2 == null) {
+ return -1;
+ }
+ final double v1 = o1.getValue();
+ final double v2 = o2.getValue();
+ return (goal == GoalType.MINIMIZE) ?
+ Double.compare(v1, v2) : Double.compare(v2, v1);
+ }
+ });
+ }
+}
diff --git a/src/main/java/org/apache/commons/math3/optimization/univariate/UnivariateOptimizer.java b/src/main/java/org/apache/commons/math3/optimization/univariate/UnivariateOptimizer.java
new file mode 100644
index 0000000..e3ebbb3
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/optimization/univariate/UnivariateOptimizer.java
@@ -0,0 +1,29 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math3.optimization.univariate;
+
+import org.apache.commons.math3.analysis.UnivariateFunction;
+
+/**
+ * Interface for univariate optimization algorithms.
+ *
+ * @deprecated As of 3.1 (to be removed in 4.0).
+ * @since 3.0
+ */
+@Deprecated
+public interface UnivariateOptimizer
+ extends BaseUnivariateOptimizer<UnivariateFunction> {}
diff --git a/src/main/java/org/apache/commons/math3/optimization/univariate/UnivariatePointValuePair.java b/src/main/java/org/apache/commons/math3/optimization/univariate/UnivariatePointValuePair.java
new file mode 100644
index 0000000..eee931c
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/optimization/univariate/UnivariatePointValuePair.java
@@ -0,0 +1,68 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math3.optimization.univariate;
+
+import java.io.Serializable;
+
+/**
+ * This class holds a point and the value of an objective function at this
+ * point.
+ * This is a simple immutable container.
+ *
+ * @deprecated As of 3.1 (to be removed in 4.0).
+ * @since 3.0
+ */
+@Deprecated
+public class UnivariatePointValuePair implements Serializable {
+ /** Serializable version identifier. */
+ private static final long serialVersionUID = 1003888396256744753L;
+ /** Point. */
+ private final double point;
+ /** Value of the objective function at the point. */
+ private final double value;
+
+ /**
+ * Build a point/objective function value pair.
+ *
+ * @param point Point.
+ * @param value Value of an objective function at the point
+ */
+ public UnivariatePointValuePair(final double point,
+ final double value) {
+ this.point = point;
+ this.value = value;
+ }
+
+ /**
+ * Get the point.
+ *
+ * @return the point.
+ */
+ public double getPoint() {
+ return point;
+ }
+
+ /**
+ * Get the value of the objective function.
+ *
+ * @return the stored value of the objective function.
+ */
+ public double getValue() {
+ return value;
+ }
+}
diff --git a/src/main/java/org/apache/commons/math3/optimization/univariate/package-info.java b/src/main/java/org/apache/commons/math3/optimization/univariate/package-info.java
new file mode 100644
index 0000000..04feb33
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/optimization/univariate/package-info.java
@@ -0,0 +1,22 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/**
+ *
+ * Univariate real functions minimum finding algorithms.
+ *
+ */
+package org.apache.commons.math3.optimization.univariate;
diff --git a/src/main/java/org/apache/commons/math3/package-info.java b/src/main/java/org/apache/commons/math3/package-info.java
new file mode 100644
index 0000000..200346d
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/package-info.java
@@ -0,0 +1,20 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/**
+ * Common classes used throughout the commons-math library.
+ */
+package org.apache.commons.math3;
diff --git a/src/main/java/org/apache/commons/math3/primes/PollardRho.java b/src/main/java/org/apache/commons/math3/primes/PollardRho.java
new file mode 100644
index 0000000..4cbc064
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/primes/PollardRho.java
@@ -0,0 +1,165 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math3.primes;
+
+import org.apache.commons.math3.util.FastMath;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Implementation of the Pollard's rho factorization algorithm.
+ *
+ * @since 3.2
+ */
+class PollardRho {
+
+ /** Hide utility class. */
+ private PollardRho() {}
+
+ /**
+ * Factorization using Pollard's rho algorithm.
+ *
+ * @param n number to factors, must be &gt; 0
+ * @return the list of prime factors of n.
+ */
+ public static List<Integer> primeFactors(int n) {
+ final List<Integer> factors = new ArrayList<Integer>();
+
+ n = SmallPrimes.smallTrialDivision(n, factors);
+ if (1 == n) {
+ return factors;
+ }
+
+ if (SmallPrimes.millerRabinPrimeTest(n)) {
+ factors.add(n);
+ return factors;
+ }
+
+ int divisor = rhoBrent(n);
+ factors.add(divisor);
+ factors.add(n / divisor);
+ return factors;
+ }
+
+ /**
+ * Implementation of the Pollard's rho factorization algorithm.
+ *
+ * <p>This implementation follows the paper "An improved Monte Carlo factorization algorithm" by
+ * Richard P. Brent. This avoids the triple computation of f(x) typically found in Pollard's rho
+ * implementations. It also batches several gcd computation into 1.
+ *
+ * <p>The backtracking is not implemented as we deal only with semi-primes.
+ *
+ * @param n number to factor, must be semi-prime.
+ * @return a prime factor of n.
+ */
+ static int rhoBrent(final int n) {
+ final int x0 = 2;
+ final int m = 25;
+ int cst = SmallPrimes.PRIMES_LAST;
+ int y = x0;
+ int r = 1;
+ do {
+ int x = y;
+ for (int i = 0; i < r; i++) {
+ final long y2 = ((long) y) * y;
+ y = (int) ((y2 + cst) % n);
+ }
+ int k = 0;
+ do {
+ final int bound = FastMath.min(m, r - k);
+ int q = 1;
+ for (int i = -3;
+ i < bound;
+ i++) { // start at -3 to ensure we enter this loop at least 3 times
+ final long y2 = ((long) y) * y;
+ y = (int) ((y2 + cst) % n);
+ final long divisor = FastMath.abs(x - y);
+ if (0 == divisor) {
+ cst += SmallPrimes.PRIMES_LAST;
+ k = -m;
+ y = x0;
+ r = 1;
+ break;
+ }
+ final long prod = divisor * q;
+ q = (int) (prod % n);
+ if (0 == q) {
+ return gcdPositive(FastMath.abs((int) divisor), n);
+ }
+ }
+ final int out = gcdPositive(FastMath.abs(q), n);
+ if (1 != out) {
+ return out;
+ }
+ k += m;
+ } while (k < r);
+ r = 2 * r;
+ } while (true);
+ }
+
+ /**
+ * Gcd between two positive numbers.
+ *
+ * <p>Gets the greatest common divisor of two numbers, using the "binary gcd" method, which
+ * avoids division and modulo operations. See Knuth 4.5.2 algorithm B. This algorithm is due to
+ * Josef Stein (1961). Special cases:
+ *
+ * <ul>
+ * <li>The result of {@code gcd(x, x)}, {@code gcd(0, x)} and {@code gcd(x, 0)} is the value
+ * of {@code x}.
+ * <li>The invocation {@code gcd(0, 0)} is the only one which returns {@code 0}.
+ * </ul>
+ *
+ * @param a first number, must be &ge; 0
+ * @param b second number, must be &ge; 0
+ * @return gcd(a,b)
+ */
+ static int gcdPositive(int a, int b) {
+ // both a and b must be positive, it is not checked here
+ // gdc(a,0) = a
+ if (a == 0) {
+ return b;
+ } else if (b == 0) {
+ return a;
+ }
+
+ // make a and b odd, keep in mind the common power of twos
+ final int aTwos = Integer.numberOfTrailingZeros(a);
+ a >>= aTwos;
+ final int bTwos = Integer.numberOfTrailingZeros(b);
+ b >>= bTwos;
+ final int shift = FastMath.min(aTwos, bTwos);
+
+ // a and b >0
+ // if a > b then gdc(a,b) = gcd(a-b,b)
+ // if a < b then gcd(a,b) = gcd(b-a,a)
+ // so next a is the absolute difference and next b is the minimum of current values
+ while (a != b) {
+ final int delta = a - b;
+ b = FastMath.min(a, b);
+ a = FastMath.abs(delta);
+ // for speed optimization:
+ // remove any power of two in a as b is guaranteed to be odd throughout all iterations
+ a >>= Integer.numberOfTrailingZeros(a);
+ }
+
+ // gcd(a,a) = a, just "add" the common power of twos
+ return a << shift;
+ }
+}
diff --git a/src/main/java/org/apache/commons/math3/primes/Primes.java b/src/main/java/org/apache/commons/math3/primes/Primes.java
new file mode 100644
index 0000000..4b003f9
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/primes/Primes.java
@@ -0,0 +1,124 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math3.primes;
+
+import org.apache.commons.math3.exception.MathIllegalArgumentException;
+import org.apache.commons.math3.exception.util.LocalizedFormats;
+
+import java.util.List;
+
+/**
+ * Methods related to prime numbers in the range of <code>int</code>:
+ *
+ * <ul>
+ * <li>primality test
+ * <li>prime number generation
+ * <li>factorization
+ * </ul>
+ *
+ * @since 3.2
+ */
+public class Primes {
+
+ /** Hide utility class. */
+ private Primes() {}
+
+ /**
+ * Primality test: tells if the argument is a (provable) prime or not.
+ *
+ * <p>It uses the Miller-Rabin probabilistic test in such a way that a result is guaranteed: it
+ * uses the firsts prime numbers as successive base (see Handbook of applied cryptography by
+ * Menezes, table 4.1).
+ *
+ * @param n number to test.
+ * @return true if n is prime. (All numbers &lt; 2 return false).
+ */
+ public static boolean isPrime(int n) {
+ if (n < 2) {
+ return false;
+ }
+
+ for (int p : SmallPrimes.PRIMES) {
+ if (0 == (n % p)) {
+ return n == p;
+ }
+ }
+ return SmallPrimes.millerRabinPrimeTest(n);
+ }
+
+ /**
+ * Return the smallest prime greater than or equal to n.
+ *
+ * @param n a positive number.
+ * @return the smallest prime greater than or equal to n.
+ * @throws MathIllegalArgumentException if n &lt; 0.
+ */
+ public static int nextPrime(int n) {
+ if (n < 0) {
+ throw new MathIllegalArgumentException(LocalizedFormats.NUMBER_TOO_SMALL, n, 0);
+ }
+ if (n == 2) {
+ return 2;
+ }
+ n |= 1; // make sure n is odd
+ if (n == 1) {
+ return 2;
+ }
+
+ if (isPrime(n)) {
+ return n;
+ }
+
+ // prepare entry in the +2, +4 loop:
+ // n should not be a multiple of 3
+ final int rem = n % 3;
+ if (0 == rem) { // if n % 3 == 0
+ n += 2; // n % 3 == 2
+ } else if (1 == rem) { // if n % 3 == 1
+ // if (isPrime(n)) return n;
+ n += 4; // n % 3 == 2
+ }
+ while (true) { // this loop skips all multiple of 3
+ if (isPrime(n)) {
+ return n;
+ }
+ n += 2; // n % 3 == 1
+ if (isPrime(n)) {
+ return n;
+ }
+ n += 4; // n % 3 == 2
+ }
+ }
+
+ /**
+ * Prime factors decomposition
+ *
+ * @param n number to factorize: must be &ge; 2
+ * @return list of prime factors of n
+ * @throws MathIllegalArgumentException if n &lt; 2.
+ */
+ public static List<Integer> primeFactors(int n) {
+
+ if (n < 2) {
+ throw new MathIllegalArgumentException(LocalizedFormats.NUMBER_TOO_SMALL, n, 2);
+ }
+ // slower than trial div unless we do an awful lot of computation
+ // (then it finally gets JIT-compiled efficiently
+ // List<Integer> out = PollardRho.primeFactors(n);
+ return SmallPrimes.trialDivision(n);
+ }
+}
diff --git a/src/main/java/org/apache/commons/math3/primes/SmallPrimes.java b/src/main/java/org/apache/commons/math3/primes/SmallPrimes.java
new file mode 100644
index 0000000..7439192
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/primes/SmallPrimes.java
@@ -0,0 +1,197 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math3.primes;
+
+import org.apache.commons.math3.util.FastMath;
+
+import java.math.BigInteger;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Utility methods to work on primes within the <code>int</code> range.
+ *
+ * @since 3.2
+ */
+class SmallPrimes {
+
+ /**
+ * The first 512 prime numbers.
+ *
+ * <p>It contains all primes smaller or equal to the cubic square of Integer.MAX_VALUE. As a
+ * result, <code>int</code> numbers which are not reduced by those primes are guaranteed to be
+ * either prime or semi prime.
+ */
+ public static final int[] PRIMES = {
+ 2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89,
+ 97, 101, 103, 107, 109, 113, 127, 131, 137, 139, 149, 151, 157, 163, 167, 173, 179, 181,
+ 191, 193, 197, 199, 211, 223, 227, 229, 233, 239, 241, 251, 257, 263, 269, 271, 277, 281,
+ 283, 293, 307, 311, 313, 317, 331, 337, 347, 349, 353, 359, 367, 373, 379, 383, 389, 397,
+ 401, 409, 419, 421, 431, 433, 439, 443, 449, 457, 461, 463, 467, 479, 487, 491, 499, 503,
+ 509, 521, 523, 541, 547, 557, 563, 569, 571, 577, 587, 593, 599, 601, 607, 613, 617, 619,
+ 631, 641, 643, 647, 653, 659, 661, 673, 677, 683, 691, 701, 709, 719, 727, 733, 739, 743,
+ 751, 757, 761, 769, 773, 787, 797, 809, 811, 821, 823, 827, 829, 839, 853, 857, 859, 863,
+ 877, 881, 883, 887, 907, 911, 919, 929, 937, 941, 947, 953, 967, 971, 977, 983, 991, 997,
+ 1009, 1013, 1019, 1021, 1031, 1033, 1039, 1049, 1051, 1061, 1063, 1069, 1087, 1091, 1093,
+ 1097, 1103, 1109, 1117, 1123, 1129, 1151, 1153, 1163, 1171, 1181, 1187, 1193, 1201, 1213,
+ 1217, 1223, 1229, 1231, 1237, 1249, 1259, 1277, 1279, 1283, 1289, 1291, 1297, 1301, 1303,
+ 1307, 1319, 1321, 1327, 1361, 1367, 1373, 1381, 1399, 1409, 1423, 1427, 1429, 1433, 1439,
+ 1447, 1451, 1453, 1459, 1471, 1481, 1483, 1487, 1489, 1493, 1499, 1511, 1523, 1531, 1543,
+ 1549, 1553, 1559, 1567, 1571, 1579, 1583, 1597, 1601, 1607, 1609, 1613, 1619, 1621, 1627,
+ 1637, 1657, 1663, 1667, 1669, 1693, 1697, 1699, 1709, 1721, 1723, 1733, 1741, 1747, 1753,
+ 1759, 1777, 1783, 1787, 1789, 1801, 1811, 1823, 1831, 1847, 1861, 1867, 1871, 1873, 1877,
+ 1879, 1889, 1901, 1907, 1913, 1931, 1933, 1949, 1951, 1973, 1979, 1987, 1993, 1997, 1999,
+ 2003, 2011, 2017, 2027, 2029, 2039, 2053, 2063, 2069, 2081, 2083, 2087, 2089, 2099, 2111,
+ 2113, 2129, 2131, 2137, 2141, 2143, 2153, 2161, 2179, 2203, 2207, 2213, 2221, 2237, 2239,
+ 2243, 2251, 2267, 2269, 2273, 2281, 2287, 2293, 2297, 2309, 2311, 2333, 2339, 2341, 2347,
+ 2351, 2357, 2371, 2377, 2381, 2383, 2389, 2393, 2399, 2411, 2417, 2423, 2437, 2441, 2447,
+ 2459, 2467, 2473, 2477, 2503, 2521, 2531, 2539, 2543, 2549, 2551, 2557, 2579, 2591, 2593,
+ 2609, 2617, 2621, 2633, 2647, 2657, 2659, 2663, 2671, 2677, 2683, 2687, 2689, 2693, 2699,
+ 2707, 2711, 2713, 2719, 2729, 2731, 2741, 2749, 2753, 2767, 2777, 2789, 2791, 2797, 2801,
+ 2803, 2819, 2833, 2837, 2843, 2851, 2857, 2861, 2879, 2887, 2897, 2903, 2909, 2917, 2927,
+ 2939, 2953, 2957, 2963, 2969, 2971, 2999, 3001, 3011, 3019, 3023, 3037, 3041, 3049, 3061,
+ 3067, 3079, 3083, 3089, 3109, 3119, 3121, 3137, 3163, 3167, 3169, 3181, 3187, 3191, 3203,
+ 3209, 3217, 3221, 3229, 3251, 3253, 3257, 3259, 3271, 3299, 3301, 3307, 3313, 3319, 3323,
+ 3329, 3331, 3343, 3347, 3359, 3361, 3371, 3373, 3389, 3391, 3407, 3413, 3433, 3449, 3457,
+ 3461, 3463, 3467, 3469, 3491, 3499, 3511, 3517, 3527, 3529, 3533, 3539, 3541, 3547, 3557,
+ 3559, 3571, 3581, 3583, 3593, 3607, 3613, 3617, 3623, 3631, 3637, 3643, 3659, 3671
+ };
+
+ /** The last number in PRIMES. */
+ public static final int PRIMES_LAST = PRIMES[PRIMES.length - 1];
+
+ /** Hide utility class. */
+ private SmallPrimes() {}
+
+ /**
+ * Extract small factors.
+ *
+ * @param n the number to factor, must be &gt; 0.
+ * @param factors the list where to add the factors.
+ * @return the part of n which remains to be factored, it is either a prime or a semi-prime
+ */
+ public static int smallTrialDivision(int n, final List<Integer> factors) {
+ for (int p : PRIMES) {
+ while (0 == n % p) {
+ n /= p;
+ factors.add(p);
+ }
+ }
+ return n;
+ }
+
+ /**
+ * Extract factors in the range <code>PRIME_LAST+2</code> to <code>maxFactors</code>.
+ *
+ * @param n the number to factorize, must be >= PRIME_LAST+2 and must not contain any factor
+ * below PRIME_LAST+2
+ * @param maxFactor the upper bound of trial division: if it is reached, the method gives up and
+ * returns n.
+ * @param factors the list where to add the factors.
+ * @return n or 1 if factorization is completed.
+ */
+ public static int boundedTrialDivision(int n, int maxFactor, List<Integer> factors) {
+ int f = PRIMES_LAST + 2;
+ // no check is done about n >= f
+ while (f <= maxFactor) {
+ if (0 == n % f) {
+ n /= f;
+ factors.add(f);
+ break;
+ }
+ f += 4;
+ if (0 == n % f) {
+ n /= f;
+ factors.add(f);
+ break;
+ }
+ f += 2;
+ }
+ if (n != 1) {
+ factors.add(n);
+ }
+ return n;
+ }
+
+ /**
+ * Factorization by trial division.
+ *
+ * @param n the number to factor
+ * @return the list of prime factors of n
+ */
+ public static List<Integer> trialDivision(int n) {
+ final List<Integer> factors = new ArrayList<Integer>(32);
+ n = smallTrialDivision(n, factors);
+ if (1 == n) {
+ return factors;
+ }
+ // here we are sure that n is either a prime or a semi prime
+ final int bound = (int) FastMath.sqrt(n);
+ boundedTrialDivision(n, bound, factors);
+ return factors;
+ }
+
+ /**
+ * Miller-Rabin probabilistic primality test for int type, used in such a way that a result is
+ * always guaranteed.
+ *
+ * <p>It uses the prime numbers as successive base therefore it is guaranteed to be always
+ * correct. (see Handbook of applied cryptography by Menezes, table 4.1)
+ *
+ * @param n number to test: an odd integer &ge; 3
+ * @return true if n is prime. false if n is definitely composite.
+ */
+ public static boolean millerRabinPrimeTest(final int n) {
+ final int nMinus1 = n - 1;
+ final int s = Integer.numberOfTrailingZeros(nMinus1);
+ final int r = nMinus1 >> s;
+ // r must be odd, it is not checked here
+ int t = 1;
+ if (n >= 2047) {
+ t = 2;
+ }
+ if (n >= 1373653) {
+ t = 3;
+ }
+ if (n >= 25326001) {
+ t = 4;
+ } // works up to 3.2 billion, int range stops at 2.7 so we are safe :-)
+ BigInteger br = BigInteger.valueOf(r);
+ BigInteger bn = BigInteger.valueOf(n);
+
+ for (int i = 0; i < t; i++) {
+ BigInteger a = BigInteger.valueOf(SmallPrimes.PRIMES[i]);
+ BigInteger bPow = a.modPow(br, bn);
+ int y = bPow.intValue();
+ if ((1 != y) && (y != nMinus1)) {
+ int j = 1;
+ while ((j <= s - 1) && (nMinus1 != y)) {
+ long square = ((long) y) * y;
+ y = (int) (square % n);
+ if (1 == y) {
+ return false;
+ } // definitely composite
+ j++;
+ }
+ if (nMinus1 != y) {
+ return false;
+ } // definitely composite
+ }
+ }
+ return true; // definitely prime
+ }
+}
diff --git a/src/main/java/org/apache/commons/math3/primes/package-info.java b/src/main/java/org/apache/commons/math3/primes/package-info.java
new file mode 100644
index 0000000..166ee0b
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/primes/package-info.java
@@ -0,0 +1,18 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/** Methods related to prime numbers like primality test, factor decomposition. */
+package org.apache.commons.math3.primes;
diff --git a/src/main/java/org/apache/commons/math3/random/AbstractRandomGenerator.java b/src/main/java/org/apache/commons/math3/random/AbstractRandomGenerator.java
new file mode 100644
index 0000000..ce8ad85
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/random/AbstractRandomGenerator.java
@@ -0,0 +1,250 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math3.random;
+
+import org.apache.commons.math3.exception.NotStrictlyPositiveException;
+import org.apache.commons.math3.util.FastMath;
+
+/**
+ * Abstract class implementing the {@link RandomGenerator} interface. Default implementations for
+ * all methods other than {@link #nextDouble()} and {@link #setSeed(long)} are provided.
+ *
+ * <p>All data generation methods are based on {@code code nextDouble()}. Concrete implementations
+ * <strong>must</strong> override this method and <strong>should</strong> provide better / more
+ * performant implementations of the other methods if the underlying PRNG supplies them.
+ *
+ * @since 1.1
+ */
+public abstract class AbstractRandomGenerator implements RandomGenerator {
+
+ /**
+ * Cached random normal value. The default implementation for {@link #nextGaussian} generates
+ * pairs of values and this field caches the second value so that the full algorithm is not
+ * executed for every activation. The value {@code Double.NaN} signals that there is no cached
+ * value. Use {@link #clear} to clear the cached value.
+ */
+ private double cachedNormalDeviate = Double.NaN;
+
+ /** Construct a RandomGenerator. */
+ public AbstractRandomGenerator() {
+ super();
+ }
+
+ /**
+ * Clears the cache used by the default implementation of {@link #nextGaussian}. Implementations
+ * that do not override the default implementation of {@code nextGaussian} should call this
+ * method in the implementation of {@link #setSeed(long)}
+ */
+ public void clear() {
+ cachedNormalDeviate = Double.NaN;
+ }
+
+ /** {@inheritDoc} */
+ public void setSeed(int seed) {
+ setSeed((long) seed);
+ }
+
+ /** {@inheritDoc} */
+ public void setSeed(int[] seed) {
+ // the following number is the largest prime that fits in 32 bits (it is 2^32 - 5)
+ final long prime = 4294967291l;
+
+ long combined = 0l;
+ for (int s : seed) {
+ combined = combined * prime + s;
+ }
+ setSeed(combined);
+ }
+
+ /**
+ * Sets the seed of the underlying random number generator using a {@code long} seed. Sequences
+ * of values generated starting with the same seeds should be identical.
+ *
+ * <p>Implementations that do not override the default implementation of {@code nextGaussian}
+ * should include a call to {@link #clear} in the implementation of this method.
+ *
+ * @param seed the seed value
+ */
+ public abstract void setSeed(long seed);
+
+ /**
+ * Generates random bytes and places them into a user-supplied byte array. The number of random
+ * bytes produced is equal to the length of the byte array.
+ *
+ * <p>The default implementation fills the array with bytes extracted from random integers
+ * generated using {@link #nextInt}.
+ *
+ * @param bytes the non-null byte array in which to put the random bytes
+ */
+ public void nextBytes(byte[] bytes) {
+ int bytesOut = 0;
+ while (bytesOut < bytes.length) {
+ int randInt = nextInt();
+ for (int i = 0; i < 3; i++) {
+ if (i > 0) {
+ randInt >>= 8;
+ }
+ bytes[bytesOut++] = (byte) randInt;
+ if (bytesOut == bytes.length) {
+ return;
+ }
+ }
+ }
+ }
+
+ /**
+ * Returns the next pseudorandom, uniformly distributed {@code int} value from this random
+ * number generator's sequence. All 2<font size="-1"><sup>32</sup></font> possible {@code int}
+ * values should be produced with (approximately) equal probability.
+ *
+ * <p>The default implementation provided here returns
+ *
+ * <pre>
+ * <code>(int) (nextDouble() * Integer.MAX_VALUE)</code>
+ * </pre>
+ *
+ * @return the next pseudorandom, uniformly distributed {@code int} value from this random
+ * number generator's sequence
+ */
+ public int nextInt() {
+ return (int) ((2d * nextDouble() - 1d) * Integer.MAX_VALUE);
+ }
+
+ /**
+ * Returns a pseudorandom, uniformly distributed {@code int} value between 0 (inclusive) and the
+ * specified value (exclusive), drawn from this random number generator's sequence.
+ *
+ * <p>The default implementation returns
+ *
+ * <pre>
+ * <code>(int) (nextDouble() * n</code>
+ * </pre>
+ *
+ * @param n the bound on the random number to be returned. Must be positive.
+ * @return a pseudorandom, uniformly distributed {@code int} value between 0 (inclusive) and n
+ * (exclusive).
+ * @throws NotStrictlyPositiveException if {@code n <= 0}.
+ */
+ public int nextInt(int n) {
+ if (n <= 0) {
+ throw new NotStrictlyPositiveException(n);
+ }
+ int result = (int) (nextDouble() * n);
+ return result < n ? result : n - 1;
+ }
+
+ /**
+ * Returns the next pseudorandom, uniformly distributed {@code long} value from this random
+ * number generator's sequence. All 2<font size="-1"><sup>64</sup></font> possible {@code long}
+ * values should be produced with (approximately) equal probability.
+ *
+ * <p>The default implementation returns
+ *
+ * <pre>
+ * <code>(long) (nextDouble() * Long.MAX_VALUE)</code>
+ * </pre>
+ *
+ * @return the next pseudorandom, uniformly distributed {@code long} value from this random
+ * number generator's sequence
+ */
+ public long nextLong() {
+ return (long) ((2d * nextDouble() - 1d) * Long.MAX_VALUE);
+ }
+
+ /**
+ * Returns the next pseudorandom, uniformly distributed {@code boolean} value from this random
+ * number generator's sequence.
+ *
+ * <p>The default implementation returns
+ *
+ * <pre>
+ * <code>nextDouble() <= 0.5</code>
+ * </pre>
+ *
+ * @return the next pseudorandom, uniformly distributed {@code boolean} value from this random
+ * number generator's sequence
+ */
+ public boolean nextBoolean() {
+ return nextDouble() <= 0.5;
+ }
+
+ /**
+ * Returns the next pseudorandom, uniformly distributed {@code float} value between {@code 0.0}
+ * and {@code 1.0} from this random number generator's sequence.
+ *
+ * <p>The default implementation returns
+ *
+ * <pre>
+ * <code>(float) nextDouble() </code>
+ * </pre>
+ *
+ * @return the next pseudorandom, uniformly distributed {@code float} value between {@code 0.0}
+ * and {@code 1.0} from this random number generator's sequence
+ */
+ public float nextFloat() {
+ return (float) nextDouble();
+ }
+
+ /**
+ * Returns the next pseudorandom, uniformly distributed {@code double} value between {@code 0.0}
+ * and {@code 1.0} from this random number generator's sequence.
+ *
+ * <p>This method provides the underlying source of random data used by the other methods.
+ *
+ * @return the next pseudorandom, uniformly distributed {@code double} value between {@code 0.0}
+ * and {@code 1.0} from this random number generator's sequence
+ */
+ public abstract double nextDouble();
+
+ /**
+ * Returns the next pseudorandom, Gaussian ("normally") distributed {@code double} value with
+ * mean {@code 0.0} and standard deviation {@code 1.0} from this random number generator's
+ * sequence.
+ *
+ * <p>The default implementation uses the <em>Polar Method</em> due to G.E.P. Box, M.E. Muller
+ * and G. Marsaglia, as described in D. Knuth, <u>The Art of Computer Programming</u>, 3.4.1C.
+ *
+ * <p>The algorithm generates a pair of independent random values. One of these is cached for
+ * reuse, so the full algorithm is not executed on each activation. Implementations that do not
+ * override this method should make sure to call {@link #clear} to clear the cached value in the
+ * implementation of {@link #setSeed(long)}.
+ *
+ * @return the next pseudorandom, Gaussian ("normally") distributed {@code double} value with
+ * mean {@code 0.0} and standard deviation {@code 1.0} from this random number generator's
+ * sequence
+ */
+ public double nextGaussian() {
+ if (!Double.isNaN(cachedNormalDeviate)) {
+ double dev = cachedNormalDeviate;
+ cachedNormalDeviate = Double.NaN;
+ return dev;
+ }
+ double v1 = 0;
+ double v2 = 0;
+ double s = 1;
+ while (s >= 1) {
+ v1 = 2 * nextDouble() - 1;
+ v2 = 2 * nextDouble() - 1;
+ s = v1 * v1 + v2 * v2;
+ }
+ if (s != 0) {
+ s = FastMath.sqrt(-2 * FastMath.log(s) / s);
+ }
+ cachedNormalDeviate = v2 * s;
+ return v1 * s;
+ }
+}
diff --git a/src/main/java/org/apache/commons/math3/random/AbstractWell.java b/src/main/java/org/apache/commons/math3/random/AbstractWell.java
new file mode 100644
index 0000000..87f2ee1
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/random/AbstractWell.java
@@ -0,0 +1,216 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math3.random;
+
+import org.apache.commons.math3.util.FastMath;
+
+import java.io.Serializable;
+
+/**
+ * This abstract class implements the WELL class of pseudo-random number generator from
+ * Fran&ccedil;ois Panneton, Pierre L'Ecuyer and Makoto Matsumoto.
+ *
+ * <p>This generator is described in a paper by Fran&ccedil;ois Panneton, Pierre L'Ecuyer and Makoto
+ * Matsumoto <a href="http://www.iro.umontreal.ca/~lecuyer/myftp/papers/wellrng.pdf">Improved
+ * Long-Period Generators Based on Linear Recurrences Modulo 2</a> ACM Transactions on Mathematical
+ * Software, 32, 1 (2006). The errata for the paper are in <a
+ * href="http://www.iro.umontreal.ca/~lecuyer/myftp/papers/wellrng-errata.txt">wellrng-errata.txt</a>.
+ *
+ * @see <a href="http://www.iro.umontreal.ca/~panneton/WELLRNG.html">WELL Random number
+ * generator</a>
+ * @since 2.2
+ */
+public abstract class AbstractWell extends BitsStreamGenerator implements Serializable {
+
+ /** Serializable version identifier. */
+ private static final long serialVersionUID = -817701723016583596L;
+
+ /** Current index in the bytes pool. */
+ protected int index;
+
+ /** Bytes pool. */
+ protected final int[] v;
+
+ /**
+ * Index indirection table giving for each index its predecessor taking table size into account.
+ */
+ protected final int[] iRm1;
+
+ /**
+ * Index indirection table giving for each index its second predecessor taking table size into
+ * account.
+ */
+ protected final int[] iRm2;
+
+ /**
+ * Index indirection table giving for each index the value index + m1 taking table size into
+ * account.
+ */
+ protected final int[] i1;
+
+ /**
+ * Index indirection table giving for each index the value index + m2 taking table size into
+ * account.
+ */
+ protected final int[] i2;
+
+ /**
+ * Index indirection table giving for each index the value index + m3 taking table size into
+ * account.
+ */
+ protected final int[] i3;
+
+ /**
+ * Creates a new random number generator.
+ *
+ * <p>The instance is initialized using the current time plus the system identity hash code of
+ * this instance as the seed.
+ *
+ * @param k number of bits in the pool (not necessarily a multiple of 32)
+ * @param m1 first parameter of the algorithm
+ * @param m2 second parameter of the algorithm
+ * @param m3 third parameter of the algorithm
+ */
+ protected AbstractWell(final int k, final int m1, final int m2, final int m3) {
+ this(k, m1, m2, m3, null);
+ }
+
+ /**
+ * Creates a new random number generator using a single int seed.
+ *
+ * @param k number of bits in the pool (not necessarily a multiple of 32)
+ * @param m1 first parameter of the algorithm
+ * @param m2 second parameter of the algorithm
+ * @param m3 third parameter of the algorithm
+ * @param seed the initial seed (32 bits integer)
+ */
+ protected AbstractWell(final int k, final int m1, final int m2, final int m3, final int seed) {
+ this(k, m1, m2, m3, new int[] {seed});
+ }
+
+ /**
+ * Creates a new random number generator using an int array seed.
+ *
+ * @param k number of bits in the pool (not necessarily a multiple of 32)
+ * @param m1 first parameter of the algorithm
+ * @param m2 second parameter of the algorithm
+ * @param m3 third parameter of the algorithm
+ * @param seed the initial seed (32 bits integers array), if null the seed of the generator will
+ * be related to the current time
+ */
+ protected AbstractWell(
+ final int k, final int m1, final int m2, final int m3, final int[] seed) {
+
+ // the bits pool contains k bits, k = r w - p where r is the number
+ // of w bits blocks, w is the block size (always 32 in the original paper)
+ // and p is the number of unused bits in the last block
+ final int w = 32;
+ final int r = (k + w - 1) / w;
+ this.v = new int[r];
+ this.index = 0;
+
+ // precompute indirection index tables. These tables are used for optimizing access
+ // they allow saving computations like "(j + r - 2) % r" with costly modulo operations
+ iRm1 = new int[r];
+ iRm2 = new int[r];
+ i1 = new int[r];
+ i2 = new int[r];
+ i3 = new int[r];
+ for (int j = 0; j < r; ++j) {
+ iRm1[j] = (j + r - 1) % r;
+ iRm2[j] = (j + r - 2) % r;
+ i1[j] = (j + m1) % r;
+ i2[j] = (j + m2) % r;
+ i3[j] = (j + m3) % r;
+ }
+
+ // initialize the pool content
+ setSeed(seed);
+ }
+
+ /**
+ * Creates a new random number generator using a single long seed.
+ *
+ * @param k number of bits in the pool (not necessarily a multiple of 32)
+ * @param m1 first parameter of the algorithm
+ * @param m2 second parameter of the algorithm
+ * @param m3 third parameter of the algorithm
+ * @param seed the initial seed (64 bits integer)
+ */
+ protected AbstractWell(final int k, final int m1, final int m2, final int m3, final long seed) {
+ this(k, m1, m2, m3, new int[] {(int) (seed >>> 32), (int) (seed & 0xffffffffl)});
+ }
+
+ /**
+ * Reinitialize the generator as if just built with the given int seed.
+ *
+ * <p>The state of the generator is exactly the same as a new generator built with the same
+ * seed.
+ *
+ * @param seed the initial seed (32 bits integer)
+ */
+ @Override
+ public void setSeed(final int seed) {
+ setSeed(new int[] {seed});
+ }
+
+ /**
+ * Reinitialize the generator as if just built with the given int array seed.
+ *
+ * <p>The state of the generator is exactly the same as a new generator built with the same
+ * seed.
+ *
+ * @param seed the initial seed (32 bits integers array). If null the seed of the generator will
+ * be the system time plus the system identity hash code of the instance.
+ */
+ @Override
+ public void setSeed(final int[] seed) {
+ if (seed == null) {
+ setSeed(System.currentTimeMillis() + System.identityHashCode(this));
+ return;
+ }
+
+ System.arraycopy(seed, 0, v, 0, FastMath.min(seed.length, v.length));
+
+ if (seed.length < v.length) {
+ for (int i = seed.length; i < v.length; ++i) {
+ final long l = v[i - seed.length];
+ v[i] = (int) ((1812433253l * (l ^ (l >> 30)) + i) & 0xffffffffL);
+ }
+ }
+
+ index = 0;
+ clear(); // Clear normal deviate cache
+ }
+
+ /**
+ * Reinitialize the generator as if just built with the given long seed.
+ *
+ * <p>The state of the generator is exactly the same as a new generator built with the same
+ * seed.
+ *
+ * @param seed the initial seed (64 bits integer)
+ */
+ @Override
+ public void setSeed(final long seed) {
+ setSeed(new int[] {(int) (seed >>> 32), (int) (seed & 0xffffffffl)});
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ protected abstract int next(final int bits);
+}
diff --git a/src/main/java/org/apache/commons/math3/random/BitsStreamGenerator.java b/src/main/java/org/apache/commons/math3/random/BitsStreamGenerator.java
new file mode 100644
index 0000000..07ab156
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/random/BitsStreamGenerator.java
@@ -0,0 +1,249 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math3.random;
+
+import org.apache.commons.math3.exception.NotStrictlyPositiveException;
+import org.apache.commons.math3.exception.OutOfRangeException;
+import org.apache.commons.math3.util.FastMath;
+
+import java.io.Serializable;
+
+/**
+ * Base class for random number generators that generates bits streams.
+ *
+ * @since 2.0
+ */
+public abstract class BitsStreamGenerator implements RandomGenerator, Serializable {
+ /** Serializable version identifier */
+ private static final long serialVersionUID = 20130104L;
+
+ /** Next gaussian. */
+ private double nextGaussian;
+
+ /** Creates a new random number generator. */
+ public BitsStreamGenerator() {
+ nextGaussian = Double.NaN;
+ }
+
+ /** {@inheritDoc} */
+ public abstract void setSeed(int seed);
+
+ /** {@inheritDoc} */
+ public abstract void setSeed(int[] seed);
+
+ /** {@inheritDoc} */
+ public abstract void setSeed(long seed);
+
+ /**
+ * Generate next pseudorandom number.
+ *
+ * <p>This method is the core generation algorithm. It is used by all the public generation
+ * methods for the various primitive types {@link #nextBoolean()}, {@link #nextBytes(byte[])},
+ * {@link #nextDouble()}, {@link #nextFloat()}, {@link #nextGaussian()}, {@link #nextInt()},
+ * {@link #next(int)} and {@link #nextLong()}.
+ *
+ * @param bits number of random bits to produce
+ * @return random bits generated
+ */
+ protected abstract int next(int bits);
+
+ /** {@inheritDoc} */
+ public boolean nextBoolean() {
+ return next(1) != 0;
+ }
+
+ /** {@inheritDoc} */
+ public double nextDouble() {
+ final long high = ((long) next(26)) << 26;
+ final int low = next(26);
+ return (high | low) * 0x1.0p-52d;
+ }
+
+ /** {@inheritDoc} */
+ public float nextFloat() {
+ return next(23) * 0x1.0p-23f;
+ }
+
+ /** {@inheritDoc} */
+ public double nextGaussian() {
+
+ final double random;
+ if (Double.isNaN(nextGaussian)) {
+ // generate a new pair of gaussian numbers
+ final double x = nextDouble();
+ final double y = nextDouble();
+ final double alpha = 2 * FastMath.PI * x;
+ final double r = FastMath.sqrt(-2 * FastMath.log(y));
+ random = r * FastMath.cos(alpha);
+ nextGaussian = r * FastMath.sin(alpha);
+ } else {
+ // use the second element of the pair already generated
+ random = nextGaussian;
+ nextGaussian = Double.NaN;
+ }
+
+ return random;
+ }
+
+ /** {@inheritDoc} */
+ public int nextInt() {
+ return next(32);
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * <p>This default implementation is copied from Apache Harmony java.util.Random (r929253).
+ *
+ * <p>Implementation notes:
+ *
+ * <ul>
+ * <li>If n is a power of 2, this method returns {@code (int) ((n * (long) next(31)) >> 31)}.
+ * <li>If n is not a power of 2, what is returned is {@code next(31) % n} with {@code
+ * next(31)} values rejected (i.e. regenerated) until a value that is larger than the
+ * remainder of {@code Integer.MAX_VALUE / n} is generated. Rejection of this initial
+ * segment is necessary to ensure a uniform distribution.
+ * </ul>
+ */
+ public int nextInt(int n) throws IllegalArgumentException {
+ if (n > 0) {
+ if ((n & -n) == n) {
+ return (int) ((n * (long) next(31)) >> 31);
+ }
+ int bits;
+ int val;
+ do {
+ bits = next(31);
+ val = bits % n;
+ } while (bits - val + (n - 1) < 0);
+ return val;
+ }
+ throw new NotStrictlyPositiveException(n);
+ }
+
+ /** {@inheritDoc} */
+ public long nextLong() {
+ final long high = ((long) next(32)) << 32;
+ final long low = ((long) next(32)) & 0xffffffffL;
+ return high | low;
+ }
+
+ /**
+ * Returns a pseudorandom, uniformly distributed {@code long} value between 0 (inclusive) and
+ * the specified value (exclusive), drawn from this random number generator's sequence.
+ *
+ * @param n the bound on the random number to be returned. Must be positive.
+ * @return a pseudorandom, uniformly distributed {@code long} value between 0 (inclusive) and n
+ * (exclusive).
+ * @throws IllegalArgumentException if n is not positive.
+ */
+ public long nextLong(long n) throws IllegalArgumentException {
+ if (n > 0) {
+ long bits;
+ long val;
+ do {
+ bits = ((long) next(31)) << 32;
+ bits |= ((long) next(32)) & 0xffffffffL;
+ val = bits % n;
+ } while (bits - val + (n - 1) < 0);
+ return val;
+ }
+ throw new NotStrictlyPositiveException(n);
+ }
+
+ /** Clears the cache used by the default implementation of {@link #nextGaussian}. */
+ public void clear() {
+ nextGaussian = Double.NaN;
+ }
+
+ /**
+ * Generates random bytes and places them into a user-supplied array.
+ *
+ * <p>The array is filled with bytes extracted from random integers. This implies that the
+ * number of random bytes generated may be larger than the length of the byte array.
+ *
+ * @param bytes Array in which to put the generated bytes. Cannot be {@code null}.
+ */
+ public void nextBytes(byte[] bytes) {
+ nextBytesFill(bytes, 0, bytes.length);
+ }
+
+ /**
+ * Generates random bytes and places them into a user-supplied array.
+ *
+ * <p>The array is filled with bytes extracted from random integers. This implies that the
+ * number of random bytes generated may be larger than the length of the byte array.
+ *
+ * @param bytes Array in which to put the generated bytes. Cannot be {@code null}.
+ * @param start Index at which to start inserting the generated bytes.
+ * @param len Number of bytes to insert.
+ * @throws OutOfRangeException if {@code start < 0} or {@code start >= bytes.length}.
+ * @throws OutOfRangeException if {@code len < 0} or {@code len > bytes.length - start}.
+ */
+ public void nextBytes(byte[] bytes, int start, int len) {
+ if (start < 0 || start >= bytes.length) {
+ throw new OutOfRangeException(start, 0, bytes.length);
+ }
+ if (len < 0 || len > bytes.length - start) {
+ throw new OutOfRangeException(len, 0, bytes.length - start);
+ }
+
+ nextBytesFill(bytes, start, len);
+ }
+
+ /**
+ * Generates random bytes and places them into a user-supplied array.
+ *
+ * <p>The array is filled with bytes extracted from random integers. This implies that the
+ * number of random bytes generated may be larger than the length of the byte array.
+ *
+ * @param bytes Array in which to put the generated bytes. Cannot be {@code null}.
+ * @param start Index at which to start inserting the generated bytes.
+ * @param len Number of bytes to insert.
+ */
+ private void nextBytesFill(byte[] bytes, int start, int len) {
+ int index = start; // Index of first insertion.
+
+ // Index of first insertion plus multiple 4 part of length (i.e. length
+ // with two least significant bits unset).
+ final int indexLoopLimit = index + (len & 0x7ffffffc);
+
+ // Start filling in the byte array, 4 bytes at a time.
+ while (index < indexLoopLimit) {
+ final int random = next(32);
+ bytes[index++] = (byte) random;
+ bytes[index++] = (byte) (random >>> 8);
+ bytes[index++] = (byte) (random >>> 16);
+ bytes[index++] = (byte) (random >>> 24);
+ }
+
+ final int indexLimit = start + len; // Index of last insertion + 1.
+
+ // Fill in the remaining bytes.
+ if (index < indexLimit) {
+ int random = next(32);
+ while (true) {
+ bytes[index++] = (byte) random;
+ if (index < indexLimit) {
+ random >>>= 8;
+ } else {
+ break;
+ }
+ }
+ }
+ }
+}
diff --git a/src/main/java/org/apache/commons/math3/random/CorrelatedRandomVectorGenerator.java b/src/main/java/org/apache/commons/math3/random/CorrelatedRandomVectorGenerator.java
new file mode 100644
index 0000000..6668356
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/random/CorrelatedRandomVectorGenerator.java
@@ -0,0 +1,178 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math3.random;
+
+import org.apache.commons.math3.exception.DimensionMismatchException;
+import org.apache.commons.math3.linear.RealMatrix;
+import org.apache.commons.math3.linear.RectangularCholeskyDecomposition;
+
+/**
+ * A {@link RandomVectorGenerator} that generates vectors with with correlated components.
+ *
+ * <p>Random vectors with correlated components are built by combining the uncorrelated components
+ * of another random vector in such a way that the resulting correlations are the ones specified by
+ * a positive definite covariance matrix.
+ *
+ * <p>The main use for correlated random vector generation is for Monte-Carlo simulation of physical
+ * problems with several variables, for example to generate error vectors to be added to a nominal
+ * vector. A particularly interesting case is when the generated vector should be drawn from a <a
+ * href="http://en.wikipedia.org/wiki/Multivariate_normal_distribution">Multivariate Normal
+ * Distribution</a>. The approach using a Cholesky decomposition is quite usual in this case.
+ * However, it can be extended to other cases as long as the underlying random generator provides
+ * {@link NormalizedRandomGenerator normalized values} like {@link GaussianRandomGenerator} or
+ * {@link UniformRandomGenerator}.
+ *
+ * <p>Sometimes, the covariance matrix for a given simulation is not strictly positive definite.
+ * This means that the correlations are not all independent from each other. In this case, however,
+ * the non strictly positive elements found during the Cholesky decomposition of the covariance
+ * matrix should not be negative either, they should be null. Another non-conventional extension
+ * handling this case is used here. Rather than computing <code>C = U<sup>T</sup>.U</code> where
+ * <code>C</code> is the covariance matrix and <code>U</code> is an upper-triangular matrix, we
+ * compute <code>C = B.B<sup>T</sup></code> where <code>B</code> is a rectangular matrix having more
+ * rows than columns. The number of columns of <code>B</code> is the rank of the covariance matrix,
+ * and it is the dimension of the uncorrelated random vector that is needed to compute the component
+ * of the correlated vector. This class handles this situation automatically.
+ *
+ * @since 1.2
+ */
+public class CorrelatedRandomVectorGenerator implements RandomVectorGenerator {
+ /** Mean vector. */
+ private final double[] mean;
+
+ /** Underlying generator. */
+ private final NormalizedRandomGenerator generator;
+
+ /** Storage for the normalized vector. */
+ private final double[] normalized;
+
+ /** Root of the covariance matrix. */
+ private final RealMatrix root;
+
+ /**
+ * Builds a correlated random vector generator from its mean vector and covariance matrix.
+ *
+ * @param mean Expected mean values for all components.
+ * @param covariance Covariance matrix.
+ * @param small Diagonal elements threshold under which column are considered to be dependent on
+ * previous ones and are discarded
+ * @param generator underlying generator for uncorrelated normalized components.
+ * @throws org.apache.commons.math3.linear.NonPositiveDefiniteMatrixException if the covariance
+ * matrix is not strictly positive definite.
+ * @throws DimensionMismatchException if the mean and covariance arrays dimensions do not match.
+ */
+ public CorrelatedRandomVectorGenerator(
+ double[] mean,
+ RealMatrix covariance,
+ double small,
+ NormalizedRandomGenerator generator) {
+ int order = covariance.getRowDimension();
+ if (mean.length != order) {
+ throw new DimensionMismatchException(mean.length, order);
+ }
+ this.mean = mean.clone();
+
+ final RectangularCholeskyDecomposition decomposition =
+ new RectangularCholeskyDecomposition(covariance, small);
+ root = decomposition.getRootMatrix();
+
+ this.generator = generator;
+ normalized = new double[decomposition.getRank()];
+ }
+
+ /**
+ * Builds a null mean random correlated vector generator from its covariance matrix.
+ *
+ * @param covariance Covariance matrix.
+ * @param small Diagonal elements threshold under which column are considered to be dependent on
+ * previous ones and are discarded.
+ * @param generator Underlying generator for uncorrelated normalized components.
+ * @throws org.apache.commons.math3.linear.NonPositiveDefiniteMatrixException if the covariance
+ * matrix is not strictly positive definite.
+ */
+ public CorrelatedRandomVectorGenerator(
+ RealMatrix covariance, double small, NormalizedRandomGenerator generator) {
+ int order = covariance.getRowDimension();
+ mean = new double[order];
+ for (int i = 0; i < order; ++i) {
+ mean[i] = 0;
+ }
+
+ final RectangularCholeskyDecomposition decomposition =
+ new RectangularCholeskyDecomposition(covariance, small);
+ root = decomposition.getRootMatrix();
+
+ this.generator = generator;
+ normalized = new double[decomposition.getRank()];
+ }
+
+ /**
+ * Get the underlying normalized components generator.
+ *
+ * @return underlying uncorrelated components generator
+ */
+ public NormalizedRandomGenerator getGenerator() {
+ return generator;
+ }
+
+ /**
+ * Get the rank of the covariance matrix. The rank is the number of independent rows in the
+ * covariance matrix, it is also the number of columns of the root matrix.
+ *
+ * @return rank of the square matrix.
+ * @see #getRootMatrix()
+ */
+ public int getRank() {
+ return normalized.length;
+ }
+
+ /**
+ * Get the root of the covariance matrix. The root is the rectangular matrix <code>B</code> such
+ * that the covariance matrix is equal to <code>B.B<sup>T</sup></code>
+ *
+ * @return root of the square matrix
+ * @see #getRank()
+ */
+ public RealMatrix getRootMatrix() {
+ return root;
+ }
+
+ /**
+ * Generate a correlated random vector.
+ *
+ * @return a random vector as an array of double. The returned array is created at each call,
+ * the caller can do what it wants with it.
+ */
+ public double[] nextVector() {
+
+ // generate uncorrelated vector
+ for (int i = 0; i < normalized.length; ++i) {
+ normalized[i] = generator.nextNormalizedDouble();
+ }
+
+ // compute correlated vector
+ double[] correlated = new double[mean.length];
+ for (int i = 0; i < correlated.length; ++i) {
+ correlated[i] = mean[i];
+ for (int j = 0; j < root.getColumnDimension(); ++j) {
+ correlated[i] += root.getEntry(i, j) * normalized[j];
+ }
+ }
+
+ return correlated;
+ }
+}
diff --git a/src/main/java/org/apache/commons/math3/random/EmpiricalDistribution.java b/src/main/java/org/apache/commons/math3/random/EmpiricalDistribution.java
new file mode 100644
index 0000000..9ed3f4a
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/random/EmpiricalDistribution.java
@@ -0,0 +1,866 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math3.random;
+
+import org.apache.commons.math3.distribution.AbstractRealDistribution;
+import org.apache.commons.math3.distribution.ConstantRealDistribution;
+import org.apache.commons.math3.distribution.NormalDistribution;
+import org.apache.commons.math3.distribution.RealDistribution;
+import org.apache.commons.math3.exception.MathIllegalStateException;
+import org.apache.commons.math3.exception.MathInternalError;
+import org.apache.commons.math3.exception.NotStrictlyPositiveException;
+import org.apache.commons.math3.exception.NullArgumentException;
+import org.apache.commons.math3.exception.OutOfRangeException;
+import org.apache.commons.math3.exception.ZeroException;
+import org.apache.commons.math3.exception.util.LocalizedFormats;
+import org.apache.commons.math3.stat.descriptive.StatisticalSummary;
+import org.apache.commons.math3.stat.descriptive.SummaryStatistics;
+import org.apache.commons.math3.util.FastMath;
+import org.apache.commons.math3.util.MathUtils;
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.net.URL;
+import java.nio.charset.Charset;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Represents an <a href="http://http://en.wikipedia.org/wiki/Empirical_distribution_function">
+ * empirical probability distribution</a> -- a probability distribution derived from observed data
+ * without making any assumptions about the functional form of the population distribution that the
+ * data come from.
+ *
+ * <p>An <code>EmpiricalDistribution</code> maintains data structures, called <i>distribution
+ * digests</i>, that describe empirical distributions and support the following operations:
+ *
+ * <ul>
+ * <li>loading the distribution from a file of observed data values
+ * <li>dividing the input data into "bin ranges" and reporting bin frequency counts (data for
+ * histogram)
+ * <li>reporting univariate statistics describing the full set of data values as well as the
+ * observations within each bin
+ * <li>generating random values from the distribution
+ * </ul>
+ *
+ * Applications can use <code>EmpiricalDistribution</code> to build grouped frequency histograms
+ * representing the input data or to generate random values "like" those in the input file -- i.e.,
+ * the values generated will follow the distribution of the values in the file.
+ *
+ * <p>The implementation uses what amounts to the <a
+ * href="http://nedwww.ipac.caltech.edu/level5/March02/Silverman/Silver2_6.html">Variable Kernel
+ * Method</a> with Gaussian smoothing:
+ *
+ * <p><strong>Digesting the input file</strong>
+ *
+ * <ol>
+ * <li>Pass the file once to compute min and max.
+ * <li>Divide the range from min-max into <code>binCount</code> "bins."
+ * <li>Pass the data file again, computing bin counts and univariate statistics (mean, std dev.)
+ * for each of the bins
+ * <li>Divide the interval (0,1) into subintervals associated with the bins, with the length of a
+ * bin's subinterval proportional to its count.
+ * </ol>
+ *
+ * <strong>Generating random values from the distribution</strong>
+ *
+ * <ol>
+ * <li>Generate a uniformly distributed value in (0,1)
+ * <li>Select the subinterval to which the value belongs.
+ * <li>Generate a random Gaussian value with mean = mean of the associated bin and std dev = std
+ * dev of associated bin.
+ * </ol>
+ *
+ * <p>EmpiricalDistribution implements the {@link RealDistribution} interface as follows. Given x
+ * within the range of values in the dataset, let B be the bin containing x and let K be the
+ * within-bin kernel for B. Let P(B-) be the sum of the probabilities of the bins below B and let
+ * K(B) be the mass of B under K (i.e., the integral of the kernel density over B). Then set P(X <
+ * x) = P(B-) + P(B) * K(x) / K(B) where K(x) is the kernel distribution evaluated at x. This
+ * results in a cdf that matches the grouped frequency distribution at the bin endpoints and
+ * interpolates within bins using within-bin kernels. <strong>USAGE NOTES:</strong>
+ *
+ * <ul>
+ * <li>The <code>binCount</code> is set by default to 1000. A good rule of thumb is to set the bin
+ * count to approximately the length of the input file divided by 10.
+ * <li>The input file <i>must</i> be a plain text file containing one valid numeric entry per
+ * line.
+ * </ul>
+ */
+public class EmpiricalDistribution extends AbstractRealDistribution {
+
+ /** Default bin count */
+ public static final int DEFAULT_BIN_COUNT = 1000;
+
+ /** Character set for file input */
+ private static final String FILE_CHARSET = "US-ASCII";
+
+ /** Serializable version identifier */
+ private static final long serialVersionUID = 5729073523949762654L;
+
+ /** RandomDataGenerator instance to use in repeated calls to getNext() */
+ protected final RandomDataGenerator randomData;
+
+ /** List of SummaryStatistics objects characterizing the bins */
+ private final List<SummaryStatistics> binStats;
+
+ /** Sample statistics */
+ private SummaryStatistics sampleStats = null;
+
+ /** Max loaded value */
+ private double max = Double.NEGATIVE_INFINITY;
+
+ /** Min loaded value */
+ private double min = Double.POSITIVE_INFINITY;
+
+ /** Grid size */
+ private double delta = 0d;
+
+ /** number of bins */
+ private final int binCount;
+
+ /** is the distribution loaded? */
+ private boolean loaded = false;
+
+ /** upper bounds of subintervals in (0,1) "belonging" to the bins */
+ private double[] upperBounds = null;
+
+ /** Creates a new EmpiricalDistribution with the default bin count. */
+ public EmpiricalDistribution() {
+ this(DEFAULT_BIN_COUNT);
+ }
+
+ /**
+ * Creates a new EmpiricalDistribution with the specified bin count.
+ *
+ * @param binCount number of bins. Must be strictly positive.
+ * @throws NotStrictlyPositiveException if {@code binCount <= 0}.
+ */
+ public EmpiricalDistribution(int binCount) {
+ this(binCount, new RandomDataGenerator());
+ }
+
+ /**
+ * Creates a new EmpiricalDistribution with the specified bin count using the provided {@link
+ * RandomGenerator} as the source of random data.
+ *
+ * @param binCount number of bins. Must be strictly positive.
+ * @param generator random data generator (may be null, resulting in default JDK generator)
+ * @throws NotStrictlyPositiveException if {@code binCount <= 0}.
+ * @since 3.0
+ */
+ public EmpiricalDistribution(int binCount, RandomGenerator generator) {
+ this(binCount, new RandomDataGenerator(generator));
+ }
+
+ /**
+ * Creates a new EmpiricalDistribution with default bin count using the provided {@link
+ * RandomGenerator} as the source of random data.
+ *
+ * @param generator random data generator (may be null, resulting in default JDK generator)
+ * @since 3.0
+ */
+ public EmpiricalDistribution(RandomGenerator generator) {
+ this(DEFAULT_BIN_COUNT, generator);
+ }
+
+ /**
+ * Creates a new EmpiricalDistribution with the specified bin count using the provided {@link
+ * RandomDataImpl} instance as the source of random data.
+ *
+ * @param binCount number of bins
+ * @param randomData random data generator (may be null, resulting in default JDK generator)
+ * @since 3.0
+ * @deprecated As of 3.1. Please use {@link #EmpiricalDistribution(int,RandomGenerator)}
+ * instead.
+ */
+ @Deprecated
+ public EmpiricalDistribution(int binCount, RandomDataImpl randomData) {
+ this(binCount, randomData.getDelegate());
+ }
+
+ /**
+ * Creates a new EmpiricalDistribution with default bin count using the provided {@link
+ * RandomDataImpl} as the source of random data.
+ *
+ * @param randomData random data generator (may be null, resulting in default JDK generator)
+ * @since 3.0
+ * @deprecated As of 3.1. Please use {@link #EmpiricalDistribution(RandomGenerator)} instead.
+ */
+ @Deprecated
+ public EmpiricalDistribution(RandomDataImpl randomData) {
+ this(DEFAULT_BIN_COUNT, randomData);
+ }
+
+ /**
+ * Private constructor to allow lazy initialisation of the RNG contained in the {@link
+ * #randomData} instance variable.
+ *
+ * @param binCount number of bins. Must be strictly positive.
+ * @param randomData Random data generator.
+ * @throws NotStrictlyPositiveException if {@code binCount <= 0}.
+ */
+ private EmpiricalDistribution(int binCount, RandomDataGenerator randomData) {
+ super(randomData.getRandomGenerator());
+ if (binCount <= 0) {
+ throw new NotStrictlyPositiveException(binCount);
+ }
+ this.binCount = binCount;
+ this.randomData = randomData;
+ binStats = new ArrayList<SummaryStatistics>();
+ }
+
+ /**
+ * Computes the empirical distribution from the provided array of numbers.
+ *
+ * @param in the input data array
+ * @exception NullArgumentException if in is null
+ */
+ public void load(double[] in) throws NullArgumentException {
+ DataAdapter da = new ArrayDataAdapter(in);
+ try {
+ da.computeStats();
+ // new adapter for the second pass
+ fillBinStats(new ArrayDataAdapter(in));
+ } catch (IOException ex) {
+ // Can't happen
+ throw new MathInternalError();
+ }
+ loaded = true;
+ }
+
+ /**
+ * Computes the empirical distribution using data read from a URL.
+ *
+ * <p>The input file <i>must</i> be an ASCII text file containing one valid numeric entry per
+ * line.
+ *
+ * @param url url of the input file
+ * @throws IOException if an IO error occurs
+ * @throws NullArgumentException if url is null
+ * @throws ZeroException if URL contains no data
+ */
+ public void load(URL url) throws IOException, NullArgumentException, ZeroException {
+ MathUtils.checkNotNull(url);
+ Charset charset = Charset.forName(FILE_CHARSET);
+ BufferedReader in = new BufferedReader(new InputStreamReader(url.openStream(), charset));
+ try {
+ DataAdapter da = new StreamDataAdapter(in);
+ da.computeStats();
+ if (sampleStats.getN() == 0) {
+ throw new ZeroException(LocalizedFormats.URL_CONTAINS_NO_DATA, url);
+ }
+ // new adapter for the second pass
+ in = new BufferedReader(new InputStreamReader(url.openStream(), charset));
+ fillBinStats(new StreamDataAdapter(in));
+ loaded = true;
+ } finally {
+ try {
+ in.close();
+ } catch (IOException ex) { // NOPMD
+ // ignore
+ }
+ }
+ }
+
+ /**
+ * Computes the empirical distribution from the input file.
+ *
+ * <p>The input file <i>must</i> be an ASCII text file containing one valid numeric entry per
+ * line.
+ *
+ * @param file the input file
+ * @throws IOException if an IO error occurs
+ * @throws NullArgumentException if file is null
+ */
+ public void load(File file) throws IOException, NullArgumentException {
+ MathUtils.checkNotNull(file);
+ Charset charset = Charset.forName(FILE_CHARSET);
+ InputStream is = new FileInputStream(file);
+ BufferedReader in = new BufferedReader(new InputStreamReader(is, charset));
+ try {
+ DataAdapter da = new StreamDataAdapter(in);
+ da.computeStats();
+ // new adapter for second pass
+ is = new FileInputStream(file);
+ in = new BufferedReader(new InputStreamReader(is, charset));
+ fillBinStats(new StreamDataAdapter(in));
+ loaded = true;
+ } finally {
+ try {
+ in.close();
+ } catch (IOException ex) { // NOPMD
+ // ignore
+ }
+ }
+ }
+
+ /**
+ * Provides methods for computing <code>sampleStats</code> and <code>beanStats</code>
+ * abstracting the source of data.
+ */
+ private abstract class DataAdapter {
+
+ /**
+ * Compute bin stats.
+ *
+ * @throws IOException if an error occurs computing bin stats
+ */
+ public abstract void computeBinStats() throws IOException;
+
+ /**
+ * Compute sample statistics.
+ *
+ * @throws IOException if an error occurs computing sample stats
+ */
+ public abstract void computeStats() throws IOException;
+ }
+
+ /** <code>DataAdapter</code> for data provided through some input stream */
+ private class StreamDataAdapter extends DataAdapter {
+
+ /** Input stream providing access to the data */
+ private BufferedReader inputStream;
+
+ /**
+ * Create a StreamDataAdapter from a BufferedReader
+ *
+ * @param in BufferedReader input stream
+ */
+ StreamDataAdapter(BufferedReader in) {
+ super();
+ inputStream = in;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void computeBinStats() throws IOException {
+ String str = null;
+ double val = 0.0d;
+ while ((str = inputStream.readLine()) != null) {
+ val = Double.parseDouble(str);
+ SummaryStatistics stats = binStats.get(findBin(val));
+ stats.addValue(val);
+ }
+
+ inputStream.close();
+ inputStream = null;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void computeStats() throws IOException {
+ String str = null;
+ double val = 0.0;
+ sampleStats = new SummaryStatistics();
+ while ((str = inputStream.readLine()) != null) {
+ val = Double.parseDouble(str);
+ sampleStats.addValue(val);
+ }
+ inputStream.close();
+ inputStream = null;
+ }
+ }
+
+ /** <code>DataAdapter</code> for data provided as array of doubles. */
+ private class ArrayDataAdapter extends DataAdapter {
+
+ /** Array of input data values */
+ private double[] inputArray;
+
+ /**
+ * Construct an ArrayDataAdapter from a double[] array
+ *
+ * @param in double[] array holding the data
+ * @throws NullArgumentException if in is null
+ */
+ ArrayDataAdapter(double[] in) throws NullArgumentException {
+ super();
+ MathUtils.checkNotNull(in);
+ inputArray = in;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void computeStats() throws IOException {
+ sampleStats = new SummaryStatistics();
+ for (int i = 0; i < inputArray.length; i++) {
+ sampleStats.addValue(inputArray[i]);
+ }
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void computeBinStats() throws IOException {
+ for (int i = 0; i < inputArray.length; i++) {
+ SummaryStatistics stats = binStats.get(findBin(inputArray[i]));
+ stats.addValue(inputArray[i]);
+ }
+ }
+ }
+
+ /**
+ * Fills binStats array (second pass through data file).
+ *
+ * @param da object providing access to the data
+ * @throws IOException if an IO error occurs
+ */
+ private void fillBinStats(final DataAdapter da) throws IOException {
+ // Set up grid
+ min = sampleStats.getMin();
+ max = sampleStats.getMax();
+ delta = (max - min) / ((double) binCount);
+
+ // Initialize binStats ArrayList
+ if (!binStats.isEmpty()) {
+ binStats.clear();
+ }
+ for (int i = 0; i < binCount; i++) {
+ SummaryStatistics stats = new SummaryStatistics();
+ binStats.add(i, stats);
+ }
+
+ // Filling data in binStats Array
+ da.computeBinStats();
+
+ // Assign upperBounds based on bin counts
+ upperBounds = new double[binCount];
+ upperBounds[0] = ((double) binStats.get(0).getN()) / (double) sampleStats.getN();
+ for (int i = 1; i < binCount - 1; i++) {
+ upperBounds[i] =
+ upperBounds[i - 1]
+ + ((double) binStats.get(i).getN()) / (double) sampleStats.getN();
+ }
+ upperBounds[binCount - 1] = 1.0d;
+ }
+
+ /**
+ * Returns the index of the bin to which the given value belongs
+ *
+ * @param value the value whose bin we are trying to find
+ * @return the index of the bin containing the value
+ */
+ private int findBin(double value) {
+ return FastMath.min(
+ FastMath.max((int) FastMath.ceil((value - min) / delta) - 1, 0), binCount - 1);
+ }
+
+ /**
+ * Generates a random value from this distribution. <strong>Preconditions:</strong>
+ *
+ * <ul>
+ * <li>the distribution must be loaded before invoking this method
+ * </ul>
+ *
+ * @return the random value.
+ * @throws MathIllegalStateException if the distribution has not been loaded
+ */
+ public double getNextValue() throws MathIllegalStateException {
+
+ if (!loaded) {
+ throw new MathIllegalStateException(LocalizedFormats.DISTRIBUTION_NOT_LOADED);
+ }
+
+ return sample();
+ }
+
+ /**
+ * Returns a {@link StatisticalSummary} describing this distribution.
+ * <strong>Preconditions:</strong>
+ *
+ * <ul>
+ * <li>the distribution must be loaded before invoking this method
+ * </ul>
+ *
+ * @return the sample statistics
+ * @throws IllegalStateException if the distribution has not been loaded
+ */
+ public StatisticalSummary getSampleStats() {
+ return sampleStats;
+ }
+
+ /**
+ * Returns the number of bins.
+ *
+ * @return the number of bins.
+ */
+ public int getBinCount() {
+ return binCount;
+ }
+
+ /**
+ * Returns a List of {@link SummaryStatistics} instances containing statistics describing the
+ * values in each of the bins. The list is indexed on the bin number.
+ *
+ * @return List of bin statistics.
+ */
+ public List<SummaryStatistics> getBinStats() {
+ return binStats;
+ }
+
+ /**
+ * Returns a fresh copy of the array of upper bounds for the bins. Bins are: <br>
+ * [min,upperBounds[0]],(upperBounds[0],upperBounds[1]],..., (upperBounds[binCount-2],
+ * upperBounds[binCount-1] = max].
+ *
+ * <p>Note: In versions 1.0-2.0 of commons-math, this method incorrectly returned the array of
+ * probability generator upper bounds now returned by {@link #getGeneratorUpperBounds()}.
+ *
+ * @return array of bin upper bounds
+ * @since 2.1
+ */
+ public double[] getUpperBounds() {
+ double[] binUpperBounds = new double[binCount];
+ for (int i = 0; i < binCount - 1; i++) {
+ binUpperBounds[i] = min + delta * (i + 1);
+ }
+ binUpperBounds[binCount - 1] = max;
+ return binUpperBounds;
+ }
+
+ /**
+ * Returns a fresh copy of the array of upper bounds of the subintervals of [0,1] used in
+ * generating data from the empirical distribution. Subintervals correspond to bins with lengths
+ * proportional to bin counts. <strong>Preconditions:</strong>
+ *
+ * <ul>
+ * <li>the distribution must be loaded before invoking this method
+ * </ul>
+ *
+ * <p>In versions 1.0-2.0 of commons-math, this array was (incorrectly) returned by {@link
+ * #getUpperBounds()}.
+ *
+ * @since 2.1
+ * @return array of upper bounds of subintervals used in data generation
+ * @throws NullPointerException unless a {@code load} method has been called beforehand.
+ */
+ public double[] getGeneratorUpperBounds() {
+ int len = upperBounds.length;
+ double[] out = new double[len];
+ System.arraycopy(upperBounds, 0, out, 0, len);
+ return out;
+ }
+
+ /**
+ * Property indicating whether or not the distribution has been loaded.
+ *
+ * @return true if the distribution has been loaded
+ */
+ public boolean isLoaded() {
+ return loaded;
+ }
+
+ /**
+ * Reseeds the random number generator used by {@link #getNextValue()}.
+ *
+ * @param seed random generator seed
+ * @since 3.0
+ */
+ public void reSeed(long seed) {
+ randomData.reSeed(seed);
+ }
+
+ // Distribution methods ---------------------------
+
+ /**
+ * {@inheritDoc}
+ *
+ * @since 3.1
+ */
+ @Override
+ public double probability(double x) {
+ return 0;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * <p>Returns the kernel density normalized so that its integral over each bin equals the bin
+ * mass.
+ *
+ * <p>Algorithm description:
+ *
+ * <ol>
+ * <li>Find the bin B that x belongs to.
+ * <li>Compute K(B) = the mass of B with respect to the within-bin kernel (i.e., the integral
+ * of the kernel density over B).
+ * <li>Return k(x) * P(B) / K(B), where k is the within-bin kernel density and P(B) is the
+ * mass of B.
+ * </ol>
+ *
+ * @since 3.1
+ */
+ public double density(double x) {
+ if (x < min || x > max) {
+ return 0d;
+ }
+ final int binIndex = findBin(x);
+ final RealDistribution kernel = getKernel(binStats.get(binIndex));
+ return kernel.density(x) * pB(binIndex) / kB(binIndex);
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * <p>Algorithm description:
+ *
+ * <ol>
+ * <li>Find the bin B that x belongs to.
+ * <li>Compute P(B) = the mass of B and P(B-) = the combined mass of the bins below B.
+ * <li>Compute K(B) = the probability mass of B with respect to the within-bin kernel and
+ * K(B-) = the kernel distribution evaluated at the lower endpoint of B
+ * <li>Return P(B-) + P(B) * [K(x) - K(B-)] / K(B) where K(x) is the within-bin kernel
+ * distribution function evaluated at x.
+ * </ol>
+ *
+ * If K is a constant distribution, we return P(B-) + P(B) (counting the full mass of B).
+ *
+ * @since 3.1
+ */
+ public double cumulativeProbability(double x) {
+ if (x < min) {
+ return 0d;
+ } else if (x >= max) {
+ return 1d;
+ }
+ final int binIndex = findBin(x);
+ final double pBminus = pBminus(binIndex);
+ final double pB = pB(binIndex);
+ final RealDistribution kernel = k(x);
+ if (kernel instanceof ConstantRealDistribution) {
+ if (x < kernel.getNumericalMean()) {
+ return pBminus;
+ } else {
+ return pBminus + pB;
+ }
+ }
+ final double[] binBounds = getUpperBounds();
+ final double kB = kB(binIndex);
+ final double lower = binIndex == 0 ? min : binBounds[binIndex - 1];
+ final double withinBinCum =
+ (kernel.cumulativeProbability(x) - kernel.cumulativeProbability(lower)) / kB;
+ return pBminus + pB * withinBinCum;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * <p>Algorithm description:
+ *
+ * <ol>
+ * <li>Find the smallest i such that the sum of the masses of the bins through i is at least
+ * p.
+ * <li>Let K be the within-bin kernel distribution for bin i.</br> Let K(B) be the mass of B
+ * under K. <br>
+ * Let K(B-) be K evaluated at the lower endpoint of B (the combined mass of the bins
+ * below B under K).<br>
+ * Let P(B) be the probability of bin i.<br>
+ * Let P(B-) be the sum of the bin masses below bin i. <br>
+ * Let pCrit = p - P(B-)<br>
+ * <li>Return the inverse of K evaluated at <br>
+ * K(B-) + pCrit * K(B) / P(B)
+ * </ol>
+ *
+ * @since 3.1
+ */
+ @Override
+ public double inverseCumulativeProbability(final double p) throws OutOfRangeException {
+ if (p < 0.0 || p > 1.0) {
+ throw new OutOfRangeException(p, 0, 1);
+ }
+
+ if (p == 0.0) {
+ return getSupportLowerBound();
+ }
+
+ if (p == 1.0) {
+ return getSupportUpperBound();
+ }
+
+ int i = 0;
+ while (cumBinP(i) < p) {
+ i++;
+ }
+
+ final RealDistribution kernel = getKernel(binStats.get(i));
+ final double kB = kB(i);
+ final double[] binBounds = getUpperBounds();
+ final double lower = i == 0 ? min : binBounds[i - 1];
+ final double kBminus = kernel.cumulativeProbability(lower);
+ final double pB = pB(i);
+ final double pBminus = pBminus(i);
+ final double pCrit = p - pBminus;
+ if (pCrit <= 0) {
+ return lower;
+ }
+ return kernel.inverseCumulativeProbability(kBminus + pCrit * kB / pB);
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @since 3.1
+ */
+ public double getNumericalMean() {
+ return sampleStats.getMean();
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @since 3.1
+ */
+ public double getNumericalVariance() {
+ return sampleStats.getVariance();
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @since 3.1
+ */
+ public double getSupportLowerBound() {
+ return min;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @since 3.1
+ */
+ public double getSupportUpperBound() {
+ return max;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @since 3.1
+ */
+ public boolean isSupportLowerBoundInclusive() {
+ return true;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @since 3.1
+ */
+ public boolean isSupportUpperBoundInclusive() {
+ return true;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @since 3.1
+ */
+ public boolean isSupportConnected() {
+ return true;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @since 3.1
+ */
+ @Override
+ public void reseedRandomGenerator(long seed) {
+ randomData.reSeed(seed);
+ }
+
+ /**
+ * The probability of bin i.
+ *
+ * @param i the index of the bin
+ * @return the probability that selection begins in bin i
+ */
+ private double pB(int i) {
+ return i == 0 ? upperBounds[0] : upperBounds[i] - upperBounds[i - 1];
+ }
+
+ /**
+ * The combined probability of the bins up to but not including bin i.
+ *
+ * @param i the index of the bin
+ * @return the probability that selection begins in a bin below bin i.
+ */
+ private double pBminus(int i) {
+ return i == 0 ? 0 : upperBounds[i - 1];
+ }
+
+ /**
+ * Mass of bin i under the within-bin kernel of the bin.
+ *
+ * @param i index of the bin
+ * @return the difference in the within-bin kernel cdf between the upper and lower endpoints of
+ * bin i
+ */
+ @SuppressWarnings("deprecation")
+ private double kB(int i) {
+ final double[] binBounds = getUpperBounds();
+ final RealDistribution kernel = getKernel(binStats.get(i));
+ return i == 0
+ ? kernel.cumulativeProbability(min, binBounds[0])
+ : kernel.cumulativeProbability(binBounds[i - 1], binBounds[i]);
+ }
+
+ /**
+ * The within-bin kernel of the bin that x belongs to.
+ *
+ * @param x the value to locate within a bin
+ * @return the within-bin kernel of the bin containing x
+ */
+ private RealDistribution k(double x) {
+ final int binIndex = findBin(x);
+ return getKernel(binStats.get(binIndex));
+ }
+
+ /**
+ * The combined probability of the bins up to and including binIndex.
+ *
+ * @param binIndex maximum bin index
+ * @return sum of the probabilities of bins through binIndex
+ */
+ private double cumBinP(int binIndex) {
+ return upperBounds[binIndex];
+ }
+
+ /**
+ * The within-bin smoothing kernel. Returns a Gaussian distribution parameterized by {@code
+ * bStats}, unless the bin contains only one observation, in which case a constant distribution
+ * is returned.
+ *
+ * @param bStats summary statistics for the bin
+ * @return within-bin kernel parameterized by bStats
+ */
+ protected RealDistribution getKernel(SummaryStatistics bStats) {
+ if (bStats.getN() == 1 || bStats.getVariance() == 0) {
+ return new ConstantRealDistribution(bStats.getMean());
+ } else {
+ return new NormalDistribution(
+ randomData.getRandomGenerator(),
+ bStats.getMean(),
+ bStats.getStandardDeviation(),
+ NormalDistribution.DEFAULT_INVERSE_ABSOLUTE_ACCURACY);
+ }
+ }
+}
diff --git a/src/main/java/org/apache/commons/math3/random/GaussianRandomGenerator.java b/src/main/java/org/apache/commons/math3/random/GaussianRandomGenerator.java
new file mode 100644
index 0000000..33eec56
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/random/GaussianRandomGenerator.java
@@ -0,0 +1,49 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math3.random;
+
+/**
+ * This class is a gaussian normalized random generator for scalars.
+ *
+ * <p>This class is a simple wrapper around the {@link RandomGenerator#nextGaussian} method.
+ *
+ * @since 1.2
+ */
+public class GaussianRandomGenerator implements NormalizedRandomGenerator {
+
+ /** Underlying generator. */
+ private final RandomGenerator generator;
+
+ /**
+ * Create a new generator.
+ *
+ * @param generator underlying random generator to use
+ */
+ public GaussianRandomGenerator(final RandomGenerator generator) {
+ this.generator = generator;
+ }
+
+ /**
+ * Generate a random scalar with null mean and unit standard deviation.
+ *
+ * @return a random scalar with null mean and unit standard deviation
+ */
+ public double nextNormalizedDouble() {
+ return generator.nextGaussian();
+ }
+}
diff --git a/src/main/java/org/apache/commons/math3/random/HaltonSequenceGenerator.java b/src/main/java/org/apache/commons/math3/random/HaltonSequenceGenerator.java
new file mode 100644
index 0000000..2b1c623
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/random/HaltonSequenceGenerator.java
@@ -0,0 +1,195 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math3.random;
+
+import org.apache.commons.math3.exception.DimensionMismatchException;
+import org.apache.commons.math3.exception.NotPositiveException;
+import org.apache.commons.math3.exception.NullArgumentException;
+import org.apache.commons.math3.exception.OutOfRangeException;
+import org.apache.commons.math3.util.MathUtils;
+
+/**
+ * Implementation of a Halton sequence.
+ *
+ * <p>A Halton sequence is a low-discrepancy sequence generating points in the interval [0, 1]
+ * according to
+ *
+ * <pre>
+ * H(n) = d_0 / b + d_1 / b^2 .... d_j / b^j+1
+ *
+ * with
+ *
+ * n = d_j * b^j-1 + ... d_1 * b + d_0 * b^0
+ * </pre>
+ *
+ * For higher dimensions, subsequent prime numbers are used as base, e.g. { 2, 3, 5 } for a Halton
+ * sequence in R^3.
+ *
+ * <p>Halton sequences are known to suffer from linear correlation for larger prime numbers, thus
+ * the individual digits are usually scrambled. This implementation already comes with support for
+ * up to 40 dimensions with optimal weight numbers from <a
+ * href="http://etd.lib.fsu.edu/theses/available/etd-07062004-140409/unrestricted/dissertation1.pdf">
+ * H. Chi: Scrambled quasirandom sequences and their applications</a>.
+ *
+ * <p>The generator supports two modes:
+ *
+ * <ul>
+ * <li>sequential generation of points: {@link #nextVector()}
+ * <li>random access to the i-th point in the sequence: {@link #skipTo(int)}
+ * </ul>
+ *
+ * @see <a href="http://en.wikipedia.org/wiki/Halton_sequence">Halton sequence (Wikipedia)</a>
+ * @see <a href="https://lirias.kuleuven.be/bitstream/123456789/131168/1/mcm2005_bartv.pdf">On the
+ * Halton sequence and its scramblings</a>
+ * @since 3.3
+ */
+public class HaltonSequenceGenerator implements RandomVectorGenerator {
+
+ /** The first 40 primes. */
+ private static final int[] PRIMES =
+ new int[] {
+ 2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, 73, 79,
+ 83, 89, 97, 101, 103, 107, 109, 113, 127, 131, 137, 139, 149, 151, 157, 163, 167,
+ 173
+ };
+
+ /** The optimal weights used for scrambling of the first 40 dimension. */
+ private static final int[] WEIGHTS =
+ new int[] {
+ 1, 2, 3, 3, 8, 11, 12, 14, 7, 18, 12, 13, 17, 18, 29, 14, 18, 43, 41, 44, 40, 30,
+ 47, 65, 71, 28, 40, 60, 79, 89, 56, 50, 52, 61, 108, 56, 66, 63, 60, 66
+ };
+
+ /** Space dimension. */
+ private final int dimension;
+
+ /** The current index in the sequence. */
+ private int count = 0;
+
+ /** The base numbers for each component. */
+ private final int[] base;
+
+ /** The scrambling weights for each component. */
+ private final int[] weight;
+
+ /**
+ * Construct a new Halton sequence generator for the given space dimension.
+ *
+ * @param dimension the space dimension
+ * @throws OutOfRangeException if the space dimension is outside the allowed range of [1, 40]
+ */
+ public HaltonSequenceGenerator(final int dimension) throws OutOfRangeException {
+ this(dimension, PRIMES, WEIGHTS);
+ }
+
+ /**
+ * Construct a new Halton sequence generator with the given base numbers and weights for each
+ * dimension. The length of the bases array defines the space dimension and is required to be
+ * &gt; 0.
+ *
+ * @param dimension the space dimension
+ * @param bases the base number for each dimension, entries should be (pairwise) prime, may not
+ * be null
+ * @param weights the weights used during scrambling, may be null in which case no scrambling
+ * will be performed
+ * @throws NullArgumentException if base is null
+ * @throws OutOfRangeException if the space dimension is outside the range [1, len], where len
+ * refers to the length of the bases array
+ * @throws DimensionMismatchException if weights is non-null and the length of the input arrays
+ * differ
+ */
+ public HaltonSequenceGenerator(final int dimension, final int[] bases, final int[] weights)
+ throws NullArgumentException, OutOfRangeException, DimensionMismatchException {
+
+ MathUtils.checkNotNull(bases);
+
+ if (dimension < 1 || dimension > bases.length) {
+ throw new OutOfRangeException(dimension, 1, PRIMES.length);
+ }
+
+ if (weights != null && weights.length != bases.length) {
+ throw new DimensionMismatchException(weights.length, bases.length);
+ }
+
+ this.dimension = dimension;
+ this.base = bases.clone();
+ this.weight = weights == null ? null : weights.clone();
+ count = 0;
+ }
+
+ /** {@inheritDoc} */
+ public double[] nextVector() {
+ final double[] v = new double[dimension];
+ for (int i = 0; i < dimension; i++) {
+ int index = count;
+ double f = 1.0 / base[i];
+
+ int j = 0;
+ while (index > 0) {
+ final int digit = scramble(i, j, base[i], index % base[i]);
+ v[i] += f * digit;
+ index /= base[i]; // floor( index / base )
+ f /= base[i];
+ }
+ }
+ count++;
+ return v;
+ }
+
+ /**
+ * Performs scrambling of digit {@code d_j} according to the formula:
+ *
+ * <pre>
+ * ( weight_i * d_j ) mod base
+ * </pre>
+ *
+ * Implementations can override this method to do a different scrambling.
+ *
+ * @param i the dimension index
+ * @param j the digit index
+ * @param b the base for this dimension
+ * @param digit the j-th digit
+ * @return the scrambled digit
+ */
+ protected int scramble(final int i, final int j, final int b, final int digit) {
+ return weight != null ? (weight[i] * digit) % b : digit;
+ }
+
+ /**
+ * Skip to the i-th point in the Halton sequence.
+ *
+ * <p>This operation can be performed in O(1).
+ *
+ * @param index the index in the sequence to skip to
+ * @return the i-th point in the Halton sequence
+ * @throws NotPositiveException if index &lt; 0
+ */
+ public double[] skipTo(final int index) throws NotPositiveException {
+ count = index;
+ return nextVector();
+ }
+
+ /**
+ * Returns the index i of the next point in the Halton sequence that will be returned by calling
+ * {@link #nextVector()}.
+ *
+ * @return the index of the next point
+ */
+ public int getNextIndex() {
+ return count;
+ }
+}
diff --git a/src/main/java/org/apache/commons/math3/random/ISAACRandom.java b/src/main/java/org/apache/commons/math3/random/ISAACRandom.java
new file mode 100644
index 0000000..8d10af5
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/random/ISAACRandom.java
@@ -0,0 +1,279 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math3.random;
+
+import org.apache.commons.math3.util.FastMath;
+
+import java.io.Serializable;
+
+/**
+ * <a href="http://burtleburtle.net/bob/rand/isaacafa.html">ISAAC: a fast cryptographic
+ * pseudo-random number generator</a> <br>
+ * ISAAC (Indirection, Shift, Accumulate, Add, and Count) generates 32-bit random numbers. ISAAC has
+ * been designed to be cryptographically secure and is inspired by RC4. Cycles are guaranteed to be
+ * at least 2<sup>40</sup> values long, and they are 2<sup>8295</sup> values long on average. The
+ * results are uniformly distributed, unbiased, and unpredictable unless you know the seed. <br>
+ * This code is based (with minor changes and improvements) on the original implementation of the
+ * algorithm by Bob Jenkins. <br>
+ *
+ * @since 3.0
+ */
+public class ISAACRandom extends BitsStreamGenerator implements Serializable {
+ /** Serializable version identifier */
+ private static final long serialVersionUID = 7288197941165002400L;
+
+ /** Log of size of rsl[] and mem[] */
+ private static final int SIZE_L = 8;
+
+ /** Size of rsl[] and mem[] */
+ private static final int SIZE = 1 << SIZE_L;
+
+ /** Half-size of rsl[] and mem[] */
+ private static final int H_SIZE = SIZE >> 1;
+
+ /** For pseudo-random lookup */
+ private static final int MASK = SIZE - 1 << 2;
+
+ /** The golden ratio */
+ private static final int GLD_RATIO = 0x9e3779b9;
+
+ /** The results given to the user */
+ private final int[] rsl = new int[SIZE];
+
+ /** The internal state */
+ private final int[] mem = new int[SIZE];
+
+ /** Count through the results in rsl[] */
+ private int count;
+
+ /** Accumulator */
+ private int isaacA;
+
+ /** The last result */
+ private int isaacB;
+
+ /** Counter, guarantees cycle is at least 2^40 */
+ private int isaacC;
+
+ /** Service variable. */
+ private final int[] arr = new int[8];
+
+ /** Service variable. */
+ private int isaacX;
+
+ /** Service variable. */
+ private int isaacI;
+
+ /** Service variable. */
+ private int isaacJ;
+
+ /**
+ * Creates a new ISAAC random number generator. <br>
+ * The instance is initialized using a combination of the current time and system hash code of
+ * the instance as the seed.
+ */
+ public ISAACRandom() {
+ setSeed(System.currentTimeMillis() + System.identityHashCode(this));
+ }
+
+ /**
+ * Creates a new ISAAC random number generator using a single long seed.
+ *
+ * @param seed Initial seed.
+ */
+ public ISAACRandom(long seed) {
+ setSeed(seed);
+ }
+
+ /**
+ * Creates a new ISAAC random number generator using an int array seed.
+ *
+ * @param seed Initial seed. If {@code null}, the seed will be related to the current time.
+ */
+ public ISAACRandom(int[] seed) {
+ setSeed(seed);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void setSeed(int seed) {
+ setSeed(new int[] {seed});
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void setSeed(long seed) {
+ setSeed(new int[] {(int) (seed >>> 32), (int) (seed & 0xffffffffL)});
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void setSeed(int[] seed) {
+ if (seed == null) {
+ setSeed(System.currentTimeMillis() + System.identityHashCode(this));
+ return;
+ }
+ final int seedLen = seed.length;
+ final int rslLen = rsl.length;
+ System.arraycopy(seed, 0, rsl, 0, FastMath.min(seedLen, rslLen));
+ if (seedLen < rslLen) {
+ for (int j = seedLen; j < rslLen; j++) {
+ long k = rsl[j - seedLen];
+ rsl[j] = (int) (0x6c078965L * (k ^ k >> 30) + j & 0xffffffffL);
+ }
+ }
+ initState();
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ protected int next(int bits) {
+ if (count < 0) {
+ isaac();
+ count = SIZE - 1;
+ }
+ return rsl[count--] >>> 32 - bits;
+ }
+
+ /** Generate 256 results */
+ private void isaac() {
+ isaacI = 0;
+ isaacJ = H_SIZE;
+ isaacB += ++isaacC;
+ while (isaacI < H_SIZE) {
+ isaac2();
+ }
+ isaacJ = 0;
+ while (isaacJ < H_SIZE) {
+ isaac2();
+ }
+ }
+
+ /** Intermediate internal loop. */
+ private void isaac2() {
+ isaacX = mem[isaacI];
+ isaacA ^= isaacA << 13;
+ isaacA += mem[isaacJ++];
+ isaac3();
+ isaacX = mem[isaacI];
+ isaacA ^= isaacA >>> 6;
+ isaacA += mem[isaacJ++];
+ isaac3();
+ isaacX = mem[isaacI];
+ isaacA ^= isaacA << 2;
+ isaacA += mem[isaacJ++];
+ isaac3();
+ isaacX = mem[isaacI];
+ isaacA ^= isaacA >>> 16;
+ isaacA += mem[isaacJ++];
+ isaac3();
+ }
+
+ /** Lowest level internal loop. */
+ private void isaac3() {
+ mem[isaacI] = mem[(isaacX & MASK) >> 2] + isaacA + isaacB;
+ isaacB = mem[(mem[isaacI] >> SIZE_L & MASK) >> 2] + isaacX;
+ rsl[isaacI++] = isaacB;
+ }
+
+ /** Initialize, or reinitialize, this instance of rand. */
+ private void initState() {
+ isaacA = 0;
+ isaacB = 0;
+ isaacC = 0;
+ for (int j = 0; j < arr.length; j++) {
+ arr[j] = GLD_RATIO;
+ }
+ for (int j = 0; j < 4; j++) {
+ shuffle();
+ }
+ // fill in mem[] with messy stuff
+ for (int j = 0; j < SIZE; j += 8) {
+ arr[0] += rsl[j];
+ arr[1] += rsl[j + 1];
+ arr[2] += rsl[j + 2];
+ arr[3] += rsl[j + 3];
+ arr[4] += rsl[j + 4];
+ arr[5] += rsl[j + 5];
+ arr[6] += rsl[j + 6];
+ arr[7] += rsl[j + 7];
+ shuffle();
+ setState(j);
+ }
+ // second pass makes all of seed affect all of mem
+ for (int j = 0; j < SIZE; j += 8) {
+ arr[0] += mem[j];
+ arr[1] += mem[j + 1];
+ arr[2] += mem[j + 2];
+ arr[3] += mem[j + 3];
+ arr[4] += mem[j + 4];
+ arr[5] += mem[j + 5];
+ arr[6] += mem[j + 6];
+ arr[7] += mem[j + 7];
+ shuffle();
+ setState(j);
+ }
+ isaac();
+ count = SIZE - 1;
+ clear();
+ }
+
+ /** Shuffle array. */
+ private void shuffle() {
+ arr[0] ^= arr[1] << 11;
+ arr[3] += arr[0];
+ arr[1] += arr[2];
+ arr[1] ^= arr[2] >>> 2;
+ arr[4] += arr[1];
+ arr[2] += arr[3];
+ arr[2] ^= arr[3] << 8;
+ arr[5] += arr[2];
+ arr[3] += arr[4];
+ arr[3] ^= arr[4] >>> 16;
+ arr[6] += arr[3];
+ arr[4] += arr[5];
+ arr[4] ^= arr[5] << 10;
+ arr[7] += arr[4];
+ arr[5] += arr[6];
+ arr[5] ^= arr[6] >>> 4;
+ arr[0] += arr[5];
+ arr[6] += arr[7];
+ arr[6] ^= arr[7] << 8;
+ arr[1] += arr[6];
+ arr[7] += arr[0];
+ arr[7] ^= arr[0] >>> 9;
+ arr[2] += arr[7];
+ arr[0] += arr[1];
+ }
+
+ /**
+ * Set the state by copying the internal arrays.
+ *
+ * @param start First index into {@link #mem} array.
+ */
+ private void setState(int start) {
+ mem[start] = arr[0];
+ mem[start + 1] = arr[1];
+ mem[start + 2] = arr[2];
+ mem[start + 3] = arr[3];
+ mem[start + 4] = arr[4];
+ mem[start + 5] = arr[5];
+ mem[start + 6] = arr[6];
+ mem[start + 7] = arr[7];
+ }
+}
diff --git a/src/main/java/org/apache/commons/math3/random/JDKRandomGenerator.java b/src/main/java/org/apache/commons/math3/random/JDKRandomGenerator.java
new file mode 100644
index 0000000..e9fe954
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/random/JDKRandomGenerator.java
@@ -0,0 +1,55 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math3.random;
+
+import java.util.Random;
+
+/**
+ * Extension of <code>java.util.Random</code> to implement {@link RandomGenerator}.
+ *
+ * @since 1.1
+ */
+public class JDKRandomGenerator extends Random implements RandomGenerator {
+
+ /** Serializable version identifier. */
+ private static final long serialVersionUID = -7745277476784028798L;
+
+ /** Create a new JDKRandomGenerator with a default seed. */
+ public JDKRandomGenerator() {
+ super();
+ }
+
+ /**
+ * Create a new JDKRandomGenerator with the given seed.
+ *
+ * @param seed initial seed
+ * @since 3.6
+ */
+ public JDKRandomGenerator(int seed) {
+ setSeed(seed);
+ }
+
+ /** {@inheritDoc} */
+ public void setSeed(int seed) {
+ setSeed((long) seed);
+ }
+
+ /** {@inheritDoc} */
+ public void setSeed(int[] seed) {
+ setSeed(RandomGeneratorFactory.convertToLong(seed));
+ }
+}
diff --git a/src/main/java/org/apache/commons/math3/random/MersenneTwister.java b/src/main/java/org/apache/commons/math3/random/MersenneTwister.java
new file mode 100644
index 0000000..44ce85c
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/random/MersenneTwister.java
@@ -0,0 +1,275 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math3.random;
+
+import org.apache.commons.math3.util.FastMath;
+
+import java.io.Serializable;
+
+/**
+ * This class implements a powerful pseudo-random number generator developed by Makoto Matsumoto and
+ * Takuji Nishimura during 1996-1997.
+ *
+ * <p>This generator features an extremely long period (2<sup>19937</sup>-1) and 623-dimensional
+ * equidistribution up to 32 bits accuracy. The home page for this generator is located at <a
+ * href="http://www.math.sci.hiroshima-u.ac.jp/~m-mat/MT/emt.html">
+ * http://www.math.sci.hiroshima-u.ac.jp/~m-mat/MT/emt.html</a>.
+ *
+ * <p>This generator is described in a paper by Makoto Matsumoto and Takuji Nishimura in 1998: <a
+ * href="http://www.math.sci.hiroshima-u.ac.jp/~m-mat/MT/ARTICLES/mt.pdf">Mersenne Twister: A
+ * 623-Dimensionally Equidistributed Uniform Pseudo-Random Number Generator</a>, ACM Transactions on
+ * Modeling and Computer Simulation, Vol. 8, No. 1, January 1998, pp 3--30
+ *
+ * <p>This class is mainly a Java port of the 2002-01-26 version of the generator written in C by
+ * Makoto Matsumoto and Takuji Nishimura. Here is their original copyright:
+ *
+ * <table border="0" width="80%" cellpadding="10" align="center" bgcolor="#E0E0E0">
+ * <tr><td>Copyright (C) 1997 - 2002, Makoto Matsumoto and Takuji Nishimura,
+ * All rights reserved.</td></tr>
+ *
+ * <tr><td>Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * <ol>
+ * <li>Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.</li>
+ * <li>Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.</li>
+ * <li>The names of its contributors may not be used to endorse or promote
+ * products derived from this software without specific prior written
+ * permission.</li>
+ * </ol></td></tr>
+ *
+ * <tr><td><strong>THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
+ * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
+ * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.</strong></td></tr>
+ * </table>
+ *
+ * @since 2.0
+ */
+public class MersenneTwister extends BitsStreamGenerator implements Serializable {
+
+ /** Serializable version identifier. */
+ private static final long serialVersionUID = 8661194735290153518L;
+
+ /** Size of the bytes pool. */
+ private static final int N = 624;
+
+ /** Period second parameter. */
+ private static final int M = 397;
+
+ /** X * MATRIX_A for X = {0, 1}. */
+ private static final int[] MAG01 = {0x0, 0x9908b0df};
+
+ /** Bytes pool. */
+ private int[] mt;
+
+ /** Current index in the bytes pool. */
+ private int mti;
+
+ /**
+ * Creates a new random number generator.
+ *
+ * <p>The instance is initialized using the current time plus the system identity hash code of
+ * this instance as the seed.
+ */
+ public MersenneTwister() {
+ mt = new int[N];
+ setSeed(System.currentTimeMillis() + System.identityHashCode(this));
+ }
+
+ /**
+ * Creates a new random number generator using a single int seed.
+ *
+ * @param seed the initial seed (32 bits integer)
+ */
+ public MersenneTwister(int seed) {
+ mt = new int[N];
+ setSeed(seed);
+ }
+
+ /**
+ * Creates a new random number generator using an int array seed.
+ *
+ * @param seed the initial seed (32 bits integers array), if null the seed of the generator will
+ * be related to the current time
+ */
+ public MersenneTwister(int[] seed) {
+ mt = new int[N];
+ setSeed(seed);
+ }
+
+ /**
+ * Creates a new random number generator using a single long seed.
+ *
+ * @param seed the initial seed (64 bits integer)
+ */
+ public MersenneTwister(long seed) {
+ mt = new int[N];
+ setSeed(seed);
+ }
+
+ /**
+ * Reinitialize the generator as if just built with the given int seed.
+ *
+ * <p>The state of the generator is exactly the same as a new generator built with the same
+ * seed.
+ *
+ * @param seed the initial seed (32 bits integer)
+ */
+ @Override
+ public void setSeed(int seed) {
+ // we use a long masked by 0xffffffffL as a poor man unsigned int
+ long longMT = seed;
+ // NB: unlike original C code, we are working with java longs, the cast below makes masking
+ // unnecessary
+ mt[0] = (int) longMT;
+ for (mti = 1; mti < N; ++mti) {
+ // See Knuth TAOCP Vol2. 3rd Ed. P.106 for multiplier.
+ // initializer from the 2002-01-09 C version by Makoto Matsumoto
+ longMT = (1812433253l * (longMT ^ (longMT >> 30)) + mti) & 0xffffffffL;
+ mt[mti] = (int) longMT;
+ }
+
+ clear(); // Clear normal deviate cache
+ }
+
+ /**
+ * Reinitialize the generator as if just built with the given int array seed.
+ *
+ * <p>The state of the generator is exactly the same as a new generator built with the same
+ * seed.
+ *
+ * @param seed the initial seed (32 bits integers array), if null the seed of the generator will
+ * be the current system time plus the system identity hash code of this instance
+ */
+ @Override
+ public void setSeed(int[] seed) {
+
+ if (seed == null) {
+ setSeed(System.currentTimeMillis() + System.identityHashCode(this));
+ return;
+ }
+
+ setSeed(19650218);
+ int i = 1;
+ int j = 0;
+
+ for (int k = FastMath.max(N, seed.length); k != 0; k--) {
+ long l0 = (mt[i] & 0x7fffffffl) | ((mt[i] < 0) ? 0x80000000l : 0x0l);
+ long l1 = (mt[i - 1] & 0x7fffffffl) | ((mt[i - 1] < 0) ? 0x80000000l : 0x0l);
+ long l = (l0 ^ ((l1 ^ (l1 >> 30)) * 1664525l)) + seed[j] + j; // non linear
+ mt[i] = (int) (l & 0xffffffffl);
+ i++;
+ j++;
+ if (i >= N) {
+ mt[0] = mt[N - 1];
+ i = 1;
+ }
+ if (j >= seed.length) {
+ j = 0;
+ }
+ }
+
+ for (int k = N - 1; k != 0; k--) {
+ long l0 = (mt[i] & 0x7fffffffl) | ((mt[i] < 0) ? 0x80000000l : 0x0l);
+ long l1 = (mt[i - 1] & 0x7fffffffl) | ((mt[i - 1] < 0) ? 0x80000000l : 0x0l);
+ long l = (l0 ^ ((l1 ^ (l1 >> 30)) * 1566083941l)) - i; // non linear
+ mt[i] = (int) (l & 0xffffffffL);
+ i++;
+ if (i >= N) {
+ mt[0] = mt[N - 1];
+ i = 1;
+ }
+ }
+
+ mt[0] = 0x80000000; // MSB is 1; assuring non-zero initial array
+
+ clear(); // Clear normal deviate cache
+ }
+
+ /**
+ * Reinitialize the generator as if just built with the given long seed.
+ *
+ * <p>The state of the generator is exactly the same as a new generator built with the same
+ * seed.
+ *
+ * @param seed the initial seed (64 bits integer)
+ */
+ @Override
+ public void setSeed(long seed) {
+ setSeed(new int[] {(int) (seed >>> 32), (int) (seed & 0xffffffffl)});
+ }
+
+ /**
+ * Generate next pseudorandom number.
+ *
+ * <p>This method is the core generation algorithm. It is used by all the public generation
+ * methods for the various primitive types {@link #nextBoolean()}, {@link #nextBytes(byte[])},
+ * {@link #nextDouble()}, {@link #nextFloat()}, {@link #nextGaussian()}, {@link #nextInt()},
+ * {@link #next(int)} and {@link #nextLong()}.
+ *
+ * @param bits number of random bits to produce
+ * @return random bits generated
+ */
+ @Override
+ protected int next(int bits) {
+
+ int y;
+
+ if (mti >= N) { // generate N words at one time
+ int mtNext = mt[0];
+ for (int k = 0; k < N - M; ++k) {
+ int mtCurr = mtNext;
+ mtNext = mt[k + 1];
+ y = (mtCurr & 0x80000000) | (mtNext & 0x7fffffff);
+ mt[k] = mt[k + M] ^ (y >>> 1) ^ MAG01[y & 0x1];
+ }
+ for (int k = N - M; k < N - 1; ++k) {
+ int mtCurr = mtNext;
+ mtNext = mt[k + 1];
+ y = (mtCurr & 0x80000000) | (mtNext & 0x7fffffff);
+ mt[k] = mt[k + (M - N)] ^ (y >>> 1) ^ MAG01[y & 0x1];
+ }
+ y = (mtNext & 0x80000000) | (mt[0] & 0x7fffffff);
+ mt[N - 1] = mt[M - 1] ^ (y >>> 1) ^ MAG01[y & 0x1];
+
+ mti = 0;
+ }
+
+ y = mt[mti++];
+
+ // tempering
+ y ^= y >>> 11;
+ y ^= (y << 7) & 0x9d2c5680;
+ y ^= (y << 15) & 0xefc60000;
+ y ^= y >>> 18;
+
+ return y >>> (32 - bits);
+ }
+}
diff --git a/src/main/java/org/apache/commons/math3/random/NormalizedRandomGenerator.java b/src/main/java/org/apache/commons/math3/random/NormalizedRandomGenerator.java
new file mode 100644
index 0000000..2abcc72
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/random/NormalizedRandomGenerator.java
@@ -0,0 +1,38 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math3.random;
+
+/**
+ * This interface represent a normalized random generator for scalars. Normalized generator provide
+ * null mean and unit standard deviation scalars.
+ *
+ * @since 1.2
+ */
+public interface NormalizedRandomGenerator {
+
+ /**
+ * Generate a random scalar with null mean and unit standard deviation.
+ *
+ * <p>This method does <strong>not</strong> specify the shape of the distribution, it is the
+ * implementing class that provides it. The only contract here is to generate numbers with null
+ * mean and unit standard deviation.
+ *
+ * @return a random scalar with null mean and unit standard deviation
+ */
+ double nextNormalizedDouble();
+}
diff --git a/src/main/java/org/apache/commons/math3/random/RandomAdaptor.java b/src/main/java/org/apache/commons/math3/random/RandomAdaptor.java
new file mode 100644
index 0000000..e7030d0
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/random/RandomAdaptor.java
@@ -0,0 +1,182 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math3.random;
+
+import java.util.Random;
+
+/**
+ * Extension of <code>java.util.Random</code> wrapping a {@link RandomGenerator}.
+ *
+ * @since 1.1
+ */
+public class RandomAdaptor extends Random implements RandomGenerator {
+
+ /** Serializable version identifier. */
+ private static final long serialVersionUID = 2306581345647615033L;
+
+ /** Wrapped randomGenerator instance */
+ private final RandomGenerator randomGenerator;
+
+ /** Prevent instantiation without a generator argument */
+ @SuppressWarnings("unused")
+ private RandomAdaptor() {
+ randomGenerator = null;
+ }
+
+ /**
+ * Construct a RandomAdaptor wrapping the supplied RandomGenerator.
+ *
+ * @param randomGenerator the wrapped generator
+ */
+ public RandomAdaptor(RandomGenerator randomGenerator) {
+ this.randomGenerator = randomGenerator;
+ }
+
+ /**
+ * Factory method to create a <code>Random</code> using the supplied <code>RandomGenerator
+ * </code>.
+ *
+ * @param randomGenerator wrapped RandomGenerator instance
+ * @return a Random instance wrapping the RandomGenerator
+ */
+ public static Random createAdaptor(RandomGenerator randomGenerator) {
+ return new RandomAdaptor(randomGenerator);
+ }
+
+ /**
+ * Returns the next pseudorandom, uniformly distributed <code>boolean</code> value from this
+ * random number generator's sequence.
+ *
+ * @return the next pseudorandom, uniformly distributed <code>boolean</code> value from this
+ * random number generator's sequence
+ */
+ @Override
+ public boolean nextBoolean() {
+ return randomGenerator.nextBoolean();
+ }
+
+ /**
+ * Generates random bytes and places them into a user-supplied byte array. The number of random
+ * bytes produced is equal to the length of the byte array.
+ *
+ * @param bytes the non-null byte array in which to put the random bytes
+ */
+ @Override
+ public void nextBytes(byte[] bytes) {
+ randomGenerator.nextBytes(bytes);
+ }
+
+ /**
+ * Returns the next pseudorandom, uniformly distributed <code>double</code> value between <code>
+ * 0.0</code> and <code>1.0</code> from this random number generator's sequence.
+ *
+ * @return the next pseudorandom, uniformly distributed <code>double</code> value between <code>
+ * 0.0</code> and <code>1.0</code> from this random number generator's sequence
+ */
+ @Override
+ public double nextDouble() {
+ return randomGenerator.nextDouble();
+ }
+
+ /**
+ * Returns the next pseudorandom, uniformly distributed <code>float</code> value between <code>
+ * 0.0</code> and <code>1.0</code> from this random number generator's sequence.
+ *
+ * @return the next pseudorandom, uniformly distributed <code>float</code> value between <code>
+ * 0.0</code> and <code>1.0</code> from this random number generator's sequence
+ */
+ @Override
+ public float nextFloat() {
+ return randomGenerator.nextFloat();
+ }
+
+ /**
+ * Returns the next pseudorandom, Gaussian ("normally") distributed <code>double</code> value
+ * with mean <code>0.0</code> and standard deviation <code>1.0</code> from this random number
+ * generator's sequence.
+ *
+ * @return the next pseudorandom, Gaussian ("normally") distributed <code>double</code> value
+ * with mean <code>0.0</code> and standard deviation <code>1.0</code> from this random
+ * number generator's sequence
+ */
+ @Override
+ public double nextGaussian() {
+ return randomGenerator.nextGaussian();
+ }
+
+ /**
+ * Returns the next pseudorandom, uniformly distributed <code>int</code> value from this random
+ * number generator's sequence. All 2<font size="-1"><sup>32</sup></font> possible {@code int}
+ * values should be produced with (approximately) equal probability.
+ *
+ * @return the next pseudorandom, uniformly distributed <code>int</code> value from this random
+ * number generator's sequence
+ */
+ @Override
+ public int nextInt() {
+ return randomGenerator.nextInt();
+ }
+
+ /**
+ * Returns a pseudorandom, uniformly distributed {@code int} value between 0 (inclusive) and the
+ * specified value (exclusive), drawn from this random number generator's sequence.
+ *
+ * @param n the bound on the random number to be returned. Must be positive.
+ * @return a pseudorandom, uniformly distributed {@code int} value between 0 (inclusive) and n
+ * (exclusive).
+ * @throws IllegalArgumentException if n is not positive.
+ */
+ @Override
+ public int nextInt(int n) {
+ return randomGenerator.nextInt(n);
+ }
+
+ /**
+ * Returns the next pseudorandom, uniformly distributed <code>long</code> value from this random
+ * number generator's sequence. All 2<font size="-1"><sup>64</sup></font> possible {@code long}
+ * values should be produced with (approximately) equal probability.
+ *
+ * @return the next pseudorandom, uniformly distributed <code>long</code> value from this random
+ * number generator's sequence
+ */
+ @Override
+ public long nextLong() {
+ return randomGenerator.nextLong();
+ }
+
+ /** {@inheritDoc} */
+ public void setSeed(int seed) {
+ if (randomGenerator != null) { // required to avoid NPE in constructor
+ randomGenerator.setSeed(seed);
+ }
+ }
+
+ /** {@inheritDoc} */
+ public void setSeed(int[] seed) {
+ if (randomGenerator != null) { // required to avoid NPE in constructor
+ randomGenerator.setSeed(seed);
+ }
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void setSeed(long seed) {
+ if (randomGenerator != null) { // required to avoid NPE in constructor
+ randomGenerator.setSeed(seed);
+ }
+ }
+}
diff --git a/src/main/java/org/apache/commons/math3/random/RandomData.java b/src/main/java/org/apache/commons/math3/random/RandomData.java
new file mode 100644
index 0000000..98662e3
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/random/RandomData.java
@@ -0,0 +1,245 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math3.random;
+
+import org.apache.commons.math3.exception.NotANumberException;
+import org.apache.commons.math3.exception.NotFiniteNumberException;
+import org.apache.commons.math3.exception.NotStrictlyPositiveException;
+import org.apache.commons.math3.exception.NumberIsTooLargeException;
+
+import java.util.Collection;
+
+/**
+ * Random data generation utilities.
+ *
+ * @deprecated to be removed in 4.0. Use {@link RandomDataGenerator} directly
+ */
+@Deprecated
+public interface RandomData {
+ /**
+ * Generates a random string of hex characters of length {@code len}.
+ *
+ * <p>The generated string will be random, but not cryptographically secure. To generate
+ * cryptographically secure strings, use {@link #nextSecureHexString(int)}.
+ *
+ * @param len the length of the string to be generated
+ * @return a random string of hex characters of length {@code len}
+ * @throws NotStrictlyPositiveException if {@code len <= 0}
+ */
+ String nextHexString(int len) throws NotStrictlyPositiveException;
+
+ /**
+ * Generates a uniformly distributed random integer between {@code lower} and {@code upper}
+ * (endpoints included).
+ *
+ * <p>The generated integer will be random, but not cryptographically secure. To generate
+ * cryptographically secure integer sequences, use {@link #nextSecureInt(int, int)}.
+ *
+ * @param lower lower bound for generated integer
+ * @param upper upper bound for generated integer
+ * @return a random integer greater than or equal to {@code lower} and less than or equal to
+ * {@code upper}
+ * @throws NumberIsTooLargeException if {@code lower >= upper}
+ */
+ int nextInt(int lower, int upper) throws NumberIsTooLargeException;
+
+ /**
+ * Generates a uniformly distributed random long integer between {@code lower} and {@code upper}
+ * (endpoints included).
+ *
+ * <p>The generated long integer values will be random, but not cryptographically secure. To
+ * generate cryptographically secure sequences of longs, use {@link #nextSecureLong(long,
+ * long)}.
+ *
+ * @param lower lower bound for generated long integer
+ * @param upper upper bound for generated long integer
+ * @return a random long integer greater than or equal to {@code lower} and less than or equal
+ * to {@code upper}
+ * @throws NumberIsTooLargeException if {@code lower >= upper}
+ */
+ long nextLong(long lower, long upper) throws NumberIsTooLargeException;
+
+ /**
+ * Generates a random string of hex characters from a secure random sequence.
+ *
+ * <p>If cryptographic security is not required, use {@link #nextHexString(int)}.
+ *
+ * @param len the length of the string to be generated
+ * @return a random string of hex characters of length {@code len}
+ * @throws NotStrictlyPositiveException if {@code len <= 0}
+ */
+ String nextSecureHexString(int len) throws NotStrictlyPositiveException;
+
+ /**
+ * Generates a uniformly distributed random integer between {@code lower} and {@code upper}
+ * (endpoints included) from a secure random sequence.
+ *
+ * <p>Sequences of integers generated using this method will be cryptographically secure. If
+ * cryptographic security is not required, {@link #nextInt(int, int)} should be used instead of
+ * this method.
+ *
+ * <p><strong>Definition</strong>: <a
+ * href="http://en.wikipedia.org/wiki/Cryptographically_secure_pseudo-random_number_generator">
+ * Secure Random Sequence</a>
+ *
+ * @param lower lower bound for generated integer
+ * @param upper upper bound for generated integer
+ * @return a random integer greater than or equal to {@code lower} and less than or equal to
+ * {@code upper}.
+ * @throws NumberIsTooLargeException if {@code lower >= upper}.
+ */
+ int nextSecureInt(int lower, int upper) throws NumberIsTooLargeException;
+
+ /**
+ * Generates a uniformly distributed random long integer between {@code lower} and {@code upper}
+ * (endpoints included) from a secure random sequence.
+ *
+ * <p>Sequences of long values generated using this method will be cryptographically secure. If
+ * cryptographic security is not required, {@link #nextLong(long, long)} should be used instead
+ * of this method.
+ *
+ * <p><strong>Definition</strong>: <a
+ * href="http://en.wikipedia.org/wiki/Cryptographically_secure_pseudo-random_number_generator">
+ * Secure Random Sequence</a>
+ *
+ * @param lower lower bound for generated integer
+ * @param upper upper bound for generated integer
+ * @return a random long integer greater than or equal to {@code lower} and less than or equal
+ * to {@code upper}.
+ * @throws NumberIsTooLargeException if {@code lower >= upper}.
+ */
+ long nextSecureLong(long lower, long upper) throws NumberIsTooLargeException;
+
+ /**
+ * Generates a random value from the Poisson distribution with the given mean.
+ *
+ * <p><strong>Definition</strong>: <a
+ * href="http://www.itl.nist.gov/div898/handbook/eda/section3/eda366j.htm">Poisson
+ * Distribution</a>
+ *
+ * @param mean the mean of the Poisson distribution
+ * @return a random value following the specified Poisson distribution
+ * @throws NotStrictlyPositiveException if {@code mean <= 0}.
+ */
+ long nextPoisson(double mean) throws NotStrictlyPositiveException;
+
+ /**
+ * Generates a random value from the Normal (or Gaussian) distribution with specified mean and
+ * standard deviation.
+ *
+ * <p><strong>Definition</strong>: <a
+ * href="http://www.itl.nist.gov/div898/handbook/eda/section3/eda3661.htm">Normal
+ * Distribution</a>
+ *
+ * @param mu the mean of the distribution
+ * @param sigma the standard deviation of the distribution
+ * @return a random value following the specified Gaussian distribution
+ * @throws NotStrictlyPositiveException if {@code sigma <= 0}.
+ */
+ double nextGaussian(double mu, double sigma) throws NotStrictlyPositiveException;
+
+ /**
+ * Generates a random value from the exponential distribution with specified mean.
+ *
+ * <p><strong>Definition</strong>: <a
+ * href="http://www.itl.nist.gov/div898/handbook/eda/section3/eda3667.htm">Exponential
+ * Distribution</a>
+ *
+ * @param mean the mean of the distribution
+ * @return a random value following the specified exponential distribution
+ * @throws NotStrictlyPositiveException if {@code mean <= 0}.
+ */
+ double nextExponential(double mean) throws NotStrictlyPositiveException;
+
+ /**
+ * Generates a uniformly distributed random value from the open interval {@code (lower, upper)}
+ * (i.e., endpoints excluded).
+ *
+ * <p><strong>Definition</strong>: <a
+ * href="http://www.itl.nist.gov/div898/handbook/eda/section3/eda3662.htm">Uniform
+ * Distribution</a> {@code lower} and {@code upper - lower} are the <a href =
+ * "http://www.itl.nist.gov/div898/handbook/eda/section3/eda364.htm"> location and scale
+ * parameters</a>, respectively.
+ *
+ * @param lower the exclusive lower bound of the support
+ * @param upper the exclusive upper bound of the support
+ * @return a uniformly distributed random value between lower and upper (exclusive)
+ * @throws NumberIsTooLargeException if {@code lower >= upper}
+ * @throws NotFiniteNumberException if one of the bounds is infinite
+ * @throws NotANumberException if one of the bounds is NaN
+ */
+ double nextUniform(double lower, double upper)
+ throws NumberIsTooLargeException, NotFiniteNumberException, NotANumberException;
+
+ /**
+ * Generates a uniformly distributed random value from the interval {@code (lower, upper)} or
+ * the interval {@code [lower, upper)}. The lower bound is thus optionally included, while the
+ * upper bound is always excluded.
+ *
+ * <p><strong>Definition</strong>: <a
+ * href="http://www.itl.nist.gov/div898/handbook/eda/section3/eda3662.htm">Uniform
+ * Distribution</a> {@code lower} and {@code upper - lower} are the <a href =
+ * "http://www.itl.nist.gov/div898/handbook/eda/section3/eda364.htm"> location and scale
+ * parameters</a>, respectively.
+ *
+ * @param lower the lower bound of the support
+ * @param upper the exclusive upper bound of the support
+ * @param lowerInclusive {@code true} if the lower bound is inclusive
+ * @return uniformly distributed random value in the {@code (lower, upper)} interval, if {@code
+ * lowerInclusive} is {@code false}, or in the {@code [lower, upper)} interval, if {@code
+ * lowerInclusive} is {@code true}
+ * @throws NumberIsTooLargeException if {@code lower >= upper}
+ * @throws NotFiniteNumberException if one of the bounds is infinite
+ * @throws NotANumberException if one of the bounds is NaN
+ */
+ double nextUniform(double lower, double upper, boolean lowerInclusive)
+ throws NumberIsTooLargeException, NotFiniteNumberException, NotANumberException;
+
+ /**
+ * Generates an integer array of length {@code k} whose entries are selected randomly, without
+ * repetition, from the integers {@code 0, ..., n - 1} (inclusive).
+ *
+ * <p>Generated arrays represent permutations of {@code n} taken {@code k} at a time.
+ *
+ * @param n the domain of the permutation
+ * @param k the size of the permutation
+ * @return a random {@code k}-permutation of {@code n}, as an array of integers
+ * @throws NumberIsTooLargeException if {@code k > n}.
+ * @throws NotStrictlyPositiveException if {@code k <= 0}.
+ */
+ int[] nextPermutation(int n, int k)
+ throws NumberIsTooLargeException, NotStrictlyPositiveException;
+
+ /**
+ * Returns an array of {@code k} objects selected randomly from the Collection {@code c}.
+ *
+ * <p>Sampling from {@code c} is without replacement; but if {@code c} contains identical
+ * objects, the sample may include repeats. If all elements of {@code c} are distinct, the
+ * resulting object array represents a <a
+ * href="http://rkb.home.cern.ch/rkb/AN16pp/node250.html#SECTION0002500000000000000000">Simple
+ * Random Sample</a> of size {@code k} from the elements of {@code c}.
+ *
+ * @param c the collection to be sampled
+ * @param k the size of the sample
+ * @return a random sample of {@code k} elements from {@code c}
+ * @throws NumberIsTooLargeException if {@code k > c.size()}.
+ * @throws NotStrictlyPositiveException if {@code k <= 0}.
+ */
+ Object[] nextSample(Collection<?> c, int k)
+ throws NumberIsTooLargeException, NotStrictlyPositiveException;
+}
diff --git a/src/main/java/org/apache/commons/math3/random/RandomDataGenerator.java b/src/main/java/org/apache/commons/math3/random/RandomDataGenerator.java
new file mode 100644
index 0000000..5d48580
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/random/RandomDataGenerator.java
@@ -0,0 +1,780 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math3.random;
+
+import org.apache.commons.math3.distribution.BetaDistribution;
+import org.apache.commons.math3.distribution.BinomialDistribution;
+import org.apache.commons.math3.distribution.CauchyDistribution;
+import org.apache.commons.math3.distribution.ChiSquaredDistribution;
+import org.apache.commons.math3.distribution.ExponentialDistribution;
+import org.apache.commons.math3.distribution.FDistribution;
+import org.apache.commons.math3.distribution.GammaDistribution;
+import org.apache.commons.math3.distribution.HypergeometricDistribution;
+import org.apache.commons.math3.distribution.PascalDistribution;
+import org.apache.commons.math3.distribution.PoissonDistribution;
+import org.apache.commons.math3.distribution.TDistribution;
+import org.apache.commons.math3.distribution.UniformIntegerDistribution;
+import org.apache.commons.math3.distribution.WeibullDistribution;
+import org.apache.commons.math3.distribution.ZipfDistribution;
+import org.apache.commons.math3.exception.MathInternalError;
+import org.apache.commons.math3.exception.NotANumberException;
+import org.apache.commons.math3.exception.NotFiniteNumberException;
+import org.apache.commons.math3.exception.NotPositiveException;
+import org.apache.commons.math3.exception.NotStrictlyPositiveException;
+import org.apache.commons.math3.exception.NumberIsTooLargeException;
+import org.apache.commons.math3.exception.OutOfRangeException;
+import org.apache.commons.math3.exception.util.LocalizedFormats;
+import org.apache.commons.math3.util.MathArrays;
+
+import java.io.Serializable;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+import java.security.NoSuchProviderException;
+import java.security.SecureRandom;
+import java.util.Collection;
+
+/**
+ * Implements the {@link RandomData} interface using a {@link RandomGenerator} instance to generate
+ * non-secure data and a {@link java.security.SecureRandom} instance to provide data for the <code>
+ * nextSecureXxx</code> methods. If no <code>RandomGenerator</code> is provided in the constructor,
+ * the default is to use a {@link Well19937c} generator. To plug in a different implementation,
+ * either implement <code>RandomGenerator</code> directly or extend {@link AbstractRandomGenerator}.
+ *
+ * <p>Supports reseeding the underlying pseudo-random number generator (PRNG). The <code>
+ * SecurityProvider</code> and <code>Algorithm</code> used by the <code>SecureRandom</code> instance
+ * can also be reset.
+ *
+ * <p>For details on the default PRNGs, see {@link java.util.Random} and {@link
+ * java.security.SecureRandom}.
+ *
+ * <p><strong>Usage Notes</strong>:
+ *
+ * <ul>
+ * <li>Instance variables are used to maintain <code>RandomGenerator</code> and <code>SecureRandom
+ * </code> instances used in data generation. Therefore, to generate a random sequence of
+ * values or strings, you should use just <strong>one</strong> <code>RandomDataImpl</code>
+ * instance repeatedly.
+ * <li>The "secure" methods are *much* slower. These should be used only when a cryptographically
+ * secure random sequence is required. A secure random sequence is a sequence of pseudo-random
+ * values which, in addition to being well-dispersed (so no subsequence of values is an any
+ * more likely than other subsequence of the the same length), also has the additional
+ * property that knowledge of values generated up to any point in the sequence does not make
+ * it any easier to predict subsequent values.
+ * <li>When a new <code>RandomDataImpl</code> is created, the underlying random number generators
+ * are <strong>not</strong> initialized. If you do not explicitly seed the default non-secure
+ * generator, it is seeded with the current time in milliseconds plus the system identity hash
+ * code on first use. The same holds for the secure generator. If you provide a <code>
+ * RandomGenerator</code> to the constructor, however, this generator is not reseeded by the
+ * constructor nor is it reseeded on first use.
+ * <li>The <code>reSeed</code> and <code>reSeedSecure</code> methods delegate to the corresponding
+ * methods on the underlying <code>RandomGenerator</code> and <code>SecureRandom</code>
+ * instances. Therefore, <code>reSeed(long)</code> fully resets the initial state of the
+ * non-secure random number generator (so that reseeding with a specific value always results
+ * in the same subsequent random sequence); whereas reSeedSecure(long) does
+ * <strong>not</strong> reinitialize the secure random number generator (so secure sequences
+ * started with calls to reseedSecure(long) won't be identical).
+ * <li>This implementation is not synchronized. The underlying <code>RandomGenerator</code> or
+ * <code>SecureRandom</code> instances are not protected by synchronization and are not
+ * guaranteed to be thread-safe. Therefore, if an instance of this class is concurrently
+ * utilized by multiple threads, it is the responsibility of client code to synchronize access
+ * to seeding and data generation methods.
+ * </ul>
+ *
+ * @since 3.1
+ */
+public class RandomDataGenerator implements RandomData, Serializable {
+
+ /** Serializable version identifier */
+ private static final long serialVersionUID = -626730818244969716L;
+
+ /** underlying random number generator */
+ private RandomGenerator rand = null;
+
+ /** underlying secure random number generator */
+ private RandomGenerator secRand = null;
+
+ /**
+ * Construct a RandomDataGenerator, using a default random generator as the source of
+ * randomness.
+ *
+ * <p>The default generator is a {@link Well19937c} seeded with {@code
+ * System.currentTimeMillis() + System.identityHashCode(this))}. The generator is initialized
+ * and seeded on first use.
+ */
+ public RandomDataGenerator() {}
+
+ /**
+ * Construct a RandomDataGenerator using the supplied {@link RandomGenerator} as the source of
+ * (non-secure) random data.
+ *
+ * @param rand the source of (non-secure) random data (may be null, resulting in the default
+ * generator)
+ */
+ public RandomDataGenerator(RandomGenerator rand) {
+ this.rand = rand;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * <p><strong>Algorithm Description:</strong> hex strings are generated using a 2-step process.
+ *
+ * <ol>
+ * <li>{@code len / 2 + 1} binary bytes are generated using the underlying Random
+ * <li>Each binary byte is translated into 2 hex digits
+ * </ol>
+ *
+ * @param len the desired string length.
+ * @return the random string.
+ * @throws NotStrictlyPositiveException if {@code len <= 0}.
+ */
+ public String nextHexString(int len) throws NotStrictlyPositiveException {
+ if (len <= 0) {
+ throw new NotStrictlyPositiveException(LocalizedFormats.LENGTH, len);
+ }
+
+ // Get a random number generator
+ RandomGenerator ran = getRandomGenerator();
+
+ // Initialize output buffer
+ StringBuilder outBuffer = new StringBuilder();
+
+ // Get int(len/2)+1 random bytes
+ byte[] randomBytes = new byte[(len / 2) + 1];
+ ran.nextBytes(randomBytes);
+
+ // Convert each byte to 2 hex digits
+ for (int i = 0; i < randomBytes.length; i++) {
+ Integer c = Integer.valueOf(randomBytes[i]);
+
+ /*
+ * Add 128 to byte value to make interval 0-255 before doing hex
+ * conversion. This guarantees <= 2 hex digits from toHexString()
+ * toHexString would otherwise add 2^32 to negative arguments.
+ */
+ String hex = Integer.toHexString(c.intValue() + 128);
+
+ // Make sure we add 2 hex digits for each byte
+ if (hex.length() == 1) {
+ hex = "0" + hex;
+ }
+ outBuffer.append(hex);
+ }
+ return outBuffer.toString().substring(0, len);
+ }
+
+ /** {@inheritDoc} */
+ public int nextInt(final int lower, final int upper) throws NumberIsTooLargeException {
+ return new UniformIntegerDistribution(getRandomGenerator(), lower, upper).sample();
+ }
+
+ /** {@inheritDoc} */
+ public long nextLong(final long lower, final long upper) throws NumberIsTooLargeException {
+ if (lower >= upper) {
+ throw new NumberIsTooLargeException(
+ LocalizedFormats.LOWER_BOUND_NOT_BELOW_UPPER_BOUND, lower, upper, false);
+ }
+ final long max = (upper - lower) + 1;
+ if (max <= 0) {
+ // the range is too wide to fit in a positive long (larger than 2^63); as it covers
+ // more than half the long range, we use directly a simple rejection method
+ final RandomGenerator rng = getRandomGenerator();
+ while (true) {
+ final long r = rng.nextLong();
+ if (r >= lower && r <= upper) {
+ return r;
+ }
+ }
+ } else if (max < Integer.MAX_VALUE) {
+ // we can shift the range and generate directly a positive int
+ return lower + getRandomGenerator().nextInt((int) max);
+ } else {
+ // we can shift the range and generate directly a positive long
+ return lower + nextLong(getRandomGenerator(), max);
+ }
+ }
+
+ /**
+ * Returns a pseudorandom, uniformly distributed {@code long} value between 0 (inclusive) and
+ * the specified value (exclusive), drawn from this random number generator's sequence.
+ *
+ * @param rng random generator to use
+ * @param n the bound on the random number to be returned. Must be positive.
+ * @return a pseudorandom, uniformly distributed {@code long} value between 0 (inclusive) and n
+ * (exclusive).
+ * @throws IllegalArgumentException if n is not positive.
+ */
+ private static long nextLong(final RandomGenerator rng, final long n)
+ throws IllegalArgumentException {
+ if (n > 0) {
+ final byte[] byteArray = new byte[8];
+ long bits;
+ long val;
+ do {
+ rng.nextBytes(byteArray);
+ bits = 0;
+ for (final byte b : byteArray) {
+ bits = (bits << 8) | (((long) b) & 0xffL);
+ }
+ bits &= 0x7fffffffffffffffL;
+ val = bits % n;
+ } while (bits - val + (n - 1) < 0);
+ return val;
+ }
+ throw new NotStrictlyPositiveException(n);
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * <p><strong>Algorithm Description:</strong> hex strings are generated in 40-byte segments
+ * using a 3-step process.
+ *
+ * <ol>
+ * <li>20 random bytes are generated using the underlying <code>SecureRandom</code>.
+ * <li>SHA-1 hash is applied to yield a 20-byte binary digest.
+ * <li>Each byte of the binary digest is converted to 2 hex digits.
+ * </ol>
+ *
+ * @throws NotStrictlyPositiveException if {@code len <= 0}
+ */
+ public String nextSecureHexString(int len) throws NotStrictlyPositiveException {
+ if (len <= 0) {
+ throw new NotStrictlyPositiveException(LocalizedFormats.LENGTH, len);
+ }
+
+ // Get SecureRandom and setup Digest provider
+ final RandomGenerator secRan = getSecRan();
+ MessageDigest alg = null;
+ try {
+ alg = MessageDigest.getInstance("SHA-1");
+ } catch (NoSuchAlgorithmException ex) {
+ // this should never happen
+ throw new MathInternalError(ex);
+ }
+ alg.reset();
+
+ // Compute number of iterations required (40 bytes each)
+ int numIter = (len / 40) + 1;
+
+ StringBuilder outBuffer = new StringBuilder();
+ for (int iter = 1; iter < numIter + 1; iter++) {
+ byte[] randomBytes = new byte[40];
+ secRan.nextBytes(randomBytes);
+ alg.update(randomBytes);
+
+ // Compute hash -- will create 20-byte binary hash
+ byte[] hash = alg.digest();
+
+ // Loop over the hash, converting each byte to 2 hex digits
+ for (int i = 0; i < hash.length; i++) {
+ Integer c = Integer.valueOf(hash[i]);
+
+ /*
+ * Add 128 to byte value to make interval 0-255 This guarantees
+ * <= 2 hex digits from toHexString() toHexString would
+ * otherwise add 2^32 to negative arguments
+ */
+ String hex = Integer.toHexString(c.intValue() + 128);
+
+ // Keep strings uniform length -- guarantees 40 bytes
+ if (hex.length() == 1) {
+ hex = "0" + hex;
+ }
+ outBuffer.append(hex);
+ }
+ }
+ return outBuffer.toString().substring(0, len);
+ }
+
+ /** {@inheritDoc} */
+ public int nextSecureInt(final int lower, final int upper) throws NumberIsTooLargeException {
+ return new UniformIntegerDistribution(getSecRan(), lower, upper).sample();
+ }
+
+ /** {@inheritDoc} */
+ public long nextSecureLong(final long lower, final long upper)
+ throws NumberIsTooLargeException {
+ if (lower >= upper) {
+ throw new NumberIsTooLargeException(
+ LocalizedFormats.LOWER_BOUND_NOT_BELOW_UPPER_BOUND, lower, upper, false);
+ }
+ final RandomGenerator rng = getSecRan();
+ final long max = (upper - lower) + 1;
+ if (max <= 0) {
+ // the range is too wide to fit in a positive long (larger than 2^63); as it covers
+ // more than half the long range, we use directly a simple rejection method
+ while (true) {
+ final long r = rng.nextLong();
+ if (r >= lower && r <= upper) {
+ return r;
+ }
+ }
+ } else if (max < Integer.MAX_VALUE) {
+ // we can shift the range and generate directly a positive int
+ return lower + rng.nextInt((int) max);
+ } else {
+ // we can shift the range and generate directly a positive long
+ return lower + nextLong(rng, max);
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * <p><strong>Algorithm Description</strong>:
+ *
+ * <ul>
+ * <li>For small means, uses simulation of a Poisson process using Uniform deviates, as
+ * described <a href="http://irmi.epfl.ch/cmos/Pmmi/interactive/rng7.htm">here.</a> The
+ * Poisson process (and hence value returned) is bounded by 1000 * mean.
+ * <li>For large means, uses the rejection algorithm described in <br>
+ * Devroye, Luc. (1981).<i>The Computer Generation of Poisson Random Variables</i>
+ * <strong>Computing</strong> vol. 26 pp. 197-207.
+ * </ul>
+ *
+ * @throws NotStrictlyPositiveException if {@code len <= 0}
+ */
+ public long nextPoisson(double mean) throws NotStrictlyPositiveException {
+ return new PoissonDistribution(
+ getRandomGenerator(),
+ mean,
+ PoissonDistribution.DEFAULT_EPSILON,
+ PoissonDistribution.DEFAULT_MAX_ITERATIONS)
+ .sample();
+ }
+
+ /** {@inheritDoc} */
+ public double nextGaussian(double mu, double sigma) throws NotStrictlyPositiveException {
+ if (sigma <= 0) {
+ throw new NotStrictlyPositiveException(LocalizedFormats.STANDARD_DEVIATION, sigma);
+ }
+ return sigma * getRandomGenerator().nextGaussian() + mu;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * <p><strong>Algorithm Description</strong>: Uses the Algorithm SA (Ahrens) from p. 876 in:
+ * [1]: Ahrens, J. H. and Dieter, U. (1972). Computer methods for sampling from the exponential
+ * and normal distributions. Communications of the ACM, 15, 873-882.
+ */
+ public double nextExponential(double mean) throws NotStrictlyPositiveException {
+ return new ExponentialDistribution(
+ getRandomGenerator(),
+ mean,
+ ExponentialDistribution.DEFAULT_INVERSE_ABSOLUTE_ACCURACY)
+ .sample();
+ }
+
+ /**
+ * Generates a random value from the {@link
+ * org.apache.commons.math3.distribution.GammaDistribution Gamma Distribution}.
+ *
+ * <p>This implementation uses the following algorithms:
+ *
+ * <p>For 0 < shape < 1: <br>
+ * Ahrens, J. H. and Dieter, U., <i>Computer methods for sampling from gamma, beta, Poisson and
+ * binomial distributions.</i> Computing, 12, 223-246, 1974.
+ *
+ * <p>For shape >= 1: <br>
+ * Marsaglia and Tsang, <i>A Simple Method for Generating Gamma Variables.</i> ACM Transactions
+ * on Mathematical Software, Volume 26 Issue 3, September, 2000.
+ *
+ * @param shape the median of the Gamma distribution
+ * @param scale the scale parameter of the Gamma distribution
+ * @return random value sampled from the Gamma(shape, scale) distribution
+ * @throws NotStrictlyPositiveException if {@code shape <= 0} or {@code scale <= 0}.
+ */
+ public double nextGamma(double shape, double scale) throws NotStrictlyPositiveException {
+ return new GammaDistribution(
+ getRandomGenerator(),
+ shape,
+ scale,
+ GammaDistribution.DEFAULT_INVERSE_ABSOLUTE_ACCURACY)
+ .sample();
+ }
+
+ /**
+ * Generates a random value from the {@link HypergeometricDistribution Hypergeometric
+ * Distribution}.
+ *
+ * @param populationSize the population size of the Hypergeometric distribution
+ * @param numberOfSuccesses number of successes in the population of the Hypergeometric
+ * distribution
+ * @param sampleSize the sample size of the Hypergeometric distribution
+ * @return random value sampled from the Hypergeometric(numberOfSuccesses, sampleSize)
+ * distribution
+ * @throws NumberIsTooLargeException if {@code numberOfSuccesses > populationSize}, or {@code
+ * sampleSize > populationSize}.
+ * @throws NotStrictlyPositiveException if {@code populationSize <= 0}.
+ * @throws NotPositiveException if {@code numberOfSuccesses < 0}.
+ */
+ public int nextHypergeometric(int populationSize, int numberOfSuccesses, int sampleSize)
+ throws NotPositiveException, NotStrictlyPositiveException, NumberIsTooLargeException {
+ return new HypergeometricDistribution(
+ getRandomGenerator(), populationSize, numberOfSuccesses, sampleSize)
+ .sample();
+ }
+
+ /**
+ * Generates a random value from the {@link PascalDistribution Pascal Distribution}.
+ *
+ * @param r the number of successes of the Pascal distribution
+ * @param p the probability of success of the Pascal distribution
+ * @return random value sampled from the Pascal(r, p) distribution
+ * @throws NotStrictlyPositiveException if the number of successes is not positive
+ * @throws OutOfRangeException if the probability of success is not in the range {@code [0, 1]}.
+ */
+ public int nextPascal(int r, double p)
+ throws NotStrictlyPositiveException, OutOfRangeException {
+ return new PascalDistribution(getRandomGenerator(), r, p).sample();
+ }
+
+ /**
+ * Generates a random value from the {@link TDistribution T Distribution}.
+ *
+ * @param df the degrees of freedom of the T distribution
+ * @return random value from the T(df) distribution
+ * @throws NotStrictlyPositiveException if {@code df <= 0}
+ */
+ public double nextT(double df) throws NotStrictlyPositiveException {
+ return new TDistribution(
+ getRandomGenerator(), df, TDistribution.DEFAULT_INVERSE_ABSOLUTE_ACCURACY)
+ .sample();
+ }
+
+ /**
+ * Generates a random value from the {@link WeibullDistribution Weibull Distribution}.
+ *
+ * @param shape the shape parameter of the Weibull distribution
+ * @param scale the scale parameter of the Weibull distribution
+ * @return random value sampled from the Weibull(shape, size) distribution
+ * @throws NotStrictlyPositiveException if {@code shape <= 0} or {@code scale <= 0}.
+ */
+ public double nextWeibull(double shape, double scale) throws NotStrictlyPositiveException {
+ return new WeibullDistribution(
+ getRandomGenerator(),
+ shape,
+ scale,
+ WeibullDistribution.DEFAULT_INVERSE_ABSOLUTE_ACCURACY)
+ .sample();
+ }
+
+ /**
+ * Generates a random value from the {@link ZipfDistribution Zipf Distribution}.
+ *
+ * @param numberOfElements the number of elements of the ZipfDistribution
+ * @param exponent the exponent of the ZipfDistribution
+ * @return random value sampled from the Zipf(numberOfElements, exponent) distribution
+ * @exception NotStrictlyPositiveException if {@code numberOfElements <= 0} or {@code exponent
+ * <= 0}.
+ */
+ public int nextZipf(int numberOfElements, double exponent) throws NotStrictlyPositiveException {
+ return new ZipfDistribution(getRandomGenerator(), numberOfElements, exponent).sample();
+ }
+
+ /**
+ * Generates a random value from the {@link BetaDistribution Beta Distribution}.
+ *
+ * @param alpha first distribution shape parameter
+ * @param beta second distribution shape parameter
+ * @return random value sampled from the beta(alpha, beta) distribution
+ */
+ public double nextBeta(double alpha, double beta) {
+ return new BetaDistribution(
+ getRandomGenerator(),
+ alpha,
+ beta,
+ BetaDistribution.DEFAULT_INVERSE_ABSOLUTE_ACCURACY)
+ .sample();
+ }
+
+ /**
+ * Generates a random value from the {@link BinomialDistribution Binomial Distribution}.
+ *
+ * @param numberOfTrials number of trials of the Binomial distribution
+ * @param probabilityOfSuccess probability of success of the Binomial distribution
+ * @return random value sampled from the Binomial(numberOfTrials, probabilityOfSuccess)
+ * distribution
+ */
+ public int nextBinomial(int numberOfTrials, double probabilityOfSuccess) {
+ return new BinomialDistribution(getRandomGenerator(), numberOfTrials, probabilityOfSuccess)
+ .sample();
+ }
+
+ /**
+ * Generates a random value from the {@link CauchyDistribution Cauchy Distribution}.
+ *
+ * @param median the median of the Cauchy distribution
+ * @param scale the scale parameter of the Cauchy distribution
+ * @return random value sampled from the Cauchy(median, scale) distribution
+ */
+ public double nextCauchy(double median, double scale) {
+ return new CauchyDistribution(
+ getRandomGenerator(),
+ median,
+ scale,
+ CauchyDistribution.DEFAULT_INVERSE_ABSOLUTE_ACCURACY)
+ .sample();
+ }
+
+ /**
+ * Generates a random value from the {@link ChiSquaredDistribution ChiSquare Distribution}.
+ *
+ * @param df the degrees of freedom of the ChiSquare distribution
+ * @return random value sampled from the ChiSquare(df) distribution
+ */
+ public double nextChiSquare(double df) {
+ return new ChiSquaredDistribution(
+ getRandomGenerator(),
+ df,
+ ChiSquaredDistribution.DEFAULT_INVERSE_ABSOLUTE_ACCURACY)
+ .sample();
+ }
+
+ /**
+ * Generates a random value from the {@link FDistribution F Distribution}.
+ *
+ * @param numeratorDf the numerator degrees of freedom of the F distribution
+ * @param denominatorDf the denominator degrees of freedom of the F distribution
+ * @return random value sampled from the F(numeratorDf, denominatorDf) distribution
+ * @throws NotStrictlyPositiveException if {@code numeratorDf <= 0} or {@code denominatorDf <=
+ * 0}.
+ */
+ public double nextF(double numeratorDf, double denominatorDf)
+ throws NotStrictlyPositiveException {
+ return new FDistribution(
+ getRandomGenerator(),
+ numeratorDf,
+ denominatorDf,
+ FDistribution.DEFAULT_INVERSE_ABSOLUTE_ACCURACY)
+ .sample();
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * <p><strong>Algorithm Description</strong>: scales the output of Random.nextDouble(), but
+ * rejects 0 values (i.e., will generate another random double if Random.nextDouble() returns
+ * 0). This is necessary to provide a symmetric output interval (both endpoints excluded).
+ *
+ * @throws NumberIsTooLargeException if {@code lower >= upper}
+ * @throws NotFiniteNumberException if one of the bounds is infinite
+ * @throws NotANumberException if one of the bounds is NaN
+ */
+ public double nextUniform(double lower, double upper)
+ throws NumberIsTooLargeException, NotFiniteNumberException, NotANumberException {
+ return nextUniform(lower, upper, false);
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * <p><strong>Algorithm Description</strong>: if the lower bound is excluded, scales the output
+ * of Random.nextDouble(), but rejects 0 values (i.e., will generate another random double if
+ * Random.nextDouble() returns 0). This is necessary to provide a symmetric output interval
+ * (both endpoints excluded).
+ *
+ * @throws NumberIsTooLargeException if {@code lower >= upper}
+ * @throws NotFiniteNumberException if one of the bounds is infinite
+ * @throws NotANumberException if one of the bounds is NaN
+ */
+ public double nextUniform(double lower, double upper, boolean lowerInclusive)
+ throws NumberIsTooLargeException, NotFiniteNumberException, NotANumberException {
+
+ if (lower >= upper) {
+ throw new NumberIsTooLargeException(
+ LocalizedFormats.LOWER_BOUND_NOT_BELOW_UPPER_BOUND, lower, upper, false);
+ }
+
+ if (Double.isInfinite(lower)) {
+ throw new NotFiniteNumberException(LocalizedFormats.INFINITE_BOUND, lower);
+ }
+ if (Double.isInfinite(upper)) {
+ throw new NotFiniteNumberException(LocalizedFormats.INFINITE_BOUND, upper);
+ }
+
+ if (Double.isNaN(lower) || Double.isNaN(upper)) {
+ throw new NotANumberException();
+ }
+
+ final RandomGenerator generator = getRandomGenerator();
+
+ // ensure nextDouble() isn't 0.0
+ double u = generator.nextDouble();
+ while (!lowerInclusive && u <= 0.0) {
+ u = generator.nextDouble();
+ }
+
+ return u * upper + (1.0 - u) * lower;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * <p>This method calls {@link MathArrays#shuffle(int[],RandomGenerator) MathArrays.shuffle} in
+ * order to create a random shuffle of the set of natural numbers {@code { 0, 1, ..., n - 1 }}.
+ *
+ * @throws NumberIsTooLargeException if {@code k > n}.
+ * @throws NotStrictlyPositiveException if {@code k <= 0}.
+ */
+ public int[] nextPermutation(int n, int k)
+ throws NumberIsTooLargeException, NotStrictlyPositiveException {
+ if (k > n) {
+ throw new NumberIsTooLargeException(LocalizedFormats.PERMUTATION_EXCEEDS_N, k, n, true);
+ }
+ if (k <= 0) {
+ throw new NotStrictlyPositiveException(LocalizedFormats.PERMUTATION_SIZE, k);
+ }
+
+ int[] index = MathArrays.natural(n);
+ MathArrays.shuffle(index, getRandomGenerator());
+
+ // Return a new array containing the first "k" entries of "index".
+ return MathArrays.copyOf(index, k);
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * <p>This method calls {@link #nextPermutation(int,int) nextPermutation(c.size(), k)} in order
+ * to sample the collection.
+ */
+ public Object[] nextSample(Collection<?> c, int k)
+ throws NumberIsTooLargeException, NotStrictlyPositiveException {
+
+ int len = c.size();
+ if (k > len) {
+ throw new NumberIsTooLargeException(
+ LocalizedFormats.SAMPLE_SIZE_EXCEEDS_COLLECTION_SIZE, k, len, true);
+ }
+ if (k <= 0) {
+ throw new NotStrictlyPositiveException(LocalizedFormats.NUMBER_OF_SAMPLES, k);
+ }
+
+ Object[] objects = c.toArray();
+ int[] index = nextPermutation(len, k);
+ Object[] result = new Object[k];
+ for (int i = 0; i < k; i++) {
+ result[i] = objects[index[i]];
+ }
+ return result;
+ }
+
+ /**
+ * Reseeds the random number generator with the supplied seed.
+ *
+ * <p>Will create and initialize if null.
+ *
+ * @param seed the seed value to use
+ */
+ public void reSeed(long seed) {
+ getRandomGenerator().setSeed(seed);
+ }
+
+ /**
+ * Reseeds the secure random number generator with the current time in milliseconds.
+ *
+ * <p>Will create and initialize if null.
+ */
+ public void reSeedSecure() {
+ getSecRan().setSeed(System.currentTimeMillis());
+ }
+
+ /**
+ * Reseeds the secure random number generator with the supplied seed.
+ *
+ * <p>Will create and initialize if null.
+ *
+ * @param seed the seed value to use
+ */
+ public void reSeedSecure(long seed) {
+ getSecRan().setSeed(seed);
+ }
+
+ /**
+ * Reseeds the random number generator with {@code System.currentTimeMillis() +
+ * System.identityHashCode(this))}.
+ */
+ public void reSeed() {
+ getRandomGenerator().setSeed(System.currentTimeMillis() + System.identityHashCode(this));
+ }
+
+ /**
+ * Sets the PRNG algorithm for the underlying SecureRandom instance using the Security Provider
+ * API. The Security Provider API is defined in <a href =
+ * "http://java.sun.com/j2se/1.3/docs/guide/security/CryptoSpec.html#AppA"> Java Cryptography
+ * Architecture API Specification & Reference.</a>
+ *
+ * <p><strong>USAGE NOTE:</strong> This method carries <i>significant</i> overhead and may take
+ * several seconds to execute.
+ *
+ * @param algorithm the name of the PRNG algorithm
+ * @param provider the name of the provider
+ * @throws NoSuchAlgorithmException if the specified algorithm is not available
+ * @throws NoSuchProviderException if the specified provider is not installed
+ */
+ public void setSecureAlgorithm(String algorithm, String provider)
+ throws NoSuchAlgorithmException, NoSuchProviderException {
+ secRand =
+ RandomGeneratorFactory.createRandomGenerator(
+ SecureRandom.getInstance(algorithm, provider));
+ }
+
+ /**
+ * Returns the RandomGenerator used to generate non-secure random data.
+ *
+ * <p>Creates and initializes a default generator if null. Uses a {@link Well19937c} generator
+ * with {@code System.currentTimeMillis() + System.identityHashCode(this))} as the default seed.
+ *
+ * @return the Random used to generate random data
+ * @since 3.2
+ */
+ public RandomGenerator getRandomGenerator() {
+ if (rand == null) {
+ initRan();
+ }
+ return rand;
+ }
+
+ /**
+ * Sets the default generator to a {@link Well19937c} generator seeded with {@code
+ * System.currentTimeMillis() + System.identityHashCode(this))}.
+ */
+ private void initRan() {
+ rand = new Well19937c(System.currentTimeMillis() + System.identityHashCode(this));
+ }
+
+ /**
+ * Returns the SecureRandom used to generate secure random data.
+ *
+ * <p>Creates and initializes if null. Uses {@code System.currentTimeMillis() +
+ * System.identityHashCode(this)} as the default seed.
+ *
+ * @return the SecureRandom used to generate secure random data, wrapped in a {@link
+ * RandomGenerator}.
+ */
+ private RandomGenerator getSecRan() {
+ if (secRand == null) {
+ secRand = RandomGeneratorFactory.createRandomGenerator(new SecureRandom());
+ secRand.setSeed(System.currentTimeMillis() + System.identityHashCode(this));
+ }
+ return secRand;
+ }
+}
diff --git a/src/main/java/org/apache/commons/math3/random/RandomDataImpl.java b/src/main/java/org/apache/commons/math3/random/RandomDataImpl.java
new file mode 100644
index 0000000..d5749e9
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/random/RandomDataImpl.java
@@ -0,0 +1,544 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math3.random;
+
+import org.apache.commons.math3.distribution.IntegerDistribution;
+import org.apache.commons.math3.distribution.RealDistribution;
+import org.apache.commons.math3.exception.MathIllegalArgumentException;
+import org.apache.commons.math3.exception.NotANumberException;
+import org.apache.commons.math3.exception.NotFiniteNumberException;
+import org.apache.commons.math3.exception.NotPositiveException;
+import org.apache.commons.math3.exception.NotStrictlyPositiveException;
+import org.apache.commons.math3.exception.NumberIsTooLargeException;
+import org.apache.commons.math3.exception.OutOfRangeException;
+
+import java.io.Serializable;
+import java.security.NoSuchAlgorithmException;
+import java.security.NoSuchProviderException;
+import java.util.Collection;
+
+/**
+ * Generates random deviates and other random data using a {@link RandomGenerator} instance to
+ * generate non-secure data and a {@link java.security.SecureRandom} instance to provide data for
+ * the <code>nextSecureXxx</code> methods. If no <code>RandomGenerator</code> is provided in the
+ * constructor, the default is to use a {@link Well19937c} generator. To plug in a different
+ * implementation, either implement <code>RandomGenerator</code> directly or extend {@link
+ * AbstractRandomGenerator}.
+ *
+ * <p>Supports reseeding the underlying pseudo-random number generator (PRNG). The <code>
+ * SecurityProvider</code> and <code>Algorithm</code> used by the <code>SecureRandom</code> instance
+ * can also be reset.
+ *
+ * <p>For details on the default PRNGs, see {@link java.util.Random} and {@link
+ * java.security.SecureRandom}.
+ *
+ * <p><strong>Usage Notes</strong>:
+ *
+ * <ul>
+ * <li>Instance variables are used to maintain <code>RandomGenerator</code> and <code>SecureRandom
+ * </code> instances used in data generation. Therefore, to generate a random sequence of
+ * values or strings, you should use just <strong>one</strong> <code>RandomDataGenerator
+ * </code> instance repeatedly.
+ * <li>The "secure" methods are *much* slower. These should be used only when a cryptographically
+ * secure random sequence is required. A secure random sequence is a sequence of pseudo-random
+ * values which, in addition to being well-dispersed (so no subsequence of values is an any
+ * more likely than other subsequence of the the same length), also has the additional
+ * property that knowledge of values generated up to any point in the sequence does not make
+ * it any easier to predict subsequent values.
+ * <li>When a new <code>RandomDataGenerator</code> is created, the underlying random number
+ * generators are <strong>not</strong> initialized. If you do not explicitly seed the default
+ * non-secure generator, it is seeded with the current time in milliseconds plus the system
+ * identity hash code on first use. The same holds for the secure generator. If you provide a
+ * <code>RandomGenerator</code> to the constructor, however, this generator is not reseeded by
+ * the constructor nor is it reseeded on first use.
+ * <li>The <code>reSeed</code> and <code>reSeedSecure</code> methods delegate to the corresponding
+ * methods on the underlying <code>RandomGenerator</code> and <code>SecureRandom</code>
+ * instances. Therefore, <code>reSeed(long)</code> fully resets the initial state of the
+ * non-secure random number generator (so that reseeding with a specific value always results
+ * in the same subsequent random sequence); whereas reSeedSecure(long) does
+ * <strong>not</strong> reinitialize the secure random number generator (so secure sequences
+ * started with calls to reseedSecure(long) won't be identical).
+ * <li>This implementation is not synchronized. The underlying <code>RandomGenerator</code> or
+ * <code>SecureRandom</code> instances are not protected by synchronization and are not
+ * guaranteed to be thread-safe. Therefore, if an instance of this class is concurrently
+ * utilized by multiple threads, it is the responsibility of client code to synchronize access
+ * to seeding and data generation methods.
+ * </ul>
+ *
+ * @deprecated to be removed in 4.0. Use {@link RandomDataGenerator} instead
+ */
+@Deprecated
+public class RandomDataImpl implements RandomData, Serializable {
+
+ /** Serializable version identifier */
+ private static final long serialVersionUID = -626730818244969716L;
+
+ /** RandomDataGenerator delegate */
+ private final RandomDataGenerator delegate;
+
+ /**
+ * Construct a RandomDataImpl, using a default random generator as the source of randomness.
+ *
+ * <p>The default generator is a {@link Well19937c} seeded with {@code
+ * System.currentTimeMillis() + System.identityHashCode(this))}. The generator is initialized
+ * and seeded on first use.
+ */
+ public RandomDataImpl() {
+ delegate = new RandomDataGenerator();
+ }
+
+ /**
+ * Construct a RandomDataImpl using the supplied {@link RandomGenerator} as the source of
+ * (non-secure) random data.
+ *
+ * @param rand the source of (non-secure) random data (may be null, resulting in the default
+ * generator)
+ * @since 1.1
+ */
+ public RandomDataImpl(RandomGenerator rand) {
+ delegate = new RandomDataGenerator(rand);
+ }
+
+ /**
+ * @return the delegate object.
+ * @deprecated To be removed in 4.0.
+ */
+ @Deprecated
+ RandomDataGenerator getDelegate() {
+ return delegate;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * <p><strong>Algorithm Description:</strong> hex strings are generated using a 2-step process.
+ *
+ * <ol>
+ * <li>{@code len / 2 + 1} binary bytes are generated using the underlying Random
+ * <li>Each binary byte is translated into 2 hex digits
+ * </ol>
+ *
+ * @param len the desired string length.
+ * @return the random string.
+ * @throws NotStrictlyPositiveException if {@code len <= 0}.
+ */
+ public String nextHexString(int len) throws NotStrictlyPositiveException {
+ return delegate.nextHexString(len);
+ }
+
+ /** {@inheritDoc} */
+ public int nextInt(int lower, int upper) throws NumberIsTooLargeException {
+ return delegate.nextInt(lower, upper);
+ }
+
+ /** {@inheritDoc} */
+ public long nextLong(long lower, long upper) throws NumberIsTooLargeException {
+ return delegate.nextLong(lower, upper);
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * <p><strong>Algorithm Description:</strong> hex strings are generated in 40-byte segments
+ * using a 3-step process.
+ *
+ * <ol>
+ * <li>20 random bytes are generated using the underlying <code>SecureRandom</code>.
+ * <li>SHA-1 hash is applied to yield a 20-byte binary digest.
+ * <li>Each byte of the binary digest is converted to 2 hex digits.
+ * </ol>
+ */
+ public String nextSecureHexString(int len) throws NotStrictlyPositiveException {
+ return delegate.nextSecureHexString(len);
+ }
+
+ /** {@inheritDoc} */
+ public int nextSecureInt(int lower, int upper) throws NumberIsTooLargeException {
+ return delegate.nextSecureInt(lower, upper);
+ }
+
+ /** {@inheritDoc} */
+ public long nextSecureLong(long lower, long upper) throws NumberIsTooLargeException {
+ return delegate.nextSecureLong(lower, upper);
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * <p><strong>Algorithm Description</strong>:
+ *
+ * <ul>
+ * <li>For small means, uses simulation of a Poisson process using Uniform deviates, as
+ * described <a href="http://irmi.epfl.ch/cmos/Pmmi/interactive/rng7.htm">here.</a> The
+ * Poisson process (and hence value returned) is bounded by 1000 * mean.
+ * <li>For large means, uses the rejection algorithm described in <br>
+ * Devroye, Luc. (1981).<i>The Computer Generation of Poisson Random Variables</i>
+ * <strong>Computing</strong> vol. 26 pp. 197-207.
+ * </ul>
+ */
+ public long nextPoisson(double mean) throws NotStrictlyPositiveException {
+ return delegate.nextPoisson(mean);
+ }
+
+ /** {@inheritDoc} */
+ public double nextGaussian(double mu, double sigma) throws NotStrictlyPositiveException {
+ return delegate.nextGaussian(mu, sigma);
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * <p><strong>Algorithm Description</strong>: Uses the Algorithm SA (Ahrens) from p. 876 in:
+ * [1]: Ahrens, J. H. and Dieter, U. (1972). Computer methods for sampling from the exponential
+ * and normal distributions. Communications of the ACM, 15, 873-882.
+ */
+ public double nextExponential(double mean) throws NotStrictlyPositiveException {
+ return delegate.nextExponential(mean);
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * <p><strong>Algorithm Description</strong>: scales the output of Random.nextDouble(), but
+ * rejects 0 values (i.e., will generate another random double if Random.nextDouble() returns
+ * 0). This is necessary to provide a symmetric output interval (both endpoints excluded).
+ */
+ public double nextUniform(double lower, double upper)
+ throws NumberIsTooLargeException, NotFiniteNumberException, NotANumberException {
+ return delegate.nextUniform(lower, upper);
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * <p><strong>Algorithm Description</strong>: if the lower bound is excluded, scales the output
+ * of Random.nextDouble(), but rejects 0 values (i.e., will generate another random double if
+ * Random.nextDouble() returns 0). This is necessary to provide a symmetric output interval
+ * (both endpoints excluded).
+ *
+ * @since 3.0
+ */
+ public double nextUniform(double lower, double upper, boolean lowerInclusive)
+ throws NumberIsTooLargeException, NotFiniteNumberException, NotANumberException {
+ return delegate.nextUniform(lower, upper, lowerInclusive);
+ }
+
+ /**
+ * Generates a random value from the {@link
+ * org.apache.commons.math3.distribution.BetaDistribution Beta Distribution}. This
+ * implementation uses {@link #nextInversionDeviate(RealDistribution) inversion} to generate
+ * random values.
+ *
+ * @param alpha first distribution shape parameter
+ * @param beta second distribution shape parameter
+ * @return random value sampled from the beta(alpha, beta) distribution
+ * @since 2.2
+ */
+ public double nextBeta(double alpha, double beta) {
+ return delegate.nextBeta(alpha, beta);
+ }
+
+ /**
+ * Generates a random value from the {@link
+ * org.apache.commons.math3.distribution.BinomialDistribution Binomial Distribution}. This
+ * implementation uses {@link #nextInversionDeviate(RealDistribution) inversion} to generate
+ * random values.
+ *
+ * @param numberOfTrials number of trials of the Binomial distribution
+ * @param probabilityOfSuccess probability of success of the Binomial distribution
+ * @return random value sampled from the Binomial(numberOfTrials, probabilityOfSuccess)
+ * distribution
+ * @since 2.2
+ */
+ public int nextBinomial(int numberOfTrials, double probabilityOfSuccess) {
+ return delegate.nextBinomial(numberOfTrials, probabilityOfSuccess);
+ }
+
+ /**
+ * Generates a random value from the {@link
+ * org.apache.commons.math3.distribution.CauchyDistribution Cauchy Distribution}. This
+ * implementation uses {@link #nextInversionDeviate(RealDistribution) inversion} to generate
+ * random values.
+ *
+ * @param median the median of the Cauchy distribution
+ * @param scale the scale parameter of the Cauchy distribution
+ * @return random value sampled from the Cauchy(median, scale) distribution
+ * @since 2.2
+ */
+ public double nextCauchy(double median, double scale) {
+ return delegate.nextCauchy(median, scale);
+ }
+
+ /**
+ * Generates a random value from the {@link
+ * org.apache.commons.math3.distribution.ChiSquaredDistribution ChiSquare Distribution}. This
+ * implementation uses {@link #nextInversionDeviate(RealDistribution) inversion} to generate
+ * random values.
+ *
+ * @param df the degrees of freedom of the ChiSquare distribution
+ * @return random value sampled from the ChiSquare(df) distribution
+ * @since 2.2
+ */
+ public double nextChiSquare(double df) {
+ return delegate.nextChiSquare(df);
+ }
+
+ /**
+ * Generates a random value from the {@link org.apache.commons.math3.distribution.FDistribution
+ * F Distribution}. This implementation uses {@link #nextInversionDeviate(RealDistribution)
+ * inversion} to generate random values.
+ *
+ * @param numeratorDf the numerator degrees of freedom of the F distribution
+ * @param denominatorDf the denominator degrees of freedom of the F distribution
+ * @return random value sampled from the F(numeratorDf, denominatorDf) distribution
+ * @throws NotStrictlyPositiveException if {@code numeratorDf <= 0} or {@code denominatorDf <=
+ * 0}.
+ * @since 2.2
+ */
+ public double nextF(double numeratorDf, double denominatorDf)
+ throws NotStrictlyPositiveException {
+ return delegate.nextF(numeratorDf, denominatorDf);
+ }
+
+ /**
+ * Generates a random value from the {@link
+ * org.apache.commons.math3.distribution.GammaDistribution Gamma Distribution}.
+ *
+ * <p>This implementation uses the following algorithms:
+ *
+ * <p>For 0 < shape < 1: <br>
+ * Ahrens, J. H. and Dieter, U., <i>Computer methods for sampling from gamma, beta, Poisson and
+ * binomial distributions.</i> Computing, 12, 223-246, 1974.
+ *
+ * <p>For shape >= 1: <br>
+ * Marsaglia and Tsang, <i>A Simple Method for Generating Gamma Variables.</i> ACM Transactions
+ * on Mathematical Software, Volume 26 Issue 3, September, 2000.
+ *
+ * @param shape the median of the Gamma distribution
+ * @param scale the scale parameter of the Gamma distribution
+ * @return random value sampled from the Gamma(shape, scale) distribution
+ * @throws NotStrictlyPositiveException if {@code shape <= 0} or {@code scale <= 0}.
+ * @since 2.2
+ */
+ public double nextGamma(double shape, double scale) throws NotStrictlyPositiveException {
+ return delegate.nextGamma(shape, scale);
+ }
+
+ /**
+ * Generates a random value from the {@link
+ * org.apache.commons.math3.distribution.HypergeometricDistribution Hypergeometric
+ * Distribution}. This implementation uses {@link #nextInversionDeviate(IntegerDistribution)
+ * inversion} to generate random values.
+ *
+ * @param populationSize the population size of the Hypergeometric distribution
+ * @param numberOfSuccesses number of successes in the population of the Hypergeometric
+ * distribution
+ * @param sampleSize the sample size of the Hypergeometric distribution
+ * @return random value sampled from the Hypergeometric(numberOfSuccesses, sampleSize)
+ * distribution
+ * @throws NumberIsTooLargeException if {@code numberOfSuccesses > populationSize}, or {@code
+ * sampleSize > populationSize}.
+ * @throws NotStrictlyPositiveException if {@code populationSize <= 0}.
+ * @throws NotPositiveException if {@code numberOfSuccesses < 0}.
+ * @since 2.2
+ */
+ public int nextHypergeometric(int populationSize, int numberOfSuccesses, int sampleSize)
+ throws NotPositiveException, NotStrictlyPositiveException, NumberIsTooLargeException {
+ return delegate.nextHypergeometric(populationSize, numberOfSuccesses, sampleSize);
+ }
+
+ /**
+ * Generates a random value from the {@link
+ * org.apache.commons.math3.distribution.PascalDistribution Pascal Distribution}. This
+ * implementation uses {@link #nextInversionDeviate(IntegerDistribution) inversion} to generate
+ * random values.
+ *
+ * @param r the number of successes of the Pascal distribution
+ * @param p the probability of success of the Pascal distribution
+ * @return random value sampled from the Pascal(r, p) distribution
+ * @since 2.2
+ * @throws NotStrictlyPositiveException if the number of successes is not positive
+ * @throws OutOfRangeException if the probability of success is not in the range {@code [0, 1]}.
+ */
+ public int nextPascal(int r, double p)
+ throws NotStrictlyPositiveException, OutOfRangeException {
+ return delegate.nextPascal(r, p);
+ }
+
+ /**
+ * Generates a random value from the {@link org.apache.commons.math3.distribution.TDistribution
+ * T Distribution}. This implementation uses {@link #nextInversionDeviate(RealDistribution)
+ * inversion} to generate random values.
+ *
+ * @param df the degrees of freedom of the T distribution
+ * @return random value from the T(df) distribution
+ * @since 2.2
+ * @throws NotStrictlyPositiveException if {@code df <= 0}
+ */
+ public double nextT(double df) throws NotStrictlyPositiveException {
+ return delegate.nextT(df);
+ }
+
+ /**
+ * Generates a random value from the {@link
+ * org.apache.commons.math3.distribution.WeibullDistribution Weibull Distribution}. This
+ * implementation uses {@link #nextInversionDeviate(RealDistribution) inversion} to generate
+ * random values.
+ *
+ * @param shape the shape parameter of the Weibull distribution
+ * @param scale the scale parameter of the Weibull distribution
+ * @return random value sampled from the Weibull(shape, size) distribution
+ * @since 2.2
+ * @throws NotStrictlyPositiveException if {@code shape <= 0} or {@code scale <= 0}.
+ */
+ public double nextWeibull(double shape, double scale) throws NotStrictlyPositiveException {
+ return delegate.nextWeibull(shape, scale);
+ }
+
+ /**
+ * Generates a random value from the {@link
+ * org.apache.commons.math3.distribution.ZipfDistribution Zipf Distribution}. This
+ * implementation uses {@link #nextInversionDeviate(IntegerDistribution) inversion} to generate
+ * random values.
+ *
+ * @param numberOfElements the number of elements of the ZipfDistribution
+ * @param exponent the exponent of the ZipfDistribution
+ * @return random value sampled from the Zipf(numberOfElements, exponent) distribution
+ * @since 2.2
+ * @exception NotStrictlyPositiveException if {@code numberOfElements <= 0} or {@code exponent
+ * <= 0}.
+ */
+ public int nextZipf(int numberOfElements, double exponent) throws NotStrictlyPositiveException {
+ return delegate.nextZipf(numberOfElements, exponent);
+ }
+
+ /**
+ * Reseeds the random number generator with the supplied seed.
+ *
+ * <p>Will create and initialize if null.
+ *
+ * @param seed the seed value to use
+ */
+ public void reSeed(long seed) {
+ delegate.reSeed(seed);
+ }
+
+ /**
+ * Reseeds the secure random number generator with the current time in milliseconds.
+ *
+ * <p>Will create and initialize if null.
+ */
+ public void reSeedSecure() {
+ delegate.reSeedSecure();
+ }
+
+ /**
+ * Reseeds the secure random number generator with the supplied seed.
+ *
+ * <p>Will create and initialize if null.
+ *
+ * @param seed the seed value to use
+ */
+ public void reSeedSecure(long seed) {
+ delegate.reSeedSecure(seed);
+ }
+
+ /**
+ * Reseeds the random number generator with {@code System.currentTimeMillis() +
+ * System.identityHashCode(this))}.
+ */
+ public void reSeed() {
+ delegate.reSeed();
+ }
+
+ /**
+ * Sets the PRNG algorithm for the underlying SecureRandom instance using the Security Provider
+ * API. The Security Provider API is defined in <a href =
+ * "http://java.sun.com/j2se/1.3/docs/guide/security/CryptoSpec.html#AppA"> Java Cryptography
+ * Architecture API Specification & Reference.</a>
+ *
+ * <p><strong>USAGE NOTE:</strong> This method carries <i>significant</i> overhead and may take
+ * several seconds to execute.
+ *
+ * @param algorithm the name of the PRNG algorithm
+ * @param provider the name of the provider
+ * @throws NoSuchAlgorithmException if the specified algorithm is not available
+ * @throws NoSuchProviderException if the specified provider is not installed
+ */
+ public void setSecureAlgorithm(String algorithm, String provider)
+ throws NoSuchAlgorithmException, NoSuchProviderException {
+ delegate.setSecureAlgorithm(algorithm, provider);
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * <p>Uses a 2-cycle permutation shuffle. The shuffling process is described <a
+ * href="http://www.maths.abdn.ac.uk/~igc/tch/mx4002/notes/node83.html">here</a>.
+ */
+ public int[] nextPermutation(int n, int k)
+ throws NotStrictlyPositiveException, NumberIsTooLargeException {
+ return delegate.nextPermutation(n, k);
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * <p><strong>Algorithm Description</strong>: Uses a 2-cycle permutation shuffle to generate a
+ * random permutation of <code>c.size()</code> and then returns the elements whose indexes
+ * correspond to the elements of the generated permutation. This technique is described, and
+ * proven to generate random samples <a
+ * href="http://www.maths.abdn.ac.uk/~igc/tch/mx4002/notes/node83.html">here</a>
+ */
+ public Object[] nextSample(Collection<?> c, int k)
+ throws NotStrictlyPositiveException, NumberIsTooLargeException {
+ return delegate.nextSample(c, k);
+ }
+
+ /**
+ * Generate a random deviate from the given distribution using the <a
+ * href="http://en.wikipedia.org/wiki/Inverse_transform_sampling">inversion method.</a>
+ *
+ * @param distribution Continuous distribution to generate a random value from
+ * @return a random value sampled from the given distribution
+ * @throws MathIllegalArgumentException if the underlynig distribution throws one
+ * @since 2.2
+ * @deprecated use the distribution's sample() method
+ */
+ @Deprecated
+ public double nextInversionDeviate(RealDistribution distribution)
+ throws MathIllegalArgumentException {
+ return distribution.inverseCumulativeProbability(nextUniform(0, 1));
+ }
+
+ /**
+ * Generate a random deviate from the given distribution using the <a
+ * href="http://en.wikipedia.org/wiki/Inverse_transform_sampling">inversion method.</a>
+ *
+ * @param distribution Integer distribution to generate a random value from
+ * @return a random value sampled from the given distribution
+ * @throws MathIllegalArgumentException if the underlynig distribution throws one
+ * @since 2.2
+ * @deprecated use the distribution's sample() method
+ */
+ @Deprecated
+ public int nextInversionDeviate(IntegerDistribution distribution)
+ throws MathIllegalArgumentException {
+ return distribution.inverseCumulativeProbability(nextUniform(0, 1));
+ }
+}
diff --git a/src/main/java/org/apache/commons/math3/random/RandomGenerator.java b/src/main/java/org/apache/commons/math3/random/RandomGenerator.java
new file mode 100644
index 0000000..aeba5e3
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/random/RandomGenerator.java
@@ -0,0 +1,130 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math3.random;
+
+/**
+ * Interface extracted from <code>java.util.Random</code>. This interface is implemented by {@link
+ * AbstractRandomGenerator}.
+ *
+ * @since 1.1
+ */
+public interface RandomGenerator {
+
+ /**
+ * Sets the seed of the underlying random number generator using an <code>int</code> seed.
+ *
+ * <p>Sequences of values generated starting with the same seeds should be identical.
+ *
+ * @param seed the seed value
+ */
+ void setSeed(int seed);
+
+ /**
+ * Sets the seed of the underlying random number generator using an <code>int</code> array seed.
+ *
+ * <p>Sequences of values generated starting with the same seeds should be identical.
+ *
+ * @param seed the seed value
+ */
+ void setSeed(int[] seed);
+
+ /**
+ * Sets the seed of the underlying random number generator using a <code>long</code> seed.
+ *
+ * <p>Sequences of values generated starting with the same seeds should be identical.
+ *
+ * @param seed the seed value
+ */
+ void setSeed(long seed);
+
+ /**
+ * Generates random bytes and places them into a user-supplied byte array. The number of random
+ * bytes produced is equal to the length of the byte array.
+ *
+ * @param bytes the non-null byte array in which to put the random bytes
+ */
+ void nextBytes(byte[] bytes);
+
+ /**
+ * Returns the next pseudorandom, uniformly distributed <code>int</code> value from this random
+ * number generator's sequence. All 2<font size="-1"><sup>32</sup></font> possible {@code int}
+ * values should be produced with (approximately) equal probability.
+ *
+ * @return the next pseudorandom, uniformly distributed <code>int</code> value from this random
+ * number generator's sequence
+ */
+ int nextInt();
+
+ /**
+ * Returns a pseudorandom, uniformly distributed {@code int} value between 0 (inclusive) and the
+ * specified value (exclusive), drawn from this random number generator's sequence.
+ *
+ * @param n the bound on the random number to be returned. Must be positive.
+ * @return a pseudorandom, uniformly distributed {@code int} value between 0 (inclusive) and n
+ * (exclusive).
+ * @throws IllegalArgumentException if n is not positive.
+ */
+ int nextInt(int n);
+
+ /**
+ * Returns the next pseudorandom, uniformly distributed <code>long</code> value from this random
+ * number generator's sequence. All 2<font size="-1"><sup>64</sup></font> possible {@code long}
+ * values should be produced with (approximately) equal probability.
+ *
+ * @return the next pseudorandom, uniformly distributed <code>long</code> value from this random
+ * number generator's sequence
+ */
+ long nextLong();
+
+ /**
+ * Returns the next pseudorandom, uniformly distributed <code>boolean</code> value from this
+ * random number generator's sequence.
+ *
+ * @return the next pseudorandom, uniformly distributed <code>boolean</code> value from this
+ * random number generator's sequence
+ */
+ boolean nextBoolean();
+
+ /**
+ * Returns the next pseudorandom, uniformly distributed <code>float</code> value between <code>
+ * 0.0</code> and <code>1.0</code> from this random number generator's sequence.
+ *
+ * @return the next pseudorandom, uniformly distributed <code>float</code> value between <code>
+ * 0.0</code> and <code>1.0</code> from this random number generator's sequence
+ */
+ float nextFloat();
+
+ /**
+ * Returns the next pseudorandom, uniformly distributed <code>double</code> value between <code>
+ * 0.0</code> and <code>1.0</code> from this random number generator's sequence.
+ *
+ * @return the next pseudorandom, uniformly distributed <code>double</code> value between <code>
+ * 0.0</code> and <code>1.0</code> from this random number generator's sequence
+ */
+ double nextDouble();
+
+ /**
+ * Returns the next pseudorandom, Gaussian ("normally") distributed <code>double</code> value
+ * with mean <code>0.0</code> and standard deviation <code>1.0</code> from this random number
+ * generator's sequence.
+ *
+ * @return the next pseudorandom, Gaussian ("normally") distributed <code>double</code> value
+ * with mean <code>0.0</code> and standard deviation <code>1.0</code> from this random
+ * number generator's sequence
+ */
+ double nextGaussian();
+}
diff --git a/src/main/java/org/apache/commons/math3/random/RandomGeneratorFactory.java b/src/main/java/org/apache/commons/math3/random/RandomGeneratorFactory.java
new file mode 100644
index 0000000..b610b44
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/random/RandomGeneratorFactory.java
@@ -0,0 +1,118 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math3.random;
+
+import org.apache.commons.math3.exception.NotStrictlyPositiveException;
+
+import java.util.Random;
+
+/**
+ * Utilities for creating {@link RandomGenerator} instances.
+ *
+ * @since 3.3
+ */
+public class RandomGeneratorFactory {
+ /** Class contains only static methods. */
+ private RandomGeneratorFactory() {}
+
+ /**
+ * Creates a {@link RandomDataGenerator} instance that wraps a {@link Random} instance.
+ *
+ * @param rng JDK {@link Random} instance that will generate the the random data.
+ * @return the given RNG, wrapped in a {@link RandomGenerator}.
+ */
+ public static RandomGenerator createRandomGenerator(final Random rng) {
+ return new RandomGenerator() {
+ /** {@inheritDoc} */
+ public void setSeed(int seed) {
+ rng.setSeed((long) seed);
+ }
+
+ /** {@inheritDoc} */
+ public void setSeed(int[] seed) {
+ rng.setSeed(convertToLong(seed));
+ }
+
+ /** {@inheritDoc} */
+ public void setSeed(long seed) {
+ rng.setSeed(seed);
+ }
+
+ /** {@inheritDoc} */
+ public void nextBytes(byte[] bytes) {
+ rng.nextBytes(bytes);
+ }
+
+ /** {@inheritDoc} */
+ public int nextInt() {
+ return rng.nextInt();
+ }
+
+ /** {@inheritDoc} */
+ public int nextInt(int n) {
+ if (n <= 0) {
+ throw new NotStrictlyPositiveException(n);
+ }
+ return rng.nextInt(n);
+ }
+
+ /** {@inheritDoc} */
+ public long nextLong() {
+ return rng.nextLong();
+ }
+
+ /** {@inheritDoc} */
+ public boolean nextBoolean() {
+ return rng.nextBoolean();
+ }
+
+ /** {@inheritDoc} */
+ public float nextFloat() {
+ return rng.nextFloat();
+ }
+
+ /** {@inheritDoc} */
+ public double nextDouble() {
+ return rng.nextDouble();
+ }
+
+ /** {@inheritDoc} */
+ public double nextGaussian() {
+ return rng.nextGaussian();
+ }
+ };
+ }
+
+ /**
+ * Converts seed from one representation to another.
+ *
+ * @param seed Original seed.
+ * @return the converted seed.
+ */
+ public static long convertToLong(int[] seed) {
+ // The following number is the largest prime that fits
+ // in 32 bits (i.e. 2^32 - 5).
+ final long prime = 4294967291l;
+
+ long combined = 0l;
+ for (int s : seed) {
+ combined = combined * prime + s;
+ }
+
+ return combined;
+ }
+}
diff --git a/src/main/java/org/apache/commons/math3/random/RandomVectorGenerator.java b/src/main/java/org/apache/commons/math3/random/RandomVectorGenerator.java
new file mode 100644
index 0000000..55571ef
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/random/RandomVectorGenerator.java
@@ -0,0 +1,33 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math3.random;
+
+/**
+ * This interface represents a random generator for whole vectors.
+ *
+ * @since 1.2
+ */
+public interface RandomVectorGenerator {
+
+ /**
+ * Generate a random vector.
+ *
+ * @return a random vector as an array of double.
+ */
+ double[] nextVector();
+}
diff --git a/src/main/java/org/apache/commons/math3/random/SobolSequenceGenerator.java b/src/main/java/org/apache/commons/math3/random/SobolSequenceGenerator.java
new file mode 100644
index 0000000..7b91c7b
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/random/SobolSequenceGenerator.java
@@ -0,0 +1,329 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math3.random;
+
+import org.apache.commons.math3.exception.MathInternalError;
+import org.apache.commons.math3.exception.MathParseException;
+import org.apache.commons.math3.exception.NotPositiveException;
+import org.apache.commons.math3.exception.NotStrictlyPositiveException;
+import org.apache.commons.math3.exception.OutOfRangeException;
+import org.apache.commons.math3.util.FastMath;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.nio.charset.Charset;
+import java.util.Arrays;
+import java.util.NoSuchElementException;
+import java.util.StringTokenizer;
+
+/**
+ * Implementation of a Sobol sequence.
+ *
+ * <p>A Sobol sequence is a low-discrepancy sequence with the property that for all values of N, its
+ * subsequence (x1, ... xN) has a low discrepancy. It can be used to generate pseudo-random points
+ * in a space S, which are equi-distributed.
+ *
+ * <p>The implementation already comes with support for up to 1000 dimensions with direction numbers
+ * calculated from <a href="http://web.maths.unsw.edu.au/~fkuo/sobol/">Stephen Joe and Frances
+ * Kuo</a>.
+ *
+ * <p>The generator supports two modes:
+ *
+ * <ul>
+ * <li>sequential generation of points: {@link #nextVector()}
+ * <li>random access to the i-th point in the sequence: {@link #skipTo(int)}
+ * </ul>
+ *
+ * @see <a href="http://en.wikipedia.org/wiki/Sobol_sequence">Sobol sequence (Wikipedia)</a>
+ * @see <a href="http://web.maths.unsw.edu.au/~fkuo/sobol/">Sobol sequence direction numbers</a>
+ * @since 3.3
+ */
+public class SobolSequenceGenerator implements RandomVectorGenerator {
+
+ /** The number of bits to use. */
+ private static final int BITS = 52;
+
+ /** The scaling factor. */
+ private static final double SCALE = FastMath.pow(2, BITS);
+
+ /** The maximum supported space dimension. */
+ private static final int MAX_DIMENSION = 1000;
+
+ /** The resource containing the direction numbers. */
+ private static final String RESOURCE_NAME =
+ "/assets/org/apache/commons/math3/random/new-joe-kuo-6.1000";
+
+ /** Character set for file input. */
+ private static final String FILE_CHARSET = "US-ASCII";
+
+ /** Space dimension. */
+ private final int dimension;
+
+ /** The current index in the sequence. */
+ private int count = 0;
+
+ /** The direction vector for each component. */
+ private final long[][] direction;
+
+ /** The current state. */
+ private final long[] x;
+
+ /**
+ * Construct a new Sobol sequence generator for the given space dimension.
+ *
+ * @param dimension the space dimension
+ * @throws OutOfRangeException if the space dimension is outside the allowed range of [1, 1000]
+ */
+ public SobolSequenceGenerator(final int dimension) throws OutOfRangeException {
+ if (dimension < 1 || dimension > MAX_DIMENSION) {
+ throw new OutOfRangeException(dimension, 1, MAX_DIMENSION);
+ }
+
+ // initialize the other dimensions with direction numbers from a resource
+ final InputStream is = getClass().getResourceAsStream(RESOURCE_NAME);
+ if (is == null) {
+ throw new MathInternalError();
+ }
+
+ this.dimension = dimension;
+
+ // init data structures
+ direction = new long[dimension][BITS + 1];
+ x = new long[dimension];
+
+ try {
+ initFromStream(is);
+ } catch (IOException e) {
+ // the internal resource file could not be read -> should not happen
+ throw new MathInternalError();
+ } catch (MathParseException e) {
+ // the internal resource file could not be parsed -> should not happen
+ throw new MathInternalError();
+ } finally {
+ try {
+ is.close();
+ } catch (IOException e) { // NOPMD
+ // ignore
+ }
+ }
+ }
+
+ /**
+ * Construct a new Sobol sequence generator for the given space dimension with direction vectors
+ * loaded from the given stream.
+ *
+ * <p>The expected format is identical to the files available from <a
+ * href="http://web.maths.unsw.edu.au/~fkuo/sobol/">Stephen Joe and Frances Kuo</a>. The first
+ * line will be ignored as it is assumed to contain only the column headers. The columns are:
+ *
+ * <ul>
+ * <li>d: the dimension
+ * <li>s: the degree of the primitive polynomial
+ * <li>a: the number representing the coefficients
+ * <li>m: the list of initial direction numbers
+ * </ul>
+ *
+ * Example:
+ *
+ * <pre>
+ * d s a m_i
+ * 2 1 0 1
+ * 3 2 1 1 3
+ * </pre>
+ *
+ * <p>The input stream <i>must</i> be an ASCII text containing one valid direction vector per
+ * line.
+ *
+ * @param dimension the space dimension
+ * @param is the stream to read the direction vectors from
+ * @throws NotStrictlyPositiveException if the space dimension is &lt; 1
+ * @throws OutOfRangeException if the space dimension is outside the range [1, max], where max
+ * refers to the maximum dimension found in the input stream
+ * @throws MathParseException if the content in the stream could not be parsed successfully
+ * @throws IOException if an error occurs while reading from the input stream
+ */
+ public SobolSequenceGenerator(final int dimension, final InputStream is)
+ throws NotStrictlyPositiveException, MathParseException, IOException {
+
+ if (dimension < 1) {
+ throw new NotStrictlyPositiveException(dimension);
+ }
+
+ this.dimension = dimension;
+
+ // init data structures
+ direction = new long[dimension][BITS + 1];
+ x = new long[dimension];
+
+ // initialize the other dimensions with direction numbers from the stream
+ int lastDimension = initFromStream(is);
+ if (lastDimension < dimension) {
+ throw new OutOfRangeException(dimension, 1, lastDimension);
+ }
+ }
+
+ /**
+ * Load the direction vector for each dimension from the given stream.
+ *
+ * <p>The input stream <i>must</i> be an ASCII text containing one valid direction vector per
+ * line.
+ *
+ * @param is the input stream to read the direction vector from
+ * @return the last dimension that has been read from the input stream
+ * @throws IOException if the stream could not be read
+ * @throws MathParseException if the content could not be parsed successfully
+ */
+ private int initFromStream(final InputStream is) throws MathParseException, IOException {
+
+ // special case: dimension 1 -> use unit initialization
+ for (int i = 1; i <= BITS; i++) {
+ direction[0][i] = 1l << (BITS - i);
+ }
+
+ final Charset charset = Charset.forName(FILE_CHARSET);
+ final BufferedReader reader = new BufferedReader(new InputStreamReader(is, charset));
+ int dim = -1;
+
+ try {
+ // ignore first line
+ reader.readLine();
+
+ int lineNumber = 2;
+ int index = 1;
+ String line = null;
+ while ((line = reader.readLine()) != null) {
+ StringTokenizer st = new StringTokenizer(line, " ");
+ try {
+ dim = Integer.parseInt(st.nextToken());
+ if (dim >= 2 && dim <= dimension) { // we have found the right dimension
+ final int s = Integer.parseInt(st.nextToken());
+ final int a = Integer.parseInt(st.nextToken());
+ final int[] m = new int[s + 1];
+ for (int i = 1; i <= s; i++) {
+ m[i] = Integer.parseInt(st.nextToken());
+ }
+ initDirectionVector(index++, a, m);
+ }
+
+ if (dim > dimension) {
+ return dim;
+ }
+ } catch (NoSuchElementException e) {
+ throw new MathParseException(line, lineNumber);
+ } catch (NumberFormatException e) {
+ throw new MathParseException(line, lineNumber);
+ }
+ lineNumber++;
+ }
+ } finally {
+ reader.close();
+ }
+
+ return dim;
+ }
+
+ /**
+ * Calculate the direction numbers from the given polynomial.
+ *
+ * @param d the dimension, zero-based
+ * @param a the coefficients of the primitive polynomial
+ * @param m the initial direction numbers
+ */
+ private void initDirectionVector(final int d, final int a, final int[] m) {
+ final int s = m.length - 1;
+ for (int i = 1; i <= s; i++) {
+ direction[d][i] = ((long) m[i]) << (BITS - i);
+ }
+ for (int i = s + 1; i <= BITS; i++) {
+ direction[d][i] = direction[d][i - s] ^ (direction[d][i - s] >> s);
+ for (int k = 1; k <= s - 1; k++) {
+ direction[d][i] ^= ((a >> (s - 1 - k)) & 1) * direction[d][i - k];
+ }
+ }
+ }
+
+ /** {@inheritDoc} */
+ public double[] nextVector() {
+ final double[] v = new double[dimension];
+ if (count == 0) {
+ count++;
+ return v;
+ }
+
+ // find the index c of the rightmost 0
+ int c = 1;
+ int value = count - 1;
+ while ((value & 1) == 1) {
+ value >>= 1;
+ c++;
+ }
+
+ for (int i = 0; i < dimension; i++) {
+ x[i] ^= direction[i][c];
+ v[i] = (double) x[i] / SCALE;
+ }
+ count++;
+ return v;
+ }
+
+ /**
+ * Skip to the i-th point in the Sobol sequence.
+ *
+ * <p>This operation can be performed in O(1).
+ *
+ * @param index the index in the sequence to skip to
+ * @return the i-th point in the Sobol sequence
+ * @throws NotPositiveException if index &lt; 0
+ */
+ public double[] skipTo(final int index) throws NotPositiveException {
+ if (index == 0) {
+ // reset x vector
+ Arrays.fill(x, 0);
+ } else {
+ final int i = index - 1;
+ final long grayCode = i ^ (i >> 1); // compute the gray code of i = i XOR floor(i / 2)
+ for (int j = 0; j < dimension; j++) {
+ long result = 0;
+ for (int k = 1; k <= BITS; k++) {
+ final long shift = grayCode >> (k - 1);
+ if (shift == 0) {
+ // stop, as all remaining bits will be zero
+ break;
+ }
+ // the k-th bit of i
+ final long ik = shift & 1;
+ result ^= ik * direction[j][k];
+ }
+ x[j] = result;
+ }
+ }
+ count = index;
+ return nextVector();
+ }
+
+ /**
+ * Returns the index i of the next point in the Sobol sequence that will be returned by calling
+ * {@link #nextVector()}.
+ *
+ * @return the index of the next point
+ */
+ public int getNextIndex() {
+ return count;
+ }
+}
diff --git a/src/main/java/org/apache/commons/math3/random/StableRandomGenerator.java b/src/main/java/org/apache/commons/math3/random/StableRandomGenerator.java
new file mode 100644
index 0000000..b43a770
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/random/StableRandomGenerator.java
@@ -0,0 +1,143 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math3.random;
+
+import org.apache.commons.math3.exception.NullArgumentException;
+import org.apache.commons.math3.exception.OutOfRangeException;
+import org.apache.commons.math3.exception.util.LocalizedFormats;
+import org.apache.commons.math3.util.FastMath;
+
+/**
+ * This class provides a stable normalized random generator. It samples from a stable distribution
+ * with location parameter 0 and scale 1.
+ *
+ * <p>The implementation uses the Chambers-Mallows-Stuck method as described in <i>Handbook of
+ * computational statistics: concepts and methods</i> by James E. Gentle, Wolfgang H&auml;rdle,
+ * Yuichi Mori.
+ *
+ * @since 3.0
+ */
+public class StableRandomGenerator implements NormalizedRandomGenerator {
+ /** Underlying generator. */
+ private final RandomGenerator generator;
+
+ /** stability parameter */
+ private final double alpha;
+
+ /** skewness parameter */
+ private final double beta;
+
+ /** cache of expression value used in generation */
+ private final double zeta;
+
+ /**
+ * Create a new generator.
+ *
+ * @param generator underlying random generator to use
+ * @param alpha Stability parameter. Must be in range (0, 2]
+ * @param beta Skewness parameter. Must be in range [-1, 1]
+ * @throws NullArgumentException if generator is null
+ * @throws OutOfRangeException if {@code alpha <= 0} or {@code alpha > 2} or {@code beta < -1}
+ * or {@code beta > 1}
+ */
+ public StableRandomGenerator(
+ final RandomGenerator generator, final double alpha, final double beta)
+ throws NullArgumentException, OutOfRangeException {
+ if (generator == null) {
+ throw new NullArgumentException();
+ }
+
+ if (!(alpha > 0d && alpha <= 2d)) {
+ throw new OutOfRangeException(LocalizedFormats.OUT_OF_RANGE_LEFT, alpha, 0, 2);
+ }
+
+ if (!(beta >= -1d && beta <= 1d)) {
+ throw new OutOfRangeException(LocalizedFormats.OUT_OF_RANGE_SIMPLE, beta, -1, 1);
+ }
+
+ this.generator = generator;
+ this.alpha = alpha;
+ this.beta = beta;
+ if (alpha < 2d && beta != 0d) {
+ zeta = beta * FastMath.tan(FastMath.PI * alpha / 2);
+ } else {
+ zeta = 0d;
+ }
+ }
+
+ /**
+ * Generate a random scalar with zero location and unit scale.
+ *
+ * @return a random scalar with zero location and unit scale
+ */
+ public double nextNormalizedDouble() {
+ // we need 2 uniform random numbers to calculate omega and phi
+ double omega = -FastMath.log(generator.nextDouble());
+ double phi = FastMath.PI * (generator.nextDouble() - 0.5);
+
+ // Normal distribution case (Box-Muller algorithm)
+ if (alpha == 2d) {
+ return FastMath.sqrt(2d * omega) * FastMath.sin(phi);
+ }
+
+ double x;
+ // when beta = 0, zeta is zero as well
+ // Thus we can exclude it from the formula
+ if (beta == 0d) {
+ // Cauchy distribution case
+ if (alpha == 1d) {
+ x = FastMath.tan(phi);
+ } else {
+ x =
+ FastMath.pow(omega * FastMath.cos((1 - alpha) * phi), 1d / alpha - 1d)
+ * FastMath.sin(alpha * phi)
+ / FastMath.pow(FastMath.cos(phi), 1d / alpha);
+ }
+ } else {
+ // Generic stable distribution
+ double cosPhi = FastMath.cos(phi);
+ // to avoid rounding errors around alpha = 1
+ if (FastMath.abs(alpha - 1d) > 1e-8) {
+ double alphaPhi = alpha * phi;
+ double invAlphaPhi = phi - alphaPhi;
+ x =
+ (FastMath.sin(alphaPhi) + zeta * FastMath.cos(alphaPhi))
+ / cosPhi
+ * (FastMath.cos(invAlphaPhi) + zeta * FastMath.sin(invAlphaPhi))
+ / FastMath.pow(omega * cosPhi, (1 - alpha) / alpha);
+ } else {
+ double betaPhi = FastMath.PI / 2 + beta * phi;
+ x =
+ 2d
+ / FastMath.PI
+ * (betaPhi * FastMath.tan(phi)
+ - beta
+ * FastMath.log(
+ FastMath.PI
+ / 2d
+ * omega
+ * cosPhi
+ / betaPhi));
+
+ if (alpha != 1d) {
+ x += beta * FastMath.tan(FastMath.PI * alpha / 2);
+ }
+ }
+ }
+ return x;
+ }
+}
diff --git a/src/main/java/org/apache/commons/math3/random/SynchronizedRandomGenerator.java b/src/main/java/org/apache/commons/math3/random/SynchronizedRandomGenerator.java
new file mode 100644
index 0000000..410ec3c
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/random/SynchronizedRandomGenerator.java
@@ -0,0 +1,95 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math3.random;
+
+/**
+ * Any {@link RandomGenerator} implementation can be thread-safe if it is used through an instance
+ * of this class. This is achieved by enclosing calls to the methods of the actual generator inside
+ * the overridden {@code synchronized} methods of this class.
+ *
+ * @since 3.1
+ */
+public class SynchronizedRandomGenerator implements RandomGenerator {
+ /** Object to which all calls will be delegated. */
+ private final RandomGenerator wrapped;
+
+ /**
+ * Creates a synchronized wrapper for the given {@code RandomGenerator} instance.
+ *
+ * @param rng Generator whose methods will be called through their corresponding overridden
+ * synchronized version. To ensure thread-safety, the wrapped generator <em>must</em> not be
+ * used directly.
+ */
+ public SynchronizedRandomGenerator(RandomGenerator rng) {
+ wrapped = rng;
+ }
+
+ /** {@inheritDoc} */
+ public synchronized void setSeed(int seed) {
+ wrapped.setSeed(seed);
+ }
+
+ /** {@inheritDoc} */
+ public synchronized void setSeed(int[] seed) {
+ wrapped.setSeed(seed);
+ }
+
+ /** {@inheritDoc} */
+ public synchronized void setSeed(long seed) {
+ wrapped.setSeed(seed);
+ }
+
+ /** {@inheritDoc} */
+ public synchronized void nextBytes(byte[] bytes) {
+ wrapped.nextBytes(bytes);
+ }
+
+ /** {@inheritDoc} */
+ public synchronized int nextInt() {
+ return wrapped.nextInt();
+ }
+
+ /** {@inheritDoc} */
+ public synchronized int nextInt(int n) {
+ return wrapped.nextInt(n);
+ }
+
+ /** {@inheritDoc} */
+ public synchronized long nextLong() {
+ return wrapped.nextLong();
+ }
+
+ /** {@inheritDoc} */
+ public synchronized boolean nextBoolean() {
+ return wrapped.nextBoolean();
+ }
+
+ /** {@inheritDoc} */
+ public synchronized float nextFloat() {
+ return wrapped.nextFloat();
+ }
+
+ /** {@inheritDoc} */
+ public synchronized double nextDouble() {
+ return wrapped.nextDouble();
+ }
+
+ /** {@inheritDoc} */
+ public synchronized double nextGaussian() {
+ return wrapped.nextGaussian();
+ }
+}
diff --git a/src/main/java/org/apache/commons/math3/random/UncorrelatedRandomVectorGenerator.java b/src/main/java/org/apache/commons/math3/random/UncorrelatedRandomVectorGenerator.java
new file mode 100644
index 0000000..ec38c4d
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/random/UncorrelatedRandomVectorGenerator.java
@@ -0,0 +1,91 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math3.random;
+
+import org.apache.commons.math3.exception.DimensionMismatchException;
+
+import java.util.Arrays;
+
+/**
+ * A {@link RandomVectorGenerator} that generates vectors with uncorrelated components. Components
+ * of generated vectors follow (independent) Gaussian distributions, with parameters supplied in the
+ * constructor.
+ *
+ * @since 1.2
+ */
+public class UncorrelatedRandomVectorGenerator implements RandomVectorGenerator {
+
+ /** Underlying scalar generator. */
+ private final NormalizedRandomGenerator generator;
+
+ /** Mean vector. */
+ private final double[] mean;
+
+ /** Standard deviation vector. */
+ private final double[] standardDeviation;
+
+ /**
+ * Simple constructor.
+ *
+ * <p>Build an uncorrelated random vector generator from its mean and standard deviation
+ * vectors.
+ *
+ * @param mean expected mean values for each component
+ * @param standardDeviation standard deviation for each component
+ * @param generator underlying generator for uncorrelated normalized components
+ */
+ public UncorrelatedRandomVectorGenerator(
+ double[] mean, double[] standardDeviation, NormalizedRandomGenerator generator) {
+ if (mean.length != standardDeviation.length) {
+ throw new DimensionMismatchException(mean.length, standardDeviation.length);
+ }
+ this.mean = mean.clone();
+ this.standardDeviation = standardDeviation.clone();
+ this.generator = generator;
+ }
+
+ /**
+ * Simple constructor.
+ *
+ * <p>Build a null mean random and unit standard deviation uncorrelated vector generator
+ *
+ * @param dimension dimension of the vectors to generate
+ * @param generator underlying generator for uncorrelated normalized components
+ */
+ public UncorrelatedRandomVectorGenerator(int dimension, NormalizedRandomGenerator generator) {
+ mean = new double[dimension];
+ standardDeviation = new double[dimension];
+ Arrays.fill(standardDeviation, 1.0);
+ this.generator = generator;
+ }
+
+ /**
+ * Generate an uncorrelated random vector.
+ *
+ * @return a random vector as a newly built array of double
+ */
+ public double[] nextVector() {
+
+ double[] random = new double[mean.length];
+ for (int i = 0; i < random.length; ++i) {
+ random[i] = mean[i] + standardDeviation[i] * generator.nextNormalizedDouble();
+ }
+
+ return random;
+ }
+}
diff --git a/src/main/java/org/apache/commons/math3/random/UniformRandomGenerator.java b/src/main/java/org/apache/commons/math3/random/UniformRandomGenerator.java
new file mode 100644
index 0000000..4db7f8e
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/random/UniformRandomGenerator.java
@@ -0,0 +1,58 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math3.random;
+
+import org.apache.commons.math3.util.FastMath;
+
+/**
+ * This class implements a normalized uniform random generator.
+ *
+ * <p>Since it is a normalized random generator, it generates values from a uniform distribution
+ * with mean equal to 0 and standard deviation equal to 1. Generated values fall in the range
+ * [-&#x0221A;3, +&#x0221A;3].
+ *
+ * @since 1.2
+ */
+public class UniformRandomGenerator implements NormalizedRandomGenerator {
+
+ /** Square root of three. */
+ private static final double SQRT3 = FastMath.sqrt(3.0);
+
+ /** Underlying generator. */
+ private final RandomGenerator generator;
+
+ /**
+ * Create a new generator.
+ *
+ * @param generator underlying random generator to use
+ */
+ public UniformRandomGenerator(RandomGenerator generator) {
+ this.generator = generator;
+ }
+
+ /**
+ * Generate a random scalar with null mean and unit standard deviation.
+ *
+ * <p>The number generated is uniformly distributed between -&sqrt;(3) and +&sqrt;(3).
+ *
+ * @return a random scalar with null mean and unit standard deviation
+ */
+ public double nextNormalizedDouble() {
+ return SQRT3 * (2 * generator.nextDouble() - 1.0);
+ }
+}
diff --git a/src/main/java/org/apache/commons/math3/random/UnitSphereRandomVectorGenerator.java b/src/main/java/org/apache/commons/math3/random/UnitSphereRandomVectorGenerator.java
new file mode 100644
index 0000000..ddcaa97
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/random/UnitSphereRandomVectorGenerator.java
@@ -0,0 +1,74 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math3.random;
+
+import org.apache.commons.math3.util.FastMath;
+
+/**
+ * Generate random vectors isotropically located on the surface of a sphere.
+ *
+ * @since 2.1
+ */
+public class UnitSphereRandomVectorGenerator implements RandomVectorGenerator {
+ /** RNG used for generating the individual components of the vectors. */
+ private final RandomGenerator rand;
+
+ /** Space dimension. */
+ private final int dimension;
+
+ /**
+ * @param dimension Space dimension.
+ * @param rand RNG for the individual components of the vectors.
+ */
+ public UnitSphereRandomVectorGenerator(final int dimension, final RandomGenerator rand) {
+ this.dimension = dimension;
+ this.rand = rand;
+ }
+
+ /**
+ * Create an object that will use a default RNG ({@link MersenneTwister}), in order to generate
+ * the individual components.
+ *
+ * @param dimension Space dimension.
+ */
+ public UnitSphereRandomVectorGenerator(final int dimension) {
+ this(dimension, new MersenneTwister());
+ }
+
+ /** {@inheritDoc} */
+ public double[] nextVector() {
+ final double[] v = new double[dimension];
+
+ // See http://mathworld.wolfram.com/SpherePointPicking.html for example.
+ // Pick a point by choosing a standard Gaussian for each element, and then
+ // normalizing to unit length.
+ double normSq = 0;
+ for (int i = 0; i < dimension; i++) {
+ final double comp = rand.nextGaussian();
+ v[i] = comp;
+ normSq += comp * comp;
+ }
+
+ final double f = 1 / FastMath.sqrt(normSq);
+ for (int i = 0; i < dimension; i++) {
+ v[i] *= f;
+ }
+
+ return v;
+ }
+}
diff --git a/src/main/java/org/apache/commons/math3/random/ValueServer.java b/src/main/java/org/apache/commons/math3/random/ValueServer.java
new file mode 100644
index 0000000..9c15292
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/random/ValueServer.java
@@ -0,0 +1,465 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math3.random;
+
+import org.apache.commons.math3.exception.MathIllegalArgumentException;
+import org.apache.commons.math3.exception.MathIllegalStateException;
+import org.apache.commons.math3.exception.NullArgumentException;
+import org.apache.commons.math3.exception.ZeroException;
+import org.apache.commons.math3.exception.util.LocalizedFormats;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.net.MalformedURLException;
+import java.net.URL;
+
+/**
+ * Generates values for use in simulation applications.
+ *
+ * <p>How values are generated is determined by the <code>mode</code> property.
+ *
+ * <p>Supported <code>mode</code> values are:
+ *
+ * <ul>
+ * <li>DIGEST_MODE -- uses an empirical distribution
+ * <li>REPLAY_MODE -- replays data from <code>valuesFileURL</code>
+ * <li>UNIFORM_MODE -- generates uniformly distributed random values with mean = <code>mu</code>
+ * <li>EXPONENTIAL_MODE -- generates exponentially distributed random values with mean = <code>mu
+ * </code>
+ * <li>GAUSSIAN_MODE -- generates Gaussian distributed random values with mean = <code>mu</code>
+ * and standard deviation = <code>sigma</code>
+ * <li>CONSTANT_MODE -- returns <code>mu</code> every time.
+ * </ul>
+ */
+public class ValueServer {
+
+ /** Use empirical distribution. */
+ public static final int DIGEST_MODE = 0;
+
+ /** Replay data from valuesFilePath. */
+ public static final int REPLAY_MODE = 1;
+
+ /** Uniform random deviates with mean = &mu;. */
+ public static final int UNIFORM_MODE = 2;
+
+ /** Exponential random deviates with mean = &mu;. */
+ public static final int EXPONENTIAL_MODE = 3;
+
+ /** Gaussian random deviates with mean = &mu;, std dev = &sigma;. */
+ public static final int GAUSSIAN_MODE = 4;
+
+ /** Always return mu */
+ public static final int CONSTANT_MODE = 5;
+
+ /** mode determines how values are generated. */
+ private int mode = 5;
+
+ /** URI to raw data values. */
+ private URL valuesFileURL = null;
+
+ /** Mean for use with non-data-driven modes. */
+ private double mu = 0.0;
+
+ /** Standard deviation for use with GAUSSIAN_MODE. */
+ private double sigma = 0.0;
+
+ /** Empirical probability distribution for use with DIGEST_MODE. */
+ private EmpiricalDistribution empiricalDistribution = null;
+
+ /** File pointer for REPLAY_MODE. */
+ private BufferedReader filePointer = null;
+
+ /** RandomDataImpl to use for random data generation. */
+ private final RandomDataGenerator randomData;
+
+ // Data generation modes ======================================
+
+ /** Creates new ValueServer */
+ public ValueServer() {
+ randomData = new RandomDataGenerator();
+ }
+
+ /**
+ * Construct a ValueServer instance using a RandomDataImpl as its source of random data.
+ *
+ * @param randomData the RandomDataImpl instance used to source random data
+ * @since 3.0
+ * @deprecated use {@link #ValueServer(RandomGenerator)}
+ */
+ @Deprecated
+ public ValueServer(RandomDataImpl randomData) {
+ this.randomData = randomData.getDelegate();
+ }
+
+ /**
+ * Construct a ValueServer instance using a RandomGenerator as its source of random data.
+ *
+ * @since 3.1
+ * @param generator source of random data
+ */
+ public ValueServer(RandomGenerator generator) {
+ this.randomData = new RandomDataGenerator(generator);
+ }
+
+ /**
+ * Returns the next generated value, generated according to the mode value (see MODE constants).
+ *
+ * @return generated value
+ * @throws IOException in REPLAY_MODE if a file I/O error occurs
+ * @throws MathIllegalStateException if mode is not recognized
+ * @throws MathIllegalArgumentException if the underlying random generator thwrows one
+ */
+ public double getNext()
+ throws IOException, MathIllegalStateException, MathIllegalArgumentException {
+ switch (mode) {
+ case DIGEST_MODE:
+ return getNextDigest();
+ case REPLAY_MODE:
+ return getNextReplay();
+ case UNIFORM_MODE:
+ return getNextUniform();
+ case EXPONENTIAL_MODE:
+ return getNextExponential();
+ case GAUSSIAN_MODE:
+ return getNextGaussian();
+ case CONSTANT_MODE:
+ return mu;
+ default:
+ throw new MathIllegalStateException(
+ LocalizedFormats.UNKNOWN_MODE,
+ mode,
+ "DIGEST_MODE",
+ DIGEST_MODE,
+ "REPLAY_MODE",
+ REPLAY_MODE,
+ "UNIFORM_MODE",
+ UNIFORM_MODE,
+ "EXPONENTIAL_MODE",
+ EXPONENTIAL_MODE,
+ "GAUSSIAN_MODE",
+ GAUSSIAN_MODE,
+ "CONSTANT_MODE",
+ CONSTANT_MODE);
+ }
+ }
+
+ /**
+ * Fills the input array with values generated using getNext() repeatedly.
+ *
+ * @param values array to be filled
+ * @throws IOException in REPLAY_MODE if a file I/O error occurs
+ * @throws MathIllegalStateException if mode is not recognized
+ * @throws MathIllegalArgumentException if the underlying random generator thwrows one
+ */
+ public void fill(double[] values)
+ throws IOException, MathIllegalStateException, MathIllegalArgumentException {
+ for (int i = 0; i < values.length; i++) {
+ values[i] = getNext();
+ }
+ }
+
+ /**
+ * Returns an array of length <code>length</code> with values generated using getNext()
+ * repeatedly.
+ *
+ * @param length length of output array
+ * @return array of generated values
+ * @throws IOException in REPLAY_MODE if a file I/O error occurs
+ * @throws MathIllegalStateException if mode is not recognized
+ * @throws MathIllegalArgumentException if the underlying random generator thwrows one
+ */
+ public double[] fill(int length)
+ throws IOException, MathIllegalStateException, MathIllegalArgumentException {
+ double[] out = new double[length];
+ for (int i = 0; i < length; i++) {
+ out[i] = getNext();
+ }
+ return out;
+ }
+
+ /**
+ * Computes the empirical distribution using values from the file in <code>valuesFileURL</code>,
+ * using the default number of bins.
+ *
+ * <p><code>valuesFileURL</code> must exist and be readable by *this at runtime.
+ *
+ * <p>This method must be called before using <code>getNext()</code> with <code>
+ * mode = DIGEST_MODE</code>
+ *
+ * @throws IOException if an I/O error occurs reading the input file
+ * @throws NullArgumentException if the {@code valuesFileURL} has not been set
+ * @throws ZeroException if URL contains no data
+ */
+ public void computeDistribution() throws IOException, ZeroException, NullArgumentException {
+ computeDistribution(EmpiricalDistribution.DEFAULT_BIN_COUNT);
+ }
+
+ /**
+ * Computes the empirical distribution using values from the file in <code>valuesFileURL</code>
+ * and <code>binCount</code> bins.
+ *
+ * <p><code>valuesFileURL</code> must exist and be readable by this process at runtime.
+ *
+ * <p>This method must be called before using <code>getNext()</code> with <code>
+ * mode = DIGEST_MODE</code>
+ *
+ * @param binCount the number of bins used in computing the empirical distribution
+ * @throws NullArgumentException if the {@code valuesFileURL} has not been set
+ * @throws IOException if an error occurs reading the input file
+ * @throws ZeroException if URL contains no data
+ */
+ public void computeDistribution(int binCount)
+ throws NullArgumentException, IOException, ZeroException {
+ empiricalDistribution =
+ new EmpiricalDistribution(binCount, randomData.getRandomGenerator());
+ empiricalDistribution.load(valuesFileURL);
+ mu = empiricalDistribution.getSampleStats().getMean();
+ sigma = empiricalDistribution.getSampleStats().getStandardDeviation();
+ }
+
+ /**
+ * Returns the data generation mode. See {@link ValueServer the class javadoc} for description
+ * of the valid values of this property.
+ *
+ * @return Value of property mode.
+ */
+ public int getMode() {
+ return mode;
+ }
+
+ /**
+ * Sets the data generation mode.
+ *
+ * @param mode New value of the data generation mode.
+ */
+ public void setMode(int mode) {
+ this.mode = mode;
+ }
+
+ /**
+ * Returns the URL for the file used to build the empirical distribution when using {@link
+ * #DIGEST_MODE}.
+ *
+ * @return Values file URL.
+ */
+ public URL getValuesFileURL() {
+ return valuesFileURL;
+ }
+
+ /**
+ * Sets the {@link #getValuesFileURL() values file URL} using a string URL representation.
+ *
+ * @param url String representation for new valuesFileURL.
+ * @throws MalformedURLException if url is not well formed
+ */
+ public void setValuesFileURL(String url) throws MalformedURLException {
+ this.valuesFileURL = new URL(url);
+ }
+
+ /**
+ * Sets the the {@link #getValuesFileURL() values file URL}.
+ *
+ * <p>The values file <i>must</i> be an ASCII text file containing one valid numeric entry per
+ * line.
+ *
+ * @param url URL of the values file.
+ */
+ public void setValuesFileURL(URL url) {
+ this.valuesFileURL = url;
+ }
+
+ /**
+ * Returns the {@link EmpiricalDistribution} used when operating in {@value #DIGEST_MODE}.
+ *
+ * @return EmpircalDistribution built by {@link #computeDistribution()}
+ */
+ public EmpiricalDistribution getEmpiricalDistribution() {
+ return empiricalDistribution;
+ }
+
+ /**
+ * Resets REPLAY_MODE file pointer to the beginning of the <code>valuesFileURL</code>.
+ *
+ * @throws IOException if an error occurs opening the file
+ * @throws NullPointerException if the {@code valuesFileURL} has not been set.
+ */
+ public void resetReplayFile() throws IOException {
+ if (filePointer != null) {
+ try {
+ filePointer.close();
+ filePointer = null;
+ } catch (IOException ex) { // NOPMD
+ // ignore
+ }
+ }
+ filePointer =
+ new BufferedReader(new InputStreamReader(valuesFileURL.openStream(), "UTF-8"));
+ }
+
+ /**
+ * Closes {@code valuesFileURL} after use in REPLAY_MODE.
+ *
+ * @throws IOException if an error occurs closing the file
+ */
+ public void closeReplayFile() throws IOException {
+ if (filePointer != null) {
+ filePointer.close();
+ filePointer = null;
+ }
+ }
+
+ /**
+ * Returns the mean used when operating in {@link #GAUSSIAN_MODE}, {@link #EXPONENTIAL_MODE} or
+ * {@link #UNIFORM_MODE}. When operating in {@link #CONSTANT_MODE}, this is the constant value
+ * always returned. Calling {@link #computeDistribution()} sets this value to the overall mean
+ * of the values in the {@link #getValuesFileURL() values file}.
+ *
+ * @return Mean used in data generation.
+ */
+ public double getMu() {
+ return mu;
+ }
+
+ /**
+ * Sets the {@link #getMu() mean} used in data generation. Note that calling this method after
+ * {@link #computeDistribution()} has been called will have no effect on data generated in
+ * {@link #DIGEST_MODE}.
+ *
+ * @param mu new Mean value.
+ */
+ public void setMu(double mu) {
+ this.mu = mu;
+ }
+
+ /**
+ * Returns the standard deviation used when operating in {@link #GAUSSIAN_MODE}. Calling {@link
+ * #computeDistribution()} sets this value to the overall standard deviation of the values in
+ * the {@link #getValuesFileURL() values file}. This property has no effect when the data
+ * generation mode is not {@link #GAUSSIAN_MODE}.
+ *
+ * @return Standard deviation used when operating in {@link #GAUSSIAN_MODE}.
+ */
+ public double getSigma() {
+ return sigma;
+ }
+
+ /**
+ * Sets the {@link #getSigma() standard deviation} used in {@link #GAUSSIAN_MODE}.
+ *
+ * @param sigma New standard deviation.
+ */
+ public void setSigma(double sigma) {
+ this.sigma = sigma;
+ }
+
+ /**
+ * Reseeds the random data generator.
+ *
+ * @param seed Value with which to reseed the {@link RandomDataImpl} used to generate random
+ * data.
+ */
+ public void reSeed(long seed) {
+ randomData.reSeed(seed);
+ }
+
+ // ------------- private methods ---------------------------------
+
+ /**
+ * Gets a random value in DIGEST_MODE.
+ *
+ * <p><strong>Preconditions</strong>:
+ *
+ * <ul>
+ * <li>Before this method is called, <code>computeDistribution()</code> must have completed
+ * successfully; otherwise an <code>IllegalStateException</code> will be thrown
+ * </ul>
+ *
+ * @return next random value from the empirical distribution digest
+ * @throws MathIllegalStateException if digest has not been initialized
+ */
+ private double getNextDigest() throws MathIllegalStateException {
+ if ((empiricalDistribution == null) || (empiricalDistribution.getBinStats().size() == 0)) {
+ throw new MathIllegalStateException(LocalizedFormats.DIGEST_NOT_INITIALIZED);
+ }
+ return empiricalDistribution.getNextValue();
+ }
+
+ /**
+ * Gets next sequential value from the <code>valuesFileURL</code>.
+ *
+ * <p>Throws an IOException if the read fails.
+ *
+ * <p>This method will open the <code>valuesFileURL</code> if there is no replay file open.
+ *
+ * <p>The <code>valuesFileURL</code> will be closed and reopened to wrap around from EOF to BOF
+ * if EOF is encountered. EOFException (which is a kind of IOException) may still be thrown if
+ * the <code>valuesFileURL</code> is empty.
+ *
+ * @return next value from the replay file
+ * @throws IOException if there is a problem reading from the file
+ * @throws MathIllegalStateException if URL contains no data
+ * @throws NumberFormatException if an invalid numeric string is encountered in the file
+ */
+ private double getNextReplay() throws IOException, MathIllegalStateException {
+ String str = null;
+ if (filePointer == null) {
+ resetReplayFile();
+ }
+ if ((str = filePointer.readLine()) == null) {
+ // we have probably reached end of file, wrap around from EOF to BOF
+ closeReplayFile();
+ resetReplayFile();
+ if ((str = filePointer.readLine()) == null) {
+ throw new MathIllegalStateException(
+ LocalizedFormats.URL_CONTAINS_NO_DATA, valuesFileURL);
+ }
+ }
+ return Double.parseDouble(str);
+ }
+
+ /**
+ * Gets a uniformly distributed random value with mean = mu.
+ *
+ * @return random uniform value
+ * @throws MathIllegalArgumentException if the underlying random generator thwrows one
+ */
+ private double getNextUniform() throws MathIllegalArgumentException {
+ return randomData.nextUniform(0, 2 * mu);
+ }
+
+ /**
+ * Gets an exponentially distributed random value with mean = mu.
+ *
+ * @return random exponential value
+ * @throws MathIllegalArgumentException if the underlying random generator thwrows one
+ */
+ private double getNextExponential() throws MathIllegalArgumentException {
+ return randomData.nextExponential(mu);
+ }
+
+ /**
+ * Gets a Gaussian distributed random value with mean = mu and standard deviation = sigma.
+ *
+ * @return random Gaussian value
+ * @throws MathIllegalArgumentException if the underlying random generator thwrows one
+ */
+ private double getNextGaussian() throws MathIllegalArgumentException {
+ return randomData.nextGaussian(mu, sigma);
+ }
+}
diff --git a/src/main/java/org/apache/commons/math3/random/Well1024a.java b/src/main/java/org/apache/commons/math3/random/Well1024a.java
new file mode 100644
index 0000000..72c7f16
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/random/Well1024a.java
@@ -0,0 +1,110 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math3.random;
+
+/**
+ * This class implements the WELL1024a pseudo-random number generator from Fran&ccedil;ois Panneton,
+ * Pierre L'Ecuyer and Makoto Matsumoto.
+ *
+ * <p>This generator is described in a paper by Fran&ccedil;ois Panneton, Pierre L'Ecuyer and Makoto
+ * Matsumoto <a href="http://www.iro.umontreal.ca/~lecuyer/myftp/papers/wellrng.pdf">Improved
+ * Long-Period Generators Based on Linear Recurrences Modulo 2</a> ACM Transactions on Mathematical
+ * Software, 32, 1 (2006). The errata for the paper are in <a
+ * href="http://www.iro.umontreal.ca/~lecuyer/myftp/papers/wellrng-errata.txt">wellrng-errata.txt</a>.
+ *
+ * @see <a href="http://www.iro.umontreal.ca/~panneton/WELLRNG.html">WELL Random number
+ * generator</a>
+ * @since 2.2
+ */
+public class Well1024a extends AbstractWell {
+
+ /** Serializable version identifier. */
+ private static final long serialVersionUID = 5680173464174485492L;
+
+ /** Number of bits in the pool. */
+ private static final int K = 1024;
+
+ /** First parameter of the algorithm. */
+ private static final int M1 = 3;
+
+ /** Second parameter of the algorithm. */
+ private static final int M2 = 24;
+
+ /** Third parameter of the algorithm. */
+ private static final int M3 = 10;
+
+ /**
+ * Creates a new random number generator.
+ *
+ * <p>The instance is initialized using the current time as the seed.
+ */
+ public Well1024a() {
+ super(K, M1, M2, M3);
+ }
+
+ /**
+ * Creates a new random number generator using a single int seed.
+ *
+ * @param seed the initial seed (32 bits integer)
+ */
+ public Well1024a(int seed) {
+ super(K, M1, M2, M3, seed);
+ }
+
+ /**
+ * Creates a new random number generator using an int array seed.
+ *
+ * @param seed the initial seed (32 bits integers array), if null the seed of the generator will
+ * be related to the current time
+ */
+ public Well1024a(int[] seed) {
+ super(K, M1, M2, M3, seed);
+ }
+
+ /**
+ * Creates a new random number generator using a single long seed.
+ *
+ * @param seed the initial seed (64 bits integer)
+ */
+ public Well1024a(long seed) {
+ super(K, M1, M2, M3, seed);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ protected int next(final int bits) {
+
+ final int indexRm1 = iRm1[index];
+
+ final int v0 = v[index];
+ final int vM1 = v[i1[index]];
+ final int vM2 = v[i2[index]];
+ final int vM3 = v[i3[index]];
+
+ final int z0 = v[indexRm1];
+ final int z1 = v0 ^ (vM1 ^ (vM1 >>> 8));
+ final int z2 = (vM2 ^ (vM2 << 19)) ^ (vM3 ^ (vM3 << 14));
+ final int z3 = z1 ^ z2;
+ final int z4 = (z0 ^ (z0 << 11)) ^ (z1 ^ (z1 << 7)) ^ (z2 ^ (z2 << 13));
+
+ v[index] = z3;
+ v[indexRm1] = z4;
+ index = indexRm1;
+
+ return z4 >>> (32 - bits);
+ }
+}
diff --git a/src/main/java/org/apache/commons/math3/random/Well19937a.java b/src/main/java/org/apache/commons/math3/random/Well19937a.java
new file mode 100644
index 0000000..a93358b
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/random/Well19937a.java
@@ -0,0 +1,112 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math3.random;
+
+/**
+ * This class implements the WELL19937a pseudo-random number generator from Fran&ccedil;ois
+ * Panneton, Pierre L'Ecuyer and Makoto Matsumoto.
+ *
+ * <p>This generator is described in a paper by Fran&ccedil;ois Panneton, Pierre L'Ecuyer and Makoto
+ * Matsumoto <a href="http://www.iro.umontreal.ca/~lecuyer/myftp/papers/wellrng.pdf">Improved
+ * Long-Period Generators Based on Linear Recurrences Modulo 2</a> ACM Transactions on Mathematical
+ * Software, 32, 1 (2006). The errata for the paper are in <a
+ * href="http://www.iro.umontreal.ca/~lecuyer/myftp/papers/wellrng-errata.txt">wellrng-errata.txt</a>.
+ *
+ * @see <a href="http://www.iro.umontreal.ca/~panneton/WELLRNG.html">WELL Random number
+ * generator</a>
+ * @since 2.2
+ */
+public class Well19937a extends AbstractWell {
+
+ /** Serializable version identifier. */
+ private static final long serialVersionUID = -7462102162223815419L;
+
+ /** Number of bits in the pool. */
+ private static final int K = 19937;
+
+ /** First parameter of the algorithm. */
+ private static final int M1 = 70;
+
+ /** Second parameter of the algorithm. */
+ private static final int M2 = 179;
+
+ /** Third parameter of the algorithm. */
+ private static final int M3 = 449;
+
+ /**
+ * Creates a new random number generator.
+ *
+ * <p>The instance is initialized using the current time as the seed.
+ */
+ public Well19937a() {
+ super(K, M1, M2, M3);
+ }
+
+ /**
+ * Creates a new random number generator using a single int seed.
+ *
+ * @param seed the initial seed (32 bits integer)
+ */
+ public Well19937a(int seed) {
+ super(K, M1, M2, M3, seed);
+ }
+
+ /**
+ * Creates a new random number generator using an int array seed.
+ *
+ * @param seed the initial seed (32 bits integers array), if null the seed of the generator will
+ * be related to the current time
+ */
+ public Well19937a(int[] seed) {
+ super(K, M1, M2, M3, seed);
+ }
+
+ /**
+ * Creates a new random number generator using a single long seed.
+ *
+ * @param seed the initial seed (64 bits integer)
+ */
+ public Well19937a(long seed) {
+ super(K, M1, M2, M3, seed);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ protected int next(final int bits) {
+
+ final int indexRm1 = iRm1[index];
+ final int indexRm2 = iRm2[index];
+
+ final int v0 = v[index];
+ final int vM1 = v[i1[index]];
+ final int vM2 = v[i2[index]];
+ final int vM3 = v[i3[index]];
+
+ final int z0 = (0x80000000 & v[indexRm1]) ^ (0x7FFFFFFF & v[indexRm2]);
+ final int z1 = (v0 ^ (v0 << 25)) ^ (vM1 ^ (vM1 >>> 27));
+ final int z2 = (vM2 >>> 9) ^ (vM3 ^ (vM3 >>> 1));
+ final int z3 = z1 ^ z2;
+ final int z4 = z0 ^ (z1 ^ (z1 << 9)) ^ (z2 ^ (z2 << 21)) ^ (z3 ^ (z3 >>> 21));
+
+ v[index] = z3;
+ v[indexRm1] = z4;
+ v[indexRm2] &= 0x80000000;
+ index = indexRm1;
+
+ return z4 >>> (32 - bits);
+ }
+}
diff --git a/src/main/java/org/apache/commons/math3/random/Well19937c.java b/src/main/java/org/apache/commons/math3/random/Well19937c.java
new file mode 100644
index 0000000..a699195
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/random/Well19937c.java
@@ -0,0 +1,117 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math3.random;
+
+/**
+ * This class implements the WELL19937c pseudo-random number generator from Fran&ccedil;ois
+ * Panneton, Pierre L'Ecuyer and Makoto Matsumoto.
+ *
+ * <p>This generator is described in a paper by Fran&ccedil;ois Panneton, Pierre L'Ecuyer and Makoto
+ * Matsumoto <a href="http://www.iro.umontreal.ca/~lecuyer/myftp/papers/wellrng.pdf">Improved
+ * Long-Period Generators Based on Linear Recurrences Modulo 2</a> ACM Transactions on Mathematical
+ * Software, 32, 1 (2006). The errata for the paper are in <a
+ * href="http://www.iro.umontreal.ca/~lecuyer/myftp/papers/wellrng-errata.txt">wellrng-errata.txt</a>.
+ *
+ * @see <a href="http://www.iro.umontreal.ca/~panneton/WELLRNG.html">WELL Random number
+ * generator</a>
+ * @since 2.2
+ */
+public class Well19937c extends AbstractWell {
+
+ /** Serializable version identifier. */
+ private static final long serialVersionUID = -7203498180754925124L;
+
+ /** Number of bits in the pool. */
+ private static final int K = 19937;
+
+ /** First parameter of the algorithm. */
+ private static final int M1 = 70;
+
+ /** Second parameter of the algorithm. */
+ private static final int M2 = 179;
+
+ /** Third parameter of the algorithm. */
+ private static final int M3 = 449;
+
+ /**
+ * Creates a new random number generator.
+ *
+ * <p>The instance is initialized using the current time as the seed.
+ */
+ public Well19937c() {
+ super(K, M1, M2, M3);
+ }
+
+ /**
+ * Creates a new random number generator using a single int seed.
+ *
+ * @param seed the initial seed (32 bits integer)
+ */
+ public Well19937c(int seed) {
+ super(K, M1, M2, M3, seed);
+ }
+
+ /**
+ * Creates a new random number generator using an int array seed.
+ *
+ * @param seed the initial seed (32 bits integers array), if null the seed of the generator will
+ * be related to the current time
+ */
+ public Well19937c(int[] seed) {
+ super(K, M1, M2, M3, seed);
+ }
+
+ /**
+ * Creates a new random number generator using a single long seed.
+ *
+ * @param seed the initial seed (64 bits integer)
+ */
+ public Well19937c(long seed) {
+ super(K, M1, M2, M3, seed);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ protected int next(final int bits) {
+
+ final int indexRm1 = iRm1[index];
+ final int indexRm2 = iRm2[index];
+
+ final int v0 = v[index];
+ final int vM1 = v[i1[index]];
+ final int vM2 = v[i2[index]];
+ final int vM3 = v[i3[index]];
+
+ final int z0 = (0x80000000 & v[indexRm1]) ^ (0x7FFFFFFF & v[indexRm2]);
+ final int z1 = (v0 ^ (v0 << 25)) ^ (vM1 ^ (vM1 >>> 27));
+ final int z2 = (vM2 >>> 9) ^ (vM3 ^ (vM3 >>> 1));
+ final int z3 = z1 ^ z2;
+ int z4 = z0 ^ (z1 ^ (z1 << 9)) ^ (z2 ^ (z2 << 21)) ^ (z3 ^ (z3 >>> 21));
+
+ v[index] = z3;
+ v[indexRm1] = z4;
+ v[indexRm2] &= 0x80000000;
+ index = indexRm1;
+
+ // add Matsumoto-Kurita tempering
+ // to get a maximally-equidistributed generator
+ z4 ^= (z4 << 7) & 0xe46e1700;
+ z4 ^= (z4 << 15) & 0x9b868000;
+
+ return z4 >>> (32 - bits);
+ }
+}
diff --git a/src/main/java/org/apache/commons/math3/random/Well44497a.java b/src/main/java/org/apache/commons/math3/random/Well44497a.java
new file mode 100644
index 0000000..7a4f34c
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/random/Well44497a.java
@@ -0,0 +1,115 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math3.random;
+
+/**
+ * This class implements the WELL44497a pseudo-random number generator from Fran&ccedil;ois
+ * Panneton, Pierre L'Ecuyer and Makoto Matsumoto.
+ *
+ * <p>This generator is described in a paper by Fran&ccedil;ois Panneton, Pierre L'Ecuyer and Makoto
+ * Matsumoto <a href="http://www.iro.umontreal.ca/~lecuyer/myftp/papers/wellrng.pdf">Improved
+ * Long-Period Generators Based on Linear Recurrences Modulo 2</a> ACM Transactions on Mathematical
+ * Software, 32, 1 (2006). The errata for the paper are in <a
+ * href="http://www.iro.umontreal.ca/~lecuyer/myftp/papers/wellrng-errata.txt">wellrng-errata.txt</a>.
+ *
+ * @see <a href="http://www.iro.umontreal.ca/~panneton/WELLRNG.html">WELL Random number
+ * generator</a>
+ * @since 2.2
+ */
+public class Well44497a extends AbstractWell {
+
+ /** Serializable version identifier. */
+ private static final long serialVersionUID = -3859207588353972099L;
+
+ /** Number of bits in the pool. */
+ private static final int K = 44497;
+
+ /** First parameter of the algorithm. */
+ private static final int M1 = 23;
+
+ /** Second parameter of the algorithm. */
+ private static final int M2 = 481;
+
+ /** Third parameter of the algorithm. */
+ private static final int M3 = 229;
+
+ /**
+ * Creates a new random number generator.
+ *
+ * <p>The instance is initialized using the current time as the seed.
+ */
+ public Well44497a() {
+ super(K, M1, M2, M3);
+ }
+
+ /**
+ * Creates a new random number generator using a single int seed.
+ *
+ * @param seed the initial seed (32 bits integer)
+ */
+ public Well44497a(int seed) {
+ super(K, M1, M2, M3, seed);
+ }
+
+ /**
+ * Creates a new random number generator using an int array seed.
+ *
+ * @param seed the initial seed (32 bits integers array), if null the seed of the generator will
+ * be related to the current time
+ */
+ public Well44497a(int[] seed) {
+ super(K, M1, M2, M3, seed);
+ }
+
+ /**
+ * Creates a new random number generator using a single long seed.
+ *
+ * @param seed the initial seed (64 bits integer)
+ */
+ public Well44497a(long seed) {
+ super(K, M1, M2, M3, seed);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ protected int next(final int bits) {
+
+ final int indexRm1 = iRm1[index];
+ final int indexRm2 = iRm2[index];
+
+ final int v0 = v[index];
+ final int vM1 = v[i1[index]];
+ final int vM2 = v[i2[index]];
+ final int vM3 = v[i3[index]];
+
+ // the values below include the errata of the original article
+ final int z0 = (0xFFFF8000 & v[indexRm1]) ^ (0x00007FFF & v[indexRm2]);
+ final int z1 = (v0 ^ (v0 << 24)) ^ (vM1 ^ (vM1 >>> 30));
+ final int z2 = (vM2 ^ (vM2 << 10)) ^ (vM3 << 26);
+ final int z3 = z1 ^ z2;
+ final int z2Prime = ((z2 << 9) ^ (z2 >>> 23)) & 0xfbffffff;
+ final int z2Second = ((z2 & 0x00020000) != 0) ? (z2Prime ^ 0xb729fcec) : z2Prime;
+ final int z4 = z0 ^ (z1 ^ (z1 >>> 20)) ^ z2Second ^ z3;
+
+ v[index] = z3;
+ v[indexRm1] = z4;
+ v[indexRm2] &= 0xFFFF8000;
+ index = indexRm1;
+
+ return z4 >>> (32 - bits);
+ }
+}
diff --git a/src/main/java/org/apache/commons/math3/random/Well44497b.java b/src/main/java/org/apache/commons/math3/random/Well44497b.java
new file mode 100644
index 0000000..9da51a5
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/random/Well44497b.java
@@ -0,0 +1,122 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math3.random;
+
+/**
+ * This class implements the WELL44497b pseudo-random number generator from Fran&ccedil;ois
+ * Panneton, Pierre L'Ecuyer and Makoto Matsumoto.
+ *
+ * <p>This generator is described in a paper by Fran&ccedil;ois Panneton, Pierre L'Ecuyer and Makoto
+ * Matsumoto <a href="http://www.iro.umontreal.ca/~lecuyer/myftp/papers/wellrng.pdf">Improved
+ * Long-Period Generators Based on Linear Recurrences Modulo 2</a> ACM Transactions on Mathematical
+ * Software, 32, 1 (2006). The errata for the paper are in <a
+ * href="http://www.iro.umontreal.ca/~lecuyer/myftp/papers/wellrng-errata.txt">wellrng-errata.txt</a>.
+ *
+ * @see <a href="http://www.iro.umontreal.ca/~panneton/WELLRNG.html">WELL Random number
+ * generator</a>
+ * @since 2.2
+ */
+public class Well44497b extends AbstractWell {
+
+ /** Serializable version identifier. */
+ private static final long serialVersionUID = 4032007538246675492L;
+
+ /** Number of bits in the pool. */
+ private static final int K = 44497;
+
+ /** First parameter of the algorithm. */
+ private static final int M1 = 23;
+
+ /** Second parameter of the algorithm. */
+ private static final int M2 = 481;
+
+ /** Third parameter of the algorithm. */
+ private static final int M3 = 229;
+
+ /**
+ * Creates a new random number generator.
+ *
+ * <p>The instance is initialized using the current time as the seed.
+ */
+ public Well44497b() {
+ super(K, M1, M2, M3);
+ }
+
+ /**
+ * Creates a new random number generator using a single int seed.
+ *
+ * @param seed the initial seed (32 bits integer)
+ */
+ public Well44497b(int seed) {
+ super(K, M1, M2, M3, seed);
+ }
+
+ /**
+ * Creates a new random number generator using an int array seed.
+ *
+ * @param seed the initial seed (32 bits integers array), if null the seed of the generator will
+ * be related to the current time
+ */
+ public Well44497b(int[] seed) {
+ super(K, M1, M2, M3, seed);
+ }
+
+ /**
+ * Creates a new random number generator using a single long seed.
+ *
+ * @param seed the initial seed (64 bits integer)
+ */
+ public Well44497b(long seed) {
+ super(K, M1, M2, M3, seed);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ protected int next(final int bits) {
+
+ // compute raw value given by WELL44497a generator
+ // which is NOT maximally-equidistributed
+ final int indexRm1 = iRm1[index];
+ final int indexRm2 = iRm2[index];
+
+ final int v0 = v[index];
+ final int vM1 = v[i1[index]];
+ final int vM2 = v[i2[index]];
+ final int vM3 = v[i3[index]];
+
+ // the values below include the errata of the original article
+ final int z0 = (0xFFFF8000 & v[indexRm1]) ^ (0x00007FFF & v[indexRm2]);
+ final int z1 = (v0 ^ (v0 << 24)) ^ (vM1 ^ (vM1 >>> 30));
+ final int z2 = (vM2 ^ (vM2 << 10)) ^ (vM3 << 26);
+ final int z3 = z1 ^ z2;
+ final int z2Prime = ((z2 << 9) ^ (z2 >>> 23)) & 0xfbffffff;
+ final int z2Second = ((z2 & 0x00020000) != 0) ? (z2Prime ^ 0xb729fcec) : z2Prime;
+ int z4 = z0 ^ (z1 ^ (z1 >>> 20)) ^ z2Second ^ z3;
+
+ v[index] = z3;
+ v[indexRm1] = z4;
+ v[indexRm2] &= 0xFFFF8000;
+ index = indexRm1;
+
+ // add Matsumoto-Kurita tempering
+ // to get a maximally-equidistributed generator
+ z4 ^= (z4 << 7) & 0x93dd1400;
+ z4 ^= (z4 << 15) & 0xfa118000;
+
+ return z4 >>> (32 - bits);
+ }
+}
diff --git a/src/main/java/org/apache/commons/math3/random/Well512a.java b/src/main/java/org/apache/commons/math3/random/Well512a.java
new file mode 100644
index 0000000..d09cbb1
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/random/Well512a.java
@@ -0,0 +1,111 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math3.random;
+
+/**
+ * This class implements the WELL512a pseudo-random number generator from Fran&ccedil;ois Panneton,
+ * Pierre L'Ecuyer and Makoto Matsumoto.
+ *
+ * <p>This generator is described in a paper by Fran&ccedil;ois Panneton, Pierre L'Ecuyer and Makoto
+ * Matsumoto <a href="http://www.iro.umontreal.ca/~lecuyer/myftp/papers/wellrng.pdf">Improved
+ * Long-Period Generators Based on Linear Recurrences Modulo 2</a> ACM Transactions on Mathematical
+ * Software, 32, 1 (2006). The errata for the paper are in <a
+ * href="http://www.iro.umontreal.ca/~lecuyer/myftp/papers/wellrng-errata.txt">wellrng-errata.txt</a>.
+ *
+ * @see <a href="http://www.iro.umontreal.ca/~panneton/WELLRNG.html">WELL Random number
+ * generator</a>
+ * @since 2.2
+ */
+public class Well512a extends AbstractWell {
+
+ /** Serializable version identifier. */
+ private static final long serialVersionUID = -6104179812103820574L;
+
+ /** Number of bits in the pool. */
+ private static final int K = 512;
+
+ /** First parameter of the algorithm. */
+ private static final int M1 = 13;
+
+ /** Second parameter of the algorithm. */
+ private static final int M2 = 9;
+
+ /** Third parameter of the algorithm. */
+ private static final int M3 = 5;
+
+ /**
+ * Creates a new random number generator.
+ *
+ * <p>The instance is initialized using the current time as the seed.
+ */
+ public Well512a() {
+ super(K, M1, M2, M3);
+ }
+
+ /**
+ * Creates a new random number generator using a single int seed.
+ *
+ * @param seed the initial seed (32 bits integer)
+ */
+ public Well512a(int seed) {
+ super(K, M1, M2, M3, seed);
+ }
+
+ /**
+ * Creates a new random number generator using an int array seed.
+ *
+ * @param seed the initial seed (32 bits integers array), if null the seed of the generator will
+ * be related to the current time
+ */
+ public Well512a(int[] seed) {
+ super(K, M1, M2, M3, seed);
+ }
+
+ /**
+ * Creates a new random number generator using a single long seed.
+ *
+ * @param seed the initial seed (64 bits integer)
+ */
+ public Well512a(long seed) {
+ super(K, M1, M2, M3, seed);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ protected int next(final int bits) {
+
+ final int indexRm1 = iRm1[index];
+
+ final int vi = v[index];
+ final int vi1 = v[i1[index]];
+ final int vi2 = v[i2[index]];
+ final int z0 = v[indexRm1];
+
+ // the values below include the errata of the original article
+ final int z1 = (vi ^ (vi << 16)) ^ (vi1 ^ (vi1 << 15));
+ final int z2 = vi2 ^ (vi2 >>> 11);
+ final int z3 = z1 ^ z2;
+ final int z4 =
+ (z0 ^ (z0 << 2)) ^ (z1 ^ (z1 << 18)) ^ (z2 << 28) ^ (z3 ^ ((z3 << 5) & 0xda442d24));
+
+ v[index] = z3;
+ v[indexRm1] = z4;
+ index = indexRm1;
+
+ return z4 >>> (32 - bits);
+ }
+}
diff --git a/src/main/java/org/apache/commons/math3/random/package-info.java b/src/main/java/org/apache/commons/math3/random/package-info.java
new file mode 100644
index 0000000..212b018
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/random/package-info.java
@@ -0,0 +1,115 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/**
+ * Random number and random data generators.
+ *
+ * <p>Commons-math provides a few pseudo random number generators. The top level interface is
+ * RandomGenerator. It is implemented by three classes:
+ *
+ * <ul>
+ * <li>{@link org.apache.commons.math3.random.JDKRandomGenerator JDKRandomGenerator} that extends
+ * the JDK provided generator
+ * <li>AbstractRandomGenerator as a helper for users generators
+ * <li>BitStreamGenerator which is an abstract class for several generators and which in turn is
+ * extended by:
+ * <ul>
+ * <li>{@link org.apache.commons.math3.random.MersenneTwister MersenneTwister}
+ * <li>{@link org.apache.commons.math3.random.Well512a Well512a}
+ * <li>{@link org.apache.commons.math3.random.Well1024a Well1024a}
+ * <li>{@link org.apache.commons.math3.random.Well19937a Well19937a}
+ * <li>{@link org.apache.commons.math3.random.Well19937c Well19937c}
+ * <li>{@link org.apache.commons.math3.random.Well44497a Well44497a}
+ * <li>{@link org.apache.commons.math3.random.Well44497b Well44497b}
+ * </ul>
+ * </ul>
+ *
+ * <p>The JDK provided generator is a simple one that can be used only for very simple needs. The
+ * Mersenne Twister is a fast generator with very good properties well suited for Monte-Carlo
+ * simulation. It is equidistributed for generating vectors up to dimension 623 and has a huge
+ * period: 2<sup>19937</sup> - 1 (which is a Mersenne prime). This generator is described in a paper
+ * by Makoto Matsumoto and Takuji Nishimura in 1998: <a
+ * href="http://www.math.sci.hiroshima-u.ac.jp/~m-mat/MT/ARTICLES/mt.pdf">Mersenne Twister: A
+ * 623-Dimensionally Equidistributed Uniform Pseudo-Random Number Generator</a>, ACM Transactions on
+ * Modeling and Computer Simulation, Vol. 8, No. 1, January 1998, pp 3--30. The WELL generators are
+ * a family of generators with period ranging from 2<sup>512</sup> - 1 to 2<sup>44497</sup> - 1
+ * (this last one is also a Mersenne prime) with even better properties than Mersenne Twister. These
+ * generators are described in a paper by Fran&ccedil;ois Panneton, Pierre L'Ecuyer and Makoto
+ * Matsumoto <a href="http://www.iro.umontreal.ca/~lecuyer/myftp/papers/wellrng.pdf">Improved
+ * Long-Period Generators Based on Linear Recurrences Modulo 2</a> ACM Transactions on Mathematical
+ * Software, 32, 1 (2006). The errata for the paper are in <a
+ * href="http://www.iro.umontreal.ca/~lecuyer/myftp/papers/wellrng-errata.txt">wellrng-errata.txt</a>.
+ *
+ * <p>For simple sampling, any of these generators is sufficient. For Monte-Carlo simulations the
+ * JDK generator does not have any of the good mathematical properties of the other generators, so
+ * it should be avoided. The Mersenne twister and WELL generators have equidistribution properties
+ * proven according to their bits pool size which is directly linked to their period (all of them
+ * have maximal period, i.e. a generator with size n pool has a period 2<sup>n</sup>-1). They also
+ * have equidistribution properties for 32 bits blocks up to s/32 dimension where s is their pool
+ * size. So WELL19937c for exemple is equidistributed up to dimension 623 (19937/32). This means a
+ * Monte-Carlo simulation generating a vector of n variables at each iteration has some guarantees
+ * on the properties of the vector as long as its dimension does not exceed the limit. However,
+ * since we use bits from two successive 32 bits generated integers to create one double, this limit
+ * is smaller when the variables are of type double. so for Monte-Carlo simulation where less the 16
+ * doubles are generated at each round, WELL1024 may be sufficient. If a larger number of doubles
+ * are needed a generator with a larger pool would be useful.
+ *
+ * <p>The WELL generators are more modern then MersenneTwister (the paper describing than has been
+ * published in 2006 instead of 1998) and fix some of its (few) drawbacks. If initialization array
+ * contains many zero bits, MersenneTwister may take a very long time (several hundreds of thousands
+ * of iterations to reach a steady state with a balanced number of zero and one in its bits pool).
+ * So the WELL generators are better to <i>escape zeroland</i> as explained by the WELL generators
+ * creators. The Well19937a and Well44497a generator are not maximally equidistributed (i.e. there
+ * are some dimensions or bits blocks size for which they are not equidistributed). The Well512a,
+ * Well1024a, Well19937c and Well44497b are maximally equidistributed for blocks size up to 32 bits
+ * (they should behave correctly also for double based on more than 32 bits blocks, but
+ * equidistribution is not proven at these blocks sizes).
+ *
+ * <p>The MersenneTwister generator uses a 624 elements integer array, so it consumes less than 2.5
+ * kilobytes. The WELL generators use 6 integer arrays with a size equal to the pool size, so for
+ * example the WELL44497b generator uses about 33 kilobytes. This may be important if a very large
+ * number of generator instances were used at the same time.
+ *
+ * <p>All generators are quite fast. As an example, here are some comparisons, obtained on a 64 bits
+ * JVM on a linux computer with a 2008 processor (AMD phenom Quad 9550 at 2.2 GHz). The generation
+ * rate for MersenneTwister was about 27 millions doubles per second (remember we generate two 32
+ * bits integers for each double). Generation rates for other PRNG, relative to MersenneTwister:
+ *
+ * <p>
+ *
+ * <table border="1" align="center">
+ * <tr BGCOLOR="#CCCCFF"><td colspan="2"><font size="+2">Example of performances</font></td></tr>
+ * <tr BGCOLOR="#EEEEFF"><font size="+1"><td>Name</td><td>generation rate (relative to MersenneTwister)</td></font></tr>
+ * <tr><td>{@link org.apache.commons.math3.random.MersenneTwister MersenneTwister}</td><td>1</td></tr>
+ * <tr><td>{@link org.apache.commons.math3.random.JDKRandomGenerator JDKRandomGenerator}</td><td>between 0.96 and 1.16</td></tr>
+ * <tr><td>{@link org.apache.commons.math3.random.Well512a Well512a}</td><td>between 0.85 and 0.88</td></tr>
+ * <tr><td>{@link org.apache.commons.math3.random.Well1024a Well1024a}</td><td>between 0.63 and 0.73</td></tr>
+ * <tr><td>{@link org.apache.commons.math3.random.Well19937a Well19937a}</td><td>between 0.70 and 0.71</td></tr>
+ * <tr><td>{@link org.apache.commons.math3.random.Well19937c Well19937c}</td><td>between 0.57 and 0.71</td></tr>
+ * <tr><td>{@link org.apache.commons.math3.random.Well44497a Well44497a}</td><td>between 0.69 and 0.71</td></tr>
+ * <tr><td>{@link org.apache.commons.math3.random.Well44497b Well44497b}</td><td>between 0.65 and 0.71</td></tr>
+ * </table>
+ *
+ * <p>So for most simulation problems, the better generators like {@link
+ * org.apache.commons.math3.random.Well19937c Well19937c} and {@link
+ * org.apache.commons.math3.random.Well44497b Well44497b} are probably very good choices.
+ *
+ * <p>Note that <em>none</em> of these generators are suitable for cryptography. They are devoted to
+ * simulation, and to generate very long series with strong properties on the series as a whole
+ * (equidistribution, no correlation ...). They do not attempt to create small series but with very
+ * strong properties of unpredictability as needed in cryptography.
+ */
+package org.apache.commons.math3.random;
diff --git a/src/main/java/org/apache/commons/math3/special/BesselJ.java b/src/main/java/org/apache/commons/math3/special/BesselJ.java
new file mode 100644
index 0000000..61aa4ff
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/special/BesselJ.java
@@ -0,0 +1,661 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math3.special;
+
+import org.apache.commons.math3.analysis.UnivariateFunction;
+import org.apache.commons.math3.exception.ConvergenceException;
+import org.apache.commons.math3.exception.MathIllegalArgumentException;
+import org.apache.commons.math3.exception.util.LocalizedFormats;
+import org.apache.commons.math3.util.FastMath;
+import org.apache.commons.math3.util.MathArrays;
+
+/**
+ * This class provides computation methods related to Bessel functions of the first kind. Detailed
+ * descriptions of these functions are available in <a
+ * href="http://en.wikipedia.org/wiki/Bessel_function">Wikipedia</a>, <a
+ * href="http://en.wikipedia.org/wiki/Abramowitz_and_Stegun">Abrabowitz and Stegun</a> (Ch. 9-11),
+ * and <a href="http://dlmf.nist.gov/">DLMF</a> (Ch. 10).
+ *
+ * <p>This implementation is based on the rjbesl Fortran routine at <a
+ * href="http://www.netlib.org/specfun/rjbesl">Netlib</a>.
+ *
+ * <p>From the Fortran code:
+ *
+ * <p>This program is based on a program written by David J. Sookne (2) that computes values of the
+ * Bessel functions J or I of real argument and integer order. Modifications include the restriction
+ * of the computation to the J Bessel function of non-negative real argument, the extension of the
+ * computation to arbitrary positive order, and the elimination of most underflow.
+ *
+ * <p>References:
+ *
+ * <ul>
+ * <li>"A Note on Backward Recurrence Algorithms," Olver, F. W. J., and Sookne, D. J., Math. Comp.
+ * 26, 1972, pp 941-947.
+ * <li>"Bessel Functions of Real Argument and Integer Order," Sookne, D. J., NBS Jour. of Res. B.
+ * 77B, 1973, pp 125-132.
+ * </ul>
+ *
+ * @since 3.4
+ */
+public class BesselJ implements UnivariateFunction {
+
+ // ---------------------------------------------------------------------
+ // Mathematical constants
+ // ---------------------------------------------------------------------
+
+ /** -2 / pi */
+ private static final double PI2 = 0.636619772367581343075535;
+
+ /** first few significant digits of 2pi */
+ private static final double TOWPI1 = 6.28125;
+
+ /** 2pi - TWOPI1 to working precision */
+ private static final double TWOPI2 = 1.935307179586476925286767e-3;
+
+ /** TOWPI1 + TWOPI2 */
+ private static final double TWOPI = TOWPI1 + TWOPI2;
+
+ // ---------------------------------------------------------------------
+ // Machine-dependent parameters
+ // ---------------------------------------------------------------------
+
+ /**
+ * 10.0^K, where K is the largest integer such that ENTEN is machine-representable in working
+ * precision
+ */
+ private static final double ENTEN = 1.0e308;
+
+ /**
+ * Decimal significance desired. Should be set to (INT(log_{10}(2) * (it)+1)). Setting NSIG
+ * lower will result in decreased accuracy while setting NSIG higher will increase CPU time
+ * without increasing accuracy. The truncation error is limited to a relative error of
+ * T=.5(10^(-NSIG)).
+ */
+ private static final double ENSIG = 1.0e16;
+
+ /** 10.0 ** (-K) for the smallest integer K such that K >= NSIG/4 */
+ private static final double RTNSIG = 1.0e-4;
+
+ /** Smallest ABS(X) such that X/4 does not underflow */
+ private static final double ENMTEN = 8.90e-308;
+
+ /** Minimum acceptable value for x */
+ private static final double X_MIN = 0.0;
+
+ /**
+ * Upper limit on the magnitude of x. If abs(x) = n, then at least n iterations of the backward
+ * recursion will be executed. The value of 10.0 ** 4 is used on every machine.
+ */
+ private static final double X_MAX = 1.0e4;
+
+ /** First 25 factorials as doubles */
+ private static final double[] FACT = {
+ 1.0,
+ 1.0,
+ 2.0,
+ 6.0,
+ 24.0,
+ 120.0,
+ 720.0,
+ 5040.0,
+ 40320.0,
+ 362880.0,
+ 3628800.0,
+ 39916800.0,
+ 479001600.0,
+ 6227020800.0,
+ 87178291200.0,
+ 1.307674368e12,
+ 2.0922789888e13,
+ 3.55687428096e14,
+ 6.402373705728e15,
+ 1.21645100408832e17,
+ 2.43290200817664e18,
+ 5.109094217170944e19,
+ 1.12400072777760768e21,
+ 2.585201673888497664e22,
+ 6.2044840173323943936e23
+ };
+
+ /** Order of the function computed when {@link #value(double)} is used */
+ private final double order;
+
+ /**
+ * Create a new BesselJ with the given order.
+ *
+ * @param order order of the function computed when using {@link #value(double)}.
+ */
+ public BesselJ(double order) {
+ this.order = order;
+ }
+
+ /**
+ * Returns the value of the constructed Bessel function of the first kind, for the passed
+ * argument.
+ *
+ * @param x Argument
+ * @return Value of the Bessel function at x
+ * @throws MathIllegalArgumentException if {@code x} is too large relative to {@code order}
+ * @throws ConvergenceException if the algorithm fails to converge
+ */
+ public double value(double x) throws MathIllegalArgumentException, ConvergenceException {
+ return BesselJ.value(order, x);
+ }
+
+ /**
+ * Returns the first Bessel function, \(J_{order}(x)\).
+ *
+ * @param order Order of the Bessel function
+ * @param x Argument
+ * @return Value of the Bessel function of the first kind, \(J_{order}(x)\)
+ * @throws MathIllegalArgumentException if {@code x} is too large relative to {@code order}
+ * @throws ConvergenceException if the algorithm fails to converge
+ */
+ public static double value(double order, double x)
+ throws MathIllegalArgumentException, ConvergenceException {
+ final int n = (int) order;
+ final double alpha = order - n;
+ final int nb = n + 1;
+ final BesselJResult res = rjBesl(x, alpha, nb);
+
+ if (res.nVals >= nb) {
+ return res.vals[n];
+ } else if (res.nVals < 0) {
+ throw new MathIllegalArgumentException(
+ LocalizedFormats.BESSEL_FUNCTION_BAD_ARGUMENT, order, x);
+ } else if (FastMath.abs(res.vals[res.nVals - 1]) < 1e-100) {
+ return res.vals[n]; // underflow; return value (will be zero)
+ }
+ throw new ConvergenceException(
+ LocalizedFormats.BESSEL_FUNCTION_FAILED_CONVERGENCE, order, x);
+ }
+
+ /**
+ * Encapsulates the results returned by {@link BesselJ#rjBesl(double, double, int)}.
+ *
+ * <p>{@link #getVals()} returns the computed function values. {@link #getnVals()} is the number
+ * of values among those returned by {@link #getnVals()} that can be considered accurate.
+ *
+ * <p>
+ *
+ * <ul>
+ * <li>nVals < 0: An argument is out of range. For example, nb <= 0, alpha < 0 or > 1, or x is
+ * too large. In this case, b(0) is set to zero, the remainder of the b-vector is not
+ * calculated, and nVals is set to MIN(nb,0) - 1 so that nVals != nb.
+ * <li>nb > nVals > 0: Not all requested function values could be calculated accurately. This
+ * usually occurs because nb is much larger than abs(x). In this case, b(n) is calculated
+ * to the desired accuracy for n < nVals, but precision is lost for nVals < n <= nb. If
+ * b(n) does not vanish for n > nVals (because it is too small to be represented), and
+ * b(n)/b(nVals) = \(10^{-k}\), then only the first NSIG-k significant figures of b(n) can
+ * be trusted.
+ * </ul>
+ */
+ public static class BesselJResult {
+
+ /** Bessel function values */
+ private final double[] vals;
+
+ /** Valid value count */
+ private final int nVals;
+
+ /**
+ * Create a new BesselJResult with the given values and valid value count.
+ *
+ * @param b values
+ * @param n count of valid values
+ */
+ public BesselJResult(double[] b, int n) {
+ vals = MathArrays.copyOf(b, b.length);
+ nVals = n;
+ }
+
+ /**
+ * @return the computed function values
+ */
+ public double[] getVals() {
+ return MathArrays.copyOf(vals, vals.length);
+ }
+
+ /**
+ * @return the number of valid function values (normally the same as the length of the array
+ * returned by {@link #getnVals()})
+ */
+ public int getnVals() {
+ return nVals;
+ }
+ }
+
+ /**
+ * Calculates Bessel functions \(J_{n+alpha}(x)\) for non-negative argument x, and non-negative
+ * order n + alpha.
+ *
+ * <p>Before using the output vector, the user should check that nVals = nb, i.e., all orders
+ * have been calculated to the desired accuracy. See BesselResult class javadoc for details on
+ * return values.
+ *
+ * @param x non-negative real argument for which J's are to be calculated
+ * @param alpha fractional part of order for which J's or exponentially scaled J's (\(J\cdot
+ * e^{x}\)) are to be calculated. 0 <= alpha < 1.0.
+ * @param nb integer number of functions to be calculated, nb > 0. The first function calculated
+ * is of order alpha, and the last is of order nb - 1 + alpha.
+ * @return BesselJResult a vector of the functions \(J_{alpha}(x)\) through
+ * \(J_{nb-1+alpha}(x)\), or the corresponding exponentially scaled functions and an integer
+ * output variable indicating possible errors
+ */
+ public static BesselJResult rjBesl(double x, double alpha, int nb) {
+ final double[] b = new double[nb];
+
+ int ncalc = 0;
+ double alpem = 0;
+ double alp2em = 0;
+
+ // ---------------------------------------------------------------------
+ // Check for out of range arguments.
+ // ---------------------------------------------------------------------
+ final int magx = (int) x;
+ if ((nb > 0) && (x >= X_MIN) && (x <= X_MAX) && (alpha >= 0) && (alpha < 1)) {
+ // ---------------------------------------------------------------------
+ // Initialize result array to zero.
+ // ---------------------------------------------------------------------
+ ncalc = nb;
+ for (int i = 0; i < nb; ++i) {
+ b[i] = 0;
+ }
+
+ // ---------------------------------------------------------------------
+ // Branch to use 2-term ascending series for small X and asymptotic
+ // form for large X when NB is not too large.
+ // ---------------------------------------------------------------------
+ double tempa;
+ double tempb;
+ double tempc;
+ double tover;
+ if (x < RTNSIG) {
+ // ---------------------------------------------------------------------
+ // Two-term ascending series for small X.
+ // ---------------------------------------------------------------------
+ tempa = 1;
+ alpem = 1 + alpha;
+ double halfx = 0;
+ if (x > ENMTEN) {
+ halfx = 0.5 * x;
+ }
+ if (alpha != 0) {
+ tempa = FastMath.pow(halfx, alpha) / (alpha * Gamma.gamma(alpha));
+ }
+ tempb = 0;
+ if (x + 1 > 1) {
+ tempb = -halfx * halfx;
+ }
+ b[0] = tempa + (tempa * tempb / alpem);
+ if ((x != 0) && (b[0] == 0)) {
+ ncalc = 0;
+ }
+ if (nb != 1) {
+ if (x <= 0) {
+ for (int n = 1; n < nb; ++n) {
+ b[n] = 0;
+ }
+ } else {
+ // ---------------------------------------------------------------------
+ // Calculate higher order functions.
+ // ---------------------------------------------------------------------
+ tempc = halfx;
+ tover = tempb != 0 ? ENMTEN / tempb : 2 * ENMTEN / x;
+ for (int n = 1; n < nb; ++n) {
+ tempa /= alpem;
+ alpem += 1;
+ tempa *= tempc;
+ if (tempa <= tover * alpem) {
+ tempa = 0;
+ }
+ b[n] = tempa + (tempa * tempb / alpem);
+ if ((b[n] == 0) && (ncalc > n)) {
+ ncalc = n;
+ }
+ }
+ }
+ }
+ } else if ((x > 25.0) && (nb <= magx + 1)) {
+ // ---------------------------------------------------------------------
+ // Asymptotic series for X > 25
+ // ---------------------------------------------------------------------
+ final double xc = FastMath.sqrt(PI2 / x);
+ final double mul = 0.125 / x;
+ final double xin = mul * mul;
+ int m = 0;
+ if (x >= 130.0) {
+ m = 4;
+ } else if (x >= 35.0) {
+ m = 8;
+ } else {
+ m = 11;
+ }
+
+ final double xm = 4.0 * m;
+ // ---------------------------------------------------------------------
+ // Argument reduction for SIN and COS routines.
+ // ---------------------------------------------------------------------
+ double t = (double) ((int) ((x / TWOPI) + 0.5));
+ final double z = x - t * TOWPI1 - t * TWOPI2 - (alpha + 0.5) / PI2;
+ double vsin = FastMath.sin(z);
+ double vcos = FastMath.cos(z);
+ double gnu = 2 * alpha;
+ double capq;
+ double capp;
+ double s;
+ double t1;
+ double xk;
+ for (int i = 1; i <= 2; i++) {
+ s = (xm - 1 - gnu) * (xm - 1 + gnu) * xin * 0.5;
+ t = (gnu - (xm - 3.0)) * (gnu + (xm - 3.0));
+ capp = (s * t) / FACT[2 * m];
+ t1 = (gnu - (xm + 1)) * (gnu + (xm + 1));
+ capq = (s * t1) / FACT[2 * m + 1];
+ xk = xm;
+ int k = 2 * m;
+ t1 = t;
+
+ for (int j = 2; j <= m; j++) {
+ xk -= 4.0;
+ s = (xk - 1 - gnu) * (xk - 1 + gnu);
+ t = (gnu - (xk - 3.0)) * (gnu + (xk - 3.0));
+ capp = (capp + 1 / FACT[k - 2]) * s * t * xin;
+ capq = (capq + 1 / FACT[k - 1]) * s * t1 * xin;
+ k -= 2;
+ t1 = t;
+ }
+
+ capp += 1;
+ capq = (capq + 1) * ((gnu * gnu) - 1) * (0.125 / x);
+ b[i - 1] = xc * (capp * vcos - capq * vsin);
+ if (nb == 1) {
+ return new BesselJResult(MathArrays.copyOf(b, b.length), ncalc);
+ }
+ t = vsin;
+ vsin = -vcos;
+ vcos = t;
+ gnu += 2.0;
+ }
+
+ // ---------------------------------------------------------------------
+ // If NB > 2, compute J(X,ORDER+I) I = 2, NB-1
+ // ---------------------------------------------------------------------
+ if (nb > 2) {
+ gnu = 2 * alpha + 2.0;
+ for (int j = 2; j < nb; ++j) {
+ b[j] = gnu * b[j - 1] / x - b[j - 2];
+ gnu += 2.0;
+ }
+ }
+ } else {
+ // ---------------------------------------------------------------------
+ // Use recurrence to generate results. First initialize the
+ // calculation of P*S.
+ // ---------------------------------------------------------------------
+ final int nbmx = nb - magx;
+ int n = magx + 1;
+ int nstart = 0;
+ int nend = 0;
+ double en = 2 * (n + alpha);
+ double plast = 1;
+ double p = en / x;
+ double pold;
+ // ---------------------------------------------------------------------
+ // Calculate general significance test.
+ // ---------------------------------------------------------------------
+ double test = 2 * ENSIG;
+ boolean readyToInitialize = false;
+ if (nbmx >= 3) {
+ // ---------------------------------------------------------------------
+ // Calculate P*S until N = NB-1. Check for possible
+ // overflow.
+ // ---------------------------------------------------------------------
+ tover = ENTEN / ENSIG;
+ nstart = magx + 2;
+ nend = nb - 1;
+ en = 2 * (nstart - 1 + alpha);
+ double psave;
+ double psavel;
+ for (int k = nstart; k <= nend; k++) {
+ n = k;
+ en += 2.0;
+ pold = plast;
+ plast = p;
+ p = (en * plast / x) - pold;
+ if (p > tover) {
+ // ---------------------------------------------------------------------
+ // To avoid overflow, divide P*S by TOVER. Calculate
+ // P*S until
+ // ABS(P) > 1.
+ // ---------------------------------------------------------------------
+ tover = ENTEN;
+ p /= tover;
+ plast /= tover;
+ psave = p;
+ psavel = plast;
+ nstart = n + 1;
+ do {
+ n += 1;
+ en += 2.0;
+ pold = plast;
+ plast = p;
+ p = (en * plast / x) - pold;
+ } while (p <= 1);
+ tempb = en / x;
+ // ---------------------------------------------------------------------
+ // Calculate backward test and find NCALC, the
+ // highest N such that
+ // the test is passed.
+ // ---------------------------------------------------------------------
+ test = pold * plast * (0.5 - 0.5 / (tempb * tempb));
+ test /= ENSIG;
+ p = plast * tover;
+ n -= 1;
+ en -= 2.0;
+ nend = FastMath.min(nb, n);
+ for (int l = nstart; l <= nend; l++) {
+ pold = psavel;
+ psavel = psave;
+ psave = (en * psavel / x) - pold;
+ if (psave * psavel > test) {
+ ncalc = l - 1;
+ readyToInitialize = true;
+ break;
+ }
+ }
+ ncalc = nend;
+ readyToInitialize = true;
+ break;
+ }
+ }
+ if (!readyToInitialize) {
+ n = nend;
+ en = 2 * (n + alpha);
+ // ---------------------------------------------------------------------
+ // Calculate special significance test for NBMX > 2.
+ // ---------------------------------------------------------------------
+ test =
+ FastMath.max(
+ test, FastMath.sqrt(plast * ENSIG) * FastMath.sqrt(2 * p));
+ }
+ }
+ // ---------------------------------------------------------------------
+ // Calculate P*S until significance test passes.
+ // ---------------------------------------------------------------------
+ if (!readyToInitialize) {
+ do {
+ n += 1;
+ en += 2.0;
+ pold = plast;
+ plast = p;
+ p = (en * plast / x) - pold;
+ } while (p < test);
+ }
+ // ---------------------------------------------------------------------
+ // Initialize the backward recursion and the normalization sum.
+ // ---------------------------------------------------------------------
+ n += 1;
+ en += 2.0;
+ tempb = 0;
+ tempa = 1 / p;
+ int m = (2 * n) - 4 * (n / 2);
+ double sum = 0;
+ double em = (double) (n / 2);
+ alpem = em - 1 + alpha;
+ alp2em = 2 * em + alpha;
+ if (m != 0) {
+ sum = tempa * alpem * alp2em / em;
+ }
+ nend = n - nb;
+
+ boolean readyToNormalize = false;
+ boolean calculatedB0 = false;
+
+ // ---------------------------------------------------------------------
+ // Recur backward via difference equation, calculating (but not
+ // storing) B(N), until N = NB.
+ // ---------------------------------------------------------------------
+ for (int l = 1; l <= nend; l++) {
+ n -= 1;
+ en -= 2.0;
+ tempc = tempb;
+ tempb = tempa;
+ tempa = (en * tempb / x) - tempc;
+ m = 2 - m;
+ if (m != 0) {
+ em -= 1;
+ alp2em = 2 * em + alpha;
+ if (n == 1) {
+ break;
+ }
+ alpem = em - 1 + alpha;
+ if (alpem == 0) {
+ alpem = 1;
+ }
+ sum = (sum + tempa * alp2em) * alpem / em;
+ }
+ }
+
+ // ---------------------------------------------------------------------
+ // Store B(NB).
+ // ---------------------------------------------------------------------
+ b[n - 1] = tempa;
+ if (nend >= 0) {
+ if (nb <= 1) {
+ alp2em = alpha;
+ if (alpha + 1 == 1) {
+ alp2em = 1;
+ }
+ sum += b[0] * alp2em;
+ readyToNormalize = true;
+ } else {
+ // ---------------------------------------------------------------------
+ // Calculate and store B(NB-1).
+ // ---------------------------------------------------------------------
+ n -= 1;
+ en -= 2.0;
+ b[n - 1] = (en * tempa / x) - tempb;
+ if (n == 1) {
+ calculatedB0 = true;
+ } else {
+ m = 2 - m;
+ if (m != 0) {
+ em -= 1;
+ alp2em = 2 * em + alpha;
+ alpem = em - 1 + alpha;
+ if (alpem == 0) {
+ alpem = 1;
+ }
+
+ sum = (sum + (b[n - 1] * alp2em)) * alpem / em;
+ }
+ }
+ }
+ }
+ if (!readyToNormalize && !calculatedB0) {
+ nend = n - 2;
+ if (nend != 0) {
+ // ---------------------------------------------------------------------
+ // Calculate via difference equation and store B(N),
+ // until N = 2.
+ // ---------------------------------------------------------------------
+
+ for (int l = 1; l <= nend; l++) {
+ n -= 1;
+ en -= 2.0;
+ b[n - 1] = (en * b[n] / x) - b[n + 1];
+ m = 2 - m;
+ if (m != 0) {
+ em -= 1;
+ alp2em = 2 * em + alpha;
+ alpem = em - 1 + alpha;
+ if (alpem == 0) {
+ alpem = 1;
+ }
+
+ sum = (sum + b[n - 1] * alp2em) * alpem / em;
+ }
+ }
+ }
+ }
+ // ---------------------------------------------------------------------
+ // Calculate b[0]
+ // ---------------------------------------------------------------------
+ if (!readyToNormalize) {
+ if (!calculatedB0) {
+ b[0] = 2.0 * (alpha + 1) * b[1] / x - b[2];
+ }
+ em -= 1;
+ alp2em = 2 * em + alpha;
+ if (alp2em == 0) {
+ alp2em = 1;
+ }
+ sum += b[0] * alp2em;
+ }
+ // ---------------------------------------------------------------------
+ // Normalize. Divide all B(N) by sum.
+ // ---------------------------------------------------------------------
+
+ if (FastMath.abs(alpha) > 1e-16) {
+ sum *= Gamma.gamma(alpha) * FastMath.pow(x * 0.5, -alpha);
+ }
+ tempa = ENMTEN;
+ if (sum > 1) {
+ tempa *= sum;
+ }
+
+ for (n = 0; n < nb; n++) {
+ if (FastMath.abs(b[n]) < tempa) {
+ b[n] = 0;
+ }
+ b[n] /= sum;
+ }
+ }
+ // ---------------------------------------------------------------------
+ // Error return -- X, NB, or ALPHA is out of range.
+ // ---------------------------------------------------------------------
+ } else {
+ if (b.length > 0) {
+ b[0] = 0;
+ }
+ ncalc = FastMath.min(nb, 0) - 1;
+ }
+ return new BesselJResult(MathArrays.copyOf(b, b.length), ncalc);
+ }
+}
diff --git a/src/main/java/org/apache/commons/math3/special/Beta.java b/src/main/java/org/apache/commons/math3/special/Beta.java
new file mode 100644
index 0000000..2f6b6da
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/special/Beta.java
@@ -0,0 +1,478 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math3.special;
+
+import org.apache.commons.math3.exception.NumberIsTooSmallException;
+import org.apache.commons.math3.exception.OutOfRangeException;
+import org.apache.commons.math3.util.ContinuedFraction;
+import org.apache.commons.math3.util.FastMath;
+
+/**
+ * This is a utility class that provides computation methods related to the Beta family of
+ * functions.
+ *
+ * <p>Implementation of {@link #logBeta(double, double)} is based on the algorithms described in
+ *
+ * <ul>
+ * <li><a href="http://dx.doi.org/10.1145/22721.23109">Didonato and Morris (1986)</a>,
+ * <em>Computation of the Incomplete Gamma Function Ratios and their Inverse</em>, TOMS 12(4),
+ * 377-393,
+ * <li><a href="http://dx.doi.org/10.1145/131766.131776">Didonato and Morris (1992)</a>,
+ * <em>Algorithm 708: Significant Digit Computation of the Incomplete Beta Function
+ * Ratios</em>, TOMS 18(3), 360-373,
+ * </ul>
+ *
+ * and implemented in the <a href="http://www.dtic.mil/docs/citations/ADA476840">NSWC Library of
+ * Mathematical Functions</a>, available <a
+ * href="http://www.ualberta.ca/CNS/RESEARCH/Software/NumericalNSWC/site.html">here</a>. This
+ * library is "approved for public release", and the <a
+ * href="http://www.dtic.mil/dtic/pdf/announcements/CopyrightGuidance.pdf">Copyright guidance</a>
+ * indicates that unless otherwise stated in the code, all FORTRAN functions in this library are
+ * license free. Since no such notice appears in the code these functions can safely be ported to
+ * Commons-Math.
+ */
+public class Beta {
+ /** Maximum allowed numerical error. */
+ private static final double DEFAULT_EPSILON = 1E-14;
+
+ /** The constant value of ½log 2π. */
+ private static final double HALF_LOG_TWO_PI = .9189385332046727;
+
+ /**
+ * <p>
+ * The coefficients of the series expansion of the Δ function. This function
+ * is defined as follows
+ * </p>
+ * <center>Δ(x) = log Γ(x) - (x - 0.5) log a + a - 0.5 log 2π,</center>
+ * <p>
+ * see equation (23) in Didonato and Morris (1992). The series expansion,
+ * which applies for x ≥ 10, reads
+ * </p>
+ * <pre>
+ * 14
+ * ====
+ * 1 \ 2 n
+ * Δ(x) = --- > d (10 / x)
+ * x / n
+ * ====
+ * n = 0
+ * <pre>
+ */
+ private static final double[] DELTA = {
+ .833333333333333333333333333333E-01,
+ -.277777777777777777777777752282E-04,
+ .793650793650793650791732130419E-07,
+ -.595238095238095232389839236182E-09,
+ .841750841750832853294451671990E-11,
+ -.191752691751854612334149171243E-12,
+ .641025640510325475730918472625E-14,
+ -.295506514125338232839867823991E-15,
+ .179643716359402238723287696452E-16,
+ -.139228964661627791231203060395E-17,
+ .133802855014020915603275339093E-18,
+ -.154246009867966094273710216533E-19,
+ .197701992980957427278370133333E-20,
+ -.234065664793997056856992426667E-21,
+ .171348014966398575409015466667E-22
+ };
+
+ /** Default constructor. Prohibit instantiation. */
+ private Beta() {}
+
+ /**
+ * Returns the <a href="http://mathworld.wolfram.com/RegularizedBetaFunction.html">regularized
+ * beta function</a> I(x, a, b).
+ *
+ * @param x Value.
+ * @param a Parameter {@code a}.
+ * @param b Parameter {@code b}.
+ * @return the regularized beta function I(x, a, b).
+ * @throws org.apache.commons.math3.exception.MaxCountExceededException if the algorithm fails
+ * to converge.
+ */
+ public static double regularizedBeta(double x, double a, double b) {
+ return regularizedBeta(x, a, b, DEFAULT_EPSILON, Integer.MAX_VALUE);
+ }
+
+ /**
+ * Returns the <a href="http://mathworld.wolfram.com/RegularizedBetaFunction.html">regularized
+ * beta function</a> I(x, a, b).
+ *
+ * @param x Value.
+ * @param a Parameter {@code a}.
+ * @param b Parameter {@code b}.
+ * @param epsilon When the absolute value of the nth item in the series is less than epsilon the
+ * approximation ceases to calculate further elements in the series.
+ * @return the regularized beta function I(x, a, b)
+ * @throws org.apache.commons.math3.exception.MaxCountExceededException if the algorithm fails
+ * to converge.
+ */
+ public static double regularizedBeta(double x, double a, double b, double epsilon) {
+ return regularizedBeta(x, a, b, epsilon, Integer.MAX_VALUE);
+ }
+
+ /**
+ * Returns the regularized beta function I(x, a, b).
+ *
+ * @param x the value.
+ * @param a Parameter {@code a}.
+ * @param b Parameter {@code b}.
+ * @param maxIterations Maximum number of "iterations" to complete.
+ * @return the regularized beta function I(x, a, b)
+ * @throws org.apache.commons.math3.exception.MaxCountExceededException if the algorithm fails
+ * to converge.
+ */
+ public static double regularizedBeta(double x, double a, double b, int maxIterations) {
+ return regularizedBeta(x, a, b, DEFAULT_EPSILON, maxIterations);
+ }
+
+ /**
+ * Returns the regularized beta function I(x, a, b).
+ *
+ * <p>The implementation of this method is based on:
+ *
+ * <ul>
+ * <li><a href="http://mathworld.wolfram.com/RegularizedBetaFunction.html">Regularized Beta
+ * Function</a>.
+ * <li><a href="http://functions.wolfram.com/06.21.10.0001.01">Regularized Beta Function</a>.
+ * </ul>
+ *
+ * @param x the value.
+ * @param a Parameter {@code a}.
+ * @param b Parameter {@code b}.
+ * @param epsilon When the absolute value of the nth item in the series is less than epsilon the
+ * approximation ceases to calculate further elements in the series.
+ * @param maxIterations Maximum number of "iterations" to complete.
+ * @return the regularized beta function I(x, a, b)
+ * @throws org.apache.commons.math3.exception.MaxCountExceededException if the algorithm fails
+ * to converge.
+ */
+ public static double regularizedBeta(
+ double x, final double a, final double b, double epsilon, int maxIterations) {
+ double ret;
+
+ if (Double.isNaN(x)
+ || Double.isNaN(a)
+ || Double.isNaN(b)
+ || x < 0
+ || x > 1
+ || a <= 0
+ || b <= 0) {
+ ret = Double.NaN;
+ } else if (x > (a + 1) / (2 + b + a) && 1 - x <= (b + 1) / (2 + b + a)) {
+ ret = 1 - regularizedBeta(1 - x, b, a, epsilon, maxIterations);
+ } else {
+ ContinuedFraction fraction =
+ new ContinuedFraction() {
+
+ /** {@inheritDoc} */
+ @Override
+ protected double getB(int n, double x) {
+ double ret;
+ double m;
+ if (n % 2 == 0) { // even
+ m = n / 2.0;
+ ret = (m * (b - m) * x) / ((a + (2 * m) - 1) * (a + (2 * m)));
+ } else {
+ m = (n - 1.0) / 2.0;
+ ret =
+ -((a + m) * (a + b + m) * x)
+ / ((a + (2 * m)) * (a + (2 * m) + 1.0));
+ }
+ return ret;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ protected double getA(int n, double x) {
+ return 1.0;
+ }
+ };
+ ret =
+ FastMath.exp(
+ (a * FastMath.log(x))
+ + (b * FastMath.log1p(-x))
+ - FastMath.log(a)
+ - logBeta(a, b))
+ * 1.0
+ / fraction.evaluate(x, epsilon, maxIterations);
+ }
+
+ return ret;
+ }
+
+ /**
+ * Returns the natural logarithm of the beta function B(a, b).
+ *
+ * <p>The implementation of this method is based on:
+ *
+ * <ul>
+ * <li><a href="http://mathworld.wolfram.com/BetaFunction.html">Beta Function</a>, equation
+ * (1).
+ * </ul>
+ *
+ * @param a Parameter {@code a}.
+ * @param b Parameter {@code b}.
+ * @param epsilon This parameter is ignored.
+ * @param maxIterations This parameter is ignored.
+ * @return log(B(a, b)).
+ * @deprecated as of version 3.1, this method is deprecated as the computation of the beta
+ * function is no longer iterative; it will be removed in version 4.0. Current
+ * implementation of this method internally calls {@link #logBeta(double, double)}.
+ */
+ @Deprecated
+ public static double logBeta(double a, double b, double epsilon, int maxIterations) {
+
+ return logBeta(a, b);
+ }
+
+ /**
+ * Returns the value of log Γ(a + b) for 1 ≤ a, b ≤ 2. Based on the <em>NSWC Library of
+ * Mathematics Subroutines</em> double precision implementation, {@code DGSMLN}. In {@code
+ * BetaTest.testLogGammaSum()}, this private method is accessed through reflection.
+ *
+ * @param a First argument.
+ * @param b Second argument.
+ * @return the value of {@code log(Gamma(a + b))}.
+ * @throws OutOfRangeException if {@code a} or {@code b} is lower than {@code 1.0} or greater
+ * than {@code 2.0}.
+ */
+ private static double logGammaSum(final double a, final double b) throws OutOfRangeException {
+
+ if ((a < 1.0) || (a > 2.0)) {
+ throw new OutOfRangeException(a, 1.0, 2.0);
+ }
+ if ((b < 1.0) || (b > 2.0)) {
+ throw new OutOfRangeException(b, 1.0, 2.0);
+ }
+
+ final double x = (a - 1.0) + (b - 1.0);
+ if (x <= 0.5) {
+ return Gamma.logGamma1p(1.0 + x);
+ } else if (x <= 1.5) {
+ return Gamma.logGamma1p(x) + FastMath.log1p(x);
+ } else {
+ return Gamma.logGamma1p(x - 1.0) + FastMath.log(x * (1.0 + x));
+ }
+ }
+
+ /**
+ * Returns the value of log[Γ(b) / Γ(a + b)] for a ≥ 0 and b ≥ 10. Based on the <em>NSWC Library
+ * of Mathematics Subroutines</em> double precision implementation, {@code DLGDIV}. In {@code
+ * BetaTest.testLogGammaMinusLogGammaSum()}, this private method is accessed through reflection.
+ *
+ * @param a First argument.
+ * @param b Second argument.
+ * @return the value of {@code log(Gamma(b) / Gamma(a + b))}.
+ * @throws NumberIsTooSmallException if {@code a < 0.0} or {@code b < 10.0}.
+ */
+ private static double logGammaMinusLogGammaSum(final double a, final double b)
+ throws NumberIsTooSmallException {
+
+ if (a < 0.0) {
+ throw new NumberIsTooSmallException(a, 0.0, true);
+ }
+ if (b < 10.0) {
+ throw new NumberIsTooSmallException(b, 10.0, true);
+ }
+
+ /*
+ * d = a + b - 0.5
+ */
+ final double d;
+ final double w;
+ if (a <= b) {
+ d = b + (a - 0.5);
+ w = deltaMinusDeltaSum(a, b);
+ } else {
+ d = a + (b - 0.5);
+ w = deltaMinusDeltaSum(b, a);
+ }
+
+ final double u = d * FastMath.log1p(a / b);
+ final double v = a * (FastMath.log(b) - 1.0);
+
+ return u <= v ? (w - u) - v : (w - v) - u;
+ }
+
+ /**
+ * Returns the value of Δ(b) - Δ(a + b), with 0 ≤ a ≤ b and b ≥ 10. Based on equations (26),
+ * (27) and (28) in Didonato and Morris (1992).
+ *
+ * @param a First argument.
+ * @param b Second argument.
+ * @return the value of {@code Delta(b) - Delta(a + b)}
+ * @throws OutOfRangeException if {@code a < 0} or {@code a > b}
+ * @throws NumberIsTooSmallException if {@code b < 10}
+ */
+ private static double deltaMinusDeltaSum(final double a, final double b)
+ throws OutOfRangeException, NumberIsTooSmallException {
+
+ if ((a < 0) || (a > b)) {
+ throw new OutOfRangeException(a, 0, b);
+ }
+ if (b < 10) {
+ throw new NumberIsTooSmallException(b, 10, true);
+ }
+
+ final double h = a / b;
+ final double p = h / (1.0 + h);
+ final double q = 1.0 / (1.0 + h);
+ final double q2 = q * q;
+ /*
+ * s[i] = 1 + q + ... - q**(2 * i)
+ */
+ final double[] s = new double[DELTA.length];
+ s[0] = 1.0;
+ for (int i = 1; i < s.length; i++) {
+ s[i] = 1.0 + (q + q2 * s[i - 1]);
+ }
+ /*
+ * w = Delta(b) - Delta(a + b)
+ */
+ final double sqrtT = 10.0 / b;
+ final double t = sqrtT * sqrtT;
+ double w = DELTA[DELTA.length - 1] * s[s.length - 1];
+ for (int i = DELTA.length - 2; i >= 0; i--) {
+ w = t * w + DELTA[i] * s[i];
+ }
+ return w * p / b;
+ }
+
+ /**
+ * Returns the value of Δ(p) + Δ(q) - Δ(p + q), with p, q ≥ 10. Based on the <em>NSWC Library of
+ * Mathematics Subroutines</em> double precision implementation, {@code DBCORR}. In {@code
+ * BetaTest.testSumDeltaMinusDeltaSum()}, this private method is accessed through reflection.
+ *
+ * @param p First argument.
+ * @param q Second argument.
+ * @return the value of {@code Delta(p) + Delta(q) - Delta(p + q)}.
+ * @throws NumberIsTooSmallException if {@code p < 10.0} or {@code q < 10.0}.
+ */
+ private static double sumDeltaMinusDeltaSum(final double p, final double q) {
+
+ if (p < 10.0) {
+ throw new NumberIsTooSmallException(p, 10.0, true);
+ }
+ if (q < 10.0) {
+ throw new NumberIsTooSmallException(q, 10.0, true);
+ }
+
+ final double a = FastMath.min(p, q);
+ final double b = FastMath.max(p, q);
+ final double sqrtT = 10.0 / a;
+ final double t = sqrtT * sqrtT;
+ double z = DELTA[DELTA.length - 1];
+ for (int i = DELTA.length - 2; i >= 0; i--) {
+ z = t * z + DELTA[i];
+ }
+ return z / a + deltaMinusDeltaSum(a, b);
+ }
+
+ /**
+ * Returns the value of log B(p, q) for 0 ≤ x ≤ 1 and p, q > 0. Based on the <em>NSWC Library of
+ * Mathematics Subroutines</em> implementation, {@code DBETLN}.
+ *
+ * @param p First argument.
+ * @param q Second argument.
+ * @return the value of {@code log(Beta(p, q))}, {@code NaN} if {@code p <= 0} or {@code q <=
+ * 0}.
+ */
+ public static double logBeta(final double p, final double q) {
+ if (Double.isNaN(p) || Double.isNaN(q) || (p <= 0.0) || (q <= 0.0)) {
+ return Double.NaN;
+ }
+
+ final double a = FastMath.min(p, q);
+ final double b = FastMath.max(p, q);
+ if (a >= 10.0) {
+ final double w = sumDeltaMinusDeltaSum(a, b);
+ final double h = a / b;
+ final double c = h / (1.0 + h);
+ final double u = -(a - 0.5) * FastMath.log(c);
+ final double v = b * FastMath.log1p(h);
+ if (u <= v) {
+ return (((-0.5 * FastMath.log(b) + HALF_LOG_TWO_PI) + w) - u) - v;
+ } else {
+ return (((-0.5 * FastMath.log(b) + HALF_LOG_TWO_PI) + w) - v) - u;
+ }
+ } else if (a > 2.0) {
+ if (b > 1000.0) {
+ final int n = (int) FastMath.floor(a - 1.0);
+ double prod = 1.0;
+ double ared = a;
+ for (int i = 0; i < n; i++) {
+ ared -= 1.0;
+ prod *= ared / (1.0 + ared / b);
+ }
+ return (FastMath.log(prod) - n * FastMath.log(b))
+ + (Gamma.logGamma(ared) + logGammaMinusLogGammaSum(ared, b));
+ } else {
+ double prod1 = 1.0;
+ double ared = a;
+ while (ared > 2.0) {
+ ared -= 1.0;
+ final double h = ared / b;
+ prod1 *= h / (1.0 + h);
+ }
+ if (b < 10.0) {
+ double prod2 = 1.0;
+ double bred = b;
+ while (bred > 2.0) {
+ bred -= 1.0;
+ prod2 *= bred / (ared + bred);
+ }
+ return FastMath.log(prod1)
+ + FastMath.log(prod2)
+ + (Gamma.logGamma(ared)
+ + (Gamma.logGamma(bred) - logGammaSum(ared, bred)));
+ } else {
+ return FastMath.log(prod1)
+ + Gamma.logGamma(ared)
+ + logGammaMinusLogGammaSum(ared, b);
+ }
+ }
+ } else if (a >= 1.0) {
+ if (b > 2.0) {
+ if (b < 10.0) {
+ double prod = 1.0;
+ double bred = b;
+ while (bred > 2.0) {
+ bred -= 1.0;
+ prod *= bred / (a + bred);
+ }
+ return FastMath.log(prod)
+ + (Gamma.logGamma(a) + (Gamma.logGamma(bred) - logGammaSum(a, bred)));
+ } else {
+ return Gamma.logGamma(a) + logGammaMinusLogGammaSum(a, b);
+ }
+ } else {
+ return Gamma.logGamma(a) + Gamma.logGamma(b) - logGammaSum(a, b);
+ }
+ } else {
+ if (b >= 10.0) {
+ return Gamma.logGamma(a) + logGammaMinusLogGammaSum(a, b);
+ } else {
+ // The following command is the original NSWC implementation.
+ // return Gamma.logGamma(a) +
+ // (Gamma.logGamma(b) - Gamma.logGamma(a + b));
+ // The following command turns out to be more accurate.
+ return FastMath.log(Gamma.gamma(a) * Gamma.gamma(b) / Gamma.gamma(a + b));
+ }
+ }
+ }
+}
diff --git a/src/main/java/org/apache/commons/math3/special/Erf.java b/src/main/java/org/apache/commons/math3/special/Erf.java
new file mode 100644
index 0000000..325b15c
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/special/Erf.java
@@ -0,0 +1,227 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math3.special;
+
+import org.apache.commons.math3.util.FastMath;
+
+/** This is a utility class that provides computation methods related to the error functions. */
+public class Erf {
+
+ /**
+ * The number {@code X_CRIT} is used by {@link #erf(double, double)} internally. This number
+ * solves {@code erf(x)=0.5} within 1ulp. More precisely, the current implementations of {@link
+ * #erf(double)} and {@link #erfc(double)} satisfy:<br>
+ * {@code erf(X_CRIT) < 0.5},<br>
+ * {@code erf(Math.nextUp(X_CRIT) > 0.5},<br>
+ * {@code erfc(X_CRIT) = 0.5}, and<br>
+ * {@code erfc(Math.nextUp(X_CRIT) < 0.5}
+ */
+ private static final double X_CRIT = 0.4769362762044697;
+
+ /** Default constructor. Prohibit instantiation. */
+ private Erf() {}
+
+ /**
+ * Returns the error function.
+ *
+ * <p>erf(x) = 2/&radic;&pi; <sub>0</sub>&int;<sup>x</sup> e<sup>-t<sup>2</sup></sup>dt
+ *
+ * <p>This implementation computes erf(x) using the {@link Gamma#regularizedGammaP(double,
+ * double, double, int) regularized gamma function}, following <a
+ * href="http://mathworld.wolfram.com/Erf.html">Erf</a>, equation (3)
+ *
+ * <p>The value returned is always between -1 and 1 (inclusive). If {@code abs(x) > 40}, then
+ * {@code erf(x)} is indistinguishable from either 1 or -1 as a double, so the appropriate
+ * extreme value is returned.
+ *
+ * @param x the value.
+ * @return the error function erf(x)
+ * @throws org.apache.commons.math3.exception.MaxCountExceededException if the algorithm fails
+ * to converge.
+ * @see Gamma#regularizedGammaP(double, double, double, int)
+ */
+ public static double erf(double x) {
+ if (FastMath.abs(x) > 40) {
+ return x > 0 ? 1 : -1;
+ }
+ final double ret = Gamma.regularizedGammaP(0.5, x * x, 1.0e-15, 10000);
+ return x < 0 ? -ret : ret;
+ }
+
+ /**
+ * Returns the complementary error function.
+ *
+ * <p>erfc(x) = 2/&radic;&pi; <sub>x</sub>&int;<sup>&infin;</sup> e<sup>-t<sup>2</sup></sup>dt
+ * <br>
+ * = 1 - {@link #erf(double) erf(x)}
+ *
+ * <p>This implementation computes erfc(x) using the {@link Gamma#regularizedGammaQ(double,
+ * double, double, int) regularized gamma function}, following <a
+ * href="http://mathworld.wolfram.com/Erf.html">Erf</a>, equation (3).
+ *
+ * <p>The value returned is always between 0 and 2 (inclusive). If {@code abs(x) > 40}, then
+ * {@code erf(x)} is indistinguishable from either 0 or 2 as a double, so the appropriate
+ * extreme value is returned.
+ *
+ * @param x the value
+ * @return the complementary error function erfc(x)
+ * @throws org.apache.commons.math3.exception.MaxCountExceededException if the algorithm fails
+ * to converge.
+ * @see Gamma#regularizedGammaQ(double, double, double, int)
+ * @since 2.2
+ */
+ public static double erfc(double x) {
+ if (FastMath.abs(x) > 40) {
+ return x > 0 ? 0 : 2;
+ }
+ final double ret = Gamma.regularizedGammaQ(0.5, x * x, 1.0e-15, 10000);
+ return x < 0 ? 2 - ret : ret;
+ }
+
+ /**
+ * Returns the difference between erf(x1) and erf(x2).
+ *
+ * <p>The implementation uses either erf(double) or erfc(double) depending on which provides the
+ * most precise result.
+ *
+ * @param x1 the first value
+ * @param x2 the second value
+ * @return erf(x2) - erf(x1)
+ */
+ public static double erf(double x1, double x2) {
+ if (x1 > x2) {
+ return -erf(x2, x1);
+ }
+
+ return x1 < -X_CRIT
+ ? x2 < 0.0 ? erfc(-x2) - erfc(-x1) : erf(x2) - erf(x1)
+ : x2 > X_CRIT && x1 > 0.0 ? erfc(x1) - erfc(x2) : erf(x2) - erf(x1);
+ }
+
+ /**
+ * Returns the inverse erf.
+ *
+ * <p>This implementation is described in the paper: <a
+ * href="http://people.maths.ox.ac.uk/gilesm/files/gems_erfinv.pdf">Approximating the erfinv
+ * function</a> by Mike Giles, Oxford-Man Institute of Quantitative Finance, which was published
+ * in GPU Computing Gems, volume 2, 2010. The source code is available <a
+ * href="http://gpucomputing.net/?q=node/1828">here</a>.
+ *
+ * @param x the value
+ * @return t such that x = erf(t)
+ * @since 3.2
+ */
+ public static double erfInv(final double x) {
+
+ // beware that the logarithm argument must be
+ // commputed as (1.0 - x) * (1.0 + x),
+ // it must NOT be simplified as 1.0 - x * x as this
+ // would induce rounding errors near the boundaries +/-1
+ double w = -FastMath.log((1.0 - x) * (1.0 + x));
+ double p;
+
+ if (w < 6.25) {
+ w -= 3.125;
+ p = -3.6444120640178196996e-21;
+ p = -1.685059138182016589e-19 + p * w;
+ p = 1.2858480715256400167e-18 + p * w;
+ p = 1.115787767802518096e-17 + p * w;
+ p = -1.333171662854620906e-16 + p * w;
+ p = 2.0972767875968561637e-17 + p * w;
+ p = 6.6376381343583238325e-15 + p * w;
+ p = -4.0545662729752068639e-14 + p * w;
+ p = -8.1519341976054721522e-14 + p * w;
+ p = 2.6335093153082322977e-12 + p * w;
+ p = -1.2975133253453532498e-11 + p * w;
+ p = -5.4154120542946279317e-11 + p * w;
+ p = 1.051212273321532285e-09 + p * w;
+ p = -4.1126339803469836976e-09 + p * w;
+ p = -2.9070369957882005086e-08 + p * w;
+ p = 4.2347877827932403518e-07 + p * w;
+ p = -1.3654692000834678645e-06 + p * w;
+ p = -1.3882523362786468719e-05 + p * w;
+ p = 0.0001867342080340571352 + p * w;
+ p = -0.00074070253416626697512 + p * w;
+ p = -0.0060336708714301490533 + p * w;
+ p = 0.24015818242558961693 + p * w;
+ p = 1.6536545626831027356 + p * w;
+ } else if (w < 16.0) {
+ w = FastMath.sqrt(w) - 3.25;
+ p = 2.2137376921775787049e-09;
+ p = 9.0756561938885390979e-08 + p * w;
+ p = -2.7517406297064545428e-07 + p * w;
+ p = 1.8239629214389227755e-08 + p * w;
+ p = 1.5027403968909827627e-06 + p * w;
+ p = -4.013867526981545969e-06 + p * w;
+ p = 2.9234449089955446044e-06 + p * w;
+ p = 1.2475304481671778723e-05 + p * w;
+ p = -4.7318229009055733981e-05 + p * w;
+ p = 6.8284851459573175448e-05 + p * w;
+ p = 2.4031110387097893999e-05 + p * w;
+ p = -0.0003550375203628474796 + p * w;
+ p = 0.00095328937973738049703 + p * w;
+ p = -0.0016882755560235047313 + p * w;
+ p = 0.0024914420961078508066 + p * w;
+ p = -0.0037512085075692412107 + p * w;
+ p = 0.005370914553590063617 + p * w;
+ p = 1.0052589676941592334 + p * w;
+ p = 3.0838856104922207635 + p * w;
+ } else if (!Double.isInfinite(w)) {
+ w = FastMath.sqrt(w) - 5.0;
+ p = -2.7109920616438573243e-11;
+ p = -2.5556418169965252055e-10 + p * w;
+ p = 1.5076572693500548083e-09 + p * w;
+ p = -3.7894654401267369937e-09 + p * w;
+ p = 7.6157012080783393804e-09 + p * w;
+ p = -1.4960026627149240478e-08 + p * w;
+ p = 2.9147953450901080826e-08 + p * w;
+ p = -6.7711997758452339498e-08 + p * w;
+ p = 2.2900482228026654717e-07 + p * w;
+ p = -9.9298272942317002539e-07 + p * w;
+ p = 4.5260625972231537039e-06 + p * w;
+ p = -1.9681778105531670567e-05 + p * w;
+ p = 7.5995277030017761139e-05 + p * w;
+ p = -0.00021503011930044477347 + p * w;
+ p = -0.00013871931833623122026 + p * w;
+ p = 1.0103004648645343977 + p * w;
+ p = 4.8499064014085844221 + p * w;
+ } else {
+ // this branch does not appears in the original code, it
+ // was added because the previous branch does not handle
+ // x = +/-1 correctly. In this case, w is positive infinity
+ // and as the first coefficient (-2.71e-11) is negative.
+ // Once the first multiplication is done, p becomes negative
+ // infinity and remains so throughout the polynomial evaluation.
+ // So the branch above incorrectly returns negative infinity
+ // instead of the correct positive infinity.
+ p = Double.POSITIVE_INFINITY;
+ }
+
+ return p * x;
+ }
+
+ /**
+ * Returns the inverse erfc.
+ *
+ * @param x the value
+ * @return t such that x = erfc(t)
+ * @since 3.2
+ */
+ public static double erfcInv(final double x) {
+ return erfInv(1 - x);
+ }
+}
diff --git a/src/main/java/org/apache/commons/math3/special/Gamma.java b/src/main/java/org/apache/commons/math3/special/Gamma.java
new file mode 100644
index 0000000..8f7e248
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/special/Gamma.java
@@ -0,0 +1,692 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math3.special;
+
+import org.apache.commons.math3.exception.MaxCountExceededException;
+import org.apache.commons.math3.exception.NumberIsTooLargeException;
+import org.apache.commons.math3.exception.NumberIsTooSmallException;
+import org.apache.commons.math3.util.ContinuedFraction;
+import org.apache.commons.math3.util.FastMath;
+
+/**
+ * This is a utility class that provides computation methods related to the &Gamma; (Gamma) family
+ * of functions.
+ *
+ * <p>Implementation of {@link #invGamma1pm1(double)} and {@link #logGamma1p(double)} is based on
+ * the algorithms described in
+ *
+ * <ul>
+ * <li><a href="http://dx.doi.org/10.1145/22721.23109">Didonato and Morris (1986)</a>,
+ * <em>Computation of the Incomplete Gamma Function Ratios and their Inverse</em>, TOMS 12(4),
+ * 377-393,
+ * <li><a href="http://dx.doi.org/10.1145/131766.131776">Didonato and Morris (1992)</a>,
+ * <em>Algorithm 708: Significant Digit Computation of the Incomplete Beta Function
+ * Ratios</em>, TOMS 18(3), 360-373,
+ * </ul>
+ *
+ * and implemented in the <a href="http://www.dtic.mil/docs/citations/ADA476840">NSWC Library of
+ * Mathematical Functions</a>, available <a
+ * href="http://www.ualberta.ca/CNS/RESEARCH/Software/NumericalNSWC/site.html">here</a>. This
+ * library is "approved for public release", and the <a
+ * href="http://www.dtic.mil/dtic/pdf/announcements/CopyrightGuidance.pdf">Copyright guidance</a>
+ * indicates that unless otherwise stated in the code, all FORTRAN functions in this library are
+ * license free. Since no such notice appears in the code these functions can safely be ported to
+ * Commons-Math.
+ */
+public class Gamma {
+ /**
+ * <a href="http://en.wikipedia.org/wiki/Euler-Mascheroni_constant">Euler-Mascheroni
+ * constant</a>
+ *
+ * @since 2.0
+ */
+ public static final double GAMMA = 0.577215664901532860606512090082;
+
+ /**
+ * The value of the {@code g} constant in the Lanczos approximation, see {@link
+ * #lanczos(double)}.
+ *
+ * @since 3.1
+ */
+ public static final double LANCZOS_G = 607.0 / 128.0;
+
+ /** Maximum allowed numerical error. */
+ private static final double DEFAULT_EPSILON = 10e-15;
+
+ /** Lanczos coefficients */
+ private static final double[] LANCZOS = {
+ 0.99999999999999709182,
+ 57.156235665862923517,
+ -59.597960355475491248,
+ 14.136097974741747174,
+ -0.49191381609762019978,
+ .33994649984811888699e-4,
+ .46523628927048575665e-4,
+ -.98374475304879564677e-4,
+ .15808870322491248884e-3,
+ -.21026444172410488319e-3,
+ .21743961811521264320e-3,
+ -.16431810653676389022e-3,
+ .84418223983852743293e-4,
+ -.26190838401581408670e-4,
+ .36899182659531622704e-5,
+ };
+
+ /** Avoid repeated computation of log of 2 PI in logGamma */
+ private static final double HALF_LOG_2_PI = 0.5 * FastMath.log(2.0 * FastMath.PI);
+
+ /** The constant value of &radic;(2&pi;). */
+ private static final double SQRT_TWO_PI = 2.506628274631000502;
+
+ // limits for switching algorithm in digamma
+ /** C limit. */
+ private static final double C_LIMIT = 49;
+
+ /** S limit. */
+ private static final double S_LIMIT = 1e-5;
+
+ /*
+ * Constants for the computation of double invGamma1pm1(double).
+ * Copied from DGAM1 in the NSWC library.
+ */
+
+ /** The constant {@code A0} defined in {@code DGAM1}. */
+ private static final double INV_GAMMA1P_M1_A0 = .611609510448141581788E-08;
+
+ /** The constant {@code A1} defined in {@code DGAM1}. */
+ private static final double INV_GAMMA1P_M1_A1 = .624730830116465516210E-08;
+
+ /** The constant {@code B1} defined in {@code DGAM1}. */
+ private static final double INV_GAMMA1P_M1_B1 = .203610414066806987300E+00;
+
+ /** The constant {@code B2} defined in {@code DGAM1}. */
+ private static final double INV_GAMMA1P_M1_B2 = .266205348428949217746E-01;
+
+ /** The constant {@code B3} defined in {@code DGAM1}. */
+ private static final double INV_GAMMA1P_M1_B3 = .493944979382446875238E-03;
+
+ /** The constant {@code B4} defined in {@code DGAM1}. */
+ private static final double INV_GAMMA1P_M1_B4 = -.851419432440314906588E-05;
+
+ /** The constant {@code B5} defined in {@code DGAM1}. */
+ private static final double INV_GAMMA1P_M1_B5 = -.643045481779353022248E-05;
+
+ /** The constant {@code B6} defined in {@code DGAM1}. */
+ private static final double INV_GAMMA1P_M1_B6 = .992641840672773722196E-06;
+
+ /** The constant {@code B7} defined in {@code DGAM1}. */
+ private static final double INV_GAMMA1P_M1_B7 = -.607761895722825260739E-07;
+
+ /** The constant {@code B8} defined in {@code DGAM1}. */
+ private static final double INV_GAMMA1P_M1_B8 = .195755836614639731882E-09;
+
+ /** The constant {@code P0} defined in {@code DGAM1}. */
+ private static final double INV_GAMMA1P_M1_P0 = .6116095104481415817861E-08;
+
+ /** The constant {@code P1} defined in {@code DGAM1}. */
+ private static final double INV_GAMMA1P_M1_P1 = .6871674113067198736152E-08;
+
+ /** The constant {@code P2} defined in {@code DGAM1}. */
+ private static final double INV_GAMMA1P_M1_P2 = .6820161668496170657918E-09;
+
+ /** The constant {@code P3} defined in {@code DGAM1}. */
+ private static final double INV_GAMMA1P_M1_P3 = .4686843322948848031080E-10;
+
+ /** The constant {@code P4} defined in {@code DGAM1}. */
+ private static final double INV_GAMMA1P_M1_P4 = .1572833027710446286995E-11;
+
+ /** The constant {@code P5} defined in {@code DGAM1}. */
+ private static final double INV_GAMMA1P_M1_P5 = -.1249441572276366213222E-12;
+
+ /** The constant {@code P6} defined in {@code DGAM1}. */
+ private static final double INV_GAMMA1P_M1_P6 = .4343529937408594255178E-14;
+
+ /** The constant {@code Q1} defined in {@code DGAM1}. */
+ private static final double INV_GAMMA1P_M1_Q1 = .3056961078365221025009E+00;
+
+ /** The constant {@code Q2} defined in {@code DGAM1}. */
+ private static final double INV_GAMMA1P_M1_Q2 = .5464213086042296536016E-01;
+
+ /** The constant {@code Q3} defined in {@code DGAM1}. */
+ private static final double INV_GAMMA1P_M1_Q3 = .4956830093825887312020E-02;
+
+ /** The constant {@code Q4} defined in {@code DGAM1}. */
+ private static final double INV_GAMMA1P_M1_Q4 = .2692369466186361192876E-03;
+
+ /** The constant {@code C} defined in {@code DGAM1}. */
+ private static final double INV_GAMMA1P_M1_C = -.422784335098467139393487909917598E+00;
+
+ /** The constant {@code C0} defined in {@code DGAM1}. */
+ private static final double INV_GAMMA1P_M1_C0 = .577215664901532860606512090082402E+00;
+
+ /** The constant {@code C1} defined in {@code DGAM1}. */
+ private static final double INV_GAMMA1P_M1_C1 = -.655878071520253881077019515145390E+00;
+
+ /** The constant {@code C2} defined in {@code DGAM1}. */
+ private static final double INV_GAMMA1P_M1_C2 = -.420026350340952355290039348754298E-01;
+
+ /** The constant {@code C3} defined in {@code DGAM1}. */
+ private static final double INV_GAMMA1P_M1_C3 = .166538611382291489501700795102105E+00;
+
+ /** The constant {@code C4} defined in {@code DGAM1}. */
+ private static final double INV_GAMMA1P_M1_C4 = -.421977345555443367482083012891874E-01;
+
+ /** The constant {@code C5} defined in {@code DGAM1}. */
+ private static final double INV_GAMMA1P_M1_C5 = -.962197152787697356211492167234820E-02;
+
+ /** The constant {@code C6} defined in {@code DGAM1}. */
+ private static final double INV_GAMMA1P_M1_C6 = .721894324666309954239501034044657E-02;
+
+ /** The constant {@code C7} defined in {@code DGAM1}. */
+ private static final double INV_GAMMA1P_M1_C7 = -.116516759185906511211397108401839E-02;
+
+ /** The constant {@code C8} defined in {@code DGAM1}. */
+ private static final double INV_GAMMA1P_M1_C8 = -.215241674114950972815729963053648E-03;
+
+ /** The constant {@code C9} defined in {@code DGAM1}. */
+ private static final double INV_GAMMA1P_M1_C9 = .128050282388116186153198626328164E-03;
+
+ /** The constant {@code C10} defined in {@code DGAM1}. */
+ private static final double INV_GAMMA1P_M1_C10 = -.201348547807882386556893914210218E-04;
+
+ /** The constant {@code C11} defined in {@code DGAM1}. */
+ private static final double INV_GAMMA1P_M1_C11 = -.125049348214267065734535947383309E-05;
+
+ /** The constant {@code C12} defined in {@code DGAM1}. */
+ private static final double INV_GAMMA1P_M1_C12 = .113302723198169588237412962033074E-05;
+
+ /** The constant {@code C13} defined in {@code DGAM1}. */
+ private static final double INV_GAMMA1P_M1_C13 = -.205633841697760710345015413002057E-06;
+
+ /** Default constructor. Prohibit instantiation. */
+ private Gamma() {}
+
+ /**
+ * Returns the value of log&nbsp;&Gamma;(x) for x&nbsp;&gt;&nbsp;0.
+ *
+ * <p>For x &le; 8, the implementation is based on the double precision implementation in the
+ * <em>NSWC Library of Mathematics Subroutines</em>, {@code DGAMLN}. For x &gt; 8, the
+ * implementation is based on
+ *
+ * <ul>
+ * <li><a href="http://mathworld.wolfram.com/GammaFunction.html">Gamma Function</a>, equation
+ * (28).
+ * <li><a href="http://mathworld.wolfram.com/LanczosApproximation.html">Lanczos
+ * Approximation</a>, equations (1) through (5).
+ * <li><a href="http://my.fit.edu/~gabdo/gamma.txt">Paul Godfrey, A note on the computation of
+ * the convergent Lanczos complex Gamma approximation</a>
+ * </ul>
+ *
+ * @param x Argument.
+ * @return the value of {@code log(Gamma(x))}, {@code Double.NaN} if {@code x <= 0.0}.
+ */
+ public static double logGamma(double x) {
+ double ret;
+
+ if (Double.isNaN(x) || (x <= 0.0)) {
+ ret = Double.NaN;
+ } else if (x < 0.5) {
+ return logGamma1p(x) - FastMath.log(x);
+ } else if (x <= 2.5) {
+ return logGamma1p((x - 0.5) - 0.5);
+ } else if (x <= 8.0) {
+ final int n = (int) FastMath.floor(x - 1.5);
+ double prod = 1.0;
+ for (int i = 1; i <= n; i++) {
+ prod *= x - i;
+ }
+ return logGamma1p(x - (n + 1)) + FastMath.log(prod);
+ } else {
+ double sum = lanczos(x);
+ double tmp = x + LANCZOS_G + .5;
+ ret = ((x + .5) * FastMath.log(tmp)) - tmp + HALF_LOG_2_PI + FastMath.log(sum / x);
+ }
+
+ return ret;
+ }
+
+ /**
+ * Returns the regularized gamma function P(a, x).
+ *
+ * @param a Parameter.
+ * @param x Value.
+ * @return the regularized gamma function P(a, x).
+ * @throws MaxCountExceededException if the algorithm fails to converge.
+ */
+ public static double regularizedGammaP(double a, double x) {
+ return regularizedGammaP(a, x, DEFAULT_EPSILON, Integer.MAX_VALUE);
+ }
+
+ /**
+ * Returns the regularized gamma function P(a, x).
+ *
+ * <p>The implementation of this method is based on:
+ *
+ * <ul>
+ * <li><a href="http://mathworld.wolfram.com/RegularizedGammaFunction.html">Regularized Gamma
+ * Function</a>, equation (1)
+ * <li><a href="http://mathworld.wolfram.com/IncompleteGammaFunction.html">Incomplete Gamma
+ * Function</a>, equation (4).
+ * <li><a
+ * href="http://mathworld.wolfram.com/ConfluentHypergeometricFunctionoftheFirstKind.html">
+ * Confluent Hypergeometric Function of the First Kind</a>, equation (1).
+ * </ul>
+ *
+ * @param a the a parameter.
+ * @param x the value.
+ * @param epsilon When the absolute value of the nth item in the series is less than epsilon the
+ * approximation ceases to calculate further elements in the series.
+ * @param maxIterations Maximum number of "iterations" to complete.
+ * @return the regularized gamma function P(a, x)
+ * @throws MaxCountExceededException if the algorithm fails to converge.
+ */
+ public static double regularizedGammaP(double a, double x, double epsilon, int maxIterations) {
+ double ret;
+
+ if (Double.isNaN(a) || Double.isNaN(x) || (a <= 0.0) || (x < 0.0)) {
+ ret = Double.NaN;
+ } else if (x == 0.0) {
+ ret = 0.0;
+ } else if (x >= a + 1) {
+ // use regularizedGammaQ because it should converge faster in this
+ // case.
+ ret = 1.0 - regularizedGammaQ(a, x, epsilon, maxIterations);
+ } else {
+ // calculate series
+ double n = 0.0; // current element index
+ double an = 1.0 / a; // n-th element in the series
+ double sum = an; // partial sum
+ while (FastMath.abs(an / sum) > epsilon
+ && n < maxIterations
+ && sum < Double.POSITIVE_INFINITY) {
+ // compute next element in the series
+ n += 1.0;
+ an *= x / (a + n);
+
+ // update partial sum
+ sum += an;
+ }
+ if (n >= maxIterations) {
+ throw new MaxCountExceededException(maxIterations);
+ } else if (Double.isInfinite(sum)) {
+ ret = 1.0;
+ } else {
+ ret = FastMath.exp(-x + (a * FastMath.log(x)) - logGamma(a)) * sum;
+ }
+ }
+
+ return ret;
+ }
+
+ /**
+ * Returns the regularized gamma function Q(a, x) = 1 - P(a, x).
+ *
+ * @param a the a parameter.
+ * @param x the value.
+ * @return the regularized gamma function Q(a, x)
+ * @throws MaxCountExceededException if the algorithm fails to converge.
+ */
+ public static double regularizedGammaQ(double a, double x) {
+ return regularizedGammaQ(a, x, DEFAULT_EPSILON, Integer.MAX_VALUE);
+ }
+
+ /**
+ * Returns the regularized gamma function Q(a, x) = 1 - P(a, x).
+ *
+ * <p>The implementation of this method is based on:
+ *
+ * <ul>
+ * <li><a href="http://mathworld.wolfram.com/RegularizedGammaFunction.html">Regularized Gamma
+ * Function</a>, equation (1).
+ * <li><a href="http://functions.wolfram.com/GammaBetaErf/GammaRegularized/10/0003/">
+ * Regularized incomplete gamma function: Continued fraction representations (formula
+ * 06.08.10.0003)</a>
+ * </ul>
+ *
+ * @param a the a parameter.
+ * @param x the value.
+ * @param epsilon When the absolute value of the nth item in the series is less than epsilon the
+ * approximation ceases to calculate further elements in the series.
+ * @param maxIterations Maximum number of "iterations" to complete.
+ * @return the regularized gamma function P(a, x)
+ * @throws MaxCountExceededException if the algorithm fails to converge.
+ */
+ public static double regularizedGammaQ(
+ final double a, double x, double epsilon, int maxIterations) {
+ double ret;
+
+ if (Double.isNaN(a) || Double.isNaN(x) || (a <= 0.0) || (x < 0.0)) {
+ ret = Double.NaN;
+ } else if (x == 0.0) {
+ ret = 1.0;
+ } else if (x < a + 1.0) {
+ // use regularizedGammaP because it should converge faster in this
+ // case.
+ ret = 1.0 - regularizedGammaP(a, x, epsilon, maxIterations);
+ } else {
+ // create continued fraction
+ ContinuedFraction cf =
+ new ContinuedFraction() {
+
+ /** {@inheritDoc} */
+ @Override
+ protected double getA(int n, double x) {
+ return ((2.0 * n) + 1.0) - a + x;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ protected double getB(int n, double x) {
+ return n * (a - n);
+ }
+ };
+
+ ret = 1.0 / cf.evaluate(x, epsilon, maxIterations);
+ ret = FastMath.exp(-x + (a * FastMath.log(x)) - logGamma(a)) * ret;
+ }
+
+ return ret;
+ }
+
+ /**
+ * Computes the digamma function of x.
+ *
+ * <p>This is an independently written implementation of the algorithm described in Jose
+ * Bernardo, Algorithm AS 103: Psi (Digamma) Function, Applied Statistics, 1976.
+ *
+ * <p>Some of the constants have been changed to increase accuracy at the moderate expense of
+ * run-time. The result should be accurate to within 10^-8 absolute tolerance for x >= 10^-5 and
+ * within 10^-8 relative tolerance for x > 0.
+ *
+ * <p>Performance for large negative values of x will be quite expensive (proportional to |x|).
+ * Accuracy for negative values of x should be about 10^-8 absolute for results less than 10^5
+ * and 10^-8 relative for results larger than that.
+ *
+ * @param x Argument.
+ * @return digamma(x) to within 10-8 relative or absolute error whichever is smaller.
+ * @see <a href="http://en.wikipedia.org/wiki/Digamma_function">Digamma</a>
+ * @see <a href="http://www.uv.es/~bernardo/1976AppStatist.pdf">Bernardo&apos;s original article
+ * </a>
+ * @since 2.0
+ */
+ public static double digamma(double x) {
+ if (Double.isNaN(x) || Double.isInfinite(x)) {
+ return x;
+ }
+
+ if (x > 0 && x <= S_LIMIT) {
+ // use method 5 from Bernardo AS103
+ // accurate to O(x)
+ return -GAMMA - 1 / x;
+ }
+
+ if (x >= C_LIMIT) {
+ // use method 4 (accurate to O(1/x^8)
+ double inv = 1 / (x * x);
+ // 1 1 1 1
+ // log(x) - --- - ------ + ------- - -------
+ // 2 x 12 x^2 120 x^4 252 x^6
+ return FastMath.log(x) - 0.5 / x - inv * ((1.0 / 12) + inv * (1.0 / 120 - inv / 252));
+ }
+
+ return digamma(x + 1) - 1 / x;
+ }
+
+ /**
+ * Computes the trigamma function of x. This function is derived by taking the derivative of the
+ * implementation of digamma.
+ *
+ * @param x Argument.
+ * @return trigamma(x) to within 10-8 relative or absolute error whichever is smaller
+ * @see <a href="http://en.wikipedia.org/wiki/Trigamma_function">Trigamma</a>
+ * @see Gamma#digamma(double)
+ * @since 2.0
+ */
+ public static double trigamma(double x) {
+ if (Double.isNaN(x) || Double.isInfinite(x)) {
+ return x;
+ }
+
+ if (x > 0 && x <= S_LIMIT) {
+ return 1 / (x * x);
+ }
+
+ if (x >= C_LIMIT) {
+ double inv = 1 / (x * x);
+ // 1 1 1 1 1
+ // - + ---- + ---- - ----- + -----
+ // x 2 3 5 7
+ // 2 x 6 x 30 x 42 x
+ return 1 / x + inv / 2 + inv / x * (1.0 / 6 - inv * (1.0 / 30 + inv / 42));
+ }
+
+ return trigamma(x + 1) + 1 / (x * x);
+ }
+
+ /**
+ * Returns the Lanczos approximation used to compute the gamma function. The Lanczos
+ * approximation is related to the Gamma function by the following equation <center> {@code
+ * gamma(x) = sqrt(2 * pi) / x * (x + g + 0.5) ^ (x + 0.5) * exp(-x - g - 0.5) * lanczos(x)},
+ * </center> where {@code g} is the Lanczos constant.
+ *
+ * @param x Argument.
+ * @return The Lanczos approximation.
+ * @see <a href="http://mathworld.wolfram.com/LanczosApproximation.html">Lanczos
+ * Approximation</a> equations (1) through (5), and Paul Godfrey's <a
+ * href="http://my.fit.edu/~gabdo/gamma.txt">Note on the computation of the convergent
+ * Lanczos complex Gamma approximation</a>
+ * @since 3.1
+ */
+ public static double lanczos(final double x) {
+ double sum = 0.0;
+ for (int i = LANCZOS.length - 1; i > 0; --i) {
+ sum += LANCZOS[i] / (x + i);
+ }
+ return sum + LANCZOS[0];
+ }
+
+ /**
+ * Returns the value of 1 / &Gamma;(1 + x) - 1 for -0&#46;5 &le; x &le; 1&#46;5. This
+ * implementation is based on the double precision implementation in the <em>NSWC Library of
+ * Mathematics Subroutines</em>, {@code DGAM1}.
+ *
+ * @param x Argument.
+ * @return The value of {@code 1.0 / Gamma(1.0 + x) - 1.0}.
+ * @throws NumberIsTooSmallException if {@code x < -0.5}
+ * @throws NumberIsTooLargeException if {@code x > 1.5}
+ * @since 3.1
+ */
+ public static double invGamma1pm1(final double x) {
+
+ if (x < -0.5) {
+ throw new NumberIsTooSmallException(x, -0.5, true);
+ }
+ if (x > 1.5) {
+ throw new NumberIsTooLargeException(x, 1.5, true);
+ }
+
+ final double ret;
+ final double t = x <= 0.5 ? x : (x - 0.5) - 0.5;
+ if (t < 0.0) {
+ final double a = INV_GAMMA1P_M1_A0 + t * INV_GAMMA1P_M1_A1;
+ double b = INV_GAMMA1P_M1_B8;
+ b = INV_GAMMA1P_M1_B7 + t * b;
+ b = INV_GAMMA1P_M1_B6 + t * b;
+ b = INV_GAMMA1P_M1_B5 + t * b;
+ b = INV_GAMMA1P_M1_B4 + t * b;
+ b = INV_GAMMA1P_M1_B3 + t * b;
+ b = INV_GAMMA1P_M1_B2 + t * b;
+ b = INV_GAMMA1P_M1_B1 + t * b;
+ b = 1.0 + t * b;
+
+ double c = INV_GAMMA1P_M1_C13 + t * (a / b);
+ c = INV_GAMMA1P_M1_C12 + t * c;
+ c = INV_GAMMA1P_M1_C11 + t * c;
+ c = INV_GAMMA1P_M1_C10 + t * c;
+ c = INV_GAMMA1P_M1_C9 + t * c;
+ c = INV_GAMMA1P_M1_C8 + t * c;
+ c = INV_GAMMA1P_M1_C7 + t * c;
+ c = INV_GAMMA1P_M1_C6 + t * c;
+ c = INV_GAMMA1P_M1_C5 + t * c;
+ c = INV_GAMMA1P_M1_C4 + t * c;
+ c = INV_GAMMA1P_M1_C3 + t * c;
+ c = INV_GAMMA1P_M1_C2 + t * c;
+ c = INV_GAMMA1P_M1_C1 + t * c;
+ c = INV_GAMMA1P_M1_C + t * c;
+ if (x > 0.5) {
+ ret = t * c / x;
+ } else {
+ ret = x * ((c + 0.5) + 0.5);
+ }
+ } else {
+ double p = INV_GAMMA1P_M1_P6;
+ p = INV_GAMMA1P_M1_P5 + t * p;
+ p = INV_GAMMA1P_M1_P4 + t * p;
+ p = INV_GAMMA1P_M1_P3 + t * p;
+ p = INV_GAMMA1P_M1_P2 + t * p;
+ p = INV_GAMMA1P_M1_P1 + t * p;
+ p = INV_GAMMA1P_M1_P0 + t * p;
+
+ double q = INV_GAMMA1P_M1_Q4;
+ q = INV_GAMMA1P_M1_Q3 + t * q;
+ q = INV_GAMMA1P_M1_Q2 + t * q;
+ q = INV_GAMMA1P_M1_Q1 + t * q;
+ q = 1.0 + t * q;
+
+ double c = INV_GAMMA1P_M1_C13 + (p / q) * t;
+ c = INV_GAMMA1P_M1_C12 + t * c;
+ c = INV_GAMMA1P_M1_C11 + t * c;
+ c = INV_GAMMA1P_M1_C10 + t * c;
+ c = INV_GAMMA1P_M1_C9 + t * c;
+ c = INV_GAMMA1P_M1_C8 + t * c;
+ c = INV_GAMMA1P_M1_C7 + t * c;
+ c = INV_GAMMA1P_M1_C6 + t * c;
+ c = INV_GAMMA1P_M1_C5 + t * c;
+ c = INV_GAMMA1P_M1_C4 + t * c;
+ c = INV_GAMMA1P_M1_C3 + t * c;
+ c = INV_GAMMA1P_M1_C2 + t * c;
+ c = INV_GAMMA1P_M1_C1 + t * c;
+ c = INV_GAMMA1P_M1_C0 + t * c;
+
+ if (x > 0.5) {
+ ret = (t / x) * ((c - 0.5) - 0.5);
+ } else {
+ ret = x * c;
+ }
+ }
+
+ return ret;
+ }
+
+ /**
+ * Returns the value of log &Gamma;(1 + x) for -0&#46;5 &le; x &le; 1&#46;5. This implementation
+ * is based on the double precision implementation in the <em>NSWC Library of Mathematics
+ * Subroutines</em>, {@code DGMLN1}.
+ *
+ * @param x Argument.
+ * @return The value of {@code log(Gamma(1 + x))}.
+ * @throws NumberIsTooSmallException if {@code x < -0.5}.
+ * @throws NumberIsTooLargeException if {@code x > 1.5}.
+ * @since 3.1
+ */
+ public static double logGamma1p(final double x)
+ throws NumberIsTooSmallException, NumberIsTooLargeException {
+
+ if (x < -0.5) {
+ throw new NumberIsTooSmallException(x, -0.5, true);
+ }
+ if (x > 1.5) {
+ throw new NumberIsTooLargeException(x, 1.5, true);
+ }
+
+ return -FastMath.log1p(invGamma1pm1(x));
+ }
+
+ /**
+ * Returns the value of Γ(x). Based on the <em>NSWC Library of Mathematics Subroutines</em>
+ * double precision implementation, {@code DGAMMA}.
+ *
+ * @param x Argument.
+ * @return the value of {@code Gamma(x)}.
+ * @since 3.1
+ */
+ public static double gamma(final double x) {
+
+ if ((x == FastMath.rint(x)) && (x <= 0.0)) {
+ return Double.NaN;
+ }
+
+ final double ret;
+ final double absX = FastMath.abs(x);
+ if (absX <= 20.0) {
+ if (x >= 1.0) {
+ /*
+ * From the recurrence relation
+ * Gamma(x) = (x - 1) * ... * (x - n) * Gamma(x - n),
+ * then
+ * Gamma(t) = 1 / [1 + invGamma1pm1(t - 1)],
+ * where t = x - n. This means that t must satisfy
+ * -0.5 <= t - 1 <= 1.5.
+ */
+ double prod = 1.0;
+ double t = x;
+ while (t > 2.5) {
+ t -= 1.0;
+ prod *= t;
+ }
+ ret = prod / (1.0 + invGamma1pm1(t - 1.0));
+ } else {
+ /*
+ * From the recurrence relation
+ * Gamma(x) = Gamma(x + n + 1) / [x * (x + 1) * ... * (x + n)]
+ * then
+ * Gamma(x + n + 1) = 1 / [1 + invGamma1pm1(x + n)],
+ * which requires -0.5 <= x + n <= 1.5.
+ */
+ double prod = x;
+ double t = x;
+ while (t < -0.5) {
+ t += 1.0;
+ prod *= t;
+ }
+ ret = 1.0 / (prod * (1.0 + invGamma1pm1(t)));
+ }
+ } else {
+ final double y = absX + LANCZOS_G + 0.5;
+ final double gammaAbs =
+ SQRT_TWO_PI
+ / absX
+ * FastMath.pow(y, absX + 0.5)
+ * FastMath.exp(-y)
+ * lanczos(absX);
+ if (x > 0.0) {
+ ret = gammaAbs;
+ } else {
+ /*
+ * From the reflection formula
+ * Gamma(x) * Gamma(1 - x) * sin(pi * x) = pi,
+ * and the recurrence relation
+ * Gamma(1 - x) = -x * Gamma(-x),
+ * it is found
+ * Gamma(x) = -pi / [x * sin(pi * x) * Gamma(-x)].
+ */
+ ret = -FastMath.PI / (x * FastMath.sin(FastMath.PI * x) * gammaAbs);
+ }
+ }
+ return ret;
+ }
+}
diff --git a/src/main/java/org/apache/commons/math3/special/package-info.java b/src/main/java/org/apache/commons/math3/special/package-info.java
new file mode 100644
index 0000000..a9fc1fc
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/special/package-info.java
@@ -0,0 +1,18 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/** Implementations of special functions such as Beta and Gamma. */
+package org.apache.commons.math3.special;
diff --git a/src/main/java/org/apache/commons/math3/stat/Frequency.java b/src/main/java/org/apache/commons/math3/stat/Frequency.java
new file mode 100644
index 0000000..276382c
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/stat/Frequency.java
@@ -0,0 +1,664 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math3.stat;
+
+import org.apache.commons.math3.exception.MathIllegalArgumentException;
+import org.apache.commons.math3.exception.NullArgumentException;
+import org.apache.commons.math3.exception.util.LocalizedFormats;
+import org.apache.commons.math3.util.MathUtils;
+
+import java.io.Serializable;
+import java.text.NumberFormat;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Comparator;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.SortedMap;
+import java.util.TreeMap;
+
+/**
+ * Maintains a frequency distribution.
+ *
+ * <p>Accepts int, long, char or Comparable values. New values added must be comparable to those
+ * that have been added, otherwise the add method will throw an IllegalArgumentException.
+ *
+ * <p>Integer values (int, long, Integer, Long) are not distinguished by type -- i.e. <code>
+ * addValue(Long.valueOf(2)), addValue(2), addValue(2l)</code> all have the same effect (similarly
+ * for arguments to <code>getCount,</code> etc.).
+ *
+ * <p>NOTE: byte and short values will be implicitly converted to int values by the compiler, thus
+ * there are no explicit overloaded methods for these primitive types.
+ *
+ * <p>char values are converted by <code>addValue</code> to Character instances. As such, these
+ * values are not comparable to integral values, so attempts to combine integral types with chars in
+ * a frequency distribution will fail.
+ *
+ * <p>Float is not coerced to Double. Since they are not Comparable with each other the user must do
+ * any necessary coercion. Float.NaN and Double.NaN are not treated specially; they may occur in
+ * input and will occur in output if appropriate. </b>
+ *
+ * <p>The values are ordered using the default (natural order), unless a <code>Comparator</code> is
+ * supplied in the constructor.
+ */
+public class Frequency implements Serializable {
+
+ /** Serializable version identifier */
+ private static final long serialVersionUID = -3845586908418844111L;
+
+ /** underlying collection */
+ private final SortedMap<Comparable<?>, Long> freqTable;
+
+ /** Default constructor. */
+ public Frequency() {
+ freqTable = new TreeMap<Comparable<?>, Long>();
+ }
+
+ /**
+ * Constructor allowing values Comparator to be specified.
+ *
+ * @param comparator Comparator used to order values
+ */
+ @SuppressWarnings("unchecked") // TODO is the cast OK?
+ public Frequency(Comparator<?> comparator) {
+ freqTable =
+ new TreeMap<Comparable<?>, Long>((Comparator<? super Comparable<?>>) comparator);
+ }
+
+ /**
+ * Return a string representation of this frequency distribution.
+ *
+ * @return a string representation.
+ */
+ @Override
+ public String toString() {
+ NumberFormat nf = NumberFormat.getPercentInstance();
+ StringBuilder outBuffer = new StringBuilder();
+ outBuffer.append("Value \t Freq. \t Pct. \t Cum Pct. \n");
+ Iterator<Comparable<?>> iter = freqTable.keySet().iterator();
+ while (iter.hasNext()) {
+ Comparable<?> value = iter.next();
+ outBuffer.append(value);
+ outBuffer.append('\t');
+ outBuffer.append(getCount(value));
+ outBuffer.append('\t');
+ outBuffer.append(nf.format(getPct(value)));
+ outBuffer.append('\t');
+ outBuffer.append(nf.format(getCumPct(value)));
+ outBuffer.append('\n');
+ }
+ return outBuffer.toString();
+ }
+
+ /**
+ * Adds 1 to the frequency count for v.
+ *
+ * <p>If other objects have already been added to this Frequency, v must be comparable to those
+ * that have already been added.
+ *
+ * @param v the value to add.
+ * @throws MathIllegalArgumentException if <code>v</code> is not comparable with previous
+ * entries
+ */
+ public void addValue(Comparable<?> v) throws MathIllegalArgumentException {
+ incrementValue(v, 1);
+ }
+
+ /**
+ * Adds 1 to the frequency count for v.
+ *
+ * @param v the value to add.
+ * @throws MathIllegalArgumentException if the table contains entries not comparable to Long
+ */
+ public void addValue(int v) throws MathIllegalArgumentException {
+ addValue(Long.valueOf(v));
+ }
+
+ /**
+ * Adds 1 to the frequency count for v.
+ *
+ * @param v the value to add.
+ * @throws MathIllegalArgumentException if the table contains entries not comparable to Long
+ */
+ public void addValue(long v) throws MathIllegalArgumentException {
+ addValue(Long.valueOf(v));
+ }
+
+ /**
+ * Adds 1 to the frequency count for v.
+ *
+ * @param v the value to add.
+ * @throws MathIllegalArgumentException if the table contains entries not comparable to Char
+ */
+ public void addValue(char v) throws MathIllegalArgumentException {
+ addValue(Character.valueOf(v));
+ }
+
+ /**
+ * Increments the frequency count for v.
+ *
+ * <p>If other objects have already been added to this Frequency, v must be comparable to those
+ * that have already been added.
+ *
+ * @param v the value to add.
+ * @param increment the amount by which the value should be incremented
+ * @throws MathIllegalArgumentException if <code>v</code> is not comparable with previous
+ * entries
+ * @since 3.1
+ */
+ public void incrementValue(Comparable<?> v, long increment)
+ throws MathIllegalArgumentException {
+ Comparable<?> obj = v;
+ if (v instanceof Integer) {
+ obj = Long.valueOf(((Integer) v).longValue());
+ }
+ try {
+ Long count = freqTable.get(obj);
+ if (count == null) {
+ freqTable.put(obj, Long.valueOf(increment));
+ } else {
+ freqTable.put(obj, Long.valueOf(count.longValue() + increment));
+ }
+ } catch (ClassCastException ex) {
+ // TreeMap will throw ClassCastException if v is not comparable
+ throw new MathIllegalArgumentException(
+ LocalizedFormats.INSTANCES_NOT_COMPARABLE_TO_EXISTING_VALUES,
+ v.getClass().getName());
+ }
+ }
+
+ /**
+ * Increments the frequency count for v.
+ *
+ * <p>If other objects have already been added to this Frequency, v must be comparable to those
+ * that have already been added.
+ *
+ * @param v the value to add.
+ * @param increment the amount by which the value should be incremented
+ * @throws MathIllegalArgumentException if the table contains entries not comparable to Long
+ * @since 3.3
+ */
+ public void incrementValue(int v, long increment) throws MathIllegalArgumentException {
+ incrementValue(Long.valueOf(v), increment);
+ }
+
+ /**
+ * Increments the frequency count for v.
+ *
+ * <p>If other objects have already been added to this Frequency, v must be comparable to those
+ * that have already been added.
+ *
+ * @param v the value to add.
+ * @param increment the amount by which the value should be incremented
+ * @throws MathIllegalArgumentException if the table contains entries not comparable to Long
+ * @since 3.3
+ */
+ public void incrementValue(long v, long increment) throws MathIllegalArgumentException {
+ incrementValue(Long.valueOf(v), increment);
+ }
+
+ /**
+ * Increments the frequency count for v.
+ *
+ * <p>If other objects have already been added to this Frequency, v must be comparable to those
+ * that have already been added.
+ *
+ * @param v the value to add.
+ * @param increment the amount by which the value should be incremented
+ * @throws MathIllegalArgumentException if the table contains entries not comparable to Char
+ * @since 3.3
+ */
+ public void incrementValue(char v, long increment) throws MathIllegalArgumentException {
+ incrementValue(Character.valueOf(v), increment);
+ }
+
+ /** Clears the frequency table */
+ public void clear() {
+ freqTable.clear();
+ }
+
+ /**
+ * Returns an Iterator over the set of values that have been added.
+ *
+ * <p>If added values are integral (i.e., integers, longs, Integers, or Longs), they are
+ * converted to Longs when they are added, so the objects returned by the Iterator will in this
+ * case be Longs.
+ *
+ * @return values Iterator
+ */
+ public Iterator<Comparable<?>> valuesIterator() {
+ return freqTable.keySet().iterator();
+ }
+
+ /**
+ * Return an Iterator over the set of keys and values that have been added. Using the entry set
+ * to iterate is more efficient in the case where you need to access respective counts as well
+ * as values, since it doesn't require a "get" for every key...the value is provided in the
+ * Map.Entry.
+ *
+ * <p>If added values are integral (i.e., integers, longs, Integers, or Longs), they are
+ * converted to Longs when they are added, so the values of the map entries returned by the
+ * Iterator will in this case be Longs.
+ *
+ * @return entry set Iterator
+ * @since 3.1
+ */
+ public Iterator<Map.Entry<Comparable<?>, Long>> entrySetIterator() {
+ return freqTable.entrySet().iterator();
+ }
+
+ // -------------------------------------------------------------------------
+
+ /**
+ * Returns the sum of all frequencies.
+ *
+ * @return the total frequency count.
+ */
+ public long getSumFreq() {
+ long result = 0;
+ Iterator<Long> iterator = freqTable.values().iterator();
+ while (iterator.hasNext()) {
+ result += iterator.next().longValue();
+ }
+ return result;
+ }
+
+ /**
+ * Returns the number of values equal to v. Returns 0 if the value is not comparable.
+ *
+ * @param v the value to lookup.
+ * @return the frequency of v.
+ */
+ public long getCount(Comparable<?> v) {
+ if (v instanceof Integer) {
+ return getCount(((Integer) v).longValue());
+ }
+ long result = 0;
+ try {
+ Long count = freqTable.get(v);
+ if (count != null) {
+ result = count.longValue();
+ }
+ } catch (ClassCastException ex) { // NOPMD
+ // ignore and return 0 -- ClassCastException will be thrown if value is not comparable
+ }
+ return result;
+ }
+
+ /**
+ * Returns the number of values equal to v.
+ *
+ * @param v the value to lookup.
+ * @return the frequency of v.
+ */
+ public long getCount(int v) {
+ return getCount(Long.valueOf(v));
+ }
+
+ /**
+ * Returns the number of values equal to v.
+ *
+ * @param v the value to lookup.
+ * @return the frequency of v.
+ */
+ public long getCount(long v) {
+ return getCount(Long.valueOf(v));
+ }
+
+ /**
+ * Returns the number of values equal to v.
+ *
+ * @param v the value to lookup.
+ * @return the frequency of v.
+ */
+ public long getCount(char v) {
+ return getCount(Character.valueOf(v));
+ }
+
+ /**
+ * Returns the number of values in the frequency table.
+ *
+ * @return the number of unique values that have been added to the frequency table.
+ * @see #valuesIterator()
+ */
+ public int getUniqueCount() {
+ return freqTable.keySet().size();
+ }
+
+ /**
+ * Returns the percentage of values that are equal to v (as a proportion between 0 and 1).
+ *
+ * <p>Returns <code>Double.NaN</code> if no values have been added. Returns 0 if at least one
+ * value has been added, but v is not comparable to the values set.
+ *
+ * @param v the value to lookup
+ * @return the proportion of values equal to v
+ */
+ public double getPct(Comparable<?> v) {
+ final long sumFreq = getSumFreq();
+ if (sumFreq == 0) {
+ return Double.NaN;
+ }
+ return (double) getCount(v) / (double) sumFreq;
+ }
+
+ /**
+ * Returns the percentage of values that are equal to v (as a proportion between 0 and 1).
+ *
+ * @param v the value to lookup
+ * @return the proportion of values equal to v
+ */
+ public double getPct(int v) {
+ return getPct(Long.valueOf(v));
+ }
+
+ /**
+ * Returns the percentage of values that are equal to v (as a proportion between 0 and 1).
+ *
+ * @param v the value to lookup
+ * @return the proportion of values equal to v
+ */
+ public double getPct(long v) {
+ return getPct(Long.valueOf(v));
+ }
+
+ /**
+ * Returns the percentage of values that are equal to v (as a proportion between 0 and 1).
+ *
+ * @param v the value to lookup
+ * @return the proportion of values equal to v
+ */
+ public double getPct(char v) {
+ return getPct(Character.valueOf(v));
+ }
+
+ // -----------------------------------------------------------------------------------------
+
+ /**
+ * Returns the cumulative frequency of values less than or equal to v.
+ *
+ * <p>Returns 0 if v is not comparable to the values set.
+ *
+ * @param v the value to lookup.
+ * @return the proportion of values equal to v
+ */
+ @SuppressWarnings({"rawtypes", "unchecked"})
+ public long getCumFreq(Comparable<?> v) {
+ if (getSumFreq() == 0) {
+ return 0;
+ }
+ if (v instanceof Integer) {
+ return getCumFreq(((Integer) v).longValue());
+ }
+ Comparator<Comparable<?>> c = (Comparator<Comparable<?>>) freqTable.comparator();
+ if (c == null) {
+ c = new NaturalComparator();
+ }
+ long result = 0;
+
+ try {
+ Long value = freqTable.get(v);
+ if (value != null) {
+ result = value.longValue();
+ }
+ } catch (ClassCastException ex) {
+ return result; // v is not comparable
+ }
+
+ if (c.compare(v, freqTable.firstKey()) < 0) {
+ return 0; // v is comparable, but less than first value
+ }
+
+ if (c.compare(v, freqTable.lastKey()) >= 0) {
+ return getSumFreq(); // v is comparable, but greater than the last value
+ }
+
+ Iterator<Comparable<?>> values = valuesIterator();
+ while (values.hasNext()) {
+ Comparable<?> nextValue = values.next();
+ if (c.compare(v, nextValue) > 0) {
+ result += getCount(nextValue);
+ } else {
+ return result;
+ }
+ }
+ return result;
+ }
+
+ /**
+ * Returns the cumulative frequency of values less than or equal to v.
+ *
+ * <p>Returns 0 if v is not comparable to the values set.
+ *
+ * @param v the value to lookup
+ * @return the proportion of values equal to v
+ */
+ public long getCumFreq(int v) {
+ return getCumFreq(Long.valueOf(v));
+ }
+
+ /**
+ * Returns the cumulative frequency of values less than or equal to v.
+ *
+ * <p>Returns 0 if v is not comparable to the values set.
+ *
+ * @param v the value to lookup
+ * @return the proportion of values equal to v
+ */
+ public long getCumFreq(long v) {
+ return getCumFreq(Long.valueOf(v));
+ }
+
+ /**
+ * Returns the cumulative frequency of values less than or equal to v.
+ *
+ * <p>Returns 0 if v is not comparable to the values set.
+ *
+ * @param v the value to lookup
+ * @return the proportion of values equal to v
+ */
+ public long getCumFreq(char v) {
+ return getCumFreq(Character.valueOf(v));
+ }
+
+ // ----------------------------------------------------------------------------------------------
+
+ /**
+ * Returns the cumulative percentage of values less than or equal to v (as a proportion between
+ * 0 and 1).
+ *
+ * <p>Returns <code>Double.NaN</code> if no values have been added. Returns 0 if at least one
+ * value has been added, but v is not comparable to the values set.
+ *
+ * @param v the value to lookup
+ * @return the proportion of values less than or equal to v
+ */
+ public double getCumPct(Comparable<?> v) {
+ final long sumFreq = getSumFreq();
+ if (sumFreq == 0) {
+ return Double.NaN;
+ }
+ return (double) getCumFreq(v) / (double) sumFreq;
+ }
+
+ /**
+ * Returns the cumulative percentage of values less than or equal to v (as a proportion between
+ * 0 and 1).
+ *
+ * <p>Returns 0 if v is not comparable to the values set.
+ *
+ * @param v the value to lookup
+ * @return the proportion of values less than or equal to v
+ */
+ public double getCumPct(int v) {
+ return getCumPct(Long.valueOf(v));
+ }
+
+ /**
+ * Returns the cumulative percentage of values less than or equal to v (as a proportion between
+ * 0 and 1).
+ *
+ * <p>Returns 0 if v is not comparable to the values set.
+ *
+ * @param v the value to lookup
+ * @return the proportion of values less than or equal to v
+ */
+ public double getCumPct(long v) {
+ return getCumPct(Long.valueOf(v));
+ }
+
+ /**
+ * Returns the cumulative percentage of values less than or equal to v (as a proportion between
+ * 0 and 1).
+ *
+ * <p>Returns 0 if v is not comparable to the values set.
+ *
+ * @param v the value to lookup
+ * @return the proportion of values less than or equal to v
+ */
+ public double getCumPct(char v) {
+ return getCumPct(Character.valueOf(v));
+ }
+
+ /**
+ * Returns the mode value(s) in comparator order.
+ *
+ * @return a list containing the value(s) which appear most often.
+ * @since 3.3
+ */
+ public List<Comparable<?>> getMode() {
+ long mostPopular = 0; // frequencies are always positive
+
+ // Get the max count first, so we avoid having to recreate the List each time
+ for (Long l : freqTable.values()) {
+ long frequency = l.longValue();
+ if (frequency > mostPopular) {
+ mostPopular = frequency;
+ }
+ }
+
+ List<Comparable<?>> modeList = new ArrayList<Comparable<?>>();
+ for (Entry<Comparable<?>, Long> ent : freqTable.entrySet()) {
+ long frequency = ent.getValue().longValue();
+ if (frequency == mostPopular) {
+ modeList.add(ent.getKey());
+ }
+ }
+ return modeList;
+ }
+
+ // ----------------------------------------------------------------------------------------------
+
+ /**
+ * Merge another Frequency object's counts into this instance. This Frequency's counts will be
+ * incremented (or set when not already set) by the counts represented by other.
+ *
+ * @param other the other {@link Frequency} object to be merged
+ * @throws NullArgumentException if {@code other} is null
+ * @since 3.1
+ */
+ public void merge(final Frequency other) throws NullArgumentException {
+ MathUtils.checkNotNull(other, LocalizedFormats.NULL_NOT_ALLOWED);
+
+ final Iterator<Map.Entry<Comparable<?>, Long>> iter = other.entrySetIterator();
+ while (iter.hasNext()) {
+ final Map.Entry<Comparable<?>, Long> entry = iter.next();
+ incrementValue(entry.getKey(), entry.getValue().longValue());
+ }
+ }
+
+ /**
+ * Merge a {@link Collection} of {@link Frequency} objects into this instance. This Frequency's
+ * counts will be incremented (or set when not already set) by the counts represented by each of
+ * the others.
+ *
+ * @param others the other {@link Frequency} objects to be merged
+ * @throws NullArgumentException if the collection is null
+ * @since 3.1
+ */
+ public void merge(final Collection<Frequency> others) throws NullArgumentException {
+ MathUtils.checkNotNull(others, LocalizedFormats.NULL_NOT_ALLOWED);
+
+ for (final Frequency freq : others) {
+ merge(freq);
+ }
+ }
+
+ // ----------------------------------------------------------------------------------------------
+
+ /**
+ * A Comparator that compares comparable objects using the natural order. Copied from Commons
+ * Collections ComparableComparator.
+ *
+ * @param <T> the type of the objects compared
+ */
+ private static class NaturalComparator<T extends Comparable<T>>
+ implements Comparator<Comparable<T>>, Serializable {
+
+ /** Serializable version identifier */
+ private static final long serialVersionUID = -3852193713161395148L;
+
+ /**
+ * Compare the two {@link Comparable Comparable} arguments. This method is equivalent to:
+ *
+ * <pre>(({@link Comparable Comparable})o1).{@link Comparable#compareTo compareTo}(o2)</pre>
+ *
+ * @param o1 the first object
+ * @param o2 the second object
+ * @return result of comparison
+ * @throws NullPointerException when <i>o1</i> is <code>null</code>, or when <code>
+ * ((Comparable)o1).compareTo(o2)</code> does
+ * @throws ClassCastException when <i>o1</i> is not a {@link Comparable Comparable}, or when
+ * <code>((Comparable)o1).compareTo(o2)</code> does
+ */
+ @SuppressWarnings("unchecked") // cast to (T) may throw ClassCastException, see Javadoc
+ public int compare(Comparable<T> o1, Comparable<T> o2) {
+ return o1.compareTo((T) o2);
+ }
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public int hashCode() {
+ final int prime = 31;
+ int result = 1;
+ result = prime * result + ((freqTable == null) ? 0 : freqTable.hashCode());
+ return result;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj) {
+ return true;
+ }
+ if (!(obj instanceof Frequency)) {
+ return false;
+ }
+ Frequency other = (Frequency) obj;
+ if (freqTable == null) {
+ if (other.freqTable != null) {
+ return false;
+ }
+ } else if (!freqTable.equals(other.freqTable)) {
+ return false;
+ }
+ return true;
+ }
+}
diff --git a/src/main/java/org/apache/commons/math3/stat/StatUtils.java b/src/main/java/org/apache/commons/math3/stat/StatUtils.java
new file mode 100644
index 0000000..31d75e8
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/stat/StatUtils.java
@@ -0,0 +1,852 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math3.stat;
+
+import org.apache.commons.math3.exception.DimensionMismatchException;
+import org.apache.commons.math3.exception.MathIllegalArgumentException;
+import org.apache.commons.math3.exception.NoDataException;
+import org.apache.commons.math3.exception.NotPositiveException;
+import org.apache.commons.math3.exception.NullArgumentException;
+import org.apache.commons.math3.exception.NumberIsTooSmallException;
+import org.apache.commons.math3.exception.util.LocalizedFormats;
+import org.apache.commons.math3.stat.descriptive.DescriptiveStatistics;
+import org.apache.commons.math3.stat.descriptive.UnivariateStatistic;
+import org.apache.commons.math3.stat.descriptive.moment.GeometricMean;
+import org.apache.commons.math3.stat.descriptive.moment.Mean;
+import org.apache.commons.math3.stat.descriptive.moment.Variance;
+import org.apache.commons.math3.stat.descriptive.rank.Max;
+import org.apache.commons.math3.stat.descriptive.rank.Min;
+import org.apache.commons.math3.stat.descriptive.rank.Percentile;
+import org.apache.commons.math3.stat.descriptive.summary.Product;
+import org.apache.commons.math3.stat.descriptive.summary.Sum;
+import org.apache.commons.math3.stat.descriptive.summary.SumOfLogs;
+import org.apache.commons.math3.stat.descriptive.summary.SumOfSquares;
+
+import java.util.List;
+
+/**
+ * StatUtils provides static methods for computing statistics based on data stored in double[]
+ * arrays.
+ */
+public final class StatUtils {
+
+ /** sum */
+ private static final UnivariateStatistic SUM = new Sum();
+
+ /** sumSq */
+ private static final UnivariateStatistic SUM_OF_SQUARES = new SumOfSquares();
+
+ /** prod */
+ private static final UnivariateStatistic PRODUCT = new Product();
+
+ /** sumLog */
+ private static final UnivariateStatistic SUM_OF_LOGS = new SumOfLogs();
+
+ /** min */
+ private static final UnivariateStatistic MIN = new Min();
+
+ /** max */
+ private static final UnivariateStatistic MAX = new Max();
+
+ /** mean */
+ private static final UnivariateStatistic MEAN = new Mean();
+
+ /** variance */
+ private static final Variance VARIANCE = new Variance();
+
+ /** percentile */
+ private static final Percentile PERCENTILE = new Percentile();
+
+ /** geometric mean */
+ private static final GeometricMean GEOMETRIC_MEAN = new GeometricMean();
+
+ /** Private Constructor */
+ private StatUtils() {}
+
+ /**
+ * Returns the sum of the values in the input array, or <code>Double.NaN</code> if the array is
+ * empty.
+ *
+ * <p>Throws <code>IllegalArgumentException</code> if the input array is null.
+ *
+ * @param values array of values to sum
+ * @return the sum of the values or <code>Double.NaN</code> if the array is empty
+ * @throws MathIllegalArgumentException if the array is null
+ */
+ public static double sum(final double[] values) throws MathIllegalArgumentException {
+ return SUM.evaluate(values);
+ }
+
+ /**
+ * Returns the sum of the entries in the specified portion of the input array, or <code>
+ * Double.NaN</code> if the designated subarray is empty.
+ *
+ * <p>Throws <code>IllegalArgumentException</code> if the array is null.
+ *
+ * @param values the input array
+ * @param begin index of the first array element to include
+ * @param length the number of elements to include
+ * @return the sum of the values or Double.NaN if length = 0
+ * @throws MathIllegalArgumentException if the array is null or the array index parameters are
+ * not valid
+ */
+ public static double sum(final double[] values, final int begin, final int length)
+ throws MathIllegalArgumentException {
+ return SUM.evaluate(values, begin, length);
+ }
+
+ /**
+ * Returns the sum of the squares of the entries in the input array, or <code>Double.NaN</code>
+ * if the array is empty.
+ *
+ * <p>Throws <code>IllegalArgumentException</code> if the array is null.
+ *
+ * @param values input array
+ * @return the sum of the squared values or <code>Double.NaN</code> if the array is empty
+ * @throws MathIllegalArgumentException if the array is null
+ */
+ public static double sumSq(final double[] values) throws MathIllegalArgumentException {
+ return SUM_OF_SQUARES.evaluate(values);
+ }
+
+ /**
+ * Returns the sum of the squares of the entries in the specified portion of the input array, or
+ * <code>Double.NaN</code> if the designated subarray is empty.
+ *
+ * <p>Throws <code>IllegalArgumentException</code> if the array is null.
+ *
+ * @param values the input array
+ * @param begin index of the first array element to include
+ * @param length the number of elements to include
+ * @return the sum of the squares of the values or Double.NaN if length = 0
+ * @throws MathIllegalArgumentException if the array is null or the array index parameters are
+ * not valid
+ */
+ public static double sumSq(final double[] values, final int begin, final int length)
+ throws MathIllegalArgumentException {
+ return SUM_OF_SQUARES.evaluate(values, begin, length);
+ }
+
+ /**
+ * Returns the product of the entries in the input array, or <code>Double.NaN</code> if the
+ * array is empty.
+ *
+ * <p>Throws <code>IllegalArgumentException</code> if the array is null.
+ *
+ * @param values the input array
+ * @return the product of the values or Double.NaN if the array is empty
+ * @throws MathIllegalArgumentException if the array is null
+ */
+ public static double product(final double[] values) throws MathIllegalArgumentException {
+ return PRODUCT.evaluate(values);
+ }
+
+ /**
+ * Returns the product of the entries in the specified portion of the input array, or <code>
+ * Double.NaN</code> if the designated subarray is empty.
+ *
+ * <p>Throws <code>IllegalArgumentException</code> if the array is null.
+ *
+ * @param values the input array
+ * @param begin index of the first array element to include
+ * @param length the number of elements to include
+ * @return the product of the values or Double.NaN if length = 0
+ * @throws MathIllegalArgumentException if the array is null or the array index parameters are
+ * not valid
+ */
+ public static double product(final double[] values, final int begin, final int length)
+ throws MathIllegalArgumentException {
+ return PRODUCT.evaluate(values, begin, length);
+ }
+
+ /**
+ * Returns the sum of the natural logs of the entries in the input array, or <code>Double.NaN
+ * </code> if the array is empty.
+ *
+ * <p>Throws <code>IllegalArgumentException</code> if the array is null.
+ *
+ * <p>See {@link org.apache.commons.math3.stat.descriptive.summary.SumOfLogs}.
+ *
+ * @param values the input array
+ * @return the sum of the natural logs of the values or Double.NaN if the array is empty
+ * @throws MathIllegalArgumentException if the array is null
+ */
+ public static double sumLog(final double[] values) throws MathIllegalArgumentException {
+ return SUM_OF_LOGS.evaluate(values);
+ }
+
+ /**
+ * Returns the sum of the natural logs of the entries in the specified portion of the input
+ * array, or <code>Double.NaN</code> if the designated subarray is empty.
+ *
+ * <p>Throws <code>IllegalArgumentException</code> if the array is null.
+ *
+ * <p>See {@link org.apache.commons.math3.stat.descriptive.summary.SumOfLogs}.
+ *
+ * @param values the input array
+ * @param begin index of the first array element to include
+ * @param length the number of elements to include
+ * @return the sum of the natural logs of the values or Double.NaN if length = 0
+ * @throws MathIllegalArgumentException if the array is null or the array index parameters are
+ * not valid
+ */
+ public static double sumLog(final double[] values, final int begin, final int length)
+ throws MathIllegalArgumentException {
+ return SUM_OF_LOGS.evaluate(values, begin, length);
+ }
+
+ /**
+ * Returns the arithmetic mean of the entries in the input array, or <code>Double.NaN</code> if
+ * the array is empty.
+ *
+ * <p>Throws <code>IllegalArgumentException</code> if the array is null.
+ *
+ * <p>See {@link org.apache.commons.math3.stat.descriptive.moment.Mean} for details on the
+ * computing algorithm.
+ *
+ * @param values the input array
+ * @return the mean of the values or Double.NaN if the array is empty
+ * @throws MathIllegalArgumentException if the array is null
+ */
+ public static double mean(final double[] values) throws MathIllegalArgumentException {
+ return MEAN.evaluate(values);
+ }
+
+ /**
+ * Returns the arithmetic mean of the entries in the specified portion of the input array, or
+ * <code>Double.NaN</code> if the designated subarray is empty.
+ *
+ * <p>Throws <code>IllegalArgumentException</code> if the array is null.
+ *
+ * <p>See {@link org.apache.commons.math3.stat.descriptive.moment.Mean} for details on the
+ * computing algorithm.
+ *
+ * @param values the input array
+ * @param begin index of the first array element to include
+ * @param length the number of elements to include
+ * @return the mean of the values or Double.NaN if length = 0
+ * @throws MathIllegalArgumentException if the array is null or the array index parameters are
+ * not valid
+ */
+ public static double mean(final double[] values, final int begin, final int length)
+ throws MathIllegalArgumentException {
+ return MEAN.evaluate(values, begin, length);
+ }
+
+ /**
+ * Returns the geometric mean of the entries in the input array, or <code>Double.NaN</code> if
+ * the array is empty.
+ *
+ * <p>Throws <code>IllegalArgumentException</code> if the array is null.
+ *
+ * <p>See {@link org.apache.commons.math3.stat.descriptive.moment.GeometricMean} for details on
+ * the computing algorithm.
+ *
+ * @param values the input array
+ * @return the geometric mean of the values or Double.NaN if the array is empty
+ * @throws MathIllegalArgumentException if the array is null
+ */
+ public static double geometricMean(final double[] values) throws MathIllegalArgumentException {
+ return GEOMETRIC_MEAN.evaluate(values);
+ }
+
+ /**
+ * Returns the geometric mean of the entries in the specified portion of the input array, or
+ * <code>Double.NaN</code> if the designated subarray is empty.
+ *
+ * <p>Throws <code>IllegalArgumentException</code> if the array is null.
+ *
+ * <p>See {@link org.apache.commons.math3.stat.descriptive.moment.GeometricMean} for details on
+ * the computing algorithm.
+ *
+ * @param values the input array
+ * @param begin index of the first array element to include
+ * @param length the number of elements to include
+ * @return the geometric mean of the values or Double.NaN if length = 0
+ * @throws MathIllegalArgumentException if the array is null or the array index parameters are
+ * not valid
+ */
+ public static double geometricMean(final double[] values, final int begin, final int length)
+ throws MathIllegalArgumentException {
+ return GEOMETRIC_MEAN.evaluate(values, begin, length);
+ }
+
+ /**
+ * Returns the variance of the entries in the input array, or <code>Double.NaN</code> if the
+ * array is empty.
+ *
+ * <p>This method returns the bias-corrected sample variance (using {@code n - 1} in the
+ * denominator). Use {@link #populationVariance(double[])} for the non-bias-corrected population
+ * variance.
+ *
+ * <p>See {@link org.apache.commons.math3.stat.descriptive.moment.Variance} for details on the
+ * computing algorithm.
+ *
+ * <p>Returns 0 for a single-value (i.e. length = 1) sample.
+ *
+ * <p>Throws <code>MathIllegalArgumentException</code> if the array is null.
+ *
+ * @param values the input array
+ * @return the variance of the values or Double.NaN if the array is empty
+ * @throws MathIllegalArgumentException if the array is null
+ */
+ public static double variance(final double[] values) throws MathIllegalArgumentException {
+ return VARIANCE.evaluate(values);
+ }
+
+ /**
+ * Returns the variance of the entries in the specified portion of the input array, or <code>
+ * Double.NaN</code> if the designated subarray is empty.
+ *
+ * <p>This method returns the bias-corrected sample variance (using {@code n - 1} in the
+ * denominator). Use {@link #populationVariance(double[], int, int)} for the non-bias-corrected
+ * population variance.
+ *
+ * <p>See {@link org.apache.commons.math3.stat.descriptive.moment.Variance} for details on the
+ * computing algorithm.
+ *
+ * <p>Returns 0 for a single-value (i.e. length = 1) sample.
+ *
+ * <p>Throws <code>MathIllegalArgumentException</code> if the array is null or the array index
+ * parameters are not valid.
+ *
+ * @param values the input array
+ * @param begin index of the first array element to include
+ * @param length the number of elements to include
+ * @return the variance of the values or Double.NaN if length = 0
+ * @throws MathIllegalArgumentException if the array is null or the array index parameters are
+ * not valid
+ */
+ public static double variance(final double[] values, final int begin, final int length)
+ throws MathIllegalArgumentException {
+ return VARIANCE.evaluate(values, begin, length);
+ }
+
+ /**
+ * Returns the variance of the entries in the specified portion of the input array, using the
+ * precomputed mean value. Returns <code>Double.NaN</code> if the designated subarray is empty.
+ *
+ * <p>This method returns the bias-corrected sample variance (using {@code n - 1} in the
+ * denominator). Use {@link #populationVariance(double[], double, int, int)} for the
+ * non-bias-corrected population variance.
+ *
+ * <p>See {@link org.apache.commons.math3.stat.descriptive.moment.Variance} for details on the
+ * computing algorithm.
+ *
+ * <p>The formula used assumes that the supplied mean value is the arithmetic mean of the sample
+ * data, not a known population parameter. This method is supplied only to save computation when
+ * the mean has already been computed.
+ *
+ * <p>Returns 0 for a single-value (i.e. length = 1) sample.
+ *
+ * <p>Throws <code>MathIllegalArgumentException</code> if the array is null or the array index
+ * parameters are not valid.
+ *
+ * @param values the input array
+ * @param mean the precomputed mean value
+ * @param begin index of the first array element to include
+ * @param length the number of elements to include
+ * @return the variance of the values or Double.NaN if length = 0
+ * @throws MathIllegalArgumentException if the array is null or the array index parameters are
+ * not valid
+ */
+ public static double variance(
+ final double[] values, final double mean, final int begin, final int length)
+ throws MathIllegalArgumentException {
+ return VARIANCE.evaluate(values, mean, begin, length);
+ }
+
+ /**
+ * Returns the variance of the entries in the input array, using the precomputed mean value.
+ * Returns <code>Double.NaN</code> if the array is empty.
+ *
+ * <p>This method returns the bias-corrected sample variance (using {@code n - 1} in the
+ * denominator). Use {@link #populationVariance(double[], double)} for the non-bias-corrected
+ * population variance.
+ *
+ * <p>See {@link org.apache.commons.math3.stat.descriptive.moment.Variance} for details on the
+ * computing algorithm.
+ *
+ * <p>The formula used assumes that the supplied mean value is the arithmetic mean of the sample
+ * data, not a known population parameter. This method is supplied only to save computation when
+ * the mean has already been computed.
+ *
+ * <p>Returns 0 for a single-value (i.e. length = 1) sample.
+ *
+ * <p>Throws <code>MathIllegalArgumentException</code> if the array is null.
+ *
+ * @param values the input array
+ * @param mean the precomputed mean value
+ * @return the variance of the values or Double.NaN if the array is empty
+ * @throws MathIllegalArgumentException if the array is null
+ */
+ public static double variance(final double[] values, final double mean)
+ throws MathIllegalArgumentException {
+ return VARIANCE.evaluate(values, mean);
+ }
+
+ /**
+ * Returns the <a href="http://en.wikibooks.org/wiki/Statistics/Summary/Variance">population
+ * variance</a> of the entries in the input array, or <code>Double.NaN</code> if the array is
+ * empty.
+ *
+ * <p>See {@link org.apache.commons.math3.stat.descriptive.moment.Variance} for details on the
+ * formula and computing algorithm.
+ *
+ * <p>Returns 0 for a single-value (i.e. length = 1) sample.
+ *
+ * <p>Throws <code>MathIllegalArgumentException</code> if the array is null.
+ *
+ * @param values the input array
+ * @return the population variance of the values or Double.NaN if the array is empty
+ * @throws MathIllegalArgumentException if the array is null
+ */
+ public static double populationVariance(final double[] values)
+ throws MathIllegalArgumentException {
+ return new Variance(false).evaluate(values);
+ }
+
+ /**
+ * Returns the <a href="http://en.wikibooks.org/wiki/Statistics/Summary/Variance">population
+ * variance</a> of the entries in the specified portion of the input array, or <code>Double.NaN
+ * </code> if the designated subarray is empty.
+ *
+ * <p>See {@link org.apache.commons.math3.stat.descriptive.moment.Variance} for details on the
+ * computing algorithm.
+ *
+ * <p>Returns 0 for a single-value (i.e. length = 1) sample.
+ *
+ * <p>Throws <code>MathIllegalArgumentException</code> if the array is null or the array index
+ * parameters are not valid.
+ *
+ * @param values the input array
+ * @param begin index of the first array element to include
+ * @param length the number of elements to include
+ * @return the population variance of the values or Double.NaN if length = 0
+ * @throws MathIllegalArgumentException if the array is null or the array index parameters are
+ * not valid
+ */
+ public static double populationVariance(
+ final double[] values, final int begin, final int length)
+ throws MathIllegalArgumentException {
+ return new Variance(false).evaluate(values, begin, length);
+ }
+
+ /**
+ * Returns the <a href="http://en.wikibooks.org/wiki/Statistics/Summary/Variance">population
+ * variance</a> of the entries in the specified portion of the input array, using the
+ * precomputed mean value. Returns <code>Double.NaN</code> if the designated subarray is empty.
+ *
+ * <p>See {@link org.apache.commons.math3.stat.descriptive.moment.Variance} for details on the
+ * computing algorithm.
+ *
+ * <p>The formula used assumes that the supplied mean value is the arithmetic mean of the sample
+ * data, not a known population parameter. This method is supplied only to save computation when
+ * the mean has already been computed.
+ *
+ * <p>Returns 0 for a single-value (i.e. length = 1) sample.
+ *
+ * <p>Throws <code>MathIllegalArgumentException</code> if the array is null or the array index
+ * parameters are not valid.
+ *
+ * @param values the input array
+ * @param mean the precomputed mean value
+ * @param begin index of the first array element to include
+ * @param length the number of elements to include
+ * @return the population variance of the values or Double.NaN if length = 0
+ * @throws MathIllegalArgumentException if the array is null or the array index parameters are
+ * not valid
+ */
+ public static double populationVariance(
+ final double[] values, final double mean, final int begin, final int length)
+ throws MathIllegalArgumentException {
+ return new Variance(false).evaluate(values, mean, begin, length);
+ }
+
+ /**
+ * Returns the <a href="http://en.wikibooks.org/wiki/Statistics/Summary/Variance">population
+ * variance</a> of the entries in the input array, using the precomputed mean value. Returns
+ * <code>Double.NaN</code> if the array is empty.
+ *
+ * <p>See {@link org.apache.commons.math3.stat.descriptive.moment.Variance} for details on the
+ * computing algorithm.
+ *
+ * <p>The formula used assumes that the supplied mean value is the arithmetic mean of the sample
+ * data, not a known population parameter. This method is supplied only to save computation when
+ * the mean has already been computed.
+ *
+ * <p>Returns 0 for a single-value (i.e. length = 1) sample.
+ *
+ * <p>Throws <code>MathIllegalArgumentException</code> if the array is null.
+ *
+ * @param values the input array
+ * @param mean the precomputed mean value
+ * @return the population variance of the values or Double.NaN if the array is empty
+ * @throws MathIllegalArgumentException if the array is null
+ */
+ public static double populationVariance(final double[] values, final double mean)
+ throws MathIllegalArgumentException {
+ return new Variance(false).evaluate(values, mean);
+ }
+
+ /**
+ * Returns the maximum of the entries in the input array, or <code>Double.NaN</code> if the
+ * array is empty.
+ *
+ * <p>Throws <code>MathIllegalArgumentException</code> if the array is null.
+ *
+ * <p>
+ *
+ * <ul>
+ * <li>The result is <code>NaN</code> iff all values are <code>NaN</code> (i.e. <code>NaN
+ * </code> values have no impact on the value of the statistic).
+ * <li>If any of the values equals <code>Double.POSITIVE_INFINITY</code>, the result is <code>
+ * Double.POSITIVE_INFINITY.</code>
+ * </ul>
+ *
+ * @param values the input array
+ * @return the maximum of the values or Double.NaN if the array is empty
+ * @throws MathIllegalArgumentException if the array is null
+ */
+ public static double max(final double[] values) throws MathIllegalArgumentException {
+ return MAX.evaluate(values);
+ }
+
+ /**
+ * Returns the maximum of the entries in the specified portion of the input array, or <code>
+ * Double.NaN</code> if the designated subarray is empty.
+ *
+ * <p>Throws <code>MathIllegalArgumentException</code> if the array is null or the array index
+ * parameters are not valid.
+ *
+ * <p>
+ *
+ * <ul>
+ * <li>The result is <code>NaN</code> iff all values are <code>NaN</code> (i.e. <code>NaN
+ * </code> values have no impact on the value of the statistic).
+ * <li>If any of the values equals <code>Double.POSITIVE_INFINITY</code>, the result is <code>
+ * Double.POSITIVE_INFINITY.</code>
+ * </ul>
+ *
+ * @param values the input array
+ * @param begin index of the first array element to include
+ * @param length the number of elements to include
+ * @return the maximum of the values or Double.NaN if length = 0
+ * @throws MathIllegalArgumentException if the array is null or the array index parameters are
+ * not valid
+ */
+ public static double max(final double[] values, final int begin, final int length)
+ throws MathIllegalArgumentException {
+ return MAX.evaluate(values, begin, length);
+ }
+
+ /**
+ * Returns the minimum of the entries in the input array, or <code>Double.NaN</code> if the
+ * array is empty.
+ *
+ * <p>Throws <code>MathIllegalArgumentException</code> if the array is null.
+ *
+ * <p>
+ *
+ * <ul>
+ * <li>The result is <code>NaN</code> iff all values are <code>NaN</code> (i.e. <code>NaN
+ * </code> values have no impact on the value of the statistic).
+ * <li>If any of the values equals <code>Double.NEGATIVE_INFINITY</code>, the result is <code>
+ * Double.NEGATIVE_INFINITY.</code>
+ * </ul>
+ *
+ * @param values the input array
+ * @return the minimum of the values or Double.NaN if the array is empty
+ * @throws MathIllegalArgumentException if the array is null
+ */
+ public static double min(final double[] values) throws MathIllegalArgumentException {
+ return MIN.evaluate(values);
+ }
+
+ /**
+ * Returns the minimum of the entries in the specified portion of the input array, or <code>
+ * Double.NaN</code> if the designated subarray is empty.
+ *
+ * <p>Throws <code>MathIllegalArgumentException</code> if the array is null or the array index
+ * parameters are not valid.
+ *
+ * <p>
+ *
+ * <ul>
+ * <li>The result is <code>NaN</code> iff all values are <code>NaN</code> (i.e. <code>NaN
+ * </code> values have no impact on the value of the statistic).
+ * <li>If any of the values equals <code>Double.NEGATIVE_INFINITY</code>, the result is <code>
+ * Double.NEGATIVE_INFINITY.</code>
+ * </ul>
+ *
+ * @param values the input array
+ * @param begin index of the first array element to include
+ * @param length the number of elements to include
+ * @return the minimum of the values or Double.NaN if length = 0
+ * @throws MathIllegalArgumentException if the array is null or the array index parameters are
+ * not valid
+ */
+ public static double min(final double[] values, final int begin, final int length)
+ throws MathIllegalArgumentException {
+ return MIN.evaluate(values, begin, length);
+ }
+
+ /**
+ * Returns an estimate of the <code>p</code>th percentile of the values in the <code>values
+ * </code> array.
+ *
+ * <p>
+ *
+ * <ul>
+ * <li>Returns <code>Double.NaN</code> if <code>values</code> has length <code>0</code>
+ * <li>Returns (for any value of <code>p</code>) <code>values[0]</code> if <code>values</code>
+ * has length <code>1</code>
+ * <li>Throws <code>IllegalArgumentException</code> if <code>values</code> is null or p is not
+ * a valid quantile value (p must be greater than 0 and less than or equal to 100)
+ * </ul>
+ *
+ * <p>See {@link org.apache.commons.math3.stat.descriptive.rank.Percentile} for a description of
+ * the percentile estimation algorithm used.
+ *
+ * @param values input array of values
+ * @param p the percentile value to compute
+ * @return the percentile value or Double.NaN if the array is empty
+ * @throws MathIllegalArgumentException if <code>values</code> is null or p is invalid
+ */
+ public static double percentile(final double[] values, final double p)
+ throws MathIllegalArgumentException {
+ return PERCENTILE.evaluate(values, p);
+ }
+
+ /**
+ * Returns an estimate of the <code>p</code>th percentile of the values in the <code>values
+ * </code> array, starting with the element in (0-based) position <code>begin</code> in the
+ * array and including <code>length</code> values.
+ *
+ * <p>
+ *
+ * <ul>
+ * <li>Returns <code>Double.NaN</code> if <code>length = 0</code>
+ * <li>Returns (for any value of <code>p</code>) <code>values[begin]</code> if <code>
+ * length = 1 </code>
+ * <li>Throws <code>MathIllegalArgumentException</code> if <code>values</code> is null ,
+ * <code>begin</code> or <code>length</code> is invalid, or <code>p</code> is not a valid
+ * quantile value (p must be greater than 0 and less than or equal to 100)
+ * </ul>
+ *
+ * <p>See {@link org.apache.commons.math3.stat.descriptive.rank.Percentile} for a description of
+ * the percentile estimation algorithm used.
+ *
+ * @param values array of input values
+ * @param p the percentile to compute
+ * @param begin the first (0-based) element to include in the computation
+ * @param length the number of array elements to include
+ * @return the percentile value
+ * @throws MathIllegalArgumentException if the parameters are not valid or the input array is
+ * null
+ */
+ public static double percentile(
+ final double[] values, final int begin, final int length, final double p)
+ throws MathIllegalArgumentException {
+ return PERCENTILE.evaluate(values, begin, length, p);
+ }
+
+ /**
+ * Returns the sum of the (signed) differences between corresponding elements of the input
+ * arrays -- i.e., sum(sample1[i] - sample2[i]).
+ *
+ * @param sample1 the first array
+ * @param sample2 the second array
+ * @return sum of paired differences
+ * @throws DimensionMismatchException if the arrays do not have the same (positive) length.
+ * @throws NoDataException if the sample arrays are empty.
+ */
+ public static double sumDifference(final double[] sample1, final double[] sample2)
+ throws DimensionMismatchException, NoDataException {
+ int n = sample1.length;
+ if (n != sample2.length) {
+ throw new DimensionMismatchException(n, sample2.length);
+ }
+ if (n <= 0) {
+ throw new NoDataException(LocalizedFormats.INSUFFICIENT_DIMENSION);
+ }
+ double result = 0;
+ for (int i = 0; i < n; i++) {
+ result += sample1[i] - sample2[i];
+ }
+ return result;
+ }
+
+ /**
+ * Returns the mean of the (signed) differences between corresponding elements of the input
+ * arrays -- i.e., sum(sample1[i] - sample2[i]) / sample1.length.
+ *
+ * @param sample1 the first array
+ * @param sample2 the second array
+ * @return mean of paired differences
+ * @throws DimensionMismatchException if the arrays do not have the same (positive) length.
+ * @throws NoDataException if the sample arrays are empty.
+ */
+ public static double meanDifference(final double[] sample1, final double[] sample2)
+ throws DimensionMismatchException, NoDataException {
+ return sumDifference(sample1, sample2) / sample1.length;
+ }
+
+ /**
+ * Returns the variance of the (signed) differences between corresponding elements of the input
+ * arrays -- i.e., var(sample1[i] - sample2[i]).
+ *
+ * @param sample1 the first array
+ * @param sample2 the second array
+ * @param meanDifference the mean difference between corresponding entries
+ * @see #meanDifference(double[],double[])
+ * @return variance of paired differences
+ * @throws DimensionMismatchException if the arrays do not have the same length.
+ * @throws NumberIsTooSmallException if the arrays length is less than 2.
+ */
+ public static double varianceDifference(
+ final double[] sample1, final double[] sample2, double meanDifference)
+ throws DimensionMismatchException, NumberIsTooSmallException {
+ double sum1 = 0d;
+ double sum2 = 0d;
+ double diff = 0d;
+ int n = sample1.length;
+ if (n != sample2.length) {
+ throw new DimensionMismatchException(n, sample2.length);
+ }
+ if (n < 2) {
+ throw new NumberIsTooSmallException(n, 2, true);
+ }
+ for (int i = 0; i < n; i++) {
+ diff = sample1[i] - sample2[i];
+ sum1 += (diff - meanDifference) * (diff - meanDifference);
+ sum2 += diff - meanDifference;
+ }
+ return (sum1 - (sum2 * sum2 / n)) / (n - 1);
+ }
+
+ /**
+ * Normalize (standardize) the sample, so it is has a mean of 0 and a standard deviation of 1.
+ *
+ * @param sample Sample to normalize.
+ * @return normalized (standardized) sample.
+ * @since 2.2
+ */
+ public static double[] normalize(final double[] sample) {
+ DescriptiveStatistics stats = new DescriptiveStatistics();
+
+ // Add the data from the series to stats
+ for (int i = 0; i < sample.length; i++) {
+ stats.addValue(sample[i]);
+ }
+
+ // Compute mean and standard deviation
+ double mean = stats.getMean();
+ double standardDeviation = stats.getStandardDeviation();
+
+ // initialize the standardizedSample, which has the same length as the sample
+ double[] standardizedSample = new double[sample.length];
+
+ for (int i = 0; i < sample.length; i++) {
+ // z = (x- mean)/standardDeviation
+ standardizedSample[i] = (sample[i] - mean) / standardDeviation;
+ }
+ return standardizedSample;
+ }
+
+ /**
+ * Returns the sample mode(s). The mode is the most frequently occurring value in the sample. If
+ * there is a unique value with maximum frequency, this value is returned as the only element of
+ * the output array. Otherwise, the returned array contains the maximum frequency elements in
+ * increasing order. For example, if {@code sample} is {0, 12, 5, 6, 0, 13, 5, 17}, the returned
+ * array will have length two, with 0 in the first element and 5 in the second.
+ *
+ * <p>NaN values are ignored when computing the mode - i.e., NaNs will never appear in the
+ * output array. If the sample includes only NaNs or has length 0, an empty array is returned.
+ *
+ * @param sample input data
+ * @return array of array of the most frequently occurring element(s) sorted in ascending order.
+ * @throws MathIllegalArgumentException if the indices are invalid or the array is null
+ * @since 3.3
+ */
+ public static double[] mode(double[] sample) throws MathIllegalArgumentException {
+ if (sample == null) {
+ throw new NullArgumentException(LocalizedFormats.INPUT_ARRAY);
+ }
+ return getMode(sample, 0, sample.length);
+ }
+
+ /**
+ * Returns the sample mode(s). The mode is the most frequently occurring value in the sample. If
+ * there is a unique value with maximum frequency, this value is returned as the only element of
+ * the output array. Otherwise, the returned array contains the maximum frequency elements in
+ * increasing order. For example, if {@code sample} is {0, 12, 5, 6, 0, 13, 5, 17}, the returned
+ * array will have length two, with 0 in the first element and 5 in the second.
+ *
+ * <p>NaN values are ignored when computing the mode - i.e., NaNs will never appear in the
+ * output array. If the sample includes only NaNs or has length 0, an empty array is returned.
+ *
+ * @param sample input data
+ * @param begin index (0-based) of the first array element to include
+ * @param length the number of elements to include
+ * @return array of array of the most frequently occurring element(s) sorted in ascending order.
+ * @throws MathIllegalArgumentException if the indices are invalid or the array is null
+ * @since 3.3
+ */
+ public static double[] mode(double[] sample, final int begin, final int length) {
+ if (sample == null) {
+ throw new NullArgumentException(LocalizedFormats.INPUT_ARRAY);
+ }
+
+ if (begin < 0) {
+ throw new NotPositiveException(LocalizedFormats.START_POSITION, Integer.valueOf(begin));
+ }
+
+ if (length < 0) {
+ throw new NotPositiveException(LocalizedFormats.LENGTH, Integer.valueOf(length));
+ }
+
+ return getMode(sample, begin, length);
+ }
+
+ /**
+ * Private helper method. Assumes parameters have been validated.
+ *
+ * @param values input data
+ * @param begin index (0-based) of the first array element to include
+ * @param length the number of elements to include
+ * @return array of array of the most frequently occurring element(s) sorted in ascending order.
+ */
+ private static double[] getMode(double[] values, final int begin, final int length) {
+ // Add the values to the frequency table
+ Frequency freq = new Frequency();
+ for (int i = begin; i < begin + length; i++) {
+ final double value = values[i];
+ if (!Double.isNaN(value)) {
+ freq.addValue(Double.valueOf(value));
+ }
+ }
+ List<Comparable<?>> list = freq.getMode();
+ // Convert the list to an array of primitive double
+ double[] modes = new double[list.size()];
+ int i = 0;
+ for (Comparable<?> c : list) {
+ modes[i++] = ((Double) c).doubleValue();
+ }
+ return modes;
+ }
+}
diff --git a/src/main/java/org/apache/commons/math3/stat/clustering/Cluster.java b/src/main/java/org/apache/commons/math3/stat/clustering/Cluster.java
new file mode 100644
index 0000000..8d9483e
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/stat/clustering/Cluster.java
@@ -0,0 +1,76 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math3.stat.clustering;
+
+import java.io.Serializable;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Cluster holding a set of {@link Clusterable} points.
+ * @param <T> the type of points that can be clustered
+ * @since 2.0
+ * @deprecated As of 3.2 (to be removed in 4.0),
+ * use {@link org.apache.commons.math3.ml.clustering.Cluster} instead
+ */
+@Deprecated
+public class Cluster<T extends Clusterable<T>> implements Serializable {
+
+ /** Serializable version identifier. */
+ private static final long serialVersionUID = -3442297081515880464L;
+
+ /** The points contained in this cluster. */
+ private final List<T> points;
+
+ /** Center of the cluster. */
+ private final T center;
+
+ /**
+ * Build a cluster centered at a specified point.
+ * @param center the point which is to be the center of this cluster
+ */
+ public Cluster(final T center) {
+ this.center = center;
+ points = new ArrayList<T>();
+ }
+
+ /**
+ * Add a point to this cluster.
+ * @param point point to add
+ */
+ public void addPoint(final T point) {
+ points.add(point);
+ }
+
+ /**
+ * Get the points contained in the cluster.
+ * @return points contained in the cluster
+ */
+ public List<T> getPoints() {
+ return points;
+ }
+
+ /**
+ * Get the point chosen to be the center of this cluster.
+ * @return chosen cluster center
+ */
+ public T getCenter() {
+ return center;
+ }
+
+}
diff --git a/src/main/java/org/apache/commons/math3/stat/clustering/Clusterable.java b/src/main/java/org/apache/commons/math3/stat/clustering/Clusterable.java
new file mode 100644
index 0000000..f9818f3
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/stat/clustering/Clusterable.java
@@ -0,0 +1,48 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math3.stat.clustering;
+
+import java.util.Collection;
+
+/**
+ * Interface for points that can be clustered together.
+ * @param <T> the type of point that can be clustered
+ * @since 2.0
+ * @deprecated As of 3.2 (to be removed in 4.0),
+ * use {@link org.apache.commons.math3.ml.clustering.Clusterable} instead
+ */
+@Deprecated
+public interface Clusterable<T> {
+
+ /**
+ * Returns the distance from the given point.
+ *
+ * @param p the point to compute the distance from
+ * @return the distance from the given point
+ */
+ double distanceFrom(T p);
+
+ /**
+ * Returns the centroid of the given Collection of points.
+ *
+ * @param p the Collection of points to compute the centroid of
+ * @return the centroid of the given Collection of Points
+ */
+ T centroidOf(Collection<T> p);
+
+}
diff --git a/src/main/java/org/apache/commons/math3/stat/clustering/DBSCANClusterer.java b/src/main/java/org/apache/commons/math3/stat/clustering/DBSCANClusterer.java
new file mode 100644
index 0000000..13247eb
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/stat/clustering/DBSCANClusterer.java
@@ -0,0 +1,226 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math3.stat.clustering;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import org.apache.commons.math3.exception.NotPositiveException;
+import org.apache.commons.math3.exception.NullArgumentException;
+import org.apache.commons.math3.util.MathUtils;
+
+/**
+ * DBSCAN (density-based spatial clustering of applications with noise) algorithm.
+ * <p>
+ * The DBSCAN algorithm forms clusters based on the idea of density connectivity, i.e.
+ * a point p is density connected to another point q, if there exists a chain of
+ * points p<sub>i</sub>, with i = 1 .. n and p<sub>1</sub> = p and p<sub>n</sub> = q,
+ * such that each pair &lt;p<sub>i</sub>, p<sub>i+1</sub>&gt; is directly density-reachable.
+ * A point q is directly density-reachable from point p if it is in the &epsilon;-neighborhood
+ * of this point.
+ * <p>
+ * Any point that is not density-reachable from a formed cluster is treated as noise, and
+ * will thus not be present in the result.
+ * <p>
+ * The algorithm requires two parameters:
+ * <ul>
+ * <li>eps: the distance that defines the &epsilon;-neighborhood of a point
+ * <li>minPoints: the minimum number of density-connected points required to form a cluster
+ * </ul>
+ * <p>
+ * <b>Note:</b> as DBSCAN is not a centroid-based clustering algorithm, the resulting
+ * {@link Cluster} objects will have no defined center, i.e. {@link Cluster#getCenter()} will
+ * return {@code null}.
+ *
+ * @param <T> type of the points to cluster
+ * @see <a href="http://en.wikipedia.org/wiki/DBSCAN">DBSCAN (wikipedia)</a>
+ * @see <a href="http://www.dbs.ifi.lmu.de/Publikationen/Papers/KDD-96.final.frame.pdf">
+ * A Density-Based Algorithm for Discovering Clusters in Large Spatial Databases with Noise</a>
+ * @since 3.1
+ * @deprecated As of 3.2 (to be removed in 4.0),
+ * use {@link org.apache.commons.math3.ml.clustering.DBSCANClusterer} instead
+ */
+@Deprecated
+public class DBSCANClusterer<T extends Clusterable<T>> {
+
+ /** Maximum radius of the neighborhood to be considered. */
+ private final double eps;
+
+ /** Minimum number of points needed for a cluster. */
+ private final int minPts;
+
+ /** Status of a point during the clustering process. */
+ private enum PointStatus {
+ /** The point has is considered to be noise. */
+ NOISE,
+ /** The point is already part of a cluster. */
+ PART_OF_CLUSTER
+ }
+
+ /**
+ * Creates a new instance of a DBSCANClusterer.
+ *
+ * @param eps maximum radius of the neighborhood to be considered
+ * @param minPts minimum number of points needed for a cluster
+ * @throws NotPositiveException if {@code eps < 0.0} or {@code minPts < 0}
+ */
+ public DBSCANClusterer(final double eps, final int minPts)
+ throws NotPositiveException {
+ if (eps < 0.0d) {
+ throw new NotPositiveException(eps);
+ }
+ if (minPts < 0) {
+ throw new NotPositiveException(minPts);
+ }
+ this.eps = eps;
+ this.minPts = minPts;
+ }
+
+ /**
+ * Returns the maximum radius of the neighborhood to be considered.
+ *
+ * @return maximum radius of the neighborhood
+ */
+ public double getEps() {
+ return eps;
+ }
+
+ /**
+ * Returns the minimum number of points needed for a cluster.
+ *
+ * @return minimum number of points needed for a cluster
+ */
+ public int getMinPts() {
+ return minPts;
+ }
+
+ /**
+ * Performs DBSCAN cluster analysis.
+ * <p>
+ * <b>Note:</b> as DBSCAN is not a centroid-based clustering algorithm, the resulting
+ * {@link Cluster} objects will have no defined center, i.e. {@link Cluster#getCenter()} will
+ * return {@code null}.
+ *
+ * @param points the points to cluster
+ * @return the list of clusters
+ * @throws NullArgumentException if the data points are null
+ */
+ public List<Cluster<T>> cluster(final Collection<T> points) throws NullArgumentException {
+
+ // sanity checks
+ MathUtils.checkNotNull(points);
+
+ final List<Cluster<T>> clusters = new ArrayList<Cluster<T>>();
+ final Map<Clusterable<T>, PointStatus> visited = new HashMap<Clusterable<T>, PointStatus>();
+
+ for (final T point : points) {
+ if (visited.get(point) != null) {
+ continue;
+ }
+ final List<T> neighbors = getNeighbors(point, points);
+ if (neighbors.size() >= minPts) {
+ // DBSCAN does not care about center points
+ final Cluster<T> cluster = new Cluster<T>(null);
+ clusters.add(expandCluster(cluster, point, neighbors, points, visited));
+ } else {
+ visited.put(point, PointStatus.NOISE);
+ }
+ }
+
+ return clusters;
+ }
+
+ /**
+ * Expands the cluster to include density-reachable items.
+ *
+ * @param cluster Cluster to expand
+ * @param point Point to add to cluster
+ * @param neighbors List of neighbors
+ * @param points the data set
+ * @param visited the set of already visited points
+ * @return the expanded cluster
+ */
+ private Cluster<T> expandCluster(final Cluster<T> cluster,
+ final T point,
+ final List<T> neighbors,
+ final Collection<T> points,
+ final Map<Clusterable<T>, PointStatus> visited) {
+ cluster.addPoint(point);
+ visited.put(point, PointStatus.PART_OF_CLUSTER);
+
+ List<T> seeds = new ArrayList<T>(neighbors);
+ int index = 0;
+ while (index < seeds.size()) {
+ final T current = seeds.get(index);
+ PointStatus pStatus = visited.get(current);
+ // only check non-visited points
+ if (pStatus == null) {
+ final List<T> currentNeighbors = getNeighbors(current, points);
+ if (currentNeighbors.size() >= minPts) {
+ seeds = merge(seeds, currentNeighbors);
+ }
+ }
+
+ if (pStatus != PointStatus.PART_OF_CLUSTER) {
+ visited.put(current, PointStatus.PART_OF_CLUSTER);
+ cluster.addPoint(current);
+ }
+
+ index++;
+ }
+ return cluster;
+ }
+
+ /**
+ * Returns a list of density-reachable neighbors of a {@code point}.
+ *
+ * @param point the point to look for
+ * @param points possible neighbors
+ * @return the List of neighbors
+ */
+ private List<T> getNeighbors(final T point, final Collection<T> points) {
+ final List<T> neighbors = new ArrayList<T>();
+ for (final T neighbor : points) {
+ if (point != neighbor && neighbor.distanceFrom(point) <= eps) {
+ neighbors.add(neighbor);
+ }
+ }
+ return neighbors;
+ }
+
+ /**
+ * Merges two lists together.
+ *
+ * @param one first list
+ * @param two second list
+ * @return merged lists
+ */
+ private List<T> merge(final List<T> one, final List<T> two) {
+ final Set<T> oneSet = new HashSet<T>(one);
+ for (T item : two) {
+ if (!oneSet.contains(item)) {
+ one.add(item);
+ }
+ }
+ return one;
+ }
+}
diff --git a/src/main/java/org/apache/commons/math3/stat/clustering/EuclideanDoublePoint.java b/src/main/java/org/apache/commons/math3/stat/clustering/EuclideanDoublePoint.java
new file mode 100644
index 0000000..32c236c
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/stat/clustering/EuclideanDoublePoint.java
@@ -0,0 +1,100 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math3.stat.clustering;
+
+import java.io.Serializable;
+import java.util.Collection;
+import java.util.Arrays;
+
+import org.apache.commons.math3.util.MathArrays;
+
+/**
+ * A simple implementation of {@link Clusterable} for points with double coordinates.
+ * @since 3.1
+ * @deprecated As of 3.2 (to be removed in 4.0),
+ * use {@link org.apache.commons.math3.ml.clustering.DoublePoint} instead
+ */
+@Deprecated
+public class EuclideanDoublePoint implements Clusterable<EuclideanDoublePoint>, Serializable {
+
+ /** Serializable version identifier. */
+ private static final long serialVersionUID = 8026472786091227632L;
+
+ /** Point coordinates. */
+ private final double[] point;
+
+ /**
+ * Build an instance wrapping an integer array.
+ * <p>
+ * The wrapped array is referenced, it is <em>not</em> copied.
+ *
+ * @param point the n-dimensional point in integer space
+ */
+ public EuclideanDoublePoint(final double[] point) {
+ this.point = point;
+ }
+
+ /** {@inheritDoc} */
+ public EuclideanDoublePoint centroidOf(final Collection<EuclideanDoublePoint> points) {
+ final double[] centroid = new double[getPoint().length];
+ for (final EuclideanDoublePoint p : points) {
+ for (int i = 0; i < centroid.length; i++) {
+ centroid[i] += p.getPoint()[i];
+ }
+ }
+ for (int i = 0; i < centroid.length; i++) {
+ centroid[i] /= points.size();
+ }
+ return new EuclideanDoublePoint(centroid);
+ }
+
+ /** {@inheritDoc} */
+ public double distanceFrom(final EuclideanDoublePoint p) {
+ return MathArrays.distance(point, p.getPoint());
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public boolean equals(final Object other) {
+ if (!(other instanceof EuclideanDoublePoint)) {
+ return false;
+ }
+ return Arrays.equals(point, ((EuclideanDoublePoint) other).point);
+ }
+
+ /**
+ * Get the n-dimensional point in integer space.
+ *
+ * @return a reference (not a copy!) to the wrapped array
+ */
+ public double[] getPoint() {
+ return point;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public int hashCode() {
+ return Arrays.hashCode(point);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public String toString() {
+ return Arrays.toString(point);
+ }
+
+}
diff --git a/src/main/java/org/apache/commons/math3/stat/clustering/EuclideanIntegerPoint.java b/src/main/java/org/apache/commons/math3/stat/clustering/EuclideanIntegerPoint.java
new file mode 100644
index 0000000..508b0fa
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/stat/clustering/EuclideanIntegerPoint.java
@@ -0,0 +1,101 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math3.stat.clustering;
+
+import java.io.Serializable;
+import java.util.Arrays;
+import java.util.Collection;
+
+import org.apache.commons.math3.util.MathArrays;
+
+/**
+ * A simple implementation of {@link Clusterable} for points with integer coordinates.
+ * @since 2.0
+ * @deprecated As of 3.2 (to be removed in 4.0),
+ * use {@link org.apache.commons.math3.ml.clustering.DoublePoint} instead
+ */
+@Deprecated
+public class EuclideanIntegerPoint implements Clusterable<EuclideanIntegerPoint>, Serializable {
+
+ /** Serializable version identifier. */
+ private static final long serialVersionUID = 3946024775784901369L;
+
+ /** Point coordinates. */
+ private final int[] point;
+
+ /**
+ * Build an instance wrapping an integer array.
+ * <p>The wrapped array is referenced, it is <em>not</em> copied.</p>
+ * @param point the n-dimensional point in integer space
+ */
+ public EuclideanIntegerPoint(final int[] point) {
+ this.point = point;
+ }
+
+ /**
+ * Get the n-dimensional point in integer space.
+ * @return a reference (not a copy!) to the wrapped array
+ */
+ public int[] getPoint() {
+ return point;
+ }
+
+ /** {@inheritDoc} */
+ public double distanceFrom(final EuclideanIntegerPoint p) {
+ return MathArrays.distance(point, p.getPoint());
+ }
+
+ /** {@inheritDoc} */
+ public EuclideanIntegerPoint centroidOf(final Collection<EuclideanIntegerPoint> points) {
+ int[] centroid = new int[getPoint().length];
+ for (EuclideanIntegerPoint p : points) {
+ for (int i = 0; i < centroid.length; i++) {
+ centroid[i] += p.getPoint()[i];
+ }
+ }
+ for (int i = 0; i < centroid.length; i++) {
+ centroid[i] /= points.size();
+ }
+ return new EuclideanIntegerPoint(centroid);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public boolean equals(final Object other) {
+ if (!(other instanceof EuclideanIntegerPoint)) {
+ return false;
+ }
+ return Arrays.equals(point, ((EuclideanIntegerPoint) other).point);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public int hashCode() {
+ return Arrays.hashCode(point);
+ }
+
+ /**
+ * {@inheritDoc}
+ * @since 2.1
+ */
+ @Override
+ public String toString() {
+ return Arrays.toString(point);
+ }
+
+}
diff --git a/src/main/java/org/apache/commons/math3/stat/clustering/KMeansPlusPlusClusterer.java b/src/main/java/org/apache/commons/math3/stat/clustering/KMeansPlusPlusClusterer.java
new file mode 100644
index 0000000..07cec09
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/stat/clustering/KMeansPlusPlusClusterer.java
@@ -0,0 +1,514 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math3.stat.clustering;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+import java.util.Random;
+
+import org.apache.commons.math3.exception.ConvergenceException;
+import org.apache.commons.math3.exception.MathIllegalArgumentException;
+import org.apache.commons.math3.exception.NumberIsTooSmallException;
+import org.apache.commons.math3.exception.util.LocalizedFormats;
+import org.apache.commons.math3.stat.descriptive.moment.Variance;
+import org.apache.commons.math3.util.MathUtils;
+
+/**
+ * Clustering algorithm based on David Arthur and Sergei Vassilvitski k-means++ algorithm.
+ * @param <T> type of the points to cluster
+ * @see <a href="http://en.wikipedia.org/wiki/K-means%2B%2B">K-means++ (wikipedia)</a>
+ * @since 2.0
+ * @deprecated As of 3.2 (to be removed in 4.0),
+ * use {@link org.apache.commons.math3.ml.clustering.KMeansPlusPlusClusterer} instead
+ */
+@Deprecated
+public class KMeansPlusPlusClusterer<T extends Clusterable<T>> {
+
+ /** Strategies to use for replacing an empty cluster. */
+ public enum EmptyClusterStrategy {
+
+ /** Split the cluster with largest distance variance. */
+ LARGEST_VARIANCE,
+
+ /** Split the cluster with largest number of points. */
+ LARGEST_POINTS_NUMBER,
+
+ /** Create a cluster around the point farthest from its centroid. */
+ FARTHEST_POINT,
+
+ /** Generate an error. */
+ ERROR
+
+ }
+
+ /** Random generator for choosing initial centers. */
+ private final Random random;
+
+ /** Selected strategy for empty clusters. */
+ private final EmptyClusterStrategy emptyStrategy;
+
+ /** Build a clusterer.
+ * <p>
+ * The default strategy for handling empty clusters that may appear during
+ * algorithm iterations is to split the cluster with largest distance variance.
+ * </p>
+ * @param random random generator to use for choosing initial centers
+ */
+ public KMeansPlusPlusClusterer(final Random random) {
+ this(random, EmptyClusterStrategy.LARGEST_VARIANCE);
+ }
+
+ /** Build a clusterer.
+ * @param random random generator to use for choosing initial centers
+ * @param emptyStrategy strategy to use for handling empty clusters that
+ * may appear during algorithm iterations
+ * @since 2.2
+ */
+ public KMeansPlusPlusClusterer(final Random random, final EmptyClusterStrategy emptyStrategy) {
+ this.random = random;
+ this.emptyStrategy = emptyStrategy;
+ }
+
+ /**
+ * Runs the K-means++ clustering algorithm.
+ *
+ * @param points the points to cluster
+ * @param k the number of clusters to split the data into
+ * @param numTrials number of trial runs
+ * @param maxIterationsPerTrial the maximum number of iterations to run the algorithm
+ * for at each trial run. If negative, no maximum will be used
+ * @return a list of clusters containing the points
+ * @throws MathIllegalArgumentException if the data points are null or the number
+ * of clusters is larger than the number of data points
+ * @throws ConvergenceException if an empty cluster is encountered and the
+ * {@link #emptyStrategy} is set to {@code ERROR}
+ */
+ public List<Cluster<T>> cluster(final Collection<T> points, final int k,
+ int numTrials, int maxIterationsPerTrial)
+ throws MathIllegalArgumentException, ConvergenceException {
+
+ // at first, we have not found any clusters list yet
+ List<Cluster<T>> best = null;
+ double bestVarianceSum = Double.POSITIVE_INFINITY;
+
+ // do several clustering trials
+ for (int i = 0; i < numTrials; ++i) {
+
+ // compute a clusters list
+ List<Cluster<T>> clusters = cluster(points, k, maxIterationsPerTrial);
+
+ // compute the variance of the current list
+ double varianceSum = 0.0;
+ for (final Cluster<T> cluster : clusters) {
+ if (!cluster.getPoints().isEmpty()) {
+
+ // compute the distance variance of the current cluster
+ final T center = cluster.getCenter();
+ final Variance stat = new Variance();
+ for (final T point : cluster.getPoints()) {
+ stat.increment(point.distanceFrom(center));
+ }
+ varianceSum += stat.getResult();
+
+ }
+ }
+
+ if (varianceSum <= bestVarianceSum) {
+ // this one is the best we have found so far, remember it
+ best = clusters;
+ bestVarianceSum = varianceSum;
+ }
+
+ }
+
+ // return the best clusters list found
+ return best;
+
+ }
+
+ /**
+ * Runs the K-means++ clustering algorithm.
+ *
+ * @param points the points to cluster
+ * @param k the number of clusters to split the data into
+ * @param maxIterations the maximum number of iterations to run the algorithm
+ * for. If negative, no maximum will be used
+ * @return a list of clusters containing the points
+ * @throws MathIllegalArgumentException if the data points are null or the number
+ * of clusters is larger than the number of data points
+ * @throws ConvergenceException if an empty cluster is encountered and the
+ * {@link #emptyStrategy} is set to {@code ERROR}
+ */
+ public List<Cluster<T>> cluster(final Collection<T> points, final int k,
+ final int maxIterations)
+ throws MathIllegalArgumentException, ConvergenceException {
+
+ // sanity checks
+ MathUtils.checkNotNull(points);
+
+ // number of clusters has to be smaller or equal the number of data points
+ if (points.size() < k) {
+ throw new NumberIsTooSmallException(points.size(), k, false);
+ }
+
+ // create the initial clusters
+ List<Cluster<T>> clusters = chooseInitialCenters(points, k, random);
+
+ // create an array containing the latest assignment of a point to a cluster
+ // no need to initialize the array, as it will be filled with the first assignment
+ int[] assignments = new int[points.size()];
+ assignPointsToClusters(clusters, points, assignments);
+
+ // iterate through updating the centers until we're done
+ final int max = (maxIterations < 0) ? Integer.MAX_VALUE : maxIterations;
+ for (int count = 0; count < max; count++) {
+ boolean emptyCluster = false;
+ List<Cluster<T>> newClusters = new ArrayList<Cluster<T>>();
+ for (final Cluster<T> cluster : clusters) {
+ final T newCenter;
+ if (cluster.getPoints().isEmpty()) {
+ switch (emptyStrategy) {
+ case LARGEST_VARIANCE :
+ newCenter = getPointFromLargestVarianceCluster(clusters);
+ break;
+ case LARGEST_POINTS_NUMBER :
+ newCenter = getPointFromLargestNumberCluster(clusters);
+ break;
+ case FARTHEST_POINT :
+ newCenter = getFarthestPoint(clusters);
+ break;
+ default :
+ throw new ConvergenceException(LocalizedFormats.EMPTY_CLUSTER_IN_K_MEANS);
+ }
+ emptyCluster = true;
+ } else {
+ newCenter = cluster.getCenter().centroidOf(cluster.getPoints());
+ }
+ newClusters.add(new Cluster<T>(newCenter));
+ }
+ int changes = assignPointsToClusters(newClusters, points, assignments);
+ clusters = newClusters;
+
+ // if there were no more changes in the point-to-cluster assignment
+ // and there are no empty clusters left, return the current clusters
+ if (changes == 0 && !emptyCluster) {
+ return clusters;
+ }
+ }
+ return clusters;
+ }
+
+ /**
+ * Adds the given points to the closest {@link Cluster}.
+ *
+ * @param <T> type of the points to cluster
+ * @param clusters the {@link Cluster}s to add the points to
+ * @param points the points to add to the given {@link Cluster}s
+ * @param assignments points assignments to clusters
+ * @return the number of points assigned to different clusters as the iteration before
+ */
+ private static <T extends Clusterable<T>> int
+ assignPointsToClusters(final List<Cluster<T>> clusters, final Collection<T> points,
+ final int[] assignments) {
+ int assignedDifferently = 0;
+ int pointIndex = 0;
+ for (final T p : points) {
+ int clusterIndex = getNearestCluster(clusters, p);
+ if (clusterIndex != assignments[pointIndex]) {
+ assignedDifferently++;
+ }
+
+ Cluster<T> cluster = clusters.get(clusterIndex);
+ cluster.addPoint(p);
+ assignments[pointIndex++] = clusterIndex;
+ }
+
+ return assignedDifferently;
+ }
+
+ /**
+ * Use K-means++ to choose the initial centers.
+ *
+ * @param <T> type of the points to cluster
+ * @param points the points to choose the initial centers from
+ * @param k the number of centers to choose
+ * @param random random generator to use
+ * @return the initial centers
+ */
+ private static <T extends Clusterable<T>> List<Cluster<T>>
+ chooseInitialCenters(final Collection<T> points, final int k, final Random random) {
+
+ // Convert to list for indexed access. Make it unmodifiable, since removal of items
+ // would screw up the logic of this method.
+ final List<T> pointList = Collections.unmodifiableList(new ArrayList<T> (points));
+
+ // The number of points in the list.
+ final int numPoints = pointList.size();
+
+ // Set the corresponding element in this array to indicate when
+ // elements of pointList are no longer available.
+ final boolean[] taken = new boolean[numPoints];
+
+ // The resulting list of initial centers.
+ final List<Cluster<T>> resultSet = new ArrayList<Cluster<T>>();
+
+ // Choose one center uniformly at random from among the data points.
+ final int firstPointIndex = random.nextInt(numPoints);
+
+ final T firstPoint = pointList.get(firstPointIndex);
+
+ resultSet.add(new Cluster<T>(firstPoint));
+
+ // Must mark it as taken
+ taken[firstPointIndex] = true;
+
+ // To keep track of the minimum distance squared of elements of
+ // pointList to elements of resultSet.
+ final double[] minDistSquared = new double[numPoints];
+
+ // Initialize the elements. Since the only point in resultSet is firstPoint,
+ // this is very easy.
+ for (int i = 0; i < numPoints; i++) {
+ if (i != firstPointIndex) { // That point isn't considered
+ double d = firstPoint.distanceFrom(pointList.get(i));
+ minDistSquared[i] = d*d;
+ }
+ }
+
+ while (resultSet.size() < k) {
+
+ // Sum up the squared distances for the points in pointList not
+ // already taken.
+ double distSqSum = 0.0;
+
+ for (int i = 0; i < numPoints; i++) {
+ if (!taken[i]) {
+ distSqSum += minDistSquared[i];
+ }
+ }
+
+ // Add one new data point as a center. Each point x is chosen with
+ // probability proportional to D(x)2
+ final double r = random.nextDouble() * distSqSum;
+
+ // The index of the next point to be added to the resultSet.
+ int nextPointIndex = -1;
+
+ // Sum through the squared min distances again, stopping when
+ // sum >= r.
+ double sum = 0.0;
+ for (int i = 0; i < numPoints; i++) {
+ if (!taken[i]) {
+ sum += minDistSquared[i];
+ if (sum >= r) {
+ nextPointIndex = i;
+ break;
+ }
+ }
+ }
+
+ // If it's not set to >= 0, the point wasn't found in the previous
+ // for loop, probably because distances are extremely small. Just pick
+ // the last available point.
+ if (nextPointIndex == -1) {
+ for (int i = numPoints - 1; i >= 0; i--) {
+ if (!taken[i]) {
+ nextPointIndex = i;
+ break;
+ }
+ }
+ }
+
+ // We found one.
+ if (nextPointIndex >= 0) {
+
+ final T p = pointList.get(nextPointIndex);
+
+ resultSet.add(new Cluster<T> (p));
+
+ // Mark it as taken.
+ taken[nextPointIndex] = true;
+
+ if (resultSet.size() < k) {
+ // Now update elements of minDistSquared. We only have to compute
+ // the distance to the new center to do this.
+ for (int j = 0; j < numPoints; j++) {
+ // Only have to worry about the points still not taken.
+ if (!taken[j]) {
+ double d = p.distanceFrom(pointList.get(j));
+ double d2 = d * d;
+ if (d2 < minDistSquared[j]) {
+ minDistSquared[j] = d2;
+ }
+ }
+ }
+ }
+
+ } else {
+ // None found --
+ // Break from the while loop to prevent
+ // an infinite loop.
+ break;
+ }
+ }
+
+ return resultSet;
+ }
+
+ /**
+ * Get a random point from the {@link Cluster} with the largest distance variance.
+ *
+ * @param clusters the {@link Cluster}s to search
+ * @return a random point from the selected cluster
+ * @throws ConvergenceException if clusters are all empty
+ */
+ private T getPointFromLargestVarianceCluster(final Collection<Cluster<T>> clusters)
+ throws ConvergenceException {
+
+ double maxVariance = Double.NEGATIVE_INFINITY;
+ Cluster<T> selected = null;
+ for (final Cluster<T> cluster : clusters) {
+ if (!cluster.getPoints().isEmpty()) {
+
+ // compute the distance variance of the current cluster
+ final T center = cluster.getCenter();
+ final Variance stat = new Variance();
+ for (final T point : cluster.getPoints()) {
+ stat.increment(point.distanceFrom(center));
+ }
+ final double variance = stat.getResult();
+
+ // select the cluster with the largest variance
+ if (variance > maxVariance) {
+ maxVariance = variance;
+ selected = cluster;
+ }
+
+ }
+ }
+
+ // did we find at least one non-empty cluster ?
+ if (selected == null) {
+ throw new ConvergenceException(LocalizedFormats.EMPTY_CLUSTER_IN_K_MEANS);
+ }
+
+ // extract a random point from the cluster
+ final List<T> selectedPoints = selected.getPoints();
+ return selectedPoints.remove(random.nextInt(selectedPoints.size()));
+
+ }
+
+ /**
+ * Get a random point from the {@link Cluster} with the largest number of points
+ *
+ * @param clusters the {@link Cluster}s to search
+ * @return a random point from the selected cluster
+ * @throws ConvergenceException if clusters are all empty
+ */
+ private T getPointFromLargestNumberCluster(final Collection<Cluster<T>> clusters) throws ConvergenceException {
+
+ int maxNumber = 0;
+ Cluster<T> selected = null;
+ for (final Cluster<T> cluster : clusters) {
+
+ // get the number of points of the current cluster
+ final int number = cluster.getPoints().size();
+
+ // select the cluster with the largest number of points
+ if (number > maxNumber) {
+ maxNumber = number;
+ selected = cluster;
+ }
+
+ }
+
+ // did we find at least one non-empty cluster ?
+ if (selected == null) {
+ throw new ConvergenceException(LocalizedFormats.EMPTY_CLUSTER_IN_K_MEANS);
+ }
+
+ // extract a random point from the cluster
+ final List<T> selectedPoints = selected.getPoints();
+ return selectedPoints.remove(random.nextInt(selectedPoints.size()));
+
+ }
+
+ /**
+ * Get the point farthest to its cluster center
+ *
+ * @param clusters the {@link Cluster}s to search
+ * @return point farthest to its cluster center
+ * @throws ConvergenceException if clusters are all empty
+ */
+ private T getFarthestPoint(final Collection<Cluster<T>> clusters) throws ConvergenceException {
+
+ double maxDistance = Double.NEGATIVE_INFINITY;
+ Cluster<T> selectedCluster = null;
+ int selectedPoint = -1;
+ for (final Cluster<T> cluster : clusters) {
+
+ // get the farthest point
+ final T center = cluster.getCenter();
+ final List<T> points = cluster.getPoints();
+ for (int i = 0; i < points.size(); ++i) {
+ final double distance = points.get(i).distanceFrom(center);
+ if (distance > maxDistance) {
+ maxDistance = distance;
+ selectedCluster = cluster;
+ selectedPoint = i;
+ }
+ }
+
+ }
+
+ // did we find at least one non-empty cluster ?
+ if (selectedCluster == null) {
+ throw new ConvergenceException(LocalizedFormats.EMPTY_CLUSTER_IN_K_MEANS);
+ }
+
+ return selectedCluster.getPoints().remove(selectedPoint);
+
+ }
+
+ /**
+ * Returns the nearest {@link Cluster} to the given point
+ *
+ * @param <T> type of the points to cluster
+ * @param clusters the {@link Cluster}s to search
+ * @param point the point to find the nearest {@link Cluster} for
+ * @return the index of the nearest {@link Cluster} to the given point
+ */
+ private static <T extends Clusterable<T>> int
+ getNearestCluster(final Collection<Cluster<T>> clusters, final T point) {
+ double minDistance = Double.MAX_VALUE;
+ int clusterIndex = 0;
+ int minCluster = 0;
+ for (final Cluster<T> c : clusters) {
+ final double distance = point.distanceFrom(c.getCenter());
+ if (distance < minDistance) {
+ minDistance = distance;
+ minCluster = clusterIndex;
+ }
+ clusterIndex++;
+ }
+ return minCluster;
+ }
+
+}
diff --git a/src/main/java/org/apache/commons/math3/stat/clustering/package-info.java b/src/main/java/org/apache/commons/math3/stat/clustering/package-info.java
new file mode 100644
index 0000000..f6b8d3e
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/stat/clustering/package-info.java
@@ -0,0 +1,29 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/**
+ * <h2>All classes and sub-packages of this package are deprecated.</h2>
+ * <h3>Please use their replacements, to be found under
+ * <ul>
+ * <li>{@link org.apache.commons.math3.ml.clustering}</li>
+ * </ul>
+ * </h3>
+ *
+ * <p>
+ * Clustering algorithms.
+ * </p>
+ */
+package org.apache.commons.math3.stat.clustering;
diff --git a/src/main/java/org/apache/commons/math3/stat/correlation/Covariance.java b/src/main/java/org/apache/commons/math3/stat/correlation/Covariance.java
new file mode 100644
index 0000000..c462401
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/stat/correlation/Covariance.java
@@ -0,0 +1,295 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math3.stat.correlation;
+
+import org.apache.commons.math3.exception.MathIllegalArgumentException;
+import org.apache.commons.math3.exception.NotStrictlyPositiveException;
+import org.apache.commons.math3.exception.util.LocalizedFormats;
+import org.apache.commons.math3.linear.RealMatrix;
+import org.apache.commons.math3.linear.BlockRealMatrix;
+import org.apache.commons.math3.stat.descriptive.moment.Mean;
+import org.apache.commons.math3.stat.descriptive.moment.Variance;
+
+/**
+ * Computes covariances for pairs of arrays or columns of a matrix.
+ *
+ * <p>The constructors that take <code>RealMatrix</code> or
+ * <code>double[][]</code> arguments generate covariance matrices. The
+ * columns of the input matrices are assumed to represent variable values.</p>
+ *
+ * <p>The constructor argument <code>biasCorrected</code> determines whether or
+ * not computed covariances are bias-corrected.</p>
+ *
+ * <p>Unbiased covariances are given by the formula</p>
+ * <code>cov(X, Y) = &Sigma;[(x<sub>i</sub> - E(X))(y<sub>i</sub> - E(Y))] / (n - 1)</code>
+ * where <code>E(X)</code> is the mean of <code>X</code> and <code>E(Y)</code>
+ * is the mean of the <code>Y</code> values.
+ *
+ * <p>Non-bias-corrected estimates use <code>n</code> in place of <code>n - 1</code>
+ *
+ * @since 2.0
+ */
+public class Covariance {
+
+ /** covariance matrix */
+ private final RealMatrix covarianceMatrix;
+
+ /**
+ * Create an empty covariance matrix.
+ */
+ /** Number of observations (length of covariate vectors) */
+ private final int n;
+
+ /**
+ * Create a Covariance with no data
+ */
+ public Covariance() {
+ super();
+ covarianceMatrix = null;
+ n = 0;
+ }
+
+ /**
+ * Create a Covariance matrix from a rectangular array
+ * whose columns represent covariates.
+ *
+ * <p>The <code>biasCorrected</code> parameter determines whether or not
+ * covariance estimates are bias-corrected.</p>
+ *
+ * <p>The input array must be rectangular with at least one column
+ * and two rows.</p>
+ *
+ * @param data rectangular array with columns representing covariates
+ * @param biasCorrected true means covariances are bias-corrected
+ * @throws MathIllegalArgumentException if the input data array is not
+ * rectangular with at least two rows and one column.
+ * @throws NotStrictlyPositiveException if the input data array is not
+ * rectangular with at least one row and one column.
+ */
+ public Covariance(double[][] data, boolean biasCorrected)
+ throws MathIllegalArgumentException, NotStrictlyPositiveException {
+ this(new BlockRealMatrix(data), biasCorrected);
+ }
+
+ /**
+ * Create a Covariance matrix from a rectangular array
+ * whose columns represent covariates.
+ *
+ * <p>The input array must be rectangular with at least one column
+ * and two rows</p>
+ *
+ * @param data rectangular array with columns representing covariates
+ * @throws MathIllegalArgumentException if the input data array is not
+ * rectangular with at least two rows and one column.
+ * @throws NotStrictlyPositiveException if the input data array is not
+ * rectangular with at least one row and one column.
+ */
+ public Covariance(double[][] data)
+ throws MathIllegalArgumentException, NotStrictlyPositiveException {
+ this(data, true);
+ }
+
+ /**
+ * Create a covariance matrix from a matrix whose columns
+ * represent covariates.
+ *
+ * <p>The <code>biasCorrected</code> parameter determines whether or not
+ * covariance estimates are bias-corrected.</p>
+ *
+ * <p>The matrix must have at least one column and two rows</p>
+ *
+ * @param matrix matrix with columns representing covariates
+ * @param biasCorrected true means covariances are bias-corrected
+ * @throws MathIllegalArgumentException if the input matrix does not have
+ * at least two rows and one column
+ */
+ public Covariance(RealMatrix matrix, boolean biasCorrected)
+ throws MathIllegalArgumentException {
+ checkSufficientData(matrix);
+ n = matrix.getRowDimension();
+ covarianceMatrix = computeCovarianceMatrix(matrix, biasCorrected);
+ }
+
+ /**
+ * Create a covariance matrix from a matrix whose columns
+ * represent covariates.
+ *
+ * <p>The matrix must have at least one column and two rows</p>
+ *
+ * @param matrix matrix with columns representing covariates
+ * @throws MathIllegalArgumentException if the input matrix does not have
+ * at least two rows and one column
+ */
+ public Covariance(RealMatrix matrix) throws MathIllegalArgumentException {
+ this(matrix, true);
+ }
+
+ /**
+ * Returns the covariance matrix
+ *
+ * @return covariance matrix
+ */
+ public RealMatrix getCovarianceMatrix() {
+ return covarianceMatrix;
+ }
+
+ /**
+ * Returns the number of observations (length of covariate vectors)
+ *
+ * @return number of observations
+ */
+ public int getN() {
+ return n;
+ }
+
+ /**
+ * Compute a covariance matrix from a matrix whose columns represent
+ * covariates.
+ * @param matrix input matrix (must have at least one column and two rows)
+ * @param biasCorrected determines whether or not covariance estimates are bias-corrected
+ * @return covariance matrix
+ * @throws MathIllegalArgumentException if the matrix does not contain sufficient data
+ */
+ protected RealMatrix computeCovarianceMatrix(RealMatrix matrix, boolean biasCorrected)
+ throws MathIllegalArgumentException {
+ int dimension = matrix.getColumnDimension();
+ Variance variance = new Variance(biasCorrected);
+ RealMatrix outMatrix = new BlockRealMatrix(dimension, dimension);
+ for (int i = 0; i < dimension; i++) {
+ for (int j = 0; j < i; j++) {
+ double cov = covariance(matrix.getColumn(i), matrix.getColumn(j), biasCorrected);
+ outMatrix.setEntry(i, j, cov);
+ outMatrix.setEntry(j, i, cov);
+ }
+ outMatrix.setEntry(i, i, variance.evaluate(matrix.getColumn(i)));
+ }
+ return outMatrix;
+ }
+
+ /**
+ * Create a covariance matrix from a matrix whose columns represent
+ * covariates. Covariances are computed using the bias-corrected formula.
+ * @param matrix input matrix (must have at least one column and two rows)
+ * @return covariance matrix
+ * @throws MathIllegalArgumentException if matrix does not contain sufficient data
+ * @see #Covariance
+ */
+ protected RealMatrix computeCovarianceMatrix(RealMatrix matrix)
+ throws MathIllegalArgumentException {
+ return computeCovarianceMatrix(matrix, true);
+ }
+
+ /**
+ * Compute a covariance matrix from a rectangular array whose columns represent
+ * covariates.
+ * @param data input array (must have at least one column and two rows)
+ * @param biasCorrected determines whether or not covariance estimates are bias-corrected
+ * @return covariance matrix
+ * @throws MathIllegalArgumentException if the data array does not contain sufficient
+ * data
+ * @throws NotStrictlyPositiveException if the input data array is not
+ * rectangular with at least one row and one column.
+ */
+ protected RealMatrix computeCovarianceMatrix(double[][] data, boolean biasCorrected)
+ throws MathIllegalArgumentException, NotStrictlyPositiveException {
+ return computeCovarianceMatrix(new BlockRealMatrix(data), biasCorrected);
+ }
+
+ /**
+ * Create a covariance matrix from a rectangular array whose columns represent
+ * covariates. Covariances are computed using the bias-corrected formula.
+ * @param data input array (must have at least one column and two rows)
+ * @return covariance matrix
+ * @throws MathIllegalArgumentException if the data array does not contain sufficient data
+ * @throws NotStrictlyPositiveException if the input data array is not
+ * rectangular with at least one row and one column.
+ * @see #Covariance
+ */
+ protected RealMatrix computeCovarianceMatrix(double[][] data)
+ throws MathIllegalArgumentException, NotStrictlyPositiveException {
+ return computeCovarianceMatrix(data, true);
+ }
+
+ /**
+ * Computes the covariance between the two arrays.
+ *
+ * <p>Array lengths must match and the common length must be at least 2.</p>
+ *
+ * @param xArray first data array
+ * @param yArray second data array
+ * @param biasCorrected if true, returned value will be bias-corrected
+ * @return returns the covariance for the two arrays
+ * @throws MathIllegalArgumentException if the arrays lengths do not match or
+ * there is insufficient data
+ */
+ public double covariance(final double[] xArray, final double[] yArray, boolean biasCorrected)
+ throws MathIllegalArgumentException {
+ Mean mean = new Mean();
+ double result = 0d;
+ int length = xArray.length;
+ if (length != yArray.length) {
+ throw new MathIllegalArgumentException(
+ LocalizedFormats.DIMENSIONS_MISMATCH_SIMPLE, length, yArray.length);
+ } else if (length < 2) {
+ throw new MathIllegalArgumentException(
+ LocalizedFormats.INSUFFICIENT_OBSERVED_POINTS_IN_SAMPLE, length, 2);
+ } else {
+ double xMean = mean.evaluate(xArray);
+ double yMean = mean.evaluate(yArray);
+ for (int i = 0; i < length; i++) {
+ double xDev = xArray[i] - xMean;
+ double yDev = yArray[i] - yMean;
+ result += (xDev * yDev - result) / (i + 1);
+ }
+ }
+ return biasCorrected ? result * ((double) length / (double)(length - 1)) : result;
+ }
+
+ /**
+ * Computes the covariance between the two arrays, using the bias-corrected
+ * formula.
+ *
+ * <p>Array lengths must match and the common length must be at least 2.</p>
+ *
+ * @param xArray first data array
+ * @param yArray second data array
+ * @return returns the covariance for the two arrays
+ * @throws MathIllegalArgumentException if the arrays lengths do not match or
+ * there is insufficient data
+ */
+ public double covariance(final double[] xArray, final double[] yArray)
+ throws MathIllegalArgumentException {
+ return covariance(xArray, yArray, true);
+ }
+
+ /**
+ * Throws MathIllegalArgumentException if the matrix does not have at least
+ * one column and two rows.
+ * @param matrix matrix to check
+ * @throws MathIllegalArgumentException if the matrix does not contain sufficient data
+ * to compute covariance
+ */
+ private void checkSufficientData(final RealMatrix matrix) throws MathIllegalArgumentException {
+ int nRows = matrix.getRowDimension();
+ int nCols = matrix.getColumnDimension();
+ if (nRows < 2 || nCols < 1) {
+ throw new MathIllegalArgumentException(
+ LocalizedFormats.INSUFFICIENT_ROWS_AND_COLUMNS,
+ nRows, nCols);
+ }
+ }
+}
diff --git a/src/main/java/org/apache/commons/math3/stat/correlation/KendallsCorrelation.java b/src/main/java/org/apache/commons/math3/stat/correlation/KendallsCorrelation.java
new file mode 100644
index 0000000..d38cf71
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/stat/correlation/KendallsCorrelation.java
@@ -0,0 +1,272 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math3.stat.correlation;
+
+import org.apache.commons.math3.exception.DimensionMismatchException;
+import org.apache.commons.math3.linear.BlockRealMatrix;
+import org.apache.commons.math3.linear.MatrixUtils;
+import org.apache.commons.math3.linear.RealMatrix;
+import org.apache.commons.math3.util.FastMath;
+import org.apache.commons.math3.util.Pair;
+
+import java.util.Arrays;
+import java.util.Comparator;
+
+/**
+ * Implementation of Kendall's Tau-b rank correlation</a>.
+ * <p>
+ * A pair of observations (x<sub>1</sub>, y<sub>1</sub>) and
+ * (x<sub>2</sub>, y<sub>2</sub>) are considered <i>concordant</i> if
+ * x<sub>1</sub> &lt; x<sub>2</sub> and y<sub>1</sub> &lt; y<sub>2</sub>
+ * or x<sub>2</sub> &lt; x<sub>1</sub> and y<sub>2</sub> &lt; y<sub>1</sub>.
+ * The pair is <i>discordant</i> if x<sub>1</sub> &lt; x<sub>2</sub> and
+ * y<sub>2</sub> &lt; y<sub>1</sub> or x<sub>2</sub> &lt; x<sub>1</sub> and
+ * y<sub>1</sub> &lt; y<sub>2</sub>. If either x<sub>1</sub> = x<sub>2</sub>
+ * or y<sub>1</sub> = y<sub>2</sub>, the pair is neither concordant nor
+ * discordant.
+ * <p>
+ * Kendall's Tau-b is defined as:
+ * <pre>
+ * tau<sub>b</sub> = (n<sub>c</sub> - n<sub>d</sub>) / sqrt((n<sub>0</sub> - n<sub>1</sub>) * (n<sub>0</sub> - n<sub>2</sub>))
+ * </pre>
+ * <p>
+ * where:
+ * <ul>
+ * <li>n<sub>0</sub> = n * (n - 1) / 2</li>
+ * <li>n<sub>c</sub> = Number of concordant pairs</li>
+ * <li>n<sub>d</sub> = Number of discordant pairs</li>
+ * <li>n<sub>1</sub> = sum of t<sub>i</sub> * (t<sub>i</sub> - 1) / 2 for all i</li>
+ * <li>n<sub>2</sub> = sum of u<sub>j</sub> * (u<sub>j</sub> - 1) / 2 for all j</li>
+ * <li>t<sub>i</sub> = Number of tied values in the i<sup>th</sup> group of ties in x</li>
+ * <li>u<sub>j</sub> = Number of tied values in the j<sup>th</sup> group of ties in y</li>
+ * </ul>
+ * <p>
+ * This implementation uses the O(n log n) algorithm described in
+ * William R. Knight's 1966 paper "A Computer Method for Calculating
+ * Kendall's Tau with Ungrouped Data" in the Journal of the American
+ * Statistical Association.
+ *
+ * @see <a href="http://en.wikipedia.org/wiki/Kendall_tau_rank_correlation_coefficient">
+ * Kendall tau rank correlation coefficient (Wikipedia)</a>
+ * @see <a href="http://www.jstor.org/stable/2282833">A Computer
+ * Method for Calculating Kendall's Tau with Ungrouped Data</a>
+ *
+ * @since 3.3
+ */
+public class KendallsCorrelation {
+
+ /** correlation matrix */
+ private final RealMatrix correlationMatrix;
+
+ /**
+ * Create a KendallsCorrelation instance without data.
+ */
+ public KendallsCorrelation() {
+ correlationMatrix = null;
+ }
+
+ /**
+ * Create a KendallsCorrelation from a rectangular array
+ * whose columns represent values of variables to be correlated.
+ *
+ * @param data rectangular array with columns representing variables
+ * @throws IllegalArgumentException if the input data array is not
+ * rectangular with at least two rows and two columns.
+ */
+ public KendallsCorrelation(double[][] data) {
+ this(MatrixUtils.createRealMatrix(data));
+ }
+
+ /**
+ * Create a KendallsCorrelation from a RealMatrix whose columns
+ * represent variables to be correlated.
+ *
+ * @param matrix matrix with columns representing variables to correlate
+ */
+ public KendallsCorrelation(RealMatrix matrix) {
+ correlationMatrix = computeCorrelationMatrix(matrix);
+ }
+
+ /**
+ * Returns the correlation matrix.
+ *
+ * @return correlation matrix
+ */
+ public RealMatrix getCorrelationMatrix() {
+ return correlationMatrix;
+ }
+
+ /**
+ * Computes the Kendall's Tau rank correlation matrix for the columns of
+ * the input matrix.
+ *
+ * @param matrix matrix with columns representing variables to correlate
+ * @return correlation matrix
+ */
+ public RealMatrix computeCorrelationMatrix(final RealMatrix matrix) {
+ int nVars = matrix.getColumnDimension();
+ RealMatrix outMatrix = new BlockRealMatrix(nVars, nVars);
+ for (int i = 0; i < nVars; i++) {
+ for (int j = 0; j < i; j++) {
+ double corr = correlation(matrix.getColumn(i), matrix.getColumn(j));
+ outMatrix.setEntry(i, j, corr);
+ outMatrix.setEntry(j, i, corr);
+ }
+ outMatrix.setEntry(i, i, 1d);
+ }
+ return outMatrix;
+ }
+
+ /**
+ * Computes the Kendall's Tau rank correlation matrix for the columns of
+ * the input rectangular array. The columns of the array represent values
+ * of variables to be correlated.
+ *
+ * @param matrix matrix with columns representing variables to correlate
+ * @return correlation matrix
+ */
+ public RealMatrix computeCorrelationMatrix(final double[][] matrix) {
+ return computeCorrelationMatrix(new BlockRealMatrix(matrix));
+ }
+
+ /**
+ * Computes the Kendall's Tau rank correlation coefficient between the two arrays.
+ *
+ * @param xArray first data array
+ * @param yArray second data array
+ * @return Returns Kendall's Tau rank correlation coefficient for the two arrays
+ * @throws DimensionMismatchException if the arrays lengths do not match
+ */
+ public double correlation(final double[] xArray, final double[] yArray)
+ throws DimensionMismatchException {
+
+ if (xArray.length != yArray.length) {
+ throw new DimensionMismatchException(xArray.length, yArray.length);
+ }
+
+ final int n = xArray.length;
+ final long numPairs = sum(n - 1);
+
+ @SuppressWarnings("unchecked")
+ Pair<Double, Double>[] pairs = new Pair[n];
+ for (int i = 0; i < n; i++) {
+ pairs[i] = new Pair<Double, Double>(xArray[i], yArray[i]);
+ }
+
+ Arrays.sort(pairs, new Comparator<Pair<Double, Double>>() {
+ /** {@inheritDoc} */
+ public int compare(Pair<Double, Double> pair1, Pair<Double, Double> pair2) {
+ int compareFirst = pair1.getFirst().compareTo(pair2.getFirst());
+ return compareFirst != 0 ? compareFirst : pair1.getSecond().compareTo(pair2.getSecond());
+ }
+ });
+
+ long tiedXPairs = 0;
+ long tiedXYPairs = 0;
+ long consecutiveXTies = 1;
+ long consecutiveXYTies = 1;
+ Pair<Double, Double> prev = pairs[0];
+ for (int i = 1; i < n; i++) {
+ final Pair<Double, Double> curr = pairs[i];
+ if (curr.getFirst().equals(prev.getFirst())) {
+ consecutiveXTies++;
+ if (curr.getSecond().equals(prev.getSecond())) {
+ consecutiveXYTies++;
+ } else {
+ tiedXYPairs += sum(consecutiveXYTies - 1);
+ consecutiveXYTies = 1;
+ }
+ } else {
+ tiedXPairs += sum(consecutiveXTies - 1);
+ consecutiveXTies = 1;
+ tiedXYPairs += sum(consecutiveXYTies - 1);
+ consecutiveXYTies = 1;
+ }
+ prev = curr;
+ }
+ tiedXPairs += sum(consecutiveXTies - 1);
+ tiedXYPairs += sum(consecutiveXYTies - 1);
+
+ long swaps = 0;
+ @SuppressWarnings("unchecked")
+ Pair<Double, Double>[] pairsDestination = new Pair[n];
+ for (int segmentSize = 1; segmentSize < n; segmentSize <<= 1) {
+ for (int offset = 0; offset < n; offset += 2 * segmentSize) {
+ int i = offset;
+ final int iEnd = FastMath.min(i + segmentSize, n);
+ int j = iEnd;
+ final int jEnd = FastMath.min(j + segmentSize, n);
+
+ int copyLocation = offset;
+ while (i < iEnd || j < jEnd) {
+ if (i < iEnd) {
+ if (j < jEnd) {
+ if (pairs[i].getSecond().compareTo(pairs[j].getSecond()) <= 0) {
+ pairsDestination[copyLocation] = pairs[i];
+ i++;
+ } else {
+ pairsDestination[copyLocation] = pairs[j];
+ j++;
+ swaps += iEnd - i;
+ }
+ } else {
+ pairsDestination[copyLocation] = pairs[i];
+ i++;
+ }
+ } else {
+ pairsDestination[copyLocation] = pairs[j];
+ j++;
+ }
+ copyLocation++;
+ }
+ }
+ final Pair<Double, Double>[] pairsTemp = pairs;
+ pairs = pairsDestination;
+ pairsDestination = pairsTemp;
+ }
+
+ long tiedYPairs = 0;
+ long consecutiveYTies = 1;
+ prev = pairs[0];
+ for (int i = 1; i < n; i++) {
+ final Pair<Double, Double> curr = pairs[i];
+ if (curr.getSecond().equals(prev.getSecond())) {
+ consecutiveYTies++;
+ } else {
+ tiedYPairs += sum(consecutiveYTies - 1);
+ consecutiveYTies = 1;
+ }
+ prev = curr;
+ }
+ tiedYPairs += sum(consecutiveYTies - 1);
+
+ final long concordantMinusDiscordant = numPairs - tiedXPairs - tiedYPairs + tiedXYPairs - 2 * swaps;
+ final double nonTiedPairsMultiplied = (numPairs - tiedXPairs) * (double) (numPairs - tiedYPairs);
+ return concordantMinusDiscordant / FastMath.sqrt(nonTiedPairsMultiplied);
+ }
+
+ /**
+ * Returns the sum of the number from 1 .. n according to Gauss' summation formula:
+ * \[ \sum\limits_{k=1}^n k = \frac{n(n + 1)}{2} \]
+ *
+ * @param n the summation end
+ * @return the sum of the number from 1 to n
+ */
+ private static long sum(long n) {
+ return n * (n + 1) / 2l;
+ }
+}
diff --git a/src/main/java/org/apache/commons/math3/stat/correlation/PearsonsCorrelation.java b/src/main/java/org/apache/commons/math3/stat/correlation/PearsonsCorrelation.java
new file mode 100644
index 0000000..53d17ab
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/stat/correlation/PearsonsCorrelation.java
@@ -0,0 +1,330 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math3.stat.correlation;
+
+import org.apache.commons.math3.distribution.TDistribution;
+import org.apache.commons.math3.exception.util.LocalizedFormats;
+import org.apache.commons.math3.exception.MathIllegalArgumentException;
+import org.apache.commons.math3.exception.NullArgumentException;
+import org.apache.commons.math3.exception.DimensionMismatchException;
+import org.apache.commons.math3.linear.RealMatrix;
+import org.apache.commons.math3.linear.BlockRealMatrix;
+import org.apache.commons.math3.stat.regression.SimpleRegression;
+import org.apache.commons.math3.util.FastMath;
+
+/**
+ * Computes Pearson's product-moment correlation coefficients for pairs of arrays
+ * or columns of a matrix.
+ *
+ * <p>The constructors that take <code>RealMatrix</code> or
+ * <code>double[][]</code> arguments generate correlation matrices. The
+ * columns of the input matrices are assumed to represent variable values.
+ * Correlations are given by the formula</p>
+ *
+ * <p><code>cor(X, Y) = &Sigma;[(x<sub>i</sub> - E(X))(y<sub>i</sub> - E(Y))] / [(n - 1)s(X)s(Y)]</code>
+ * where <code>E(X)</code> is the mean of <code>X</code>, <code>E(Y)</code>
+ * is the mean of the <code>Y</code> values and s(X), s(Y) are standard deviations.</p>
+ *
+ * <p>To compute the correlation coefficient for a single pair of arrays, use {@link #PearsonsCorrelation()}
+ * to construct an instance with no data and then {@link #correlation(double[], double[])}.
+ * Correlation matrices can also be computed directly from an instance with no data using
+ * {@link #computeCorrelationMatrix(double[][])}. In order to use {@link #getCorrelationMatrix()},
+ * {@link #getCorrelationPValues()}, or {@link #getCorrelationStandardErrors()}; however, one of the
+ * constructors supplying data or a covariance matrix must be used to create the instance.</p>
+ *
+ * @since 2.0
+ */
+public class PearsonsCorrelation {
+
+ /** correlation matrix */
+ private final RealMatrix correlationMatrix;
+
+ /** number of observations */
+ private final int nObs;
+
+ /**
+ * Create a PearsonsCorrelation instance without data.
+ */
+ public PearsonsCorrelation() {
+ super();
+ correlationMatrix = null;
+ nObs = 0;
+ }
+
+ /**
+ * Create a PearsonsCorrelation from a rectangular array
+ * whose columns represent values of variables to be correlated.
+ *
+ * Throws MathIllegalArgumentException if the input array does not have at least
+ * two columns and two rows. Pairwise correlations are set to NaN if one
+ * of the correlates has zero variance.
+ *
+ * @param data rectangular array with columns representing variables
+ * @throws MathIllegalArgumentException if the input data array is not
+ * rectangular with at least two rows and two columns.
+ * @see #correlation(double[], double[])
+ */
+ public PearsonsCorrelation(double[][] data) {
+ this(new BlockRealMatrix(data));
+ }
+
+ /**
+ * Create a PearsonsCorrelation from a RealMatrix whose columns
+ * represent variables to be correlated.
+ *
+ * Throws MathIllegalArgumentException if the matrix does not have at least
+ * two columns and two rows. Pairwise correlations are set to NaN if one
+ * of the correlates has zero variance.
+ *
+ * @param matrix matrix with columns representing variables to correlate
+ * @throws MathIllegalArgumentException if the matrix does not contain sufficient data
+ * @see #correlation(double[], double[])
+ */
+ public PearsonsCorrelation(RealMatrix matrix) {
+ nObs = matrix.getRowDimension();
+ correlationMatrix = computeCorrelationMatrix(matrix);
+ }
+
+ /**
+ * Create a PearsonsCorrelation from a {@link Covariance}. The correlation
+ * matrix is computed by scaling the Covariance's covariance matrix.
+ * The Covariance instance must have been created from a data matrix with
+ * columns representing variable values.
+ *
+ * @param covariance Covariance instance
+ */
+ public PearsonsCorrelation(Covariance covariance) {
+ RealMatrix covarianceMatrix = covariance.getCovarianceMatrix();
+ if (covarianceMatrix == null) {
+ throw new NullArgumentException(LocalizedFormats.COVARIANCE_MATRIX);
+ }
+ nObs = covariance.getN();
+ correlationMatrix = covarianceToCorrelation(covarianceMatrix);
+ }
+
+ /**
+ * Create a PearsonsCorrelation from a covariance matrix. The correlation
+ * matrix is computed by scaling the covariance matrix.
+ *
+ * @param covarianceMatrix covariance matrix
+ * @param numberOfObservations the number of observations in the dataset used to compute
+ * the covariance matrix
+ */
+ public PearsonsCorrelation(RealMatrix covarianceMatrix, int numberOfObservations) {
+ nObs = numberOfObservations;
+ correlationMatrix = covarianceToCorrelation(covarianceMatrix);
+ }
+
+ /**
+ * Returns the correlation matrix.
+ *
+ * <p>This method will return null if the argumentless constructor was used
+ * to create this instance, even if {@link #computeCorrelationMatrix(double[][])}
+ * has been called before it is activated.</p>
+ *
+ * @return correlation matrix
+ */
+ public RealMatrix getCorrelationMatrix() {
+ return correlationMatrix;
+ }
+
+ /**
+ * Returns a matrix of standard errors associated with the estimates
+ * in the correlation matrix.<br/>
+ * <code>getCorrelationStandardErrors().getEntry(i,j)</code> is the standard
+ * error associated with <code>getCorrelationMatrix.getEntry(i,j)</code>
+ *
+ * <p>The formula used to compute the standard error is <br/>
+ * <code>SE<sub>r</sub> = ((1 - r<sup>2</sup>) / (n - 2))<sup>1/2</sup></code>
+ * where <code>r</code> is the estimated correlation coefficient and
+ * <code>n</code> is the number of observations in the source dataset.</p>
+ *
+ * <p>To use this method, one of the constructors that supply an input
+ * matrix must have been used to create this instance.</p>
+ *
+ * @return matrix of correlation standard errors
+ * @throws NullPointerException if this instance was created with no data
+ */
+ public RealMatrix getCorrelationStandardErrors() {
+ int nVars = correlationMatrix.getColumnDimension();
+ double[][] out = new double[nVars][nVars];
+ for (int i = 0; i < nVars; i++) {
+ for (int j = 0; j < nVars; j++) {
+ double r = correlationMatrix.getEntry(i, j);
+ out[i][j] = FastMath.sqrt((1 - r * r) /(nObs - 2));
+ }
+ }
+ return new BlockRealMatrix(out);
+ }
+
+ /**
+ * Returns a matrix of p-values associated with the (two-sided) null
+ * hypothesis that the corresponding correlation coefficient is zero.
+ *
+ * <p><code>getCorrelationPValues().getEntry(i,j)</code> is the probability
+ * that a random variable distributed as <code>t<sub>n-2</sub></code> takes
+ * a value with absolute value greater than or equal to <br>
+ * <code>|r|((n - 2) / (1 - r<sup>2</sup>))<sup>1/2</sup></code></p>
+ *
+ * <p>The values in the matrix are sometimes referred to as the
+ * <i>significance</i> of the corresponding correlation coefficients.</p>
+ *
+ * <p>To use this method, one of the constructors that supply an input
+ * matrix must have been used to create this instance.</p>
+ *
+ * @return matrix of p-values
+ * @throws org.apache.commons.math3.exception.MaxCountExceededException
+ * if an error occurs estimating probabilities
+ * @throws NullPointerException if this instance was created with no data
+ */
+ public RealMatrix getCorrelationPValues() {
+ TDistribution tDistribution = new TDistribution(nObs - 2);
+ int nVars = correlationMatrix.getColumnDimension();
+ double[][] out = new double[nVars][nVars];
+ for (int i = 0; i < nVars; i++) {
+ for (int j = 0; j < nVars; j++) {
+ if (i == j) {
+ out[i][j] = 0d;
+ } else {
+ double r = correlationMatrix.getEntry(i, j);
+ double t = FastMath.abs(r * FastMath.sqrt((nObs - 2)/(1 - r * r)));
+ out[i][j] = 2 * tDistribution.cumulativeProbability(-t);
+ }
+ }
+ }
+ return new BlockRealMatrix(out);
+ }
+
+
+ /**
+ * Computes the correlation matrix for the columns of the
+ * input matrix, using {@link #correlation(double[], double[])}.
+ *
+ * Throws MathIllegalArgumentException if the matrix does not have at least
+ * two columns and two rows. Pairwise correlations are set to NaN if one
+ * of the correlates has zero variance.
+ *
+ * @param matrix matrix with columns representing variables to correlate
+ * @return correlation matrix
+ * @throws MathIllegalArgumentException if the matrix does not contain sufficient data
+ * @see #correlation(double[], double[])
+ */
+ public RealMatrix computeCorrelationMatrix(RealMatrix matrix) {
+ checkSufficientData(matrix);
+ int nVars = matrix.getColumnDimension();
+ RealMatrix outMatrix = new BlockRealMatrix(nVars, nVars);
+ for (int i = 0; i < nVars; i++) {
+ for (int j = 0; j < i; j++) {
+ double corr = correlation(matrix.getColumn(i), matrix.getColumn(j));
+ outMatrix.setEntry(i, j, corr);
+ outMatrix.setEntry(j, i, corr);
+ }
+ outMatrix.setEntry(i, i, 1d);
+ }
+ return outMatrix;
+ }
+
+ /**
+ * Computes the correlation matrix for the columns of the
+ * input rectangular array. The columns of the array represent values
+ * of variables to be correlated.
+ *
+ * Throws MathIllegalArgumentException if the matrix does not have at least
+ * two columns and two rows or if the array is not rectangular. Pairwise
+ * correlations are set to NaN if one of the correlates has zero variance.
+ *
+ * @param data matrix with columns representing variables to correlate
+ * @return correlation matrix
+ * @throws MathIllegalArgumentException if the array does not contain sufficient data
+ * @see #correlation(double[], double[])
+ */
+ public RealMatrix computeCorrelationMatrix(double[][] data) {
+ return computeCorrelationMatrix(new BlockRealMatrix(data));
+ }
+
+ /**
+ * Computes the Pearson's product-moment correlation coefficient between two arrays.
+ *
+ * <p>Throws MathIllegalArgumentException if the arrays do not have the same length
+ * or their common length is less than 2. Returns {@code NaN} if either of the arrays
+ * has zero variance (i.e., if one of the arrays does not contain at least two distinct
+ * values).</p>
+ *
+ * @param xArray first data array
+ * @param yArray second data array
+ * @return Returns Pearson's correlation coefficient for the two arrays
+ * @throws DimensionMismatchException if the arrays lengths do not match
+ * @throws MathIllegalArgumentException if there is insufficient data
+ */
+ public double correlation(final double[] xArray, final double[] yArray) {
+ SimpleRegression regression = new SimpleRegression();
+ if (xArray.length != yArray.length) {
+ throw new DimensionMismatchException(xArray.length, yArray.length);
+ } else if (xArray.length < 2) {
+ throw new MathIllegalArgumentException(LocalizedFormats.INSUFFICIENT_DIMENSION,
+ xArray.length, 2);
+ } else {
+ for(int i=0; i<xArray.length; i++) {
+ regression.addData(xArray[i], yArray[i]);
+ }
+ return regression.getR();
+ }
+ }
+
+ /**
+ * Derives a correlation matrix from a covariance matrix.
+ *
+ * <p>Uses the formula <br/>
+ * <code>r(X,Y) = cov(X,Y)/s(X)s(Y)</code> where
+ * <code>r(&middot,&middot;)</code> is the correlation coefficient and
+ * <code>s(&middot;)</code> means standard deviation.</p>
+ *
+ * @param covarianceMatrix the covariance matrix
+ * @return correlation matrix
+ */
+ public RealMatrix covarianceToCorrelation(RealMatrix covarianceMatrix) {
+ int nVars = covarianceMatrix.getColumnDimension();
+ RealMatrix outMatrix = new BlockRealMatrix(nVars, nVars);
+ for (int i = 0; i < nVars; i++) {
+ double sigma = FastMath.sqrt(covarianceMatrix.getEntry(i, i));
+ outMatrix.setEntry(i, i, 1d);
+ for (int j = 0; j < i; j++) {
+ double entry = covarianceMatrix.getEntry(i, j) /
+ (sigma * FastMath.sqrt(covarianceMatrix.getEntry(j, j)));
+ outMatrix.setEntry(i, j, entry);
+ outMatrix.setEntry(j, i, entry);
+ }
+ }
+ return outMatrix;
+ }
+
+ /**
+ * Throws MathIllegalArgumentException if the matrix does not have at least
+ * two columns and two rows.
+ *
+ * @param matrix matrix to check for sufficiency
+ * @throws MathIllegalArgumentException if there is insufficient data
+ */
+ private void checkSufficientData(final RealMatrix matrix) {
+ int nRows = matrix.getRowDimension();
+ int nCols = matrix.getColumnDimension();
+ if (nRows < 2 || nCols < 2) {
+ throw new MathIllegalArgumentException(LocalizedFormats.INSUFFICIENT_ROWS_AND_COLUMNS,
+ nRows, nCols);
+ }
+ }
+}
diff --git a/src/main/java/org/apache/commons/math3/stat/correlation/SpearmansCorrelation.java b/src/main/java/org/apache/commons/math3/stat/correlation/SpearmansCorrelation.java
new file mode 100644
index 0000000..80c0a54
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/stat/correlation/SpearmansCorrelation.java
@@ -0,0 +1,262 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math3.stat.correlation;
+
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+import org.apache.commons.math3.exception.DimensionMismatchException;
+import org.apache.commons.math3.exception.MathIllegalArgumentException;
+import org.apache.commons.math3.exception.util.LocalizedFormats;
+import org.apache.commons.math3.linear.BlockRealMatrix;
+import org.apache.commons.math3.linear.RealMatrix;
+import org.apache.commons.math3.stat.ranking.NaNStrategy;
+import org.apache.commons.math3.stat.ranking.NaturalRanking;
+import org.apache.commons.math3.stat.ranking.RankingAlgorithm;
+
+/**
+ * Spearman's rank correlation. This implementation performs a rank
+ * transformation on the input data and then computes {@link PearsonsCorrelation}
+ * on the ranked data.
+ * <p>
+ * By default, ranks are computed using {@link NaturalRanking} with default
+ * strategies for handling NaNs and ties in the data (NaNs maximal, ties averaged).
+ * The ranking algorithm can be set using a constructor argument.
+ *
+ * @since 2.0
+ */
+public class SpearmansCorrelation {
+
+ /** Input data */
+ private final RealMatrix data;
+
+ /** Ranking algorithm */
+ private final RankingAlgorithm rankingAlgorithm;
+
+ /** Rank correlation */
+ private final PearsonsCorrelation rankCorrelation;
+
+ /**
+ * Create a SpearmansCorrelation without data.
+ */
+ public SpearmansCorrelation() {
+ this(new NaturalRanking());
+ }
+
+ /**
+ * Create a SpearmansCorrelation with the given ranking algorithm.
+ * <p>
+ * From version 4.0 onwards this constructor will throw an exception
+ * if the provided {@link NaturalRanking} uses a {@link NaNStrategy#REMOVED} strategy.
+ *
+ * @param rankingAlgorithm ranking algorithm
+ * @since 3.1
+ */
+ public SpearmansCorrelation(final RankingAlgorithm rankingAlgorithm) {
+ data = null;
+ this.rankingAlgorithm = rankingAlgorithm;
+ rankCorrelation = null;
+ }
+
+ /**
+ * Create a SpearmansCorrelation from the given data matrix.
+ *
+ * @param dataMatrix matrix of data with columns representing
+ * variables to correlate
+ */
+ public SpearmansCorrelation(final RealMatrix dataMatrix) {
+ this(dataMatrix, new NaturalRanking());
+ }
+
+ /**
+ * Create a SpearmansCorrelation with the given input data matrix
+ * and ranking algorithm.
+ * <p>
+ * From version 4.0 onwards this constructor will throw an exception
+ * if the provided {@link NaturalRanking} uses a {@link NaNStrategy#REMOVED} strategy.
+ *
+ * @param dataMatrix matrix of data with columns representing
+ * variables to correlate
+ * @param rankingAlgorithm ranking algorithm
+ */
+ public SpearmansCorrelation(final RealMatrix dataMatrix, final RankingAlgorithm rankingAlgorithm) {
+ this.rankingAlgorithm = rankingAlgorithm;
+ this.data = rankTransform(dataMatrix);
+ rankCorrelation = new PearsonsCorrelation(data);
+ }
+
+ /**
+ * Calculate the Spearman Rank Correlation Matrix.
+ *
+ * @return Spearman Rank Correlation Matrix
+ * @throws NullPointerException if this instance was created with no data
+ */
+ public RealMatrix getCorrelationMatrix() {
+ return rankCorrelation.getCorrelationMatrix();
+ }
+
+ /**
+ * Returns a {@link PearsonsCorrelation} instance constructed from the
+ * ranked input data. That is,
+ * <code>new SpearmansCorrelation(matrix).getRankCorrelation()</code>
+ * is equivalent to
+ * <code>new PearsonsCorrelation(rankTransform(matrix))</code> where
+ * <code>rankTransform(matrix)</code> is the result of applying the
+ * configured <code>RankingAlgorithm</code> to each of the columns of
+ * <code>matrix.</code>
+ *
+ * <p>Returns null if this instance was created with no data.</p>
+ *
+ * @return PearsonsCorrelation among ranked column data
+ */
+ public PearsonsCorrelation getRankCorrelation() {
+ return rankCorrelation;
+ }
+
+ /**
+ * Computes the Spearman's rank correlation matrix for the columns of the
+ * input matrix.
+ *
+ * @param matrix matrix with columns representing variables to correlate
+ * @return correlation matrix
+ */
+ public RealMatrix computeCorrelationMatrix(final RealMatrix matrix) {
+ final RealMatrix matrixCopy = rankTransform(matrix);
+ return new PearsonsCorrelation().computeCorrelationMatrix(matrixCopy);
+ }
+
+ /**
+ * Computes the Spearman's rank correlation matrix for the columns of the
+ * input rectangular array. The columns of the array represent values
+ * of variables to be correlated.
+ *
+ * @param matrix matrix with columns representing variables to correlate
+ * @return correlation matrix
+ */
+ public RealMatrix computeCorrelationMatrix(final double[][] matrix) {
+ return computeCorrelationMatrix(new BlockRealMatrix(matrix));
+ }
+
+ /**
+ * Computes the Spearman's rank correlation coefficient between the two arrays.
+ *
+ * @param xArray first data array
+ * @param yArray second data array
+ * @return Returns Spearman's rank correlation coefficient for the two arrays
+ * @throws DimensionMismatchException if the arrays lengths do not match
+ * @throws MathIllegalArgumentException if the array length is less than 2
+ */
+ public double correlation(final double[] xArray, final double[] yArray) {
+ if (xArray.length != yArray.length) {
+ throw new DimensionMismatchException(xArray.length, yArray.length);
+ } else if (xArray.length < 2) {
+ throw new MathIllegalArgumentException(LocalizedFormats.INSUFFICIENT_DIMENSION,
+ xArray.length, 2);
+ } else {
+ double[] x = xArray;
+ double[] y = yArray;
+ if (rankingAlgorithm instanceof NaturalRanking &&
+ NaNStrategy.REMOVED == ((NaturalRanking) rankingAlgorithm).getNanStrategy()) {
+ final Set<Integer> nanPositions = new HashSet<Integer>();
+
+ nanPositions.addAll(getNaNPositions(xArray));
+ nanPositions.addAll(getNaNPositions(yArray));
+
+ x = removeValues(xArray, nanPositions);
+ y = removeValues(yArray, nanPositions);
+ }
+ return new PearsonsCorrelation().correlation(rankingAlgorithm.rank(x), rankingAlgorithm.rank(y));
+ }
+ }
+
+ /**
+ * Applies rank transform to each of the columns of <code>matrix</code>
+ * using the current <code>rankingAlgorithm</code>.
+ *
+ * @param matrix matrix to transform
+ * @return a rank-transformed matrix
+ */
+ private RealMatrix rankTransform(final RealMatrix matrix) {
+ RealMatrix transformed = null;
+
+ if (rankingAlgorithm instanceof NaturalRanking &&
+ ((NaturalRanking) rankingAlgorithm).getNanStrategy() == NaNStrategy.REMOVED) {
+ final Set<Integer> nanPositions = new HashSet<Integer>();
+ for (int i = 0; i < matrix.getColumnDimension(); i++) {
+ nanPositions.addAll(getNaNPositions(matrix.getColumn(i)));
+ }
+
+ // if we have found NaN values, we have to update the matrix size
+ if (!nanPositions.isEmpty()) {
+ transformed = new BlockRealMatrix(matrix.getRowDimension() - nanPositions.size(),
+ matrix.getColumnDimension());
+ for (int i = 0; i < transformed.getColumnDimension(); i++) {
+ transformed.setColumn(i, removeValues(matrix.getColumn(i), nanPositions));
+ }
+ }
+ }
+
+ if (transformed == null) {
+ transformed = matrix.copy();
+ }
+
+ for (int i = 0; i < transformed.getColumnDimension(); i++) {
+ transformed.setColumn(i, rankingAlgorithm.rank(transformed.getColumn(i)));
+ }
+
+ return transformed;
+ }
+
+ /**
+ * Returns a list containing the indices of NaN values in the input array.
+ *
+ * @param input the input array
+ * @return a list of NaN positions in the input array
+ */
+ private List<Integer> getNaNPositions(final double[] input) {
+ final List<Integer> positions = new ArrayList<Integer>();
+ for (int i = 0; i < input.length; i++) {
+ if (Double.isNaN(input[i])) {
+ positions.add(i);
+ }
+ }
+ return positions;
+ }
+
+ /**
+ * Removes all values from the input array at the specified indices.
+ *
+ * @param input the input array
+ * @param indices a set containing the indices to be removed
+ * @return the input array without the values at the specified indices
+ */
+ private double[] removeValues(final double[] input, final Set<Integer> indices) {
+ if (indices.isEmpty()) {
+ return input;
+ }
+ final double[] result = new double[input.length - indices.size()];
+ for (int i = 0, j = 0; i < input.length; i++) {
+ if (!indices.contains(i)) {
+ result[j++] = input[i];
+ }
+ }
+ return result;
+ }
+}
diff --git a/src/main/java/org/apache/commons/math3/stat/correlation/StorelessBivariateCovariance.java b/src/main/java/org/apache/commons/math3/stat/correlation/StorelessBivariateCovariance.java
new file mode 100644
index 0000000..1a798d2
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/stat/correlation/StorelessBivariateCovariance.java
@@ -0,0 +1,138 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math3.stat.correlation;
+
+import org.apache.commons.math3.exception.NumberIsTooSmallException;
+import org.apache.commons.math3.exception.util.LocalizedFormats;
+
+/**
+ * Bivariate Covariance implementation that does not require input data to be
+ * stored in memory.
+ *
+ * <p>This class is based on a paper written by Philippe P&eacute;bay:
+ * <a href="http://prod.sandia.gov/techlib/access-control.cgi/2008/086212.pdf">
+ * Formulas for Robust, One-Pass Parallel Computation of Covariances and
+ * Arbitrary-Order Statistical Moments</a>, 2008, Technical Report SAND2008-6212,
+ * Sandia National Laboratories. It computes the covariance for a pair of variables.
+ * Use {@link StorelessCovariance} to estimate an entire covariance matrix.</p>
+ *
+ * <p>Note: This class is package private as it is only used internally in
+ * the {@link StorelessCovariance} class.</p>
+ *
+ * @since 3.0
+ */
+class StorelessBivariateCovariance {
+
+ /** the mean of variable x */
+ private double meanX;
+
+ /** the mean of variable y */
+ private double meanY;
+
+ /** number of observations */
+ private double n;
+
+ /** the running covariance estimate */
+ private double covarianceNumerator;
+
+ /** flag for bias correction */
+ private boolean biasCorrected;
+
+ /**
+ * Create an empty {@link StorelessBivariateCovariance} instance with
+ * bias correction.
+ */
+ StorelessBivariateCovariance() {
+ this(true);
+ }
+
+ /**
+ * Create an empty {@link StorelessBivariateCovariance} instance.
+ *
+ * @param biasCorrection if <code>true</code> the covariance estimate is corrected
+ * for bias, i.e. n-1 in the denominator, otherwise there is no bias correction,
+ * i.e. n in the denominator.
+ */
+ StorelessBivariateCovariance(final boolean biasCorrection) {
+ meanX = meanY = 0.0;
+ n = 0;
+ covarianceNumerator = 0.0;
+ biasCorrected = biasCorrection;
+ }
+
+ /**
+ * Update the covariance estimation with a pair of variables (x, y).
+ *
+ * @param x the x value
+ * @param y the y value
+ */
+ public void increment(final double x, final double y) {
+ n++;
+ final double deltaX = x - meanX;
+ final double deltaY = y - meanY;
+ meanX += deltaX / n;
+ meanY += deltaY / n;
+ covarianceNumerator += ((n - 1.0) / n) * deltaX * deltaY;
+ }
+
+ /**
+ * Appends another bivariate covariance calculation to this.
+ * After this operation, statistics returned should be close to what would
+ * have been obtained by by performing all of the {@link #increment(double, double)}
+ * operations in {@code cov} directly on this.
+ *
+ * @param cov StorelessBivariateCovariance instance to append.
+ */
+ public void append(StorelessBivariateCovariance cov) {
+ double oldN = n;
+ n += cov.n;
+ final double deltaX = cov.meanX - meanX;
+ final double deltaY = cov.meanY - meanY;
+ meanX += deltaX * cov.n / n;
+ meanY += deltaY * cov.n / n;
+ covarianceNumerator += cov.covarianceNumerator + oldN * cov.n / n * deltaX * deltaY;
+ }
+
+ /**
+ * Returns the number of observations.
+ *
+ * @return number of observations
+ */
+ public double getN() {
+ return n;
+ }
+
+ /**
+ * Return the current covariance estimate.
+ *
+ * @return the current covariance
+ * @throws NumberIsTooSmallException if the number of observations
+ * is &lt; 2
+ */
+ public double getResult() throws NumberIsTooSmallException {
+ if (n < 2) {
+ throw new NumberIsTooSmallException(LocalizedFormats.INSUFFICIENT_DIMENSION,
+ n, 2, true);
+ }
+ if (biasCorrected) {
+ return covarianceNumerator / (n - 1d);
+ } else {
+ return covarianceNumerator / n;
+ }
+ }
+}
+
diff --git a/src/main/java/org/apache/commons/math3/stat/correlation/StorelessCovariance.java b/src/main/java/org/apache/commons/math3/stat/correlation/StorelessCovariance.java
new file mode 100644
index 0000000..7e927ca
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/stat/correlation/StorelessCovariance.java
@@ -0,0 +1,229 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math3.stat.correlation;
+
+import org.apache.commons.math3.exception.DimensionMismatchException;
+import org.apache.commons.math3.exception.MathUnsupportedOperationException;
+import org.apache.commons.math3.exception.NumberIsTooSmallException;
+import org.apache.commons.math3.linear.MatrixUtils;
+import org.apache.commons.math3.linear.RealMatrix;
+
+/**
+ * Covariance implementation that does not require input data to be
+ * stored in memory. The size of the covariance matrix is specified in the
+ * constructor. Specific elements of the matrix are incrementally updated with
+ * calls to incrementRow() or increment Covariance().
+ *
+ * <p>This class is based on a paper written by Philippe P&eacute;bay:
+ * <a href="http://prod.sandia.gov/techlib/access-control.cgi/2008/086212.pdf">
+ * Formulas for Robust, One-Pass Parallel Computation of Covariances and
+ * Arbitrary-Order Statistical Moments</a>, 2008, Technical Report SAND2008-6212,
+ * Sandia National Laboratories.</p>
+ *
+ * <p>Note: the underlying covariance matrix is symmetric, thus only the
+ * upper triangular part of the matrix is stored and updated each increment.</p>
+ *
+ * @since 3.0
+ */
+public class StorelessCovariance extends Covariance {
+
+ /** the square covariance matrix (upper triangular part) */
+ private StorelessBivariateCovariance[] covMatrix;
+
+ /** dimension of the square covariance matrix */
+ private int dimension;
+
+ /**
+ * Create a bias corrected covariance matrix with a given dimension.
+ *
+ * @param dim the dimension of the square covariance matrix
+ */
+ public StorelessCovariance(final int dim) {
+ this(dim, true);
+ }
+
+ /**
+ * Create a covariance matrix with a given number of rows and columns and the
+ * indicated bias correction.
+ *
+ * @param dim the dimension of the covariance matrix
+ * @param biasCorrected if <code>true</code> the covariance estimate is corrected
+ * for bias, i.e. n-1 in the denominator, otherwise there is no bias correction,
+ * i.e. n in the denominator.
+ */
+ public StorelessCovariance(final int dim, final boolean biasCorrected) {
+ dimension = dim;
+ covMatrix = new StorelessBivariateCovariance[dimension * (dimension + 1) / 2];
+ initializeMatrix(biasCorrected);
+ }
+
+ /**
+ * Initialize the internal two-dimensional array of
+ * {@link StorelessBivariateCovariance} instances.
+ *
+ * @param biasCorrected if the covariance estimate shall be corrected for bias
+ */
+ private void initializeMatrix(final boolean biasCorrected) {
+ for(int i = 0; i < dimension; i++){
+ for(int j = 0; j < dimension; j++){
+ setElement(i, j, new StorelessBivariateCovariance(biasCorrected));
+ }
+ }
+ }
+
+ /**
+ * Returns the index (i, j) translated into the one-dimensional
+ * array used to store the upper triangular part of the symmetric
+ * covariance matrix.
+ *
+ * @param i the row index
+ * @param j the column index
+ * @return the corresponding index in the matrix array
+ */
+ private int indexOf(final int i, final int j) {
+ return j < i ? i * (i + 1) / 2 + j : j * (j + 1) / 2 + i;
+ }
+
+ /**
+ * Gets the element at index (i, j) from the covariance matrix
+ * @param i the row index
+ * @param j the column index
+ * @return the {@link StorelessBivariateCovariance} element at the given index
+ */
+ private StorelessBivariateCovariance getElement(final int i, final int j) {
+ return covMatrix[indexOf(i, j)];
+ }
+
+ /**
+ * Sets the covariance element at index (i, j) in the covariance matrix
+ * @param i the row index
+ * @param j the column index
+ * @param cov the {@link StorelessBivariateCovariance} element to be set
+ */
+ private void setElement(final int i, final int j,
+ final StorelessBivariateCovariance cov) {
+ covMatrix[indexOf(i, j)] = cov;
+ }
+
+ /**
+ * Get the covariance for an individual element of the covariance matrix.
+ *
+ * @param xIndex row index in the covariance matrix
+ * @param yIndex column index in the covariance matrix
+ * @return the covariance of the given element
+ * @throws NumberIsTooSmallException if the number of observations
+ * in the cell is &lt; 2
+ */
+ public double getCovariance(final int xIndex,
+ final int yIndex)
+ throws NumberIsTooSmallException {
+
+ return getElement(xIndex, yIndex).getResult();
+
+ }
+
+ /**
+ * Increment the covariance matrix with one row of data.
+ *
+ * @param data array representing one row of data.
+ * @throws DimensionMismatchException if the length of <code>rowData</code>
+ * does not match with the covariance matrix
+ */
+ public void increment(final double[] data)
+ throws DimensionMismatchException {
+
+ int length = data.length;
+ if (length != dimension) {
+ throw new DimensionMismatchException(length, dimension);
+ }
+
+ // only update the upper triangular part of the covariance matrix
+ // as only these parts are actually stored
+ for (int i = 0; i < length; i++){
+ for (int j = i; j < length; j++){
+ getElement(i, j).increment(data[i], data[j]);
+ }
+ }
+
+ }
+
+ /**
+ * Appends {@code sc} to this, effectively aggregating the computations in {@code sc}
+ * with this. After invoking this method, covariances returned should be close
+ * to what would have been obtained by performing all of the {@link #increment(double[])}
+ * operations in {@code sc} directly on this.
+ *
+ * @param sc externally computed StorelessCovariance to add to this
+ * @throws DimensionMismatchException if the dimension of sc does not match this
+ * @since 3.3
+ */
+ public void append(StorelessCovariance sc) throws DimensionMismatchException {
+ if (sc.dimension != dimension) {
+ throw new DimensionMismatchException(sc.dimension, dimension);
+ }
+
+ // only update the upper triangular part of the covariance matrix
+ // as only these parts are actually stored
+ for (int i = 0; i < dimension; i++) {
+ for (int j = i; j < dimension; j++) {
+ getElement(i, j).append(sc.getElement(i, j));
+ }
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ * @throws NumberIsTooSmallException if the number of observations
+ * in a cell is &lt; 2
+ */
+ @Override
+ public RealMatrix getCovarianceMatrix() throws NumberIsTooSmallException {
+ return MatrixUtils.createRealMatrix(getData());
+ }
+
+ /**
+ * Return the covariance matrix as two-dimensional array.
+ *
+ * @return a two-dimensional double array of covariance values
+ * @throws NumberIsTooSmallException if the number of observations
+ * for a cell is &lt; 2
+ */
+ public double[][] getData() throws NumberIsTooSmallException {
+ final double[][] data = new double[dimension][dimension];
+ for (int i = 0; i < dimension; i++) {
+ for (int j = 0; j < dimension; j++) {
+ data[i][j] = getElement(i, j).getResult();
+ }
+ }
+ return data;
+ }
+
+ /**
+ * This {@link Covariance} method is not supported by a {@link StorelessCovariance},
+ * since the number of bivariate observations does not have to be the same for different
+ * pairs of covariates - i.e., N as defined in {@link Covariance#getN()} is undefined.
+ *
+ * @return nothing as this implementation always throws a
+ * {@link MathUnsupportedOperationException}
+ * @throws MathUnsupportedOperationException in all cases
+ */
+ @Override
+ public int getN()
+ throws MathUnsupportedOperationException {
+ throw new MathUnsupportedOperationException();
+ }
+}
diff --git a/src/main/java/org/apache/commons/math3/stat/correlation/package-info.java b/src/main/java/org/apache/commons/math3/stat/correlation/package-info.java
new file mode 100644
index 0000000..adf285e
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/stat/correlation/package-info.java
@@ -0,0 +1,22 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/**
+ *
+ * Correlations/Covariance computations.
+ *
+ */
+package org.apache.commons.math3.stat.correlation;
diff --git a/src/main/java/org/apache/commons/math3/stat/descriptive/AbstractStorelessUnivariateStatistic.java b/src/main/java/org/apache/commons/math3/stat/descriptive/AbstractStorelessUnivariateStatistic.java
new file mode 100644
index 0000000..4249994
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/stat/descriptive/AbstractStorelessUnivariateStatistic.java
@@ -0,0 +1,187 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math3.stat.descriptive;
+
+import org.apache.commons.math3.exception.util.LocalizedFormats;
+import org.apache.commons.math3.exception.MathIllegalArgumentException;
+import org.apache.commons.math3.exception.NullArgumentException;
+import org.apache.commons.math3.util.MathUtils;
+import org.apache.commons.math3.util.Precision;
+
+/**
+ *
+ * Abstract implementation of the {@link StorelessUnivariateStatistic} interface.
+ * <p>
+ * Provides default <code>evaluate()</code> and <code>incrementAll(double[])</code>
+ * implementations.</p>
+ * <p>
+ * <strong>Note that these implementations are not synchronized.</strong></p>
+ *
+ */
+public abstract class AbstractStorelessUnivariateStatistic
+ extends AbstractUnivariateStatistic
+ implements StorelessUnivariateStatistic {
+
+ /**
+ * This default implementation calls {@link #clear}, then invokes
+ * {@link #increment} in a loop over the the input array, and then uses
+ * {@link #getResult} to compute the return value.
+ * <p>
+ * Note that this implementation changes the internal state of the
+ * statistic. Its side effects are the same as invoking {@link #clear} and
+ * then {@link #incrementAll(double[])}.</p>
+ * <p>
+ * Implementations may override this method with a more efficient and
+ * possibly more accurate implementation that works directly with the
+ * input array.</p>
+ * <p>
+ * If the array is null, a MathIllegalArgumentException is thrown.</p>
+ * @param values input array
+ * @return the value of the statistic applied to the input array
+ * @throws MathIllegalArgumentException if values is null
+ * @see org.apache.commons.math3.stat.descriptive.UnivariateStatistic#evaluate(double[])
+ */
+ @Override
+ public double evaluate(final double[] values) throws MathIllegalArgumentException {
+ if (values == null) {
+ throw new NullArgumentException(LocalizedFormats.INPUT_ARRAY);
+ }
+ return evaluate(values, 0, values.length);
+ }
+
+ /**
+ * This default implementation calls {@link #clear}, then invokes
+ * {@link #increment} in a loop over the specified portion of the input
+ * array, and then uses {@link #getResult} to compute the return value.
+ * <p>
+ * Note that this implementation changes the internal state of the
+ * statistic. Its side effects are the same as invoking {@link #clear} and
+ * then {@link #incrementAll(double[], int, int)}.</p>
+ * <p>
+ * Implementations may override this method with a more efficient and
+ * possibly more accurate implementation that works directly with the
+ * input array.</p>
+ * <p>
+ * If the array is null or the index parameters are not valid, an
+ * MathIllegalArgumentException is thrown.</p>
+ * @param values the input array
+ * @param begin the index of the first element to include
+ * @param length the number of elements to include
+ * @return the value of the statistic applied to the included array entries
+ * @throws MathIllegalArgumentException if the array is null or the indices are not valid
+ * @see org.apache.commons.math3.stat.descriptive.UnivariateStatistic#evaluate(double[], int, int)
+ */
+ @Override
+ public double evaluate(final double[] values, final int begin,
+ final int length) throws MathIllegalArgumentException {
+ if (test(values, begin, length)) {
+ clear();
+ incrementAll(values, begin, length);
+ }
+ return getResult();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public abstract StorelessUnivariateStatistic copy();
+
+ /**
+ * {@inheritDoc}
+ */
+ public abstract void clear();
+
+ /**
+ * {@inheritDoc}
+ */
+ public abstract double getResult();
+
+ /**
+ * {@inheritDoc}
+ */
+ public abstract void increment(final double d);
+
+ /**
+ * This default implementation just calls {@link #increment} in a loop over
+ * the input array.
+ * <p>
+ * Throws IllegalArgumentException if the input values array is null.</p>
+ *
+ * @param values values to add
+ * @throws MathIllegalArgumentException if values is null
+ * @see org.apache.commons.math3.stat.descriptive.StorelessUnivariateStatistic#incrementAll(double[])
+ */
+ public void incrementAll(double[] values) throws MathIllegalArgumentException {
+ if (values == null) {
+ throw new NullArgumentException(LocalizedFormats.INPUT_ARRAY);
+ }
+ incrementAll(values, 0, values.length);
+ }
+
+ /**
+ * This default implementation just calls {@link #increment} in a loop over
+ * the specified portion of the input array.
+ * <p>
+ * Throws IllegalArgumentException if the input values array is null.</p>
+ *
+ * @param values array holding values to add
+ * @param begin index of the first array element to add
+ * @param length number of array elements to add
+ * @throws MathIllegalArgumentException if values is null
+ * @see org.apache.commons.math3.stat.descriptive.StorelessUnivariateStatistic#incrementAll(double[], int, int)
+ */
+ public void incrementAll(double[] values, int begin, int length) throws MathIllegalArgumentException {
+ if (test(values, begin, length)) {
+ int k = begin + length;
+ for (int i = begin; i < k; i++) {
+ increment(values[i]);
+ }
+ }
+ }
+
+ /**
+ * Returns true iff <code>object</code> is an
+ * <code>AbstractStorelessUnivariateStatistic</code> returning the same
+ * values as this for <code>getResult()</code> and <code>getN()</code>
+ * @param object object to test equality against.
+ * @return true if object returns the same value as this
+ */
+ @Override
+ public boolean equals(Object object) {
+ if (object == this ) {
+ return true;
+ }
+ if (object instanceof AbstractStorelessUnivariateStatistic == false) {
+ return false;
+ }
+ AbstractStorelessUnivariateStatistic stat = (AbstractStorelessUnivariateStatistic) object;
+ return Precision.equalsIncludingNaN(stat.getResult(), this.getResult()) &&
+ Precision.equalsIncludingNaN(stat.getN(), this.getN());
+ }
+
+ /**
+ * Returns hash code based on getResult() and getN()
+ *
+ * @return hash code
+ */
+ @Override
+ public int hashCode() {
+ return 31* (31 + MathUtils.hash(getResult())) + MathUtils.hash(getN());
+ }
+
+}
diff --git a/src/main/java/org/apache/commons/math3/stat/descriptive/AbstractUnivariateStatistic.java b/src/main/java/org/apache/commons/math3/stat/descriptive/AbstractUnivariateStatistic.java
new file mode 100644
index 0000000..9abe45a
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/stat/descriptive/AbstractUnivariateStatistic.java
@@ -0,0 +1,263 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math3.stat.descriptive;
+
+import org.apache.commons.math3.exception.NotPositiveException;
+import org.apache.commons.math3.exception.NullArgumentException;
+import org.apache.commons.math3.exception.NumberIsTooLargeException;
+import org.apache.commons.math3.exception.MathIllegalArgumentException;
+import org.apache.commons.math3.exception.util.LocalizedFormats;
+import org.apache.commons.math3.util.MathArrays;
+
+/**
+ * Abstract base class for all implementations of the
+ * {@link UnivariateStatistic} interface.
+ * <p>
+ * Provides a default implementation of <code>evaluate(double[]),</code>
+ * delegating to <code>evaluate(double[], int, int)</code> in the natural way.
+ * </p>
+ * <p>
+ * Also includes a <code>test</code> method that performs generic parameter
+ * validation for the <code>evaluate</code> methods.</p>
+ *
+ */
+public abstract class AbstractUnivariateStatistic
+ implements UnivariateStatistic {
+
+ /** Stored data. */
+ private double[] storedData;
+
+ /**
+ * Set the data array.
+ * <p>
+ * The stored value is a copy of the parameter array, not the array itself.
+ * </p>
+ * @param values data array to store (may be null to remove stored data)
+ * @see #evaluate()
+ */
+ public void setData(final double[] values) {
+ storedData = (values == null) ? null : values.clone();
+ }
+
+ /**
+ * Get a copy of the stored data array.
+ * @return copy of the stored data array (may be null)
+ */
+ public double[] getData() {
+ return (storedData == null) ? null : storedData.clone();
+ }
+
+ /**
+ * Get a reference to the stored data array.
+ * @return reference to the stored data array (may be null)
+ */
+ protected double[] getDataRef() {
+ return storedData;
+ }
+
+ /**
+ * Set the data array. The input array is copied, not referenced.
+ *
+ * @param values data array to store
+ * @param begin the index of the first element to include
+ * @param length the number of elements to include
+ * @throws MathIllegalArgumentException if values is null or the indices
+ * are not valid
+ * @see #evaluate()
+ */
+ public void setData(final double[] values, final int begin, final int length)
+ throws MathIllegalArgumentException {
+ if (values == null) {
+ throw new NullArgumentException(LocalizedFormats.INPUT_ARRAY);
+ }
+
+ if (begin < 0) {
+ throw new NotPositiveException(LocalizedFormats.START_POSITION, begin);
+ }
+
+ if (length < 0) {
+ throw new NotPositiveException(LocalizedFormats.LENGTH, length);
+ }
+
+ if (begin + length > values.length) {
+ throw new NumberIsTooLargeException(LocalizedFormats.SUBARRAY_ENDS_AFTER_ARRAY_END,
+ begin + length, values.length, true);
+ }
+ storedData = new double[length];
+ System.arraycopy(values, begin, storedData, 0, length);
+ }
+
+ /**
+ * Returns the result of evaluating the statistic over the stored data.
+ * <p>
+ * The stored array is the one which was set by previous calls to {@link #setData(double[])}.
+ * </p>
+ * @return the value of the statistic applied to the stored data
+ * @throws MathIllegalArgumentException if the stored data array is null
+ */
+ public double evaluate() throws MathIllegalArgumentException {
+ return evaluate(storedData);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public double evaluate(final double[] values) throws MathIllegalArgumentException {
+ test(values, 0, 0);
+ return evaluate(values, 0, values.length);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public abstract double evaluate(final double[] values, final int begin, final int length)
+ throws MathIllegalArgumentException;
+
+ /**
+ * {@inheritDoc}
+ */
+ public abstract UnivariateStatistic copy();
+
+ /**
+ * This method is used by <code>evaluate(double[], int, int)</code> methods
+ * to verify that the input parameters designate a subarray of positive length.
+ * <p>
+ * <ul>
+ * <li>returns <code>true</code> iff the parameters designate a subarray of
+ * positive length</li>
+ * <li>throws <code>MathIllegalArgumentException</code> if the array is null or
+ * or the indices are invalid</li>
+ * <li>returns <code>false</li> if the array is non-null, but
+ * <code>length</code> is 0.
+ * </ul></p>
+ *
+ * @param values the input array
+ * @param begin index of the first array element to include
+ * @param length the number of elements to include
+ * @return true if the parameters are valid and designate a subarray of positive length
+ * @throws MathIllegalArgumentException if the indices are invalid or the array is null
+ */
+ protected boolean test(
+ final double[] values,
+ final int begin,
+ final int length) throws MathIllegalArgumentException {
+ return MathArrays.verifyValues(values, begin, length, false);
+ }
+
+ /**
+ * This method is used by <code>evaluate(double[], int, int)</code> methods
+ * to verify that the input parameters designate a subarray of positive length.
+ * <p>
+ * <ul>
+ * <li>returns <code>true</code> iff the parameters designate a subarray of
+ * non-negative length</li>
+ * <li>throws <code>IllegalArgumentException</code> if the array is null or
+ * or the indices are invalid</li>
+ * <li>returns <code>false</li> if the array is non-null, but
+ * <code>length</code> is 0 unless <code>allowEmpty</code> is <code>true</code>
+ * </ul></p>
+ *
+ * @param values the input array
+ * @param begin index of the first array element to include
+ * @param length the number of elements to include
+ * @param allowEmpty if <code>true</code> then zero length arrays are allowed
+ * @return true if the parameters are valid
+ * @throws MathIllegalArgumentException if the indices are invalid or the array is null
+ * @since 3.0
+ */
+ protected boolean test(final double[] values, final int begin,
+ final int length, final boolean allowEmpty) throws MathIllegalArgumentException {
+ return MathArrays.verifyValues(values, begin, length, allowEmpty);
+ }
+
+ /**
+ * This method is used by <code>evaluate(double[], double[], int, int)</code> methods
+ * to verify that the begin and length parameters designate a subarray of positive length
+ * and the weights are all non-negative, non-NaN, finite, and not all zero.
+ * <p>
+ * <ul>
+ * <li>returns <code>true</code> iff the parameters designate a subarray of
+ * positive length and the weights array contains legitimate values.</li>
+ * <li>throws <code>IllegalArgumentException</code> if any of the following are true:
+ * <ul><li>the values array is null</li>
+ * <li>the weights array is null</li>
+ * <li>the weights array does not have the same length as the values array</li>
+ * <li>the weights array contains one or more infinite values</li>
+ * <li>the weights array contains one or more NaN values</li>
+ * <li>the weights array contains negative values</li>
+ * <li>the start and length arguments do not determine a valid array</li></ul>
+ * </li>
+ * <li>returns <code>false</li> if the array is non-null, but
+ * <code>length</code> is 0.
+ * </ul></p>
+ *
+ * @param values the input array
+ * @param weights the weights array
+ * @param begin index of the first array element to include
+ * @param length the number of elements to include
+ * @return true if the parameters are valid and designate a subarray of positive length
+ * @throws MathIllegalArgumentException if the indices are invalid or the array is null
+ * @since 2.1
+ */
+ protected boolean test(
+ final double[] values,
+ final double[] weights,
+ final int begin,
+ final int length) throws MathIllegalArgumentException {
+ return MathArrays.verifyValues(values, weights, begin, length, false);
+ }
+
+ /**
+ * This method is used by <code>evaluate(double[], double[], int, int)</code> methods
+ * to verify that the begin and length parameters designate a subarray of positive length
+ * and the weights are all non-negative, non-NaN, finite, and not all zero.
+ * <p>
+ * <ul>
+ * <li>returns <code>true</code> iff the parameters designate a subarray of
+ * non-negative length and the weights array contains legitimate values.</li>
+ * <li>throws <code>MathIllegalArgumentException</code> if any of the following are true:
+ * <ul><li>the values array is null</li>
+ * <li>the weights array is null</li>
+ * <li>the weights array does not have the same length as the values array</li>
+ * <li>the weights array contains one or more infinite values</li>
+ * <li>the weights array contains one or more NaN values</li>
+ * <li>the weights array contains negative values</li>
+ * <li>the start and length arguments do not determine a valid array</li></ul>
+ * </li>
+ * <li>returns <code>false</li> if the array is non-null, but
+ * <code>length</code> is 0 unless <code>allowEmpty</code> is <code>true</code>.
+ * </ul></p>
+ *
+ * @param values the input array.
+ * @param weights the weights array.
+ * @param begin index of the first array element to include.
+ * @param length the number of elements to include.
+ * @param allowEmpty if {@code true} than allow zero length arrays to pass.
+ * @return {@code true} if the parameters are valid.
+ * @throws NullArgumentException if either of the arrays are null
+ * @throws MathIllegalArgumentException if the array indices are not valid,
+ * the weights array contains NaN, infinite or negative elements, or there
+ * are no positive weights.
+ * @since 3.0
+ */
+ protected boolean test(final double[] values, final double[] weights,
+ final int begin, final int length, final boolean allowEmpty) throws MathIllegalArgumentException {
+
+ return MathArrays.verifyValues(values, weights, begin, length, allowEmpty);
+ }
+}
+
diff --git a/src/main/java/org/apache/commons/math3/stat/descriptive/AggregateSummaryStatistics.java b/src/main/java/org/apache/commons/math3/stat/descriptive/AggregateSummaryStatistics.java
new file mode 100644
index 0000000..6ab3c33
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/stat/descriptive/AggregateSummaryStatistics.java
@@ -0,0 +1,422 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math3.stat.descriptive;
+
+import java.io.Serializable;
+import java.util.Collection;
+import java.util.Iterator;
+
+import org.apache.commons.math3.exception.NullArgumentException;
+
+/**
+ * <p>
+ * An aggregator for {@code SummaryStatistics} from several data sets or
+ * data set partitions. In its simplest usage mode, the client creates an
+ * instance via the zero-argument constructor, then uses
+ * {@link #createContributingStatistics()} to obtain a {@code SummaryStatistics}
+ * for each individual data set / partition. The per-set statistics objects
+ * are used as normal, and at any time the aggregate statistics for all the
+ * contributors can be obtained from this object.
+ * </p><p>
+ * Clients with specialized requirements can use alternative constructors to
+ * control the statistics implementations and initial values used by the
+ * contributing and the internal aggregate {@code SummaryStatistics} objects.
+ * </p><p>
+ * A static {@link #aggregate(Collection)} method is also included that computes
+ * aggregate statistics directly from a Collection of SummaryStatistics instances.
+ * </p><p>
+ * When {@link #createContributingStatistics()} is used to create SummaryStatistics
+ * instances to be aggregated concurrently, the created instances'
+ * {@link SummaryStatistics#addValue(double)} methods must synchronize on the aggregating
+ * instance maintained by this class. In multithreaded environments, if the functionality
+ * provided by {@link #aggregate(Collection)} is adequate, that method should be used
+ * to avoid unnecessary computation and synchronization delays.</p>
+ *
+ * @since 2.0
+ *
+ */
+public class AggregateSummaryStatistics implements StatisticalSummary,
+ Serializable {
+
+
+ /** Serializable version identifier */
+ private static final long serialVersionUID = -8207112444016386906L;
+
+ /**
+ * A SummaryStatistics serving as a prototype for creating SummaryStatistics
+ * contributing to this aggregate
+ */
+ private final SummaryStatistics statisticsPrototype;
+
+ /**
+ * The SummaryStatistics in which aggregate statistics are accumulated.
+ */
+ private final SummaryStatistics statistics;
+
+ /**
+ * Initializes a new AggregateSummaryStatistics with default statistics
+ * implementations.
+ *
+ */
+ public AggregateSummaryStatistics() {
+ // No try-catch or throws NAE because arg is guaranteed non-null
+ this(new SummaryStatistics());
+ }
+
+ /**
+ * Initializes a new AggregateSummaryStatistics with the specified statistics
+ * object as a prototype for contributing statistics and for the internal
+ * aggregate statistics. This provides for customized statistics implementations
+ * to be used by contributing and aggregate statistics.
+ *
+ * @param prototypeStatistics a {@code SummaryStatistics} serving as a
+ * prototype both for the internal aggregate statistics and for
+ * contributing statistics obtained via the
+ * {@code createContributingStatistics()} method. Being a prototype
+ * means that other objects are initialized by copying this object's state.
+ * If {@code null}, a new, default statistics object is used. Any statistic
+ * values in the prototype are propagated to contributing statistics
+ * objects and (once) into these aggregate statistics.
+ * @throws NullArgumentException if prototypeStatistics is null
+ * @see #createContributingStatistics()
+ */
+ public AggregateSummaryStatistics(SummaryStatistics prototypeStatistics) throws NullArgumentException {
+ this(prototypeStatistics,
+ prototypeStatistics == null ? null : new SummaryStatistics(prototypeStatistics));
+ }
+
+ /**
+ * Initializes a new AggregateSummaryStatistics with the specified statistics
+ * object as a prototype for contributing statistics and for the internal
+ * aggregate statistics. This provides for different statistics implementations
+ * to be used by contributing and aggregate statistics and for an initial
+ * state to be supplied for the aggregate statistics.
+ *
+ * @param prototypeStatistics a {@code SummaryStatistics} serving as a
+ * prototype both for the internal aggregate statistics and for
+ * contributing statistics obtained via the
+ * {@code createContributingStatistics()} method. Being a prototype
+ * means that other objects are initialized by copying this object's state.
+ * If {@code null}, a new, default statistics object is used. Any statistic
+ * values in the prototype are propagated to contributing statistics
+ * objects, but not into these aggregate statistics.
+ * @param initialStatistics a {@code SummaryStatistics} to serve as the
+ * internal aggregate statistics object. If {@code null}, a new, default
+ * statistics object is used.
+ * @see #createContributingStatistics()
+ */
+ public AggregateSummaryStatistics(SummaryStatistics prototypeStatistics,
+ SummaryStatistics initialStatistics) {
+ this.statisticsPrototype =
+ (prototypeStatistics == null) ? new SummaryStatistics() : prototypeStatistics;
+ this.statistics =
+ (initialStatistics == null) ? new SummaryStatistics() : initialStatistics;
+ }
+
+ /**
+ * {@inheritDoc}. This version returns the maximum over all the aggregated
+ * data.
+ *
+ * @see StatisticalSummary#getMax()
+ */
+ public double getMax() {
+ synchronized (statistics) {
+ return statistics.getMax();
+ }
+ }
+
+ /**
+ * {@inheritDoc}. This version returns the mean of all the aggregated data.
+ *
+ * @see StatisticalSummary#getMean()
+ */
+ public double getMean() {
+ synchronized (statistics) {
+ return statistics.getMean();
+ }
+ }
+
+ /**
+ * {@inheritDoc}. This version returns the minimum over all the aggregated
+ * data.
+ *
+ * @see StatisticalSummary#getMin()
+ */
+ public double getMin() {
+ synchronized (statistics) {
+ return statistics.getMin();
+ }
+ }
+
+ /**
+ * {@inheritDoc}. This version returns a count of all the aggregated data.
+ *
+ * @see StatisticalSummary#getN()
+ */
+ public long getN() {
+ synchronized (statistics) {
+ return statistics.getN();
+ }
+ }
+
+ /**
+ * {@inheritDoc}. This version returns the standard deviation of all the
+ * aggregated data.
+ *
+ * @see StatisticalSummary#getStandardDeviation()
+ */
+ public double getStandardDeviation() {
+ synchronized (statistics) {
+ return statistics.getStandardDeviation();
+ }
+ }
+
+ /**
+ * {@inheritDoc}. This version returns a sum of all the aggregated data.
+ *
+ * @see StatisticalSummary#getSum()
+ */
+ public double getSum() {
+ synchronized (statistics) {
+ return statistics.getSum();
+ }
+ }
+
+ /**
+ * {@inheritDoc}. This version returns the variance of all the aggregated
+ * data.
+ *
+ * @see StatisticalSummary#getVariance()
+ */
+ public double getVariance() {
+ synchronized (statistics) {
+ return statistics.getVariance();
+ }
+ }
+
+ /**
+ * Returns the sum of the logs of all the aggregated data.
+ *
+ * @return the sum of logs
+ * @see SummaryStatistics#getSumOfLogs()
+ */
+ public double getSumOfLogs() {
+ synchronized (statistics) {
+ return statistics.getSumOfLogs();
+ }
+ }
+
+ /**
+ * Returns the geometric mean of all the aggregated data.
+ *
+ * @return the geometric mean
+ * @see SummaryStatistics#getGeometricMean()
+ */
+ public double getGeometricMean() {
+ synchronized (statistics) {
+ return statistics.getGeometricMean();
+ }
+ }
+
+ /**
+ * Returns the sum of the squares of all the aggregated data.
+ *
+ * @return The sum of squares
+ * @see SummaryStatistics#getSumsq()
+ */
+ public double getSumsq() {
+ synchronized (statistics) {
+ return statistics.getSumsq();
+ }
+ }
+
+ /**
+ * Returns a statistic related to the Second Central Moment. Specifically,
+ * what is returned is the sum of squared deviations from the sample mean
+ * among the all of the aggregated data.
+ *
+ * @return second central moment statistic
+ * @see SummaryStatistics#getSecondMoment()
+ */
+ public double getSecondMoment() {
+ synchronized (statistics) {
+ return statistics.getSecondMoment();
+ }
+ }
+
+ /**
+ * Return a {@link StatisticalSummaryValues} instance reporting current
+ * aggregate statistics.
+ *
+ * @return Current values of aggregate statistics
+ */
+ public StatisticalSummary getSummary() {
+ synchronized (statistics) {
+ return new StatisticalSummaryValues(getMean(), getVariance(), getN(),
+ getMax(), getMin(), getSum());
+ }
+ }
+
+ /**
+ * Creates and returns a {@code SummaryStatistics} whose data will be
+ * aggregated with those of this {@code AggregateSummaryStatistics}.
+ *
+ * @return a {@code SummaryStatistics} whose data will be aggregated with
+ * those of this {@code AggregateSummaryStatistics}. The initial state
+ * is a copy of the configured prototype statistics.
+ */
+ public SummaryStatistics createContributingStatistics() {
+ SummaryStatistics contributingStatistics
+ = new AggregatingSummaryStatistics(statistics);
+
+ // No try - catch or advertising NAE because neither argument will ever be null
+ SummaryStatistics.copy(statisticsPrototype, contributingStatistics);
+
+ return contributingStatistics;
+ }
+
+ /**
+ * Computes aggregate summary statistics. This method can be used to combine statistics
+ * computed over partitions or subsamples - i.e., the StatisticalSummaryValues returned
+ * should contain the same values that would have been obtained by computing a single
+ * StatisticalSummary over the combined dataset.
+ * <p>
+ * Returns null if the collection is empty or null.
+ * </p>
+ *
+ * @param statistics collection of SummaryStatistics to aggregate
+ * @return summary statistics for the combined dataset
+ */
+ public static StatisticalSummaryValues aggregate(Collection<? extends StatisticalSummary> statistics) {
+ if (statistics == null) {
+ return null;
+ }
+ Iterator<? extends StatisticalSummary> iterator = statistics.iterator();
+ if (!iterator.hasNext()) {
+ return null;
+ }
+ StatisticalSummary current = iterator.next();
+ long n = current.getN();
+ double min = current.getMin();
+ double sum = current.getSum();
+ double max = current.getMax();
+ double var = current.getVariance();
+ double m2 = var * (n - 1d);
+ double mean = current.getMean();
+ while (iterator.hasNext()) {
+ current = iterator.next();
+ if (current.getMin() < min || Double.isNaN(min)) {
+ min = current.getMin();
+ }
+ if (current.getMax() > max || Double.isNaN(max)) {
+ max = current.getMax();
+ }
+ sum += current.getSum();
+ final double oldN = n;
+ final double curN = current.getN();
+ n += curN;
+ final double meanDiff = current.getMean() - mean;
+ mean = sum / n;
+ final double curM2 = current.getVariance() * (curN - 1d);
+ m2 = m2 + curM2 + meanDiff * meanDiff * oldN * curN / n;
+ }
+ final double variance;
+ if (n == 0) {
+ variance = Double.NaN;
+ } else if (n == 1) {
+ variance = 0d;
+ } else {
+ variance = m2 / (n - 1);
+ }
+ return new StatisticalSummaryValues(mean, variance, n, max, min, sum);
+ }
+
+ /**
+ * A SummaryStatistics that also forwards all values added to it to a second
+ * {@code SummaryStatistics} for aggregation.
+ *
+ * @since 2.0
+ */
+ private static class AggregatingSummaryStatistics extends SummaryStatistics {
+
+ /**
+ * The serialization version of this class
+ */
+ private static final long serialVersionUID = 1L;
+
+ /**
+ * An additional SummaryStatistics into which values added to these
+ * statistics (and possibly others) are aggregated
+ */
+ private final SummaryStatistics aggregateStatistics;
+
+ /**
+ * Initializes a new AggregatingSummaryStatistics with the specified
+ * aggregate statistics object
+ *
+ * @param aggregateStatistics a {@code SummaryStatistics} into which
+ * values added to this statistics object should be aggregated
+ */
+ AggregatingSummaryStatistics(SummaryStatistics aggregateStatistics) {
+ this.aggregateStatistics = aggregateStatistics;
+ }
+
+ /**
+ * {@inheritDoc}. This version adds the provided value to the configured
+ * aggregate after adding it to these statistics.
+ *
+ * @see SummaryStatistics#addValue(double)
+ */
+ @Override
+ public void addValue(double value) {
+ super.addValue(value);
+ synchronized (aggregateStatistics) {
+ aggregateStatistics.addValue(value);
+ }
+ }
+
+ /**
+ * Returns true iff <code>object</code> is a
+ * <code>SummaryStatistics</code> instance and all statistics have the
+ * same values as this.
+ * @param object the object to test equality against.
+ * @return true if object equals this
+ */
+ @Override
+ public boolean equals(Object object) {
+ if (object == this) {
+ return true;
+ }
+ if (object instanceof AggregatingSummaryStatistics == false) {
+ return false;
+ }
+ AggregatingSummaryStatistics stat = (AggregatingSummaryStatistics)object;
+ return super.equals(stat) &&
+ aggregateStatistics.equals(stat.aggregateStatistics);
+ }
+
+ /**
+ * Returns hash code based on values of statistics
+ * @return hash code
+ */
+ @Override
+ public int hashCode() {
+ return 123 + super.hashCode() + aggregateStatistics.hashCode();
+ }
+ }
+}
diff --git a/src/main/java/org/apache/commons/math3/stat/descriptive/DescriptiveStatistics.java b/src/main/java/org/apache/commons/math3/stat/descriptive/DescriptiveStatistics.java
new file mode 100644
index 0000000..b215bc8
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/stat/descriptive/DescriptiveStatistics.java
@@ -0,0 +1,777 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math3.stat.descriptive;
+
+import java.io.Serializable;
+import java.lang.reflect.InvocationTargetException;
+import java.util.Arrays;
+
+import org.apache.commons.math3.exception.MathIllegalArgumentException;
+import org.apache.commons.math3.exception.NullArgumentException;
+import org.apache.commons.math3.exception.MathIllegalStateException;
+import org.apache.commons.math3.exception.util.LocalizedFormats;
+import org.apache.commons.math3.stat.descriptive.moment.GeometricMean;
+import org.apache.commons.math3.stat.descriptive.moment.Kurtosis;
+import org.apache.commons.math3.stat.descriptive.moment.Mean;
+import org.apache.commons.math3.stat.descriptive.moment.Skewness;
+import org.apache.commons.math3.stat.descriptive.moment.Variance;
+import org.apache.commons.math3.stat.descriptive.rank.Max;
+import org.apache.commons.math3.stat.descriptive.rank.Min;
+import org.apache.commons.math3.stat.descriptive.rank.Percentile;
+import org.apache.commons.math3.stat.descriptive.summary.Sum;
+import org.apache.commons.math3.stat.descriptive.summary.SumOfSquares;
+import org.apache.commons.math3.util.MathUtils;
+import org.apache.commons.math3.util.ResizableDoubleArray;
+import org.apache.commons.math3.util.FastMath;
+
+
+/**
+ * Maintains a dataset of values of a single variable and computes descriptive
+ * statistics based on stored data. The {@link #getWindowSize() windowSize}
+ * property sets a limit on the number of values that can be stored in the
+ * dataset. The default value, INFINITE_WINDOW, puts no limit on the size of
+ * the dataset. This value should be used with caution, as the backing store
+ * will grow without bound in this case. For very large datasets,
+ * {@link SummaryStatistics}, which does not store the dataset, should be used
+ * instead of this class. If <code>windowSize</code> is not INFINITE_WINDOW and
+ * more values are added than can be stored in the dataset, new values are
+ * added in a "rolling" manner, with new values replacing the "oldest" values
+ * in the dataset.
+ *
+ * <p>Note: this class is not threadsafe. Use
+ * {@link SynchronizedDescriptiveStatistics} if concurrent access from multiple
+ * threads is required.</p>
+ *
+ */
+public class DescriptiveStatistics implements StatisticalSummary, Serializable {
+
+ /**
+ * Represents an infinite window size. When the {@link #getWindowSize()}
+ * returns this value, there is no limit to the number of data values
+ * that can be stored in the dataset.
+ */
+ public static final int INFINITE_WINDOW = -1;
+
+ /** Serialization UID */
+ private static final long serialVersionUID = 4133067267405273064L;
+
+ /** Name of the setQuantile method. */
+ private static final String SET_QUANTILE_METHOD_NAME = "setQuantile";
+
+ /** hold the window size **/
+ protected int windowSize = INFINITE_WINDOW;
+
+ /**
+ * Stored data values
+ */
+ private ResizableDoubleArray eDA = new ResizableDoubleArray();
+
+ /** Mean statistic implementation - can be reset by setter. */
+ private UnivariateStatistic meanImpl = new Mean();
+
+ /** Geometric mean statistic implementation - can be reset by setter. */
+ private UnivariateStatistic geometricMeanImpl = new GeometricMean();
+
+ /** Kurtosis statistic implementation - can be reset by setter. */
+ private UnivariateStatistic kurtosisImpl = new Kurtosis();
+
+ /** Maximum statistic implementation - can be reset by setter. */
+ private UnivariateStatistic maxImpl = new Max();
+
+ /** Minimum statistic implementation - can be reset by setter. */
+ private UnivariateStatistic minImpl = new Min();
+
+ /** Percentile statistic implementation - can be reset by setter. */
+ private UnivariateStatistic percentileImpl = new Percentile();
+
+ /** Skewness statistic implementation - can be reset by setter. */
+ private UnivariateStatistic skewnessImpl = new Skewness();
+
+ /** Variance statistic implementation - can be reset by setter. */
+ private UnivariateStatistic varianceImpl = new Variance();
+
+ /** Sum of squares statistic implementation - can be reset by setter. */
+ private UnivariateStatistic sumsqImpl = new SumOfSquares();
+
+ /** Sum statistic implementation - can be reset by setter. */
+ private UnivariateStatistic sumImpl = new Sum();
+
+ /**
+ * Construct a DescriptiveStatistics instance with an infinite window
+ */
+ public DescriptiveStatistics() {
+ }
+
+ /**
+ * Construct a DescriptiveStatistics instance with the specified window
+ *
+ * @param window the window size.
+ * @throws MathIllegalArgumentException if window size is less than 1 but
+ * not equal to {@link #INFINITE_WINDOW}
+ */
+ public DescriptiveStatistics(int window) throws MathIllegalArgumentException {
+ setWindowSize(window);
+ }
+
+ /**
+ * Construct a DescriptiveStatistics instance with an infinite window
+ * and the initial data values in double[] initialDoubleArray.
+ * If initialDoubleArray is null, then this constructor corresponds to
+ * DescriptiveStatistics()
+ *
+ * @param initialDoubleArray the initial double[].
+ */
+ public DescriptiveStatistics(double[] initialDoubleArray) {
+ if (initialDoubleArray != null) {
+ eDA = new ResizableDoubleArray(initialDoubleArray);
+ }
+ }
+
+ /**
+ * Copy constructor. Construct a new DescriptiveStatistics instance that
+ * is a copy of original.
+ *
+ * @param original DescriptiveStatistics instance to copy
+ * @throws NullArgumentException if original is null
+ */
+ public DescriptiveStatistics(DescriptiveStatistics original) throws NullArgumentException {
+ copy(original, this);
+ }
+
+ /**
+ * Adds the value to the dataset. If the dataset is at the maximum size
+ * (i.e., the number of stored elements equals the currently configured
+ * windowSize), the first (oldest) element in the dataset is discarded
+ * to make room for the new value.
+ *
+ * @param v the value to be added
+ */
+ public void addValue(double v) {
+ if (windowSize != INFINITE_WINDOW) {
+ if (getN() == windowSize) {
+ eDA.addElementRolling(v);
+ } else if (getN() < windowSize) {
+ eDA.addElement(v);
+ }
+ } else {
+ eDA.addElement(v);
+ }
+ }
+
+ /**
+ * Removes the most recent value from the dataset.
+ *
+ * @throws MathIllegalStateException if there are no elements stored
+ */
+ public void removeMostRecentValue() throws MathIllegalStateException {
+ try {
+ eDA.discardMostRecentElements(1);
+ } catch (MathIllegalArgumentException ex) {
+ throw new MathIllegalStateException(LocalizedFormats.NO_DATA);
+ }
+ }
+
+ /**
+ * Replaces the most recently stored value with the given value.
+ * There must be at least one element stored to call this method.
+ *
+ * @param v the value to replace the most recent stored value
+ * @return replaced value
+ * @throws MathIllegalStateException if there are no elements stored
+ */
+ public double replaceMostRecentValue(double v) throws MathIllegalStateException {
+ return eDA.substituteMostRecentElement(v);
+ }
+
+ /**
+ * Returns the <a href="http://www.xycoon.com/arithmetic_mean.htm">
+ * arithmetic mean </a> of the available values
+ * @return The mean or Double.NaN if no values have been added.
+ */
+ public double getMean() {
+ return apply(meanImpl);
+ }
+
+ /**
+ * Returns the <a href="http://www.xycoon.com/geometric_mean.htm">
+ * geometric mean </a> of the available values.
+ * <p>
+ * See {@link GeometricMean} for details on the computing algorithm.</p>
+ *
+ * @return The geometricMean, Double.NaN if no values have been added,
+ * or if any negative values have been added.
+ */
+ public double getGeometricMean() {
+ return apply(geometricMeanImpl);
+ }
+
+ /**
+ * Returns the (sample) variance of the available values.
+ *
+ * <p>This method returns the bias-corrected sample variance (using {@code n - 1} in
+ * the denominator). Use {@link #getPopulationVariance()} for the non-bias-corrected
+ * population variance.</p>
+ *
+ * @return The variance, Double.NaN if no values have been added
+ * or 0.0 for a single value set.
+ */
+ public double getVariance() {
+ return apply(varianceImpl);
+ }
+
+ /**
+ * Returns the <a href="http://en.wikibooks.org/wiki/Statistics/Summary/Variance">
+ * population variance</a> of the available values.
+ *
+ * @return The population variance, Double.NaN if no values have been added,
+ * or 0.0 for a single value set.
+ */
+ public double getPopulationVariance() {
+ return apply(new Variance(false));
+ }
+
+ /**
+ * Returns the standard deviation of the available values.
+ * @return The standard deviation, Double.NaN if no values have been added
+ * or 0.0 for a single value set.
+ */
+ public double getStandardDeviation() {
+ double stdDev = Double.NaN;
+ if (getN() > 0) {
+ if (getN() > 1) {
+ stdDev = FastMath.sqrt(getVariance());
+ } else {
+ stdDev = 0.0;
+ }
+ }
+ return stdDev;
+ }
+
+ /**
+ * Returns the quadratic mean, a.k.a.
+ * <a href="http://mathworld.wolfram.com/Root-Mean-Square.html">
+ * root-mean-square</a> of the available values
+ * @return The quadratic mean or {@code Double.NaN} if no values
+ * have been added.
+ */
+ public double getQuadraticMean() {
+ final long n = getN();
+ return n > 0 ? FastMath.sqrt(getSumsq() / n) : Double.NaN;
+ }
+
+ /**
+ * Returns the skewness of the available values. Skewness is a
+ * measure of the asymmetry of a given distribution.
+ *
+ * @return The skewness, Double.NaN if less than 3 values have been added.
+ */
+ public double getSkewness() {
+ return apply(skewnessImpl);
+ }
+
+ /**
+ * Returns the Kurtosis of the available values. Kurtosis is a
+ * measure of the "peakedness" of a distribution.
+ *
+ * @return The kurtosis, Double.NaN if less than 4 values have been added.
+ */
+ public double getKurtosis() {
+ return apply(kurtosisImpl);
+ }
+
+ /**
+ * Returns the maximum of the available values
+ * @return The max or Double.NaN if no values have been added.
+ */
+ public double getMax() {
+ return apply(maxImpl);
+ }
+
+ /**
+ * Returns the minimum of the available values
+ * @return The min or Double.NaN if no values have been added.
+ */
+ public double getMin() {
+ return apply(minImpl);
+ }
+
+ /**
+ * Returns the number of available values
+ * @return The number of available values
+ */
+ public long getN() {
+ return eDA.getNumElements();
+ }
+
+ /**
+ * Returns the sum of the values that have been added to Univariate.
+ * @return The sum or Double.NaN if no values have been added
+ */
+ public double getSum() {
+ return apply(sumImpl);
+ }
+
+ /**
+ * Returns the sum of the squares of the available values.
+ * @return The sum of the squares or Double.NaN if no
+ * values have been added.
+ */
+ public double getSumsq() {
+ return apply(sumsqImpl);
+ }
+
+ /**
+ * Resets all statistics and storage
+ */
+ public void clear() {
+ eDA.clear();
+ }
+
+
+ /**
+ * Returns the maximum number of values that can be stored in the
+ * dataset, or INFINITE_WINDOW (-1) if there is no limit.
+ *
+ * @return The current window size or -1 if its Infinite.
+ */
+ public int getWindowSize() {
+ return windowSize;
+ }
+
+ /**
+ * WindowSize controls the number of values that contribute to the
+ * reported statistics. For example, if windowSize is set to 3 and the
+ * values {1,2,3,4,5} have been added <strong> in that order</strong> then
+ * the <i>available values</i> are {3,4,5} and all reported statistics will
+ * be based on these values. If {@code windowSize} is decreased as a result
+ * of this call and there are more than the new value of elements in the
+ * current dataset, values from the front of the array are discarded to
+ * reduce the dataset to {@code windowSize} elements.
+ *
+ * @param windowSize sets the size of the window.
+ * @throws MathIllegalArgumentException if window size is less than 1 but
+ * not equal to {@link #INFINITE_WINDOW}
+ */
+ public void setWindowSize(int windowSize) throws MathIllegalArgumentException {
+ if (windowSize < 1 && windowSize != INFINITE_WINDOW) {
+ throw new MathIllegalArgumentException(
+ LocalizedFormats.NOT_POSITIVE_WINDOW_SIZE, windowSize);
+ }
+
+ this.windowSize = windowSize;
+
+ // We need to check to see if we need to discard elements
+ // from the front of the array. If the windowSize is less than
+ // the current number of elements.
+ if (windowSize != INFINITE_WINDOW && windowSize < eDA.getNumElements()) {
+ eDA.discardFrontElements(eDA.getNumElements() - windowSize);
+ }
+ }
+
+ /**
+ * Returns the current set of values in an array of double primitives.
+ * The order of addition is preserved. The returned array is a fresh
+ * copy of the underlying data -- i.e., it is not a reference to the
+ * stored data.
+ *
+ * @return returns the current set of numbers in the order in which they
+ * were added to this set
+ */
+ public double[] getValues() {
+ return eDA.getElements();
+ }
+
+ /**
+ * Returns the current set of values in an array of double primitives,
+ * sorted in ascending order. The returned array is a fresh
+ * copy of the underlying data -- i.e., it is not a reference to the
+ * stored data.
+ * @return returns the current set of
+ * numbers sorted in ascending order
+ */
+ public double[] getSortedValues() {
+ double[] sort = getValues();
+ Arrays.sort(sort);
+ return sort;
+ }
+
+ /**
+ * Returns the element at the specified index
+ * @param index The Index of the element
+ * @return return the element at the specified index
+ */
+ public double getElement(int index) {
+ return eDA.getElement(index);
+ }
+
+ /**
+ * Returns an estimate for the pth percentile of the stored values.
+ * <p>
+ * The implementation provided here follows the first estimation procedure presented
+ * <a href="http://www.itl.nist.gov/div898/handbook/prc/section2/prc252.htm">here.</a>
+ * </p><p>
+ * <strong>Preconditions</strong>:<ul>
+ * <li><code>0 &lt; p &le; 100</code> (otherwise an
+ * <code>MathIllegalArgumentException</code> is thrown)</li>
+ * <li>at least one value must be stored (returns <code>Double.NaN
+ * </code> otherwise)</li>
+ * </ul></p>
+ *
+ * @param p the requested percentile (scaled from 0 - 100)
+ * @return An estimate for the pth percentile of the stored data
+ * @throws MathIllegalStateException if percentile implementation has been
+ * overridden and the supplied implementation does not support setQuantile
+ * @throws MathIllegalArgumentException if p is not a valid quantile
+ */
+ public double getPercentile(double p) throws MathIllegalStateException, MathIllegalArgumentException {
+ if (percentileImpl instanceof Percentile) {
+ ((Percentile) percentileImpl).setQuantile(p);
+ } else {
+ try {
+ percentileImpl.getClass().getMethod(SET_QUANTILE_METHOD_NAME,
+ new Class[] {Double.TYPE}).invoke(percentileImpl,
+ new Object[] {Double.valueOf(p)});
+ } catch (NoSuchMethodException e1) { // Setter guard should prevent
+ throw new MathIllegalStateException(
+ LocalizedFormats.PERCENTILE_IMPLEMENTATION_UNSUPPORTED_METHOD,
+ percentileImpl.getClass().getName(), SET_QUANTILE_METHOD_NAME);
+ } catch (IllegalAccessException e2) {
+ throw new MathIllegalStateException(
+ LocalizedFormats.PERCENTILE_IMPLEMENTATION_CANNOT_ACCESS_METHOD,
+ SET_QUANTILE_METHOD_NAME, percentileImpl.getClass().getName());
+ } catch (InvocationTargetException e3) {
+ throw new IllegalStateException(e3.getCause());
+ }
+ }
+ return apply(percentileImpl);
+ }
+
+ /**
+ * Generates a text report displaying univariate statistics from values
+ * that have been added. Each statistic is displayed on a separate
+ * line.
+ *
+ * @return String with line feeds displaying statistics
+ */
+ @Override
+ public String toString() {
+ StringBuilder outBuffer = new StringBuilder();
+ String endl = "\n";
+ outBuffer.append("DescriptiveStatistics:").append(endl);
+ outBuffer.append("n: ").append(getN()).append(endl);
+ outBuffer.append("min: ").append(getMin()).append(endl);
+ outBuffer.append("max: ").append(getMax()).append(endl);
+ outBuffer.append("mean: ").append(getMean()).append(endl);
+ outBuffer.append("std dev: ").append(getStandardDeviation())
+ .append(endl);
+ try {
+ // No catch for MIAE because actual parameter is valid below
+ outBuffer.append("median: ").append(getPercentile(50)).append(endl);
+ } catch (MathIllegalStateException ex) {
+ outBuffer.append("median: unavailable").append(endl);
+ }
+ outBuffer.append("skewness: ").append(getSkewness()).append(endl);
+ outBuffer.append("kurtosis: ").append(getKurtosis()).append(endl);
+ return outBuffer.toString();
+ }
+
+ /**
+ * Apply the given statistic to the data associated with this set of statistics.
+ * @param stat the statistic to apply
+ * @return the computed value of the statistic.
+ */
+ public double apply(UnivariateStatistic stat) {
+ // No try-catch or advertised exception here because arguments are guaranteed valid
+ return eDA.compute(stat);
+ }
+
+ // Implementation getters and setter
+
+ /**
+ * Returns the currently configured mean implementation.
+ *
+ * @return the UnivariateStatistic implementing the mean
+ * @since 1.2
+ */
+ public synchronized UnivariateStatistic getMeanImpl() {
+ return meanImpl;
+ }
+
+ /**
+ * <p>Sets the implementation for the mean.</p>
+ *
+ * @param meanImpl the UnivariateStatistic instance to use
+ * for computing the mean
+ * @since 1.2
+ */
+ public synchronized void setMeanImpl(UnivariateStatistic meanImpl) {
+ this.meanImpl = meanImpl;
+ }
+
+ /**
+ * Returns the currently configured geometric mean implementation.
+ *
+ * @return the UnivariateStatistic implementing the geometric mean
+ * @since 1.2
+ */
+ public synchronized UnivariateStatistic getGeometricMeanImpl() {
+ return geometricMeanImpl;
+ }
+
+ /**
+ * <p>Sets the implementation for the gemoetric mean.</p>
+ *
+ * @param geometricMeanImpl the UnivariateStatistic instance to use
+ * for computing the geometric mean
+ * @since 1.2
+ */
+ public synchronized void setGeometricMeanImpl(
+ UnivariateStatistic geometricMeanImpl) {
+ this.geometricMeanImpl = geometricMeanImpl;
+ }
+
+ /**
+ * Returns the currently configured kurtosis implementation.
+ *
+ * @return the UnivariateStatistic implementing the kurtosis
+ * @since 1.2
+ */
+ public synchronized UnivariateStatistic getKurtosisImpl() {
+ return kurtosisImpl;
+ }
+
+ /**
+ * <p>Sets the implementation for the kurtosis.</p>
+ *
+ * @param kurtosisImpl the UnivariateStatistic instance to use
+ * for computing the kurtosis
+ * @since 1.2
+ */
+ public synchronized void setKurtosisImpl(UnivariateStatistic kurtosisImpl) {
+ this.kurtosisImpl = kurtosisImpl;
+ }
+
+ /**
+ * Returns the currently configured maximum implementation.
+ *
+ * @return the UnivariateStatistic implementing the maximum
+ * @since 1.2
+ */
+ public synchronized UnivariateStatistic getMaxImpl() {
+ return maxImpl;
+ }
+
+ /**
+ * <p>Sets the implementation for the maximum.</p>
+ *
+ * @param maxImpl the UnivariateStatistic instance to use
+ * for computing the maximum
+ * @since 1.2
+ */
+ public synchronized void setMaxImpl(UnivariateStatistic maxImpl) {
+ this.maxImpl = maxImpl;
+ }
+
+ /**
+ * Returns the currently configured minimum implementation.
+ *
+ * @return the UnivariateStatistic implementing the minimum
+ * @since 1.2
+ */
+ public synchronized UnivariateStatistic getMinImpl() {
+ return minImpl;
+ }
+
+ /**
+ * <p>Sets the implementation for the minimum.</p>
+ *
+ * @param minImpl the UnivariateStatistic instance to use
+ * for computing the minimum
+ * @since 1.2
+ */
+ public synchronized void setMinImpl(UnivariateStatistic minImpl) {
+ this.minImpl = minImpl;
+ }
+
+ /**
+ * Returns the currently configured percentile implementation.
+ *
+ * @return the UnivariateStatistic implementing the percentile
+ * @since 1.2
+ */
+ public synchronized UnivariateStatistic getPercentileImpl() {
+ return percentileImpl;
+ }
+
+ /**
+ * Sets the implementation to be used by {@link #getPercentile(double)}.
+ * The supplied <code>UnivariateStatistic</code> must provide a
+ * <code>setQuantile(double)</code> method; otherwise
+ * <code>IllegalArgumentException</code> is thrown.
+ *
+ * @param percentileImpl the percentileImpl to set
+ * @throws MathIllegalArgumentException if the supplied implementation does not
+ * provide a <code>setQuantile</code> method
+ * @since 1.2
+ */
+ public synchronized void setPercentileImpl(UnivariateStatistic percentileImpl)
+ throws MathIllegalArgumentException {
+ try {
+ percentileImpl.getClass().getMethod(SET_QUANTILE_METHOD_NAME,
+ new Class[] {Double.TYPE}).invoke(percentileImpl,
+ new Object[] {Double.valueOf(50.0d)});
+ } catch (NoSuchMethodException e1) {
+ throw new MathIllegalArgumentException(
+ LocalizedFormats.PERCENTILE_IMPLEMENTATION_UNSUPPORTED_METHOD,
+ percentileImpl.getClass().getName(), SET_QUANTILE_METHOD_NAME);
+ } catch (IllegalAccessException e2) {
+ throw new MathIllegalArgumentException(
+ LocalizedFormats.PERCENTILE_IMPLEMENTATION_CANNOT_ACCESS_METHOD,
+ SET_QUANTILE_METHOD_NAME, percentileImpl.getClass().getName());
+ } catch (InvocationTargetException e3) {
+ throw new IllegalArgumentException(e3.getCause());
+ }
+ this.percentileImpl = percentileImpl;
+ }
+
+ /**
+ * Returns the currently configured skewness implementation.
+ *
+ * @return the UnivariateStatistic implementing the skewness
+ * @since 1.2
+ */
+ public synchronized UnivariateStatistic getSkewnessImpl() {
+ return skewnessImpl;
+ }
+
+ /**
+ * <p>Sets the implementation for the skewness.</p>
+ *
+ * @param skewnessImpl the UnivariateStatistic instance to use
+ * for computing the skewness
+ * @since 1.2
+ */
+ public synchronized void setSkewnessImpl(
+ UnivariateStatistic skewnessImpl) {
+ this.skewnessImpl = skewnessImpl;
+ }
+
+ /**
+ * Returns the currently configured variance implementation.
+ *
+ * @return the UnivariateStatistic implementing the variance
+ * @since 1.2
+ */
+ public synchronized UnivariateStatistic getVarianceImpl() {
+ return varianceImpl;
+ }
+
+ /**
+ * <p>Sets the implementation for the variance.</p>
+ *
+ * @param varianceImpl the UnivariateStatistic instance to use
+ * for computing the variance
+ * @since 1.2
+ */
+ public synchronized void setVarianceImpl(
+ UnivariateStatistic varianceImpl) {
+ this.varianceImpl = varianceImpl;
+ }
+
+ /**
+ * Returns the currently configured sum of squares implementation.
+ *
+ * @return the UnivariateStatistic implementing the sum of squares
+ * @since 1.2
+ */
+ public synchronized UnivariateStatistic getSumsqImpl() {
+ return sumsqImpl;
+ }
+
+ /**
+ * <p>Sets the implementation for the sum of squares.</p>
+ *
+ * @param sumsqImpl the UnivariateStatistic instance to use
+ * for computing the sum of squares
+ * @since 1.2
+ */
+ public synchronized void setSumsqImpl(UnivariateStatistic sumsqImpl) {
+ this.sumsqImpl = sumsqImpl;
+ }
+
+ /**
+ * Returns the currently configured sum implementation.
+ *
+ * @return the UnivariateStatistic implementing the sum
+ * @since 1.2
+ */
+ public synchronized UnivariateStatistic getSumImpl() {
+ return sumImpl;
+ }
+
+ /**
+ * <p>Sets the implementation for the sum.</p>
+ *
+ * @param sumImpl the UnivariateStatistic instance to use
+ * for computing the sum
+ * @since 1.2
+ */
+ public synchronized void setSumImpl(UnivariateStatistic sumImpl) {
+ this.sumImpl = sumImpl;
+ }
+
+ /**
+ * Returns a copy of this DescriptiveStatistics instance with the same internal state.
+ *
+ * @return a copy of this
+ */
+ public DescriptiveStatistics copy() {
+ DescriptiveStatistics result = new DescriptiveStatistics();
+ // No try-catch or advertised exception because parms are guaranteed valid
+ copy(this, result);
+ return result;
+ }
+
+ /**
+ * Copies source to dest.
+ * <p>Neither source nor dest can be null.</p>
+ *
+ * @param source DescriptiveStatistics to copy
+ * @param dest DescriptiveStatistics to copy to
+ * @throws NullArgumentException if either source or dest is null
+ */
+ public static void copy(DescriptiveStatistics source, DescriptiveStatistics dest)
+ throws NullArgumentException {
+ MathUtils.checkNotNull(source);
+ MathUtils.checkNotNull(dest);
+ // Copy data and window size
+ dest.eDA = source.eDA.copy();
+ dest.windowSize = source.windowSize;
+
+ // Copy implementations
+ dest.maxImpl = source.maxImpl.copy();
+ dest.meanImpl = source.meanImpl.copy();
+ dest.minImpl = source.minImpl.copy();
+ dest.sumImpl = source.sumImpl.copy();
+ dest.varianceImpl = source.varianceImpl.copy();
+ dest.sumsqImpl = source.sumsqImpl.copy();
+ dest.geometricMeanImpl = source.geometricMeanImpl.copy();
+ dest.kurtosisImpl = source.kurtosisImpl;
+ dest.skewnessImpl = source.skewnessImpl;
+ dest.percentileImpl = source.percentileImpl;
+ }
+}
diff --git a/src/main/java/org/apache/commons/math3/stat/descriptive/MultivariateSummaryStatistics.java b/src/main/java/org/apache/commons/math3/stat/descriptive/MultivariateSummaryStatistics.java
new file mode 100644
index 0000000..3ede26e
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/stat/descriptive/MultivariateSummaryStatistics.java
@@ -0,0 +1,635 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math3.stat.descriptive;
+
+import java.io.Serializable;
+import java.util.Arrays;
+
+import org.apache.commons.math3.exception.util.LocalizedFormats;
+import org.apache.commons.math3.exception.DimensionMismatchException;
+import org.apache.commons.math3.exception.MathIllegalStateException;
+import org.apache.commons.math3.linear.RealMatrix;
+import org.apache.commons.math3.stat.descriptive.moment.GeometricMean;
+import org.apache.commons.math3.stat.descriptive.moment.Mean;
+import org.apache.commons.math3.stat.descriptive.moment.VectorialCovariance;
+import org.apache.commons.math3.stat.descriptive.rank.Max;
+import org.apache.commons.math3.stat.descriptive.rank.Min;
+import org.apache.commons.math3.stat.descriptive.summary.Sum;
+import org.apache.commons.math3.stat.descriptive.summary.SumOfLogs;
+import org.apache.commons.math3.stat.descriptive.summary.SumOfSquares;
+import org.apache.commons.math3.util.MathUtils;
+import org.apache.commons.math3.util.MathArrays;
+import org.apache.commons.math3.util.Precision;
+import org.apache.commons.math3.util.FastMath;
+
+/**
+ * <p>Computes summary statistics for a stream of n-tuples added using the
+ * {@link #addValue(double[]) addValue} method. The data values are not stored
+ * in memory, so this class can be used to compute statistics for very large
+ * n-tuple streams.</p>
+ *
+ * <p>The {@link StorelessUnivariateStatistic} instances used to maintain
+ * summary state and compute statistics are configurable via setters.
+ * For example, the default implementation for the mean can be overridden by
+ * calling {@link #setMeanImpl(StorelessUnivariateStatistic[])}. Actual
+ * parameters to these methods must implement the
+ * {@link StorelessUnivariateStatistic} interface and configuration must be
+ * completed before <code>addValue</code> is called. No configuration is
+ * necessary to use the default, commons-math provided implementations.</p>
+ *
+ * <p>To compute statistics for a stream of n-tuples, construct a
+ * MultivariateStatistics instance with dimension n and then use
+ * {@link #addValue(double[])} to add n-tuples. The <code>getXxx</code>
+ * methods where Xxx is a statistic return an array of <code>double</code>
+ * values, where for <code>i = 0,...,n-1</code> the i<sup>th</sup> array element is the
+ * value of the given statistic for data range consisting of the i<sup>th</sup> element of
+ * each of the input n-tuples. For example, if <code>addValue</code> is called
+ * with actual parameters {0, 1, 2}, then {3, 4, 5} and finally {6, 7, 8},
+ * <code>getSum</code> will return a three-element array with values
+ * {0+3+6, 1+4+7, 2+5+8}</p>
+ *
+ * <p>Note: This class is not thread-safe. Use
+ * {@link SynchronizedMultivariateSummaryStatistics} if concurrent access from multiple
+ * threads is required.</p>
+ *
+ * @since 1.2
+ */
+public class MultivariateSummaryStatistics
+ implements StatisticalMultivariateSummary, Serializable {
+
+ /** Serialization UID */
+ private static final long serialVersionUID = 2271900808994826718L;
+
+ /** Dimension of the data. */
+ private int k;
+
+ /** Count of values that have been added */
+ private long n = 0;
+
+ /** Sum statistic implementation - can be reset by setter. */
+ private StorelessUnivariateStatistic[] sumImpl;
+
+ /** Sum of squares statistic implementation - can be reset by setter. */
+ private StorelessUnivariateStatistic[] sumSqImpl;
+
+ /** Minimum statistic implementation - can be reset by setter. */
+ private StorelessUnivariateStatistic[] minImpl;
+
+ /** Maximum statistic implementation - can be reset by setter. */
+ private StorelessUnivariateStatistic[] maxImpl;
+
+ /** Sum of log statistic implementation - can be reset by setter. */
+ private StorelessUnivariateStatistic[] sumLogImpl;
+
+ /** Geometric mean statistic implementation - can be reset by setter. */
+ private StorelessUnivariateStatistic[] geoMeanImpl;
+
+ /** Mean statistic implementation - can be reset by setter. */
+ private StorelessUnivariateStatistic[] meanImpl;
+
+ /** Covariance statistic implementation - cannot be reset. */
+ private VectorialCovariance covarianceImpl;
+
+ /**
+ * Construct a MultivariateSummaryStatistics instance
+ * @param k dimension of the data
+ * @param isCovarianceBiasCorrected if true, the unbiased sample
+ * covariance is computed, otherwise the biased population covariance
+ * is computed
+ */
+ public MultivariateSummaryStatistics(int k, boolean isCovarianceBiasCorrected) {
+ this.k = k;
+
+ sumImpl = new StorelessUnivariateStatistic[k];
+ sumSqImpl = new StorelessUnivariateStatistic[k];
+ minImpl = new StorelessUnivariateStatistic[k];
+ maxImpl = new StorelessUnivariateStatistic[k];
+ sumLogImpl = new StorelessUnivariateStatistic[k];
+ geoMeanImpl = new StorelessUnivariateStatistic[k];
+ meanImpl = new StorelessUnivariateStatistic[k];
+
+ for (int i = 0; i < k; ++i) {
+ sumImpl[i] = new Sum();
+ sumSqImpl[i] = new SumOfSquares();
+ minImpl[i] = new Min();
+ maxImpl[i] = new Max();
+ sumLogImpl[i] = new SumOfLogs();
+ geoMeanImpl[i] = new GeometricMean();
+ meanImpl[i] = new Mean();
+ }
+
+ covarianceImpl =
+ new VectorialCovariance(k, isCovarianceBiasCorrected);
+
+ }
+
+ /**
+ * Add an n-tuple to the data
+ *
+ * @param value the n-tuple to add
+ * @throws DimensionMismatchException if the length of the array
+ * does not match the one used at construction
+ */
+ public void addValue(double[] value) throws DimensionMismatchException {
+ checkDimension(value.length);
+ for (int i = 0; i < k; ++i) {
+ double v = value[i];
+ sumImpl[i].increment(v);
+ sumSqImpl[i].increment(v);
+ minImpl[i].increment(v);
+ maxImpl[i].increment(v);
+ sumLogImpl[i].increment(v);
+ geoMeanImpl[i].increment(v);
+ meanImpl[i].increment(v);
+ }
+ covarianceImpl.increment(value);
+ n++;
+ }
+
+ /**
+ * Returns the dimension of the data
+ * @return The dimension of the data
+ */
+ public int getDimension() {
+ return k;
+ }
+
+ /**
+ * Returns the number of available values
+ * @return The number of available values
+ */
+ public long getN() {
+ return n;
+ }
+
+ /**
+ * Returns an array of the results of a statistic.
+ * @param stats univariate statistic array
+ * @return results array
+ */
+ private double[] getResults(StorelessUnivariateStatistic[] stats) {
+ double[] results = new double[stats.length];
+ for (int i = 0; i < results.length; ++i) {
+ results[i] = stats[i].getResult();
+ }
+ return results;
+ }
+
+ /**
+ * Returns an array whose i<sup>th</sup> entry is the sum of the
+ * i<sup>th</sup> entries of the arrays that have been added using
+ * {@link #addValue(double[])}
+ *
+ * @return the array of component sums
+ */
+ public double[] getSum() {
+ return getResults(sumImpl);
+ }
+
+ /**
+ * Returns an array whose i<sup>th</sup> entry is the sum of squares of the
+ * i<sup>th</sup> entries of the arrays that have been added using
+ * {@link #addValue(double[])}
+ *
+ * @return the array of component sums of squares
+ */
+ public double[] getSumSq() {
+ return getResults(sumSqImpl);
+ }
+
+ /**
+ * Returns an array whose i<sup>th</sup> entry is the sum of logs of the
+ * i<sup>th</sup> entries of the arrays that have been added using
+ * {@link #addValue(double[])}
+ *
+ * @return the array of component log sums
+ */
+ public double[] getSumLog() {
+ return getResults(sumLogImpl);
+ }
+
+ /**
+ * Returns an array whose i<sup>th</sup> entry is the mean of the
+ * i<sup>th</sup> entries of the arrays that have been added using
+ * {@link #addValue(double[])}
+ *
+ * @return the array of component means
+ */
+ public double[] getMean() {
+ return getResults(meanImpl);
+ }
+
+ /**
+ * Returns an array whose i<sup>th</sup> entry is the standard deviation of the
+ * i<sup>th</sup> entries of the arrays that have been added using
+ * {@link #addValue(double[])}
+ *
+ * @return the array of component standard deviations
+ */
+ public double[] getStandardDeviation() {
+ double[] stdDev = new double[k];
+ if (getN() < 1) {
+ Arrays.fill(stdDev, Double.NaN);
+ } else if (getN() < 2) {
+ Arrays.fill(stdDev, 0.0);
+ } else {
+ RealMatrix matrix = covarianceImpl.getResult();
+ for (int i = 0; i < k; ++i) {
+ stdDev[i] = FastMath.sqrt(matrix.getEntry(i, i));
+ }
+ }
+ return stdDev;
+ }
+
+ /**
+ * Returns the covariance matrix of the values that have been added.
+ *
+ * @return the covariance matrix
+ */
+ public RealMatrix getCovariance() {
+ return covarianceImpl.getResult();
+ }
+
+ /**
+ * Returns an array whose i<sup>th</sup> entry is the maximum of the
+ * i<sup>th</sup> entries of the arrays that have been added using
+ * {@link #addValue(double[])}
+ *
+ * @return the array of component maxima
+ */
+ public double[] getMax() {
+ return getResults(maxImpl);
+ }
+
+ /**
+ * Returns an array whose i<sup>th</sup> entry is the minimum of the
+ * i<sup>th</sup> entries of the arrays that have been added using
+ * {@link #addValue(double[])}
+ *
+ * @return the array of component minima
+ */
+ public double[] getMin() {
+ return getResults(minImpl);
+ }
+
+ /**
+ * Returns an array whose i<sup>th</sup> entry is the geometric mean of the
+ * i<sup>th</sup> entries of the arrays that have been added using
+ * {@link #addValue(double[])}
+ *
+ * @return the array of component geometric means
+ */
+ public double[] getGeometricMean() {
+ return getResults(geoMeanImpl);
+ }
+
+ /**
+ * Generates a text report displaying
+ * summary statistics from values that
+ * have been added.
+ * @return String with line feeds displaying statistics
+ */
+ @Override
+ public String toString() {
+ final String separator = ", ";
+ final String suffix = System.getProperty("line.separator");
+ StringBuilder outBuffer = new StringBuilder();
+ outBuffer.append("MultivariateSummaryStatistics:" + suffix);
+ outBuffer.append("n: " + getN() + suffix);
+ append(outBuffer, getMin(), "min: ", separator, suffix);
+ append(outBuffer, getMax(), "max: ", separator, suffix);
+ append(outBuffer, getMean(), "mean: ", separator, suffix);
+ append(outBuffer, getGeometricMean(), "geometric mean: ", separator, suffix);
+ append(outBuffer, getSumSq(), "sum of squares: ", separator, suffix);
+ append(outBuffer, getSumLog(), "sum of logarithms: ", separator, suffix);
+ append(outBuffer, getStandardDeviation(), "standard deviation: ", separator, suffix);
+ outBuffer.append("covariance: " + getCovariance().toString() + suffix);
+ return outBuffer.toString();
+ }
+
+ /**
+ * Append a text representation of an array to a buffer.
+ * @param buffer buffer to fill
+ * @param data data array
+ * @param prefix text prefix
+ * @param separator elements separator
+ * @param suffix text suffix
+ */
+ private void append(StringBuilder buffer, double[] data,
+ String prefix, String separator, String suffix) {
+ buffer.append(prefix);
+ for (int i = 0; i < data.length; ++i) {
+ if (i > 0) {
+ buffer.append(separator);
+ }
+ buffer.append(data[i]);
+ }
+ buffer.append(suffix);
+ }
+
+ /**
+ * Resets all statistics and storage
+ */
+ public void clear() {
+ this.n = 0;
+ for (int i = 0; i < k; ++i) {
+ minImpl[i].clear();
+ maxImpl[i].clear();
+ sumImpl[i].clear();
+ sumLogImpl[i].clear();
+ sumSqImpl[i].clear();
+ geoMeanImpl[i].clear();
+ meanImpl[i].clear();
+ }
+ covarianceImpl.clear();
+ }
+
+ /**
+ * Returns true iff <code>object</code> is a <code>MultivariateSummaryStatistics</code>
+ * instance and all statistics have the same values as this.
+ * @param object the object to test equality against.
+ * @return true if object equals this
+ */
+ @Override
+ public boolean equals(Object object) {
+ if (object == this ) {
+ return true;
+ }
+ if (object instanceof MultivariateSummaryStatistics == false) {
+ return false;
+ }
+ MultivariateSummaryStatistics stat = (MultivariateSummaryStatistics) object;
+ return MathArrays.equalsIncludingNaN(stat.getGeometricMean(), getGeometricMean()) &&
+ MathArrays.equalsIncludingNaN(stat.getMax(), getMax()) &&
+ MathArrays.equalsIncludingNaN(stat.getMean(), getMean()) &&
+ MathArrays.equalsIncludingNaN(stat.getMin(), getMin()) &&
+ Precision.equalsIncludingNaN(stat.getN(), getN()) &&
+ MathArrays.equalsIncludingNaN(stat.getSum(), getSum()) &&
+ MathArrays.equalsIncludingNaN(stat.getSumSq(), getSumSq()) &&
+ MathArrays.equalsIncludingNaN(stat.getSumLog(), getSumLog()) &&
+ stat.getCovariance().equals( getCovariance());
+ }
+
+ /**
+ * Returns hash code based on values of statistics
+ *
+ * @return hash code
+ */
+ @Override
+ public int hashCode() {
+ int result = 31 + MathUtils.hash(getGeometricMean());
+ result = result * 31 + MathUtils.hash(getGeometricMean());
+ result = result * 31 + MathUtils.hash(getMax());
+ result = result * 31 + MathUtils.hash(getMean());
+ result = result * 31 + MathUtils.hash(getMin());
+ result = result * 31 + MathUtils.hash(getN());
+ result = result * 31 + MathUtils.hash(getSum());
+ result = result * 31 + MathUtils.hash(getSumSq());
+ result = result * 31 + MathUtils.hash(getSumLog());
+ result = result * 31 + getCovariance().hashCode();
+ return result;
+ }
+
+ // Getters and setters for statistics implementations
+ /**
+ * Sets statistics implementations.
+ * @param newImpl new implementations for statistics
+ * @param oldImpl old implementations for statistics
+ * @throws DimensionMismatchException if the array dimension
+ * does not match the one used at construction
+ * @throws MathIllegalStateException if data has already been added
+ * (i.e. if n > 0)
+ */
+ private void setImpl(StorelessUnivariateStatistic[] newImpl,
+ StorelessUnivariateStatistic[] oldImpl) throws MathIllegalStateException,
+ DimensionMismatchException {
+ checkEmpty();
+ checkDimension(newImpl.length);
+ System.arraycopy(newImpl, 0, oldImpl, 0, newImpl.length);
+ }
+
+ /**
+ * Returns the currently configured Sum implementation
+ *
+ * @return the StorelessUnivariateStatistic implementing the sum
+ */
+ public StorelessUnivariateStatistic[] getSumImpl() {
+ return sumImpl.clone();
+ }
+
+ /**
+ * <p>Sets the implementation for the Sum.</p>
+ * <p>This method must be activated before any data has been added - i.e.,
+ * before {@link #addValue(double[]) addValue} has been used to add data;
+ * otherwise an IllegalStateException will be thrown.</p>
+ *
+ * @param sumImpl the StorelessUnivariateStatistic instance to use
+ * for computing the Sum
+ * @throws DimensionMismatchException if the array dimension
+ * does not match the one used at construction
+ * @throws MathIllegalStateException if data has already been added
+ * (i.e if n > 0)
+ */
+ public void setSumImpl(StorelessUnivariateStatistic[] sumImpl)
+ throws MathIllegalStateException, DimensionMismatchException {
+ setImpl(sumImpl, this.sumImpl);
+ }
+
+ /**
+ * Returns the currently configured sum of squares implementation
+ *
+ * @return the StorelessUnivariateStatistic implementing the sum of squares
+ */
+ public StorelessUnivariateStatistic[] getSumsqImpl() {
+ return sumSqImpl.clone();
+ }
+
+ /**
+ * <p>Sets the implementation for the sum of squares.</p>
+ * <p>This method must be activated before any data has been added - i.e.,
+ * before {@link #addValue(double[]) addValue} has been used to add data;
+ * otherwise an IllegalStateException will be thrown.</p>
+ *
+ * @param sumsqImpl the StorelessUnivariateStatistic instance to use
+ * for computing the sum of squares
+ * @throws DimensionMismatchException if the array dimension
+ * does not match the one used at construction
+ * @throws MathIllegalStateException if data has already been added
+ * (i.e if n > 0)
+ */
+ public void setSumsqImpl(StorelessUnivariateStatistic[] sumsqImpl)
+ throws MathIllegalStateException, DimensionMismatchException {
+ setImpl(sumsqImpl, this.sumSqImpl);
+ }
+
+ /**
+ * Returns the currently configured minimum implementation
+ *
+ * @return the StorelessUnivariateStatistic implementing the minimum
+ */
+ public StorelessUnivariateStatistic[] getMinImpl() {
+ return minImpl.clone();
+ }
+
+ /**
+ * <p>Sets the implementation for the minimum.</p>
+ * <p>This method must be activated before any data has been added - i.e.,
+ * before {@link #addValue(double[]) addValue} has been used to add data;
+ * otherwise an IllegalStateException will be thrown.</p>
+ *
+ * @param minImpl the StorelessUnivariateStatistic instance to use
+ * for computing the minimum
+ * @throws DimensionMismatchException if the array dimension
+ * does not match the one used at construction
+ * @throws MathIllegalStateException if data has already been added
+ * (i.e if n > 0)
+ */
+ public void setMinImpl(StorelessUnivariateStatistic[] minImpl)
+ throws MathIllegalStateException, DimensionMismatchException {
+ setImpl(minImpl, this.minImpl);
+ }
+
+ /**
+ * Returns the currently configured maximum implementation
+ *
+ * @return the StorelessUnivariateStatistic implementing the maximum
+ */
+ public StorelessUnivariateStatistic[] getMaxImpl() {
+ return maxImpl.clone();
+ }
+
+ /**
+ * <p>Sets the implementation for the maximum.</p>
+ * <p>This method must be activated before any data has been added - i.e.,
+ * before {@link #addValue(double[]) addValue} has been used to add data;
+ * otherwise an IllegalStateException will be thrown.</p>
+ *
+ * @param maxImpl the StorelessUnivariateStatistic instance to use
+ * for computing the maximum
+ * @throws DimensionMismatchException if the array dimension
+ * does not match the one used at construction
+ * @throws MathIllegalStateException if data has already been added
+ * (i.e if n > 0)
+ */
+ public void setMaxImpl(StorelessUnivariateStatistic[] maxImpl)
+ throws MathIllegalStateException, DimensionMismatchException{
+ setImpl(maxImpl, this.maxImpl);
+ }
+
+ /**
+ * Returns the currently configured sum of logs implementation
+ *
+ * @return the StorelessUnivariateStatistic implementing the log sum
+ */
+ public StorelessUnivariateStatistic[] getSumLogImpl() {
+ return sumLogImpl.clone();
+ }
+
+ /**
+ * <p>Sets the implementation for the sum of logs.</p>
+ * <p>This method must be activated before any data has been added - i.e.,
+ * before {@link #addValue(double[]) addValue} has been used to add data;
+ * otherwise an IllegalStateException will be thrown.</p>
+ *
+ * @param sumLogImpl the StorelessUnivariateStatistic instance to use
+ * for computing the log sum
+ * @throws DimensionMismatchException if the array dimension
+ * does not match the one used at construction
+ * @throws MathIllegalStateException if data has already been added
+ * (i.e if n > 0)
+ */
+ public void setSumLogImpl(StorelessUnivariateStatistic[] sumLogImpl)
+ throws MathIllegalStateException, DimensionMismatchException{
+ setImpl(sumLogImpl, this.sumLogImpl);
+ }
+
+ /**
+ * Returns the currently configured geometric mean implementation
+ *
+ * @return the StorelessUnivariateStatistic implementing the geometric mean
+ */
+ public StorelessUnivariateStatistic[] getGeoMeanImpl() {
+ return geoMeanImpl.clone();
+ }
+
+ /**
+ * <p>Sets the implementation for the geometric mean.</p>
+ * <p>This method must be activated before any data has been added - i.e.,
+ * before {@link #addValue(double[]) addValue} has been used to add data;
+ * otherwise an IllegalStateException will be thrown.</p>
+ *
+ * @param geoMeanImpl the StorelessUnivariateStatistic instance to use
+ * for computing the geometric mean
+ * @throws DimensionMismatchException if the array dimension
+ * does not match the one used at construction
+ * @throws MathIllegalStateException if data has already been added
+ * (i.e if n > 0)
+ */
+ public void setGeoMeanImpl(StorelessUnivariateStatistic[] geoMeanImpl)
+ throws MathIllegalStateException, DimensionMismatchException {
+ setImpl(geoMeanImpl, this.geoMeanImpl);
+ }
+
+ /**
+ * Returns the currently configured mean implementation
+ *
+ * @return the StorelessUnivariateStatistic implementing the mean
+ */
+ public StorelessUnivariateStatistic[] getMeanImpl() {
+ return meanImpl.clone();
+ }
+
+ /**
+ * <p>Sets the implementation for the mean.</p>
+ * <p>This method must be activated before any data has been added - i.e.,
+ * before {@link #addValue(double[]) addValue} has been used to add data;
+ * otherwise an IllegalStateException will be thrown.</p>
+ *
+ * @param meanImpl the StorelessUnivariateStatistic instance to use
+ * for computing the mean
+ * @throws DimensionMismatchException if the array dimension
+ * does not match the one used at construction
+ * @throws MathIllegalStateException if data has already been added
+ * (i.e if n > 0)
+ */
+ public void setMeanImpl(StorelessUnivariateStatistic[] meanImpl)
+ throws MathIllegalStateException, DimensionMismatchException{
+ setImpl(meanImpl, this.meanImpl);
+ }
+
+ /**
+ * Throws MathIllegalStateException if the statistic is not empty.
+ * @throws MathIllegalStateException if n > 0.
+ */
+ private void checkEmpty() throws MathIllegalStateException {
+ if (n > 0) {
+ throw new MathIllegalStateException(
+ LocalizedFormats.VALUES_ADDED_BEFORE_CONFIGURING_STATISTIC, n);
+ }
+ }
+
+ /**
+ * Throws DimensionMismatchException if dimension != k.
+ * @param dimension dimension to check
+ * @throws DimensionMismatchException if dimension != k
+ */
+ private void checkDimension(int dimension) throws DimensionMismatchException {
+ if (dimension != k) {
+ throw new DimensionMismatchException(dimension, k);
+ }
+ }
+}
diff --git a/src/main/java/org/apache/commons/math3/stat/descriptive/StatisticalMultivariateSummary.java b/src/main/java/org/apache/commons/math3/stat/descriptive/StatisticalMultivariateSummary.java
new file mode 100644
index 0000000..bfe4deb
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/stat/descriptive/StatisticalMultivariateSummary.java
@@ -0,0 +1,119 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math3.stat.descriptive;
+
+import org.apache.commons.math3.linear.RealMatrix;
+
+/**
+ * Reporting interface for basic multivariate statistics.
+ *
+ * @since 1.2
+ */
+public interface StatisticalMultivariateSummary {
+
+ /**
+ * Returns the dimension of the data
+ * @return The dimension of the data
+ */
+ int getDimension();
+
+ /**
+ * Returns an array whose i<sup>th</sup> entry is the
+ * mean of the i<sup>th</sup> entries of the arrays
+ * that correspond to each multivariate sample
+ *
+ * @return the array of component means
+ */
+ double[] getMean();
+
+ /**
+ * Returns the covariance of the available values.
+ * @return The covariance, null if no multivariate sample
+ * have been added or a zeroed matrix for a single value set.
+ */
+ RealMatrix getCovariance();
+
+ /**
+ * Returns an array whose i<sup>th</sup> entry is the
+ * standard deviation of the i<sup>th</sup> entries of the arrays
+ * that correspond to each multivariate sample
+ *
+ * @return the array of component standard deviations
+ */
+ double[] getStandardDeviation();
+
+ /**
+ * Returns an array whose i<sup>th</sup> entry is the
+ * maximum of the i<sup>th</sup> entries of the arrays
+ * that correspond to each multivariate sample
+ *
+ * @return the array of component maxima
+ */
+ double[] getMax();
+
+ /**
+ * Returns an array whose i<sup>th</sup> entry is the
+ * minimum of the i<sup>th</sup> entries of the arrays
+ * that correspond to each multivariate sample
+ *
+ * @return the array of component minima
+ */
+ double[] getMin();
+
+ /**
+ * Returns the number of available values
+ * @return The number of available values
+ */
+ long getN();
+
+ /**
+ * Returns an array whose i<sup>th</sup> entry is the
+ * geometric mean of the i<sup>th</sup> entries of the arrays
+ * that correspond to each multivariate sample
+ *
+ * @return the array of component geometric means
+ */
+ double[] getGeometricMean();
+
+ /**
+ * Returns an array whose i<sup>th</sup> entry is the
+ * sum of the i<sup>th</sup> entries of the arrays
+ * that correspond to each multivariate sample
+ *
+ * @return the array of component sums
+ */
+ double[] getSum();
+
+ /**
+ * Returns an array whose i<sup>th</sup> entry is the
+ * sum of squares of the i<sup>th</sup> entries of the arrays
+ * that correspond to each multivariate sample
+ *
+ * @return the array of component sums of squares
+ */
+ double[] getSumSq();
+
+ /**
+ * Returns an array whose i<sup>th</sup> entry is the
+ * sum of logs of the i<sup>th</sup> entries of the arrays
+ * that correspond to each multivariate sample
+ *
+ * @return the array of component log sums
+ */
+ double[] getSumLog();
+
+}
diff --git a/src/main/java/org/apache/commons/math3/stat/descriptive/StatisticalSummary.java b/src/main/java/org/apache/commons/math3/stat/descriptive/StatisticalSummary.java
new file mode 100644
index 0000000..2f310ac
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/stat/descriptive/StatisticalSummary.java
@@ -0,0 +1,64 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math3.stat.descriptive;
+
+/**
+ * Reporting interface for basic univariate statistics.
+ *
+ */
+public interface StatisticalSummary {
+
+ /**
+ * Returns the <a href="http://www.xycoon.com/arithmetic_mean.htm">
+ * arithmetic mean </a> of the available values
+ * @return The mean or Double.NaN if no values have been added.
+ */
+ double getMean();
+ /**
+ * Returns the variance of the available values.
+ * @return The variance, Double.NaN if no values have been added
+ * or 0.0 for a single value set.
+ */
+ double getVariance();
+ /**
+ * Returns the standard deviation of the available values.
+ * @return The standard deviation, Double.NaN if no values have been added
+ * or 0.0 for a single value set.
+ */
+ double getStandardDeviation();
+ /**
+ * Returns the maximum of the available values
+ * @return The max or Double.NaN if no values have been added.
+ */
+ double getMax();
+ /**
+ * Returns the minimum of the available values
+ * @return The min or Double.NaN if no values have been added.
+ */
+ double getMin();
+ /**
+ * Returns the number of available values
+ * @return The number of available values
+ */
+ long getN();
+ /**
+ * Returns the sum of the values that have been added to Univariate.
+ * @return The sum or Double.NaN if no values have been added
+ */
+ double getSum();
+
+}
diff --git a/src/main/java/org/apache/commons/math3/stat/descriptive/StatisticalSummaryValues.java b/src/main/java/org/apache/commons/math3/stat/descriptive/StatisticalSummaryValues.java
new file mode 100644
index 0000000..e216e9b
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/stat/descriptive/StatisticalSummaryValues.java
@@ -0,0 +1,186 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math3.stat.descriptive;
+
+import java.io.Serializable;
+
+import org.apache.commons.math3.util.FastMath;
+import org.apache.commons.math3.util.MathUtils;
+import org.apache.commons.math3.util.Precision;
+
+/**
+ * Value object representing the results of a univariate statistical summary.
+ *
+ */
+public class StatisticalSummaryValues implements Serializable,
+ StatisticalSummary {
+
+ /** Serialization id */
+ private static final long serialVersionUID = -5108854841843722536L;
+
+ /** The sample mean */
+ private final double mean;
+
+ /** The sample variance */
+ private final double variance;
+
+ /** The number of observations in the sample */
+ private final long n;
+
+ /** The maximum value */
+ private final double max;
+
+ /** The minimum value */
+ private final double min;
+
+ /** The sum of the sample values */
+ private final double sum;
+
+ /**
+ * Constructor
+ *
+ * @param mean the sample mean
+ * @param variance the sample variance
+ * @param n the number of observations in the sample
+ * @param max the maximum value
+ * @param min the minimum value
+ * @param sum the sum of the values
+ */
+ public StatisticalSummaryValues(double mean, double variance, long n,
+ double max, double min, double sum) {
+ super();
+ this.mean = mean;
+ this.variance = variance;
+ this.n = n;
+ this.max = max;
+ this.min = min;
+ this.sum = sum;
+ }
+
+ /**
+ * @return Returns the max.
+ */
+ public double getMax() {
+ return max;
+ }
+
+ /**
+ * @return Returns the mean.
+ */
+ public double getMean() {
+ return mean;
+ }
+
+ /**
+ * @return Returns the min.
+ */
+ public double getMin() {
+ return min;
+ }
+
+ /**
+ * @return Returns the number of values.
+ */
+ public long getN() {
+ return n;
+ }
+
+ /**
+ * @return Returns the sum.
+ */
+ public double getSum() {
+ return sum;
+ }
+
+ /**
+ * @return Returns the standard deviation
+ */
+ public double getStandardDeviation() {
+ return FastMath.sqrt(variance);
+ }
+
+ /**
+ * @return Returns the variance.
+ */
+ public double getVariance() {
+ return variance;
+ }
+
+ /**
+ * Returns true iff <code>object</code> is a
+ * <code>StatisticalSummaryValues</code> instance and all statistics have
+ * the same values as this.
+ *
+ * @param object the object to test equality against.
+ * @return true if object equals this
+ */
+ @Override
+ public boolean equals(Object object) {
+ if (object == this ) {
+ return true;
+ }
+ if (object instanceof StatisticalSummaryValues == false) {
+ return false;
+ }
+ StatisticalSummaryValues stat = (StatisticalSummaryValues) object;
+ return Precision.equalsIncludingNaN(stat.getMax(), getMax()) &&
+ Precision.equalsIncludingNaN(stat.getMean(), getMean()) &&
+ Precision.equalsIncludingNaN(stat.getMin(), getMin()) &&
+ Precision.equalsIncludingNaN(stat.getN(), getN()) &&
+ Precision.equalsIncludingNaN(stat.getSum(), getSum()) &&
+ Precision.equalsIncludingNaN(stat.getVariance(), getVariance());
+ }
+
+ /**
+ * Returns hash code based on values of statistics
+ *
+ * @return hash code
+ */
+ @Override
+ public int hashCode() {
+ int result = 31 + MathUtils.hash(getMax());
+ result = result * 31 + MathUtils.hash(getMean());
+ result = result * 31 + MathUtils.hash(getMin());
+ result = result * 31 + MathUtils.hash(getN());
+ result = result * 31 + MathUtils.hash(getSum());
+ result = result * 31 + MathUtils.hash(getVariance());
+ return result;
+ }
+
+ /**
+ * Generates a text report displaying values of statistics.
+ * Each statistic is displayed on a separate line.
+ *
+ * @return String with line feeds displaying statistics
+ */
+ @Override
+ public String toString() {
+ StringBuffer outBuffer = new StringBuffer();
+ String endl = "\n";
+ outBuffer.append("StatisticalSummaryValues:").append(endl);
+ outBuffer.append("n: ").append(getN()).append(endl);
+ outBuffer.append("min: ").append(getMin()).append(endl);
+ outBuffer.append("max: ").append(getMax()).append(endl);
+ outBuffer.append("mean: ").append(getMean()).append(endl);
+ outBuffer.append("std dev: ").append(getStandardDeviation())
+ .append(endl);
+ outBuffer.append("variance: ").append(getVariance()).append(endl);
+ outBuffer.append("sum: ").append(getSum()).append(endl);
+ return outBuffer.toString();
+ }
+
+}
diff --git a/src/main/java/org/apache/commons/math3/stat/descriptive/StorelessUnivariateStatistic.java b/src/main/java/org/apache/commons/math3/stat/descriptive/StorelessUnivariateStatistic.java
new file mode 100644
index 0000000..e1c2464
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/stat/descriptive/StorelessUnivariateStatistic.java
@@ -0,0 +1,87 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math3.stat.descriptive;
+
+import org.apache.commons.math3.exception.MathIllegalArgumentException;
+
+/**
+ * Extends the definition of {@link UnivariateStatistic} with
+ * {@link #increment} and {@link #incrementAll(double[])} methods for adding
+ * values and updating internal state.
+ * <p>
+ * This interface is designed to be used for calculating statistics that can be
+ * computed in one pass through the data without storing the full array of
+ * sample values.</p>
+ *
+ */
+public interface StorelessUnivariateStatistic extends UnivariateStatistic {
+
+ /**
+ * Updates the internal state of the statistic to reflect the addition of the new value.
+ * @param d the new value.
+ */
+ void increment(double d);
+
+ /**
+ * Updates the internal state of the statistic to reflect addition of
+ * all values in the values array. Does not clear the statistic first --
+ * i.e., the values are added <strong>incrementally</strong> to the dataset.
+ *
+ * @param values array holding the new values to add
+ * @throws MathIllegalArgumentException if the array is null
+ */
+ void incrementAll(double[] values) throws MathIllegalArgumentException;
+
+ /**
+ * Updates the internal state of the statistic to reflect addition of
+ * the values in the designated portion of the values array. Does not
+ * clear the statistic first -- i.e., the values are added
+ * <strong>incrementally</strong> to the dataset.
+ *
+ * @param values array holding the new values to add
+ * @param start the array index of the first value to add
+ * @param length the number of elements to add
+ * @throws MathIllegalArgumentException if the array is null or the index
+ */
+ void incrementAll(double[] values, int start, int length) throws MathIllegalArgumentException;
+
+ /**
+ * Returns the current value of the Statistic.
+ * @return value of the statistic, <code>Double.NaN</code> if it
+ * has been cleared or just instantiated.
+ */
+ double getResult();
+
+ /**
+ * Returns the number of values that have been added.
+ * @return the number of values.
+ */
+ long getN();
+
+ /**
+ * Clears the internal state of the Statistic
+ */
+ void clear();
+
+ /**
+ * Returns a copy of the statistic with the same internal state.
+ *
+ * @return a copy of the statistic
+ */
+ StorelessUnivariateStatistic copy();
+
+}
diff --git a/src/main/java/org/apache/commons/math3/stat/descriptive/SummaryStatistics.java b/src/main/java/org/apache/commons/math3/stat/descriptive/SummaryStatistics.java
new file mode 100644
index 0000000..62fee80
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/stat/descriptive/SummaryStatistics.java
@@ -0,0 +1,765 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math3.stat.descriptive;
+
+import java.io.Serializable;
+
+import org.apache.commons.math3.exception.MathIllegalStateException;
+import org.apache.commons.math3.exception.NullArgumentException;
+import org.apache.commons.math3.exception.util.LocalizedFormats;
+import org.apache.commons.math3.stat.descriptive.moment.GeometricMean;
+import org.apache.commons.math3.stat.descriptive.moment.Mean;
+import org.apache.commons.math3.stat.descriptive.moment.SecondMoment;
+import org.apache.commons.math3.stat.descriptive.moment.Variance;
+import org.apache.commons.math3.stat.descriptive.rank.Max;
+import org.apache.commons.math3.stat.descriptive.rank.Min;
+import org.apache.commons.math3.stat.descriptive.summary.Sum;
+import org.apache.commons.math3.stat.descriptive.summary.SumOfLogs;
+import org.apache.commons.math3.stat.descriptive.summary.SumOfSquares;
+import org.apache.commons.math3.util.MathUtils;
+import org.apache.commons.math3.util.Precision;
+import org.apache.commons.math3.util.FastMath;
+
+/**
+ * <p>
+ * Computes summary statistics for a stream of data values added using the
+ * {@link #addValue(double) addValue} method. The data values are not stored in
+ * memory, so this class can be used to compute statistics for very large data
+ * streams.
+ * </p>
+ * <p>
+ * The {@link StorelessUnivariateStatistic} instances used to maintain summary
+ * state and compute statistics are configurable via setters. For example, the
+ * default implementation for the variance can be overridden by calling
+ * {@link #setVarianceImpl(StorelessUnivariateStatistic)}. Actual parameters to
+ * these methods must implement the {@link StorelessUnivariateStatistic}
+ * interface and configuration must be completed before <code>addValue</code>
+ * is called. No configuration is necessary to use the default, commons-math
+ * provided implementations.
+ * </p>
+ * <p>
+ * Note: This class is not thread-safe. Use
+ * {@link SynchronizedSummaryStatistics} if concurrent access from multiple
+ * threads is required.
+ * </p>
+ */
+public class SummaryStatistics implements StatisticalSummary, Serializable {
+
+ /** Serialization UID */
+ private static final long serialVersionUID = -2021321786743555871L;
+
+ /** count of values that have been added */
+ private long n = 0;
+
+ /** SecondMoment is used to compute the mean and variance */
+ private SecondMoment secondMoment = new SecondMoment();
+
+ /** sum of values that have been added */
+ private Sum sum = new Sum();
+
+ /** sum of the square of each value that has been added */
+ private SumOfSquares sumsq = new SumOfSquares();
+
+ /** min of values that have been added */
+ private Min min = new Min();
+
+ /** max of values that have been added */
+ private Max max = new Max();
+
+ /** sumLog of values that have been added */
+ private SumOfLogs sumLog = new SumOfLogs();
+
+ /** geoMean of values that have been added */
+ private GeometricMean geoMean = new GeometricMean(sumLog);
+
+ /** mean of values that have been added */
+ private Mean mean = new Mean(secondMoment);
+
+ /** variance of values that have been added */
+ private Variance variance = new Variance(secondMoment);
+
+ /** Sum statistic implementation - can be reset by setter. */
+ private StorelessUnivariateStatistic sumImpl = sum;
+
+ /** Sum of squares statistic implementation - can be reset by setter. */
+ private StorelessUnivariateStatistic sumsqImpl = sumsq;
+
+ /** Minimum statistic implementation - can be reset by setter. */
+ private StorelessUnivariateStatistic minImpl = min;
+
+ /** Maximum statistic implementation - can be reset by setter. */
+ private StorelessUnivariateStatistic maxImpl = max;
+
+ /** Sum of log statistic implementation - can be reset by setter. */
+ private StorelessUnivariateStatistic sumLogImpl = sumLog;
+
+ /** Geometric mean statistic implementation - can be reset by setter. */
+ private StorelessUnivariateStatistic geoMeanImpl = geoMean;
+
+ /** Mean statistic implementation - can be reset by setter. */
+ private StorelessUnivariateStatistic meanImpl = mean;
+
+ /** Variance statistic implementation - can be reset by setter. */
+ private StorelessUnivariateStatistic varianceImpl = variance;
+
+ /**
+ * Construct a SummaryStatistics instance
+ */
+ public SummaryStatistics() {
+ }
+
+ /**
+ * A copy constructor. Creates a deep-copy of the {@code original}.
+ *
+ * @param original the {@code SummaryStatistics} instance to copy
+ * @throws NullArgumentException if original is null
+ */
+ public SummaryStatistics(SummaryStatistics original) throws NullArgumentException {
+ copy(original, this);
+ }
+
+ /**
+ * Return a {@link StatisticalSummaryValues} instance reporting current
+ * statistics.
+ * @return Current values of statistics
+ */
+ public StatisticalSummary getSummary() {
+ return new StatisticalSummaryValues(getMean(), getVariance(), getN(),
+ getMax(), getMin(), getSum());
+ }
+
+ /**
+ * Add a value to the data
+ * @param value the value to add
+ */
+ public void addValue(double value) {
+ sumImpl.increment(value);
+ sumsqImpl.increment(value);
+ minImpl.increment(value);
+ maxImpl.increment(value);
+ sumLogImpl.increment(value);
+ secondMoment.increment(value);
+ // If mean, variance or geomean have been overridden,
+ // need to increment these
+ if (meanImpl != mean) {
+ meanImpl.increment(value);
+ }
+ if (varianceImpl != variance) {
+ varianceImpl.increment(value);
+ }
+ if (geoMeanImpl != geoMean) {
+ geoMeanImpl.increment(value);
+ }
+ n++;
+ }
+
+ /**
+ * Returns the number of available values
+ * @return The number of available values
+ */
+ public long getN() {
+ return n;
+ }
+
+ /**
+ * Returns the sum of the values that have been added
+ * @return The sum or <code>Double.NaN</code> if no values have been added
+ */
+ public double getSum() {
+ return sumImpl.getResult();
+ }
+
+ /**
+ * Returns the sum of the squares of the values that have been added.
+ * <p>
+ * Double.NaN is returned if no values have been added.
+ * </p>
+ * @return The sum of squares
+ */
+ public double getSumsq() {
+ return sumsqImpl.getResult();
+ }
+
+ /**
+ * Returns the mean of the values that have been added.
+ * <p>
+ * Double.NaN is returned if no values have been added.
+ * </p>
+ * @return the mean
+ */
+ public double getMean() {
+ return meanImpl.getResult();
+ }
+
+ /**
+ * Returns the standard deviation of the values that have been added.
+ * <p>
+ * Double.NaN is returned if no values have been added.
+ * </p>
+ * @return the standard deviation
+ */
+ public double getStandardDeviation() {
+ double stdDev = Double.NaN;
+ if (getN() > 0) {
+ if (getN() > 1) {
+ stdDev = FastMath.sqrt(getVariance());
+ } else {
+ stdDev = 0.0;
+ }
+ }
+ return stdDev;
+ }
+
+ /**
+ * Returns the quadratic mean, a.k.a.
+ * <a href="http://mathworld.wolfram.com/Root-Mean-Square.html">
+ * root-mean-square</a> of the available values
+ * @return The quadratic mean or {@code Double.NaN} if no values
+ * have been added.
+ */
+ public double getQuadraticMean() {
+ final long size = getN();
+ return size > 0 ? FastMath.sqrt(getSumsq() / size) : Double.NaN;
+ }
+
+ /**
+ * Returns the (sample) variance of the available values.
+ *
+ * <p>This method returns the bias-corrected sample variance (using {@code n - 1} in
+ * the denominator). Use {@link #getPopulationVariance()} for the non-bias-corrected
+ * population variance.</p>
+ *
+ * <p>Double.NaN is returned if no values have been added.</p>
+ *
+ * @return the variance
+ */
+ public double getVariance() {
+ return varianceImpl.getResult();
+ }
+
+ /**
+ * Returns the <a href="http://en.wikibooks.org/wiki/Statistics/Summary/Variance">
+ * population variance</a> of the values that have been added.
+ *
+ * <p>Double.NaN is returned if no values have been added.</p>
+ *
+ * @return the population variance
+ */
+ public double getPopulationVariance() {
+ Variance populationVariance = new Variance(secondMoment);
+ populationVariance.setBiasCorrected(false);
+ return populationVariance.getResult();
+ }
+
+ /**
+ * Returns the maximum of the values that have been added.
+ * <p>
+ * Double.NaN is returned if no values have been added.
+ * </p>
+ * @return the maximum
+ */
+ public double getMax() {
+ return maxImpl.getResult();
+ }
+
+ /**
+ * Returns the minimum of the values that have been added.
+ * <p>
+ * Double.NaN is returned if no values have been added.
+ * </p>
+ * @return the minimum
+ */
+ public double getMin() {
+ return minImpl.getResult();
+ }
+
+ /**
+ * Returns the geometric mean of the values that have been added.
+ * <p>
+ * Double.NaN is returned if no values have been added.
+ * </p>
+ * @return the geometric mean
+ */
+ public double getGeometricMean() {
+ return geoMeanImpl.getResult();
+ }
+
+ /**
+ * Returns the sum of the logs of the values that have been added.
+ * <p>
+ * Double.NaN is returned if no values have been added.
+ * </p>
+ * @return the sum of logs
+ * @since 1.2
+ */
+ public double getSumOfLogs() {
+ return sumLogImpl.getResult();
+ }
+
+ /**
+ * Returns a statistic related to the Second Central Moment. Specifically,
+ * what is returned is the sum of squared deviations from the sample mean
+ * among the values that have been added.
+ * <p>
+ * Returns <code>Double.NaN</code> if no data values have been added and
+ * returns <code>0</code> if there is just one value in the data set.</p>
+ * <p>
+ * @return second central moment statistic
+ * @since 2.0
+ */
+ public double getSecondMoment() {
+ return secondMoment.getResult();
+ }
+
+ /**
+ * Generates a text report displaying summary statistics from values that
+ * have been added.
+ * @return String with line feeds displaying statistics
+ * @since 1.2
+ */
+ @Override
+ public String toString() {
+ StringBuilder outBuffer = new StringBuilder();
+ String endl = "\n";
+ outBuffer.append("SummaryStatistics:").append(endl);
+ outBuffer.append("n: ").append(getN()).append(endl);
+ outBuffer.append("min: ").append(getMin()).append(endl);
+ outBuffer.append("max: ").append(getMax()).append(endl);
+ outBuffer.append("sum: ").append(getSum()).append(endl);
+ outBuffer.append("mean: ").append(getMean()).append(endl);
+ outBuffer.append("geometric mean: ").append(getGeometricMean())
+ .append(endl);
+ outBuffer.append("variance: ").append(getVariance()).append(endl);
+ outBuffer.append("population variance: ").append(getPopulationVariance()).append(endl);
+ outBuffer.append("second moment: ").append(getSecondMoment()).append(endl);
+ outBuffer.append("sum of squares: ").append(getSumsq()).append(endl);
+ outBuffer.append("standard deviation: ").append(getStandardDeviation())
+ .append(endl);
+ outBuffer.append("sum of logs: ").append(getSumOfLogs()).append(endl);
+ return outBuffer.toString();
+ }
+
+ /**
+ * Resets all statistics and storage
+ */
+ public void clear() {
+ this.n = 0;
+ minImpl.clear();
+ maxImpl.clear();
+ sumImpl.clear();
+ sumLogImpl.clear();
+ sumsqImpl.clear();
+ geoMeanImpl.clear();
+ secondMoment.clear();
+ if (meanImpl != mean) {
+ meanImpl.clear();
+ }
+ if (varianceImpl != variance) {
+ varianceImpl.clear();
+ }
+ }
+
+ /**
+ * Returns true iff <code>object</code> is a
+ * <code>SummaryStatistics</code> instance and all statistics have the
+ * same values as this.
+ * @param object the object to test equality against.
+ * @return true if object equals this
+ */
+ @Override
+ public boolean equals(Object object) {
+ if (object == this) {
+ return true;
+ }
+ if (object instanceof SummaryStatistics == false) {
+ return false;
+ }
+ SummaryStatistics stat = (SummaryStatistics)object;
+ return Precision.equalsIncludingNaN(stat.getGeometricMean(), getGeometricMean()) &&
+ Precision.equalsIncludingNaN(stat.getMax(), getMax()) &&
+ Precision.equalsIncludingNaN(stat.getMean(), getMean()) &&
+ Precision.equalsIncludingNaN(stat.getMin(), getMin()) &&
+ Precision.equalsIncludingNaN(stat.getN(), getN()) &&
+ Precision.equalsIncludingNaN(stat.getSum(), getSum()) &&
+ Precision.equalsIncludingNaN(stat.getSumsq(), getSumsq()) &&
+ Precision.equalsIncludingNaN(stat.getVariance(), getVariance());
+ }
+
+ /**
+ * Returns hash code based on values of statistics
+ * @return hash code
+ */
+ @Override
+ public int hashCode() {
+ int result = 31 + MathUtils.hash(getGeometricMean());
+ result = result * 31 + MathUtils.hash(getGeometricMean());
+ result = result * 31 + MathUtils.hash(getMax());
+ result = result * 31 + MathUtils.hash(getMean());
+ result = result * 31 + MathUtils.hash(getMin());
+ result = result * 31 + MathUtils.hash(getN());
+ result = result * 31 + MathUtils.hash(getSum());
+ result = result * 31 + MathUtils.hash(getSumsq());
+ result = result * 31 + MathUtils.hash(getVariance());
+ return result;
+ }
+
+ // Getters and setters for statistics implementations
+ /**
+ * Returns the currently configured Sum implementation
+ * @return the StorelessUnivariateStatistic implementing the sum
+ * @since 1.2
+ */
+ public StorelessUnivariateStatistic getSumImpl() {
+ return sumImpl;
+ }
+
+ /**
+ * <p>
+ * Sets the implementation for the Sum.
+ * </p>
+ * <p>
+ * This method cannot be activated after data has been added - i.e.,
+ * after {@link #addValue(double) addValue} has been used to add data.
+ * If it is activated after data has been added, an IllegalStateException
+ * will be thrown.
+ * </p>
+ * @param sumImpl the StorelessUnivariateStatistic instance to use for
+ * computing the Sum
+ * @throws MathIllegalStateException if data has already been added (i.e if n >0)
+ * @since 1.2
+ */
+ public void setSumImpl(StorelessUnivariateStatistic sumImpl)
+ throws MathIllegalStateException {
+ checkEmpty();
+ this.sumImpl = sumImpl;
+ }
+
+ /**
+ * Returns the currently configured sum of squares implementation
+ * @return the StorelessUnivariateStatistic implementing the sum of squares
+ * @since 1.2
+ */
+ public StorelessUnivariateStatistic getSumsqImpl() {
+ return sumsqImpl;
+ }
+
+ /**
+ * <p>
+ * Sets the implementation for the sum of squares.
+ * </p>
+ * <p>
+ * This method cannot be activated after data has been added - i.e.,
+ * after {@link #addValue(double) addValue} has been used to add data.
+ * If it is activated after data has been added, an IllegalStateException
+ * will be thrown.
+ * </p>
+ * @param sumsqImpl the StorelessUnivariateStatistic instance to use for
+ * computing the sum of squares
+ * @throws MathIllegalStateException if data has already been added (i.e if n > 0)
+ * @since 1.2
+ */
+ public void setSumsqImpl(StorelessUnivariateStatistic sumsqImpl)
+ throws MathIllegalStateException {
+ checkEmpty();
+ this.sumsqImpl = sumsqImpl;
+ }
+
+ /**
+ * Returns the currently configured minimum implementation
+ * @return the StorelessUnivariateStatistic implementing the minimum
+ * @since 1.2
+ */
+ public StorelessUnivariateStatistic getMinImpl() {
+ return minImpl;
+ }
+
+ /**
+ * <p>
+ * Sets the implementation for the minimum.
+ * </p>
+ * <p>
+ * This method cannot be activated after data has been added - i.e.,
+ * after {@link #addValue(double) addValue} has been used to add data.
+ * If it is activated after data has been added, an IllegalStateException
+ * will be thrown.
+ * </p>
+ * @param minImpl the StorelessUnivariateStatistic instance to use for
+ * computing the minimum
+ * @throws MathIllegalStateException if data has already been added (i.e if n > 0)
+ * @since 1.2
+ */
+ public void setMinImpl(StorelessUnivariateStatistic minImpl)
+ throws MathIllegalStateException {
+ checkEmpty();
+ this.minImpl = minImpl;
+ }
+
+ /**
+ * Returns the currently configured maximum implementation
+ * @return the StorelessUnivariateStatistic implementing the maximum
+ * @since 1.2
+ */
+ public StorelessUnivariateStatistic getMaxImpl() {
+ return maxImpl;
+ }
+
+ /**
+ * <p>
+ * Sets the implementation for the maximum.
+ * </p>
+ * <p>
+ * This method cannot be activated after data has been added - i.e.,
+ * after {@link #addValue(double) addValue} has been used to add data.
+ * If it is activated after data has been added, an IllegalStateException
+ * will be thrown.
+ * </p>
+ * @param maxImpl the StorelessUnivariateStatistic instance to use for
+ * computing the maximum
+ * @throws MathIllegalStateException if data has already been added (i.e if n > 0)
+ * @since 1.2
+ */
+ public void setMaxImpl(StorelessUnivariateStatistic maxImpl)
+ throws MathIllegalStateException {
+ checkEmpty();
+ this.maxImpl = maxImpl;
+ }
+
+ /**
+ * Returns the currently configured sum of logs implementation
+ * @return the StorelessUnivariateStatistic implementing the log sum
+ * @since 1.2
+ */
+ public StorelessUnivariateStatistic getSumLogImpl() {
+ return sumLogImpl;
+ }
+
+ /**
+ * <p>
+ * Sets the implementation for the sum of logs.
+ * </p>
+ * <p>
+ * This method cannot be activated after data has been added - i.e.,
+ * after {@link #addValue(double) addValue} has been used to add data.
+ * If it is activated after data has been added, an IllegalStateException
+ * will be thrown.
+ * </p>
+ * @param sumLogImpl the StorelessUnivariateStatistic instance to use for
+ * computing the log sum
+ * @throws MathIllegalStateException if data has already been added (i.e if n > 0)
+ * @since 1.2
+ */
+ public void setSumLogImpl(StorelessUnivariateStatistic sumLogImpl)
+ throws MathIllegalStateException {
+ checkEmpty();
+ this.sumLogImpl = sumLogImpl;
+ geoMean.setSumLogImpl(sumLogImpl);
+ }
+
+ /**
+ * Returns the currently configured geometric mean implementation
+ * @return the StorelessUnivariateStatistic implementing the geometric mean
+ * @since 1.2
+ */
+ public StorelessUnivariateStatistic getGeoMeanImpl() {
+ return geoMeanImpl;
+ }
+
+ /**
+ * <p>
+ * Sets the implementation for the geometric mean.
+ * </p>
+ * <p>
+ * This method cannot be activated after data has been added - i.e.,
+ * after {@link #addValue(double) addValue} has been used to add data.
+ * If it is activated after data has been added, an IllegalStateException
+ * will be thrown.
+ * </p>
+ * @param geoMeanImpl the StorelessUnivariateStatistic instance to use for
+ * computing the geometric mean
+ * @throws MathIllegalStateException if data has already been added (i.e if n > 0)
+ * @since 1.2
+ */
+ public void setGeoMeanImpl(StorelessUnivariateStatistic geoMeanImpl)
+ throws MathIllegalStateException {
+ checkEmpty();
+ this.geoMeanImpl = geoMeanImpl;
+ }
+
+ /**
+ * Returns the currently configured mean implementation
+ * @return the StorelessUnivariateStatistic implementing the mean
+ * @since 1.2
+ */
+ public StorelessUnivariateStatistic getMeanImpl() {
+ return meanImpl;
+ }
+
+ /**
+ * <p>
+ * Sets the implementation for the mean.
+ * </p>
+ * <p>
+ * This method cannot be activated after data has been added - i.e.,
+ * after {@link #addValue(double) addValue} has been used to add data.
+ * If it is activated after data has been added, an IllegalStateException
+ * will be thrown.
+ * </p>
+ * @param meanImpl the StorelessUnivariateStatistic instance to use for
+ * computing the mean
+ * @throws MathIllegalStateException if data has already been added (i.e if n > 0)
+ * @since 1.2
+ */
+ public void setMeanImpl(StorelessUnivariateStatistic meanImpl)
+ throws MathIllegalStateException {
+ checkEmpty();
+ this.meanImpl = meanImpl;
+ }
+
+ /**
+ * Returns the currently configured variance implementation
+ * @return the StorelessUnivariateStatistic implementing the variance
+ * @since 1.2
+ */
+ public StorelessUnivariateStatistic getVarianceImpl() {
+ return varianceImpl;
+ }
+
+ /**
+ * <p>
+ * Sets the implementation for the variance.
+ * </p>
+ * <p>
+ * This method cannot be activated after data has been added - i.e.,
+ * after {@link #addValue(double) addValue} has been used to add data.
+ * If it is activated after data has been added, an IllegalStateException
+ * will be thrown.
+ * </p>
+ * @param varianceImpl the StorelessUnivariateStatistic instance to use for
+ * computing the variance
+ * @throws MathIllegalStateException if data has already been added (i.e if n > 0)
+ * @since 1.2
+ */
+ public void setVarianceImpl(StorelessUnivariateStatistic varianceImpl)
+ throws MathIllegalStateException {
+ checkEmpty();
+ this.varianceImpl = varianceImpl;
+ }
+
+ /**
+ * Throws IllegalStateException if n > 0.
+ * @throws MathIllegalStateException if data has been added
+ */
+ private void checkEmpty() throws MathIllegalStateException {
+ if (n > 0) {
+ throw new MathIllegalStateException(
+ LocalizedFormats.VALUES_ADDED_BEFORE_CONFIGURING_STATISTIC, n);
+ }
+ }
+
+ /**
+ * Returns a copy of this SummaryStatistics instance with the same internal state.
+ *
+ * @return a copy of this
+ */
+ public SummaryStatistics copy() {
+ SummaryStatistics result = new SummaryStatistics();
+ // No try-catch or advertised exception because arguments are guaranteed non-null
+ copy(this, result);
+ return result;
+ }
+
+ /**
+ * Copies source to dest.
+ * <p>Neither source nor dest can be null.</p>
+ *
+ * @param source SummaryStatistics to copy
+ * @param dest SummaryStatistics to copy to
+ * @throws NullArgumentException if either source or dest is null
+ */
+ public static void copy(SummaryStatistics source, SummaryStatistics dest)
+ throws NullArgumentException {
+ MathUtils.checkNotNull(source);
+ MathUtils.checkNotNull(dest);
+ dest.maxImpl = source.maxImpl.copy();
+ dest.minImpl = source.minImpl.copy();
+ dest.sumImpl = source.sumImpl.copy();
+ dest.sumLogImpl = source.sumLogImpl.copy();
+ dest.sumsqImpl = source.sumsqImpl.copy();
+ dest.secondMoment = source.secondMoment.copy();
+ dest.n = source.n;
+
+ // Keep commons-math supplied statistics with embedded moments in synch
+ if (source.getVarianceImpl() instanceof Variance) {
+ dest.varianceImpl = new Variance(dest.secondMoment);
+ } else {
+ dest.varianceImpl = source.varianceImpl.copy();
+ }
+ if (source.meanImpl instanceof Mean) {
+ dest.meanImpl = new Mean(dest.secondMoment);
+ } else {
+ dest.meanImpl = source.meanImpl.copy();
+ }
+ if (source.getGeoMeanImpl() instanceof GeometricMean) {
+ dest.geoMeanImpl = new GeometricMean((SumOfLogs) dest.sumLogImpl);
+ } else {
+ dest.geoMeanImpl = source.geoMeanImpl.copy();
+ }
+
+ // Make sure that if stat == statImpl in source, same
+ // holds in dest; otherwise copy stat
+ if (source.geoMean == source.geoMeanImpl) {
+ dest.geoMean = (GeometricMean) dest.geoMeanImpl;
+ } else {
+ GeometricMean.copy(source.geoMean, dest.geoMean);
+ }
+ if (source.max == source.maxImpl) {
+ dest.max = (Max) dest.maxImpl;
+ } else {
+ Max.copy(source.max, dest.max);
+ }
+ if (source.mean == source.meanImpl) {
+ dest.mean = (Mean) dest.meanImpl;
+ } else {
+ Mean.copy(source.mean, dest.mean);
+ }
+ if (source.min == source.minImpl) {
+ dest.min = (Min) dest.minImpl;
+ } else {
+ Min.copy(source.min, dest.min);
+ }
+ if (source.sum == source.sumImpl) {
+ dest.sum = (Sum) dest.sumImpl;
+ } else {
+ Sum.copy(source.sum, dest.sum);
+ }
+ if (source.variance == source.varianceImpl) {
+ dest.variance = (Variance) dest.varianceImpl;
+ } else {
+ Variance.copy(source.variance, dest.variance);
+ }
+ if (source.sumLog == source.sumLogImpl) {
+ dest.sumLog = (SumOfLogs) dest.sumLogImpl;
+ } else {
+ SumOfLogs.copy(source.sumLog, dest.sumLog);
+ }
+ if (source.sumsq == source.sumsqImpl) {
+ dest.sumsq = (SumOfSquares) dest.sumsqImpl;
+ } else {
+ SumOfSquares.copy(source.sumsq, dest.sumsq);
+ }
+ }
+}
diff --git a/src/main/java/org/apache/commons/math3/stat/descriptive/SynchronizedDescriptiveStatistics.java b/src/main/java/org/apache/commons/math3/stat/descriptive/SynchronizedDescriptiveStatistics.java
new file mode 100644
index 0000000..270e4aa
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/stat/descriptive/SynchronizedDescriptiveStatistics.java
@@ -0,0 +1,192 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math3.stat.descriptive;
+
+import org.apache.commons.math3.exception.MathIllegalArgumentException;
+import org.apache.commons.math3.exception.NullArgumentException;
+import org.apache.commons.math3.util.MathUtils;
+
+/**
+ * Implementation of
+ * {@link org.apache.commons.math3.stat.descriptive.DescriptiveStatistics} that
+ * is safe to use in a multithreaded environment. Multiple threads can safely
+ * operate on a single instance without causing runtime exceptions due to race
+ * conditions. In effect, this implementation makes modification and access
+ * methods atomic operations for a single instance. That is to say, as one
+ * thread is computing a statistic from the instance, no other thread can modify
+ * the instance nor compute another statistic.
+ *
+ * @since 1.2
+ */
+public class SynchronizedDescriptiveStatistics extends DescriptiveStatistics {
+
+ /** Serialization UID */
+ private static final long serialVersionUID = 1L;
+
+ /**
+ * Construct an instance with infinite window
+ */
+ public SynchronizedDescriptiveStatistics() {
+ // no try-catch or advertized IAE because arg is valid
+ this(INFINITE_WINDOW);
+ }
+
+ /**
+ * Construct an instance with finite window
+ * @param window the finite window size.
+ * @throws MathIllegalArgumentException if window size is less than 1 but
+ * not equal to {@link #INFINITE_WINDOW}
+ */
+ public SynchronizedDescriptiveStatistics(int window) throws MathIllegalArgumentException {
+ super(window);
+ }
+
+ /**
+ * A copy constructor. Creates a deep-copy of the {@code original}.
+ *
+ * @param original the {@code SynchronizedDescriptiveStatistics} instance to copy
+ * @throws NullArgumentException if original is null
+ */
+ public SynchronizedDescriptiveStatistics(SynchronizedDescriptiveStatistics original)
+ throws NullArgumentException {
+ copy(original, this);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public synchronized void addValue(double v) {
+ super.addValue(v);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public synchronized double apply(UnivariateStatistic stat) {
+ return super.apply(stat);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public synchronized void clear() {
+ super.clear();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public synchronized double getElement(int index) {
+ return super.getElement(index);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public synchronized long getN() {
+ return super.getN();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public synchronized double getStandardDeviation() {
+ return super.getStandardDeviation();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public synchronized double getQuadraticMean() {
+ return super.getQuadraticMean();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public synchronized double[] getValues() {
+ return super.getValues();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public synchronized int getWindowSize() {
+ return super.getWindowSize();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public synchronized void setWindowSize(int windowSize) throws MathIllegalArgumentException {
+ super.setWindowSize(windowSize);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public synchronized String toString() {
+ return super.toString();
+ }
+
+ /**
+ * Returns a copy of this SynchronizedDescriptiveStatistics instance with the
+ * same internal state.
+ *
+ * @return a copy of this
+ */
+ @Override
+ public synchronized SynchronizedDescriptiveStatistics copy() {
+ SynchronizedDescriptiveStatistics result =
+ new SynchronizedDescriptiveStatistics();
+ // No try-catch or advertised exception because arguments are guaranteed non-null
+ copy(this, result);
+ return result;
+ }
+
+ /**
+ * Copies source to dest.
+ * <p>Neither source nor dest can be null.</p>
+ * <p>Acquires synchronization lock on source, then dest before copying.</p>
+ *
+ * @param source SynchronizedDescriptiveStatistics to copy
+ * @param dest SynchronizedDescriptiveStatistics to copy to
+ * @throws NullArgumentException if either source or dest is null
+ */
+ public static void copy(SynchronizedDescriptiveStatistics source,
+ SynchronizedDescriptiveStatistics dest)
+ throws NullArgumentException {
+ MathUtils.checkNotNull(source);
+ MathUtils.checkNotNull(dest);
+ synchronized (source) {
+ synchronized (dest) {
+ DescriptiveStatistics.copy(source, dest);
+ }
+ }
+ }
+}
diff --git a/src/main/java/org/apache/commons/math3/stat/descriptive/SynchronizedMultivariateSummaryStatistics.java b/src/main/java/org/apache/commons/math3/stat/descriptive/SynchronizedMultivariateSummaryStatistics.java
new file mode 100644
index 0000000..889eb3a
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/stat/descriptive/SynchronizedMultivariateSummaryStatistics.java
@@ -0,0 +1,297 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math3.stat.descriptive;
+
+import org.apache.commons.math3.exception.DimensionMismatchException;
+import org.apache.commons.math3.exception.MathIllegalStateException;
+import org.apache.commons.math3.linear.RealMatrix;
+
+/**
+ * Implementation of
+ * {@link org.apache.commons.math3.stat.descriptive.MultivariateSummaryStatistics} that
+ * is safe to use in a multithreaded environment. Multiple threads can safely
+ * operate on a single instance without causing runtime exceptions due to race
+ * conditions. In effect, this implementation makes modification and access
+ * methods atomic operations for a single instance. That is to say, as one
+ * thread is computing a statistic from the instance, no other thread can modify
+ * the instance nor compute another statistic.
+ * @since 1.2
+ */
+public class SynchronizedMultivariateSummaryStatistics
+ extends MultivariateSummaryStatistics {
+
+ /** Serialization UID */
+ private static final long serialVersionUID = 7099834153347155363L;
+
+ /**
+ * Construct a SynchronizedMultivariateSummaryStatistics instance
+ * @param k dimension of the data
+ * @param isCovarianceBiasCorrected if true, the unbiased sample
+ * covariance is computed, otherwise the biased population covariance
+ * is computed
+ */
+ public SynchronizedMultivariateSummaryStatistics(int k, boolean isCovarianceBiasCorrected) {
+ super(k, isCovarianceBiasCorrected);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public synchronized void addValue(double[] value) throws DimensionMismatchException {
+ super.addValue(value);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public synchronized int getDimension() {
+ return super.getDimension();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public synchronized long getN() {
+ return super.getN();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public synchronized double[] getSum() {
+ return super.getSum();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public synchronized double[] getSumSq() {
+ return super.getSumSq();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public synchronized double[] getSumLog() {
+ return super.getSumLog();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public synchronized double[] getMean() {
+ return super.getMean();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public synchronized double[] getStandardDeviation() {
+ return super.getStandardDeviation();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public synchronized RealMatrix getCovariance() {
+ return super.getCovariance();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public synchronized double[] getMax() {
+ return super.getMax();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public synchronized double[] getMin() {
+ return super.getMin();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public synchronized double[] getGeometricMean() {
+ return super.getGeometricMean();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public synchronized String toString() {
+ return super.toString();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public synchronized void clear() {
+ super.clear();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public synchronized boolean equals(Object object) {
+ return super.equals(object);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public synchronized int hashCode() {
+ return super.hashCode();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public synchronized StorelessUnivariateStatistic[] getSumImpl() {
+ return super.getSumImpl();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public synchronized void setSumImpl(StorelessUnivariateStatistic[] sumImpl)
+ throws DimensionMismatchException, MathIllegalStateException {
+ super.setSumImpl(sumImpl);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public synchronized StorelessUnivariateStatistic[] getSumsqImpl() {
+ return super.getSumsqImpl();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public synchronized void setSumsqImpl(StorelessUnivariateStatistic[] sumsqImpl)
+ throws DimensionMismatchException, MathIllegalStateException {
+ super.setSumsqImpl(sumsqImpl);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public synchronized StorelessUnivariateStatistic[] getMinImpl() {
+ return super.getMinImpl();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public synchronized void setMinImpl(StorelessUnivariateStatistic[] minImpl)
+ throws DimensionMismatchException, MathIllegalStateException {
+ super.setMinImpl(minImpl);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public synchronized StorelessUnivariateStatistic[] getMaxImpl() {
+ return super.getMaxImpl();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public synchronized void setMaxImpl(StorelessUnivariateStatistic[] maxImpl)
+ throws DimensionMismatchException, MathIllegalStateException{
+ super.setMaxImpl(maxImpl);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public synchronized StorelessUnivariateStatistic[] getSumLogImpl() {
+ return super.getSumLogImpl();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public synchronized void setSumLogImpl(StorelessUnivariateStatistic[] sumLogImpl)
+ throws DimensionMismatchException, MathIllegalStateException {
+ super.setSumLogImpl(sumLogImpl);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public synchronized StorelessUnivariateStatistic[] getGeoMeanImpl() {
+ return super.getGeoMeanImpl();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public synchronized void setGeoMeanImpl(StorelessUnivariateStatistic[] geoMeanImpl)
+ throws DimensionMismatchException, MathIllegalStateException {
+ super.setGeoMeanImpl(geoMeanImpl);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public synchronized StorelessUnivariateStatistic[] getMeanImpl() {
+ return super.getMeanImpl();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public synchronized void setMeanImpl(StorelessUnivariateStatistic[] meanImpl)
+ throws DimensionMismatchException, MathIllegalStateException {
+ super.setMeanImpl(meanImpl);
+ }
+}
diff --git a/src/main/java/org/apache/commons/math3/stat/descriptive/SynchronizedSummaryStatistics.java b/src/main/java/org/apache/commons/math3/stat/descriptive/SynchronizedSummaryStatistics.java
new file mode 100644
index 0000000..7eaf9ac
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/stat/descriptive/SynchronizedSummaryStatistics.java
@@ -0,0 +1,366 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math3.stat.descriptive;
+
+import org.apache.commons.math3.exception.MathIllegalStateException;
+import org.apache.commons.math3.exception.NullArgumentException;
+import org.apache.commons.math3.util.MathUtils;
+
+/**
+ * Implementation of
+ * {@link org.apache.commons.math3.stat.descriptive.SummaryStatistics} that
+ * is safe to use in a multithreaded environment. Multiple threads can safely
+ * operate on a single instance without causing runtime exceptions due to race
+ * conditions. In effect, this implementation makes modification and access
+ * methods atomic operations for a single instance. That is to say, as one
+ * thread is computing a statistic from the instance, no other thread can modify
+ * the instance nor compute another statistic.
+ *
+ * @since 1.2
+ */
+public class SynchronizedSummaryStatistics extends SummaryStatistics {
+
+ /** Serialization UID */
+ private static final long serialVersionUID = 1909861009042253704L;
+
+ /**
+ * Construct a SynchronizedSummaryStatistics instance
+ */
+ public SynchronizedSummaryStatistics() {
+ super();
+ }
+
+ /**
+ * A copy constructor. Creates a deep-copy of the {@code original}.
+ *
+ * @param original the {@code SynchronizedSummaryStatistics} instance to copy
+ * @throws NullArgumentException if original is null
+ */
+ public SynchronizedSummaryStatistics(SynchronizedSummaryStatistics original)
+ throws NullArgumentException {
+ copy(original, this);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public synchronized StatisticalSummary getSummary() {
+ return super.getSummary();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public synchronized void addValue(double value) {
+ super.addValue(value);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public synchronized long getN() {
+ return super.getN();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public synchronized double getSum() {
+ return super.getSum();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public synchronized double getSumsq() {
+ return super.getSumsq();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public synchronized double getMean() {
+ return super.getMean();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public synchronized double getStandardDeviation() {
+ return super.getStandardDeviation();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public synchronized double getQuadraticMean() {
+ return super.getQuadraticMean();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public synchronized double getVariance() {
+ return super.getVariance();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public synchronized double getPopulationVariance() {
+ return super.getPopulationVariance();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public synchronized double getMax() {
+ return super.getMax();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public synchronized double getMin() {
+ return super.getMin();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public synchronized double getGeometricMean() {
+ return super.getGeometricMean();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public synchronized String toString() {
+ return super.toString();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public synchronized void clear() {
+ super.clear();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public synchronized boolean equals(Object object) {
+ return super.equals(object);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public synchronized int hashCode() {
+ return super.hashCode();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public synchronized StorelessUnivariateStatistic getSumImpl() {
+ return super.getSumImpl();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public synchronized void setSumImpl(StorelessUnivariateStatistic sumImpl)
+ throws MathIllegalStateException {
+ super.setSumImpl(sumImpl);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public synchronized StorelessUnivariateStatistic getSumsqImpl() {
+ return super.getSumsqImpl();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public synchronized void setSumsqImpl(StorelessUnivariateStatistic sumsqImpl)
+ throws MathIllegalStateException {
+ super.setSumsqImpl(sumsqImpl);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public synchronized StorelessUnivariateStatistic getMinImpl() {
+ return super.getMinImpl();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public synchronized void setMinImpl(StorelessUnivariateStatistic minImpl)
+ throws MathIllegalStateException {
+ super.setMinImpl(minImpl);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public synchronized StorelessUnivariateStatistic getMaxImpl() {
+ return super.getMaxImpl();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public synchronized void setMaxImpl(StorelessUnivariateStatistic maxImpl)
+ throws MathIllegalStateException {
+ super.setMaxImpl(maxImpl);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public synchronized StorelessUnivariateStatistic getSumLogImpl() {
+ return super.getSumLogImpl();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public synchronized void setSumLogImpl(StorelessUnivariateStatistic sumLogImpl)
+ throws MathIllegalStateException {
+ super.setSumLogImpl(sumLogImpl);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public synchronized StorelessUnivariateStatistic getGeoMeanImpl() {
+ return super.getGeoMeanImpl();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public synchronized void setGeoMeanImpl(StorelessUnivariateStatistic geoMeanImpl)
+ throws MathIllegalStateException {
+ super.setGeoMeanImpl(geoMeanImpl);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public synchronized StorelessUnivariateStatistic getMeanImpl() {
+ return super.getMeanImpl();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public synchronized void setMeanImpl(StorelessUnivariateStatistic meanImpl)
+ throws MathIllegalStateException {
+ super.setMeanImpl(meanImpl);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public synchronized StorelessUnivariateStatistic getVarianceImpl() {
+ return super.getVarianceImpl();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public synchronized void setVarianceImpl(StorelessUnivariateStatistic varianceImpl)
+ throws MathIllegalStateException {
+ super.setVarianceImpl(varianceImpl);
+ }
+
+ /**
+ * Returns a copy of this SynchronizedSummaryStatistics instance with the
+ * same internal state.
+ *
+ * @return a copy of this
+ */
+ @Override
+ public synchronized SynchronizedSummaryStatistics copy() {
+ SynchronizedSummaryStatistics result =
+ new SynchronizedSummaryStatistics();
+ // No try-catch or advertised exception because arguments are guaranteed non-null
+ copy(this, result);
+ return result;
+ }
+
+ /**
+ * Copies source to dest.
+ * <p>Neither source nor dest can be null.</p>
+ * <p>Acquires synchronization lock on source, then dest before copying.</p>
+ *
+ * @param source SynchronizedSummaryStatistics to copy
+ * @param dest SynchronizedSummaryStatistics to copy to
+ * @throws NullArgumentException if either source or dest is null
+ */
+ public static void copy(SynchronizedSummaryStatistics source,
+ SynchronizedSummaryStatistics dest)
+ throws NullArgumentException {
+ MathUtils.checkNotNull(source);
+ MathUtils.checkNotNull(dest);
+ synchronized (source) {
+ synchronized (dest) {
+ SummaryStatistics.copy(source, dest);
+ }
+ }
+ }
+
+}
diff --git a/src/main/java/org/apache/commons/math3/stat/descriptive/UnivariateStatistic.java b/src/main/java/org/apache/commons/math3/stat/descriptive/UnivariateStatistic.java
new file mode 100644
index 0000000..5d6c9fe
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/stat/descriptive/UnivariateStatistic.java
@@ -0,0 +1,55 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math3.stat.descriptive;
+
+import org.apache.commons.math3.exception.MathIllegalArgumentException;
+import org.apache.commons.math3.util.MathArrays;
+
+
+/**
+ * Base interface implemented by all statistics.
+ *
+ */
+public interface UnivariateStatistic extends MathArrays.Function {
+ /**
+ * Returns the result of evaluating the statistic over the input array.
+ *
+ * @param values input array
+ * @return the value of the statistic applied to the input array
+ * @throws MathIllegalArgumentException if values is null
+ */
+ double evaluate(double[] values) throws MathIllegalArgumentException;
+
+ /**
+ * Returns the result of evaluating the statistic over the specified entries
+ * in the input array.
+ *
+ * @param values the input array
+ * @param begin the index of the first element to include
+ * @param length the number of elements to include
+ * @return the value of the statistic applied to the included array entries
+ * @throws MathIllegalArgumentException if values is null or the indices are invalid
+ */
+ double evaluate(double[] values, int begin, int length) throws MathIllegalArgumentException;
+
+ /**
+ * Returns a copy of the statistic with the same internal state.
+ *
+ * @return a copy of the statistic
+ */
+ UnivariateStatistic copy();
+}
diff --git a/src/main/java/org/apache/commons/math3/stat/descriptive/WeightedEvaluation.java b/src/main/java/org/apache/commons/math3/stat/descriptive/WeightedEvaluation.java
new file mode 100644
index 0000000..01693dc
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/stat/descriptive/WeightedEvaluation.java
@@ -0,0 +1,57 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math3.stat.descriptive;
+
+import org.apache.commons.math3.exception.MathIllegalArgumentException;
+
+/**
+ * Weighted evaluation for statistics.
+ *
+ * @since 2.1
+ */
+public interface WeightedEvaluation {
+
+ /**
+ * Returns the result of evaluating the statistic over the input array,
+ * using the supplied weights.
+ *
+ * @param values input array
+ * @param weights array of weights
+ * @return the value of the weighted statistic applied to the input array
+ * @throws MathIllegalArgumentException if either array is null, lengths
+ * do not match, weights contain NaN, negative or infinite values, or
+ * weights does not include at least on positive value
+ */
+ double evaluate(double[] values, double[] weights) throws MathIllegalArgumentException;
+
+ /**
+ * Returns the result of evaluating the statistic over the specified entries
+ * in the input array, using corresponding entries in the supplied weights array.
+ *
+ * @param values the input array
+ * @param weights array of weights
+ * @param begin the index of the first element to include
+ * @param length the number of elements to include
+ * @return the value of the weighted statistic applied to the included array entries
+ * @throws MathIllegalArgumentException if either array is null, lengths
+ * do not match, indices are invalid, weights contain NaN, negative or
+ * infinite values, or weights does not include at least on positive value
+ */
+ double evaluate(double[] values, double[] weights, int begin, int length)
+ throws MathIllegalArgumentException;
+
+}
diff --git a/src/main/java/org/apache/commons/math3/stat/descriptive/moment/FirstMoment.java b/src/main/java/org/apache/commons/math3/stat/descriptive/moment/FirstMoment.java
new file mode 100644
index 0000000..c153724
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/stat/descriptive/moment/FirstMoment.java
@@ -0,0 +1,169 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math3.stat.descriptive.moment;
+
+import java.io.Serializable;
+
+import org.apache.commons.math3.exception.NullArgumentException;
+import org.apache.commons.math3.stat.descriptive.AbstractStorelessUnivariateStatistic;
+import org.apache.commons.math3.util.MathUtils;
+
+/**
+ * Computes the first moment (arithmetic mean). Uses the definitional formula:
+ * <p>
+ * mean = sum(x_i) / n </p>
+ * <p>
+ * where <code>n</code> is the number of observations. </p>
+ * <p>
+ * To limit numeric errors, the value of the statistic is computed using the
+ * following recursive updating algorithm: </p>
+ * <p>
+ * <ol>
+ * <li>Initialize <code>m = </code> the first value</li>
+ * <li>For each additional value, update using <br>
+ * <code>m = m + (new value - m) / (number of observations)</code></li>
+ * </ol></p>
+ * <p>
+ * Returns <code>Double.NaN</code> if the dataset is empty. Note that
+ * Double.NaN may also be returned if the input includes NaN and / or infinite
+ * values.</p>
+ * <p>
+ * <strong>Note that this implementation is not synchronized.</strong> If
+ * multiple threads access an instance of this class concurrently, and at least
+ * one of the threads invokes the <code>increment()</code> or
+ * <code>clear()</code> method, it must be synchronized externally.</p>
+ *
+ */
+class FirstMoment extends AbstractStorelessUnivariateStatistic
+ implements Serializable {
+
+ /** Serializable version identifier */
+ private static final long serialVersionUID = 6112755307178490473L;
+
+
+ /** Count of values that have been added */
+ protected long n;
+
+ /** First moment of values that have been added */
+ protected double m1;
+
+ /**
+ * Deviation of most recently added value from previous first moment.
+ * Retained to prevent repeated computation in higher order moments.
+ */
+ protected double dev;
+
+ /**
+ * Deviation of most recently added value from previous first moment,
+ * normalized by previous sample size. Retained to prevent repeated
+ * computation in higher order moments
+ */
+ protected double nDev;
+
+ /**
+ * Create a FirstMoment instance
+ */
+ FirstMoment() {
+ n = 0;
+ m1 = Double.NaN;
+ dev = Double.NaN;
+ nDev = Double.NaN;
+ }
+
+ /**
+ * Copy constructor, creates a new {@code FirstMoment} identical
+ * to the {@code original}
+ *
+ * @param original the {@code FirstMoment} instance to copy
+ * @throws NullArgumentException if original is null
+ */
+ FirstMoment(FirstMoment original) throws NullArgumentException {
+ super();
+ copy(original, this);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void increment(final double d) {
+ if (n == 0) {
+ m1 = 0.0;
+ }
+ n++;
+ double n0 = n;
+ dev = d - m1;
+ nDev = dev / n0;
+ m1 += nDev;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void clear() {
+ m1 = Double.NaN;
+ n = 0;
+ dev = Double.NaN;
+ nDev = Double.NaN;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public double getResult() {
+ return m1;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public long getN() {
+ return n;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public FirstMoment copy() {
+ FirstMoment result = new FirstMoment();
+ // No try-catch or advertised exception because args are guaranteed non-null
+ copy(this, result);
+ return result;
+ }
+
+ /**
+ * Copies source to dest.
+ * <p>Neither source nor dest can be null.</p>
+ *
+ * @param source FirstMoment to copy
+ * @param dest FirstMoment to copy to
+ * @throws NullArgumentException if either source or dest is null
+ */
+ public static void copy(FirstMoment source, FirstMoment dest)
+ throws NullArgumentException {
+ MathUtils.checkNotNull(source);
+ MathUtils.checkNotNull(dest);
+ dest.setData(source.getDataRef());
+ dest.n = source.n;
+ dest.m1 = source.m1;
+ dest.dev = source.dev;
+ dest.nDev = source.nDev;
+ }
+}
diff --git a/src/main/java/org/apache/commons/math3/stat/descriptive/moment/FourthMoment.java b/src/main/java/org/apache/commons/math3/stat/descriptive/moment/FourthMoment.java
new file mode 100644
index 0000000..0c199d8
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/stat/descriptive/moment/FourthMoment.java
@@ -0,0 +1,151 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math3.stat.descriptive.moment;
+
+import java.io.Serializable;
+
+import org.apache.commons.math3.exception.NullArgumentException;
+import org.apache.commons.math3.util.MathUtils;
+
+/**
+ * Computes a statistic related to the Fourth Central Moment. Specifically,
+ * what is computed is the sum of
+ * <p>
+ * (x_i - xbar) ^ 4, </p>
+ * <p>
+ * where the x_i are the
+ * sample observations and xbar is the sample mean. </p>
+ * <p>
+ * The following recursive updating formula is used: </p>
+ * <p>
+ * Let <ul>
+ * <li> dev = (current obs - previous mean) </li>
+ * <li> m2 = previous value of {@link SecondMoment} </li>
+ * <li> m2 = previous value of {@link ThirdMoment} </li>
+ * <li> n = number of observations (including current obs) </li>
+ * </ul>
+ * Then </p>
+ * <p>
+ * new value = old value - 4 * (dev/n) * m3 + 6 * (dev/n)^2 * m2 + <br>
+ * [n^2 - 3 * (n-1)] * dev^4 * (n-1) / n^3 </p>
+ * <p>
+ * Returns <code>Double.NaN</code> if no data values have been added and
+ * returns <code>0</code> if there is just one value in the data set. Note that
+ * Double.NaN may also be returned if the input includes NaN and / or infinite
+ * values. </p>
+ * <p>
+ * <strong>Note that this implementation is not synchronized.</strong> If
+ * multiple threads access an instance of this class concurrently, and at least
+ * one of the threads invokes the <code>increment()</code> or
+ * <code>clear()</code> method, it must be synchronized externally. </p>
+ *
+ */
+class FourthMoment extends ThirdMoment implements Serializable{
+
+ /** Serializable version identifier */
+ private static final long serialVersionUID = 4763990447117157611L;
+
+ /** fourth moment of values that have been added */
+ private double m4;
+
+ /**
+ * Create a FourthMoment instance
+ */
+ FourthMoment() {
+ super();
+ m4 = Double.NaN;
+ }
+
+ /**
+ * Copy constructor, creates a new {@code FourthMoment} identical
+ * to the {@code original}
+ *
+ * @param original the {@code FourthMoment} instance to copy
+ * @throws NullArgumentException if original is null
+ */
+ FourthMoment(FourthMoment original) throws NullArgumentException {
+ super();
+ copy(original, this);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void increment(final double d) {
+ if (n < 1) {
+ m4 = 0.0;
+ m3 = 0.0;
+ m2 = 0.0;
+ m1 = 0.0;
+ }
+
+ double prevM3 = m3;
+ double prevM2 = m2;
+
+ super.increment(d);
+
+ double n0 = n;
+
+ m4 = m4 - 4.0 * nDev * prevM3 + 6.0 * nDevSq * prevM2 +
+ ((n0 * n0) - 3 * (n0 -1)) * (nDevSq * nDevSq * (n0 - 1) * n0);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public double getResult() {
+ return m4;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void clear() {
+ super.clear();
+ m4 = Double.NaN;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public FourthMoment copy() {
+ FourthMoment result = new FourthMoment();
+ // No try-catch or advertised exception because args are guaranteed non-null
+ copy(this, result);
+ return result;
+ }
+
+ /**
+ * Copies source to dest.
+ * <p>Neither source nor dest can be null.</p>
+ *
+ * @param source FourthMoment to copy
+ * @param dest FourthMoment to copy to
+ * @throws NullArgumentException if either source or dest is null
+ */
+ public static void copy(FourthMoment source, FourthMoment dest)
+ throws NullArgumentException {
+ MathUtils.checkNotNull(source);
+ MathUtils.checkNotNull(dest);
+ ThirdMoment.copy(source, dest);
+ dest.m4 = source.m4;
+ }
+}
diff --git a/src/main/java/org/apache/commons/math3/stat/descriptive/moment/GeometricMean.java b/src/main/java/org/apache/commons/math3/stat/descriptive/moment/GeometricMean.java
new file mode 100644
index 0000000..bfee9df
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/stat/descriptive/moment/GeometricMean.java
@@ -0,0 +1,214 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math3.stat.descriptive.moment;
+
+import java.io.Serializable;
+
+import org.apache.commons.math3.exception.MathIllegalArgumentException;
+import org.apache.commons.math3.exception.MathIllegalStateException;
+import org.apache.commons.math3.exception.NullArgumentException;
+import org.apache.commons.math3.exception.util.LocalizedFormats;
+import org.apache.commons.math3.stat.descriptive.AbstractStorelessUnivariateStatistic;
+import org.apache.commons.math3.stat.descriptive.StorelessUnivariateStatistic;
+import org.apache.commons.math3.stat.descriptive.summary.SumOfLogs;
+import org.apache.commons.math3.util.FastMath;
+import org.apache.commons.math3.util.MathUtils;
+
+/**
+ * Returns the <a href="http://www.xycoon.com/geometric_mean.htm">
+ * geometric mean </a> of the available values.
+ * <p>
+ * Uses a {@link SumOfLogs} instance to compute sum of logs and returns
+ * <code> exp( 1/n (sum of logs) ).</code> Therefore, </p>
+ * <ul>
+ * <li>If any of values are < 0, the result is <code>NaN.</code></li>
+ * <li>If all values are non-negative and less than
+ * <code>Double.POSITIVE_INFINITY</code>, but at least one value is 0, the
+ * result is <code>0.</code></li>
+ * <li>If both <code>Double.POSITIVE_INFINITY</code> and
+ * <code>Double.NEGATIVE_INFINITY</code> are among the values, the result is
+ * <code>NaN.</code></li>
+ * </ul> </p>
+ * <p>
+ * <strong>Note that this implementation is not synchronized.</strong> If
+ * multiple threads access an instance of this class concurrently, and at least
+ * one of the threads invokes the <code>increment()</code> or
+ * <code>clear()</code> method, it must be synchronized externally.</p>
+ *
+ *
+ */
+public class GeometricMean extends AbstractStorelessUnivariateStatistic implements Serializable {
+
+ /** Serializable version identifier */
+ private static final long serialVersionUID = -8178734905303459453L;
+
+ /** Wrapped SumOfLogs instance */
+ private StorelessUnivariateStatistic sumOfLogs;
+
+ /**
+ * Create a GeometricMean instance
+ */
+ public GeometricMean() {
+ sumOfLogs = new SumOfLogs();
+ }
+
+ /**
+ * Copy constructor, creates a new {@code GeometricMean} identical
+ * to the {@code original}
+ *
+ * @param original the {@code GeometricMean} instance to copy
+ * @throws NullArgumentException if original is null
+ */
+ public GeometricMean(GeometricMean original) throws NullArgumentException {
+ super();
+ copy(original, this);
+ }
+
+ /**
+ * Create a GeometricMean instance using the given SumOfLogs instance
+ * @param sumOfLogs sum of logs instance to use for computation
+ */
+ public GeometricMean(SumOfLogs sumOfLogs) {
+ this.sumOfLogs = sumOfLogs;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public GeometricMean copy() {
+ GeometricMean result = new GeometricMean();
+ // no try-catch or advertised exception because args guaranteed non-null
+ copy(this, result);
+ return result;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void increment(final double d) {
+ sumOfLogs.increment(d);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public double getResult() {
+ if (sumOfLogs.getN() > 0) {
+ return FastMath.exp(sumOfLogs.getResult() / sumOfLogs.getN());
+ } else {
+ return Double.NaN;
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void clear() {
+ sumOfLogs.clear();
+ }
+
+ /**
+ * Returns the geometric mean of the entries in the specified portion
+ * of the input array.
+ * <p>
+ * See {@link GeometricMean} for details on the computing algorithm.</p>
+ * <p>
+ * Throws <code>IllegalArgumentException</code> if the array is null.</p>
+ *
+ * @param values input array containing the values
+ * @param begin first array element to include
+ * @param length the number of elements to include
+ * @return the geometric mean or Double.NaN if length = 0 or
+ * any of the values are &lt;= 0.
+ * @throws MathIllegalArgumentException if the input array is null or the array
+ * index parameters are not valid
+ */
+ @Override
+ public double evaluate(
+ final double[] values, final int begin, final int length)
+ throws MathIllegalArgumentException {
+ return FastMath.exp(
+ sumOfLogs.evaluate(values, begin, length) / length);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public long getN() {
+ return sumOfLogs.getN();
+ }
+
+ /**
+ * <p>Sets the implementation for the sum of logs.</p>
+ * <p>This method must be activated before any data has been added - i.e.,
+ * before {@link #increment(double) increment} has been used to add data;
+ * otherwise an IllegalStateException will be thrown.</p>
+ *
+ * @param sumLogImpl the StorelessUnivariateStatistic instance to use
+ * for computing the log sum
+ * @throws MathIllegalStateException if data has already been added
+ * (i.e if n > 0)
+ */
+ public void setSumLogImpl(StorelessUnivariateStatistic sumLogImpl)
+ throws MathIllegalStateException {
+ checkEmpty();
+ this.sumOfLogs = sumLogImpl;
+ }
+
+ /**
+ * Returns the currently configured sum of logs implementation
+ *
+ * @return the StorelessUnivariateStatistic implementing the log sum
+ */
+ public StorelessUnivariateStatistic getSumLogImpl() {
+ return sumOfLogs;
+ }
+
+ /**
+ * Copies source to dest.
+ * <p>Neither source nor dest can be null.</p>
+ *
+ * @param source GeometricMean to copy
+ * @param dest GeometricMean to copy to
+ * @throws NullArgumentException if either source or dest is null
+ */
+ public static void copy(GeometricMean source, GeometricMean dest)
+ throws NullArgumentException {
+ MathUtils.checkNotNull(source);
+ MathUtils.checkNotNull(dest);
+ dest.setData(source.getDataRef());
+ dest.sumOfLogs = source.sumOfLogs.copy();
+ }
+
+
+ /**
+ * Throws MathIllegalStateException if n > 0.
+ * @throws MathIllegalStateException if data has been added to this statistic
+ */
+ private void checkEmpty() throws MathIllegalStateException {
+ if (getN() > 0) {
+ throw new MathIllegalStateException(
+ LocalizedFormats.VALUES_ADDED_BEFORE_CONFIGURING_STATISTIC,
+ getN());
+ }
+ }
+
+}
diff --git a/src/main/java/org/apache/commons/math3/stat/descriptive/moment/Kurtosis.java b/src/main/java/org/apache/commons/math3/stat/descriptive/moment/Kurtosis.java
new file mode 100644
index 0000000..be04fbe
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/stat/descriptive/moment/Kurtosis.java
@@ -0,0 +1,226 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math3.stat.descriptive.moment;
+
+import java.io.Serializable;
+
+import org.apache.commons.math3.exception.MathIllegalArgumentException;
+import org.apache.commons.math3.exception.NullArgumentException;
+import org.apache.commons.math3.stat.descriptive.AbstractStorelessUnivariateStatistic;
+import org.apache.commons.math3.util.FastMath;
+import org.apache.commons.math3.util.MathUtils;
+
+
+/**
+ * Computes the Kurtosis of the available values.
+ * <p>
+ * We use the following (unbiased) formula to define kurtosis:</p>
+ * <p>
+ * kurtosis = { [n(n+1) / (n -1)(n - 2)(n-3)] sum[(x_i - mean)^4] / std^4 } - [3(n-1)^2 / (n-2)(n-3)]
+ * </p><p>
+ * where n is the number of values, mean is the {@link Mean} and std is the
+ * {@link StandardDeviation}</p>
+ * <p>
+ * Note that this statistic is undefined for n < 4. <code>Double.Nan</code>
+ * is returned when there is not sufficient data to compute the statistic.
+ * Note that Double.NaN may also be returned if the input includes NaN
+ * and / or infinite values.</p>
+ * <p>
+ * <strong>Note that this implementation is not synchronized.</strong> If
+ * multiple threads access an instance of this class concurrently, and at least
+ * one of the threads invokes the <code>increment()</code> or
+ * <code>clear()</code> method, it must be synchronized externally.</p>
+ *
+ */
+public class Kurtosis extends AbstractStorelessUnivariateStatistic implements Serializable {
+
+ /** Serializable version identifier */
+ private static final long serialVersionUID = 2784465764798260919L;
+
+ /**Fourth Moment on which this statistic is based */
+ protected FourthMoment moment;
+
+ /**
+ * Determines whether or not this statistic can be incremented or cleared.
+ * <p>
+ * Statistics based on (constructed from) external moments cannot
+ * be incremented or cleared.</p>
+ */
+ protected boolean incMoment;
+
+ /**
+ * Construct a Kurtosis
+ */
+ public Kurtosis() {
+ incMoment = true;
+ moment = new FourthMoment();
+ }
+
+ /**
+ * Construct a Kurtosis from an external moment
+ *
+ * @param m4 external Moment
+ */
+ public Kurtosis(final FourthMoment m4) {
+ incMoment = false;
+ this.moment = m4;
+ }
+
+ /**
+ * Copy constructor, creates a new {@code Kurtosis} identical
+ * to the {@code original}
+ *
+ * @param original the {@code Kurtosis} instance to copy
+ * @throws NullArgumentException if original is null
+ */
+ public Kurtosis(Kurtosis original) throws NullArgumentException {
+ copy(original, this);
+ }
+
+ /**
+ * {@inheritDoc}
+ * <p>Note that when {@link #Kurtosis(FourthMoment)} is used to
+ * create a Variance, this method does nothing. In that case, the
+ * FourthMoment should be incremented directly.</p>
+ */
+ @Override
+ public void increment(final double d) {
+ if (incMoment) {
+ moment.increment(d);
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public double getResult() {
+ double kurtosis = Double.NaN;
+ if (moment.getN() > 3) {
+ double variance = moment.m2 / (moment.n - 1);
+ if (moment.n <= 3 || variance < 10E-20) {
+ kurtosis = 0.0;
+ } else {
+ double n = moment.n;
+ kurtosis =
+ (n * (n + 1) * moment.getResult() -
+ 3 * moment.m2 * moment.m2 * (n - 1)) /
+ ((n - 1) * (n -2) * (n -3) * variance * variance);
+ }
+ }
+ return kurtosis;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void clear() {
+ if (incMoment) {
+ moment.clear();
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public long getN() {
+ return moment.getN();
+ }
+
+ /* UnvariateStatistic Approach */
+
+ /**
+ * Returns the kurtosis of the entries in the specified portion of the
+ * input array.
+ * <p>
+ * See {@link Kurtosis} for details on the computing algorithm.</p>
+ * <p>
+ * Throws <code>IllegalArgumentException</code> if the array is null.</p>
+ *
+ * @param values the input array
+ * @param begin index of the first array element to include
+ * @param length the number of elements to include
+ * @return the kurtosis of the values or Double.NaN if length is less than 4
+ * @throws MathIllegalArgumentException if the input array is null or the array
+ * index parameters are not valid
+ */
+ @Override
+ public double evaluate(final double[] values,final int begin, final int length)
+ throws MathIllegalArgumentException {
+ // Initialize the kurtosis
+ double kurt = Double.NaN;
+
+ if (test(values, begin, length) && length > 3) {
+
+ // Compute the mean and standard deviation
+ Variance variance = new Variance();
+ variance.incrementAll(values, begin, length);
+ double mean = variance.moment.m1;
+ double stdDev = FastMath.sqrt(variance.getResult());
+
+ // Sum the ^4 of the distance from the mean divided by the
+ // standard deviation
+ double accum3 = 0.0;
+ for (int i = begin; i < begin + length; i++) {
+ accum3 += FastMath.pow(values[i] - mean, 4.0);
+ }
+ accum3 /= FastMath.pow(stdDev, 4.0d);
+
+ // Get N
+ double n0 = length;
+
+ double coefficientOne =
+ (n0 * (n0 + 1)) / ((n0 - 1) * (n0 - 2) * (n0 - 3));
+ double termTwo =
+ (3 * FastMath.pow(n0 - 1, 2.0)) / ((n0 - 2) * (n0 - 3));
+
+ // Calculate kurtosis
+ kurt = (coefficientOne * accum3) - termTwo;
+ }
+ return kurt;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public Kurtosis copy() {
+ Kurtosis result = new Kurtosis();
+ // No try-catch because args are guaranteed non-null
+ copy(this, result);
+ return result;
+ }
+
+ /**
+ * Copies source to dest.
+ * <p>Neither source nor dest can be null.</p>
+ *
+ * @param source Kurtosis to copy
+ * @param dest Kurtosis to copy to
+ * @throws NullArgumentException if either source or dest is null
+ */
+ public static void copy(Kurtosis source, Kurtosis dest)
+ throws NullArgumentException {
+ MathUtils.checkNotNull(source);
+ MathUtils.checkNotNull(dest);
+ dest.setData(source.getDataRef());
+ dest.moment = source.moment.copy();
+ dest.incMoment = source.incMoment;
+ }
+
+}
diff --git a/src/main/java/org/apache/commons/math3/stat/descriptive/moment/Mean.java b/src/main/java/org/apache/commons/math3/stat/descriptive/moment/Mean.java
new file mode 100644
index 0000000..aac3d78
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/stat/descriptive/moment/Mean.java
@@ -0,0 +1,286 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math3.stat.descriptive.moment;
+
+import java.io.Serializable;
+
+import org.apache.commons.math3.exception.MathIllegalArgumentException;
+import org.apache.commons.math3.exception.NullArgumentException;
+import org.apache.commons.math3.stat.descriptive.AbstractStorelessUnivariateStatistic;
+import org.apache.commons.math3.stat.descriptive.WeightedEvaluation;
+import org.apache.commons.math3.stat.descriptive.summary.Sum;
+import org.apache.commons.math3.util.MathUtils;
+
+/**
+ * <p>Computes the arithmetic mean of a set of values. Uses the definitional
+ * formula:</p>
+ * <p>
+ * mean = sum(x_i) / n
+ * </p>
+ * <p>where <code>n</code> is the number of observations.
+ * </p>
+ * <p>When {@link #increment(double)} is used to add data incrementally from a
+ * stream of (unstored) values, the value of the statistic that
+ * {@link #getResult()} returns is computed using the following recursive
+ * updating algorithm: </p>
+ * <ol>
+ * <li>Initialize <code>m = </code> the first value</li>
+ * <li>For each additional value, update using <br>
+ * <code>m = m + (new value - m) / (number of observations)</code></li>
+ * </ol>
+ * <p> If {@link #evaluate(double[])} is used to compute the mean of an array
+ * of stored values, a two-pass, corrected algorithm is used, starting with
+ * the definitional formula computed using the array of stored values and then
+ * correcting this by adding the mean deviation of the data values from the
+ * arithmetic mean. See, e.g. "Comparison of Several Algorithms for Computing
+ * Sample Means and Variances," Robert F. Ling, Journal of the American
+ * Statistical Association, Vol. 69, No. 348 (Dec., 1974), pp. 859-866. </p>
+ * <p>
+ * Returns <code>Double.NaN</code> if the dataset is empty. Note that
+ * Double.NaN may also be returned if the input includes NaN and / or infinite
+ * values.
+ * </p>
+ * <strong>Note that this implementation is not synchronized.</strong> If
+ * multiple threads access an instance of this class concurrently, and at least
+ * one of the threads invokes the <code>increment()</code> or
+ * <code>clear()</code> method, it must be synchronized externally.
+ *
+ */
+public class Mean extends AbstractStorelessUnivariateStatistic
+ implements Serializable, WeightedEvaluation {
+
+ /** Serializable version identifier */
+ private static final long serialVersionUID = -1296043746617791564L;
+
+ /** First moment on which this statistic is based. */
+ protected FirstMoment moment;
+
+ /**
+ * Determines whether or not this statistic can be incremented or cleared.
+ * <p>
+ * Statistics based on (constructed from) external moments cannot
+ * be incremented or cleared.</p>
+ */
+ protected boolean incMoment;
+
+ /** Constructs a Mean. */
+ public Mean() {
+ incMoment = true;
+ moment = new FirstMoment();
+ }
+
+ /**
+ * Constructs a Mean with an External Moment.
+ *
+ * @param m1 the moment
+ */
+ public Mean(final FirstMoment m1) {
+ this.moment = m1;
+ incMoment = false;
+ }
+
+ /**
+ * Copy constructor, creates a new {@code Mean} identical
+ * to the {@code original}
+ *
+ * @param original the {@code Mean} instance to copy
+ * @throws NullArgumentException if original is null
+ */
+ public Mean(Mean original) throws NullArgumentException {
+ copy(original, this);
+ }
+
+ /**
+ * {@inheritDoc}
+ * <p>Note that when {@link #Mean(FirstMoment)} is used to
+ * create a Mean, this method does nothing. In that case, the
+ * FirstMoment should be incremented directly.</p>
+ */
+ @Override
+ public void increment(final double d) {
+ if (incMoment) {
+ moment.increment(d);
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void clear() {
+ if (incMoment) {
+ moment.clear();
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public double getResult() {
+ return moment.m1;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public long getN() {
+ return moment.getN();
+ }
+
+ /**
+ * Returns the arithmetic mean of the entries in the specified portion of
+ * the input array, or <code>Double.NaN</code> if the designated subarray
+ * is empty.
+ * <p>
+ * Throws <code>IllegalArgumentException</code> if the array is null.</p>
+ * <p>
+ * See {@link Mean} for details on the computing algorithm.</p>
+ *
+ * @param values the input array
+ * @param begin index of the first array element to include
+ * @param length the number of elements to include
+ * @return the mean of the values or Double.NaN if length = 0
+ * @throws MathIllegalArgumentException if the array is null or the array index
+ * parameters are not valid
+ */
+ @Override
+ public double evaluate(final double[] values,final int begin, final int length)
+ throws MathIllegalArgumentException {
+ if (test(values, begin, length)) {
+ Sum sum = new Sum();
+ double sampleSize = length;
+
+ // Compute initial estimate using definitional formula
+ double xbar = sum.evaluate(values, begin, length) / sampleSize;
+
+ // Compute correction factor in second pass
+ double correction = 0;
+ for (int i = begin; i < begin + length; i++) {
+ correction += values[i] - xbar;
+ }
+ return xbar + (correction/sampleSize);
+ }
+ return Double.NaN;
+ }
+
+ /**
+ * Returns the weighted arithmetic mean of the entries in the specified portion of
+ * the input array, or <code>Double.NaN</code> if the designated subarray
+ * is empty.
+ * <p>
+ * Throws <code>IllegalArgumentException</code> if either array is null.</p>
+ * <p>
+ * See {@link Mean} for details on the computing algorithm. The two-pass algorithm
+ * described above is used here, with weights applied in computing both the original
+ * estimate and the correction factor.</p>
+ * <p>
+ * Throws <code>IllegalArgumentException</code> if any of the following are true:
+ * <ul><li>the values array is null</li>
+ * <li>the weights array is null</li>
+ * <li>the weights array does not have the same length as the values array</li>
+ * <li>the weights array contains one or more infinite values</li>
+ * <li>the weights array contains one or more NaN values</li>
+ * <li>the weights array contains negative values</li>
+ * <li>the start and length arguments do not determine a valid array</li>
+ * </ul></p>
+ *
+ * @param values the input array
+ * @param weights the weights array
+ * @param begin index of the first array element to include
+ * @param length the number of elements to include
+ * @return the mean of the values or Double.NaN if length = 0
+ * @throws MathIllegalArgumentException if the parameters are not valid
+ * @since 2.1
+ */
+ public double evaluate(final double[] values, final double[] weights,
+ final int begin, final int length) throws MathIllegalArgumentException {
+ if (test(values, weights, begin, length)) {
+ Sum sum = new Sum();
+
+ // Compute initial estimate using definitional formula
+ double sumw = sum.evaluate(weights,begin,length);
+ double xbarw = sum.evaluate(values, weights, begin, length) / sumw;
+
+ // Compute correction factor in second pass
+ double correction = 0;
+ for (int i = begin; i < begin + length; i++) {
+ correction += weights[i] * (values[i] - xbarw);
+ }
+ return xbarw + (correction/sumw);
+ }
+ return Double.NaN;
+ }
+
+ /**
+ * Returns the weighted arithmetic mean of the entries in the input array.
+ * <p>
+ * Throws <code>MathIllegalArgumentException</code> if either array is null.</p>
+ * <p>
+ * See {@link Mean} for details on the computing algorithm. The two-pass algorithm
+ * described above is used here, with weights applied in computing both the original
+ * estimate and the correction factor.</p>
+ * <p>
+ * Throws <code>MathIllegalArgumentException</code> if any of the following are true:
+ * <ul><li>the values array is null</li>
+ * <li>the weights array is null</li>
+ * <li>the weights array does not have the same length as the values array</li>
+ * <li>the weights array contains one or more infinite values</li>
+ * <li>the weights array contains one or more NaN values</li>
+ * <li>the weights array contains negative values</li>
+ * </ul></p>
+ *
+ * @param values the input array
+ * @param weights the weights array
+ * @return the mean of the values or Double.NaN if length = 0
+ * @throws MathIllegalArgumentException if the parameters are not valid
+ * @since 2.1
+ */
+ public double evaluate(final double[] values, final double[] weights)
+ throws MathIllegalArgumentException {
+ return evaluate(values, weights, 0, values.length);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public Mean copy() {
+ Mean result = new Mean();
+ // No try-catch or advertised exception because args are guaranteed non-null
+ copy(this, result);
+ return result;
+ }
+
+
+ /**
+ * Copies source to dest.
+ * <p>Neither source nor dest can be null.</p>
+ *
+ * @param source Mean to copy
+ * @param dest Mean to copy to
+ * @throws NullArgumentException if either source or dest is null
+ */
+ public static void copy(Mean source, Mean dest)
+ throws NullArgumentException {
+ MathUtils.checkNotNull(source);
+ MathUtils.checkNotNull(dest);
+ dest.setData(source.getDataRef());
+ dest.incMoment = source.incMoment;
+ dest.moment = source.moment.copy();
+ }
+}
diff --git a/src/main/java/org/apache/commons/math3/stat/descriptive/moment/SecondMoment.java b/src/main/java/org/apache/commons/math3/stat/descriptive/moment/SecondMoment.java
new file mode 100644
index 0000000..12715c0
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/stat/descriptive/moment/SecondMoment.java
@@ -0,0 +1,134 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math3.stat.descriptive.moment;
+
+import java.io.Serializable;
+
+import org.apache.commons.math3.exception.NullArgumentException;
+import org.apache.commons.math3.util.MathUtils;
+
+/**
+ * Computes a statistic related to the Second Central Moment. Specifically,
+ * what is computed is the sum of squared deviations from the sample mean.
+ * <p>
+ * The following recursive updating formula is used:</p>
+ * <p>
+ * Let <ul>
+ * <li> dev = (current obs - previous mean) </li>
+ * <li> n = number of observations (including current obs) </li>
+ * </ul>
+ * Then</p>
+ * <p>
+ * new value = old value + dev^2 * (n -1) / n.</p>
+ * <p>
+ * Returns <code>Double.NaN</code> if no data values have been added and
+ * returns <code>0</code> if there is just one value in the data set.
+ * Note that Double.NaN may also be returned if the input includes NaN
+ * and / or infinite values.</p>
+ * <p>
+ * <strong>Note that this implementation is not synchronized.</strong> If
+ * multiple threads access an instance of this class concurrently, and at least
+ * one of the threads invokes the <code>increment()</code> or
+ * <code>clear()</code> method, it must be synchronized externally.</p>
+ *
+ */
+public class SecondMoment extends FirstMoment implements Serializable {
+
+ /** Serializable version identifier */
+ private static final long serialVersionUID = 3942403127395076445L;
+
+ /** second moment of values that have been added */
+ protected double m2;
+
+ /**
+ * Create a SecondMoment instance
+ */
+ public SecondMoment() {
+ super();
+ m2 = Double.NaN;
+ }
+
+ /**
+ * Copy constructor, creates a new {@code SecondMoment} identical
+ * to the {@code original}
+ *
+ * @param original the {@code SecondMoment} instance to copy
+ * @throws NullArgumentException if original is null
+ */
+ public SecondMoment(SecondMoment original)
+ throws NullArgumentException {
+ super(original);
+ this.m2 = original.m2;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void increment(final double d) {
+ if (n < 1) {
+ m1 = m2 = 0.0;
+ }
+ super.increment(d);
+ m2 += ((double) n - 1) * dev * nDev;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void clear() {
+ super.clear();
+ m2 = Double.NaN;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public double getResult() {
+ return m2;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public SecondMoment copy() {
+ SecondMoment result = new SecondMoment();
+ // no try-catch or advertised NAE because args are guaranteed non-null
+ copy(this, result);
+ return result;
+ }
+
+ /**
+ * Copies source to dest.
+ * <p>Neither source nor dest can be null.</p>
+ *
+ * @param source SecondMoment to copy
+ * @param dest SecondMoment to copy to
+ * @throws NullArgumentException if either source or dest is null
+ */
+ public static void copy(SecondMoment source, SecondMoment dest)
+ throws NullArgumentException {
+ MathUtils.checkNotNull(source);
+ MathUtils.checkNotNull(dest);
+ FirstMoment.copy(source, dest);
+ dest.m2 = source.m2;
+ }
+
+}
diff --git a/src/main/java/org/apache/commons/math3/stat/descriptive/moment/SemiVariance.java b/src/main/java/org/apache/commons/math3/stat/descriptive/moment/SemiVariance.java
new file mode 100644
index 0000000..563119a
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/stat/descriptive/moment/SemiVariance.java
@@ -0,0 +1,369 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math3.stat.descriptive.moment;
+
+import java.io.Serializable;
+
+import org.apache.commons.math3.exception.MathIllegalArgumentException;
+import org.apache.commons.math3.exception.NullArgumentException;
+import org.apache.commons.math3.stat.descriptive.AbstractUnivariateStatistic;
+import org.apache.commons.math3.util.MathUtils;
+
+/**
+ * <p>Computes the semivariance of a set of values with respect to a given cutoff value.
+ * We define the <i>downside semivariance</i> of a set of values <code>x</code>
+ * against the <i>cutoff value</i> <code>cutoff</code> to be <br/>
+ * <code>&Sigma; (x[i] - target)<sup>2</sup> / df</code> <br/>
+ * where the sum is taken over all <code>i</code> such that <code>x[i] < cutoff</code>
+ * and <code>df</code> is the length of <code>x</code> (non-bias-corrected) or
+ * one less than this number (bias corrected). The <i>upside semivariance</i>
+ * is defined similarly, with the sum taken over values of <code>x</code> that
+ * exceed the cutoff value.</p>
+ *
+ * <p>The cutoff value defaults to the mean, bias correction defaults to <code>true</code>
+ * and the "variance direction" (upside or downside) defaults to downside. The variance direction
+ * and bias correction may be set using property setters or their values can provided as
+ * parameters to {@link #evaluate(double[], double, Direction, boolean, int, int)}.</p>
+ *
+ * <p>If the input array is null, <code>evaluate</code> methods throw
+ * <code>IllegalArgumentException.</code> If the array has length 1, <code>0</code>
+ * is returned, regardless of the value of the <code>cutoff.</code>
+ *
+ * <p><strong>Note that this class is not intended to be threadsafe.</strong> If
+ * multiple threads access an instance of this class concurrently, and one or
+ * more of these threads invoke property setters, external synchronization must
+ * be provided to ensure correct results.</p>
+ *
+ * @since 2.1
+ */
+public class SemiVariance extends AbstractUnivariateStatistic implements Serializable {
+
+ /**
+ * The UPSIDE Direction is used to specify that the observations above the
+ * cutoff point will be used to calculate SemiVariance.
+ */
+ public static final Direction UPSIDE_VARIANCE = Direction.UPSIDE;
+
+ /**
+ * The DOWNSIDE Direction is used to specify that the observations below
+ * the cutoff point will be used to calculate SemiVariance
+ */
+ public static final Direction DOWNSIDE_VARIANCE = Direction.DOWNSIDE;
+
+ /** Serializable version identifier */
+ private static final long serialVersionUID = -2653430366886024994L;
+
+ /**
+ * Determines whether or not bias correction is applied when computing the
+ * value of the statisic. True means that bias is corrected.
+ */
+ private boolean biasCorrected = true;
+
+ /**
+ * Determines whether to calculate downside or upside SemiVariance.
+ */
+ private Direction varianceDirection = Direction.DOWNSIDE;
+
+ /**
+ * Constructs a SemiVariance with default (true) <code>biasCorrected</code>
+ * property and default (Downside) <code>varianceDirection</code> property.
+ */
+ public SemiVariance() {
+ }
+
+ /**
+ * Constructs a SemiVariance with the specified <code>biasCorrected</code>
+ * property and default (Downside) <code>varianceDirection</code> property.
+ *
+ * @param biasCorrected setting for bias correction - true means
+ * bias will be corrected and is equivalent to using the argumentless
+ * constructor
+ */
+ public SemiVariance(final boolean biasCorrected) {
+ this.biasCorrected = biasCorrected;
+ }
+
+
+ /**
+ * Constructs a SemiVariance with the specified <code>Direction</code> property
+ * and default (true) <code>biasCorrected</code> property
+ *
+ * @param direction setting for the direction of the SemiVariance
+ * to calculate
+ */
+ public SemiVariance(final Direction direction) {
+ this.varianceDirection = direction;
+ }
+
+
+ /**
+ * Constructs a SemiVariance with the specified <code>isBiasCorrected</code>
+ * property and the specified <code>Direction</code> property.
+ *
+ * @param corrected setting for bias correction - true means
+ * bias will be corrected and is equivalent to using the argumentless
+ * constructor
+ *
+ * @param direction setting for the direction of the SemiVariance
+ * to calculate
+ */
+ public SemiVariance(final boolean corrected, final Direction direction) {
+ this.biasCorrected = corrected;
+ this.varianceDirection = direction;
+ }
+
+
+ /**
+ * Copy constructor, creates a new {@code SemiVariance} identical
+ * to the {@code original}
+ *
+ * @param original the {@code SemiVariance} instance to copy
+ * @throws NullArgumentException if original is null
+ */
+ public SemiVariance(final SemiVariance original) throws NullArgumentException {
+ copy(original, this);
+ }
+
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public SemiVariance copy() {
+ SemiVariance result = new SemiVariance();
+ // No try-catch or advertised exception because args are guaranteed non-null
+ copy(this, result);
+ return result;
+ }
+
+
+ /**
+ * Copies source to dest.
+ * <p>Neither source nor dest can be null.</p>
+ *
+ * @param source SemiVariance to copy
+ * @param dest SemiVariance to copy to
+ * @throws NullArgumentException if either source or dest is null
+ */
+ public static void copy(final SemiVariance source, SemiVariance dest)
+ throws NullArgumentException {
+ MathUtils.checkNotNull(source);
+ MathUtils.checkNotNull(dest);
+ dest.setData(source.getDataRef());
+ dest.biasCorrected = source.biasCorrected;
+ dest.varianceDirection = source.varianceDirection;
+ }
+
+ /**
+ * <p>Returns the {@link SemiVariance} of the designated values against the mean, using
+ * instance properties varianceDirection and biasCorrection.</p>
+ *
+ * <p>Returns <code>NaN</code> if the array is empty and throws
+ * <code>IllegalArgumentException</code> if the array is null.</p>
+ *
+ * @param values the input array
+ * @param start index of the first array element to include
+ * @param length the number of elements to include
+ * @return the SemiVariance
+ * @throws MathIllegalArgumentException if the parameters are not valid
+ *
+ */
+ @Override
+ public double evaluate(final double[] values, final int start, final int length)
+ throws MathIllegalArgumentException {
+ double m = (new Mean()).evaluate(values, start, length);
+ return evaluate(values, m, varianceDirection, biasCorrected, 0, values.length);
+ }
+
+
+ /**
+ * This method calculates {@link SemiVariance} for the entire array against the mean, using
+ * the current value of the biasCorrection instance property.
+ *
+ * @param values the input array
+ * @param direction the {@link Direction} of the semivariance
+ * @return the SemiVariance
+ * @throws MathIllegalArgumentException if values is null
+ *
+ */
+ public double evaluate(final double[] values, Direction direction)
+ throws MathIllegalArgumentException {
+ double m = (new Mean()).evaluate(values);
+ return evaluate (values, m, direction, biasCorrected, 0, values.length);
+ }
+
+ /**
+ * <p>Returns the {@link SemiVariance} of the designated values against the cutoff, using
+ * instance properties variancDirection and biasCorrection.</p>
+ *
+ * <p>Returns <code>NaN</code> if the array is empty and throws
+ * <code>MathIllegalArgumentException</code> if the array is null.</p>
+ *
+ * @param values the input array
+ * @param cutoff the reference point
+ * @return the SemiVariance
+ * @throws MathIllegalArgumentException if values is null
+ */
+ public double evaluate(final double[] values, final double cutoff)
+ throws MathIllegalArgumentException {
+ return evaluate(values, cutoff, varianceDirection, biasCorrected, 0, values.length);
+ }
+
+ /**
+ * <p>Returns the {@link SemiVariance} of the designated values against the cutoff in the
+ * given direction, using the current value of the biasCorrection instance property.</p>
+ *
+ * <p>Returns <code>NaN</code> if the array is empty and throws
+ * <code>MathIllegalArgumentException</code> if the array is null.</p>
+ *
+ * @param values the input array
+ * @param cutoff the reference point
+ * @param direction the {@link Direction} of the semivariance
+ * @return the SemiVariance
+ * @throws MathIllegalArgumentException if values is null
+ */
+ public double evaluate(final double[] values, final double cutoff, final Direction direction)
+ throws MathIllegalArgumentException {
+ return evaluate(values, cutoff, direction, biasCorrected, 0, values.length);
+ }
+
+
+ /**
+ * <p>Returns the {@link SemiVariance} of the designated values against the cutoff
+ * in the given direction with the provided bias correction.</p>
+ *
+ * <p>Returns <code>NaN</code> if the array is empty and throws
+ * <code>IllegalArgumentException</code> if the array is null.</p>
+ *
+ * @param values the input array
+ * @param cutoff the reference point
+ * @param direction the {@link Direction} of the semivariance
+ * @param corrected the BiasCorrection flag
+ * @param start index of the first array element to include
+ * @param length the number of elements to include
+ * @return the SemiVariance
+ * @throws MathIllegalArgumentException if the parameters are not valid
+ *
+ */
+ public double evaluate (final double[] values, final double cutoff, final Direction direction,
+ final boolean corrected, final int start, final int length) throws MathIllegalArgumentException {
+
+ test(values, start, length);
+ if (values.length == 0) {
+ return Double.NaN;
+ } else {
+ if (values.length == 1) {
+ return 0.0;
+ } else {
+ final boolean booleanDirection = direction.getDirection();
+
+ double dev = 0.0;
+ double sumsq = 0.0;
+ for (int i = start; i < length; i++) {
+ if ((values[i] > cutoff) == booleanDirection) {
+ dev = values[i] - cutoff;
+ sumsq += dev * dev;
+ }
+ }
+
+ if (corrected) {
+ return sumsq / (length - 1.0);
+ } else {
+ return sumsq / length;
+ }
+ }
+ }
+ }
+
+ /**
+ * Returns true iff biasCorrected property is set to true.
+ *
+ * @return the value of biasCorrected.
+ */
+ public boolean isBiasCorrected() {
+ return biasCorrected;
+ }
+
+ /**
+ * Sets the biasCorrected property.
+ *
+ * @param biasCorrected new biasCorrected property value
+ */
+ public void setBiasCorrected(boolean biasCorrected) {
+ this.biasCorrected = biasCorrected;
+ }
+
+ /**
+ * Returns the varianceDirection property.
+ *
+ * @return the varianceDirection
+ */
+ public Direction getVarianceDirection () {
+ return varianceDirection;
+ }
+
+ /**
+ * Sets the variance direction
+ *
+ * @param varianceDirection the direction of the semivariance
+ */
+ public void setVarianceDirection(Direction varianceDirection) {
+ this.varianceDirection = varianceDirection;
+ }
+
+ /**
+ * The direction of the semivariance - either upside or downside. The direction
+ * is represented by boolean, with true corresponding to UPSIDE semivariance.
+ */
+ public enum Direction {
+ /**
+ * The UPSIDE Direction is used to specify that the observations above the
+ * cutoff point will be used to calculate SemiVariance
+ */
+ UPSIDE (true),
+
+ /**
+ * The DOWNSIDE Direction is used to specify that the observations below
+ * the cutoff point will be used to calculate SemiVariance
+ */
+ DOWNSIDE (false);
+
+ /**
+ * boolean value UPSIDE <-> true
+ */
+ private boolean direction;
+
+ /**
+ * Create a Direction with the given value.
+ *
+ * @param b boolean value representing the Direction. True corresponds to UPSIDE.
+ */
+ Direction (boolean b) {
+ direction = b;
+ }
+
+ /**
+ * Returns the value of this Direction. True corresponds to UPSIDE.
+ *
+ * @return true if direction is UPSIDE; false otherwise
+ */
+ boolean getDirection () {
+ return direction;
+ }
+ }
+}
diff --git a/src/main/java/org/apache/commons/math3/stat/descriptive/moment/Skewness.java b/src/main/java/org/apache/commons/math3/stat/descriptive/moment/Skewness.java
new file mode 100644
index 0000000..b4703eb
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/stat/descriptive/moment/Skewness.java
@@ -0,0 +1,228 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math3.stat.descriptive.moment;
+
+import java.io.Serializable;
+
+import org.apache.commons.math3.exception.MathIllegalArgumentException;
+import org.apache.commons.math3.exception.NullArgumentException;
+import org.apache.commons.math3.stat.descriptive.AbstractStorelessUnivariateStatistic;
+import org.apache.commons.math3.util.FastMath;
+import org.apache.commons.math3.util.MathUtils;
+
+/**
+ * Computes the skewness of the available values.
+ * <p>
+ * We use the following (unbiased) formula to define skewness:</p>
+ * <p>
+ * skewness = [n / (n -1) (n - 2)] sum[(x_i - mean)^3] / std^3 </p>
+ * <p>
+ * where n is the number of values, mean is the {@link Mean} and std is the
+ * {@link StandardDeviation} </p>
+ * <p>
+ * Note that this statistic is undefined for n < 3. <code>Double.Nan</code>
+ * is returned when there is not sufficient data to compute the statistic.
+ * Double.NaN may also be returned if the input includes NaN and / or
+ * infinite values.</p>
+ * <p>
+ * <strong>Note that this implementation is not synchronized.</strong> If
+ * multiple threads access an instance of this class concurrently, and at least
+ * one of the threads invokes the <code>increment()</code> or
+ * <code>clear()</code> method, it must be synchronized externally. </p>
+ *
+ */
+public class Skewness extends AbstractStorelessUnivariateStatistic implements Serializable {
+
+ /** Serializable version identifier */
+ private static final long serialVersionUID = 7101857578996691352L;
+
+ /** Third moment on which this statistic is based */
+ protected ThirdMoment moment = null;
+
+ /**
+ * Determines whether or not this statistic can be incremented or cleared.
+ * <p>
+ * Statistics based on (constructed from) external moments cannot
+ * be incremented or cleared.</p>
+ */
+ protected boolean incMoment;
+
+ /**
+ * Constructs a Skewness
+ */
+ public Skewness() {
+ incMoment = true;
+ moment = new ThirdMoment();
+ }
+
+ /**
+ * Constructs a Skewness with an external moment
+ * @param m3 external moment
+ */
+ public Skewness(final ThirdMoment m3) {
+ incMoment = false;
+ this.moment = m3;
+ }
+
+ /**
+ * Copy constructor, creates a new {@code Skewness} identical
+ * to the {@code original}
+ *
+ * @param original the {@code Skewness} instance to copy
+ * @throws NullArgumentException if original is null
+ */
+ public Skewness(Skewness original) throws NullArgumentException {
+ copy(original, this);
+ }
+
+ /**
+ * {@inheritDoc}
+ * <p>Note that when {@link #Skewness(ThirdMoment)} is used to
+ * create a Skewness, this method does nothing. In that case, the
+ * ThirdMoment should be incremented directly.</p>
+ */
+ @Override
+ public void increment(final double d) {
+ if (incMoment) {
+ moment.increment(d);
+ }
+ }
+
+ /**
+ * Returns the value of the statistic based on the values that have been added.
+ * <p>
+ * See {@link Skewness} for the definition used in the computation.</p>
+ *
+ * @return the skewness of the available values.
+ */
+ @Override
+ public double getResult() {
+
+ if (moment.n < 3) {
+ return Double.NaN;
+ }
+ double variance = moment.m2 / (moment.n - 1);
+ if (variance < 10E-20) {
+ return 0.0d;
+ } else {
+ double n0 = moment.getN();
+ return (n0 * moment.m3) /
+ ((n0 - 1) * (n0 -2) * FastMath.sqrt(variance) * variance);
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public long getN() {
+ return moment.getN();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void clear() {
+ if (incMoment) {
+ moment.clear();
+ }
+ }
+
+ /**
+ * Returns the Skewness of the entries in the specifed portion of the
+ * input array.
+ * <p>
+ * See {@link Skewness} for the definition used in the computation.</p>
+ * <p>
+ * Throws <code>IllegalArgumentException</code> if the array is null.</p>
+ *
+ * @param values the input array
+ * @param begin the index of the first array element to include
+ * @param length the number of elements to include
+ * @return the skewness of the values or Double.NaN if length is less than
+ * 3
+ * @throws MathIllegalArgumentException if the array is null or the array index
+ * parameters are not valid
+ */
+ @Override
+ public double evaluate(final double[] values,final int begin,
+ final int length) throws MathIllegalArgumentException {
+
+ // Initialize the skewness
+ double skew = Double.NaN;
+
+ if (test(values, begin, length) && length > 2 ){
+ Mean mean = new Mean();
+ // Get the mean and the standard deviation
+ double m = mean.evaluate(values, begin, length);
+
+ // Calc the std, this is implemented here instead
+ // of using the standardDeviation method eliminate
+ // a duplicate pass to get the mean
+ double accum = 0.0;
+ double accum2 = 0.0;
+ for (int i = begin; i < begin + length; i++) {
+ final double d = values[i] - m;
+ accum += d * d;
+ accum2 += d;
+ }
+ final double variance = (accum - (accum2 * accum2 / length)) / (length - 1);
+
+ double accum3 = 0.0;
+ for (int i = begin; i < begin + length; i++) {
+ final double d = values[i] - m;
+ accum3 += d * d * d;
+ }
+ accum3 /= variance * FastMath.sqrt(variance);
+
+ // Get N
+ double n0 = length;
+
+ // Calculate skewness
+ skew = (n0 / ((n0 - 1) * (n0 - 2))) * accum3;
+ }
+ return skew;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public Skewness copy() {
+ Skewness result = new Skewness();
+ // No try-catch or advertised exception because args are guaranteed non-null
+ copy(this, result);
+ return result;
+ }
+
+ /**
+ * Copies source to dest.
+ * <p>Neither source nor dest can be null.</p>
+ *
+ * @param source Skewness to copy
+ * @param dest Skewness to copy to
+ * @throws NullArgumentException if either source or dest is null
+ */
+ public static void copy(Skewness source, Skewness dest)
+ throws NullArgumentException {
+ MathUtils.checkNotNull(source);
+ MathUtils.checkNotNull(dest);
+ dest.setData(source.getDataRef());
+ dest.moment = new ThirdMoment(source.moment.copy());
+ dest.incMoment = source.incMoment;
+ }
+}
diff --git a/src/main/java/org/apache/commons/math3/stat/descriptive/moment/StandardDeviation.java b/src/main/java/org/apache/commons/math3/stat/descriptive/moment/StandardDeviation.java
new file mode 100644
index 0000000..a6248c5
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/stat/descriptive/moment/StandardDeviation.java
@@ -0,0 +1,280 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math3.stat.descriptive.moment;
+
+import java.io.Serializable;
+
+import org.apache.commons.math3.exception.MathIllegalArgumentException;
+import org.apache.commons.math3.exception.NullArgumentException;
+import org.apache.commons.math3.stat.descriptive.AbstractStorelessUnivariateStatistic;
+import org.apache.commons.math3.util.FastMath;
+import org.apache.commons.math3.util.MathUtils;
+
+/**
+ * Computes the sample standard deviation. The standard deviation
+ * is the positive square root of the variance. This implementation wraps a
+ * {@link Variance} instance. The <code>isBiasCorrected</code> property of the
+ * wrapped Variance instance is exposed, so that this class can be used to
+ * compute both the "sample standard deviation" (the square root of the
+ * bias-corrected "sample variance") or the "population standard deviation"
+ * (the square root of the non-bias-corrected "population variance"). See
+ * {@link Variance} for more information.
+ * <p>
+ * <strong>Note that this implementation is not synchronized.</strong> If
+ * multiple threads access an instance of this class concurrently, and at least
+ * one of the threads invokes the <code>increment()</code> or
+ * <code>clear()</code> method, it must be synchronized externally.</p>
+ *
+ */
+public class StandardDeviation extends AbstractStorelessUnivariateStatistic
+ implements Serializable {
+
+ /** Serializable version identifier */
+ private static final long serialVersionUID = 5728716329662425188L;
+
+ /** Wrapped Variance instance */
+ private Variance variance = null;
+
+ /**
+ * Constructs a StandardDeviation. Sets the underlying {@link Variance}
+ * instance's <code>isBiasCorrected</code> property to true.
+ */
+ public StandardDeviation() {
+ variance = new Variance();
+ }
+
+ /**
+ * Constructs a StandardDeviation from an external second moment.
+ *
+ * @param m2 the external moment
+ */
+ public StandardDeviation(final SecondMoment m2) {
+ variance = new Variance(m2);
+ }
+
+ /**
+ * Copy constructor, creates a new {@code StandardDeviation} identical
+ * to the {@code original}
+ *
+ * @param original the {@code StandardDeviation} instance to copy
+ * @throws NullArgumentException if original is null
+ */
+ public StandardDeviation(StandardDeviation original) throws NullArgumentException {
+ copy(original, this);
+ }
+
+ /**
+ * Contructs a StandardDeviation with the specified value for the
+ * <code>isBiasCorrected</code> property. If this property is set to
+ * <code>true</code>, the {@link Variance} used in computing results will
+ * use the bias-corrected, or "sample" formula. See {@link Variance} for
+ * details.
+ *
+ * @param isBiasCorrected whether or not the variance computation will use
+ * the bias-corrected formula
+ */
+ public StandardDeviation(boolean isBiasCorrected) {
+ variance = new Variance(isBiasCorrected);
+ }
+
+ /**
+ * Contructs a StandardDeviation with the specified value for the
+ * <code>isBiasCorrected</code> property and the supplied external moment.
+ * If <code>isBiasCorrected</code> is set to <code>true</code>, the
+ * {@link Variance} used in computing results will use the bias-corrected,
+ * or "sample" formula. See {@link Variance} for details.
+ *
+ * @param isBiasCorrected whether or not the variance computation will use
+ * the bias-corrected formula
+ * @param m2 the external moment
+ */
+ public StandardDeviation(boolean isBiasCorrected, SecondMoment m2) {
+ variance = new Variance(isBiasCorrected, m2);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void increment(final double d) {
+ variance.increment(d);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public long getN() {
+ return variance.getN();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public double getResult() {
+ return FastMath.sqrt(variance.getResult());
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void clear() {
+ variance.clear();
+ }
+
+ /**
+ * Returns the Standard Deviation of the entries in the input array, or
+ * <code>Double.NaN</code> if the array is empty.
+ * <p>
+ * Returns 0 for a single-value (i.e. length = 1) sample.</p>
+ * <p>
+ * Throws <code>MathIllegalArgumentException</code> if the array is null.</p>
+ * <p>
+ * Does not change the internal state of the statistic.</p>
+ *
+ * @param values the input array
+ * @return the standard deviation of the values or Double.NaN if length = 0
+ * @throws MathIllegalArgumentException if the array is null
+ */
+ @Override
+ public double evaluate(final double[] values) throws MathIllegalArgumentException {
+ return FastMath.sqrt(variance.evaluate(values));
+ }
+
+ /**
+ * Returns the Standard Deviation of the entries in the specified portion of
+ * the input array, or <code>Double.NaN</code> if the designated subarray
+ * is empty.
+ * <p>
+ * Returns 0 for a single-value (i.e. length = 1) sample. </p>
+ * <p>
+ * Throws <code>MathIllegalArgumentException</code> if the array is null.</p>
+ * <p>
+ * Does not change the internal state of the statistic.</p>
+ *
+ * @param values the input array
+ * @param begin index of the first array element to include
+ * @param length the number of elements to include
+ * @return the standard deviation of the values or Double.NaN if length = 0
+ * @throws MathIllegalArgumentException if the array is null or the array index
+ * parameters are not valid
+ */
+ @Override
+ public double evaluate(final double[] values, final int begin, final int length)
+ throws MathIllegalArgumentException {
+ return FastMath.sqrt(variance.evaluate(values, begin, length));
+ }
+
+ /**
+ * Returns the Standard Deviation of the entries in the specified portion of
+ * the input array, using the precomputed mean value. Returns
+ * <code>Double.NaN</code> if the designated subarray is empty.
+ * <p>
+ * Returns 0 for a single-value (i.e. length = 1) sample.</p>
+ * <p>
+ * The formula used assumes that the supplied mean value is the arithmetic
+ * mean of the sample data, not a known population parameter. This method
+ * is supplied only to save computation when the mean has already been
+ * computed.</p>
+ * <p>
+ * Throws <code>IllegalArgumentException</code> if the array is null.</p>
+ * <p>
+ * Does not change the internal state of the statistic.</p>
+ *
+ * @param values the input array
+ * @param mean the precomputed mean value
+ * @param begin index of the first array element to include
+ * @param length the number of elements to include
+ * @return the standard deviation of the values or Double.NaN if length = 0
+ * @throws MathIllegalArgumentException if the array is null or the array index
+ * parameters are not valid
+ */
+ public double evaluate(final double[] values, final double mean,
+ final int begin, final int length) throws MathIllegalArgumentException {
+ return FastMath.sqrt(variance.evaluate(values, mean, begin, length));
+ }
+
+ /**
+ * Returns the Standard Deviation of the entries in the input array, using
+ * the precomputed mean value. Returns
+ * <code>Double.NaN</code> if the designated subarray is empty.
+ * <p>
+ * Returns 0 for a single-value (i.e. length = 1) sample.</p>
+ * <p>
+ * The formula used assumes that the supplied mean value is the arithmetic
+ * mean of the sample data, not a known population parameter. This method
+ * is supplied only to save computation when the mean has already been
+ * computed.</p>
+ * <p>
+ * Throws <code>MathIllegalArgumentException</code> if the array is null.</p>
+ * <p>
+ * Does not change the internal state of the statistic.</p>
+ *
+ * @param values the input array
+ * @param mean the precomputed mean value
+ * @return the standard deviation of the values or Double.NaN if length = 0
+ * @throws MathIllegalArgumentException if the array is null
+ */
+ public double evaluate(final double[] values, final double mean)
+ throws MathIllegalArgumentException {
+ return FastMath.sqrt(variance.evaluate(values, mean));
+ }
+
+ /**
+ * @return Returns the isBiasCorrected.
+ */
+ public boolean isBiasCorrected() {
+ return variance.isBiasCorrected();
+ }
+
+ /**
+ * @param isBiasCorrected The isBiasCorrected to set.
+ */
+ public void setBiasCorrected(boolean isBiasCorrected) {
+ variance.setBiasCorrected(isBiasCorrected);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public StandardDeviation copy() {
+ StandardDeviation result = new StandardDeviation();
+ // No try-catch or advertised exception because args are guaranteed non-null
+ copy(this, result);
+ return result;
+ }
+
+
+ /**
+ * Copies source to dest.
+ * <p>Neither source nor dest can be null.</p>
+ *
+ * @param source StandardDeviation to copy
+ * @param dest StandardDeviation to copy to
+ * @throws NullArgumentException if either source or dest is null
+ */
+ public static void copy(StandardDeviation source, StandardDeviation dest)
+ throws NullArgumentException {
+ MathUtils.checkNotNull(source);
+ MathUtils.checkNotNull(dest);
+ dest.setData(source.getDataRef());
+ dest.variance = source.variance.copy();
+ }
+
+}
diff --git a/src/main/java/org/apache/commons/math3/stat/descriptive/moment/ThirdMoment.java b/src/main/java/org/apache/commons/math3/stat/descriptive/moment/ThirdMoment.java
new file mode 100644
index 0000000..43a9ca1
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/stat/descriptive/moment/ThirdMoment.java
@@ -0,0 +1,148 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math3.stat.descriptive.moment;
+
+import java.io.Serializable;
+
+import org.apache.commons.math3.exception.NullArgumentException;
+import org.apache.commons.math3.util.MathUtils;
+
+
+/**
+ * Computes a statistic related to the Third Central Moment. Specifically,
+ * what is computed is the sum of cubed deviations from the sample mean.
+ * <p>
+ * The following recursive updating formula is used:</p>
+ * <p>
+ * Let <ul>
+ * <li> dev = (current obs - previous mean) </li>
+ * <li> m2 = previous value of {@link SecondMoment} </li>
+ * <li> n = number of observations (including current obs) </li>
+ * </ul>
+ * Then</p>
+ * <p>
+ * new value = old value - 3 * (dev/n) * m2 + (n-1) * (n -2) * (dev^3/n^2)</p>
+ * <p>
+ * Returns <code>Double.NaN</code> if no data values have been added and
+ * returns <code>0</code> if there is just one value in the data set.
+ * Note that Double.NaN may also be returned if the input includes NaN
+ * and / or infinite values.</p>
+ * <p>
+ * <strong>Note that this implementation is not synchronized.</strong> If
+ * multiple threads access an instance of this class concurrently, and at least
+ * one of the threads invokes the <code>increment()</code> or
+ * <code>clear()</code> method, it must be synchronized externally.</p>
+ *
+ */
+class ThirdMoment extends SecondMoment implements Serializable {
+
+ /** Serializable version identifier */
+ private static final long serialVersionUID = -7818711964045118679L;
+
+ /** third moment of values that have been added */
+ protected double m3;
+
+ /**
+ * Square of deviation of most recently added value from previous first
+ * moment, normalized by previous sample size. Retained to prevent
+ * repeated computation in higher order moments. nDevSq = nDev * nDev.
+ */
+ protected double nDevSq;
+
+ /**
+ * Create a FourthMoment instance
+ */
+ ThirdMoment() {
+ super();
+ m3 = Double.NaN;
+ nDevSq = Double.NaN;
+ }
+
+ /**
+ * Copy constructor, creates a new {@code ThirdMoment} identical
+ * to the {@code original}
+ *
+ * @param original the {@code ThirdMoment} instance to copy
+ * @throws NullArgumentException if orginal is null
+ */
+ ThirdMoment(ThirdMoment original) throws NullArgumentException {
+ copy(original, this);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void increment(final double d) {
+ if (n < 1) {
+ m3 = m2 = m1 = 0.0;
+ }
+
+ double prevM2 = m2;
+ super.increment(d);
+ nDevSq = nDev * nDev;
+ double n0 = n;
+ m3 = m3 - 3.0 * nDev * prevM2 + (n0 - 1) * (n0 - 2) * nDevSq * dev;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public double getResult() {
+ return m3;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void clear() {
+ super.clear();
+ m3 = Double.NaN;
+ nDevSq = Double.NaN;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public ThirdMoment copy() {
+ ThirdMoment result = new ThirdMoment();
+ // No try-catch or advertised exception because args are guaranteed non-null
+ copy(this, result);
+ return result;
+ }
+
+ /**
+ * Copies source to dest.
+ * <p>Neither source nor dest can be null.</p>
+ *
+ * @param source ThirdMoment to copy
+ * @param dest ThirdMoment to copy to
+ * @throws NullArgumentException if either source or dest is null
+ */
+ public static void copy(ThirdMoment source, ThirdMoment dest)
+ throws NullArgumentException {
+ MathUtils.checkNotNull(source);
+ MathUtils.checkNotNull(dest);
+ SecondMoment.copy(source, dest);
+ dest.m3 = source.m3;
+ dest.nDevSq = source.nDevSq;
+ }
+
+}
diff --git a/src/main/java/org/apache/commons/math3/stat/descriptive/moment/Variance.java b/src/main/java/org/apache/commons/math3/stat/descriptive/moment/Variance.java
new file mode 100644
index 0000000..1ba48e9
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/stat/descriptive/moment/Variance.java
@@ -0,0 +1,627 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math3.stat.descriptive.moment;
+
+import java.io.Serializable;
+
+import org.apache.commons.math3.exception.MathIllegalArgumentException;
+import org.apache.commons.math3.exception.NullArgumentException;
+import org.apache.commons.math3.exception.util.LocalizedFormats;
+import org.apache.commons.math3.stat.descriptive.WeightedEvaluation;
+import org.apache.commons.math3.stat.descriptive.AbstractStorelessUnivariateStatistic;
+import org.apache.commons.math3.util.MathUtils;
+
+/**
+ * Computes the variance of the available values. By default, the unbiased
+ * "sample variance" definitional formula is used:
+ * <p>
+ * variance = sum((x_i - mean)^2) / (n - 1) </p>
+ * <p>
+ * where mean is the {@link Mean} and <code>n</code> is the number
+ * of sample observations.</p>
+ * <p>
+ * The definitional formula does not have good numerical properties, so
+ * this implementation does not compute the statistic using the definitional
+ * formula. <ul>
+ * <li> The <code>getResult</code> method computes the variance using
+ * updating formulas based on West's algorithm, as described in
+ * <a href="http://doi.acm.org/10.1145/359146.359152"> Chan, T. F. and
+ * J. G. Lewis 1979, <i>Communications of the ACM</i>,
+ * vol. 22 no. 9, pp. 526-531.</a></li>
+ * <li> The <code>evaluate</code> methods leverage the fact that they have the
+ * full array of values in memory to execute a two-pass algorithm.
+ * Specifically, these methods use the "corrected two-pass algorithm" from
+ * Chan, Golub, Levesque, <i>Algorithms for Computing the Sample Variance</i>,
+ * American Statistician, vol. 37, no. 3 (1983) pp. 242-247.</li></ul>
+ * Note that adding values using <code>increment</code> or
+ * <code>incrementAll</code> and then executing <code>getResult</code> will
+ * sometimes give a different, less accurate, result than executing
+ * <code>evaluate</code> with the full array of values. The former approach
+ * should only be used when the full array of values is not available.</p>
+ * <p>
+ * The "population variance" ( sum((x_i - mean)^2) / n ) can also
+ * be computed using this statistic. The <code>isBiasCorrected</code>
+ * property determines whether the "population" or "sample" value is
+ * returned by the <code>evaluate</code> and <code>getResult</code> methods.
+ * To compute population variances, set this property to <code>false.</code>
+ * </p>
+ * <p>
+ * <strong>Note that this implementation is not synchronized.</strong> If
+ * multiple threads access an instance of this class concurrently, and at least
+ * one of the threads invokes the <code>increment()</code> or
+ * <code>clear()</code> method, it must be synchronized externally.</p>
+ *
+ */
+public class Variance extends AbstractStorelessUnivariateStatistic implements Serializable, WeightedEvaluation {
+
+ /** Serializable version identifier */
+ private static final long serialVersionUID = -9111962718267217978L;
+
+ /** SecondMoment is used in incremental calculation of Variance*/
+ protected SecondMoment moment = null;
+
+ /**
+ * Whether or not {@link #increment(double)} should increment
+ * the internal second moment. When a Variance is constructed with an
+ * external SecondMoment as a constructor parameter, this property is
+ * set to false and increments must be applied to the second moment
+ * directly.
+ */
+ protected boolean incMoment = true;
+
+ /**
+ * Whether or not bias correction is applied when computing the
+ * value of the statistic. True means that bias is corrected. See
+ * {@link Variance} for details on the formula.
+ */
+ private boolean isBiasCorrected = true;
+
+ /**
+ * Constructs a Variance with default (true) <code>isBiasCorrected</code>
+ * property.
+ */
+ public Variance() {
+ moment = new SecondMoment();
+ }
+
+ /**
+ * Constructs a Variance based on an external second moment.
+ * When this constructor is used, the statistic may only be
+ * incremented via the moment, i.e., {@link #increment(double)}
+ * does nothing; whereas {@code m2.increment(value)} increments
+ * both {@code m2} and the Variance instance constructed from it.
+ *
+ * @param m2 the SecondMoment (Third or Fourth moments work
+ * here as well.)
+ */
+ public Variance(final SecondMoment m2) {
+ incMoment = false;
+ this.moment = m2;
+ }
+
+ /**
+ * Constructs a Variance with the specified <code>isBiasCorrected</code>
+ * property
+ *
+ * @param isBiasCorrected setting for bias correction - true means
+ * bias will be corrected and is equivalent to using the argumentless
+ * constructor
+ */
+ public Variance(boolean isBiasCorrected) {
+ moment = new SecondMoment();
+ this.isBiasCorrected = isBiasCorrected;
+ }
+
+ /**
+ * Constructs a Variance with the specified <code>isBiasCorrected</code>
+ * property and the supplied external second moment.
+ *
+ * @param isBiasCorrected setting for bias correction - true means
+ * bias will be corrected
+ * @param m2 the SecondMoment (Third or Fourth moments work
+ * here as well.)
+ */
+ public Variance(boolean isBiasCorrected, SecondMoment m2) {
+ incMoment = false;
+ this.moment = m2;
+ this.isBiasCorrected = isBiasCorrected;
+ }
+
+ /**
+ * Copy constructor, creates a new {@code Variance} identical
+ * to the {@code original}
+ *
+ * @param original the {@code Variance} instance to copy
+ * @throws NullArgumentException if original is null
+ */
+ public Variance(Variance original) throws NullArgumentException {
+ copy(original, this);
+ }
+
+ /**
+ * {@inheritDoc}
+ * <p>If all values are available, it is more accurate to use
+ * {@link #evaluate(double[])} rather than adding values one at a time
+ * using this method and then executing {@link #getResult}, since
+ * <code>evaluate</code> leverages the fact that is has the full
+ * list of values together to execute a two-pass algorithm.
+ * See {@link Variance}.</p>
+ *
+ * <p>Note also that when {@link #Variance(SecondMoment)} is used to
+ * create a Variance, this method does nothing. In that case, the
+ * SecondMoment should be incremented directly.</p>
+ */
+ @Override
+ public void increment(final double d) {
+ if (incMoment) {
+ moment.increment(d);
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public double getResult() {
+ if (moment.n == 0) {
+ return Double.NaN;
+ } else if (moment.n == 1) {
+ return 0d;
+ } else {
+ if (isBiasCorrected) {
+ return moment.m2 / (moment.n - 1d);
+ } else {
+ return moment.m2 / (moment.n);
+ }
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public long getN() {
+ return moment.getN();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void clear() {
+ if (incMoment) {
+ moment.clear();
+ }
+ }
+
+ /**
+ * Returns the variance of the entries in the input array, or
+ * <code>Double.NaN</code> if the array is empty.
+ * <p>
+ * See {@link Variance} for details on the computing algorithm.</p>
+ * <p>
+ * Returns 0 for a single-value (i.e. length = 1) sample.</p>
+ * <p>
+ * Throws <code>MathIllegalArgumentException</code> if the array is null.</p>
+ * <p>
+ * Does not change the internal state of the statistic.</p>
+ *
+ * @param values the input array
+ * @return the variance of the values or Double.NaN if length = 0
+ * @throws MathIllegalArgumentException if the array is null
+ */
+ @Override
+ public double evaluate(final double[] values) throws MathIllegalArgumentException {
+ if (values == null) {
+ throw new NullArgumentException(LocalizedFormats.INPUT_ARRAY);
+ }
+ return evaluate(values, 0, values.length);
+ }
+
+ /**
+ * Returns the variance of the entries in the specified portion of
+ * the input array, or <code>Double.NaN</code> if the designated subarray
+ * is empty. Note that Double.NaN may also be returned if the input
+ * includes NaN and / or infinite values.
+ * <p>
+ * See {@link Variance} for details on the computing algorithm.</p>
+ * <p>
+ * Returns 0 for a single-value (i.e. length = 1) sample.</p>
+ * <p>
+ * Does not change the internal state of the statistic.</p>
+ * <p>
+ * Throws <code>MathIllegalArgumentException</code> if the array is null.</p>
+ *
+ * @param values the input array
+ * @param begin index of the first array element to include
+ * @param length the number of elements to include
+ * @return the variance of the values or Double.NaN if length = 0
+ * @throws MathIllegalArgumentException if the array is null or the array index
+ * parameters are not valid
+ */
+ @Override
+ public double evaluate(final double[] values, final int begin, final int length)
+ throws MathIllegalArgumentException {
+
+ double var = Double.NaN;
+
+ if (test(values, begin, length)) {
+ clear();
+ if (length == 1) {
+ var = 0.0;
+ } else if (length > 1) {
+ Mean mean = new Mean();
+ double m = mean.evaluate(values, begin, length);
+ var = evaluate(values, m, begin, length);
+ }
+ }
+ return var;
+ }
+
+ /**
+ * <p>Returns the weighted variance of the entries in the specified portion of
+ * the input array, or <code>Double.NaN</code> if the designated subarray
+ * is empty.</p>
+ * <p>
+ * Uses the formula <pre>
+ * &Sigma;(weights[i]*(values[i] - weightedMean)<sup>2</sup>)/(&Sigma;(weights[i]) - 1)
+ * </pre>
+ * where weightedMean is the weighted mean</p>
+ * <p>
+ * This formula will not return the same result as the unweighted variance when all
+ * weights are equal, unless all weights are equal to 1. The formula assumes that
+ * weights are to be treated as "expansion values," as will be the case if for example
+ * the weights represent frequency counts. To normalize weights so that the denominator
+ * in the variance computation equals the length of the input vector minus one, use <pre>
+ * <code>evaluate(values, MathArrays.normalizeArray(weights, values.length)); </code>
+ * </pre>
+ * <p>
+ * Returns 0 for a single-value (i.e. length = 1) sample.</p>
+ * <p>
+ * Throws <code>IllegalArgumentException</code> if any of the following are true:
+ * <ul><li>the values array is null</li>
+ * <li>the weights array is null</li>
+ * <li>the weights array does not have the same length as the values array</li>
+ * <li>the weights array contains one or more infinite values</li>
+ * <li>the weights array contains one or more NaN values</li>
+ * <li>the weights array contains negative values</li>
+ * <li>the start and length arguments do not determine a valid array</li>
+ * </ul></p>
+ * <p>
+ * Does not change the internal state of the statistic.</p>
+ * <p>
+ * Throws <code>MathIllegalArgumentException</code> if either array is null.</p>
+ *
+ * @param values the input array
+ * @param weights the weights array
+ * @param begin index of the first array element to include
+ * @param length the number of elements to include
+ * @return the weighted variance of the values or Double.NaN if length = 0
+ * @throws MathIllegalArgumentException if the parameters are not valid
+ * @since 2.1
+ */
+ public double evaluate(final double[] values, final double[] weights,
+ final int begin, final int length) throws MathIllegalArgumentException {
+
+ double var = Double.NaN;
+
+ if (test(values, weights,begin, length)) {
+ clear();
+ if (length == 1) {
+ var = 0.0;
+ } else if (length > 1) {
+ Mean mean = new Mean();
+ double m = mean.evaluate(values, weights, begin, length);
+ var = evaluate(values, weights, m, begin, length);
+ }
+ }
+ return var;
+ }
+
+ /**
+ * <p>
+ * Returns the weighted variance of the entries in the the input array.</p>
+ * <p>
+ * Uses the formula <pre>
+ * &Sigma;(weights[i]*(values[i] - weightedMean)<sup>2</sup>)/(&Sigma;(weights[i]) - 1)
+ * </pre>
+ * where weightedMean is the weighted mean</p>
+ * <p>
+ * This formula will not return the same result as the unweighted variance when all
+ * weights are equal, unless all weights are equal to 1. The formula assumes that
+ * weights are to be treated as "expansion values," as will be the case if for example
+ * the weights represent frequency counts. To normalize weights so that the denominator
+ * in the variance computation equals the length of the input vector minus one, use <pre>
+ * <code>evaluate(values, MathArrays.normalizeArray(weights, values.length)); </code>
+ * </pre>
+ * <p>
+ * Returns 0 for a single-value (i.e. length = 1) sample.</p>
+ * <p>
+ * Throws <code>MathIllegalArgumentException</code> if any of the following are true:
+ * <ul><li>the values array is null</li>
+ * <li>the weights array is null</li>
+ * <li>the weights array does not have the same length as the values array</li>
+ * <li>the weights array contains one or more infinite values</li>
+ * <li>the weights array contains one or more NaN values</li>
+ * <li>the weights array contains negative values</li>
+ * </ul></p>
+ * <p>
+ * Does not change the internal state of the statistic.</p>
+ * <p>
+ * Throws <code>MathIllegalArgumentException</code> if either array is null.</p>
+ *
+ * @param values the input array
+ * @param weights the weights array
+ * @return the weighted variance of the values
+ * @throws MathIllegalArgumentException if the parameters are not valid
+ * @since 2.1
+ */
+ public double evaluate(final double[] values, final double[] weights)
+ throws MathIllegalArgumentException {
+ return evaluate(values, weights, 0, values.length);
+ }
+
+ /**
+ * Returns the variance of the entries in the specified portion of
+ * the input array, using the precomputed mean value. Returns
+ * <code>Double.NaN</code> if the designated subarray is empty.
+ * <p>
+ * See {@link Variance} for details on the computing algorithm.</p>
+ * <p>
+ * The formula used assumes that the supplied mean value is the arithmetic
+ * mean of the sample data, not a known population parameter. This method
+ * is supplied only to save computation when the mean has already been
+ * computed.</p>
+ * <p>
+ * Returns 0 for a single-value (i.e. length = 1) sample.</p>
+ * <p>
+ * Throws <code>MathIllegalArgumentException</code> if the array is null.</p>
+ * <p>
+ * Does not change the internal state of the statistic.</p>
+ *
+ * @param values the input array
+ * @param mean the precomputed mean value
+ * @param begin index of the first array element to include
+ * @param length the number of elements to include
+ * @return the variance of the values or Double.NaN if length = 0
+ * @throws MathIllegalArgumentException if the array is null or the array index
+ * parameters are not valid
+ */
+ public double evaluate(final double[] values, final double mean,
+ final int begin, final int length) throws MathIllegalArgumentException {
+
+ double var = Double.NaN;
+
+ if (test(values, begin, length)) {
+ if (length == 1) {
+ var = 0.0;
+ } else if (length > 1) {
+ double accum = 0.0;
+ double dev = 0.0;
+ double accum2 = 0.0;
+ for (int i = begin; i < begin + length; i++) {
+ dev = values[i] - mean;
+ accum += dev * dev;
+ accum2 += dev;
+ }
+ double len = length;
+ if (isBiasCorrected) {
+ var = (accum - (accum2 * accum2 / len)) / (len - 1.0);
+ } else {
+ var = (accum - (accum2 * accum2 / len)) / len;
+ }
+ }
+ }
+ return var;
+ }
+
+ /**
+ * Returns the variance of the entries in the input array, using the
+ * precomputed mean value. Returns <code>Double.NaN</code> if the array
+ * is empty.
+ * <p>
+ * See {@link Variance} for details on the computing algorithm.</p>
+ * <p>
+ * If <code>isBiasCorrected</code> is <code>true</code> the formula used
+ * assumes that the supplied mean value is the arithmetic mean of the
+ * sample data, not a known population parameter. If the mean is a known
+ * population parameter, or if the "population" version of the variance is
+ * desired, set <code>isBiasCorrected</code> to <code>false</code> before
+ * invoking this method.</p>
+ * <p>
+ * Returns 0 for a single-value (i.e. length = 1) sample.</p>
+ * <p>
+ * Throws <code>MathIllegalArgumentException</code> if the array is null.</p>
+ * <p>
+ * Does not change the internal state of the statistic.</p>
+ *
+ * @param values the input array
+ * @param mean the precomputed mean value
+ * @return the variance of the values or Double.NaN if the array is empty
+ * @throws MathIllegalArgumentException if the array is null
+ */
+ public double evaluate(final double[] values, final double mean) throws MathIllegalArgumentException {
+ return evaluate(values, mean, 0, values.length);
+ }
+
+ /**
+ * Returns the weighted variance of the entries in the specified portion of
+ * the input array, using the precomputed weighted mean value. Returns
+ * <code>Double.NaN</code> if the designated subarray is empty.
+ * <p>
+ * Uses the formula <pre>
+ * &Sigma;(weights[i]*(values[i] - mean)<sup>2</sup>)/(&Sigma;(weights[i]) - 1)
+ * </pre></p>
+ * <p>
+ * The formula used assumes that the supplied mean value is the weighted arithmetic
+ * mean of the sample data, not a known population parameter. This method
+ * is supplied only to save computation when the mean has already been
+ * computed.</p>
+ * <p>
+ * This formula will not return the same result as the unweighted variance when all
+ * weights are equal, unless all weights are equal to 1. The formula assumes that
+ * weights are to be treated as "expansion values," as will be the case if for example
+ * the weights represent frequency counts. To normalize weights so that the denominator
+ * in the variance computation equals the length of the input vector minus one, use <pre>
+ * <code>evaluate(values, MathArrays.normalizeArray(weights, values.length), mean); </code>
+ * </pre>
+ * <p>
+ * Returns 0 for a single-value (i.e. length = 1) sample.</p>
+ * <p>
+ * Throws <code>MathIllegalArgumentException</code> if any of the following are true:
+ * <ul><li>the values array is null</li>
+ * <li>the weights array is null</li>
+ * <li>the weights array does not have the same length as the values array</li>
+ * <li>the weights array contains one or more infinite values</li>
+ * <li>the weights array contains one or more NaN values</li>
+ * <li>the weights array contains negative values</li>
+ * <li>the start and length arguments do not determine a valid array</li>
+ * </ul></p>
+ * <p>
+ * Does not change the internal state of the statistic.</p>
+ *
+ * @param values the input array
+ * @param weights the weights array
+ * @param mean the precomputed weighted mean value
+ * @param begin index of the first array element to include
+ * @param length the number of elements to include
+ * @return the variance of the values or Double.NaN if length = 0
+ * @throws MathIllegalArgumentException if the parameters are not valid
+ * @since 2.1
+ */
+ public double evaluate(final double[] values, final double[] weights,
+ final double mean, final int begin, final int length)
+ throws MathIllegalArgumentException {
+
+ double var = Double.NaN;
+
+ if (test(values, weights, begin, length)) {
+ if (length == 1) {
+ var = 0.0;
+ } else if (length > 1) {
+ double accum = 0.0;
+ double dev = 0.0;
+ double accum2 = 0.0;
+ for (int i = begin; i < begin + length; i++) {
+ dev = values[i] - mean;
+ accum += weights[i] * (dev * dev);
+ accum2 += weights[i] * dev;
+ }
+
+ double sumWts = 0;
+ for (int i = begin; i < begin + length; i++) {
+ sumWts += weights[i];
+ }
+
+ if (isBiasCorrected) {
+ var = (accum - (accum2 * accum2 / sumWts)) / (sumWts - 1.0);
+ } else {
+ var = (accum - (accum2 * accum2 / sumWts)) / sumWts;
+ }
+ }
+ }
+ return var;
+ }
+
+ /**
+ * <p>Returns the weighted variance of the values in the input array, using
+ * the precomputed weighted mean value.</p>
+ * <p>
+ * Uses the formula <pre>
+ * &Sigma;(weights[i]*(values[i] - mean)<sup>2</sup>)/(&Sigma;(weights[i]) - 1)
+ * </pre></p>
+ * <p>
+ * The formula used assumes that the supplied mean value is the weighted arithmetic
+ * mean of the sample data, not a known population parameter. This method
+ * is supplied only to save computation when the mean has already been
+ * computed.</p>
+ * <p>
+ * This formula will not return the same result as the unweighted variance when all
+ * weights are equal, unless all weights are equal to 1. The formula assumes that
+ * weights are to be treated as "expansion values," as will be the case if for example
+ * the weights represent frequency counts. To normalize weights so that the denominator
+ * in the variance computation equals the length of the input vector minus one, use <pre>
+ * <code>evaluate(values, MathArrays.normalizeArray(weights, values.length), mean); </code>
+ * </pre>
+ * <p>
+ * Returns 0 for a single-value (i.e. length = 1) sample.</p>
+ * <p>
+ * Throws <code>MathIllegalArgumentException</code> if any of the following are true:
+ * <ul><li>the values array is null</li>
+ * <li>the weights array is null</li>
+ * <li>the weights array does not have the same length as the values array</li>
+ * <li>the weights array contains one or more infinite values</li>
+ * <li>the weights array contains one or more NaN values</li>
+ * <li>the weights array contains negative values</li>
+ * </ul></p>
+ * <p>
+ * Does not change the internal state of the statistic.</p>
+ *
+ * @param values the input array
+ * @param weights the weights array
+ * @param mean the precomputed weighted mean value
+ * @return the variance of the values or Double.NaN if length = 0
+ * @throws MathIllegalArgumentException if the parameters are not valid
+ * @since 2.1
+ */
+ public double evaluate(final double[] values, final double[] weights, final double mean)
+ throws MathIllegalArgumentException {
+ return evaluate(values, weights, mean, 0, values.length);
+ }
+
+ /**
+ * @return Returns the isBiasCorrected.
+ */
+ public boolean isBiasCorrected() {
+ return isBiasCorrected;
+ }
+
+ /**
+ * @param biasCorrected The isBiasCorrected to set.
+ */
+ public void setBiasCorrected(boolean biasCorrected) {
+ this.isBiasCorrected = biasCorrected;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public Variance copy() {
+ Variance result = new Variance();
+ // No try-catch or advertised exception because parameters are guaranteed non-null
+ copy(this, result);
+ return result;
+ }
+
+ /**
+ * Copies source to dest.
+ * <p>Neither source nor dest can be null.</p>
+ *
+ * @param source Variance to copy
+ * @param dest Variance to copy to
+ * @throws NullArgumentException if either source or dest is null
+ */
+ public static void copy(Variance source, Variance dest)
+ throws NullArgumentException {
+ MathUtils.checkNotNull(source);
+ MathUtils.checkNotNull(dest);
+ dest.setData(source.getDataRef());
+ dest.moment = source.moment.copy();
+ dest.isBiasCorrected = source.isBiasCorrected;
+ dest.incMoment = source.incMoment;
+ }
+}
diff --git a/src/main/java/org/apache/commons/math3/stat/descriptive/moment/VectorialCovariance.java b/src/main/java/org/apache/commons/math3/stat/descriptive/moment/VectorialCovariance.java
new file mode 100644
index 0000000..7f6f903
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/stat/descriptive/moment/VectorialCovariance.java
@@ -0,0 +1,157 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math3.stat.descriptive.moment;
+
+import java.io.Serializable;
+import java.util.Arrays;
+
+import org.apache.commons.math3.exception.DimensionMismatchException;
+import org.apache.commons.math3.linear.MatrixUtils;
+import org.apache.commons.math3.linear.RealMatrix;
+
+/**
+ * Returns the covariance matrix of the available vectors.
+ * @since 1.2
+ */
+public class VectorialCovariance implements Serializable {
+
+ /** Serializable version identifier */
+ private static final long serialVersionUID = 4118372414238930270L;
+
+ /** Sums for each component. */
+ private final double[] sums;
+
+ /** Sums of products for each component. */
+ private final double[] productsSums;
+
+ /** Indicator for bias correction. */
+ private final boolean isBiasCorrected;
+
+ /** Number of vectors in the sample. */
+ private long n;
+
+ /** Constructs a VectorialCovariance.
+ * @param dimension vectors dimension
+ * @param isBiasCorrected if true, computed the unbiased sample covariance,
+ * otherwise computes the biased population covariance
+ */
+ public VectorialCovariance(int dimension, boolean isBiasCorrected) {
+ sums = new double[dimension];
+ productsSums = new double[dimension * (dimension + 1) / 2];
+ n = 0;
+ this.isBiasCorrected = isBiasCorrected;
+ }
+
+ /**
+ * Add a new vector to the sample.
+ * @param v vector to add
+ * @throws DimensionMismatchException if the vector does not have the right dimension
+ */
+ public void increment(double[] v) throws DimensionMismatchException {
+ if (v.length != sums.length) {
+ throw new DimensionMismatchException(v.length, sums.length);
+ }
+ int k = 0;
+ for (int i = 0; i < v.length; ++i) {
+ sums[i] += v[i];
+ for (int j = 0; j <= i; ++j) {
+ productsSums[k++] += v[i] * v[j];
+ }
+ }
+ n++;
+ }
+
+ /**
+ * Get the covariance matrix.
+ * @return covariance matrix
+ */
+ public RealMatrix getResult() {
+
+ int dimension = sums.length;
+ RealMatrix result = MatrixUtils.createRealMatrix(dimension, dimension);
+
+ if (n > 1) {
+ double c = 1.0 / (n * (isBiasCorrected ? (n - 1) : n));
+ int k = 0;
+ for (int i = 0; i < dimension; ++i) {
+ for (int j = 0; j <= i; ++j) {
+ double e = c * (n * productsSums[k++] - sums[i] * sums[j]);
+ result.setEntry(i, j, e);
+ result.setEntry(j, i, e);
+ }
+ }
+ }
+
+ return result;
+
+ }
+
+ /**
+ * Get the number of vectors in the sample.
+ * @return number of vectors in the sample
+ */
+ public long getN() {
+ return n;
+ }
+
+ /**
+ * Clears the internal state of the Statistic
+ */
+ public void clear() {
+ n = 0;
+ Arrays.fill(sums, 0.0);
+ Arrays.fill(productsSums, 0.0);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public int hashCode() {
+ final int prime = 31;
+ int result = 1;
+ result = prime * result + (isBiasCorrected ? 1231 : 1237);
+ result = prime * result + (int) (n ^ (n >>> 32));
+ result = prime * result + Arrays.hashCode(productsSums);
+ result = prime * result + Arrays.hashCode(sums);
+ return result;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj) {
+ return true;
+ }
+ if (!(obj instanceof VectorialCovariance)) {
+ return false;
+ }
+ VectorialCovariance other = (VectorialCovariance) obj;
+ if (isBiasCorrected != other.isBiasCorrected) {
+ return false;
+ }
+ if (n != other.n) {
+ return false;
+ }
+ if (!Arrays.equals(productsSums, other.productsSums)) {
+ return false;
+ }
+ if (!Arrays.equals(sums, other.sums)) {
+ return false;
+ }
+ return true;
+ }
+
+}
diff --git a/src/main/java/org/apache/commons/math3/stat/descriptive/moment/VectorialMean.java b/src/main/java/org/apache/commons/math3/stat/descriptive/moment/VectorialMean.java
new file mode 100644
index 0000000..e06b3bc
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/stat/descriptive/moment/VectorialMean.java
@@ -0,0 +1,105 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math3.stat.descriptive.moment;
+
+import java.io.Serializable;
+import java.util.Arrays;
+
+import org.apache.commons.math3.exception.DimensionMismatchException;
+
+/**
+ * Returns the arithmetic mean of the available vectors.
+ * @since 1.2
+ */
+public class VectorialMean implements Serializable {
+
+ /** Serializable version identifier */
+ private static final long serialVersionUID = 8223009086481006892L;
+
+ /** Means for each component. */
+ private final Mean[] means;
+
+ /** Constructs a VectorialMean.
+ * @param dimension vectors dimension
+ */
+ public VectorialMean(int dimension) {
+ means = new Mean[dimension];
+ for (int i = 0; i < dimension; ++i) {
+ means[i] = new Mean();
+ }
+ }
+
+ /**
+ * Add a new vector to the sample.
+ * @param v vector to add
+ * @throws DimensionMismatchException if the vector does not have the right dimension
+ */
+ public void increment(double[] v) throws DimensionMismatchException {
+ if (v.length != means.length) {
+ throw new DimensionMismatchException(v.length, means.length);
+ }
+ for (int i = 0; i < v.length; ++i) {
+ means[i].increment(v[i]);
+ }
+ }
+
+ /**
+ * Get the mean vector.
+ * @return mean vector
+ */
+ public double[] getResult() {
+ double[] result = new double[means.length];
+ for (int i = 0; i < result.length; ++i) {
+ result[i] = means[i].getResult();
+ }
+ return result;
+ }
+
+ /**
+ * Get the number of vectors in the sample.
+ * @return number of vectors in the sample
+ */
+ public long getN() {
+ return (means.length == 0) ? 0 : means[0].getN();
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public int hashCode() {
+ final int prime = 31;
+ int result = 1;
+ result = prime * result + Arrays.hashCode(means);
+ return result;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj) {
+ return true;
+ }
+ if (!(obj instanceof VectorialMean)) {
+ return false;
+ }
+ VectorialMean other = (VectorialMean) obj;
+ if (!Arrays.equals(means, other.means)) {
+ return false;
+ }
+ return true;
+ }
+
+}
diff --git a/src/main/java/org/apache/commons/math3/stat/descriptive/moment/package-info.java b/src/main/java/org/apache/commons/math3/stat/descriptive/moment/package-info.java
new file mode 100644
index 0000000..e23ead7
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/stat/descriptive/moment/package-info.java
@@ -0,0 +1,20 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/**
+ * Summary statistics based on moments.
+ */
+package org.apache.commons.math3.stat.descriptive.moment;
diff --git a/src/main/java/org/apache/commons/math3/stat/descriptive/package-info.java b/src/main/java/org/apache/commons/math3/stat/descriptive/package-info.java
new file mode 100644
index 0000000..92fa5b3
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/stat/descriptive/package-info.java
@@ -0,0 +1,44 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/**
+ *
+ * Generic univariate summary statistic objects.
+ *
+ * <h3>UnivariateStatistic API Usage Examples:</h3>
+ *
+ * <h4>UnivariateStatistic:</h4>
+ * <code>/&lowast; evaluation approach &lowast;/<br/>
+ * double[] values = new double[] { 1, 2, 3, 4, 5 };<br/>
+ * <span style="font-weight: bold;">UnivariateStatistic stat = new Mean();</span><br/>
+ * out.println("mean = " + <span style="font-weight: bold;">stat.evaluate(values)</span>);<br/>
+ * </code>
+ *
+ * <h4>StorelessUnivariateStatistic:</h4>
+ * <code>/&lowast; incremental approach &lowast;/<br/>
+ * double[] values = new double[] { 1, 2, 3, 4, 5 };<br/>
+ * <span style="font-weight: bold;">StorelessUnivariateStatistic stat = new Mean();</span><br/>
+ * out.println("mean before adding a value is NaN = " + <span style="font-weight: bold;">stat.getResult()</span>);<br/>
+ * for (int i = 0; i &lt; values.length; i++) {<br/>
+ * &nbsp;&nbsp;&nbsp; <span style="font-weight: bold;">stat.increment(values[i]);</span><br/>
+ * &nbsp;&nbsp;&nbsp; out.println("current mean = " + <span style="font-weight: bold;">stat2.getResult()</span>);<br/>
+ * }<br/>
+ * <span style="font-weight: bold;"> stat.clear();</span><br/>
+ * out.println("mean after clear is NaN = " + <span style="font-weight: bold;">stat.getResult()</span>);
+ * </code>
+ *
+ */
+package org.apache.commons.math3.stat.descriptive;
diff --git a/src/main/java/org/apache/commons/math3/stat/descriptive/rank/Max.java b/src/main/java/org/apache/commons/math3/stat/descriptive/rank/Max.java
new file mode 100644
index 0000000..75f145f
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/stat/descriptive/rank/Max.java
@@ -0,0 +1,171 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math3.stat.descriptive.rank;
+
+import java.io.Serializable;
+
+import org.apache.commons.math3.exception.MathIllegalArgumentException;
+import org.apache.commons.math3.exception.NullArgumentException;
+import org.apache.commons.math3.stat.descriptive.AbstractStorelessUnivariateStatistic;
+import org.apache.commons.math3.util.MathUtils;
+
+/**
+ * Returns the maximum of the available values.
+ * <p>
+ * <ul>
+ * <li>The result is <code>NaN</code> iff all values are <code>NaN</code>
+ * (i.e. <code>NaN</code> values have no impact on the value of the statistic).</li>
+ * <li>If any of the values equals <code>Double.POSITIVE_INFINITY</code>,
+ * the result is <code>Double.POSITIVE_INFINITY.</code></li>
+ * </ul></p>
+* <p>
+ * <strong>Note that this implementation is not synchronized.</strong> If
+ * multiple threads access an instance of this class concurrently, and at least
+ * one of the threads invokes the <code>increment()</code> or
+ * <code>clear()</code> method, it must be synchronized externally.</p>
+ *
+ */
+public class Max extends AbstractStorelessUnivariateStatistic implements Serializable {
+
+ /** Serializable version identifier */
+ private static final long serialVersionUID = -5593383832225844641L;
+
+ /** Number of values that have been added */
+ private long n;
+
+ /** Current value of the statistic */
+ private double value;
+
+ /**
+ * Create a Max instance
+ */
+ public Max() {
+ n = 0;
+ value = Double.NaN;
+ }
+
+ /**
+ * Copy constructor, creates a new {@code Max} identical
+ * to the {@code original}
+ *
+ * @param original the {@code Max} instance to copy
+ * @throws NullArgumentException if original is null
+ */
+ public Max(Max original) throws NullArgumentException {
+ copy(original, this);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void increment(final double d) {
+ if (d > value || Double.isNaN(value)) {
+ value = d;
+ }
+ n++;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void clear() {
+ value = Double.NaN;
+ n = 0;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public double getResult() {
+ return value;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public long getN() {
+ return n;
+ }
+
+ /**
+ * Returns the maximum of the entries in the specified portion of
+ * the input array, or <code>Double.NaN</code> if the designated subarray
+ * is empty.
+ * <p>
+ * Throws <code>MathIllegalArgumentException</code> if the array is null or
+ * the array index parameters are not valid.</p>
+ * <p>
+ * <ul>
+ * <li>The result is <code>NaN</code> iff all values are <code>NaN</code>
+ * (i.e. <code>NaN</code> values have no impact on the value of the statistic).</li>
+ * <li>If any of the values equals <code>Double.POSITIVE_INFINITY</code>,
+ * the result is <code>Double.POSITIVE_INFINITY.</code></li>
+ * </ul></p>
+ *
+ * @param values the input array
+ * @param begin index of the first array element to include
+ * @param length the number of elements to include
+ * @return the maximum of the values or Double.NaN if length = 0
+ * @throws MathIllegalArgumentException if the array is null or the array index
+ * parameters are not valid
+ */
+ @Override
+ public double evaluate(final double[] values, final int begin, final int length)
+ throws MathIllegalArgumentException {
+ double max = Double.NaN;
+ if (test(values, begin, length)) {
+ max = values[begin];
+ for (int i = begin; i < begin + length; i++) {
+ if (!Double.isNaN(values[i])) {
+ max = (max > values[i]) ? max : values[i];
+ }
+ }
+ }
+ return max;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public Max copy() {
+ Max result = new Max();
+ // No try-catch or advertised exception because args are non-null
+ copy(this, result);
+ return result;
+ }
+
+ /**
+ * Copies source to dest.
+ * <p>Neither source nor dest can be null.</p>
+ *
+ * @param source Max to copy
+ * @param dest Max to copy to
+ * @throws NullArgumentException if either source or dest is null
+ */
+ public static void copy(Max source, Max dest)
+ throws NullArgumentException {
+ MathUtils.checkNotNull(source);
+ MathUtils.checkNotNull(dest);
+ dest.setData(source.getDataRef());
+ dest.n = source.n;
+ dest.value = source.value;
+ }
+}
diff --git a/src/main/java/org/apache/commons/math3/stat/descriptive/rank/Median.java b/src/main/java/org/apache/commons/math3/stat/descriptive/rank/Median.java
new file mode 100644
index 0000000..6350a0b
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/stat/descriptive/rank/Median.java
@@ -0,0 +1,97 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math3.stat.descriptive.rank;
+
+import java.io.Serializable;
+
+import org.apache.commons.math3.exception.MathIllegalArgumentException;
+import org.apache.commons.math3.exception.NullArgumentException;
+import org.apache.commons.math3.stat.ranking.NaNStrategy;
+import org.apache.commons.math3.util.KthSelector;
+
+
+/**
+ * Returns the median of the available values. This is the same as the 50th percentile.
+ * See {@link Percentile} for a description of the algorithm used.
+ * <p>
+ * <strong>Note that this implementation is not synchronized.</strong> If
+ * multiple threads access an instance of this class concurrently, and at least
+ * one of the threads invokes the <code>increment()</code> or
+ * <code>clear()</code> method, it must be synchronized externally.</p>
+ *
+ */
+public class Median extends Percentile implements Serializable {
+
+ /** Serializable version identifier */
+ private static final long serialVersionUID = -3961477041290915687L;
+
+ /** Fixed quantile. */
+ private static final double FIXED_QUANTILE_50 = 50.0;
+
+ /**
+ * Default constructor.
+ */
+ public Median() {
+ // No try-catch or advertised exception - arg is valid
+ super(FIXED_QUANTILE_50);
+ }
+
+ /**
+ * Copy constructor, creates a new {@code Median} identical
+ * to the {@code original}
+ *
+ * @param original the {@code Median} instance to copy
+ * @throws NullArgumentException if original is null
+ */
+ public Median(Median original) throws NullArgumentException {
+ super(original);
+ }
+
+ /**
+ * Constructs a Median with the specific {@link EstimationType}, {@link NaNStrategy} and {@link PivotingStrategy}.
+ *
+ * @param estimationType one of the percentile {@link EstimationType estimation types}
+ * @param nanStrategy one of {@link NaNStrategy} to handle with NaNs
+ * @param kthSelector {@link KthSelector} to use for pivoting during search
+ * @throws MathIllegalArgumentException if p is not within (0,100]
+ * @throws NullArgumentException if type or NaNStrategy passed is null
+ */
+ private Median(final EstimationType estimationType, final NaNStrategy nanStrategy,
+ final KthSelector kthSelector)
+ throws MathIllegalArgumentException {
+ super(FIXED_QUANTILE_50, estimationType, nanStrategy, kthSelector);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public Median withEstimationType(final EstimationType newEstimationType) {
+ return new Median(newEstimationType, getNaNStrategy(), getKthSelector());
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public Median withNaNStrategy(final NaNStrategy newNaNStrategy) {
+ return new Median(getEstimationType(), newNaNStrategy, getKthSelector());
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public Median withKthSelector(final KthSelector newKthSelector) {
+ return new Median(getEstimationType(), getNaNStrategy(), newKthSelector);
+ }
+
+}
diff --git a/src/main/java/org/apache/commons/math3/stat/descriptive/rank/Min.java b/src/main/java/org/apache/commons/math3/stat/descriptive/rank/Min.java
new file mode 100644
index 0000000..c87e6f1
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/stat/descriptive/rank/Min.java
@@ -0,0 +1,171 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math3.stat.descriptive.rank;
+
+import java.io.Serializable;
+
+import org.apache.commons.math3.exception.MathIllegalArgumentException;
+import org.apache.commons.math3.exception.NullArgumentException;
+import org.apache.commons.math3.stat.descriptive.AbstractStorelessUnivariateStatistic;
+import org.apache.commons.math3.util.MathUtils;
+
+/**
+ * Returns the minimum of the available values.
+ * <p>
+ * <ul>
+ * <li>The result is <code>NaN</code> iff all values are <code>NaN</code>
+ * (i.e. <code>NaN</code> values have no impact on the value of the statistic).</li>
+ * <li>If any of the values equals <code>Double.NEGATIVE_INFINITY</code>,
+ * the result is <code>Double.NEGATIVE_INFINITY.</code></li>
+ * </ul></p>
+ * <p>
+ * <strong>Note that this implementation is not synchronized.</strong> If
+ * multiple threads access an instance of this class concurrently, and at least
+ * one of the threads invokes the <code>increment()</code> or
+ * <code>clear()</code> method, it must be synchronized externally.</p>
+ *
+ */
+public class Min extends AbstractStorelessUnivariateStatistic implements Serializable {
+
+ /** Serializable version identifier */
+ private static final long serialVersionUID = -2941995784909003131L;
+
+ /**Number of values that have been added */
+ private long n;
+
+ /**Current value of the statistic */
+ private double value;
+
+ /**
+ * Create a Min instance
+ */
+ public Min() {
+ n = 0;
+ value = Double.NaN;
+ }
+
+ /**
+ * Copy constructor, creates a new {@code Min} identical
+ * to the {@code original}
+ *
+ * @param original the {@code Min} instance to copy
+ * @throws NullArgumentException if original is null
+ */
+ public Min(Min original) throws NullArgumentException {
+ copy(original, this);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void increment(final double d) {
+ if (d < value || Double.isNaN(value)) {
+ value = d;
+ }
+ n++;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void clear() {
+ value = Double.NaN;
+ n = 0;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public double getResult() {
+ return value;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public long getN() {
+ return n;
+ }
+
+ /**
+ * Returns the minimum of the entries in the specified portion of
+ * the input array, or <code>Double.NaN</code> if the designated subarray
+ * is empty.
+ * <p>
+ * Throws <code>MathIllegalArgumentException</code> if the array is null or
+ * the array index parameters are not valid.</p>
+ * <p>
+ * <ul>
+ * <li>The result is <code>NaN</code> iff all values are <code>NaN</code>
+ * (i.e. <code>NaN</code> values have no impact on the value of the statistic).</li>
+ * <li>If any of the values equals <code>Double.NEGATIVE_INFINITY</code>,
+ * the result is <code>Double.NEGATIVE_INFINITY.</code></li>
+ * </ul> </p>
+ *
+ * @param values the input array
+ * @param begin index of the first array element to include
+ * @param length the number of elements to include
+ * @return the minimum of the values or Double.NaN if length = 0
+ * @throws MathIllegalArgumentException if the array is null or the array index
+ * parameters are not valid
+ */
+ @Override
+ public double evaluate(final double[] values,final int begin, final int length)
+ throws MathIllegalArgumentException {
+ double min = Double.NaN;
+ if (test(values, begin, length)) {
+ min = values[begin];
+ for (int i = begin; i < begin + length; i++) {
+ if (!Double.isNaN(values[i])) {
+ min = (min < values[i]) ? min : values[i];
+ }
+ }
+ }
+ return min;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public Min copy() {
+ Min result = new Min();
+ // No try-catch or advertised exception - args are non-null
+ copy(this, result);
+ return result;
+ }
+
+ /**
+ * Copies source to dest.
+ * <p>Neither source nor dest can be null.</p>
+ *
+ * @param source Min to copy
+ * @param dest Min to copy to
+ * @throws NullArgumentException if either source or dest is null
+ */
+ public static void copy(Min source, Min dest)
+ throws NullArgumentException {
+ MathUtils.checkNotNull(source);
+ MathUtils.checkNotNull(dest);
+ dest.setData(source.getDataRef());
+ dest.n = source.n;
+ dest.value = source.value;
+ }
+}
diff --git a/src/main/java/org/apache/commons/math3/stat/descriptive/rank/PSquarePercentile.java b/src/main/java/org/apache/commons/math3/stat/descriptive/rank/PSquarePercentile.java
new file mode 100644
index 0000000..b8bc274
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/stat/descriptive/rank/PSquarePercentile.java
@@ -0,0 +1,997 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math3.stat.descriptive.rank;
+
+import java.io.IOException;
+import java.io.ObjectInputStream;
+import java.io.Serializable;
+import java.text.DecimalFormat;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+
+import org.apache.commons.math3.analysis.UnivariateFunction;
+import org.apache.commons.math3.analysis.interpolation.LinearInterpolator;
+import org.apache.commons.math3.analysis.interpolation.NevilleInterpolator;
+import org.apache.commons.math3.analysis.interpolation.UnivariateInterpolator;
+import org.apache.commons.math3.exception.InsufficientDataException;
+import org.apache.commons.math3.exception.OutOfRangeException;
+import org.apache.commons.math3.exception.util.LocalizedFormats;
+import org.apache.commons.math3.stat.descriptive.AbstractStorelessUnivariateStatistic;
+import org.apache.commons.math3.stat.descriptive.StorelessUnivariateStatistic;
+import org.apache.commons.math3.util.MathArrays;
+import org.apache.commons.math3.util.MathUtils;
+import org.apache.commons.math3.util.Precision;
+
+/**
+ * A {@link StorelessUnivariateStatistic} estimating percentiles using the
+ * <ahref=http://www.cs.wustl.edu/~jain/papers/ftp/psqr.pdf>P<SUP>2</SUP></a>
+ * Algorithm as explained by <a href=http://www.cse.wustl.edu/~jain/>Raj
+ * Jain</a> and Imrich Chlamtac in
+ * <a href=http://www.cse.wustl.edu/~jain/papers/psqr.htm>P<SUP>2</SUP> Algorithm
+ * for Dynamic Calculation of Quantiles and Histogram Without Storing
+ * Observations</a>.
+ * <p>
+ * Note: This implementation is not synchronized and produces an approximate
+ * result. For small samples, where data can be stored and processed in memory,
+ * {@link Percentile} should be used.</p>
+ *
+ */
+public class PSquarePercentile extends AbstractStorelessUnivariateStatistic
+ implements StorelessUnivariateStatistic, Serializable {
+
+ /**
+ * The maximum array size used for psquare algorithm
+ */
+ private static final int PSQUARE_CONSTANT = 5;
+
+ /**
+ * A Default quantile needed in case if user prefers to use default no
+ * argument constructor.
+ */
+ private static final double DEFAULT_QUANTILE_DESIRED = 50d;
+
+ /**
+ * Serial ID
+ */
+ private static final long serialVersionUID = 2283912083175715479L;
+
+ /**
+ * A decimal formatter for print convenience
+ */
+ private static final DecimalFormat DECIMAL_FORMAT = new DecimalFormat(
+ "00.00");
+
+ /**
+ * Initial list of 5 numbers corresponding to 5 markers. <b>NOTE:</b>watch
+ * out for the add methods that are overloaded
+ */
+ private final List<Double> initialFive = new FixedCapacityList<Double>(
+ PSQUARE_CONSTANT);
+
+ /**
+ * The quantile needed should be in range of 0-1. The constructor
+ * {@link #PSquarePercentile(double)} ensures that passed in percentile is
+ * divided by 100.
+ */
+ private final double quantile;
+
+ /**
+ * lastObservation is the last observation value/input sample. No need to
+ * serialize
+ */
+ private transient double lastObservation;
+
+ /**
+ * Markers is the marker collection object which comes to effect
+ * only after 5 values are inserted
+ */
+ private PSquareMarkers markers = null;
+
+ /**
+ * Computed p value (i,e percentile value of data set hither to received)
+ */
+ private double pValue = Double.NaN;
+
+ /**
+ * Counter to count the values/observations accepted into this data set
+ */
+ private long countOfObservations;
+
+ /**
+ * Constructs a PSquarePercentile with the specific percentile value.
+ * @param p the percentile
+ * @throws OutOfRangeException if p is not greater than 0 and less
+ * than or equal to 100
+ */
+ public PSquarePercentile(final double p) {
+ if (p > 100 || p < 0) {
+ throw new OutOfRangeException(LocalizedFormats.OUT_OF_RANGE,
+ p, 0, 100);
+ }
+ this.quantile = p / 100d;// always set it within (0,1]
+ }
+
+ /**
+ * Default constructor that assumes a {@link #DEFAULT_QUANTILE_DESIRED
+ * default quantile} needed
+ */
+ PSquarePercentile() {
+ this(DEFAULT_QUANTILE_DESIRED);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public int hashCode() {
+ double result = getResult();
+ result = Double.isNaN(result) ? 37 : result;
+ final double markersHash = markers == null ? 0 : markers.hashCode();
+ final double[] toHash = {result, quantile, markersHash, countOfObservations};
+ return Arrays.hashCode(toHash);
+ }
+
+ /**
+ * Returns true iff {@code o} is a {@code PSquarePercentile} returning the
+ * same values as this for {@code getResult()} and {@code getN()} and also
+ * having equal markers
+ *
+ * @param o object to compare
+ * @return true if {@code o} is a {@code PSquarePercentile} with
+ * equivalent internal state
+ */
+ @Override
+ public boolean equals(Object o) {
+ boolean result = false;
+ if (this == o) {
+ result = true;
+ } else if (o != null && o instanceof PSquarePercentile) {
+ PSquarePercentile that = (PSquarePercentile) o;
+ boolean isNotNull = markers != null && that.markers != null;
+ boolean isNull = markers == null && that.markers == null;
+ result = isNotNull ? markers.equals(that.markers) : isNull;
+ // markers as in the case of first
+ // five observations
+ result = result && getN() == that.getN();
+ }
+ return result;
+ }
+
+ /**
+ * {@inheritDoc}The internal state updated due to the new value in this
+ * context is basically of the marker positions and computation of the
+ * approximate quantile.
+ *
+ * @param observation the observation currently being added.
+ */
+ @Override
+ public void increment(final double observation) {
+ // Increment counter
+ countOfObservations++;
+
+ // Store last observation
+ this.lastObservation = observation;
+
+ // 0. Use Brute force for <5
+ if (markers == null) {
+ if (initialFive.add(observation)) {
+ Collections.sort(initialFive);
+ pValue =
+ initialFive
+ .get((int) (quantile * (initialFive.size() - 1)));
+ return;
+ }
+ // 1. Initialize once after 5th observation
+ markers = newMarkers(initialFive, quantile);
+ }
+ // 2. process a Data Point and return pValue
+ pValue = markers.processDataPoint(observation);
+ }
+
+ /**
+ * Returns a string containing the last observation, the current estimate
+ * of the quantile and all markers.
+ *
+ * @return string representation of state data
+ */
+ @Override
+ public String toString() {
+
+ if (markers == null) {
+ return String.format("obs=%s pValue=%s",
+ DECIMAL_FORMAT.format(lastObservation),
+ DECIMAL_FORMAT.format(pValue));
+ } else {
+ return String.format("obs=%s markers=%s",
+ DECIMAL_FORMAT.format(lastObservation), markers.toString());
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public long getN() {
+ return countOfObservations;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public StorelessUnivariateStatistic copy() {
+ // multiply quantile by 100 now as anyway constructor divides it by 100
+ PSquarePercentile copy = new PSquarePercentile(100d * quantile);
+
+ if (markers != null) {
+ copy.markers = (PSquareMarkers) markers.clone();
+ }
+ copy.countOfObservations = countOfObservations;
+ copy.pValue = pValue;
+ copy.initialFive.clear();
+ copy.initialFive.addAll(initialFive);
+ return copy;
+ }
+
+ /**
+ * Returns the quantile estimated by this statistic in the range [0.0-1.0]
+ *
+ * @return quantile estimated by {@link #getResult()}
+ */
+ public double quantile() {
+ return quantile;
+ }
+
+ /**
+ * {@inheritDoc}. This basically clears all the markers, the
+ * initialFive list and sets countOfObservations to 0.
+ */
+ @Override
+ public void clear() {
+ markers = null;
+ initialFive.clear();
+ countOfObservations = 0L;
+ pValue = Double.NaN;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public double getResult() {
+ if (Double.compare(quantile, 1d) == 0) {
+ pValue = maximum();
+ } else if (Double.compare(quantile, 0d) == 0) {
+ pValue = minimum();
+ }
+ return pValue;
+ }
+
+ /**
+ * @return maximum in the data set added to this statistic
+ */
+ private double maximum() {
+ double val = Double.NaN;
+ if (markers != null) {
+ val = markers.height(PSQUARE_CONSTANT);
+ } else if (!initialFive.isEmpty()) {
+ val = initialFive.get(initialFive.size() - 1);
+ }
+ return val;
+ }
+
+ /**
+ * @return minimum in the data set added to this statistic
+ */
+ private double minimum() {
+ double val = Double.NaN;
+ if (markers != null) {
+ val = markers.height(1);
+ } else if (!initialFive.isEmpty()) {
+ val = initialFive.get(0);
+ }
+ return val;
+ }
+
+ /**
+ * Markers is an encapsulation of the five markers/buckets as indicated in
+ * the original works.
+ */
+ private static class Markers implements PSquareMarkers, Serializable {
+ /**
+ * Serial version id
+ */
+ private static final long serialVersionUID = 1L;
+
+ /** Low marker index */
+ private static final int LOW = 2;
+
+ /** High marker index */
+ private static final int HIGH = 4;
+
+ /**
+ * Array of 5+1 Markers (The first marker is dummy just so we
+ * can match the rest of indexes [1-5] indicated in the original works
+ * which follows unit based index)
+ */
+ private final Marker[] markerArray;
+
+ /**
+ * Kth cell belonging to [1-5] of the markerArray. No need for
+ * this to be serialized
+ */
+ private transient int k = -1;
+
+ /**
+ * Constructor
+ *
+ * @param theMarkerArray marker array to be used
+ */
+ private Markers(final Marker[] theMarkerArray) {
+ MathUtils.checkNotNull(theMarkerArray);
+ markerArray = theMarkerArray;
+ for (int i = 1; i < PSQUARE_CONSTANT; i++) {
+ markerArray[i].previous(markerArray[i - 1])
+ .next(markerArray[i + 1]).index(i);
+ }
+ markerArray[0].previous(markerArray[0]).next(markerArray[1])
+ .index(0);
+ markerArray[5].previous(markerArray[4]).next(markerArray[5])
+ .index(5);
+ }
+
+ /**
+ * Constructor
+ *
+ * @param initialFive elements required to build Marker
+ * @param p quantile required to be computed
+ */
+ private Markers(final List<Double> initialFive, final double p) {
+ this(createMarkerArray(initialFive, p));
+ }
+
+ /**
+ * Creates a marker array using initial five elements and a quantile
+ *
+ * @param initialFive list of initial five elements
+ * @param p the pth quantile
+ * @return Marker array
+ */
+ private static Marker[] createMarkerArray(
+ final List<Double> initialFive, final double p) {
+ final int countObserved =
+ initialFive == null ? -1 : initialFive.size();
+ if (countObserved < PSQUARE_CONSTANT) {
+ throw new InsufficientDataException(
+ LocalizedFormats.INSUFFICIENT_OBSERVED_POINTS_IN_SAMPLE,
+ countObserved, PSQUARE_CONSTANT);
+ }
+ Collections.sort(initialFive);
+ return new Marker[] {
+ new Marker(),// Null Marker
+ new Marker(initialFive.get(0), 1, 0, 1),
+ new Marker(initialFive.get(1), 1 + 2 * p, p / 2, 2),
+ new Marker(initialFive.get(2), 1 + 4 * p, p, 3),
+ new Marker(initialFive.get(3), 3 + 2 * p, (1 + p) / 2, 4),
+ new Marker(initialFive.get(4), 5, 1, 5) };
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public int hashCode() {
+ return Arrays.deepHashCode(markerArray);
+ }
+
+ /**
+ * {@inheritDoc}.This equals method basically checks for marker array to
+ * be deep equals.
+ *
+ * @param o is the other object
+ * @return true if the object compares with this object are equivalent
+ */
+ @Override
+ public boolean equals(Object o) {
+ boolean result = false;
+ if (this == o) {
+ result = true;
+ } else if (o != null && o instanceof Markers) {
+ Markers that = (Markers) o;
+ result = Arrays.deepEquals(markerArray, that.markerArray);
+ }
+ return result;
+ }
+
+ /**
+ * Process a data point
+ *
+ * @param inputDataPoint is the data point passed
+ * @return computed percentile
+ */
+ public double processDataPoint(final double inputDataPoint) {
+
+ // 1. Find cell and update minima and maxima
+ final int kthCell = findCellAndUpdateMinMax(inputDataPoint);
+
+ // 2. Increment positions
+ incrementPositions(1, kthCell + 1, 5);
+
+ // 2a. Update desired position with increments
+ updateDesiredPositions();
+
+ // 3. Adjust heights of m[2-4] if necessary
+ adjustHeightsOfMarkers();
+
+ // 4. Return percentile
+ return getPercentileValue();
+ }
+
+ /**
+ * Returns the percentile computed thus far.
+ *
+ * @return height of mid point marker
+ */
+ public double getPercentileValue() {
+ return height(3);
+ }
+
+ /**
+ * Finds the cell where the input observation / value fits.
+ *
+ * @param observation the input value to be checked for
+ * @return kth cell (of the markers ranging from 1-5) where observed
+ * sample fits
+ */
+ private int findCellAndUpdateMinMax(final double observation) {
+ k = -1;
+ if (observation < height(1)) {
+ markerArray[1].markerHeight = observation;
+ k = 1;
+ } else if (observation < height(2)) {
+ k = 1;
+ } else if (observation < height(3)) {
+ k = 2;
+ } else if (observation < height(4)) {
+ k = 3;
+ } else if (observation <= height(5)) {
+ k = 4;
+ } else {
+ markerArray[5].markerHeight = observation;
+ k = 4;
+ }
+ return k;
+ }
+
+ /**
+ * Adjust marker heights by setting quantile estimates to middle markers.
+ */
+ private void adjustHeightsOfMarkers() {
+ for (int i = LOW; i <= HIGH; i++) {
+ estimate(i);
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public double estimate(final int index) {
+ if (index < LOW || index > HIGH) {
+ throw new OutOfRangeException(index, LOW, HIGH);
+ }
+ return markerArray[index].estimate();
+ }
+
+ /**
+ * Increment positions by d. Refer to algorithm paper for the
+ * definition of d.
+ *
+ * @param d The increment value for the position
+ * @param startIndex start index of the marker array
+ * @param endIndex end index of the marker array
+ */
+ private void incrementPositions(final int d, final int startIndex,
+ final int endIndex) {
+ for (int i = startIndex; i <= endIndex; i++) {
+ markerArray[i].incrementPosition(d);
+ }
+ }
+
+ /**
+ * Desired positions incremented by bucket width. The bucket width is
+ * basically the desired increments.
+ */
+ private void updateDesiredPositions() {
+ for (int i = 1; i < markerArray.length; i++) {
+ markerArray[i].updateDesiredPosition();
+ }
+ }
+
+ /**
+ * Sets previous and next markers after default read is done.
+ *
+ * @param anInputStream the input stream to be deserialized
+ * @throws ClassNotFoundException thrown when a desired class not found
+ * @throws IOException thrown due to any io errors
+ */
+ private void readObject(ObjectInputStream anInputStream)
+ throws ClassNotFoundException, IOException {
+ // always perform the default de-serialization first
+ anInputStream.defaultReadObject();
+ // Build links
+ for (int i = 1; i < PSQUARE_CONSTANT; i++) {
+ markerArray[i].previous(markerArray[i - 1])
+ .next(markerArray[i + 1]).index(i);
+ }
+ markerArray[0].previous(markerArray[0]).next(markerArray[1])
+ .index(0);
+ markerArray[5].previous(markerArray[4]).next(markerArray[5])
+ .index(5);
+ }
+
+ /**
+ * Return marker height given index
+ *
+ * @param markerIndex index of marker within (1,6)
+ * @return marker height
+ */
+ public double height(final int markerIndex) {
+ if (markerIndex >= markerArray.length || markerIndex <= 0) {
+ throw new OutOfRangeException(markerIndex, 1,
+ markerArray.length);
+ }
+ return markerArray[markerIndex].markerHeight;
+ }
+
+ /**
+ * {@inheritDoc}.Clone Markers
+ *
+ * @return cloned object
+ */
+ @Override
+ public Object clone() {
+ return new Markers(new Marker[] { new Marker(),
+ (Marker) markerArray[1].clone(),
+ (Marker) markerArray[2].clone(),
+ (Marker) markerArray[3].clone(),
+ (Marker) markerArray[4].clone(),
+ (Marker) markerArray[5].clone() });
+
+ }
+
+ /**
+ * Returns string representation of the Marker array.
+ *
+ * @return Markers as a string
+ */
+ @Override
+ public String toString() {
+ return String.format("m1=[%s],m2=[%s],m3=[%s],m4=[%s],m5=[%s]",
+ markerArray[1].toString(), markerArray[2].toString(),
+ markerArray[3].toString(), markerArray[4].toString(),
+ markerArray[5].toString());
+ }
+
+ }
+
+ /**
+ * The class modeling the attributes of the marker of the P-square algorithm
+ */
+ private static class Marker implements Serializable, Cloneable {
+
+ /**
+ * Serial Version ID
+ */
+ private static final long serialVersionUID = -3575879478288538431L;
+
+ /**
+ * The marker index which is just a serial number for the marker in the
+ * marker array of 5+1.
+ */
+ private int index;
+
+ /**
+ * The integral marker position. Refer to the variable n in the original
+ * works.
+ */
+ private double intMarkerPosition;
+
+ /**
+ * Desired marker position. Refer to the variable n' in the original
+ * works.
+ */
+ private double desiredMarkerPosition;
+
+ /**
+ * Marker height or the quantile. Refer to the variable q in the
+ * original works.
+ */
+ private double markerHeight;
+
+ /**
+ * Desired marker increment. Refer to the variable dn' in the original
+ * works.
+ */
+ private double desiredMarkerIncrement;
+
+ /**
+ * Next and previous markers for easy linked navigation in loops. this
+ * is not serialized as they can be rebuilt during deserialization.
+ */
+ private transient Marker next;
+
+ /**
+ * The previous marker links
+ */
+ private transient Marker previous;
+
+ /**
+ * Nonlinear interpolator
+ */
+ private final UnivariateInterpolator nonLinear =
+ new NevilleInterpolator();
+
+ /**
+ * Linear interpolator which is not serializable
+ */
+ private transient UnivariateInterpolator linear =
+ new LinearInterpolator();
+
+ /**
+ * Default constructor
+ */
+ private Marker() {
+ this.next = this.previous = this;
+ }
+
+ /**
+ * Constructor of the marker with parameters
+ *
+ * @param heightOfMarker represent the quantile value
+ * @param makerPositionDesired represent the desired marker position
+ * @param markerPositionIncrement represent increments for position
+ * @param markerPositionNumber represent the position number of marker
+ */
+ private Marker(double heightOfMarker, double makerPositionDesired,
+ double markerPositionIncrement, double markerPositionNumber) {
+ this();
+ this.markerHeight = heightOfMarker;
+ this.desiredMarkerPosition = makerPositionDesired;
+ this.desiredMarkerIncrement = markerPositionIncrement;
+ this.intMarkerPosition = markerPositionNumber;
+ }
+
+ /**
+ * Sets the previous marker.
+ *
+ * @param previousMarker the previous marker to the current marker in
+ * the array of markers
+ * @return this instance
+ */
+ private Marker previous(final Marker previousMarker) {
+ MathUtils.checkNotNull(previousMarker);
+ this.previous = previousMarker;
+ return this;
+ }
+
+ /**
+ * Sets the next marker.
+ *
+ * @param nextMarker the next marker to the current marker in the array
+ * of markers
+ * @return this instance
+ */
+ private Marker next(final Marker nextMarker) {
+ MathUtils.checkNotNull(nextMarker);
+ this.next = nextMarker;
+ return this;
+ }
+
+ /**
+ * Sets the index of the marker.
+ *
+ * @param indexOfMarker the array index of the marker in marker array
+ * @return this instance
+ */
+ private Marker index(final int indexOfMarker) {
+ this.index = indexOfMarker;
+ return this;
+ }
+
+ /**
+ * Update desired Position with increment.
+ */
+ private void updateDesiredPosition() {
+ desiredMarkerPosition += desiredMarkerIncrement;
+ }
+
+ /**
+ * Increment Position by d.
+ *
+ * @param d a delta value to increment
+ */
+ private void incrementPosition(final int d) {
+ intMarkerPosition += d;
+ }
+
+ /**
+ * Difference between desired and actual position
+ *
+ * @return difference between desired and actual position
+ */
+ private double difference() {
+ return desiredMarkerPosition - intMarkerPosition;
+ }
+
+ /**
+ * Estimate the quantile for the current marker.
+ *
+ * @return estimated quantile
+ */
+ private double estimate() {
+ final double di = difference();
+ final boolean isNextHigher =
+ next.intMarkerPosition - intMarkerPosition > 1;
+ final boolean isPreviousLower =
+ previous.intMarkerPosition - intMarkerPosition < -1;
+
+ if (di >= 1 && isNextHigher || di <= -1 && isPreviousLower) {
+ final int d = di >= 0 ? 1 : -1;
+ final double[] xval =
+ new double[] { previous.intMarkerPosition,
+ intMarkerPosition, next.intMarkerPosition };
+ final double[] yval =
+ new double[] { previous.markerHeight, markerHeight,
+ next.markerHeight };
+ final double xD = intMarkerPosition + d;
+
+ UnivariateFunction univariateFunction =
+ nonLinear.interpolate(xval, yval);
+ markerHeight = univariateFunction.value(xD);
+
+ // If parabolic estimate is bad then turn linear
+ if (isEstimateBad(yval, markerHeight)) {
+ int delta = xD - xval[1] > 0 ? 1 : -1;
+ final double[] xBad =
+ new double[] { xval[1], xval[1 + delta] };
+ final double[] yBad =
+ new double[] { yval[1], yval[1 + delta] };
+ MathArrays.sortInPlace(xBad, yBad);// since d can be +/- 1
+ univariateFunction = linear.interpolate(xBad, yBad);
+ markerHeight = univariateFunction.value(xD);
+ }
+ incrementPosition(d);
+ }
+ return markerHeight;
+ }
+
+ /**
+ * Check if parabolic/nonlinear estimate is bad by checking if the
+ * ordinate found is beyond the y[0] and y[2].
+ *
+ * @param y the array to get the bounds
+ * @param yD the estimate
+ * @return true if yD is a bad estimate
+ */
+ private boolean isEstimateBad(final double[] y, final double yD) {
+ return yD <= y[0] || yD >= y[2];
+ }
+
+ /**
+ * {@inheritDoc}<i>This equals method checks for marker attributes and
+ * as well checks if navigation pointers (next and previous) are the same
+ * between this and passed in object</i>
+ *
+ * @param o Other object
+ * @return true if this equals passed in other object o
+ */
+ @Override
+ public boolean equals(Object o) {
+ boolean result = false;
+ if (this == o) {
+ result = true;
+ } else if (o != null && o instanceof Marker) {
+ Marker that = (Marker) o;
+
+ result = Double.compare(markerHeight, that.markerHeight) == 0;
+ result =
+ result &&
+ Double.compare(intMarkerPosition,
+ that.intMarkerPosition) == 0;
+ result =
+ result &&
+ Double.compare(desiredMarkerPosition,
+ that.desiredMarkerPosition) == 0;
+ result =
+ result &&
+ Double.compare(desiredMarkerIncrement,
+ that.desiredMarkerIncrement) == 0;
+
+ result = result && next.index == that.next.index;
+ result = result && previous.index == that.previous.index;
+ }
+ return result;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public int hashCode() {
+ return Arrays.hashCode(new double[] {markerHeight, intMarkerPosition,
+ desiredMarkerIncrement, desiredMarkerPosition, previous.index, next.index});
+ }
+
+ /**
+ * Read Object to deserialize.
+ *
+ * @param anInstream Stream Object data
+ * @throws IOException thrown for IO Errors
+ * @throws ClassNotFoundException thrown for class not being found
+ */
+ private void readObject(ObjectInputStream anInstream)
+ throws ClassNotFoundException, IOException {
+ anInstream.defaultReadObject();
+ previous=next=this;
+ linear = new LinearInterpolator();
+ }
+
+ /**
+ * Clone this instance.
+ *
+ * @return cloned marker
+ */
+ @Override
+ public Object clone() {
+ return new Marker(markerHeight, desiredMarkerPosition,
+ desiredMarkerIncrement, intMarkerPosition);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public String toString() {
+ return String.format(
+ "index=%.0f,n=%.0f,np=%.2f,q=%.2f,dn=%.2f,prev=%d,next=%d",
+ (double) index, Precision.round(intMarkerPosition, 0),
+ Precision.round(desiredMarkerPosition, 2),
+ Precision.round(markerHeight, 2),
+ Precision.round(desiredMarkerIncrement, 2), previous.index,
+ next.index);
+ }
+ }
+
+ /**
+ * A simple fixed capacity list that has an upper bound to growth.
+ * Once its capacity is reached, {@code add} is a no-op, returning
+ * {@code false}.
+ *
+ * @param <E>
+ */
+ private static class FixedCapacityList<E> extends ArrayList<E> implements
+ Serializable {
+ /**
+ * Serialization Version Id
+ */
+ private static final long serialVersionUID = 2283952083075725479L;
+ /**
+ * Capacity of the list
+ */
+ private final int capacity;
+
+ /**
+ * This constructor constructs the list with given capacity and as well
+ * as stores the capacity
+ *
+ * @param fixedCapacity the capacity to be fixed for this list
+ */
+ FixedCapacityList(final int fixedCapacity) {
+ super(fixedCapacity);
+ this.capacity = fixedCapacity;
+ }
+
+ /**
+ * {@inheritDoc} In addition it checks if the {@link #size()} returns a
+ * size that is within capacity and if true it adds; otherwise the list
+ * contents are unchanged and {@code false} is returned.
+ *
+ * @return true if addition is successful and false otherwise
+ */
+ @Override
+ public boolean add(final E e) {
+ return size() < capacity ? super.add(e) : false;
+ }
+
+ /**
+ * {@inheritDoc} In addition it checks if the sum of Collection size and
+ * this instance's {@link #size()} returns a value that is within
+ * capacity and if true it adds the collection; otherwise the list
+ * contents are unchanged and {@code false} is returned.
+ *
+ * @return true if addition is successful and false otherwise
+ */
+ @Override
+ public boolean addAll(Collection<? extends E> collection) {
+ boolean isCollectionLess =
+ collection != null &&
+ collection.size() + size() <= capacity;
+ return isCollectionLess ? super.addAll(collection) : false;
+ }
+ }
+
+ /**
+ * A creation method to build Markers
+ *
+ * @param initialFive list of initial five elements
+ * @param p the quantile desired
+ * @return an instance of PSquareMarkers
+ */
+ public static PSquareMarkers newMarkers(final List<Double> initialFive,
+ final double p) {
+ return new Markers(initialFive, p);
+ }
+
+ /**
+ * An interface that encapsulates abstractions of the
+ * P-square algorithm markers as is explained in the original works. This
+ * interface is exposed with protected access to help in testability.
+ */
+ protected interface PSquareMarkers extends Cloneable {
+ /**
+ * Returns Percentile value computed thus far.
+ *
+ * @return percentile
+ */
+ double getPercentileValue();
+
+ /**
+ * A clone function to clone the current instance. It's created as an
+ * interface method as well for convenience though Cloneable is just a
+ * marker interface.
+ *
+ * @return clone of this instance
+ */
+ Object clone();
+
+ /**
+ * Returns the marker height (or percentile) of a given marker index.
+ *
+ * @param markerIndex is the index of marker in the marker array
+ * @return percentile value of the marker index passed
+ * @throws OutOfRangeException in case the index is not within [1-5]
+ */
+ double height(final int markerIndex);
+
+ /**
+ * Process a data point by moving the marker heights based on estimator.
+ *
+ * @param inputDataPoint is the data point passed
+ * @return computed percentile
+ */
+ double processDataPoint(final double inputDataPoint);
+
+ /**
+ * An Estimate of the percentile value of a given Marker
+ *
+ * @param index the marker's index in the array of markers
+ * @return percentile estimate
+ * @throws OutOfRangeException in case if index is not within [1-5]
+ */
+ double estimate(final int index);
+ }
+}
diff --git a/src/main/java/org/apache/commons/math3/stat/descriptive/rank/Percentile.java b/src/main/java/org/apache/commons/math3/stat/descriptive/rank/Percentile.java
new file mode 100644
index 0000000..bba9e7c
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/stat/descriptive/rank/Percentile.java
@@ -0,0 +1,1072 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math3.stat.descriptive.rank;
+
+import java.io.Serializable;
+import java.util.Arrays;
+import java.util.BitSet;
+
+import org.apache.commons.math3.exception.MathIllegalArgumentException;
+import org.apache.commons.math3.exception.MathUnsupportedOperationException;
+import org.apache.commons.math3.exception.NullArgumentException;
+import org.apache.commons.math3.exception.OutOfRangeException;
+import org.apache.commons.math3.exception.util.LocalizedFormats;
+import org.apache.commons.math3.stat.descriptive.AbstractUnivariateStatistic;
+import org.apache.commons.math3.stat.ranking.NaNStrategy;
+import org.apache.commons.math3.util.FastMath;
+import org.apache.commons.math3.util.KthSelector;
+import org.apache.commons.math3.util.MathArrays;
+import org.apache.commons.math3.util.MathUtils;
+import org.apache.commons.math3.util.MedianOf3PivotingStrategy;
+import org.apache.commons.math3.util.PivotingStrategyInterface;
+import org.apache.commons.math3.util.Precision;
+
+/**
+ * Provides percentile computation.
+ * <p>
+ * There are several commonly used methods for estimating percentiles (a.k.a.
+ * quantiles) based on sample data. For large samples, the different methods
+ * agree closely, but when sample sizes are small, different methods will give
+ * significantly different results. The algorithm implemented here works as follows:
+ * <ol>
+ * <li>Let <code>n</code> be the length of the (sorted) array and
+ * <code>0 < p <= 100</code> be the desired percentile.</li>
+ * <li>If <code> n = 1 </code> return the unique array element (regardless of
+ * the value of <code>p</code>); otherwise </li>
+ * <li>Compute the estimated percentile position
+ * <code> pos = p * (n + 1) / 100</code> and the difference, <code>d</code>
+ * between <code>pos</code> and <code>floor(pos)</code> (i.e. the fractional
+ * part of <code>pos</code>).</li>
+ * <li> If <code>pos < 1</code> return the smallest element in the array.</li>
+ * <li> Else if <code>pos >= n</code> return the largest element in the array.</li>
+ * <li> Else let <code>lower</code> be the element in position
+ * <code>floor(pos)</code> in the array and let <code>upper</code> be the
+ * next element in the array. Return <code>lower + d * (upper - lower)</code>
+ * </li>
+ * </ol></p>
+ * <p>
+ * To compute percentiles, the data must be at least partially ordered. Input
+ * arrays are copied and recursively partitioned using an ordering definition.
+ * The ordering used by <code>Arrays.sort(double[])</code> is the one determined
+ * by {@link java.lang.Double#compareTo(Double)}. This ordering makes
+ * <code>Double.NaN</code> larger than any other value (including
+ * <code>Double.POSITIVE_INFINITY</code>). Therefore, for example, the median
+ * (50th percentile) of
+ * <code>{0, 1, 2, 3, 4, Double.NaN}</code> evaluates to <code>2.5.</code></p>
+ * <p>
+ * Since percentile estimation usually involves interpolation between array
+ * elements, arrays containing <code>NaN</code> or infinite values will often
+ * result in <code>NaN</code> or infinite values returned.</p>
+ * <p>
+ * Further, to include different estimation types such as R1, R2 as mentioned in
+ * <a href="http://en.wikipedia.org/wiki/Quantile">Quantile page(wikipedia)</a>,
+ * a type specific NaN handling strategy is used to closely match with the
+ * typically observed results from popular tools like R(R1-R9), Excel(R7).</p>
+ * <p>
+ * Since 2.2, Percentile uses only selection instead of complete sorting
+ * and caches selection algorithm state between calls to the various
+ * {@code evaluate} methods. This greatly improves efficiency, both for a single
+ * percentile and multiple percentile computations. To maximize performance when
+ * multiple percentiles are computed based on the same data, users should set the
+ * data array once using either one of the {@link #evaluate(double[], double)} or
+ * {@link #setData(double[])} methods and thereafter {@link #evaluate(double)}
+ * with just the percentile provided.
+ * </p>
+ * <p>
+ * <strong>Note that this implementation is not synchronized.</strong> If
+ * multiple threads access an instance of this class concurrently, and at least
+ * one of the threads invokes the <code>increment()</code> or
+ * <code>clear()</code> method, it must be synchronized externally.</p>
+ *
+ */
+public class Percentile extends AbstractUnivariateStatistic implements Serializable {
+
+ /** Serializable version identifier */
+ private static final long serialVersionUID = -8091216485095130416L;
+
+ /** Maximum number of partitioning pivots cached (each level double the number of pivots). */
+ private static final int MAX_CACHED_LEVELS = 10;
+
+ /** Maximum number of cached pivots in the pivots cached array */
+ private static final int PIVOTS_HEAP_LENGTH = 0x1 << MAX_CACHED_LEVELS - 1;
+
+ /** Default KthSelector used with default pivoting strategy */
+ private final KthSelector kthSelector;
+
+ /** Any of the {@link EstimationType}s such as {@link EstimationType#LEGACY CM} can be used. */
+ private final EstimationType estimationType;
+
+ /** NaN Handling of the input as defined by {@link NaNStrategy} */
+ private final NaNStrategy nanStrategy;
+
+ /** Determines what percentile is computed when evaluate() is activated
+ * with no quantile argument */
+ private double quantile;
+
+ /** Cached pivots. */
+ private int[] cachedPivots;
+
+ /**
+ * Constructs a Percentile with the following defaults.
+ * <ul>
+ * <li>default quantile: 50.0, can be reset with {@link #setQuantile(double)}</li>
+ * <li>default estimation type: {@link EstimationType#LEGACY},
+ * can be reset with {@link #withEstimationType(EstimationType)}</li>
+ * <li>default NaN strategy: {@link NaNStrategy#REMOVED},
+ * can be reset with {@link #withNaNStrategy(NaNStrategy)}</li>
+ * <li>a KthSelector that makes use of {@link MedianOf3PivotingStrategy},
+ * can be reset with {@link #withKthSelector(KthSelector)}</li>
+ * </ul>
+ */
+ public Percentile() {
+ // No try-catch or advertised exception here - arg is valid
+ this(50.0);
+ }
+
+ /**
+ * Constructs a Percentile with the specific quantile value and the following
+ * <ul>
+ * <li>default method type: {@link EstimationType#LEGACY}</li>
+ * <li>default NaN strategy: {@link NaNStrategy#REMOVED}</li>
+ * <li>a Kth Selector : {@link KthSelector}</li>
+ * </ul>
+ * @param quantile the quantile
+ * @throws MathIllegalArgumentException if p is not greater than 0 and less
+ * than or equal to 100
+ */
+ public Percentile(final double quantile) throws MathIllegalArgumentException {
+ this(quantile, EstimationType.LEGACY, NaNStrategy.REMOVED,
+ new KthSelector(new MedianOf3PivotingStrategy()));
+ }
+
+ /**
+ * Copy constructor, creates a new {@code Percentile} identical
+ * to the {@code original}
+ *
+ * @param original the {@code Percentile} instance to copy
+ * @throws NullArgumentException if original is null
+ */
+ public Percentile(final Percentile original) throws NullArgumentException {
+
+ MathUtils.checkNotNull(original);
+ estimationType = original.getEstimationType();
+ nanStrategy = original.getNaNStrategy();
+ kthSelector = original.getKthSelector();
+
+ setData(original.getDataRef());
+ if (original.cachedPivots != null) {
+ System.arraycopy(original.cachedPivots, 0, cachedPivots, 0, original.cachedPivots.length);
+ }
+ setQuantile(original.quantile);
+
+ }
+
+ /**
+ * Constructs a Percentile with the specific quantile value,
+ * {@link EstimationType}, {@link NaNStrategy} and {@link KthSelector}.
+ *
+ * @param quantile the quantile to be computed
+ * @param estimationType one of the percentile {@link EstimationType estimation types}
+ * @param nanStrategy one of {@link NaNStrategy} to handle with NaNs
+ * @param kthSelector a {@link KthSelector} to use for pivoting during search
+ * @throws MathIllegalArgumentException if p is not within (0,100]
+ * @throws NullArgumentException if type or NaNStrategy passed is null
+ */
+ protected Percentile(final double quantile,
+ final EstimationType estimationType,
+ final NaNStrategy nanStrategy,
+ final KthSelector kthSelector)
+ throws MathIllegalArgumentException {
+ setQuantile(quantile);
+ cachedPivots = null;
+ MathUtils.checkNotNull(estimationType);
+ MathUtils.checkNotNull(nanStrategy);
+ MathUtils.checkNotNull(kthSelector);
+ this.estimationType = estimationType;
+ this.nanStrategy = nanStrategy;
+ this.kthSelector = kthSelector;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void setData(final double[] values) {
+ if (values == null) {
+ cachedPivots = null;
+ } else {
+ cachedPivots = new int[PIVOTS_HEAP_LENGTH];
+ Arrays.fill(cachedPivots, -1);
+ }
+ super.setData(values);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void setData(final double[] values, final int begin, final int length)
+ throws MathIllegalArgumentException {
+ if (values == null) {
+ cachedPivots = null;
+ } else {
+ cachedPivots = new int[PIVOTS_HEAP_LENGTH];
+ Arrays.fill(cachedPivots, -1);
+ }
+ super.setData(values, begin, length);
+ }
+
+ /**
+ * Returns the result of evaluating the statistic over the stored data.
+ * <p>
+ * The stored array is the one which was set by previous calls to
+ * {@link #setData(double[])}
+ * </p>
+ * @param p the percentile value to compute
+ * @return the value of the statistic applied to the stored data
+ * @throws MathIllegalArgumentException if p is not a valid quantile value
+ * (p must be greater than 0 and less than or equal to 100)
+ */
+ public double evaluate(final double p) throws MathIllegalArgumentException {
+ return evaluate(getDataRef(), p);
+ }
+
+ /**
+ * Returns an estimate of the <code>p</code>th percentile of the values
+ * in the <code>values</code> array.
+ * <p>
+ * Calls to this method do not modify the internal <code>quantile</code>
+ * state of this statistic.</p>
+ * <p>
+ * <ul>
+ * <li>Returns <code>Double.NaN</code> if <code>values</code> has length
+ * <code>0</code></li>
+ * <li>Returns (for any value of <code>p</code>) <code>values[0]</code>
+ * if <code>values</code> has length <code>1</code></li>
+ * <li>Throws <code>MathIllegalArgumentException</code> if <code>values</code>
+ * is null or p is not a valid quantile value (p must be greater than 0
+ * and less than or equal to 100) </li>
+ * </ul></p>
+ * <p>
+ * See {@link Percentile} for a description of the percentile estimation
+ * algorithm used.</p>
+ *
+ * @param values input array of values
+ * @param p the percentile value to compute
+ * @return the percentile value or Double.NaN if the array is empty
+ * @throws MathIllegalArgumentException if <code>values</code> is null
+ * or p is invalid
+ */
+ public double evaluate(final double[] values, final double p)
+ throws MathIllegalArgumentException {
+ test(values, 0, 0);
+ return evaluate(values, 0, values.length, p);
+ }
+
+ /**
+ * Returns an estimate of the <code>quantile</code>th percentile of the
+ * designated values in the <code>values</code> array. The quantile
+ * estimated is determined by the <code>quantile</code> property.
+ * <p>
+ * <ul>
+ * <li>Returns <code>Double.NaN</code> if <code>length = 0</code></li>
+ * <li>Returns (for any value of <code>quantile</code>)
+ * <code>values[begin]</code> if <code>length = 1 </code></li>
+ * <li>Throws <code>MathIllegalArgumentException</code> if <code>values</code>
+ * is null, or <code>start</code> or <code>length</code> is invalid</li>
+ * </ul></p>
+ * <p>
+ * See {@link Percentile} for a description of the percentile estimation
+ * algorithm used.</p>
+ *
+ * @param values the input array
+ * @param start index of the first array element to include
+ * @param length the number of elements to include
+ * @return the percentile value
+ * @throws MathIllegalArgumentException if the parameters are not valid
+ *
+ */
+ @Override
+ public double evaluate(final double[] values, final int start, final int length)
+ throws MathIllegalArgumentException {
+ return evaluate(values, start, length, quantile);
+ }
+
+ /**
+ * Returns an estimate of the <code>p</code>th percentile of the values
+ * in the <code>values</code> array, starting with the element in (0-based)
+ * position <code>begin</code> in the array and including <code>length</code>
+ * values.
+ * <p>
+ * Calls to this method do not modify the internal <code>quantile</code>
+ * state of this statistic.</p>
+ * <p>
+ * <ul>
+ * <li>Returns <code>Double.NaN</code> if <code>length = 0</code></li>
+ * <li>Returns (for any value of <code>p</code>) <code>values[begin]</code>
+ * if <code>length = 1 </code></li>
+ * <li>Throws <code>MathIllegalArgumentException</code> if <code>values</code>
+ * is null , <code>begin</code> or <code>length</code> is invalid, or
+ * <code>p</code> is not a valid quantile value (p must be greater than 0
+ * and less than or equal to 100)</li>
+ * </ul></p>
+ * <p>
+ * See {@link Percentile} for a description of the percentile estimation
+ * algorithm used.</p>
+ *
+ * @param values array of input values
+ * @param p the percentile to compute
+ * @param begin the first (0-based) element to include in the computation
+ * @param length the number of array elements to include
+ * @return the percentile value
+ * @throws MathIllegalArgumentException if the parameters are not valid or the
+ * input array is null
+ */
+ public double evaluate(final double[] values, final int begin,
+ final int length, final double p)
+ throws MathIllegalArgumentException {
+
+ test(values, begin, length);
+ if (p > 100 || p <= 0) {
+ throw new OutOfRangeException(
+ LocalizedFormats.OUT_OF_BOUNDS_QUANTILE_VALUE, p, 0, 100);
+ }
+ if (length == 0) {
+ return Double.NaN;
+ }
+ if (length == 1) {
+ return values[begin]; // always return single value for n = 1
+ }
+
+ final double[] work = getWorkArray(values, begin, length);
+ final int[] pivotsHeap = getPivots(values);
+ return work.length == 0 ? Double.NaN :
+ estimationType.evaluate(work, pivotsHeap, p, kthSelector);
+ }
+
+ /** Select a pivot index as the median of three
+ * <p>
+ * <b>Note:</b> With the effect of allowing {@link KthSelector} to be set on
+ * {@link Percentile} instances(thus indirectly {@link PivotingStrategy})
+ * this method wont take effect any more and hence is unsupported.
+ * @param work data array
+ * @param begin index of the first element of the slice
+ * @param end index after the last element of the slice
+ * @return the index of the median element chosen between the
+ * first, the middle and the last element of the array slice
+ * @deprecated Please refrain from using this method (as it wont take effect)
+ * and instead use {@link Percentile#withKthSelector(newKthSelector)} if
+ * required.
+ *
+ */
+ @Deprecated
+ int medianOf3(final double[] work, final int begin, final int end) {
+ return new MedianOf3PivotingStrategy().pivotIndex(work, begin, end);
+ //throw new MathUnsupportedOperationException();
+ }
+
+ /**
+ * Returns the value of the quantile field (determines what percentile is
+ * computed when evaluate() is called with no quantile argument).
+ *
+ * @return quantile set while construction or {@link #setQuantile(double)}
+ */
+ public double getQuantile() {
+ return quantile;
+ }
+
+ /**
+ * Sets the value of the quantile field (determines what percentile is
+ * computed when evaluate() is called with no quantile argument).
+ *
+ * @param p a value between 0 < p <= 100
+ * @throws MathIllegalArgumentException if p is not greater than 0 and less
+ * than or equal to 100
+ */
+ public void setQuantile(final double p) throws MathIllegalArgumentException {
+ if (p <= 0 || p > 100) {
+ throw new OutOfRangeException(
+ LocalizedFormats.OUT_OF_BOUNDS_QUANTILE_VALUE, p, 0, 100);
+ }
+ quantile = p;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public Percentile copy() {
+ return new Percentile(this);
+ }
+
+ /**
+ * Copies source to dest.
+ * @param source Percentile to copy
+ * @param dest Percentile to copy to
+ * @exception MathUnsupportedOperationException always thrown since 3.4
+ * @deprecated as of 3.4 this method does not work anymore, as it fails to
+ * copy internal states between instances configured with different
+ * {@link EstimationType estimation type}, {@link NaNStrategy NaN handling strategies}
+ * and {@link KthSelector kthSelector}, it therefore always
+ * throw {@link MathUnsupportedOperationException}
+ */
+ @Deprecated
+ public static void copy(final Percentile source, final Percentile dest)
+ throws MathUnsupportedOperationException {
+ throw new MathUnsupportedOperationException();
+ }
+
+ /**
+ * Get the work array to operate. Makes use of prior {@code storedData} if
+ * it exists or else do a check on NaNs and copy a subset of the array
+ * defined by begin and length parameters. The set {@link #nanStrategy} will
+ * be used to either retain/remove/replace any NaNs present before returning
+ * the resultant array.
+ *
+ * @param values the array of numbers
+ * @param begin index to start reading the array
+ * @param length the length of array to be read from the begin index
+ * @return work array sliced from values in the range [begin,begin+length)
+ * @throws MathIllegalArgumentException if values or indices are invalid
+ */
+ protected double[] getWorkArray(final double[] values, final int begin, final int length) {
+ final double[] work;
+ if (values == getDataRef()) {
+ work = getDataRef();
+ } else {
+ switch (nanStrategy) {
+ case MAXIMAL:// Replace NaNs with +INFs
+ work = replaceAndSlice(values, begin, length, Double.NaN, Double.POSITIVE_INFINITY);
+ break;
+ case MINIMAL:// Replace NaNs with -INFs
+ work = replaceAndSlice(values, begin, length, Double.NaN, Double.NEGATIVE_INFINITY);
+ break;
+ case REMOVED:// Drop NaNs from data
+ work = removeAndSlice(values, begin, length, Double.NaN);
+ break;
+ case FAILED:// just throw exception as NaN is un-acceptable
+ work = copyOf(values, begin, length);
+ MathArrays.checkNotNaN(work);
+ break;
+ default: //FIXED
+ work = copyOf(values,begin,length);
+ break;
+ }
+ }
+ return work;
+ }
+
+ /**
+ * Make a copy of the array for the slice defined by array part from
+ * [begin, begin+length)
+ * @param values the input array
+ * @param begin start index of the array to include
+ * @param length number of elements to include from begin
+ * @return copy of a slice of the original array
+ */
+ private static double[] copyOf(final double[] values, final int begin, final int length) {
+ MathArrays.verifyValues(values, begin, length);
+ return MathArrays.copyOfRange(values, begin, begin + length);
+ }
+
+ /**
+ * Replace every occurrence of a given value with a replacement value in a
+ * copied slice of array defined by array part from [begin, begin+length).
+ * @param values the input array
+ * @param begin start index of the array to include
+ * @param length number of elements to include from begin
+ * @param original the value to be replaced with
+ * @param replacement the value to be used for replacement
+ * @return the copy of sliced array with replaced values
+ */
+ private static double[] replaceAndSlice(final double[] values,
+ final int begin, final int length,
+ final double original,
+ final double replacement) {
+ final double[] temp = copyOf(values, begin, length);
+ for(int i = 0; i < length; i++) {
+ temp[i] = Precision.equalsIncludingNaN(original, temp[i]) ?
+ replacement : temp[i];
+ }
+ return temp;
+ }
+
+ /**
+ * Remove the occurrence of a given value in a copied slice of array
+ * defined by the array part from [begin, begin+length).
+ * @param values the input array
+ * @param begin start index of the array to include
+ * @param length number of elements to include from begin
+ * @param removedValue the value to be removed from the sliced array
+ * @return the copy of the sliced array after removing the removedValue
+ */
+ private static double[] removeAndSlice(final double[] values,
+ final int begin, final int length,
+ final double removedValue) {
+ MathArrays.verifyValues(values, begin, length);
+ final double[] temp;
+ //BitSet(length) to indicate where the removedValue is located
+ final BitSet bits = new BitSet(length);
+ for (int i = begin; i < begin+length; i++) {
+ if (Precision.equalsIncludingNaN(removedValue, values[i])) {
+ bits.set(i - begin);
+ }
+ }
+ //Check if empty then create a new copy
+ if (bits.isEmpty()) {
+ temp = copyOf(values, begin, length); // Nothing removed, just copy
+ } else if(bits.cardinality() == length){
+ temp = new double[0]; // All removed, just empty
+ }else { // Some removable, so new
+ temp = new double[length - bits.cardinality()];
+ int start = begin; //start index from source array (i.e values)
+ int dest = 0; //dest index in destination array(i.e temp)
+ int nextOne = -1; //nextOne is the index of bit set of next one
+ int bitSetPtr = 0; //bitSetPtr is start index pointer of bitset
+ while ((nextOne = bits.nextSetBit(bitSetPtr)) != -1) {
+ final int lengthToCopy = nextOne - bitSetPtr;
+ System.arraycopy(values, start, temp, dest, lengthToCopy);
+ dest += lengthToCopy;
+ start = begin + (bitSetPtr = bits.nextClearBit(nextOne));
+ }
+ //Copy any residue past start index till begin+length
+ if (start < begin + length) {
+ System.arraycopy(values,start,temp,dest,begin + length - start);
+ }
+ }
+ return temp;
+ }
+
+ /**
+ * Get pivots which is either cached or a newly created one
+ *
+ * @param values array containing the input numbers
+ * @return cached pivots or a newly created one
+ */
+ private int[] getPivots(final double[] values) {
+ final int[] pivotsHeap;
+ if (values == getDataRef()) {
+ pivotsHeap = cachedPivots;
+ } else {
+ pivotsHeap = new int[PIVOTS_HEAP_LENGTH];
+ Arrays.fill(pivotsHeap, -1);
+ }
+ return pivotsHeap;
+ }
+
+ /**
+ * Get the estimation {@link EstimationType type} used for computation.
+ *
+ * @return the {@code estimationType} set
+ */
+ public EstimationType getEstimationType() {
+ return estimationType;
+ }
+
+ /**
+ * Build a new instance similar to the current one except for the
+ * {@link EstimationType estimation type}.
+ * <p>
+ * This method is intended to be used as part of a fluent-type builder
+ * pattern. Building finely tune instances should be done as follows:
+ * </p>
+ * <pre>
+ * Percentile customized = new Percentile(quantile).
+ * withEstimationType(estimationType).
+ * withNaNStrategy(nanStrategy).
+ * withKthSelector(kthSelector);
+ * </pre>
+ * <p>
+ * If any of the {@code withXxx} method is omitted, the default value for
+ * the corresponding customization parameter will be used.
+ * </p>
+ * @param newEstimationType estimation type for the new instance
+ * @return a new instance, with changed estimation type
+ * @throws NullArgumentException when newEstimationType is null
+ */
+ public Percentile withEstimationType(final EstimationType newEstimationType) {
+ return new Percentile(quantile, newEstimationType, nanStrategy, kthSelector);
+ }
+
+ /**
+ * Get the {@link NaNStrategy NaN Handling} strategy used for computation.
+ * @return {@code NaN Handling} strategy set during construction
+ */
+ public NaNStrategy getNaNStrategy() {
+ return nanStrategy;
+ }
+
+ /**
+ * Build a new instance similar to the current one except for the
+ * {@link NaNStrategy NaN handling} strategy.
+ * <p>
+ * This method is intended to be used as part of a fluent-type builder
+ * pattern. Building finely tune instances should be done as follows:
+ * </p>
+ * <pre>
+ * Percentile customized = new Percentile(quantile).
+ * withEstimationType(estimationType).
+ * withNaNStrategy(nanStrategy).
+ * withKthSelector(kthSelector);
+ * </pre>
+ * <p>
+ * If any of the {@code withXxx} method is omitted, the default value for
+ * the corresponding customization parameter will be used.
+ * </p>
+ * @param newNaNStrategy NaN strategy for the new instance
+ * @return a new instance, with changed NaN handling strategy
+ * @throws NullArgumentException when newNaNStrategy is null
+ */
+ public Percentile withNaNStrategy(final NaNStrategy newNaNStrategy) {
+ return new Percentile(quantile, estimationType, newNaNStrategy, kthSelector);
+ }
+
+ /**
+ * Get the {@link KthSelector kthSelector} used for computation.
+ * @return the {@code kthSelector} set
+ */
+ public KthSelector getKthSelector() {
+ return kthSelector;
+ }
+
+ /**
+ * Get the {@link PivotingStrategyInterface} used in KthSelector for computation.
+ * @return the pivoting strategy set
+ */
+ public PivotingStrategyInterface getPivotingStrategy() {
+ return kthSelector.getPivotingStrategy();
+ }
+
+ /**
+ * Build a new instance similar to the current one except for the
+ * {@link KthSelector kthSelector} instance specifically set.
+ * <p>
+ * This method is intended to be used as part of a fluent-type builder
+ * pattern. Building finely tune instances should be done as follows:
+ * </p>
+ * <pre>
+ * Percentile customized = new Percentile(quantile).
+ * withEstimationType(estimationType).
+ * withNaNStrategy(nanStrategy).
+ * withKthSelector(newKthSelector);
+ * </pre>
+ * <p>
+ * If any of the {@code withXxx} method is omitted, the default value for
+ * the corresponding customization parameter will be used.
+ * </p>
+ * @param newKthSelector KthSelector for the new instance
+ * @return a new instance, with changed KthSelector
+ * @throws NullArgumentException when newKthSelector is null
+ */
+ public Percentile withKthSelector(final KthSelector newKthSelector) {
+ return new Percentile(quantile, estimationType, nanStrategy,
+ newKthSelector);
+ }
+
+ /**
+ * An enum for various estimation strategies of a percentile referred in
+ * <a href="http://en.wikipedia.org/wiki/Quantile">wikipedia on quantile</a>
+ * with the names of enum matching those of types mentioned in
+ * wikipedia.
+ * <p>
+ * Each enum corresponding to the specific type of estimation in wikipedia
+ * implements the respective formulae that specializes in the below aspects
+ * <ul>
+ * <li>An <b>index method</b> to calculate approximate index of the
+ * estimate</li>
+ * <li>An <b>estimate method</b> to estimate a value found at the earlier
+ * computed index</li>
+ * <li>A <b> minLimit</b> on the quantile for which first element of sorted
+ * input is returned as an estimate </li>
+ * <li>A <b> maxLimit</b> on the quantile for which last element of sorted
+ * input is returned as an estimate </li>
+ * </ul>
+ * <p>
+ * Users can now create {@link Percentile} by explicitly passing this enum;
+ * such as by invoking {@link Percentile#withEstimationType(EstimationType)}
+ * <p>
+ * References:
+ * <ol>
+ * <li>
+ * <a href="http://en.wikipedia.org/wiki/Quantile">Wikipedia on quantile</a>
+ * </li>
+ * <li>
+ * <a href="https://www.amherst.edu/media/view/129116/.../Sample+Quantiles.pdf">
+ * Hyndman, R. J. and Fan, Y. (1996) Sample quantiles in statistical
+ * packages, American Statistician 50, 361–365</a> </li>
+ * <li>
+ * <a href="http://stat.ethz.ch/R-manual/R-devel/library/stats/html/quantile.html">
+ * R-Manual </a></li>
+ * </ol>
+ *
+ */
+ public enum EstimationType {
+ /**
+ * This is the default type used in the {@link Percentile}.This method
+ * has the following formulae for index and estimates<br>
+ * \( \begin{align}
+ * &amp;index = (N+1)p\ \\
+ * &amp;estimate = x_{\lceil h\,-\,1/2 \rceil} \\
+ * &amp;minLimit = 0 \\
+ * &amp;maxLimit = 1 \\
+ * \end{align}\)
+ */
+ LEGACY("Legacy Apache Commons Math") {
+ /**
+ * {@inheritDoc}.This method in particular makes use of existing
+ * Apache Commons Math style of picking up the index.
+ */
+ @Override
+ protected double index(final double p, final int length) {
+ final double minLimit = 0d;
+ final double maxLimit = 1d;
+ return Double.compare(p, minLimit) == 0 ? 0 :
+ Double.compare(p, maxLimit) == 0 ?
+ length : p * (length + 1);
+ }
+ },
+ /**
+ * The method R_1 has the following formulae for index and estimates<br>
+ * \( \begin{align}
+ * &amp;index= Np + 1/2\, \\
+ * &amp;estimate= x_{\lceil h\,-\,1/2 \rceil} \\
+ * &amp;minLimit = 0 \\
+ * \end{align}\)
+ */
+ R_1("R-1") {
+
+ @Override
+ protected double index(final double p, final int length) {
+ final double minLimit = 0d;
+ return Double.compare(p, minLimit) == 0 ? 0 : length * p + 0.5;
+ }
+
+ /**
+ * {@inheritDoc}This method in particular for R_1 uses ceil(pos-0.5)
+ */
+ @Override
+ protected double estimate(final double[] values,
+ final int[] pivotsHeap, final double pos,
+ final int length, final KthSelector selector) {
+ return super.estimate(values, pivotsHeap, FastMath.ceil(pos - 0.5), length, selector);
+ }
+
+ },
+ /**
+ * The method R_2 has the following formulae for index and estimates<br>
+ * \( \begin{align}
+ * &amp;index= Np + 1/2\, \\
+ * &amp;estimate=\frac{x_{\lceil h\,-\,1/2 \rceil} +
+ * x_{\lfloor h\,+\,1/2 \rfloor}}{2} \\
+ * &amp;minLimit = 0 \\
+ * &amp;maxLimit = 1 \\
+ * \end{align}\)
+ */
+ R_2("R-2") {
+
+ @Override
+ protected double index(final double p, final int length) {
+ final double minLimit = 0d;
+ final double maxLimit = 1d;
+ return Double.compare(p, maxLimit) == 0 ? length :
+ Double.compare(p, minLimit) == 0 ? 0 : length * p + 0.5;
+ }
+
+ /**
+ * {@inheritDoc}This method in particular for R_2 averages the
+ * values at ceil(p+0.5) and floor(p-0.5).
+ */
+ @Override
+ protected double estimate(final double[] values,
+ final int[] pivotsHeap, final double pos,
+ final int length, final KthSelector selector) {
+ final double low =
+ super.estimate(values, pivotsHeap, FastMath.ceil(pos - 0.5), length, selector);
+ final double high =
+ super.estimate(values, pivotsHeap,FastMath.floor(pos + 0.5), length, selector);
+ return (low + high) / 2;
+ }
+
+ },
+ /**
+ * The method R_3 has the following formulae for index and estimates<br>
+ * \( \begin{align}
+ * &amp;index= Np \\
+ * &amp;estimate= x_{\lfloor h \rceil}\, \\
+ * &amp;minLimit = 0.5/N \\
+ * \end{align}\)
+ */
+ R_3("R-3") {
+ @Override
+ protected double index(final double p, final int length) {
+ final double minLimit = 1d/2 / length;
+ return Double.compare(p, minLimit) <= 0 ?
+ 0 : FastMath.rint(length * p);
+ }
+
+ },
+ /**
+ * The method R_4 has the following formulae for index and estimates<br>
+ * \( \begin{align}
+ * &amp;index= Np\, \\
+ * &amp;estimate= x_{\lfloor h \rfloor} + (h -
+ * \lfloor h \rfloor) (x_{\lfloor h \rfloor + 1} - x_{\lfloor h
+ * \rfloor}) \\
+ * &amp;minLimit = 1/N \\
+ * &amp;maxLimit = 1 \\
+ * \end{align}\)
+ */
+ R_4("R-4") {
+ @Override
+ protected double index(final double p, final int length) {
+ final double minLimit = 1d / length;
+ final double maxLimit = 1d;
+ return Double.compare(p, minLimit) < 0 ? 0 :
+ Double.compare(p, maxLimit) == 0 ? length : length * p;
+ }
+
+ },
+ /**
+ * The method R_5 has the following formulae for index and estimates<br>
+ * \( \begin{align}
+ * &amp;index= Np + 1/2\\
+ * &amp;estimate= x_{\lfloor h \rfloor} + (h -
+ * \lfloor h \rfloor) (x_{\lfloor h \rfloor + 1} - x_{\lfloor h
+ * \rfloor}) \\
+ * &amp;minLimit = 0.5/N \\
+ * &amp;maxLimit = (N-0.5)/N
+ * \end{align}\)
+ */
+ R_5("R-5"){
+
+ @Override
+ protected double index(final double p, final int length) {
+ final double minLimit = 1d/2 / length;
+ final double maxLimit = (length - 0.5) / length;
+ return Double.compare(p, minLimit) < 0 ? 0 :
+ Double.compare(p, maxLimit) >= 0 ?
+ length : length * p + 0.5;
+ }
+ },
+ /**
+ * The method R_6 has the following formulae for index and estimates<br>
+ * \( \begin{align}
+ * &amp;index= (N + 1)p \\
+ * &amp;estimate= x_{\lfloor h \rfloor} + (h -
+ * \lfloor h \rfloor) (x_{\lfloor h \rfloor + 1} - x_{\lfloor h
+ * \rfloor}) \\
+ * &amp;minLimit = 1/(N+1) \\
+ * &amp;maxLimit = N/(N+1) \\
+ * \end{align}\)
+ * <p>
+ * <b>Note:</b> This method computes the index in a manner very close to
+ * the default Commons Math Percentile existing implementation. However
+ * the difference to be noted is in picking up the limits with which
+ * first element (p&lt;1(N+1)) and last elements (p&gt;N/(N+1))are done.
+ * While in default case; these are done with p=0 and p=1 respectively.
+ */
+ R_6("R-6"){
+
+ @Override
+ protected double index(final double p, final int length) {
+ final double minLimit = 1d / (length + 1);
+ final double maxLimit = 1d * length / (length + 1);
+ return Double.compare(p, minLimit) < 0 ? 0 :
+ Double.compare(p, maxLimit) >= 0 ?
+ length : (length + 1) * p;
+ }
+ },
+
+ /**
+ * The method R_7 implements Microsoft Excel style computation has the
+ * following formulae for index and estimates.<br>
+ * \( \begin{align}
+ * &amp;index = (N-1)p + 1 \\
+ * &amp;estimate = x_{\lfloor h \rfloor} + (h -
+ * \lfloor h \rfloor) (x_{\lfloor h \rfloor + 1} - x_{\lfloor h
+ * \rfloor}) \\
+ * &amp;minLimit = 0 \\
+ * &amp;maxLimit = 1 \\
+ * \end{align}\)
+ */
+ R_7("R-7") {
+ @Override
+ protected double index(final double p, final int length) {
+ final double minLimit = 0d;
+ final double maxLimit = 1d;
+ return Double.compare(p, minLimit) == 0 ? 0 :
+ Double.compare(p, maxLimit) == 0 ?
+ length : 1 + (length - 1) * p;
+ }
+
+ },
+
+ /**
+ * The method R_8 has the following formulae for index and estimates<br>
+ * \( \begin{align}
+ * &amp;index = (N + 1/3)p + 1/3 \\
+ * &amp;estimate = x_{\lfloor h \rfloor} + (h -
+ \lfloor h \rfloor) (x_{\lfloor h \rfloor + 1} - x_{\lfloor h
+ * \rfloor}) \\
+ * &amp;minLimit = (2/3)/(N+1/3) \\
+ * &amp;maxLimit = (N-1/3)/(N+1/3) \\
+ * \end{align}\)
+ * <p>
+ * As per Ref [2,3] this approach is most recommended as it provides
+ * an approximate median-unbiased estimate regardless of distribution.
+ */
+ R_8("R-8") {
+ @Override
+ protected double index(final double p, final int length) {
+ final double minLimit = 2 * (1d / 3) / (length + 1d / 3);
+ final double maxLimit =
+ (length - 1d / 3) / (length + 1d / 3);
+ return Double.compare(p, minLimit) < 0 ? 0 :
+ Double.compare(p, maxLimit) >= 0 ? length :
+ (length + 1d / 3) * p + 1d / 3;
+ }
+ },
+
+ /**
+ * The method R_9 has the following formulae for index and estimates<br>
+ * \( \begin{align}
+ * &amp;index = (N + 1/4)p + 3/8\\
+ * &amp;estimate = x_{\lfloor h \rfloor} + (h -
+ \lfloor h \rfloor) (x_{\lfloor h \rfloor + 1} - x_{\lfloor h
+ * \rfloor}) \\
+ * &amp;minLimit = (5/8)/(N+1/4) \\
+ * &amp;maxLimit = (N-3/8)/(N+1/4) \\
+ * \end{align}\)
+ */
+ R_9("R-9") {
+ @Override
+ protected double index(final double p, final int length) {
+ final double minLimit = 5d/8 / (length + 0.25);
+ final double maxLimit = (length - 3d/8) / (length + 0.25);
+ return Double.compare(p, minLimit) < 0 ? 0 :
+ Double.compare(p, maxLimit) >= 0 ? length :
+ (length + 0.25) * p + 3d/8;
+ }
+
+ },
+ ;
+
+ /** Simple name such as R-1, R-2 corresponding to those in wikipedia. */
+ private final String name;
+
+ /**
+ * Constructor
+ *
+ * @param type name of estimation type as per wikipedia
+ */
+ EstimationType(final String type) {
+ this.name = type;
+ }
+
+ /**
+ * Finds the index of array that can be used as starting index to
+ * {@link #estimate(double[], int[], double, int, KthSelector) estimate}
+ * percentile. The calculation of index calculation is specific to each
+ * {@link EstimationType}.
+ *
+ * @param p the p<sup>th</sup> quantile
+ * @param length the total number of array elements in the work array
+ * @return a computed real valued index as explained in the wikipedia
+ */
+ protected abstract double index(final double p, final int length);
+
+ /**
+ * Estimation based on K<sup>th</sup> selection. This may be overridden
+ * in specific enums to compute slightly different estimations.
+ *
+ * @param work array of numbers to be used for finding the percentile
+ * @param pos indicated positional index prior computed from calling
+ * {@link #index(double, int)}
+ * @param pivotsHeap an earlier populated cache if exists; will be used
+ * @param length size of array considered
+ * @param selector a {@link KthSelector} used for pivoting during search
+ * @return estimated percentile
+ */
+ protected double estimate(final double[] work, final int[] pivotsHeap,
+ final double pos, final int length,
+ final KthSelector selector) {
+
+ final double fpos = FastMath.floor(pos);
+ final int intPos = (int) fpos;
+ final double dif = pos - fpos;
+
+ if (pos < 1) {
+ return selector.select(work, pivotsHeap, 0);
+ }
+ if (pos >= length) {
+ return selector.select(work, pivotsHeap, length - 1);
+ }
+
+ final double lower = selector.select(work, pivotsHeap, intPos - 1);
+ final double upper = selector.select(work, pivotsHeap, intPos);
+ return lower + dif * (upper - lower);
+ }
+
+ /**
+ * Evaluate method to compute the percentile for a given bounded array
+ * using earlier computed pivots heap.<br>
+ * This basically calls the {@link #index(double, int) index} and then
+ * {@link #estimate(double[], int[], double, int, KthSelector) estimate}
+ * functions to return the estimated percentile value.
+ *
+ * @param work array of numbers to be used for finding the percentile
+ * @param pivotsHeap a prior cached heap which can speed up estimation
+ * @param p the p<sup>th</sup> quantile to be computed
+ * @param selector a {@link KthSelector} used for pivoting during search
+ * @return estimated percentile
+ * @throws OutOfRangeException if p is out of range
+ * @throws NullArgumentException if work array is null
+ */
+ protected double evaluate(final double[] work, final int[] pivotsHeap, final double p,
+ final KthSelector selector) {
+ MathUtils.checkNotNull(work);
+ if (p > 100 || p <= 0) {
+ throw new OutOfRangeException(LocalizedFormats.OUT_OF_BOUNDS_QUANTILE_VALUE,
+ p, 0, 100);
+ }
+ return estimate(work, pivotsHeap, index(p/100d, work.length), work.length, selector);
+ }
+
+ /**
+ * Evaluate method to compute the percentile for a given bounded array.
+ * This basically calls the {@link #index(double, int) index} and then
+ * {@link #estimate(double[], int[], double, int, KthSelector) estimate}
+ * functions to return the estimated percentile value. Please
+ * note that this method does not make use of cached pivots.
+ *
+ * @param work array of numbers to be used for finding the percentile
+ * @param p the p<sup>th</sup> quantile to be computed
+ * @return estimated percentile
+ * @param selector a {@link KthSelector} used for pivoting during search
+ * @throws OutOfRangeException if length or p is out of range
+ * @throws NullArgumentException if work array is null
+ */
+ public double evaluate(final double[] work, final double p, final KthSelector selector) {
+ return this.evaluate(work, null, p, selector);
+ }
+
+ /**
+ * Gets the name of the enum
+ *
+ * @return the name
+ */
+ String getName() {
+ return name;
+ }
+ }
+}
diff --git a/src/main/java/org/apache/commons/math3/stat/descriptive/rank/package-info.java b/src/main/java/org/apache/commons/math3/stat/descriptive/rank/package-info.java
new file mode 100644
index 0000000..da37b37
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/stat/descriptive/rank/package-info.java
@@ -0,0 +1,20 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/**
+ * Summary statistics based on ranks.
+ */
+package org.apache.commons.math3.stat.descriptive.rank;
diff --git a/src/main/java/org/apache/commons/math3/stat/descriptive/summary/Product.java b/src/main/java/org/apache/commons/math3/stat/descriptive/summary/Product.java
new file mode 100644
index 0000000..7d313a5
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/stat/descriptive/summary/Product.java
@@ -0,0 +1,230 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math3.stat.descriptive.summary;
+
+import java.io.Serializable;
+
+import org.apache.commons.math3.exception.MathIllegalArgumentException;
+import org.apache.commons.math3.exception.NullArgumentException;
+import org.apache.commons.math3.stat.descriptive.AbstractStorelessUnivariateStatistic;
+import org.apache.commons.math3.stat.descriptive.WeightedEvaluation;
+import org.apache.commons.math3.util.FastMath;
+import org.apache.commons.math3.util.MathUtils;
+
+/**
+ * Returns the product of the available values.
+ * <p>
+ * If there are no values in the dataset, then 1 is returned.
+ * If any of the values are
+ * <code>NaN</code>, then <code>NaN</code> is returned.</p>
+ * <p>
+ * <strong>Note that this implementation is not synchronized.</strong> If
+ * multiple threads access an instance of this class concurrently, and at least
+ * one of the threads invokes the <code>increment()</code> or
+ * <code>clear()</code> method, it must be synchronized externally.</p>
+ *
+ */
+public class Product extends AbstractStorelessUnivariateStatistic implements Serializable, WeightedEvaluation {
+
+ /** Serializable version identifier */
+ private static final long serialVersionUID = 2824226005990582538L;
+
+ /**The number of values that have been added */
+ private long n;
+
+ /**
+ * The current Running Product.
+ */
+ private double value;
+
+ /**
+ * Create a Product instance
+ */
+ public Product() {
+ n = 0;
+ value = 1;
+ }
+
+ /**
+ * Copy constructor, creates a new {@code Product} identical
+ * to the {@code original}
+ *
+ * @param original the {@code Product} instance to copy
+ * @throws NullArgumentException if original is null
+ */
+ public Product(Product original) throws NullArgumentException {
+ copy(original, this);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void increment(final double d) {
+ value *= d;
+ n++;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public double getResult() {
+ return value;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public long getN() {
+ return n;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void clear() {
+ value = 1;
+ n = 0;
+ }
+
+ /**
+ * Returns the product of the entries in the specified portion of
+ * the input array, or <code>Double.NaN</code> if the designated subarray
+ * is empty.
+ * <p>
+ * Throws <code>MathIllegalArgumentException</code> if the array is null.</p>
+ *
+ * @param values the input array
+ * @param begin index of the first array element to include
+ * @param length the number of elements to include
+ * @return the product of the values or 1 if length = 0
+ * @throws MathIllegalArgumentException if the array is null or the array index
+ * parameters are not valid
+ */
+ @Override
+ public double evaluate(final double[] values, final int begin, final int length)
+ throws MathIllegalArgumentException {
+ double product = Double.NaN;
+ if (test(values, begin, length, true)) {
+ product = 1.0;
+ for (int i = begin; i < begin + length; i++) {
+ product *= values[i];
+ }
+ }
+ return product;
+ }
+
+ /**
+ * <p>Returns the weighted product of the entries in the specified portion of
+ * the input array, or <code>Double.NaN</code> if the designated subarray
+ * is empty.</p>
+ *
+ * <p>Throws <code>MathIllegalArgumentException</code> if any of the following are true:
+ * <ul><li>the values array is null</li>
+ * <li>the weights array is null</li>
+ * <li>the weights array does not have the same length as the values array</li>
+ * <li>the weights array contains one or more infinite values</li>
+ * <li>the weights array contains one or more NaN values</li>
+ * <li>the weights array contains negative values</li>
+ * <li>the start and length arguments do not determine a valid array</li>
+ * </ul></p>
+ *
+ * <p>Uses the formula, <pre>
+ * weighted product = &prod;values[i]<sup>weights[i]</sup>
+ * </pre>
+ * that is, the weights are applied as exponents when computing the weighted product.</p>
+ *
+ * @param values the input array
+ * @param weights the weights array
+ * @param begin index of the first array element to include
+ * @param length the number of elements to include
+ * @return the product of the values or 1 if length = 0
+ * @throws MathIllegalArgumentException if the parameters are not valid
+ * @since 2.1
+ */
+ public double evaluate(final double[] values, final double[] weights,
+ final int begin, final int length) throws MathIllegalArgumentException {
+ double product = Double.NaN;
+ if (test(values, weights, begin, length, true)) {
+ product = 1.0;
+ for (int i = begin; i < begin + length; i++) {
+ product *= FastMath.pow(values[i], weights[i]);
+ }
+ }
+ return product;
+ }
+
+ /**
+ * <p>Returns the weighted product of the entries in the input array.</p>
+ *
+ * <p>Throws <code>MathIllegalArgumentException</code> if any of the following are true:
+ * <ul><li>the values array is null</li>
+ * <li>the weights array is null</li>
+ * <li>the weights array does not have the same length as the values array</li>
+ * <li>the weights array contains one or more infinite values</li>
+ * <li>the weights array contains one or more NaN values</li>
+ * <li>the weights array contains negative values</li>
+ * </ul></p>
+ *
+ * <p>Uses the formula, <pre>
+ * weighted product = &prod;values[i]<sup>weights[i]</sup>
+ * </pre>
+ * that is, the weights are applied as exponents when computing the weighted product.</p>
+ *
+ * @param values the input array
+ * @param weights the weights array
+ * @return the product of the values or Double.NaN if length = 0
+ * @throws MathIllegalArgumentException if the parameters are not valid
+ * @since 2.1
+ */
+ public double evaluate(final double[] values, final double[] weights)
+ throws MathIllegalArgumentException {
+ return evaluate(values, weights, 0, values.length);
+ }
+
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public Product copy() {
+ Product result = new Product();
+ // No try-catch or advertised exception because args are valid
+ copy(this, result);
+ return result;
+ }
+
+ /**
+ * Copies source to dest.
+ * <p>Neither source nor dest can be null.</p>
+ *
+ * @param source Product to copy
+ * @param dest Product to copy to
+ * @throws NullArgumentException if either source or dest is null
+ */
+ public static void copy(Product source, Product dest)
+ throws NullArgumentException {
+ MathUtils.checkNotNull(source);
+ MathUtils.checkNotNull(dest);
+ dest.setData(source.getDataRef());
+ dest.n = source.n;
+ dest.value = source.value;
+ }
+
+}
diff --git a/src/main/java/org/apache/commons/math3/stat/descriptive/summary/Sum.java b/src/main/java/org/apache/commons/math3/stat/descriptive/summary/Sum.java
new file mode 100644
index 0000000..e12b6a1
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/stat/descriptive/summary/Sum.java
@@ -0,0 +1,226 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math3.stat.descriptive.summary;
+
+import java.io.Serializable;
+
+import org.apache.commons.math3.exception.MathIllegalArgumentException;
+import org.apache.commons.math3.exception.NullArgumentException;
+import org.apache.commons.math3.stat.descriptive.AbstractStorelessUnivariateStatistic;
+import org.apache.commons.math3.util.MathUtils;
+
+
+/**
+ * Returns the sum of the available values.
+ * <p>
+ * If there are no values in the dataset, then 0 is returned.
+ * If any of the values are
+ * <code>NaN</code>, then <code>NaN</code> is returned.</p>
+ * <p>
+ * <strong>Note that this implementation is not synchronized.</strong> If
+ * multiple threads access an instance of this class concurrently, and at least
+ * one of the threads invokes the <code>increment()</code> or
+ * <code>clear()</code> method, it must be synchronized externally.</p>
+ *
+ */
+public class Sum extends AbstractStorelessUnivariateStatistic implements Serializable {
+
+ /** Serializable version identifier */
+ private static final long serialVersionUID = -8231831954703408316L;
+
+ /** */
+ private long n;
+
+ /**
+ * The currently running sum.
+ */
+ private double value;
+
+ /**
+ * Create a Sum instance
+ */
+ public Sum() {
+ n = 0;
+ value = 0;
+ }
+
+ /**
+ * Copy constructor, creates a new {@code Sum} identical
+ * to the {@code original}
+ *
+ * @param original the {@code Sum} instance to copy
+ * @throws NullArgumentException if original is null
+ */
+ public Sum(Sum original) throws NullArgumentException {
+ copy(original, this);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void increment(final double d) {
+ value += d;
+ n++;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public double getResult() {
+ return value;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public long getN() {
+ return n;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void clear() {
+ value = 0;
+ n = 0;
+ }
+
+ /**
+ * The sum of the entries in the specified portion of
+ * the input array, or 0 if the designated subarray
+ * is empty.
+ * <p>
+ * Throws <code>MathIllegalArgumentException</code> if the array is null.</p>
+ *
+ * @param values the input array
+ * @param begin index of the first array element to include
+ * @param length the number of elements to include
+ * @return the sum of the values or 0 if length = 0
+ * @throws MathIllegalArgumentException if the array is null or the array index
+ * parameters are not valid
+ */
+ @Override
+ public double evaluate(final double[] values, final int begin, final int length)
+ throws MathIllegalArgumentException {
+ double sum = Double.NaN;
+ if (test(values, begin, length, true)) {
+ sum = 0.0;
+ for (int i = begin; i < begin + length; i++) {
+ sum += values[i];
+ }
+ }
+ return sum;
+ }
+
+ /**
+ * The weighted sum of the entries in the specified portion of
+ * the input array, or 0 if the designated subarray
+ * is empty.
+ * <p>
+ * Throws <code>MathIllegalArgumentException</code> if any of the following are true:
+ * <ul><li>the values array is null</li>
+ * <li>the weights array is null</li>
+ * <li>the weights array does not have the same length as the values array</li>
+ * <li>the weights array contains one or more infinite values</li>
+ * <li>the weights array contains one or more NaN values</li>
+ * <li>the weights array contains negative values</li>
+ * <li>the start and length arguments do not determine a valid array</li>
+ * </ul></p>
+ * <p>
+ * Uses the formula, <pre>
+ * weighted sum = &Sigma;(values[i] * weights[i])
+ * </pre></p>
+ *
+ * @param values the input array
+ * @param weights the weights array
+ * @param begin index of the first array element to include
+ * @param length the number of elements to include
+ * @return the sum of the values or 0 if length = 0
+ * @throws MathIllegalArgumentException if the parameters are not valid
+ * @since 2.1
+ */
+ public double evaluate(final double[] values, final double[] weights,
+ final int begin, final int length) throws MathIllegalArgumentException {
+ double sum = Double.NaN;
+ if (test(values, weights, begin, length, true)) {
+ sum = 0.0;
+ for (int i = begin; i < begin + length; i++) {
+ sum += values[i] * weights[i];
+ }
+ }
+ return sum;
+ }
+
+ /**
+ * The weighted sum of the entries in the the input array.
+ * <p>
+ * Throws <code>MathIllegalArgumentException</code> if any of the following are true:
+ * <ul><li>the values array is null</li>
+ * <li>the weights array is null</li>
+ * <li>the weights array does not have the same length as the values array</li>
+ * <li>the weights array contains one or more infinite values</li>
+ * <li>the weights array contains one or more NaN values</li>
+ * <li>the weights array contains negative values</li>
+ * </ul></p>
+ * <p>
+ * Uses the formula, <pre>
+ * weighted sum = &Sigma;(values[i] * weights[i])
+ * </pre></p>
+ *
+ * @param values the input array
+ * @param weights the weights array
+ * @return the sum of the values or Double.NaN if length = 0
+ * @throws MathIllegalArgumentException if the parameters are not valid
+ * @since 2.1
+ */
+ public double evaluate(final double[] values, final double[] weights)
+ throws MathIllegalArgumentException {
+ return evaluate(values, weights, 0, values.length);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public Sum copy() {
+ Sum result = new Sum();
+ // No try-catch or advertised exception because args are valid
+ copy(this, result);
+ return result;
+ }
+
+ /**
+ * Copies source to dest.
+ * <p>Neither source nor dest can be null.</p>
+ *
+ * @param source Sum to copy
+ * @param dest Sum to copy to
+ * @throws NullArgumentException if either source or dest is null
+ */
+ public static void copy(Sum source, Sum dest)
+ throws NullArgumentException {
+ MathUtils.checkNotNull(source);
+ MathUtils.checkNotNull(dest);
+ dest.setData(source.getDataRef());
+ dest.n = source.n;
+ dest.value = source.value;
+ }
+
+}
diff --git a/src/main/java/org/apache/commons/math3/stat/descriptive/summary/SumOfLogs.java b/src/main/java/org/apache/commons/math3/stat/descriptive/summary/SumOfLogs.java
new file mode 100644
index 0000000..19718af
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/stat/descriptive/summary/SumOfLogs.java
@@ -0,0 +1,170 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math3.stat.descriptive.summary;
+
+import java.io.Serializable;
+
+import org.apache.commons.math3.exception.MathIllegalArgumentException;
+import org.apache.commons.math3.exception.NullArgumentException;
+import org.apache.commons.math3.stat.descriptive.AbstractStorelessUnivariateStatistic;
+import org.apache.commons.math3.util.FastMath;
+import org.apache.commons.math3.util.MathUtils;
+
+/**
+ * Returns the sum of the natural logs for this collection of values.
+ * <p>
+ * Uses {@link org.apache.commons.math3.util.FastMath#log(double)} to compute the logs.
+ * Therefore,
+ * <ul>
+ * <li>If any of values are &lt; 0, the result is <code>NaN.</code></li>
+ * <li>If all values are non-negative and less than
+ * <code>Double.POSITIVE_INFINITY</code>, but at least one value is 0, the
+ * result is <code>Double.NEGATIVE_INFINITY.</code></li>
+ * <li>If both <code>Double.POSITIVE_INFINITY</code> and
+ * <code>Double.NEGATIVE_INFINITY</code> are among the values, the result is
+ * <code>NaN.</code></li>
+ * </ul></p>
+ * <p>
+ * <strong>Note that this implementation is not synchronized.</strong> If
+ * multiple threads access an instance of this class concurrently, and at least
+ * one of the threads invokes the <code>increment()</code> or
+ * <code>clear()</code> method, it must be synchronized externally.</p>
+ *
+ */
+public class SumOfLogs extends AbstractStorelessUnivariateStatistic implements Serializable {
+
+ /** Serializable version identifier */
+ private static final long serialVersionUID = -370076995648386763L;
+
+ /**Number of values that have been added */
+ private int n;
+
+ /**
+ * The currently running value
+ */
+ private double value;
+
+ /**
+ * Create a SumOfLogs instance
+ */
+ public SumOfLogs() {
+ value = 0d;
+ n = 0;
+ }
+
+ /**
+ * Copy constructor, creates a new {@code SumOfLogs} identical
+ * to the {@code original}
+ *
+ * @param original the {@code SumOfLogs} instance to copy
+ * @throws NullArgumentException if original is null
+ */
+ public SumOfLogs(SumOfLogs original) throws NullArgumentException {
+ copy(original, this);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void increment(final double d) {
+ value += FastMath.log(d);
+ n++;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public double getResult() {
+ return value;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public long getN() {
+ return n;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void clear() {
+ value = 0d;
+ n = 0;
+ }
+
+ /**
+ * Returns the sum of the natural logs of the entries in the specified portion of
+ * the input array, or <code>Double.NaN</code> if the designated subarray
+ * is empty.
+ * <p>
+ * Throws <code>MathIllegalArgumentException</code> if the array is null.</p>
+ * <p>
+ * See {@link SumOfLogs}.</p>
+ *
+ * @param values the input array
+ * @param begin index of the first array element to include
+ * @param length the number of elements to include
+ * @return the sum of the natural logs of the values or 0 if
+ * length = 0
+ * @throws MathIllegalArgumentException if the array is null or the array index
+ * parameters are not valid
+ */
+ @Override
+ public double evaluate(final double[] values, final int begin, final int length)
+ throws MathIllegalArgumentException {
+ double sumLog = Double.NaN;
+ if (test(values, begin, length, true)) {
+ sumLog = 0.0;
+ for (int i = begin; i < begin + length; i++) {
+ sumLog += FastMath.log(values[i]);
+ }
+ }
+ return sumLog;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public SumOfLogs copy() {
+ SumOfLogs result = new SumOfLogs();
+ // No try-catch or advertised exception here because args are valid
+ copy(this, result);
+ return result;
+ }
+
+ /**
+ * Copies source to dest.
+ * <p>Neither source nor dest can be null.</p>
+ *
+ * @param source SumOfLogs to copy
+ * @param dest SumOfLogs to copy to
+ * @throws NullArgumentException if either source or dest is null
+ */
+ public static void copy(SumOfLogs source, SumOfLogs dest)
+ throws NullArgumentException {
+ MathUtils.checkNotNull(source);
+ MathUtils.checkNotNull(dest);
+ dest.setData(source.getDataRef());
+ dest.n = source.n;
+ dest.value = source.value;
+ }
+}
diff --git a/src/main/java/org/apache/commons/math3/stat/descriptive/summary/SumOfSquares.java b/src/main/java/org/apache/commons/math3/stat/descriptive/summary/SumOfSquares.java
new file mode 100644
index 0000000..161d8c8
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/stat/descriptive/summary/SumOfSquares.java
@@ -0,0 +1,159 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math3.stat.descriptive.summary;
+
+import java.io.Serializable;
+
+import org.apache.commons.math3.exception.MathIllegalArgumentException;
+import org.apache.commons.math3.exception.NullArgumentException;
+import org.apache.commons.math3.stat.descriptive.AbstractStorelessUnivariateStatistic;
+import org.apache.commons.math3.util.MathUtils;
+
+/**
+ * Returns the sum of the squares of the available values.
+ * <p>
+ * If there are no values in the dataset, then 0 is returned.
+ * If any of the values are
+ * <code>NaN</code>, then <code>NaN</code> is returned.</p>
+ * <p>
+ * <strong>Note that this implementation is not synchronized.</strong> If
+ * multiple threads access an instance of this class concurrently, and at least
+ * one of the threads invokes the <code>increment()</code> or
+ * <code>clear()</code> method, it must be synchronized externally.</p>
+ *
+ */
+public class SumOfSquares extends AbstractStorelessUnivariateStatistic implements Serializable {
+
+ /** Serializable version identifier */
+ private static final long serialVersionUID = 1460986908574398008L;
+
+ /** */
+ private long n;
+
+ /**
+ * The currently running sumSq
+ */
+ private double value;
+
+ /**
+ * Create a SumOfSquares instance
+ */
+ public SumOfSquares() {
+ n = 0;
+ value = 0;
+ }
+
+ /**
+ * Copy constructor, creates a new {@code SumOfSquares} identical
+ * to the {@code original}
+ *
+ * @param original the {@code SumOfSquares} instance to copy
+ * @throws NullArgumentException if original is null
+ */
+ public SumOfSquares(SumOfSquares original) throws NullArgumentException {
+ copy(original, this);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void increment(final double d) {
+ value += d * d;
+ n++;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public double getResult() {
+ return value;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public long getN() {
+ return n;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void clear() {
+ value = 0;
+ n = 0;
+ }
+
+ /**
+ * Returns the sum of the squares of the entries in the specified portion of
+ * the input array, or <code>Double.NaN</code> if the designated subarray
+ * is empty.
+ * <p>
+ * Throws <code>MathIllegalArgumentException</code> if the array is null.</p>
+ *
+ * @param values the input array
+ * @param begin index of the first array element to include
+ * @param length the number of elements to include
+ * @return the sum of the squares of the values or 0 if length = 0
+ * @throws MathIllegalArgumentException if the array is null or the array index
+ * parameters are not valid
+ */
+ @Override
+ public double evaluate(final double[] values,final int begin, final int length)
+ throws MathIllegalArgumentException {
+ double sumSq = Double.NaN;
+ if (test(values, begin, length, true)) {
+ sumSq = 0.0;
+ for (int i = begin; i < begin + length; i++) {
+ sumSq += values[i] * values[i];
+ }
+ }
+ return sumSq;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public SumOfSquares copy() {
+ SumOfSquares result = new SumOfSquares();
+ // no try-catch or advertised exception here because args are valid
+ copy(this, result);
+ return result;
+ }
+
+ /**
+ * Copies source to dest.
+ * <p>Neither source nor dest can be null.</p>
+ *
+ * @param source SumOfSquares to copy
+ * @param dest SumOfSquares to copy to
+ * @throws NullArgumentException if either source or dest is null
+ */
+ public static void copy(SumOfSquares source, SumOfSquares dest)
+ throws NullArgumentException {
+ MathUtils.checkNotNull(source);
+ MathUtils.checkNotNull(dest);
+ dest.setData(source.getDataRef());
+ dest.n = source.n;
+ dest.value = source.value;
+ }
+
+}
diff --git a/src/main/java/org/apache/commons/math3/stat/descriptive/summary/package-info.java b/src/main/java/org/apache/commons/math3/stat/descriptive/summary/package-info.java
new file mode 100644
index 0000000..2f07145
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/stat/descriptive/summary/package-info.java
@@ -0,0 +1,20 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/**
+ * Other summary statistics.
+ */
+package org.apache.commons.math3.stat.descriptive.summary;
diff --git a/src/main/java/org/apache/commons/math3/stat/inference/AlternativeHypothesis.java b/src/main/java/org/apache/commons/math3/stat/inference/AlternativeHypothesis.java
new file mode 100644
index 0000000..527067e
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/stat/inference/AlternativeHypothesis.java
@@ -0,0 +1,40 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math3.stat.inference;
+
+/**
+ * Represents an alternative hypothesis for a hypothesis test.
+ *
+ * @since 3.3
+ */
+public enum AlternativeHypothesis {
+
+ /**
+ * Represents a two-sided test. H0: p=p0, H1: p &ne; p0
+ */
+ TWO_SIDED,
+
+ /**
+ * Represents a right-sided test. H0: p &le; p0, H1: p &gt; p0.
+ */
+ GREATER_THAN,
+
+ /**
+ * Represents a left-sided test. H0: p &ge; p0, H1: p &lt; p0.
+ */
+ LESS_THAN
+}
diff --git a/src/main/java/org/apache/commons/math3/stat/inference/BinomialTest.java b/src/main/java/org/apache/commons/math3/stat/inference/BinomialTest.java
new file mode 100644
index 0000000..2efe091
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/stat/inference/BinomialTest.java
@@ -0,0 +1,160 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math3.stat.inference;
+
+import org.apache.commons.math3.distribution.BinomialDistribution;
+import org.apache.commons.math3.exception.MathIllegalArgumentException;
+import org.apache.commons.math3.exception.MathInternalError;
+import org.apache.commons.math3.exception.NotPositiveException;
+import org.apache.commons.math3.exception.NullArgumentException;
+import org.apache.commons.math3.exception.OutOfRangeException;
+import org.apache.commons.math3.exception.util.LocalizedFormats;
+
+/**
+ * Implements binomial test statistics.
+ * <p>
+ * Exact test for the statistical significance of deviations from a
+ * theoretically expected distribution of observations into two categories.
+ *
+ * @see <a href="http://en.wikipedia.org/wiki/Binomial_test">Binomial test (Wikipedia)</a>
+ * @since 3.3
+ */
+public class BinomialTest {
+
+ /**
+ * Returns whether the null hypothesis can be rejected with the given confidence level.
+ * <p>
+ * <strong>Preconditions</strong>:
+ * <ul>
+ * <li>Number of trials must be &ge; 0.</li>
+ * <li>Number of successes must be &ge; 0.</li>
+ * <li>Number of successes must be &le; number of trials.</li>
+ * <li>Probability must be &ge; 0 and &le; 1.</li>
+ * </ul>
+ *
+ * @param numberOfTrials number of trials performed
+ * @param numberOfSuccesses number of successes observed
+ * @param probability assumed probability of a single trial under the null hypothesis
+ * @param alternativeHypothesis type of hypothesis being evaluated (one- or two-sided)
+ * @param alpha significance level of the test
+ * @return true if the null hypothesis can be rejected with confidence {@code 1 - alpha}
+ * @throws NotPositiveException if {@code numberOfTrials} or {@code numberOfSuccesses} is negative
+ * @throws OutOfRangeException if {@code probability} is not between 0 and 1
+ * @throws MathIllegalArgumentException if {@code numberOfTrials} &lt; {@code numberOfSuccesses} or
+ * if {@code alternateHypothesis} is null.
+ * @see AlternativeHypothesis
+ */
+ public boolean binomialTest(int numberOfTrials, int numberOfSuccesses, double probability,
+ AlternativeHypothesis alternativeHypothesis, double alpha) {
+ double pValue = binomialTest(numberOfTrials, numberOfSuccesses, probability, alternativeHypothesis);
+ return pValue < alpha;
+ }
+
+ /**
+ * Returns the <i>observed significance level</i>, or
+ * <a href="http://www.cas.lancs.ac.uk/glossary_v1.1/hyptest.html#pvalue">p-value</a>,
+ * associated with a <a href="http://en.wikipedia.org/wiki/Binomial_test"> Binomial test</a>.
+ * <p>
+ * The number returned is the smallest significance level at which one can reject the null hypothesis.
+ * The form of the hypothesis depends on {@code alternativeHypothesis}.</p>
+ * <p>
+ * The p-Value represents the likelihood of getting a result at least as extreme as the sample,
+ * given the provided {@code probability} of success on a single trial. For single-sided tests,
+ * this value can be directly derived from the Binomial distribution. For the two-sided test,
+ * the implementation works as follows: we start by looking at the most extreme cases
+ * (0 success and n success where n is the number of trials from the sample) and determine their likelihood.
+ * The lower value is added to the p-Value (if both values are equal, both are added). Then we continue with
+ * the next extreme value, until we added the value for the actual observed sample.</p>
+ * <p>
+ * <strong>Preconditions</strong>:
+ * <ul>
+ * <li>Number of trials must be &ge; 0.</li>
+ * <li>Number of successes must be &ge; 0.</li>
+ * <li>Number of successes must be &le; number of trials.</li>
+ * <li>Probability must be &ge; 0 and &le; 1.</li>
+ * </ul></p>
+ *
+ * @param numberOfTrials number of trials performed
+ * @param numberOfSuccesses number of successes observed
+ * @param probability assumed probability of a single trial under the null hypothesis
+ * @param alternativeHypothesis type of hypothesis being evaluated (one- or two-sided)
+ * @return p-value
+ * @throws NotPositiveException if {@code numberOfTrials} or {@code numberOfSuccesses} is negative
+ * @throws OutOfRangeException if {@code probability} is not between 0 and 1
+ * @throws MathIllegalArgumentException if {@code numberOfTrials} &lt; {@code numberOfSuccesses} or
+ * if {@code alternateHypothesis} is null.
+ * @see AlternativeHypothesis
+ */
+ public double binomialTest(int numberOfTrials, int numberOfSuccesses, double probability,
+ AlternativeHypothesis alternativeHypothesis) {
+ if (numberOfTrials < 0) {
+ throw new NotPositiveException(numberOfTrials);
+ }
+ if (numberOfSuccesses < 0) {
+ throw new NotPositiveException(numberOfSuccesses);
+ }
+ if (probability < 0 || probability > 1) {
+ throw new OutOfRangeException(probability, 0, 1);
+ }
+ if (numberOfTrials < numberOfSuccesses) {
+ throw new MathIllegalArgumentException(
+ LocalizedFormats.BINOMIAL_INVALID_PARAMETERS_ORDER,
+ numberOfTrials, numberOfSuccesses);
+ }
+ if (alternativeHypothesis == null) {
+ throw new NullArgumentException();
+ }
+
+ // pass a null rng to avoid unneeded overhead as we will not sample from this distribution
+ final BinomialDistribution distribution = new BinomialDistribution(null, numberOfTrials, probability);
+ switch (alternativeHypothesis) {
+ case GREATER_THAN:
+ return 1 - distribution.cumulativeProbability(numberOfSuccesses - 1);
+ case LESS_THAN:
+ return distribution.cumulativeProbability(numberOfSuccesses);
+ case TWO_SIDED:
+ int criticalValueLow = 0;
+ int criticalValueHigh = numberOfTrials;
+ double pTotal = 0;
+
+ while (true) {
+ double pLow = distribution.probability(criticalValueLow);
+ double pHigh = distribution.probability(criticalValueHigh);
+
+ if (pLow == pHigh) {
+ pTotal += 2 * pLow;
+ criticalValueLow++;
+ criticalValueHigh--;
+ } else if (pLow < pHigh) {
+ pTotal += pLow;
+ criticalValueLow++;
+ } else {
+ pTotal += pHigh;
+ criticalValueHigh--;
+ }
+
+ if (criticalValueLow > numberOfSuccesses || criticalValueHigh < numberOfSuccesses) {
+ break;
+ }
+ }
+ return pTotal;
+ default:
+ throw new MathInternalError(LocalizedFormats. OUT_OF_RANGE_SIMPLE, alternativeHypothesis,
+ AlternativeHypothesis.TWO_SIDED, AlternativeHypothesis.LESS_THAN);
+ }
+ }
+}
diff --git a/src/main/java/org/apache/commons/math3/stat/inference/ChiSquareTest.java b/src/main/java/org/apache/commons/math3/stat/inference/ChiSquareTest.java
new file mode 100644
index 0000000..7e97ac1
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/stat/inference/ChiSquareTest.java
@@ -0,0 +1,602 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math3.stat.inference;
+
+import org.apache.commons.math3.distribution.ChiSquaredDistribution;
+import org.apache.commons.math3.exception.DimensionMismatchException;
+import org.apache.commons.math3.exception.MaxCountExceededException;
+import org.apache.commons.math3.exception.NotPositiveException;
+import org.apache.commons.math3.exception.NotStrictlyPositiveException;
+import org.apache.commons.math3.exception.NullArgumentException;
+import org.apache.commons.math3.exception.OutOfRangeException;
+import org.apache.commons.math3.exception.ZeroException;
+import org.apache.commons.math3.exception.util.LocalizedFormats;
+import org.apache.commons.math3.util.FastMath;
+import org.apache.commons.math3.util.MathArrays;
+
+/**
+ * Implements Chi-Square test statistics.
+ *
+ * <p>This implementation handles both known and unknown distributions.</p>
+ *
+ * <p>Two samples tests can be used when the distribution is unknown <i>a priori</i>
+ * but provided by one sample, or when the hypothesis under test is that the two
+ * samples come from the same underlying distribution.</p>
+ *
+ */
+public class ChiSquareTest {
+
+ /**
+ * Construct a ChiSquareTest
+ */
+ public ChiSquareTest() {
+ super();
+ }
+
+ /**
+ * Computes the <a href="http://www.itl.nist.gov/div898/handbook/eda/section3/eda35f.htm">
+ * Chi-Square statistic</a> comparing <code>observed</code> and <code>expected</code>
+ * frequency counts.
+ * <p>
+ * This statistic can be used to perform a Chi-Square test evaluating the null
+ * hypothesis that the observed counts follow the expected distribution.</p>
+ * <p>
+ * <strong>Preconditions</strong>: <ul>
+ * <li>Expected counts must all be positive.
+ * </li>
+ * <li>Observed counts must all be &ge; 0.
+ * </li>
+ * <li>The observed and expected arrays must have the same length and
+ * their common length must be at least 2.
+ * </li></ul></p><p>
+ * If any of the preconditions are not met, an
+ * <code>IllegalArgumentException</code> is thrown.</p>
+ * <p><strong>Note: </strong>This implementation rescales the
+ * <code>expected</code> array if necessary to ensure that the sum of the
+ * expected and observed counts are equal.</p>
+ *
+ * @param observed array of observed frequency counts
+ * @param expected array of expected frequency counts
+ * @return chiSquare test statistic
+ * @throws NotPositiveException if <code>observed</code> has negative entries
+ * @throws NotStrictlyPositiveException if <code>expected</code> has entries that are
+ * not strictly positive
+ * @throws DimensionMismatchException if the arrays length is less than 2
+ */
+ public double chiSquare(final double[] expected, final long[] observed)
+ throws NotPositiveException, NotStrictlyPositiveException,
+ DimensionMismatchException {
+
+ if (expected.length < 2) {
+ throw new DimensionMismatchException(expected.length, 2);
+ }
+ if (expected.length != observed.length) {
+ throw new DimensionMismatchException(expected.length, observed.length);
+ }
+ MathArrays.checkPositive(expected);
+ MathArrays.checkNonNegative(observed);
+
+ double sumExpected = 0d;
+ double sumObserved = 0d;
+ for (int i = 0; i < observed.length; i++) {
+ sumExpected += expected[i];
+ sumObserved += observed[i];
+ }
+ double ratio = 1.0d;
+ boolean rescale = false;
+ if (FastMath.abs(sumExpected - sumObserved) > 10E-6) {
+ ratio = sumObserved / sumExpected;
+ rescale = true;
+ }
+ double sumSq = 0.0d;
+ for (int i = 0; i < observed.length; i++) {
+ if (rescale) {
+ final double dev = observed[i] - ratio * expected[i];
+ sumSq += dev * dev / (ratio * expected[i]);
+ } else {
+ final double dev = observed[i] - expected[i];
+ sumSq += dev * dev / expected[i];
+ }
+ }
+ return sumSq;
+
+ }
+
+ /**
+ * Returns the <i>observed significance level</i>, or <a href=
+ * "http://www.cas.lancs.ac.uk/glossary_v1.1/hyptest.html#pvalue">
+ * p-value</a>, associated with a
+ * <a href="http://www.itl.nist.gov/div898/handbook/eda/section3/eda35f.htm">
+ * Chi-square goodness of fit test</a> comparing the <code>observed</code>
+ * frequency counts to those in the <code>expected</code> array.
+ * <p>
+ * The number returned is the smallest significance level at which one can reject
+ * the null hypothesis that the observed counts conform to the frequency distribution
+ * described by the expected counts.</p>
+ * <p>
+ * <strong>Preconditions</strong>: <ul>
+ * <li>Expected counts must all be positive.
+ * </li>
+ * <li>Observed counts must all be &ge; 0.
+ * </li>
+ * <li>The observed and expected arrays must have the same length and
+ * their common length must be at least 2.
+ * </li></ul></p><p>
+ * If any of the preconditions are not met, an
+ * <code>IllegalArgumentException</code> is thrown.</p>
+ * <p><strong>Note: </strong>This implementation rescales the
+ * <code>expected</code> array if necessary to ensure that the sum of the
+ * expected and observed counts are equal.</p>
+ *
+ * @param observed array of observed frequency counts
+ * @param expected array of expected frequency counts
+ * @return p-value
+ * @throws NotPositiveException if <code>observed</code> has negative entries
+ * @throws NotStrictlyPositiveException if <code>expected</code> has entries that are
+ * not strictly positive
+ * @throws DimensionMismatchException if the arrays length is less than 2
+ * @throws MaxCountExceededException if an error occurs computing the p-value
+ */
+ public double chiSquareTest(final double[] expected, final long[] observed)
+ throws NotPositiveException, NotStrictlyPositiveException,
+ DimensionMismatchException, MaxCountExceededException {
+
+ // pass a null rng to avoid unneeded overhead as we will not sample from this distribution
+ final ChiSquaredDistribution distribution =
+ new ChiSquaredDistribution(null, expected.length - 1.0);
+ return 1.0 - distribution.cumulativeProbability(chiSquare(expected, observed));
+ }
+
+ /**
+ * Performs a <a href="http://www.itl.nist.gov/div898/handbook/eda/section3/eda35f.htm">
+ * Chi-square goodness of fit test</a> evaluating the null hypothesis that the
+ * observed counts conform to the frequency distribution described by the expected
+ * counts, with significance level <code>alpha</code>. Returns true iff the null
+ * hypothesis can be rejected with 100 * (1 - alpha) percent confidence.
+ * <p>
+ * <strong>Example:</strong><br>
+ * To test the hypothesis that <code>observed</code> follows
+ * <code>expected</code> at the 99% level, use </p><p>
+ * <code>chiSquareTest(expected, observed, 0.01) </code></p>
+ * <p>
+ * <strong>Preconditions</strong>: <ul>
+ * <li>Expected counts must all be positive.
+ * </li>
+ * <li>Observed counts must all be &ge; 0.
+ * </li>
+ * <li>The observed and expected arrays must have the same length and
+ * their common length must be at least 2.
+ * <li> <code> 0 &lt; alpha &lt; 0.5 </code>
+ * </li></ul></p><p>
+ * If any of the preconditions are not met, an
+ * <code>IllegalArgumentException</code> is thrown.</p>
+ * <p><strong>Note: </strong>This implementation rescales the
+ * <code>expected</code> array if necessary to ensure that the sum of the
+ * expected and observed counts are equal.</p>
+ *
+ * @param observed array of observed frequency counts
+ * @param expected array of expected frequency counts
+ * @param alpha significance level of the test
+ * @return true iff null hypothesis can be rejected with confidence
+ * 1 - alpha
+ * @throws NotPositiveException if <code>observed</code> has negative entries
+ * @throws NotStrictlyPositiveException if <code>expected</code> has entries that are
+ * not strictly positive
+ * @throws DimensionMismatchException if the arrays length is less than 2
+ * @throws OutOfRangeException if <code>alpha</code> is not in the range (0, 0.5]
+ * @throws MaxCountExceededException if an error occurs computing the p-value
+ */
+ public boolean chiSquareTest(final double[] expected, final long[] observed,
+ final double alpha)
+ throws NotPositiveException, NotStrictlyPositiveException,
+ DimensionMismatchException, OutOfRangeException, MaxCountExceededException {
+
+ if ((alpha <= 0) || (alpha > 0.5)) {
+ throw new OutOfRangeException(LocalizedFormats.OUT_OF_BOUND_SIGNIFICANCE_LEVEL,
+ alpha, 0, 0.5);
+ }
+ return chiSquareTest(expected, observed) < alpha;
+
+ }
+
+ /**
+ * Computes the Chi-Square statistic associated with a
+ * <a href="http://www.itl.nist.gov/div898/handbook/prc/section4/prc45.htm">
+ * chi-square test of independence</a> based on the input <code>counts</code>
+ * array, viewed as a two-way table.
+ * <p>
+ * The rows of the 2-way table are
+ * <code>count[0], ... , count[count.length - 1] </code></p>
+ * <p>
+ * <strong>Preconditions</strong>: <ul>
+ * <li>All counts must be &ge; 0.
+ * </li>
+ * <li>The count array must be rectangular (i.e. all count[i] subarrays
+ * must have the same length).
+ * </li>
+ * <li>The 2-way table represented by <code>counts</code> must have at
+ * least 2 columns and at least 2 rows.
+ * </li>
+ * </li></ul></p><p>
+ * If any of the preconditions are not met, an
+ * <code>IllegalArgumentException</code> is thrown.</p>
+ *
+ * @param counts array representation of 2-way table
+ * @return chiSquare test statistic
+ * @throws NullArgumentException if the array is null
+ * @throws DimensionMismatchException if the array is not rectangular
+ * @throws NotPositiveException if {@code counts} has negative entries
+ */
+ public double chiSquare(final long[][] counts)
+ throws NullArgumentException, NotPositiveException,
+ DimensionMismatchException {
+
+ checkArray(counts);
+ int nRows = counts.length;
+ int nCols = counts[0].length;
+
+ // compute row, column and total sums
+ double[] rowSum = new double[nRows];
+ double[] colSum = new double[nCols];
+ double total = 0.0d;
+ for (int row = 0; row < nRows; row++) {
+ for (int col = 0; col < nCols; col++) {
+ rowSum[row] += counts[row][col];
+ colSum[col] += counts[row][col];
+ total += counts[row][col];
+ }
+ }
+
+ // compute expected counts and chi-square
+ double sumSq = 0.0d;
+ double expected = 0.0d;
+ for (int row = 0; row < nRows; row++) {
+ for (int col = 0; col < nCols; col++) {
+ expected = (rowSum[row] * colSum[col]) / total;
+ sumSq += ((counts[row][col] - expected) *
+ (counts[row][col] - expected)) / expected;
+ }
+ }
+ return sumSq;
+
+ }
+
+ /**
+ * Returns the <i>observed significance level</i>, or <a href=
+ * "http://www.cas.lancs.ac.uk/glossary_v1.1/hyptest.html#pvalue">
+ * p-value</a>, associated with a
+ * <a href="http://www.itl.nist.gov/div898/handbook/prc/section4/prc45.htm">
+ * chi-square test of independence</a> based on the input <code>counts</code>
+ * array, viewed as a two-way table.
+ * <p>
+ * The rows of the 2-way table are
+ * <code>count[0], ... , count[count.length - 1] </code></p>
+ * <p>
+ * <strong>Preconditions</strong>: <ul>
+ * <li>All counts must be &ge; 0.
+ * </li>
+ * <li>The count array must be rectangular (i.e. all count[i] subarrays must have
+ * the same length).
+ * </li>
+ * <li>The 2-way table represented by <code>counts</code> must have at least 2
+ * columns and at least 2 rows.
+ * </li>
+ * </li></ul></p><p>
+ * If any of the preconditions are not met, an
+ * <code>IllegalArgumentException</code> is thrown.</p>
+ *
+ * @param counts array representation of 2-way table
+ * @return p-value
+ * @throws NullArgumentException if the array is null
+ * @throws DimensionMismatchException if the array is not rectangular
+ * @throws NotPositiveException if {@code counts} has negative entries
+ * @throws MaxCountExceededException if an error occurs computing the p-value
+ */
+ public double chiSquareTest(final long[][] counts)
+ throws NullArgumentException, DimensionMismatchException,
+ NotPositiveException, MaxCountExceededException {
+
+ checkArray(counts);
+ double df = ((double) counts.length -1) * ((double) counts[0].length - 1);
+ // pass a null rng to avoid unneeded overhead as we will not sample from this distribution
+ final ChiSquaredDistribution distribution = new ChiSquaredDistribution(df);
+ return 1 - distribution.cumulativeProbability(chiSquare(counts));
+
+ }
+
+ /**
+ * Performs a <a href="http://www.itl.nist.gov/div898/handbook/prc/section4/prc45.htm">
+ * chi-square test of independence</a> evaluating the null hypothesis that the
+ * classifications represented by the counts in the columns of the input 2-way table
+ * are independent of the rows, with significance level <code>alpha</code>.
+ * Returns true iff the null hypothesis can be rejected with 100 * (1 - alpha) percent
+ * confidence.
+ * <p>
+ * The rows of the 2-way table are
+ * <code>count[0], ... , count[count.length - 1] </code></p>
+ * <p>
+ * <strong>Example:</strong><br>
+ * To test the null hypothesis that the counts in
+ * <code>count[0], ... , count[count.length - 1] </code>
+ * all correspond to the same underlying probability distribution at the 99% level, use</p>
+ * <p><code>chiSquareTest(counts, 0.01)</code></p>
+ * <p>
+ * <strong>Preconditions</strong>: <ul>
+ * <li>All counts must be &ge; 0.
+ * </li>
+ * <li>The count array must be rectangular (i.e. all count[i] subarrays must have the
+ * same length).</li>
+ * <li>The 2-way table represented by <code>counts</code> must have at least 2 columns and
+ * at least 2 rows.</li>
+ * </li></ul></p><p>
+ * If any of the preconditions are not met, an
+ * <code>IllegalArgumentException</code> is thrown.</p>
+ *
+ * @param counts array representation of 2-way table
+ * @param alpha significance level of the test
+ * @return true iff null hypothesis can be rejected with confidence
+ * 1 - alpha
+ * @throws NullArgumentException if the array is null
+ * @throws DimensionMismatchException if the array is not rectangular
+ * @throws NotPositiveException if {@code counts} has any negative entries
+ * @throws OutOfRangeException if <code>alpha</code> is not in the range (0, 0.5]
+ * @throws MaxCountExceededException if an error occurs computing the p-value
+ */
+ public boolean chiSquareTest(final long[][] counts, final double alpha)
+ throws NullArgumentException, DimensionMismatchException,
+ NotPositiveException, OutOfRangeException, MaxCountExceededException {
+
+ if ((alpha <= 0) || (alpha > 0.5)) {
+ throw new OutOfRangeException(LocalizedFormats.OUT_OF_BOUND_SIGNIFICANCE_LEVEL,
+ alpha, 0, 0.5);
+ }
+ return chiSquareTest(counts) < alpha;
+
+ }
+
+ /**
+ * <p>Computes a
+ * <a href="http://www.itl.nist.gov/div898/software/dataplot/refman1/auxillar/chi2samp.htm">
+ * Chi-Square two sample test statistic</a> comparing bin frequency counts
+ * in <code>observed1</code> and <code>observed2</code>. The
+ * sums of frequency counts in the two samples are not required to be the
+ * same. The formula used to compute the test statistic is</p>
+ * <code>
+ * &sum;[(K * observed1[i] - observed2[i]/K)<sup>2</sup> / (observed1[i] + observed2[i])]
+ * </code> where
+ * <br/><code>K = &sqrt;[&sum(observed2 / &sum;(observed1)]</code>
+ * </p>
+ * <p>This statistic can be used to perform a Chi-Square test evaluating the
+ * null hypothesis that both observed counts follow the same distribution.</p>
+ * <p>
+ * <strong>Preconditions</strong>: <ul>
+ * <li>Observed counts must be non-negative.
+ * </li>
+ * <li>Observed counts for a specific bin must not both be zero.
+ * </li>
+ * <li>Observed counts for a specific sample must not all be 0.
+ * </li>
+ * <li>The arrays <code>observed1</code> and <code>observed2</code> must have
+ * the same length and their common length must be at least 2.
+ * </li></ul></p><p>
+ * If any of the preconditions are not met, an
+ * <code>IllegalArgumentException</code> is thrown.</p>
+ *
+ * @param observed1 array of observed frequency counts of the first data set
+ * @param observed2 array of observed frequency counts of the second data set
+ * @return chiSquare test statistic
+ * @throws DimensionMismatchException the the length of the arrays does not match
+ * @throws NotPositiveException if any entries in <code>observed1</code> or
+ * <code>observed2</code> are negative
+ * @throws ZeroException if either all counts of <code>observed1</code> or
+ * <code>observed2</code> are zero, or if the count at some index is zero
+ * for both arrays
+ * @since 1.2
+ */
+ public double chiSquareDataSetsComparison(long[] observed1, long[] observed2)
+ throws DimensionMismatchException, NotPositiveException, ZeroException {
+
+ // Make sure lengths are same
+ if (observed1.length < 2) {
+ throw new DimensionMismatchException(observed1.length, 2);
+ }
+ if (observed1.length != observed2.length) {
+ throw new DimensionMismatchException(observed1.length, observed2.length);
+ }
+
+ // Ensure non-negative counts
+ MathArrays.checkNonNegative(observed1);
+ MathArrays.checkNonNegative(observed2);
+
+ // Compute and compare count sums
+ long countSum1 = 0;
+ long countSum2 = 0;
+ boolean unequalCounts = false;
+ double weight = 0.0;
+ for (int i = 0; i < observed1.length; i++) {
+ countSum1 += observed1[i];
+ countSum2 += observed2[i];
+ }
+ // Ensure neither sample is uniformly 0
+ if (countSum1 == 0 || countSum2 == 0) {
+ throw new ZeroException();
+ }
+ // Compare and compute weight only if different
+ unequalCounts = countSum1 != countSum2;
+ if (unequalCounts) {
+ weight = FastMath.sqrt((double) countSum1 / (double) countSum2);
+ }
+ // Compute ChiSquare statistic
+ double sumSq = 0.0d;
+ double dev = 0.0d;
+ double obs1 = 0.0d;
+ double obs2 = 0.0d;
+ for (int i = 0; i < observed1.length; i++) {
+ if (observed1[i] == 0 && observed2[i] == 0) {
+ throw new ZeroException(LocalizedFormats.OBSERVED_COUNTS_BOTTH_ZERO_FOR_ENTRY, i);
+ } else {
+ obs1 = observed1[i];
+ obs2 = observed2[i];
+ if (unequalCounts) { // apply weights
+ dev = obs1/weight - obs2 * weight;
+ } else {
+ dev = obs1 - obs2;
+ }
+ sumSq += (dev * dev) / (obs1 + obs2);
+ }
+ }
+ return sumSq;
+ }
+
+ /**
+ * <p>Returns the <i>observed significance level</i>, or <a href=
+ * "http://www.cas.lancs.ac.uk/glossary_v1.1/hyptest.html#pvalue">
+ * p-value</a>, associated with a Chi-Square two sample test comparing
+ * bin frequency counts in <code>observed1</code> and
+ * <code>observed2</code>.
+ * </p>
+ * <p>The number returned is the smallest significance level at which one
+ * can reject the null hypothesis that the observed counts conform to the
+ * same distribution.
+ * </p>
+ * <p>See {@link #chiSquareDataSetsComparison(long[], long[])} for details
+ * on the formula used to compute the test statistic. The degrees of
+ * of freedom used to perform the test is one less than the common length
+ * of the input observed count arrays.
+ * </p>
+ * <strong>Preconditions</strong>: <ul>
+ * <li>Observed counts must be non-negative.
+ * </li>
+ * <li>Observed counts for a specific bin must not both be zero.
+ * </li>
+ * <li>Observed counts for a specific sample must not all be 0.
+ * </li>
+ * <li>The arrays <code>observed1</code> and <code>observed2</code> must
+ * have the same length and
+ * their common length must be at least 2.
+ * </li></ul><p>
+ * If any of the preconditions are not met, an
+ * <code>IllegalArgumentException</code> is thrown.</p>
+ *
+ * @param observed1 array of observed frequency counts of the first data set
+ * @param observed2 array of observed frequency counts of the second data set
+ * @return p-value
+ * @throws DimensionMismatchException the the length of the arrays does not match
+ * @throws NotPositiveException if any entries in <code>observed1</code> or
+ * <code>observed2</code> are negative
+ * @throws ZeroException if either all counts of <code>observed1</code> or
+ * <code>observed2</code> are zero, or if the count at the same index is zero
+ * for both arrays
+ * @throws MaxCountExceededException if an error occurs computing the p-value
+ * @since 1.2
+ */
+ public double chiSquareTestDataSetsComparison(long[] observed1, long[] observed2)
+ throws DimensionMismatchException, NotPositiveException, ZeroException,
+ MaxCountExceededException {
+
+ // pass a null rng to avoid unneeded overhead as we will not sample from this distribution
+ final ChiSquaredDistribution distribution =
+ new ChiSquaredDistribution(null, (double) observed1.length - 1);
+ return 1 - distribution.cumulativeProbability(
+ chiSquareDataSetsComparison(observed1, observed2));
+
+ }
+
+ /**
+ * <p>Performs a Chi-Square two sample test comparing two binned data
+ * sets. The test evaluates the null hypothesis that the two lists of
+ * observed counts conform to the same frequency distribution, with
+ * significance level <code>alpha</code>. Returns true iff the null
+ * hypothesis can be rejected with 100 * (1 - alpha) percent confidence.
+ * </p>
+ * <p>See {@link #chiSquareDataSetsComparison(long[], long[])} for
+ * details on the formula used to compute the Chisquare statistic used
+ * in the test. The degrees of of freedom used to perform the test is
+ * one less than the common length of the input observed count arrays.
+ * </p>
+ * <strong>Preconditions</strong>: <ul>
+ * <li>Observed counts must be non-negative.
+ * </li>
+ * <li>Observed counts for a specific bin must not both be zero.
+ * </li>
+ * <li>Observed counts for a specific sample must not all be 0.
+ * </li>
+ * <li>The arrays <code>observed1</code> and <code>observed2</code> must
+ * have the same length and their common length must be at least 2.
+ * </li>
+ * <li> <code> 0 < alpha < 0.5 </code>
+ * </li></ul><p>
+ * If any of the preconditions are not met, an
+ * <code>IllegalArgumentException</code> is thrown.</p>
+ *
+ * @param observed1 array of observed frequency counts of the first data set
+ * @param observed2 array of observed frequency counts of the second data set
+ * @param alpha significance level of the test
+ * @return true iff null hypothesis can be rejected with confidence
+ * 1 - alpha
+ * @throws DimensionMismatchException the the length of the arrays does not match
+ * @throws NotPositiveException if any entries in <code>observed1</code> or
+ * <code>observed2</code> are negative
+ * @throws ZeroException if either all counts of <code>observed1</code> or
+ * <code>observed2</code> are zero, or if the count at the same index is zero
+ * for both arrays
+ * @throws OutOfRangeException if <code>alpha</code> is not in the range (0, 0.5]
+ * @throws MaxCountExceededException if an error occurs performing the test
+ * @since 1.2
+ */
+ public boolean chiSquareTestDataSetsComparison(final long[] observed1,
+ final long[] observed2,
+ final double alpha)
+ throws DimensionMismatchException, NotPositiveException,
+ ZeroException, OutOfRangeException, MaxCountExceededException {
+
+ if (alpha <= 0 ||
+ alpha > 0.5) {
+ throw new OutOfRangeException(LocalizedFormats.OUT_OF_BOUND_SIGNIFICANCE_LEVEL,
+ alpha, 0, 0.5);
+ }
+ return chiSquareTestDataSetsComparison(observed1, observed2) < alpha;
+
+ }
+
+ /**
+ * Checks to make sure that the input long[][] array is rectangular,
+ * has at least 2 rows and 2 columns, and has all non-negative entries.
+ *
+ * @param in input 2-way table to check
+ * @throws NullArgumentException if the array is null
+ * @throws DimensionMismatchException if the array is not valid
+ * @throws NotPositiveException if the array contains any negative entries
+ */
+ private void checkArray(final long[][] in)
+ throws NullArgumentException, DimensionMismatchException,
+ NotPositiveException {
+
+ if (in.length < 2) {
+ throw new DimensionMismatchException(in.length, 2);
+ }
+
+ if (in[0].length < 2) {
+ throw new DimensionMismatchException(in[0].length, 2);
+ }
+
+ MathArrays.checkRectangular(in);
+ MathArrays.checkNonNegative(in);
+
+ }
+
+}
diff --git a/src/main/java/org/apache/commons/math3/stat/inference/GTest.java b/src/main/java/org/apache/commons/math3/stat/inference/GTest.java
new file mode 100644
index 0000000..de1fbe3
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/stat/inference/GTest.java
@@ -0,0 +1,538 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math3.stat.inference;
+
+import org.apache.commons.math3.distribution.ChiSquaredDistribution;
+import org.apache.commons.math3.exception.DimensionMismatchException;
+import org.apache.commons.math3.exception.MaxCountExceededException;
+import org.apache.commons.math3.exception.NotPositiveException;
+import org.apache.commons.math3.exception.NotStrictlyPositiveException;
+import org.apache.commons.math3.exception.OutOfRangeException;
+import org.apache.commons.math3.exception.ZeroException;
+import org.apache.commons.math3.exception.util.LocalizedFormats;
+import org.apache.commons.math3.util.FastMath;
+import org.apache.commons.math3.util.MathArrays;
+
+/**
+ * Implements <a href="http://en.wikipedia.org/wiki/G-test">G Test</a>
+ * statistics.
+ *
+ * <p>This is known in statistical genetics as the McDonald-Kreitman test.
+ * The implementation handles both known and unknown distributions.</p>
+ *
+ * <p>Two samples tests can be used when the distribution is unknown <i>a priori</i>
+ * but provided by one sample, or when the hypothesis under test is that the two
+ * samples come from the same underlying distribution.</p>
+ *
+ * @since 3.1
+ */
+public class GTest {
+
+ /**
+ * Computes the <a href="http://en.wikipedia.org/wiki/G-test">G statistic
+ * for Goodness of Fit</a> comparing {@code observed} and {@code expected}
+ * frequency counts.
+ *
+ * <p>This statistic can be used to perform a G test (Log-Likelihood Ratio
+ * Test) evaluating the null hypothesis that the observed counts follow the
+ * expected distribution.</p>
+ *
+ * <p><strong>Preconditions</strong>: <ul>
+ * <li>Expected counts must all be positive. </li>
+ * <li>Observed counts must all be &ge; 0. </li>
+ * <li>The observed and expected arrays must have the same length and their
+ * common length must be at least 2. </li></ul></p>
+ *
+ * <p>If any of the preconditions are not met, a
+ * {@code MathIllegalArgumentException} is thrown.</p>
+ *
+ * <p><strong>Note:</strong>This implementation rescales the
+ * {@code expected} array if necessary to ensure that the sum of the
+ * expected and observed counts are equal.</p>
+ *
+ * @param observed array of observed frequency counts
+ * @param expected array of expected frequency counts
+ * @return G-Test statistic
+ * @throws NotPositiveException if {@code observed} has negative entries
+ * @throws NotStrictlyPositiveException if {@code expected} has entries that
+ * are not strictly positive
+ * @throws DimensionMismatchException if the array lengths do not match or
+ * are less than 2.
+ */
+ public double g(final double[] expected, final long[] observed)
+ throws NotPositiveException, NotStrictlyPositiveException,
+ DimensionMismatchException {
+
+ if (expected.length < 2) {
+ throw new DimensionMismatchException(expected.length, 2);
+ }
+ if (expected.length != observed.length) {
+ throw new DimensionMismatchException(expected.length, observed.length);
+ }
+ MathArrays.checkPositive(expected);
+ MathArrays.checkNonNegative(observed);
+
+ double sumExpected = 0d;
+ double sumObserved = 0d;
+ for (int i = 0; i < observed.length; i++) {
+ sumExpected += expected[i];
+ sumObserved += observed[i];
+ }
+ double ratio = 1d;
+ boolean rescale = false;
+ if (FastMath.abs(sumExpected - sumObserved) > 10E-6) {
+ ratio = sumObserved / sumExpected;
+ rescale = true;
+ }
+ double sum = 0d;
+ for (int i = 0; i < observed.length; i++) {
+ final double dev = rescale ?
+ FastMath.log((double) observed[i] / (ratio * expected[i])) :
+ FastMath.log((double) observed[i] / expected[i]);
+ sum += ((double) observed[i]) * dev;
+ }
+ return 2d * sum;
+ }
+
+ /**
+ * Returns the <i>observed significance level</i>, or <a href=
+ * "http://www.cas.lancs.ac.uk/glossary_v1.1/hyptest.html#pvalue"> p-value</a>,
+ * associated with a G-Test for goodness of fit</a> comparing the
+ * {@code observed} frequency counts to those in the {@code expected} array.
+ *
+ * <p>The number returned is the smallest significance level at which one
+ * can reject the null hypothesis that the observed counts conform to the
+ * frequency distribution described by the expected counts.</p>
+ *
+ * <p>The probability returned is the tail probability beyond
+ * {@link #g(double[], long[]) g(expected, observed)}
+ * in the ChiSquare distribution with degrees of freedom one less than the
+ * common length of {@code expected} and {@code observed}.</p>
+ *
+ * <p> <strong>Preconditions</strong>: <ul>
+ * <li>Expected counts must all be positive. </li>
+ * <li>Observed counts must all be &ge; 0. </li>
+ * <li>The observed and expected arrays must have the
+ * same length and their common length must be at least 2.</li>
+ * </ul></p>
+ *
+ * <p>If any of the preconditions are not met, a
+ * {@code MathIllegalArgumentException} is thrown.</p>
+ *
+ * <p><strong>Note:</strong>This implementation rescales the
+ * {@code expected} array if necessary to ensure that the sum of the
+ * expected and observed counts are equal.</p>
+ *
+ * @param observed array of observed frequency counts
+ * @param expected array of expected frequency counts
+ * @return p-value
+ * @throws NotPositiveException if {@code observed} has negative entries
+ * @throws NotStrictlyPositiveException if {@code expected} has entries that
+ * are not strictly positive
+ * @throws DimensionMismatchException if the array lengths do not match or
+ * are less than 2.
+ * @throws MaxCountExceededException if an error occurs computing the
+ * p-value.
+ */
+ public double gTest(final double[] expected, final long[] observed)
+ throws NotPositiveException, NotStrictlyPositiveException,
+ DimensionMismatchException, MaxCountExceededException {
+
+ // pass a null rng to avoid unneeded overhead as we will not sample from this distribution
+ final ChiSquaredDistribution distribution =
+ new ChiSquaredDistribution(null, expected.length - 1.0);
+ return 1.0 - distribution.cumulativeProbability(g(expected, observed));
+ }
+
+ /**
+ * Returns the intrinsic (Hardy-Weinberg proportions) p-Value, as described
+ * in p64-69 of McDonald, J.H. 2009. Handbook of Biological Statistics
+ * (2nd ed.). Sparky House Publishing, Baltimore, Maryland.
+ *
+ * <p> The probability returned is the tail probability beyond
+ * {@link #g(double[], long[]) g(expected, observed)}
+ * in the ChiSquare distribution with degrees of freedom two less than the
+ * common length of {@code expected} and {@code observed}.</p>
+ *
+ * @param observed array of observed frequency counts
+ * @param expected array of expected frequency counts
+ * @return p-value
+ * @throws NotPositiveException if {@code observed} has negative entries
+ * @throws NotStrictlyPositiveException {@code expected} has entries that are
+ * not strictly positive
+ * @throws DimensionMismatchException if the array lengths do not match or
+ * are less than 2.
+ * @throws MaxCountExceededException if an error occurs computing the
+ * p-value.
+ */
+ public double gTestIntrinsic(final double[] expected, final long[] observed)
+ throws NotPositiveException, NotStrictlyPositiveException,
+ DimensionMismatchException, MaxCountExceededException {
+
+ // pass a null rng to avoid unneeded overhead as we will not sample from this distribution
+ final ChiSquaredDistribution distribution =
+ new ChiSquaredDistribution(null, expected.length - 2.0);
+ return 1.0 - distribution.cumulativeProbability(g(expected, observed));
+ }
+
+ /**
+ * Performs a G-Test (Log-Likelihood Ratio Test) for goodness of fit
+ * evaluating the null hypothesis that the observed counts conform to the
+ * frequency distribution described by the expected counts, with
+ * significance level {@code alpha}. Returns true iff the null
+ * hypothesis can be rejected with {@code 100 * (1 - alpha)} percent confidence.
+ *
+ * <p><strong>Example:</strong><br> To test the hypothesis that
+ * {@code observed} follows {@code expected} at the 99% level,
+ * use </p><p>
+ * {@code gTest(expected, observed, 0.01)}</p>
+ *
+ * <p>Returns true iff {@link #gTest(double[], long[])
+ * gTestGoodnessOfFitPValue(expected, observed)} < alpha</p>
+ *
+ * <p><strong>Preconditions</strong>: <ul>
+ * <li>Expected counts must all be positive. </li>
+ * <li>Observed counts must all be &ge; 0. </li>
+ * <li>The observed and expected arrays must have the same length and their
+ * common length must be at least 2.
+ * <li> {@code 0 < alpha < 0.5} </li></ul></p>
+ *
+ * <p>If any of the preconditions are not met, a
+ * {@code MathIllegalArgumentException} is thrown.</p>
+ *
+ * <p><strong>Note:</strong>This implementation rescales the
+ * {@code expected} array if necessary to ensure that the sum of the
+ * expected and observed counts are equal.</p>
+ *
+ * @param observed array of observed frequency counts
+ * @param expected array of expected frequency counts
+ * @param alpha significance level of the test
+ * @return true iff null hypothesis can be rejected with confidence 1 -
+ * alpha
+ * @throws NotPositiveException if {@code observed} has negative entries
+ * @throws NotStrictlyPositiveException if {@code expected} has entries that
+ * are not strictly positive
+ * @throws DimensionMismatchException if the array lengths do not match or
+ * are less than 2.
+ * @throws MaxCountExceededException if an error occurs computing the
+ * p-value.
+ * @throws OutOfRangeException if alpha is not strictly greater than zero
+ * and less than or equal to 0.5
+ */
+ public boolean gTest(final double[] expected, final long[] observed,
+ final double alpha)
+ throws NotPositiveException, NotStrictlyPositiveException,
+ DimensionMismatchException, OutOfRangeException, MaxCountExceededException {
+
+ if ((alpha <= 0) || (alpha > 0.5)) {
+ throw new OutOfRangeException(LocalizedFormats.OUT_OF_BOUND_SIGNIFICANCE_LEVEL,
+ alpha, 0, 0.5);
+ }
+ return gTest(expected, observed) < alpha;
+ }
+
+ /**
+ * Calculates the <a href=
+ * "http://en.wikipedia.org/wiki/Entropy_%28information_theory%29">Shannon
+ * entropy</a> for 2 Dimensional Matrix. The value returned is the entropy
+ * of the vector formed by concatenating the rows (or columns) of {@code k}
+ * to form a vector. See {@link #entropy(long[])}.
+ *
+ * @param k 2 Dimensional Matrix of long values (for ex. the counts of a
+ * trials)
+ * @return Shannon Entropy of the given Matrix
+ *
+ */
+ private double entropy(final long[][] k) {
+ double h = 0d;
+ double sum_k = 0d;
+ for (int i = 0; i < k.length; i++) {
+ for (int j = 0; j < k[i].length; j++) {
+ sum_k += (double) k[i][j];
+ }
+ }
+ for (int i = 0; i < k.length; i++) {
+ for (int j = 0; j < k[i].length; j++) {
+ if (k[i][j] != 0) {
+ final double p_ij = (double) k[i][j] / sum_k;
+ h += p_ij * FastMath.log(p_ij);
+ }
+ }
+ }
+ return -h;
+ }
+
+ /**
+ * Calculates the <a href="http://en.wikipedia.org/wiki/Entropy_%28information_theory%29">
+ * Shannon entropy</a> for a vector. The values of {@code k} are taken to be
+ * incidence counts of the values of a random variable. What is returned is <br/>
+ * &sum;p<sub>i</sub>log(p<sub>i</sub><br/>
+ * where p<sub>i</sub> = k[i] / (sum of elements in k)
+ *
+ * @param k Vector (for ex. Row Sums of a trials)
+ * @return Shannon Entropy of the given Vector
+ *
+ */
+ private double entropy(final long[] k) {
+ double h = 0d;
+ double sum_k = 0d;
+ for (int i = 0; i < k.length; i++) {
+ sum_k += (double) k[i];
+ }
+ for (int i = 0; i < k.length; i++) {
+ if (k[i] != 0) {
+ final double p_i = (double) k[i] / sum_k;
+ h += p_i * FastMath.log(p_i);
+ }
+ }
+ return -h;
+ }
+
+ /**
+ * <p>Computes a G (Log-Likelihood Ratio) two sample test statistic for
+ * independence comparing frequency counts in
+ * {@code observed1} and {@code observed2}. The sums of frequency
+ * counts in the two samples are not required to be the same. The formula
+ * used to compute the test statistic is </p>
+ *
+ * <p>{@code 2 * totalSum * [H(rowSums) + H(colSums) - H(k)]}</p>
+ *
+ * <p> where {@code H} is the
+ * <a href="http://en.wikipedia.org/wiki/Entropy_%28information_theory%29">
+ * Shannon Entropy</a> of the random variable formed by viewing the elements
+ * of the argument array as incidence counts; <br/>
+ * {@code k} is a matrix with rows {@code [observed1, observed2]}; <br/>
+ * {@code rowSums, colSums} are the row/col sums of {@code k}; <br>
+ * and {@code totalSum} is the overall sum of all entries in {@code k}.</p>
+ *
+ * <p>This statistic can be used to perform a G test evaluating the null
+ * hypothesis that both observed counts are independent </p>
+ *
+ * <p> <strong>Preconditions</strong>: <ul>
+ * <li>Observed counts must be non-negative. </li>
+ * <li>Observed counts for a specific bin must not both be zero. </li>
+ * <li>Observed counts for a specific sample must not all be 0. </li>
+ * <li>The arrays {@code observed1} and {@code observed2} must have
+ * the same length and their common length must be at least 2. </li></ul></p>
+ *
+ * <p>If any of the preconditions are not met, a
+ * {@code MathIllegalArgumentException} is thrown.</p>
+ *
+ * @param observed1 array of observed frequency counts of the first data set
+ * @param observed2 array of observed frequency counts of the second data
+ * set
+ * @return G-Test statistic
+ * @throws DimensionMismatchException the the lengths of the arrays do not
+ * match or their common length is less than 2
+ * @throws NotPositiveException if any entry in {@code observed1} or
+ * {@code observed2} is negative
+ * @throws ZeroException if either all counts of
+ * {@code observed1} or {@code observed2} are zero, or if the count
+ * at the same index is zero for both arrays.
+ */
+ public double gDataSetsComparison(final long[] observed1, final long[] observed2)
+ throws DimensionMismatchException, NotPositiveException, ZeroException {
+
+ // Make sure lengths are same
+ if (observed1.length < 2) {
+ throw new DimensionMismatchException(observed1.length, 2);
+ }
+ if (observed1.length != observed2.length) {
+ throw new DimensionMismatchException(observed1.length, observed2.length);
+ }
+
+ // Ensure non-negative counts
+ MathArrays.checkNonNegative(observed1);
+ MathArrays.checkNonNegative(observed2);
+
+ // Compute and compare count sums
+ long countSum1 = 0;
+ long countSum2 = 0;
+
+ // Compute and compare count sums
+ final long[] collSums = new long[observed1.length];
+ final long[][] k = new long[2][observed1.length];
+
+ for (int i = 0; i < observed1.length; i++) {
+ if (observed1[i] == 0 && observed2[i] == 0) {
+ throw new ZeroException(LocalizedFormats.OBSERVED_COUNTS_BOTTH_ZERO_FOR_ENTRY, i);
+ } else {
+ countSum1 += observed1[i];
+ countSum2 += observed2[i];
+ collSums[i] = observed1[i] + observed2[i];
+ k[0][i] = observed1[i];
+ k[1][i] = observed2[i];
+ }
+ }
+ // Ensure neither sample is uniformly 0
+ if (countSum1 == 0 || countSum2 == 0) {
+ throw new ZeroException();
+ }
+ final long[] rowSums = {countSum1, countSum2};
+ final double sum = (double) countSum1 + (double) countSum2;
+ return 2 * sum * (entropy(rowSums) + entropy(collSums) - entropy(k));
+ }
+
+ /**
+ * Calculates the root log-likelihood ratio for 2 state Datasets. See
+ * {@link #gDataSetsComparison(long[], long[] )}.
+ *
+ * <p>Given two events A and B, let k11 be the number of times both events
+ * occur, k12 the incidence of B without A, k21 the count of A without B,
+ * and k22 the number of times neither A nor B occurs. What is returned
+ * by this method is </p>
+ *
+ * <p>{@code (sgn) sqrt(gValueDataSetsComparison({k11, k12}, {k21, k22})}</p>
+ *
+ * <p>where {@code sgn} is -1 if {@code k11 / (k11 + k12) < k21 / (k21 + k22))};<br/>
+ * 1 otherwise.</p>
+ *
+ * <p>Signed root LLR has two advantages over the basic LLR: a) it is positive
+ * where k11 is bigger than expected, negative where it is lower b) if there is
+ * no difference it is asymptotically normally distributed. This allows one
+ * to talk about "number of standard deviations" which is a more common frame
+ * of reference than the chi^2 distribution.</p>
+ *
+ * @param k11 number of times the two events occurred together (AB)
+ * @param k12 number of times the second event occurred WITHOUT the
+ * first event (notA,B)
+ * @param k21 number of times the first event occurred WITHOUT the
+ * second event (A, notB)
+ * @param k22 number of times something else occurred (i.e. was neither
+ * of these events (notA, notB)
+ * @return root log-likelihood ratio
+ *
+ */
+ public double rootLogLikelihoodRatio(final long k11, long k12,
+ final long k21, final long k22) {
+ final double llr = gDataSetsComparison(
+ new long[]{k11, k12}, new long[]{k21, k22});
+ double sqrt = FastMath.sqrt(llr);
+ if ((double) k11 / (k11 + k12) < (double) k21 / (k21 + k22)) {
+ sqrt = -sqrt;
+ }
+ return sqrt;
+ }
+
+ /**
+ * <p>Returns the <i>observed significance level</i>, or <a href=
+ * "http://www.cas.lancs.ac.uk/glossary_v1.1/hyptest.html#pvalue">
+ * p-value</a>, associated with a G-Value (Log-Likelihood Ratio) for two
+ * sample test comparing bin frequency counts in {@code observed1} and
+ * {@code observed2}.</p>
+ *
+ * <p>The number returned is the smallest significance level at which one
+ * can reject the null hypothesis that the observed counts conform to the
+ * same distribution. </p>
+ *
+ * <p>See {@link #gTest(double[], long[])} for details
+ * on how the p-value is computed. The degrees of of freedom used to
+ * perform the test is one less than the common length of the input observed
+ * count arrays.</p>
+ *
+ * <p><strong>Preconditions</strong>:
+ * <ul> <li>Observed counts must be non-negative. </li>
+ * <li>Observed counts for a specific bin must not both be zero. </li>
+ * <li>Observed counts for a specific sample must not all be 0. </li>
+ * <li>The arrays {@code observed1} and {@code observed2} must
+ * have the same length and their common length must be at least 2. </li>
+ * </ul><p>
+ * <p> If any of the preconditions are not met, a
+ * {@code MathIllegalArgumentException} is thrown.</p>
+ *
+ * @param observed1 array of observed frequency counts of the first data set
+ * @param observed2 array of observed frequency counts of the second data
+ * set
+ * @return p-value
+ * @throws DimensionMismatchException the the length of the arrays does not
+ * match or their common length is less than 2
+ * @throws NotPositiveException if any of the entries in {@code observed1} or
+ * {@code observed2} are negative
+ * @throws ZeroException if either all counts of {@code observed1} or
+ * {@code observed2} are zero, or if the count at some index is
+ * zero for both arrays
+ * @throws MaxCountExceededException if an error occurs computing the
+ * p-value.
+ */
+ public double gTestDataSetsComparison(final long[] observed1,
+ final long[] observed2)
+ throws DimensionMismatchException, NotPositiveException, ZeroException,
+ MaxCountExceededException {
+
+ // pass a null rng to avoid unneeded overhead as we will not sample from this distribution
+ final ChiSquaredDistribution distribution =
+ new ChiSquaredDistribution(null, (double) observed1.length - 1);
+ return 1 - distribution.cumulativeProbability(
+ gDataSetsComparison(observed1, observed2));
+ }
+
+ /**
+ * <p>Performs a G-Test (Log-Likelihood Ratio Test) comparing two binned
+ * data sets. The test evaluates the null hypothesis that the two lists
+ * of observed counts conform to the same frequency distribution, with
+ * significance level {@code alpha}. Returns true iff the null
+ * hypothesis can be rejected with 100 * (1 - alpha) percent confidence.
+ * </p>
+ * <p>See {@link #gDataSetsComparison(long[], long[])} for details
+ * on the formula used to compute the G (LLR) statistic used in the test and
+ * {@link #gTest(double[], long[])} for information on how
+ * the observed significance level is computed. The degrees of of freedom used
+ * to perform the test is one less than the common length of the input observed
+ * count arrays. </p>
+ *
+ * <strong>Preconditions</strong>: <ul>
+ * <li>Observed counts must be non-negative. </li>
+ * <li>Observed counts for a specific bin must not both be zero. </li>
+ * <li>Observed counts for a specific sample must not all be 0. </li>
+ * <li>The arrays {@code observed1} and {@code observed2} must
+ * have the same length and their common length must be at least 2. </li>
+ * <li>{@code 0 < alpha < 0.5} </li></ul></p>
+ *
+ * <p>If any of the preconditions are not met, a
+ * {@code MathIllegalArgumentException} is thrown.</p>
+ *
+ * @param observed1 array of observed frequency counts of the first data set
+ * @param observed2 array of observed frequency counts of the second data
+ * set
+ * @param alpha significance level of the test
+ * @return true iff null hypothesis can be rejected with confidence 1 -
+ * alpha
+ * @throws DimensionMismatchException the the length of the arrays does not
+ * match
+ * @throws NotPositiveException if any of the entries in {@code observed1} or
+ * {@code observed2} are negative
+ * @throws ZeroException if either all counts of {@code observed1} or
+ * {@code observed2} are zero, or if the count at some index is
+ * zero for both arrays
+ * @throws OutOfRangeException if {@code alpha} is not in the range
+ * (0, 0.5]
+ * @throws MaxCountExceededException if an error occurs performing the test
+ */
+ public boolean gTestDataSetsComparison(
+ final long[] observed1,
+ final long[] observed2,
+ final double alpha)
+ throws DimensionMismatchException, NotPositiveException,
+ ZeroException, OutOfRangeException, MaxCountExceededException {
+
+ if (alpha <= 0 || alpha > 0.5) {
+ throw new OutOfRangeException(
+ LocalizedFormats.OUT_OF_BOUND_SIGNIFICANCE_LEVEL, alpha, 0, 0.5);
+ }
+ return gTestDataSetsComparison(observed1, observed2) < alpha;
+ }
+}
diff --git a/src/main/java/org/apache/commons/math3/stat/inference/KolmogorovSmirnovTest.java b/src/main/java/org/apache/commons/math3/stat/inference/KolmogorovSmirnovTest.java
new file mode 100644
index 0000000..6b70e9b
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/stat/inference/KolmogorovSmirnovTest.java
@@ -0,0 +1,1270 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math3.stat.inference;
+
+import java.math.BigDecimal;
+import java.util.Arrays;
+import java.util.HashSet;
+
+import org.apache.commons.math3.distribution.EnumeratedRealDistribution;
+import org.apache.commons.math3.distribution.RealDistribution;
+import org.apache.commons.math3.distribution.UniformRealDistribution;
+import org.apache.commons.math3.exception.InsufficientDataException;
+import org.apache.commons.math3.exception.MathArithmeticException;
+import org.apache.commons.math3.exception.MathInternalError;
+import org.apache.commons.math3.exception.NullArgumentException;
+import org.apache.commons.math3.exception.NumberIsTooLargeException;
+import org.apache.commons.math3.exception.OutOfRangeException;
+import org.apache.commons.math3.exception.TooManyIterationsException;
+import org.apache.commons.math3.exception.util.LocalizedFormats;
+import org.apache.commons.math3.fraction.BigFraction;
+import org.apache.commons.math3.fraction.BigFractionField;
+import org.apache.commons.math3.fraction.FractionConversionException;
+import org.apache.commons.math3.linear.Array2DRowFieldMatrix;
+import org.apache.commons.math3.linear.FieldMatrix;
+import org.apache.commons.math3.linear.MatrixUtils;
+import org.apache.commons.math3.linear.RealMatrix;
+import org.apache.commons.math3.random.JDKRandomGenerator;
+import org.apache.commons.math3.random.RandomGenerator;
+import org.apache.commons.math3.random.Well19937c;
+import org.apache.commons.math3.util.CombinatoricsUtils;
+import org.apache.commons.math3.util.FastMath;
+import org.apache.commons.math3.util.MathArrays;
+import org.apache.commons.math3.util.MathUtils;
+
+/**
+ * Implementation of the <a href="http://en.wikipedia.org/wiki/Kolmogorov-Smirnov_test">
+ * Kolmogorov-Smirnov (K-S) test</a> for equality of continuous distributions.
+ * <p>
+ * The K-S test uses a statistic based on the maximum deviation of the empirical distribution of
+ * sample data points from the distribution expected under the null hypothesis. For one-sample tests
+ * evaluating the null hypothesis that a set of sample data points follow a given distribution, the
+ * test statistic is \(D_n=\sup_x |F_n(x)-F(x)|\), where \(F\) is the expected distribution and
+ * \(F_n\) is the empirical distribution of the \(n\) sample data points. The distribution of
+ * \(D_n\) is estimated using a method based on [1] with certain quick decisions for extreme values
+ * given in [2].
+ * </p>
+ * <p>
+ * Two-sample tests are also supported, evaluating the null hypothesis that the two samples
+ * {@code x} and {@code y} come from the same underlying distribution. In this case, the test
+ * statistic is \(D_{n,m}=\sup_t | F_n(t)-F_m(t)|\) where \(n\) is the length of {@code x}, \(m\) is
+ * the length of {@code y}, \(F_n\) is the empirical distribution that puts mass \(1/n\) at each of
+ * the values in {@code x} and \(F_m\) is the empirical distribution of the {@code y} values. The
+ * default 2-sample test method, {@link #kolmogorovSmirnovTest(double[], double[])} works as
+ * follows:
+ * <ul>
+ * <li>For small samples (where the product of the sample sizes is less than
+ * {@value #LARGE_SAMPLE_PRODUCT}), the method presented in [4] is used to compute the
+ * exact p-value for the 2-sample test.</li>
+ * <li>When the product of the sample sizes exceeds {@value #LARGE_SAMPLE_PRODUCT}, the asymptotic
+ * distribution of \(D_{n,m}\) is used. See {@link #approximateP(double, int, int)} for details on
+ * the approximation.</li>
+ * </ul></p><p>
+ * If the product of the sample sizes is less than {@value #LARGE_SAMPLE_PRODUCT} and the sample
+ * data contains ties, random jitter is added to the sample data to break ties before applying
+ * the algorithm above. Alternatively, the {@link #bootstrap(double[], double[], int, boolean)}
+ * method, modeled after <a href="http://sekhon.berkeley.edu/matching/ks.boot.html">ks.boot</a>
+ * in the R Matching package [3], can be used if ties are known to be present in the data.
+ * </p>
+ * <p>
+ * In the two-sample case, \(D_{n,m}\) has a discrete distribution. This makes the p-value
+ * associated with the null hypothesis \(H_0 : D_{n,m} \ge d \) differ from \(H_0 : D_{n,m} > d \)
+ * by the mass of the observed value \(d\). To distinguish these, the two-sample tests use a boolean
+ * {@code strict} parameter. This parameter is ignored for large samples.
+ * </p>
+ * <p>
+ * The methods used by the 2-sample default implementation are also exposed directly:
+ * <ul>
+ * <li>{@link #exactP(double, int, int, boolean)} computes exact 2-sample p-values</li>
+ * <li>{@link #approximateP(double, int, int)} uses the asymptotic distribution The {@code boolean}
+ * arguments in the first two methods allow the probability used to estimate the p-value to be
+ * expressed using strict or non-strict inequality. See
+ * {@link #kolmogorovSmirnovTest(double[], double[], boolean)}.</li>
+ * </ul>
+ * </p>
+ * <p>
+ * References:
+ * <ul>
+ * <li>[1] <a href="http://www.jstatsoft.org/v08/i18/"> Evaluating Kolmogorov's Distribution</a> by
+ * George Marsaglia, Wai Wan Tsang, and Jingbo Wang</li>
+ * <li>[2] <a href="http://www.jstatsoft.org/v39/i11/"> Computing the Two-Sided Kolmogorov-Smirnov
+ * Distribution</a> by Richard Simard and Pierre L'Ecuyer</li>
+ * <li>[3] Jasjeet S. Sekhon. 2011. <a href="http://www.jstatsoft.org/article/view/v042i07">
+ * Multivariate and Propensity Score Matching Software with Automated Balance Optimization:
+ * The Matching package for R</a> Journal of Statistical Software, 42(7): 1-52.</li>
+ * <li>[4] Wilcox, Rand. 2012. Introduction to Robust Estimation and Hypothesis Testing,
+ * Chapter 5, 3rd Ed. Academic Press.</li>
+ * </ul>
+ * <br/>
+ * Note that [1] contains an error in computing h, refer to <a
+ * href="https://issues.apache.org/jira/browse/MATH-437">MATH-437</a> for details.
+ * </p>
+ *
+ * @since 3.3
+ */
+public class KolmogorovSmirnovTest {
+
+ /**
+ * Bound on the number of partial sums in {@link #ksSum(double, double, int)}
+ */
+ protected static final int MAXIMUM_PARTIAL_SUM_COUNT = 100000;
+
+ /** Convergence criterion for {@link #ksSum(double, double, int)} */
+ protected static final double KS_SUM_CAUCHY_CRITERION = 1E-20;
+
+ /** Convergence criterion for the sums in #pelzGood(double, double, int)} */
+ protected static final double PG_SUM_RELATIVE_ERROR = 1.0e-10;
+
+ /** No longer used. */
+ @Deprecated
+ protected static final int SMALL_SAMPLE_PRODUCT = 200;
+
+ /**
+ * When product of sample sizes exceeds this value, 2-sample K-S test uses asymptotic
+ * distribution to compute the p-value.
+ */
+ protected static final int LARGE_SAMPLE_PRODUCT = 10000;
+
+ /** Default number of iterations used by {@link #monteCarloP(double, int, int, boolean, int)}.
+ * Deprecated as of version 3.6, as this method is no longer needed. */
+ @Deprecated
+ protected static final int MONTE_CARLO_ITERATIONS = 1000000;
+
+ /** Random data generator used by {@link #monteCarloP(double, int, int, boolean, int)} */
+ private final RandomGenerator rng;
+
+ /**
+ * Construct a KolmogorovSmirnovTest instance with a default random data generator.
+ */
+ public KolmogorovSmirnovTest() {
+ rng = new Well19937c();
+ }
+
+ /**
+ * Construct a KolmogorovSmirnovTest with the provided random data generator.
+ * The #monteCarloP(double, int, int, boolean, int) that uses the generator supplied to this
+ * constructor is deprecated as of version 3.6.
+ *
+ * @param rng random data generator used by {@link #monteCarloP(double, int, int, boolean, int)}
+ */
+ @Deprecated
+ public KolmogorovSmirnovTest(RandomGenerator rng) {
+ this.rng = rng;
+ }
+
+ /**
+ * Computes the <i>p-value</i>, or <i>observed significance level</i>, of a one-sample <a
+ * href="http://en.wikipedia.org/wiki/Kolmogorov-Smirnov_test"> Kolmogorov-Smirnov test</a>
+ * evaluating the null hypothesis that {@code data} conforms to {@code distribution}. If
+ * {@code exact} is true, the distribution used to compute the p-value is computed using
+ * extended precision. See {@link #cdfExact(double, int)}.
+ *
+ * @param distribution reference distribution
+ * @param data sample being being evaluated
+ * @param exact whether or not to force exact computation of the p-value
+ * @return the p-value associated with the null hypothesis that {@code data} is a sample from
+ * {@code distribution}
+ * @throws InsufficientDataException if {@code data} does not have length at least 2
+ * @throws NullArgumentException if {@code data} is null
+ */
+ public double kolmogorovSmirnovTest(RealDistribution distribution, double[] data, boolean exact) {
+ return 1d - cdf(kolmogorovSmirnovStatistic(distribution, data), data.length, exact);
+ }
+
+ /**
+ * Computes the one-sample Kolmogorov-Smirnov test statistic, \(D_n=\sup_x |F_n(x)-F(x)|\) where
+ * \(F\) is the distribution (cdf) function associated with {@code distribution}, \(n\) is the
+ * length of {@code data} and \(F_n\) is the empirical distribution that puts mass \(1/n\) at
+ * each of the values in {@code data}.
+ *
+ * @param distribution reference distribution
+ * @param data sample being evaluated
+ * @return Kolmogorov-Smirnov statistic \(D_n\)
+ * @throws InsufficientDataException if {@code data} does not have length at least 2
+ * @throws NullArgumentException if {@code data} is null
+ */
+ public double kolmogorovSmirnovStatistic(RealDistribution distribution, double[] data) {
+ checkArray(data);
+ final int n = data.length;
+ final double nd = n;
+ final double[] dataCopy = new double[n];
+ System.arraycopy(data, 0, dataCopy, 0, n);
+ Arrays.sort(dataCopy);
+ double d = 0d;
+ for (int i = 1; i <= n; i++) {
+ final double yi = distribution.cumulativeProbability(dataCopy[i - 1]);
+ final double currD = FastMath.max(yi - (i - 1) / nd, i / nd - yi);
+ if (currD > d) {
+ d = currD;
+ }
+ }
+ return d;
+ }
+
+ /**
+ * Computes the <i>p-value</i>, or <i>observed significance level</i>, of a two-sample <a
+ * href="http://en.wikipedia.org/wiki/Kolmogorov-Smirnov_test"> Kolmogorov-Smirnov test</a>
+ * evaluating the null hypothesis that {@code x} and {@code y} are samples drawn from the same
+ * probability distribution. Specifically, what is returned is an estimate of the probability
+ * that the {@link #kolmogorovSmirnovStatistic(double[], double[])} associated with a randomly
+ * selected partition of the combined sample into subsamples of sizes {@code x.length} and
+ * {@code y.length} will strictly exceed (if {@code strict} is {@code true}) or be at least as
+ * large as {@code strict = false}) as {@code kolmogorovSmirnovStatistic(x, y)}.
+ * <ul>
+ * <li>For small samples (where the product of the sample sizes is less than
+ * {@value #LARGE_SAMPLE_PRODUCT}), the exact p-value is computed using the method presented
+ * in [4], implemented in {@link #exactP(double, int, int, boolean)}. </li>
+ * <li>When the product of the sample sizes exceeds {@value #LARGE_SAMPLE_PRODUCT}, the
+ * asymptotic distribution of \(D_{n,m}\) is used. See {@link #approximateP(double, int, int)}
+ * for details on the approximation.</li>
+ * </ul><p>
+ * If {@code x.length * y.length} < {@value #LARGE_SAMPLE_PRODUCT} and the combined set of values in
+ * {@code x} and {@code y} contains ties, random jitter is added to {@code x} and {@code y} to
+ * break ties before computing \(D_{n,m}\) and the p-value. The jitter is uniformly distributed
+ * on (-minDelta / 2, minDelta / 2) where minDelta is the smallest pairwise difference between
+ * values in the combined sample.</p>
+ * <p>
+ * If ties are known to be present in the data, {@link #bootstrap(double[], double[], int, boolean)}
+ * may be used as an alternative method for estimating the p-value.</p>
+ *
+ * @param x first sample dataset
+ * @param y second sample dataset
+ * @param strict whether or not the probability to compute is expressed as a strict inequality
+ * (ignored for large samples)
+ * @return p-value associated with the null hypothesis that {@code x} and {@code y} represent
+ * samples from the same distribution
+ * @throws InsufficientDataException if either {@code x} or {@code y} does not have length at
+ * least 2
+ * @throws NullArgumentException if either {@code x} or {@code y} is null
+ * @see #bootstrap(double[], double[], int, boolean)
+ */
+ public double kolmogorovSmirnovTest(double[] x, double[] y, boolean strict) {
+ final long lengthProduct = (long) x.length * y.length;
+ double[] xa = null;
+ double[] ya = null;
+ if (lengthProduct < LARGE_SAMPLE_PRODUCT && hasTies(x,y)) {
+ xa = MathArrays.copyOf(x);
+ ya = MathArrays.copyOf(y);
+ fixTies(xa, ya);
+ } else {
+ xa = x;
+ ya = y;
+ }
+ if (lengthProduct < LARGE_SAMPLE_PRODUCT) {
+ return exactP(kolmogorovSmirnovStatistic(xa, ya), x.length, y.length, strict);
+ }
+ return approximateP(kolmogorovSmirnovStatistic(x, y), x.length, y.length);
+ }
+
+ /**
+ * Computes the <i>p-value</i>, or <i>observed significance level</i>, of a two-sample <a
+ * href="http://en.wikipedia.org/wiki/Kolmogorov-Smirnov_test"> Kolmogorov-Smirnov test</a>
+ * evaluating the null hypothesis that {@code x} and {@code y} are samples drawn from the same
+ * probability distribution. Assumes the strict form of the inequality used to compute the
+ * p-value. See {@link #kolmogorovSmirnovTest(RealDistribution, double[], boolean)}.
+ *
+ * @param x first sample dataset
+ * @param y second sample dataset
+ * @return p-value associated with the null hypothesis that {@code x} and {@code y} represent
+ * samples from the same distribution
+ * @throws InsufficientDataException if either {@code x} or {@code y} does not have length at
+ * least 2
+ * @throws NullArgumentException if either {@code x} or {@code y} is null
+ */
+ public double kolmogorovSmirnovTest(double[] x, double[] y) {
+ return kolmogorovSmirnovTest(x, y, true);
+ }
+
+ /**
+ * Computes the two-sample Kolmogorov-Smirnov test statistic, \(D_{n,m}=\sup_x |F_n(x)-F_m(x)|\)
+ * where \(n\) is the length of {@code x}, \(m\) is the length of {@code y}, \(F_n\) is the
+ * empirical distribution that puts mass \(1/n\) at each of the values in {@code x} and \(F_m\)
+ * is the empirical distribution of the {@code y} values.
+ *
+ * @param x first sample
+ * @param y second sample
+ * @return test statistic \(D_{n,m}\) used to evaluate the null hypothesis that {@code x} and
+ * {@code y} represent samples from the same underlying distribution
+ * @throws InsufficientDataException if either {@code x} or {@code y} does not have length at
+ * least 2
+ * @throws NullArgumentException if either {@code x} or {@code y} is null
+ */
+ public double kolmogorovSmirnovStatistic(double[] x, double[] y) {
+ return integralKolmogorovSmirnovStatistic(x, y)/((double)(x.length * (long)y.length));
+ }
+
+ /**
+ * Computes the two-sample Kolmogorov-Smirnov test statistic, \(D_{n,m}=\sup_x |F_n(x)-F_m(x)|\)
+ * where \(n\) is the length of {@code x}, \(m\) is the length of {@code y}, \(F_n\) is the
+ * empirical distribution that puts mass \(1/n\) at each of the values in {@code x} and \(F_m\)
+ * is the empirical distribution of the {@code y} values. Finally \(n m D_{n,m}\) is returned
+ * as long value.
+ *
+ * @param x first sample
+ * @param y second sample
+ * @return test statistic \(n m D_{n,m}\) used to evaluate the null hypothesis that {@code x} and
+ * {@code y} represent samples from the same underlying distribution
+ * @throws InsufficientDataException if either {@code x} or {@code y} does not have length at
+ * least 2
+ * @throws NullArgumentException if either {@code x} or {@code y} is null
+ */
+ private long integralKolmogorovSmirnovStatistic(double[] x, double[] y) {
+ checkArray(x);
+ checkArray(y);
+ // Copy and sort the sample arrays
+ final double[] sx = MathArrays.copyOf(x);
+ final double[] sy = MathArrays.copyOf(y);
+ Arrays.sort(sx);
+ Arrays.sort(sy);
+ final int n = sx.length;
+ final int m = sy.length;
+
+ int rankX = 0;
+ int rankY = 0;
+ long curD = 0l;
+
+ // Find the max difference between cdf_x and cdf_y
+ long supD = 0l;
+ do {
+ double z = Double.compare(sx[rankX], sy[rankY]) <= 0 ? sx[rankX] : sy[rankY];
+ while(rankX < n && Double.compare(sx[rankX], z) == 0) {
+ rankX += 1;
+ curD += m;
+ }
+ while(rankY < m && Double.compare(sy[rankY], z) == 0) {
+ rankY += 1;
+ curD -= n;
+ }
+ if (curD > supD) {
+ supD = curD;
+ }
+ else if (-curD > supD) {
+ supD = -curD;
+ }
+ } while(rankX < n && rankY < m);
+ return supD;
+ }
+
+ /**
+ * Computes the <i>p-value</i>, or <i>observed significance level</i>, of a one-sample <a
+ * href="http://en.wikipedia.org/wiki/Kolmogorov-Smirnov_test"> Kolmogorov-Smirnov test</a>
+ * evaluating the null hypothesis that {@code data} conforms to {@code distribution}.
+ *
+ * @param distribution reference distribution
+ * @param data sample being being evaluated
+ * @return the p-value associated with the null hypothesis that {@code data} is a sample from
+ * {@code distribution}
+ * @throws InsufficientDataException if {@code data} does not have length at least 2
+ * @throws NullArgumentException if {@code data} is null
+ */
+ public double kolmogorovSmirnovTest(RealDistribution distribution, double[] data) {
+ return kolmogorovSmirnovTest(distribution, data, false);
+ }
+
+ /**
+ * Performs a <a href="http://en.wikipedia.org/wiki/Kolmogorov-Smirnov_test"> Kolmogorov-Smirnov
+ * test</a> evaluating the null hypothesis that {@code data} conforms to {@code distribution}.
+ *
+ * @param distribution reference distribution
+ * @param data sample being being evaluated
+ * @param alpha significance level of the test
+ * @return true iff the null hypothesis that {@code data} is a sample from {@code distribution}
+ * can be rejected with confidence 1 - {@code alpha}
+ * @throws InsufficientDataException if {@code data} does not have length at least 2
+ * @throws NullArgumentException if {@code data} is null
+ */
+ public boolean kolmogorovSmirnovTest(RealDistribution distribution, double[] data, double alpha) {
+ if ((alpha <= 0) || (alpha > 0.5)) {
+ throw new OutOfRangeException(LocalizedFormats.OUT_OF_BOUND_SIGNIFICANCE_LEVEL, alpha, 0, 0.5);
+ }
+ return kolmogorovSmirnovTest(distribution, data) < alpha;
+ }
+
+ /**
+ * Estimates the <i>p-value</i> of a two-sample
+ * <a href="http://en.wikipedia.org/wiki/Kolmogorov-Smirnov_test"> Kolmogorov-Smirnov test</a>
+ * evaluating the null hypothesis that {@code x} and {@code y} are samples drawn from the same
+ * probability distribution. This method estimates the p-value by repeatedly sampling sets of size
+ * {@code x.length} and {@code y.length} from the empirical distribution of the combined sample.
+ * When {@code strict} is true, this is equivalent to the algorithm implemented in the R function
+ * {@code ks.boot}, described in <pre>
+ * Jasjeet S. Sekhon. 2011. 'Multivariate and Propensity Score Matching
+ * Software with Automated Balance Optimization: The Matching package for R.'
+ * Journal of Statistical Software, 42(7): 1-52.
+ * </pre>
+ * @param x first sample
+ * @param y second sample
+ * @param iterations number of bootstrap resampling iterations
+ * @param strict whether or not the null hypothesis is expressed as a strict inequality
+ * @return estimated p-value
+ */
+ public double bootstrap(double[] x, double[] y, int iterations, boolean strict) {
+ final int xLength = x.length;
+ final int yLength = y.length;
+ final double[] combined = new double[xLength + yLength];
+ System.arraycopy(x, 0, combined, 0, xLength);
+ System.arraycopy(y, 0, combined, xLength, yLength);
+ final EnumeratedRealDistribution dist = new EnumeratedRealDistribution(rng, combined);
+ final long d = integralKolmogorovSmirnovStatistic(x, y);
+ int greaterCount = 0;
+ int equalCount = 0;
+ double[] curX;
+ double[] curY;
+ long curD;
+ for (int i = 0; i < iterations; i++) {
+ curX = dist.sample(xLength);
+ curY = dist.sample(yLength);
+ curD = integralKolmogorovSmirnovStatistic(curX, curY);
+ if (curD > d) {
+ greaterCount++;
+ } else if (curD == d) {
+ equalCount++;
+ }
+ }
+ return strict ? greaterCount / (double) iterations :
+ (greaterCount + equalCount) / (double) iterations;
+ }
+
+ /**
+ * Computes {@code bootstrap(x, y, iterations, true)}.
+ * This is equivalent to ks.boot(x,y, nboots=iterations) using the R Matching
+ * package function. See #bootstrap(double[], double[], int, boolean).
+ *
+ * @param x first sample
+ * @param y second sample
+ * @param iterations number of bootstrap resampling iterations
+ * @return estimated p-value
+ */
+ public double bootstrap(double[] x, double[] y, int iterations) {
+ return bootstrap(x, y, iterations, true);
+ }
+
+ /**
+ * Calculates \(P(D_n < d)\) using the method described in [1] with quick decisions for extreme
+ * values given in [2] (see above). The result is not exact as with
+ * {@link #cdfExact(double, int)} because calculations are based on
+ * {@code double} rather than {@link org.apache.commons.math3.fraction.BigFraction}.
+ *
+ * @param d statistic
+ * @param n sample size
+ * @return \(P(D_n < d)\)
+ * @throws MathArithmeticException if algorithm fails to convert {@code h} to a
+ * {@link org.apache.commons.math3.fraction.BigFraction} in expressing {@code d} as \((k
+ * - h) / m\) for integer {@code k, m} and \(0 \le h < 1\)
+ */
+ public double cdf(double d, int n)
+ throws MathArithmeticException {
+ return cdf(d, n, false);
+ }
+
+ /**
+ * Calculates {@code P(D_n < d)}. The result is exact in the sense that BigFraction/BigReal is
+ * used everywhere at the expense of very slow execution time. Almost never choose this in real
+ * applications unless you are very sure; this is almost solely for verification purposes.
+ * Normally, you would choose {@link #cdf(double, int)}. See the class
+ * javadoc for definitions and algorithm description.
+ *
+ * @param d statistic
+ * @param n sample size
+ * @return \(P(D_n < d)\)
+ * @throws MathArithmeticException if the algorithm fails to convert {@code h} to a
+ * {@link org.apache.commons.math3.fraction.BigFraction} in expressing {@code d} as \((k
+ * - h) / m\) for integer {@code k, m} and \(0 \le h < 1\)
+ */
+ public double cdfExact(double d, int n)
+ throws MathArithmeticException {
+ return cdf(d, n, true);
+ }
+
+ /**
+ * Calculates {@code P(D_n < d)} using method described in [1] with quick decisions for extreme
+ * values given in [2] (see above).
+ *
+ * @param d statistic
+ * @param n sample size
+ * @param exact whether the probability should be calculated exact using
+ * {@link org.apache.commons.math3.fraction.BigFraction} everywhere at the expense of
+ * very slow execution time, or if {@code double} should be used convenient places to
+ * gain speed. Almost never choose {@code true} in real applications unless you are very
+ * sure; {@code true} is almost solely for verification purposes.
+ * @return \(P(D_n < d)\)
+ * @throws MathArithmeticException if algorithm fails to convert {@code h} to a
+ * {@link org.apache.commons.math3.fraction.BigFraction} in expressing {@code d} as \((k
+ * - h) / m\) for integer {@code k, m} and \(0 \le h < 1\).
+ */
+ public double cdf(double d, int n, boolean exact)
+ throws MathArithmeticException {
+
+ final double ninv = 1 / ((double) n);
+ final double ninvhalf = 0.5 * ninv;
+
+ if (d <= ninvhalf) {
+ return 0;
+ } else if (ninvhalf < d && d <= ninv) {
+ double res = 1;
+ final double f = 2 * d - ninv;
+ // n! f^n = n*f * (n-1)*f * ... * 1*x
+ for (int i = 1; i <= n; ++i) {
+ res *= i * f;
+ }
+ return res;
+ } else if (1 - ninv <= d && d < 1) {
+ return 1 - 2 * Math.pow(1 - d, n);
+ } else if (1 <= d) {
+ return 1;
+ }
+ if (exact) {
+ return exactK(d, n);
+ }
+ if (n <= 140) {
+ return roundedK(d, n);
+ }
+ return pelzGood(d, n);
+ }
+
+ /**
+ * Calculates the exact value of {@code P(D_n < d)} using the method described in [1] (reference
+ * in class javadoc above) and {@link org.apache.commons.math3.fraction.BigFraction} (see
+ * above).
+ *
+ * @param d statistic
+ * @param n sample size
+ * @return the two-sided probability of \(P(D_n < d)\)
+ * @throws MathArithmeticException if algorithm fails to convert {@code h} to a
+ * {@link org.apache.commons.math3.fraction.BigFraction} in expressing {@code d} as \((k
+ * - h) / m\) for integer {@code k, m} and \(0 \le h < 1\).
+ */
+ private double exactK(double d, int n)
+ throws MathArithmeticException {
+
+ final int k = (int) Math.ceil(n * d);
+
+ final FieldMatrix<BigFraction> H = this.createExactH(d, n);
+ final FieldMatrix<BigFraction> Hpower = H.power(n);
+
+ BigFraction pFrac = Hpower.getEntry(k - 1, k - 1);
+
+ for (int i = 1; i <= n; ++i) {
+ pFrac = pFrac.multiply(i).divide(n);
+ }
+
+ /*
+ * BigFraction.doubleValue converts numerator to double and the denominator to double and
+ * divides afterwards. That gives NaN quite easy. This does not (scale is the number of
+ * digits):
+ */
+ return pFrac.bigDecimalValue(20, BigDecimal.ROUND_HALF_UP).doubleValue();
+ }
+
+ /**
+ * Calculates {@code P(D_n < d)} using method described in [1] and doubles (see above).
+ *
+ * @param d statistic
+ * @param n sample size
+ * @return \(P(D_n < d)\)
+ */
+ private double roundedK(double d, int n) {
+
+ final int k = (int) Math.ceil(n * d);
+ final RealMatrix H = this.createRoundedH(d, n);
+ final RealMatrix Hpower = H.power(n);
+
+ double pFrac = Hpower.getEntry(k - 1, k - 1);
+ for (int i = 1; i <= n; ++i) {
+ pFrac *= (double) i / (double) n;
+ }
+
+ return pFrac;
+ }
+
+ /**
+ * Computes the Pelz-Good approximation for \(P(D_n < d)\) as described in [2] in the class javadoc.
+ *
+ * @param d value of d-statistic (x in [2])
+ * @param n sample size
+ * @return \(P(D_n < d)\)
+ * @since 3.4
+ */
+ public double pelzGood(double d, int n) {
+ // Change the variable since approximation is for the distribution evaluated at d / sqrt(n)
+ final double sqrtN = FastMath.sqrt(n);
+ final double z = d * sqrtN;
+ final double z2 = d * d * n;
+ final double z4 = z2 * z2;
+ final double z6 = z4 * z2;
+ final double z8 = z4 * z4;
+
+ // Eventual return value
+ double ret = 0;
+
+ // Compute K_0(z)
+ double sum = 0;
+ double increment = 0;
+ double kTerm = 0;
+ double z2Term = MathUtils.PI_SQUARED / (8 * z2);
+ int k = 1;
+ for (; k < MAXIMUM_PARTIAL_SUM_COUNT; k++) {
+ kTerm = 2 * k - 1;
+ increment = FastMath.exp(-z2Term * kTerm * kTerm);
+ sum += increment;
+ if (increment <= PG_SUM_RELATIVE_ERROR * sum) {
+ break;
+ }
+ }
+ if (k == MAXIMUM_PARTIAL_SUM_COUNT) {
+ throw new TooManyIterationsException(MAXIMUM_PARTIAL_SUM_COUNT);
+ }
+ ret = sum * FastMath.sqrt(2 * FastMath.PI) / z;
+
+ // K_1(z)
+ // Sum is -inf to inf, but k term is always (k + 1/2) ^ 2, so really have
+ // twice the sum from k = 0 to inf (k = -1 is same as 0, -2 same as 1, ...)
+ final double twoZ2 = 2 * z2;
+ sum = 0;
+ kTerm = 0;
+ double kTerm2 = 0;
+ for (k = 0; k < MAXIMUM_PARTIAL_SUM_COUNT; k++) {
+ kTerm = k + 0.5;
+ kTerm2 = kTerm * kTerm;
+ increment = (MathUtils.PI_SQUARED * kTerm2 - z2) * FastMath.exp(-MathUtils.PI_SQUARED * kTerm2 / twoZ2);
+ sum += increment;
+ if (FastMath.abs(increment) < PG_SUM_RELATIVE_ERROR * FastMath.abs(sum)) {
+ break;
+ }
+ }
+ if (k == MAXIMUM_PARTIAL_SUM_COUNT) {
+ throw new TooManyIterationsException(MAXIMUM_PARTIAL_SUM_COUNT);
+ }
+ final double sqrtHalfPi = FastMath.sqrt(FastMath.PI / 2);
+ // Instead of doubling sum, divide by 3 instead of 6
+ ret += sum * sqrtHalfPi / (3 * z4 * sqrtN);
+
+ // K_2(z)
+ // Same drill as K_1, but with two doubly infinite sums, all k terms are even powers.
+ final double z4Term = 2 * z4;
+ final double z6Term = 6 * z6;
+ z2Term = 5 * z2;
+ final double pi4 = MathUtils.PI_SQUARED * MathUtils.PI_SQUARED;
+ sum = 0;
+ kTerm = 0;
+ kTerm2 = 0;
+ for (k = 0; k < MAXIMUM_PARTIAL_SUM_COUNT; k++) {
+ kTerm = k + 0.5;
+ kTerm2 = kTerm * kTerm;
+ increment = (z6Term + z4Term + MathUtils.PI_SQUARED * (z4Term - z2Term) * kTerm2 +
+ pi4 * (1 - twoZ2) * kTerm2 * kTerm2) * FastMath.exp(-MathUtils.PI_SQUARED * kTerm2 / twoZ2);
+ sum += increment;
+ if (FastMath.abs(increment) < PG_SUM_RELATIVE_ERROR * FastMath.abs(sum)) {
+ break;
+ }
+ }
+ if (k == MAXIMUM_PARTIAL_SUM_COUNT) {
+ throw new TooManyIterationsException(MAXIMUM_PARTIAL_SUM_COUNT);
+ }
+ double sum2 = 0;
+ kTerm2 = 0;
+ for (k = 1; k < MAXIMUM_PARTIAL_SUM_COUNT; k++) {
+ kTerm2 = k * k;
+ increment = MathUtils.PI_SQUARED * kTerm2 * FastMath.exp(-MathUtils.PI_SQUARED * kTerm2 / twoZ2);
+ sum2 += increment;
+ if (FastMath.abs(increment) < PG_SUM_RELATIVE_ERROR * FastMath.abs(sum2)) {
+ break;
+ }
+ }
+ if (k == MAXIMUM_PARTIAL_SUM_COUNT) {
+ throw new TooManyIterationsException(MAXIMUM_PARTIAL_SUM_COUNT);
+ }
+ // Again, adjust coefficients instead of doubling sum, sum2
+ ret += (sqrtHalfPi / n) * (sum / (36 * z2 * z2 * z2 * z) - sum2 / (18 * z2 * z));
+
+ // K_3(z) One more time with feeling - two doubly infinite sums, all k powers even.
+ // Multiply coefficient denominators by 2, so omit doubling sums.
+ final double pi6 = pi4 * MathUtils.PI_SQUARED;
+ sum = 0;
+ double kTerm4 = 0;
+ double kTerm6 = 0;
+ for (k = 0; k < MAXIMUM_PARTIAL_SUM_COUNT; k++) {
+ kTerm = k + 0.5;
+ kTerm2 = kTerm * kTerm;
+ kTerm4 = kTerm2 * kTerm2;
+ kTerm6 = kTerm4 * kTerm2;
+ increment = (pi6 * kTerm6 * (5 - 30 * z2) + pi4 * kTerm4 * (-60 * z2 + 212 * z4) +
+ MathUtils.PI_SQUARED * kTerm2 * (135 * z4 - 96 * z6) - 30 * z6 - 90 * z8) *
+ FastMath.exp(-MathUtils.PI_SQUARED * kTerm2 / twoZ2);
+ sum += increment;
+ if (FastMath.abs(increment) < PG_SUM_RELATIVE_ERROR * FastMath.abs(sum)) {
+ break;
+ }
+ }
+ if (k == MAXIMUM_PARTIAL_SUM_COUNT) {
+ throw new TooManyIterationsException(MAXIMUM_PARTIAL_SUM_COUNT);
+ }
+ sum2 = 0;
+ for (k = 1; k < MAXIMUM_PARTIAL_SUM_COUNT; k++) {
+ kTerm2 = k * k;
+ kTerm4 = kTerm2 * kTerm2;
+ increment = (-pi4 * kTerm4 + 3 * MathUtils.PI_SQUARED * kTerm2 * z2) *
+ FastMath.exp(-MathUtils.PI_SQUARED * kTerm2 / twoZ2);
+ sum2 += increment;
+ if (FastMath.abs(increment) < PG_SUM_RELATIVE_ERROR * FastMath.abs(sum2)) {
+ break;
+ }
+ }
+ if (k == MAXIMUM_PARTIAL_SUM_COUNT) {
+ throw new TooManyIterationsException(MAXIMUM_PARTIAL_SUM_COUNT);
+ }
+ return ret + (sqrtHalfPi / (sqrtN * n)) * (sum / (3240 * z6 * z4) +
+ + sum2 / (108 * z6));
+
+ }
+
+ /***
+ * Creates {@code H} of size {@code m x m} as described in [1] (see above).
+ *
+ * @param d statistic
+ * @param n sample size
+ * @return H matrix
+ * @throws NumberIsTooLargeException if fractional part is greater than 1
+ * @throws FractionConversionException if algorithm fails to convert {@code h} to a
+ * {@link org.apache.commons.math3.fraction.BigFraction} in expressing {@code d} as \((k
+ * - h) / m\) for integer {@code k, m} and \(0 <= h < 1\).
+ */
+ private FieldMatrix<BigFraction> createExactH(double d, int n)
+ throws NumberIsTooLargeException, FractionConversionException {
+
+ final int k = (int) Math.ceil(n * d);
+ final int m = 2 * k - 1;
+ final double hDouble = k - n * d;
+ if (hDouble >= 1) {
+ throw new NumberIsTooLargeException(hDouble, 1.0, false);
+ }
+ BigFraction h = null;
+ try {
+ h = new BigFraction(hDouble, 1.0e-20, 10000);
+ } catch (final FractionConversionException e1) {
+ try {
+ h = new BigFraction(hDouble, 1.0e-10, 10000);
+ } catch (final FractionConversionException e2) {
+ h = new BigFraction(hDouble, 1.0e-5, 10000);
+ }
+ }
+ final BigFraction[][] Hdata = new BigFraction[m][m];
+
+ /*
+ * Start by filling everything with either 0 or 1.
+ */
+ for (int i = 0; i < m; ++i) {
+ for (int j = 0; j < m; ++j) {
+ if (i - j + 1 < 0) {
+ Hdata[i][j] = BigFraction.ZERO;
+ } else {
+ Hdata[i][j] = BigFraction.ONE;
+ }
+ }
+ }
+
+ /*
+ * Setting up power-array to avoid calculating the same value twice: hPowers[0] = h^1 ...
+ * hPowers[m-1] = h^m
+ */
+ final BigFraction[] hPowers = new BigFraction[m];
+ hPowers[0] = h;
+ for (int i = 1; i < m; ++i) {
+ hPowers[i] = h.multiply(hPowers[i - 1]);
+ }
+
+ /*
+ * First column and last row has special values (each other reversed).
+ */
+ for (int i = 0; i < m; ++i) {
+ Hdata[i][0] = Hdata[i][0].subtract(hPowers[i]);
+ Hdata[m - 1][i] = Hdata[m - 1][i].subtract(hPowers[m - i - 1]);
+ }
+
+ /*
+ * [1] states: "For 1/2 < h < 1 the bottom left element of the matrix should be (1 - 2*h^m +
+ * (2h - 1)^m )/m!" Since 0 <= h < 1, then if h > 1/2 is sufficient to check:
+ */
+ if (h.compareTo(BigFraction.ONE_HALF) == 1) {
+ Hdata[m - 1][0] = Hdata[m - 1][0].add(h.multiply(2).subtract(1).pow(m));
+ }
+
+ /*
+ * Aside from the first column and last row, the (i, j)-th element is 1/(i - j + 1)! if i -
+ * j + 1 >= 0, else 0. 1's and 0's are already put, so only division with (i - j + 1)! is
+ * needed in the elements that have 1's. There is no need to calculate (i - j + 1)! and then
+ * divide - small steps avoid overflows. Note that i - j + 1 > 0 <=> i + 1 > j instead of
+ * j'ing all the way to m. Also note that it is started at g = 2 because dividing by 1 isn't
+ * really necessary.
+ */
+ for (int i = 0; i < m; ++i) {
+ for (int j = 0; j < i + 1; ++j) {
+ if (i - j + 1 > 0) {
+ for (int g = 2; g <= i - j + 1; ++g) {
+ Hdata[i][j] = Hdata[i][j].divide(g);
+ }
+ }
+ }
+ }
+ return new Array2DRowFieldMatrix<BigFraction>(BigFractionField.getInstance(), Hdata);
+ }
+
+ /***
+ * Creates {@code H} of size {@code m x m} as described in [1] (see above)
+ * using double-precision.
+ *
+ * @param d statistic
+ * @param n sample size
+ * @return H matrix
+ * @throws NumberIsTooLargeException if fractional part is greater than 1
+ */
+ private RealMatrix createRoundedH(double d, int n)
+ throws NumberIsTooLargeException {
+
+ final int k = (int) Math.ceil(n * d);
+ final int m = 2 * k - 1;
+ final double h = k - n * d;
+ if (h >= 1) {
+ throw new NumberIsTooLargeException(h, 1.0, false);
+ }
+ final double[][] Hdata = new double[m][m];
+
+ /*
+ * Start by filling everything with either 0 or 1.
+ */
+ for (int i = 0; i < m; ++i) {
+ for (int j = 0; j < m; ++j) {
+ if (i - j + 1 < 0) {
+ Hdata[i][j] = 0;
+ } else {
+ Hdata[i][j] = 1;
+ }
+ }
+ }
+
+ /*
+ * Setting up power-array to avoid calculating the same value twice: hPowers[0] = h^1 ...
+ * hPowers[m-1] = h^m
+ */
+ final double[] hPowers = new double[m];
+ hPowers[0] = h;
+ for (int i = 1; i < m; ++i) {
+ hPowers[i] = h * hPowers[i - 1];
+ }
+
+ /*
+ * First column and last row has special values (each other reversed).
+ */
+ for (int i = 0; i < m; ++i) {
+ Hdata[i][0] = Hdata[i][0] - hPowers[i];
+ Hdata[m - 1][i] -= hPowers[m - i - 1];
+ }
+
+ /*
+ * [1] states: "For 1/2 < h < 1 the bottom left element of the matrix should be (1 - 2*h^m +
+ * (2h - 1)^m )/m!" Since 0 <= h < 1, then if h > 1/2 is sufficient to check:
+ */
+ if (Double.compare(h, 0.5) > 0) {
+ Hdata[m - 1][0] += FastMath.pow(2 * h - 1, m);
+ }
+
+ /*
+ * Aside from the first column and last row, the (i, j)-th element is 1/(i - j + 1)! if i -
+ * j + 1 >= 0, else 0. 1's and 0's are already put, so only division with (i - j + 1)! is
+ * needed in the elements that have 1's. There is no need to calculate (i - j + 1)! and then
+ * divide - small steps avoid overflows. Note that i - j + 1 > 0 <=> i + 1 > j instead of
+ * j'ing all the way to m. Also note that it is started at g = 2 because dividing by 1 isn't
+ * really necessary.
+ */
+ for (int i = 0; i < m; ++i) {
+ for (int j = 0; j < i + 1; ++j) {
+ if (i - j + 1 > 0) {
+ for (int g = 2; g <= i - j + 1; ++g) {
+ Hdata[i][j] /= g;
+ }
+ }
+ }
+ }
+ return MatrixUtils.createRealMatrix(Hdata);
+ }
+
+ /**
+ * Verifies that {@code array} has length at least 2.
+ *
+ * @param array array to test
+ * @throws NullArgumentException if array is null
+ * @throws InsufficientDataException if array is too short
+ */
+ private void checkArray(double[] array) {
+ if (array == null) {
+ throw new NullArgumentException(LocalizedFormats.NULL_NOT_ALLOWED);
+ }
+ if (array.length < 2) {
+ throw new InsufficientDataException(LocalizedFormats.INSUFFICIENT_OBSERVED_POINTS_IN_SAMPLE, array.length,
+ 2);
+ }
+ }
+
+ /**
+ * Computes \( 1 + 2 \sum_{i=1}^\infty (-1)^i e^{-2 i^2 t^2} \) stopping when successive partial
+ * sums are within {@code tolerance} of one another, or when {@code maxIterations} partial sums
+ * have been computed. If the sum does not converge before {@code maxIterations} iterations a
+ * {@link TooManyIterationsException} is thrown.
+ *
+ * @param t argument
+ * @param tolerance Cauchy criterion for partial sums
+ * @param maxIterations maximum number of partial sums to compute
+ * @return Kolmogorov sum evaluated at t
+ * @throws TooManyIterationsException if the series does not converge
+ */
+ public double ksSum(double t, double tolerance, int maxIterations) {
+ if (t == 0.0) {
+ return 0.0;
+ }
+
+ // TODO: for small t (say less than 1), the alternative expansion in part 3 of [1]
+ // from class javadoc should be used.
+
+ final double x = -2 * t * t;
+ int sign = -1;
+ long i = 1;
+ double partialSum = 0.5d;
+ double delta = 1;
+ while (delta > tolerance && i < maxIterations) {
+ delta = FastMath.exp(x * i * i);
+ partialSum += sign * delta;
+ sign *= -1;
+ i++;
+ }
+ if (i == maxIterations) {
+ throw new TooManyIterationsException(maxIterations);
+ }
+ return partialSum * 2;
+ }
+
+ /**
+ * Given a d-statistic in the range [0, 1] and the two sample sizes n and m,
+ * an integral d-statistic in the range [0, n*m] is calculated, that can be used for
+ * comparison with other integral d-statistics. Depending whether {@code strict} is
+ * {@code true} or not, the returned value divided by (n*m) is greater than
+ * (resp greater than or equal to) the given d value (allowing some tolerance).
+ *
+ * @param d a d-statistic in the range [0, 1]
+ * @param n first sample size
+ * @param m second sample size
+ * @param strict whether the returned value divided by (n*m) is allowed to be equal to d
+ * @return the integral d-statistic in the range [0, n*m]
+ */
+ private static long calculateIntegralD(double d, int n, int m, boolean strict) {
+ final double tol = 1e-12; // d-values within tol of one another are considered equal
+ long nm = n * (long)m;
+ long upperBound = (long)FastMath.ceil((d - tol) * nm);
+ long lowerBound = (long)FastMath.floor((d + tol) * nm);
+ if (strict && lowerBound == upperBound) {
+ return upperBound + 1l;
+ }
+ else {
+ return upperBound;
+ }
+ }
+
+ /**
+ * Computes \(P(D_{n,m} > d)\) if {@code strict} is {@code true}; otherwise \(P(D_{n,m} \ge
+ * d)\), where \(D_{n,m}\) is the 2-sample Kolmogorov-Smirnov statistic. See
+ * {@link #kolmogorovSmirnovStatistic(double[], double[])} for the definition of \(D_{n,m}\).
+ * <p>
+ * The returned probability is exact, implemented by unwinding the recursive function
+ * definitions presented in [4] (class javadoc).
+ * </p>
+ *
+ * @param d D-statistic value
+ * @param n first sample size
+ * @param m second sample size
+ * @param strict whether or not the probability to compute is expressed as a strict inequality
+ * @return probability that a randomly selected m-n partition of m + n generates \(D_{n,m}\)
+ * greater than (resp. greater than or equal to) {@code d}
+ */
+ public double exactP(double d, int n, int m, boolean strict) {
+ return 1 - n(m, n, m, n, calculateIntegralD(d, m, n, strict), strict) /
+ CombinatoricsUtils.binomialCoefficientDouble(n + m, m);
+ }
+
+ /**
+ * Uses the Kolmogorov-Smirnov distribution to approximate \(P(D_{n,m} > d)\) where \(D_{n,m}\)
+ * is the 2-sample Kolmogorov-Smirnov statistic. See
+ * {@link #kolmogorovSmirnovStatistic(double[], double[])} for the definition of \(D_{n,m}\).
+ * <p>
+ * Specifically, what is returned is \(1 - k(d \sqrt{mn / (m + n)})\) where \(k(t) = 1 + 2
+ * \sum_{i=1}^\infty (-1)^i e^{-2 i^2 t^2}\). See {@link #ksSum(double, double, int)} for
+ * details on how convergence of the sum is determined. This implementation passes {@code ksSum}
+ * {@value #KS_SUM_CAUCHY_CRITERION} as {@code tolerance} and
+ * {@value #MAXIMUM_PARTIAL_SUM_COUNT} as {@code maxIterations}.
+ * </p>
+ *
+ * @param d D-statistic value
+ * @param n first sample size
+ * @param m second sample size
+ * @return approximate probability that a randomly selected m-n partition of m + n generates
+ * \(D_{n,m}\) greater than {@code d}
+ */
+ public double approximateP(double d, int n, int m) {
+ final double dm = m;
+ final double dn = n;
+ return 1 - ksSum(d * FastMath.sqrt((dm * dn) / (dm + dn)),
+ KS_SUM_CAUCHY_CRITERION, MAXIMUM_PARTIAL_SUM_COUNT);
+ }
+
+ /**
+ * Fills a boolean array randomly with a fixed number of {@code true} values.
+ * The method uses a simplified version of the Fisher-Yates shuffle algorithm.
+ * By processing first the {@code true} values followed by the remaining {@code false} values
+ * less random numbers need to be generated. The method is optimized for the case
+ * that the number of {@code true} values is larger than or equal to the number of
+ * {@code false} values.
+ *
+ * @param b boolean array
+ * @param numberOfTrueValues number of {@code true} values the boolean array should finally have
+ * @param rng random data generator
+ */
+ static void fillBooleanArrayRandomlyWithFixedNumberTrueValues(final boolean[] b, final int numberOfTrueValues, final RandomGenerator rng) {
+ Arrays.fill(b, true);
+ for (int k = numberOfTrueValues; k < b.length; k++) {
+ final int r = rng.nextInt(k + 1);
+ b[(b[r]) ? r : k] = false;
+ }
+ }
+
+ /**
+ * Uses Monte Carlo simulation to approximate \(P(D_{n,m} > d)\) where \(D_{n,m}\) is the
+ * 2-sample Kolmogorov-Smirnov statistic. See
+ * {@link #kolmogorovSmirnovStatistic(double[], double[])} for the definition of \(D_{n,m}\).
+ * <p>
+ * The simulation generates {@code iterations} random partitions of {@code m + n} into an
+ * {@code n} set and an {@code m} set, computing \(D_{n,m}\) for each partition and returning
+ * the proportion of values that are greater than {@code d}, or greater than or equal to
+ * {@code d} if {@code strict} is {@code false}.
+ * </p>
+ *
+ * @param d D-statistic value
+ * @param n first sample size
+ * @param m second sample size
+ * @param iterations number of random partitions to generate
+ * @param strict whether or not the probability to compute is expressed as a strict inequality
+ * @return proportion of randomly generated m-n partitions of m + n that result in \(D_{n,m}\)
+ * greater than (resp. greater than or equal to) {@code d}
+ */
+ public double monteCarloP(final double d, final int n, final int m, final boolean strict,
+ final int iterations) {
+ return integralMonteCarloP(calculateIntegralD(d, n, m, strict), n, m, iterations);
+ }
+
+ /**
+ * Uses Monte Carlo simulation to approximate \(P(D_{n,m} >= d/(n*m))\) where \(D_{n,m}\) is the
+ * 2-sample Kolmogorov-Smirnov statistic.
+ * <p>
+ * Here d is the D-statistic represented as long value.
+ * The real D-statistic is obtained by dividing d by n*m.
+ * See also {@link #monteCarloP(double, int, int, boolean, int)}.
+ *
+ * @param d integral D-statistic
+ * @param n first sample size
+ * @param m second sample size
+ * @param iterations number of random partitions to generate
+ * @return proportion of randomly generated m-n partitions of m + n that result in \(D_{n,m}\)
+ * greater than or equal to {@code d/(n*m))}
+ */
+ private double integralMonteCarloP(final long d, final int n, final int m, final int iterations) {
+
+ // ensure that nn is always the max of (n, m) to require fewer random numbers
+ final int nn = FastMath.max(n, m);
+ final int mm = FastMath.min(n, m);
+ final int sum = nn + mm;
+
+ int tail = 0;
+ final boolean b[] = new boolean[sum];
+ for (int i = 0; i < iterations; i++) {
+ fillBooleanArrayRandomlyWithFixedNumberTrueValues(b, nn, rng);
+ long curD = 0l;
+ for(int j = 0; j < b.length; ++j) {
+ if (b[j]) {
+ curD += mm;
+ if (curD >= d) {
+ tail++;
+ break;
+ }
+ } else {
+ curD -= nn;
+ if (curD <= -d) {
+ tail++;
+ break;
+ }
+ }
+ }
+ }
+ return (double) tail / iterations;
+ }
+
+ /**
+ * If there are no ties in the combined dataset formed from x and y, this
+ * method is a no-op. If there are ties, a uniform random deviate in
+ * (-minDelta / 2, minDelta / 2) - {0} is added to each value in x and y, where
+ * minDelta is the minimum difference between unequal values in the combined
+ * sample. A fixed seed is used to generate the jitter, so repeated activations
+ * with the same input arrays result in the same values.
+ *
+ * NOTE: if there are ties in the data, this method overwrites the data in
+ * x and y with the jittered values.
+ *
+ * @param x first sample
+ * @param y second sample
+ */
+ private static void fixTies(double[] x, double[] y) {
+ final double[] values = MathArrays.unique(MathArrays.concatenate(x,y));
+ if (values.length == x.length + y.length) {
+ return; // There are no ties
+ }
+
+ // Find the smallest difference between values, or 1 if all values are the same
+ double minDelta = 1;
+ double prev = values[0];
+ double delta = 1;
+ for (int i = 1; i < values.length; i++) {
+ delta = prev - values[i];
+ if (delta < minDelta) {
+ minDelta = delta;
+ }
+ prev = values[i];
+ }
+ minDelta /= 2;
+
+ // Add jitter using a fixed seed (so same arguments always give same results),
+ // low-initialization-overhead generator
+ final RealDistribution dist =
+ new UniformRealDistribution(new JDKRandomGenerator(100), -minDelta, minDelta);
+
+ // It is theoretically possible that jitter does not break ties, so repeat
+ // until all ties are gone. Bound the loop and throw MIE if bound is exceeded.
+ int ct = 0;
+ boolean ties = true;
+ do {
+ jitter(x, dist);
+ jitter(y, dist);
+ ties = hasTies(x, y);
+ ct++;
+ } while (ties && ct < 1000);
+ if (ties) {
+ throw new MathInternalError(); // Should never happen
+ }
+ }
+
+ /**
+ * Returns true iff there are ties in the combined sample
+ * formed from x and y.
+ *
+ * @param x first sample
+ * @param y second sample
+ * @return true if x and y together contain ties
+ */
+ private static boolean hasTies(double[] x, double[] y) {
+ final HashSet<Double> values = new HashSet<Double>();
+ for (int i = 0; i < x.length; i++) {
+ if (!values.add(x[i])) {
+ return true;
+ }
+ }
+ for (int i = 0; i < y.length; i++) {
+ if (!values.add(y[i])) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Adds random jitter to {@code data} using deviates sampled from {@code dist}.
+ * <p>
+ * Note that jitter is applied in-place - i.e., the array
+ * values are overwritten with the result of applying jitter.</p>
+ *
+ * @param data input/output data array - entries overwritten by the method
+ * @param dist probability distribution to sample for jitter values
+ * @throws NullPointerException if either of the parameters is null
+ */
+ private static void jitter(double[] data, RealDistribution dist) {
+ for (int i = 0; i < data.length; i++) {
+ data[i] += dist.sample();
+ }
+ }
+
+ /**
+ * The function C(i, j) defined in [4] (class javadoc), formula (5.5).
+ * defined to return 1 if |i/n - j/m| <= c; 0 otherwise. Here c is scaled up
+ * and recoded as a long to avoid rounding errors in comparison tests, so what
+ * is actually tested is |im - jn| <= cmn.
+ *
+ * @param i first path parameter
+ * @param j second path paramter
+ * @param m first sample size
+ * @param n second sample size
+ * @param cmn integral D-statistic (see {@link #calculateIntegralD(double, int, int, boolean)})
+ * @param strict whether or not the null hypothesis uses strict inequality
+ * @return C(i,j) for given m, n, c
+ */
+ private static int c(int i, int j, int m, int n, long cmn, boolean strict) {
+ if (strict) {
+ return FastMath.abs(i*(long)n - j*(long)m) <= cmn ? 1 : 0;
+ }
+ return FastMath.abs(i*(long)n - j*(long)m) < cmn ? 1 : 0;
+ }
+
+ /**
+ * The function N(i, j) defined in [4] (class javadoc).
+ * Returns the number of paths over the lattice {(i,j) : 0 <= i <= n, 0 <= j <= m}
+ * from (0,0) to (i,j) satisfying C(h,k, m, n, c) = 1 for each (h,k) on the path.
+ * The return value is integral, but subject to overflow, so it is maintained and
+ * returned as a double.
+ *
+ * @param i first path parameter
+ * @param j second path parameter
+ * @param m first sample size
+ * @param n second sample size
+ * @param cnm integral D-statistic (see {@link #calculateIntegralD(double, int, int, boolean)})
+ * @param strict whether or not the null hypothesis uses strict inequality
+ * @return number or paths to (i, j) from (0,0) representing D-values as large as c for given m, n
+ */
+ private static double n(int i, int j, int m, int n, long cnm, boolean strict) {
+ /*
+ * Unwind the recursive definition given in [4].
+ * Compute n(1,1), n(1,2)...n(2,1), n(2,2)... up to n(i,j), one row at a time.
+ * When n(i,*) are being computed, lag[] holds the values of n(i - 1, *).
+ */
+ final double[] lag = new double[n];
+ double last = 0;
+ for (int k = 0; k < n; k++) {
+ lag[k] = c(0, k + 1, m, n, cnm, strict);
+ }
+ for (int k = 1; k <= i; k++) {
+ last = c(k, 0, m, n, cnm, strict);
+ for (int l = 1; l <= j; l++) {
+ lag[l - 1] = c(k, l, m, n, cnm, strict) * (last + lag[l - 1]);
+ last = lag[l - 1];
+ }
+ }
+ return last;
+ }
+}
diff --git a/src/main/java/org/apache/commons/math3/stat/inference/MannWhitneyUTest.java b/src/main/java/org/apache/commons/math3/stat/inference/MannWhitneyUTest.java
new file mode 100644
index 0000000..82fddb3
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/stat/inference/MannWhitneyUTest.java
@@ -0,0 +1,238 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math3.stat.inference;
+
+import org.apache.commons.math3.distribution.NormalDistribution;
+import org.apache.commons.math3.exception.ConvergenceException;
+import org.apache.commons.math3.exception.MaxCountExceededException;
+import org.apache.commons.math3.exception.NoDataException;
+import org.apache.commons.math3.exception.NullArgumentException;
+import org.apache.commons.math3.stat.ranking.NaNStrategy;
+import org.apache.commons.math3.stat.ranking.NaturalRanking;
+import org.apache.commons.math3.stat.ranking.TiesStrategy;
+import org.apache.commons.math3.util.FastMath;
+
+/**
+ * An implementation of the Mann-Whitney U test (also called Wilcoxon rank-sum test).
+ *
+ */
+public class MannWhitneyUTest {
+
+ /** Ranking algorithm. */
+ private NaturalRanking naturalRanking;
+
+ /**
+ * Create a test instance using where NaN's are left in place and ties get
+ * the average of applicable ranks. Use this unless you are very sure of
+ * what you are doing.
+ */
+ public MannWhitneyUTest() {
+ naturalRanking = new NaturalRanking(NaNStrategy.FIXED,
+ TiesStrategy.AVERAGE);
+ }
+
+ /**
+ * Create a test instance using the given strategies for NaN's and ties.
+ * Only use this if you are sure of what you are doing.
+ *
+ * @param nanStrategy
+ * specifies the strategy that should be used for Double.NaN's
+ * @param tiesStrategy
+ * specifies the strategy that should be used for ties
+ */
+ public MannWhitneyUTest(final NaNStrategy nanStrategy,
+ final TiesStrategy tiesStrategy) {
+ naturalRanking = new NaturalRanking(nanStrategy, tiesStrategy);
+ }
+
+ /**
+ * Ensures that the provided arrays fulfills the assumptions.
+ *
+ * @param x first sample
+ * @param y second sample
+ * @throws NullArgumentException if {@code x} or {@code y} are {@code null}.
+ * @throws NoDataException if {@code x} or {@code y} are zero-length.
+ */
+ private void ensureDataConformance(final double[] x, final double[] y)
+ throws NullArgumentException, NoDataException {
+
+ if (x == null ||
+ y == null) {
+ throw new NullArgumentException();
+ }
+ if (x.length == 0 ||
+ y.length == 0) {
+ throw new NoDataException();
+ }
+ }
+
+ /** Concatenate the samples into one array.
+ * @param x first sample
+ * @param y second sample
+ * @return concatenated array
+ */
+ private double[] concatenateSamples(final double[] x, final double[] y) {
+ final double[] z = new double[x.length + y.length];
+
+ System.arraycopy(x, 0, z, 0, x.length);
+ System.arraycopy(y, 0, z, x.length, y.length);
+
+ return z;
+ }
+
+ /**
+ * Computes the <a
+ * href="http://en.wikipedia.org/wiki/Mann%E2%80%93Whitney_U"> Mann-Whitney
+ * U statistic</a> comparing mean for two independent samples possibly of
+ * different length.
+ * <p>
+ * This statistic can be used to perform a Mann-Whitney U test evaluating
+ * the null hypothesis that the two independent samples has equal mean.
+ * </p>
+ * <p>
+ * Let X<sub>i</sub> denote the i'th individual of the first sample and
+ * Y<sub>j</sub> the j'th individual in the second sample. Note that the
+ * samples would often have different length.
+ * </p>
+ * <p>
+ * <strong>Preconditions</strong>:
+ * <ul>
+ * <li>All observations in the two samples are independent.</li>
+ * <li>The observations are at least ordinal (continuous are also ordinal).</li>
+ * </ul>
+ * </p>
+ *
+ * @param x the first sample
+ * @param y the second sample
+ * @return Mann-Whitney U statistic (maximum of U<sup>x</sup> and U<sup>y</sup>)
+ * @throws NullArgumentException if {@code x} or {@code y} are {@code null}.
+ * @throws NoDataException if {@code x} or {@code y} are zero-length.
+ */
+ public double mannWhitneyU(final double[] x, final double[] y)
+ throws NullArgumentException, NoDataException {
+
+ ensureDataConformance(x, y);
+
+ final double[] z = concatenateSamples(x, y);
+ final double[] ranks = naturalRanking.rank(z);
+
+ double sumRankX = 0;
+
+ /*
+ * The ranks for x is in the first x.length entries in ranks because x
+ * is in the first x.length entries in z
+ */
+ for (int i = 0; i < x.length; ++i) {
+ sumRankX += ranks[i];
+ }
+
+ /*
+ * U1 = R1 - (n1 * (n1 + 1)) / 2 where R1 is sum of ranks for sample 1,
+ * e.g. x, n1 is the number of observations in sample 1.
+ */
+ final double U1 = sumRankX - ((long) x.length * (x.length + 1)) / 2;
+
+ /*
+ * It can be shown that U1 + U2 = n1 * n2
+ */
+ final double U2 = (long) x.length * y.length - U1;
+
+ return FastMath.max(U1, U2);
+ }
+
+ /**
+ * @param Umin smallest Mann-Whitney U value
+ * @param n1 number of subjects in first sample
+ * @param n2 number of subjects in second sample
+ * @return two-sided asymptotic p-value
+ * @throws ConvergenceException if the p-value can not be computed
+ * due to a convergence error
+ * @throws MaxCountExceededException if the maximum number of
+ * iterations is exceeded
+ */
+ private double calculateAsymptoticPValue(final double Umin,
+ final int n1,
+ final int n2)
+ throws ConvergenceException, MaxCountExceededException {
+
+ /* long multiplication to avoid overflow (double not used due to efficiency
+ * and to avoid precision loss)
+ */
+ final long n1n2prod = (long) n1 * n2;
+
+ // http://en.wikipedia.org/wiki/Mann%E2%80%93Whitney_U#Normal_approximation
+ final double EU = n1n2prod / 2.0;
+ final double VarU = n1n2prod * (n1 + n2 + 1) / 12.0;
+
+ final double z = (Umin - EU) / FastMath.sqrt(VarU);
+
+ // No try-catch or advertised exception because args are valid
+ // pass a null rng to avoid unneeded overhead as we will not sample from this distribution
+ final NormalDistribution standardNormal = new NormalDistribution(null, 0, 1);
+
+ return 2 * standardNormal.cumulativeProbability(z);
+ }
+
+ /**
+ * Returns the asymptotic <i>observed significance level</i>, or <a href=
+ * "http://www.cas.lancs.ac.uk/glossary_v1.1/hyptest.html#pvalue">
+ * p-value</a>, associated with a <a
+ * href="http://en.wikipedia.org/wiki/Mann%E2%80%93Whitney_U"> Mann-Whitney
+ * U statistic</a> comparing mean for two independent samples.
+ * <p>
+ * Let X<sub>i</sub> denote the i'th individual of the first sample and
+ * Y<sub>j</sub> the j'th individual in the second sample. Note that the
+ * samples would often have different length.
+ * </p>
+ * <p>
+ * <strong>Preconditions</strong>:
+ * <ul>
+ * <li>All observations in the two samples are independent.</li>
+ * <li>The observations are at least ordinal (continuous are also ordinal).</li>
+ * </ul>
+ * </p><p>
+ * Ties give rise to biased variance at the moment. See e.g. <a
+ * href="http://mlsc.lboro.ac.uk/resources/statistics/Mannwhitney.pdf"
+ * >http://mlsc.lboro.ac.uk/resources/statistics/Mannwhitney.pdf</a>.</p>
+ *
+ * @param x the first sample
+ * @param y the second sample
+ * @return asymptotic p-value
+ * @throws NullArgumentException if {@code x} or {@code y} are {@code null}.
+ * @throws NoDataException if {@code x} or {@code y} are zero-length.
+ * @throws ConvergenceException if the p-value can not be computed due to a
+ * convergence error
+ * @throws MaxCountExceededException if the maximum number of iterations
+ * is exceeded
+ */
+ public double mannWhitneyUTest(final double[] x, final double[] y)
+ throws NullArgumentException, NoDataException,
+ ConvergenceException, MaxCountExceededException {
+
+ ensureDataConformance(x, y);
+
+ final double Umax = mannWhitneyU(x, y);
+
+ /*
+ * It can be shown that U1 + U2 = n1 * n2
+ */
+ final double Umin = (long) x.length * y.length - Umax;
+
+ return calculateAsymptoticPValue(Umin, x.length, y.length);
+ }
+
+}
diff --git a/src/main/java/org/apache/commons/math3/stat/inference/OneWayAnova.java b/src/main/java/org/apache/commons/math3/stat/inference/OneWayAnova.java
new file mode 100644
index 0000000..d0c5fc1
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/stat/inference/OneWayAnova.java
@@ -0,0 +1,355 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math3.stat.inference;
+
+import java.util.ArrayList;
+import java.util.Collection;
+
+import org.apache.commons.math3.distribution.FDistribution;
+import org.apache.commons.math3.exception.ConvergenceException;
+import org.apache.commons.math3.exception.DimensionMismatchException;
+import org.apache.commons.math3.exception.MaxCountExceededException;
+import org.apache.commons.math3.exception.NullArgumentException;
+import org.apache.commons.math3.exception.OutOfRangeException;
+import org.apache.commons.math3.exception.util.LocalizedFormats;
+import org.apache.commons.math3.stat.descriptive.SummaryStatistics;
+import org.apache.commons.math3.util.MathUtils;
+
+/**
+ * Implements one-way ANOVA (analysis of variance) statistics.
+ *
+ * <p> Tests for differences between two or more categories of univariate data
+ * (for example, the body mass index of accountants, lawyers, doctors and
+ * computer programmers). When two categories are given, this is equivalent to
+ * the {@link org.apache.commons.math3.stat.inference.TTest}.
+ * </p><p>
+ * Uses the {@link org.apache.commons.math3.distribution.FDistribution
+ * commons-math F Distribution implementation} to estimate exact p-values.</p>
+ * <p>This implementation is based on a description at
+ * http://faculty.vassar.edu/lowry/ch13pt1.html</p>
+ * <pre>
+ * Abbreviations: bg = between groups,
+ * wg = within groups,
+ * ss = sum squared deviations
+ * </pre>
+ *
+ * @since 1.2
+ */
+public class OneWayAnova {
+
+ /**
+ * Default constructor.
+ */
+ public OneWayAnova() {
+ }
+
+ /**
+ * Computes the ANOVA F-value for a collection of <code>double[]</code>
+ * arrays.
+ *
+ * <p><strong>Preconditions</strong>: <ul>
+ * <li>The categoryData <code>Collection</code> must contain
+ * <code>double[]</code> arrays.</li>
+ * <li> There must be at least two <code>double[]</code> arrays in the
+ * <code>categoryData</code> collection and each of these arrays must
+ * contain at least two values.</li></ul></p><p>
+ * This implementation computes the F statistic using the definitional
+ * formula<pre>
+ * F = msbg/mswg</pre>
+ * where<pre>
+ * msbg = between group mean square
+ * mswg = within group mean square</pre>
+ * are as defined <a href="http://faculty.vassar.edu/lowry/ch13pt1.html">
+ * here</a></p>
+ *
+ * @param categoryData <code>Collection</code> of <code>double[]</code>
+ * arrays each containing data for one category
+ * @return Fvalue
+ * @throws NullArgumentException if <code>categoryData</code> is <code>null</code>
+ * @throws DimensionMismatchException if the length of the <code>categoryData</code>
+ * array is less than 2 or a contained <code>double[]</code> array does not have
+ * at least two values
+ */
+ public double anovaFValue(final Collection<double[]> categoryData)
+ throws NullArgumentException, DimensionMismatchException {
+
+ AnovaStats a = anovaStats(categoryData);
+ return a.F;
+
+ }
+
+ /**
+ * Computes the ANOVA P-value for a collection of <code>double[]</code>
+ * arrays.
+ *
+ * <p><strong>Preconditions</strong>: <ul>
+ * <li>The categoryData <code>Collection</code> must contain
+ * <code>double[]</code> arrays.</li>
+ * <li> There must be at least two <code>double[]</code> arrays in the
+ * <code>categoryData</code> collection and each of these arrays must
+ * contain at least two values.</li></ul></p><p>
+ * This implementation uses the
+ * {@link org.apache.commons.math3.distribution.FDistribution
+ * commons-math F Distribution implementation} to estimate the exact
+ * p-value, using the formula<pre>
+ * p = 1 - cumulativeProbability(F)</pre>
+ * where <code>F</code> is the F value and <code>cumulativeProbability</code>
+ * is the commons-math implementation of the F distribution.</p>
+ *
+ * @param categoryData <code>Collection</code> of <code>double[]</code>
+ * arrays each containing data for one category
+ * @return Pvalue
+ * @throws NullArgumentException if <code>categoryData</code> is <code>null</code>
+ * @throws DimensionMismatchException if the length of the <code>categoryData</code>
+ * array is less than 2 or a contained <code>double[]</code> array does not have
+ * at least two values
+ * @throws ConvergenceException if the p-value can not be computed due to a convergence error
+ * @throws MaxCountExceededException if the maximum number of iterations is exceeded
+ */
+ public double anovaPValue(final Collection<double[]> categoryData)
+ throws NullArgumentException, DimensionMismatchException,
+ ConvergenceException, MaxCountExceededException {
+
+ final AnovaStats a = anovaStats(categoryData);
+ // No try-catch or advertised exception because args are valid
+ // pass a null rng to avoid unneeded overhead as we will not sample from this distribution
+ final FDistribution fdist = new FDistribution(null, a.dfbg, a.dfwg);
+ return 1.0 - fdist.cumulativeProbability(a.F);
+
+ }
+
+ /**
+ * Computes the ANOVA P-value for a collection of {@link SummaryStatistics}.
+ *
+ * <p><strong>Preconditions</strong>: <ul>
+ * <li>The categoryData <code>Collection</code> must contain
+ * {@link SummaryStatistics}.</li>
+ * <li> There must be at least two {@link SummaryStatistics} in the
+ * <code>categoryData</code> collection and each of these statistics must
+ * contain at least two values.</li></ul></p><p>
+ * This implementation uses the
+ * {@link org.apache.commons.math3.distribution.FDistribution
+ * commons-math F Distribution implementation} to estimate the exact
+ * p-value, using the formula<pre>
+ * p = 1 - cumulativeProbability(F)</pre>
+ * where <code>F</code> is the F value and <code>cumulativeProbability</code>
+ * is the commons-math implementation of the F distribution.</p>
+ *
+ * @param categoryData <code>Collection</code> of {@link SummaryStatistics}
+ * each containing data for one category
+ * @param allowOneElementData if true, allow computation for one catagory
+ * only or for one data element per category
+ * @return Pvalue
+ * @throws NullArgumentException if <code>categoryData</code> is <code>null</code>
+ * @throws DimensionMismatchException if the length of the <code>categoryData</code>
+ * array is less than 2 or a contained {@link SummaryStatistics} does not have
+ * at least two values
+ * @throws ConvergenceException if the p-value can not be computed due to a convergence error
+ * @throws MaxCountExceededException if the maximum number of iterations is exceeded
+ * @since 3.2
+ */
+ public double anovaPValue(final Collection<SummaryStatistics> categoryData,
+ final boolean allowOneElementData)
+ throws NullArgumentException, DimensionMismatchException,
+ ConvergenceException, MaxCountExceededException {
+
+ final AnovaStats a = anovaStats(categoryData, allowOneElementData);
+ // pass a null rng to avoid unneeded overhead as we will not sample from this distribution
+ final FDistribution fdist = new FDistribution(null, a.dfbg, a.dfwg);
+ return 1.0 - fdist.cumulativeProbability(a.F);
+
+ }
+
+ /**
+ * This method calls the method that actually does the calculations (except
+ * P-value).
+ *
+ * @param categoryData
+ * <code>Collection</code> of <code>double[]</code> arrays each
+ * containing data for one category
+ * @return computed AnovaStats
+ * @throws NullArgumentException
+ * if <code>categoryData</code> is <code>null</code>
+ * @throws DimensionMismatchException
+ * if the length of the <code>categoryData</code> array is less
+ * than 2 or a contained <code>double[]</code> array does not
+ * contain at least two values
+ */
+ private AnovaStats anovaStats(final Collection<double[]> categoryData)
+ throws NullArgumentException, DimensionMismatchException {
+
+ MathUtils.checkNotNull(categoryData);
+
+ final Collection<SummaryStatistics> categoryDataSummaryStatistics =
+ new ArrayList<SummaryStatistics>(categoryData.size());
+
+ // convert arrays to SummaryStatistics
+ for (final double[] data : categoryData) {
+ final SummaryStatistics dataSummaryStatistics = new SummaryStatistics();
+ categoryDataSummaryStatistics.add(dataSummaryStatistics);
+ for (final double val : data) {
+ dataSummaryStatistics.addValue(val);
+ }
+ }
+
+ return anovaStats(categoryDataSummaryStatistics, false);
+
+ }
+
+ /**
+ * Performs an ANOVA test, evaluating the null hypothesis that there
+ * is no difference among the means of the data categories.
+ *
+ * <p><strong>Preconditions</strong>: <ul>
+ * <li>The categoryData <code>Collection</code> must contain
+ * <code>double[]</code> arrays.</li>
+ * <li> There must be at least two <code>double[]</code> arrays in the
+ * <code>categoryData</code> collection and each of these arrays must
+ * contain at least two values.</li>
+ * <li>alpha must be strictly greater than 0 and less than or equal to 0.5.
+ * </li></ul></p><p>
+ * This implementation uses the
+ * {@link org.apache.commons.math3.distribution.FDistribution
+ * commons-math F Distribution implementation} to estimate the exact
+ * p-value, using the formula<pre>
+ * p = 1 - cumulativeProbability(F)</pre>
+ * where <code>F</code> is the F value and <code>cumulativeProbability</code>
+ * is the commons-math implementation of the F distribution.</p>
+ * <p>True is returned iff the estimated p-value is less than alpha.</p>
+ *
+ * @param categoryData <code>Collection</code> of <code>double[]</code>
+ * arrays each containing data for one category
+ * @param alpha significance level of the test
+ * @return true if the null hypothesis can be rejected with
+ * confidence 1 - alpha
+ * @throws NullArgumentException if <code>categoryData</code> is <code>null</code>
+ * @throws DimensionMismatchException if the length of the <code>categoryData</code>
+ * array is less than 2 or a contained <code>double[]</code> array does not have
+ * at least two values
+ * @throws OutOfRangeException if <code>alpha</code> is not in the range (0, 0.5]
+ * @throws ConvergenceException if the p-value can not be computed due to a convergence error
+ * @throws MaxCountExceededException if the maximum number of iterations is exceeded
+ */
+ public boolean anovaTest(final Collection<double[]> categoryData,
+ final double alpha)
+ throws NullArgumentException, DimensionMismatchException,
+ OutOfRangeException, ConvergenceException, MaxCountExceededException {
+
+ if ((alpha <= 0) || (alpha > 0.5)) {
+ throw new OutOfRangeException(
+ LocalizedFormats.OUT_OF_BOUND_SIGNIFICANCE_LEVEL,
+ alpha, 0, 0.5);
+ }
+ return anovaPValue(categoryData) < alpha;
+
+ }
+
+ /**
+ * This method actually does the calculations (except P-value).
+ *
+ * @param categoryData <code>Collection</code> of <code>double[]</code>
+ * arrays each containing data for one category
+ * @param allowOneElementData if true, allow computation for one catagory
+ * only or for one data element per category
+ * @return computed AnovaStats
+ * @throws NullArgumentException if <code>categoryData</code> is <code>null</code>
+ * @throws DimensionMismatchException if <code>allowOneElementData</code> is false and the number of
+ * categories is less than 2 or a contained SummaryStatistics does not contain
+ * at least two values
+ */
+ private AnovaStats anovaStats(final Collection<SummaryStatistics> categoryData,
+ final boolean allowOneElementData)
+ throws NullArgumentException, DimensionMismatchException {
+
+ MathUtils.checkNotNull(categoryData);
+
+ if (!allowOneElementData) {
+ // check if we have enough categories
+ if (categoryData.size() < 2) {
+ throw new DimensionMismatchException(LocalizedFormats.TWO_OR_MORE_CATEGORIES_REQUIRED,
+ categoryData.size(), 2);
+ }
+
+ // check if each category has enough data
+ for (final SummaryStatistics array : categoryData) {
+ if (array.getN() <= 1) {
+ throw new DimensionMismatchException(LocalizedFormats.TWO_OR_MORE_VALUES_IN_CATEGORY_REQUIRED,
+ (int) array.getN(), 2);
+ }
+ }
+ }
+
+ int dfwg = 0;
+ double sswg = 0;
+ double totsum = 0;
+ double totsumsq = 0;
+ int totnum = 0;
+
+ for (final SummaryStatistics data : categoryData) {
+
+ final double sum = data.getSum();
+ final double sumsq = data.getSumsq();
+ final int num = (int) data.getN();
+ totnum += num;
+ totsum += sum;
+ totsumsq += sumsq;
+
+ dfwg += num - 1;
+ final double ss = sumsq - ((sum * sum) / num);
+ sswg += ss;
+ }
+
+ final double sst = totsumsq - ((totsum * totsum) / totnum);
+ final double ssbg = sst - sswg;
+ final int dfbg = categoryData.size() - 1;
+ final double msbg = ssbg / dfbg;
+ final double mswg = sswg / dfwg;
+ final double F = msbg / mswg;
+
+ return new AnovaStats(dfbg, dfwg, F);
+
+ }
+
+ /**
+ Convenience class to pass dfbg,dfwg,F values around within OneWayAnova.
+ No get/set methods provided.
+ */
+ private static class AnovaStats {
+
+ /** Degrees of freedom in numerator (between groups). */
+ private final int dfbg;
+
+ /** Degrees of freedom in denominator (within groups). */
+ private final int dfwg;
+
+ /** Statistic. */
+ private final double F;
+
+ /**
+ * Constructor
+ * @param dfbg degrees of freedom in numerator (between groups)
+ * @param dfwg degrees of freedom in denominator (within groups)
+ * @param F statistic
+ */
+ private AnovaStats(int dfbg, int dfwg, double F) {
+ this.dfbg = dfbg;
+ this.dfwg = dfwg;
+ this.F = F;
+ }
+ }
+
+}
diff --git a/src/main/java/org/apache/commons/math3/stat/inference/TTest.java b/src/main/java/org/apache/commons/math3/stat/inference/TTest.java
new file mode 100644
index 0000000..b0f76f6
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/stat/inference/TTest.java
@@ -0,0 +1,1184 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math3.stat.inference;
+
+import org.apache.commons.math3.distribution.TDistribution;
+import org.apache.commons.math3.exception.DimensionMismatchException;
+import org.apache.commons.math3.exception.MathIllegalArgumentException;
+import org.apache.commons.math3.exception.MaxCountExceededException;
+import org.apache.commons.math3.exception.NoDataException;
+import org.apache.commons.math3.exception.NotStrictlyPositiveException;
+import org.apache.commons.math3.exception.NullArgumentException;
+import org.apache.commons.math3.exception.NumberIsTooSmallException;
+import org.apache.commons.math3.exception.OutOfRangeException;
+import org.apache.commons.math3.exception.util.LocalizedFormats;
+import org.apache.commons.math3.stat.StatUtils;
+import org.apache.commons.math3.stat.descriptive.StatisticalSummary;
+import org.apache.commons.math3.util.FastMath;
+
+/**
+ * An implementation for Student's t-tests.
+ * <p>
+ * Tests can be:<ul>
+ * <li>One-sample or two-sample</li>
+ * <li>One-sided or two-sided</li>
+ * <li>Paired or unpaired (for two-sample tests)</li>
+ * <li>Homoscedastic (equal variance assumption) or heteroscedastic
+ * (for two sample tests)</li>
+ * <li>Fixed significance level (boolean-valued) or returning p-values.
+ * </li></ul></p>
+ * <p>
+ * Test statistics are available for all tests. Methods including "Test" in
+ * in their names perform tests, all other methods return t-statistics. Among
+ * the "Test" methods, <code>double-</code>valued methods return p-values;
+ * <code>boolean-</code>valued methods perform fixed significance level tests.
+ * Significance levels are always specified as numbers between 0 and 0.5
+ * (e.g. tests at the 95% level use <code>alpha=0.05</code>).</p>
+ * <p>
+ * Input to tests can be either <code>double[]</code> arrays or
+ * {@link StatisticalSummary} instances.</p><p>
+ * Uses commons-math {@link org.apache.commons.math3.distribution.TDistribution}
+ * implementation to estimate exact p-values.</p>
+ *
+ */
+public class TTest {
+ /**
+ * Computes a paired, 2-sample t-statistic based on the data in the input
+ * arrays. The t-statistic returned is equivalent to what would be returned by
+ * computing the one-sample t-statistic {@link #t(double, double[])}, with
+ * <code>mu = 0</code> and the sample array consisting of the (signed)
+ * differences between corresponding entries in <code>sample1</code> and
+ * <code>sample2.</code>
+ * <p>
+ * <strong>Preconditions</strong>: <ul>
+ * <li>The input arrays must have the same length and their common length
+ * must be at least 2.
+ * </li></ul></p>
+ *
+ * @param sample1 array of sample data values
+ * @param sample2 array of sample data values
+ * @return t statistic
+ * @throws NullArgumentException if the arrays are <code>null</code>
+ * @throws NoDataException if the arrays are empty
+ * @throws DimensionMismatchException if the length of the arrays is not equal
+ * @throws NumberIsTooSmallException if the length of the arrays is &lt; 2
+ */
+ public double pairedT(final double[] sample1, final double[] sample2)
+ throws NullArgumentException, NoDataException,
+ DimensionMismatchException, NumberIsTooSmallException {
+
+ checkSampleData(sample1);
+ checkSampleData(sample2);
+ double meanDifference = StatUtils.meanDifference(sample1, sample2);
+ return t(meanDifference, 0,
+ StatUtils.varianceDifference(sample1, sample2, meanDifference),
+ sample1.length);
+
+ }
+
+ /**
+ * Returns the <i>observed significance level</i>, or
+ * <i> p-value</i>, associated with a paired, two-sample, two-tailed t-test
+ * based on the data in the input arrays.
+ * <p>
+ * The number returned is the smallest significance level
+ * at which one can reject the null hypothesis that the mean of the paired
+ * differences is 0 in favor of the two-sided alternative that the mean paired
+ * difference is not equal to 0. For a one-sided test, divide the returned
+ * value by 2.</p>
+ * <p>
+ * This test is equivalent to a one-sample t-test computed using
+ * {@link #tTest(double, double[])} with <code>mu = 0</code> and the sample
+ * array consisting of the signed differences between corresponding elements of
+ * <code>sample1</code> and <code>sample2.</code></p>
+ * <p>
+ * <strong>Usage Note:</strong><br>
+ * The validity of the p-value depends on the assumptions of the parametric
+ * t-test procedure, as discussed
+ * <a href="http://www.basic.nwu.edu/statguidefiles/ttest_unpaired_ass_viol.html">
+ * here</a></p>
+ * <p>
+ * <strong>Preconditions</strong>: <ul>
+ * <li>The input array lengths must be the same and their common length must
+ * be at least 2.
+ * </li></ul></p>
+ *
+ * @param sample1 array of sample data values
+ * @param sample2 array of sample data values
+ * @return p-value for t-test
+ * @throws NullArgumentException if the arrays are <code>null</code>
+ * @throws NoDataException if the arrays are empty
+ * @throws DimensionMismatchException if the length of the arrays is not equal
+ * @throws NumberIsTooSmallException if the length of the arrays is &lt; 2
+ * @throws MaxCountExceededException if an error occurs computing the p-value
+ */
+ public double pairedTTest(final double[] sample1, final double[] sample2)
+ throws NullArgumentException, NoDataException, DimensionMismatchException,
+ NumberIsTooSmallException, MaxCountExceededException {
+
+ double meanDifference = StatUtils.meanDifference(sample1, sample2);
+ return tTest(meanDifference, 0,
+ StatUtils.varianceDifference(sample1, sample2, meanDifference),
+ sample1.length);
+
+ }
+
+ /**
+ * Performs a paired t-test evaluating the null hypothesis that the
+ * mean of the paired differences between <code>sample1</code> and
+ * <code>sample2</code> is 0 in favor of the two-sided alternative that the
+ * mean paired difference is not equal to 0, with significance level
+ * <code>alpha</code>.
+ * <p>
+ * Returns <code>true</code> iff the null hypothesis can be rejected with
+ * confidence <code>1 - alpha</code>. To perform a 1-sided test, use
+ * <code>alpha * 2</code></p>
+ * <p>
+ * <strong>Usage Note:</strong><br>
+ * The validity of the test depends on the assumptions of the parametric
+ * t-test procedure, as discussed
+ * <a href="http://www.basic.nwu.edu/statguidefiles/ttest_unpaired_ass_viol.html">
+ * here</a></p>
+ * <p>
+ * <strong>Preconditions</strong>: <ul>
+ * <li>The input array lengths must be the same and their common length
+ * must be at least 2.
+ * </li>
+ * <li> <code> 0 &lt; alpha &lt; 0.5 </code>
+ * </li></ul></p>
+ *
+ * @param sample1 array of sample data values
+ * @param sample2 array of sample data values
+ * @param alpha significance level of the test
+ * @return true if the null hypothesis can be rejected with
+ * confidence 1 - alpha
+ * @throws NullArgumentException if the arrays are <code>null</code>
+ * @throws NoDataException if the arrays are empty
+ * @throws DimensionMismatchException if the length of the arrays is not equal
+ * @throws NumberIsTooSmallException if the length of the arrays is &lt; 2
+ * @throws OutOfRangeException if <code>alpha</code> is not in the range (0, 0.5]
+ * @throws MaxCountExceededException if an error occurs computing the p-value
+ */
+ public boolean pairedTTest(final double[] sample1, final double[] sample2,
+ final double alpha)
+ throws NullArgumentException, NoDataException, DimensionMismatchException,
+ NumberIsTooSmallException, OutOfRangeException, MaxCountExceededException {
+
+ checkSignificanceLevel(alpha);
+ return pairedTTest(sample1, sample2) < alpha;
+
+ }
+
+ /**
+ * Computes a <a href="http://www.itl.nist.gov/div898/handbook/prc/section2/prc22.htm#formula">
+ * t statistic </a> given observed values and a comparison constant.
+ * <p>
+ * This statistic can be used to perform a one sample t-test for the mean.
+ * </p><p>
+ * <strong>Preconditions</strong>: <ul>
+ * <li>The observed array length must be at least 2.
+ * </li></ul></p>
+ *
+ * @param mu comparison constant
+ * @param observed array of values
+ * @return t statistic
+ * @throws NullArgumentException if <code>observed</code> is <code>null</code>
+ * @throws NumberIsTooSmallException if the length of <code>observed</code> is &lt; 2
+ */
+ public double t(final double mu, final double[] observed)
+ throws NullArgumentException, NumberIsTooSmallException {
+
+ checkSampleData(observed);
+ // No try-catch or advertised exception because args have just been checked
+ return t(StatUtils.mean(observed), mu, StatUtils.variance(observed),
+ observed.length);
+
+ }
+
+ /**
+ * Computes a <a href="http://www.itl.nist.gov/div898/handbook/prc/section2/prc22.htm#formula">
+ * t statistic </a> to use in comparing the mean of the dataset described by
+ * <code>sampleStats</code> to <code>mu</code>.
+ * <p>
+ * This statistic can be used to perform a one sample t-test for the mean.
+ * </p><p>
+ * <strong>Preconditions</strong>: <ul>
+ * <li><code>observed.getN() &ge; 2</code>.
+ * </li></ul></p>
+ *
+ * @param mu comparison constant
+ * @param sampleStats DescriptiveStatistics holding sample summary statitstics
+ * @return t statistic
+ * @throws NullArgumentException if <code>sampleStats</code> is <code>null</code>
+ * @throws NumberIsTooSmallException if the number of samples is &lt; 2
+ */
+ public double t(final double mu, final StatisticalSummary sampleStats)
+ throws NullArgumentException, NumberIsTooSmallException {
+
+ checkSampleData(sampleStats);
+ return t(sampleStats.getMean(), mu, sampleStats.getVariance(),
+ sampleStats.getN());
+
+ }
+
+ /**
+ * Computes a 2-sample t statistic, under the hypothesis of equal
+ * subpopulation variances. To compute a t-statistic without the
+ * equal variances hypothesis, use {@link #t(double[], double[])}.
+ * <p>
+ * This statistic can be used to perform a (homoscedastic) two-sample
+ * t-test to compare sample means.</p>
+ * <p>
+ * The t-statistic is</p>
+ * <p>
+ * &nbsp;&nbsp;<code> t = (m1 - m2) / (sqrt(1/n1 +1/n2) sqrt(var))</code>
+ * </p><p>
+ * where <strong><code>n1</code></strong> is the size of first sample;
+ * <strong><code> n2</code></strong> is the size of second sample;
+ * <strong><code> m1</code></strong> is the mean of first sample;
+ * <strong><code> m2</code></strong> is the mean of second sample</li>
+ * </ul>
+ * and <strong><code>var</code></strong> is the pooled variance estimate:
+ * </p><p>
+ * <code>var = sqrt(((n1 - 1)var1 + (n2 - 1)var2) / ((n1-1) + (n2-1)))</code>
+ * </p><p>
+ * with <strong><code>var1</code></strong> the variance of the first sample and
+ * <strong><code>var2</code></strong> the variance of the second sample.
+ * </p><p>
+ * <strong>Preconditions</strong>: <ul>
+ * <li>The observed array lengths must both be at least 2.
+ * </li></ul></p>
+ *
+ * @param sample1 array of sample data values
+ * @param sample2 array of sample data values
+ * @return t statistic
+ * @throws NullArgumentException if the arrays are <code>null</code>
+ * @throws NumberIsTooSmallException if the length of the arrays is &lt; 2
+ */
+ public double homoscedasticT(final double[] sample1, final double[] sample2)
+ throws NullArgumentException, NumberIsTooSmallException {
+
+ checkSampleData(sample1);
+ checkSampleData(sample2);
+ // No try-catch or advertised exception because args have just been checked
+ return homoscedasticT(StatUtils.mean(sample1), StatUtils.mean(sample2),
+ StatUtils.variance(sample1), StatUtils.variance(sample2),
+ sample1.length, sample2.length);
+
+ }
+
+ /**
+ * Computes a 2-sample t statistic, without the hypothesis of equal
+ * subpopulation variances. To compute a t-statistic assuming equal
+ * variances, use {@link #homoscedasticT(double[], double[])}.
+ * <p>
+ * This statistic can be used to perform a two-sample t-test to compare
+ * sample means.</p>
+ * <p>
+ * The t-statistic is</p>
+ * <p>
+ * &nbsp;&nbsp; <code> t = (m1 - m2) / sqrt(var1/n1 + var2/n2)</code>
+ * </p><p>
+ * where <strong><code>n1</code></strong> is the size of the first sample
+ * <strong><code> n2</code></strong> is the size of the second sample;
+ * <strong><code> m1</code></strong> is the mean of the first sample;
+ * <strong><code> m2</code></strong> is the mean of the second sample;
+ * <strong><code> var1</code></strong> is the variance of the first sample;
+ * <strong><code> var2</code></strong> is the variance of the second sample;
+ * </p><p>
+ * <strong>Preconditions</strong>: <ul>
+ * <li>The observed array lengths must both be at least 2.
+ * </li></ul></p>
+ *
+ * @param sample1 array of sample data values
+ * @param sample2 array of sample data values
+ * @return t statistic
+ * @throws NullArgumentException if the arrays are <code>null</code>
+ * @throws NumberIsTooSmallException if the length of the arrays is &lt; 2
+ */
+ public double t(final double[] sample1, final double[] sample2)
+ throws NullArgumentException, NumberIsTooSmallException {
+
+ checkSampleData(sample1);
+ checkSampleData(sample2);
+ // No try-catch or advertised exception because args have just been checked
+ return t(StatUtils.mean(sample1), StatUtils.mean(sample2),
+ StatUtils.variance(sample1), StatUtils.variance(sample2),
+ sample1.length, sample2.length);
+
+ }
+
+ /**
+ * Computes a 2-sample t statistic </a>, comparing the means of the datasets
+ * described by two {@link StatisticalSummary} instances, without the
+ * assumption of equal subpopulation variances. Use
+ * {@link #homoscedasticT(StatisticalSummary, StatisticalSummary)} to
+ * compute a t-statistic under the equal variances assumption.
+ * <p>
+ * This statistic can be used to perform a two-sample t-test to compare
+ * sample means.</p>
+ * <p>
+ * The returned t-statistic is</p>
+ * <p>
+ * &nbsp;&nbsp; <code> t = (m1 - m2) / sqrt(var1/n1 + var2/n2)</code>
+ * </p><p>
+ * where <strong><code>n1</code></strong> is the size of the first sample;
+ * <strong><code> n2</code></strong> is the size of the second sample;
+ * <strong><code> m1</code></strong> is the mean of the first sample;
+ * <strong><code> m2</code></strong> is the mean of the second sample
+ * <strong><code> var1</code></strong> is the variance of the first sample;
+ * <strong><code> var2</code></strong> is the variance of the second sample
+ * </p><p>
+ * <strong>Preconditions</strong>: <ul>
+ * <li>The datasets described by the two Univariates must each contain
+ * at least 2 observations.
+ * </li></ul></p>
+ *
+ * @param sampleStats1 StatisticalSummary describing data from the first sample
+ * @param sampleStats2 StatisticalSummary describing data from the second sample
+ * @return t statistic
+ * @throws NullArgumentException if the sample statistics are <code>null</code>
+ * @throws NumberIsTooSmallException if the number of samples is &lt; 2
+ */
+ public double t(final StatisticalSummary sampleStats1,
+ final StatisticalSummary sampleStats2)
+ throws NullArgumentException, NumberIsTooSmallException {
+
+ checkSampleData(sampleStats1);
+ checkSampleData(sampleStats2);
+ return t(sampleStats1.getMean(), sampleStats2.getMean(),
+ sampleStats1.getVariance(), sampleStats2.getVariance(),
+ sampleStats1.getN(), sampleStats2.getN());
+
+ }
+
+ /**
+ * Computes a 2-sample t statistic, comparing the means of the datasets
+ * described by two {@link StatisticalSummary} instances, under the
+ * assumption of equal subpopulation variances. To compute a t-statistic
+ * without the equal variances assumption, use
+ * {@link #t(StatisticalSummary, StatisticalSummary)}.
+ * <p>
+ * This statistic can be used to perform a (homoscedastic) two-sample
+ * t-test to compare sample means.</p>
+ * <p>
+ * The t-statistic returned is</p>
+ * <p>
+ * &nbsp;&nbsp;<code> t = (m1 - m2) / (sqrt(1/n1 +1/n2) sqrt(var))</code>
+ * </p><p>
+ * where <strong><code>n1</code></strong> is the size of first sample;
+ * <strong><code> n2</code></strong> is the size of second sample;
+ * <strong><code> m1</code></strong> is the mean of first sample;
+ * <strong><code> m2</code></strong> is the mean of second sample
+ * and <strong><code>var</code></strong> is the pooled variance estimate:
+ * </p><p>
+ * <code>var = sqrt(((n1 - 1)var1 + (n2 - 1)var2) / ((n1-1) + (n2-1)))</code>
+ * </p><p>
+ * with <strong><code>var1</code></strong> the variance of the first sample and
+ * <strong><code>var2</code></strong> the variance of the second sample.
+ * </p><p>
+ * <strong>Preconditions</strong>: <ul>
+ * <li>The datasets described by the two Univariates must each contain
+ * at least 2 observations.
+ * </li></ul></p>
+ *
+ * @param sampleStats1 StatisticalSummary describing data from the first sample
+ * @param sampleStats2 StatisticalSummary describing data from the second sample
+ * @return t statistic
+ * @throws NullArgumentException if the sample statistics are <code>null</code>
+ * @throws NumberIsTooSmallException if the number of samples is &lt; 2
+ */
+ public double homoscedasticT(final StatisticalSummary sampleStats1,
+ final StatisticalSummary sampleStats2)
+ throws NullArgumentException, NumberIsTooSmallException {
+
+ checkSampleData(sampleStats1);
+ checkSampleData(sampleStats2);
+ return homoscedasticT(sampleStats1.getMean(), sampleStats2.getMean(),
+ sampleStats1.getVariance(), sampleStats2.getVariance(),
+ sampleStats1.getN(), sampleStats2.getN());
+
+ }
+
+ /**
+ * Returns the <i>observed significance level</i>, or
+ * <i>p-value</i>, associated with a one-sample, two-tailed t-test
+ * comparing the mean of the input array with the constant <code>mu</code>.
+ * <p>
+ * The number returned is the smallest significance level
+ * at which one can reject the null hypothesis that the mean equals
+ * <code>mu</code> in favor of the two-sided alternative that the mean
+ * is different from <code>mu</code>. For a one-sided test, divide the
+ * returned value by 2.</p>
+ * <p>
+ * <strong>Usage Note:</strong><br>
+ * The validity of the test depends on the assumptions of the parametric
+ * t-test procedure, as discussed
+ * <a href="http://www.basic.nwu.edu/statguidefiles/ttest_unpaired_ass_viol.html">here</a>
+ * </p><p>
+ * <strong>Preconditions</strong>: <ul>
+ * <li>The observed array length must be at least 2.
+ * </li></ul></p>
+ *
+ * @param mu constant value to compare sample mean against
+ * @param sample array of sample data values
+ * @return p-value
+ * @throws NullArgumentException if the sample array is <code>null</code>
+ * @throws NumberIsTooSmallException if the length of the array is &lt; 2
+ * @throws MaxCountExceededException if an error occurs computing the p-value
+ */
+ public double tTest(final double mu, final double[] sample)
+ throws NullArgumentException, NumberIsTooSmallException,
+ MaxCountExceededException {
+
+ checkSampleData(sample);
+ // No try-catch or advertised exception because args have just been checked
+ return tTest(StatUtils.mean(sample), mu, StatUtils.variance(sample),
+ sample.length);
+
+ }
+
+ /**
+ * Performs a <a href="http://www.itl.nist.gov/div898/handbook/eda/section3/eda353.htm">
+ * two-sided t-test</a> evaluating the null hypothesis that the mean of the population from
+ * which <code>sample</code> is drawn equals <code>mu</code>.
+ * <p>
+ * Returns <code>true</code> iff the null hypothesis can be
+ * rejected with confidence <code>1 - alpha</code>. To
+ * perform a 1-sided test, use <code>alpha * 2</code></p>
+ * <p>
+ * <strong>Examples:</strong><br><ol>
+ * <li>To test the (2-sided) hypothesis <code>sample mean = mu </code> at
+ * the 95% level, use <br><code>tTest(mu, sample, 0.05) </code>
+ * </li>
+ * <li>To test the (one-sided) hypothesis <code> sample mean < mu </code>
+ * at the 99% level, first verify that the measured sample mean is less
+ * than <code>mu</code> and then use
+ * <br><code>tTest(mu, sample, 0.02) </code>
+ * </li></ol></p>
+ * <p>
+ * <strong>Usage Note:</strong><br>
+ * The validity of the test depends on the assumptions of the one-sample
+ * parametric t-test procedure, as discussed
+ * <a href="http://www.basic.nwu.edu/statguidefiles/sg_glos.html#one-sample">here</a>
+ * </p><p>
+ * <strong>Preconditions</strong>: <ul>
+ * <li>The observed array length must be at least 2.
+ * </li></ul></p>
+ *
+ * @param mu constant value to compare sample mean against
+ * @param sample array of sample data values
+ * @param alpha significance level of the test
+ * @return p-value
+ * @throws NullArgumentException if the sample array is <code>null</code>
+ * @throws NumberIsTooSmallException if the length of the array is &lt; 2
+ * @throws OutOfRangeException if <code>alpha</code> is not in the range (0, 0.5]
+ * @throws MaxCountExceededException if an error computing the p-value
+ */
+ public boolean tTest(final double mu, final double[] sample, final double alpha)
+ throws NullArgumentException, NumberIsTooSmallException,
+ OutOfRangeException, MaxCountExceededException {
+
+ checkSignificanceLevel(alpha);
+ return tTest(mu, sample) < alpha;
+
+ }
+
+ /**
+ * Returns the <i>observed significance level</i>, or
+ * <i>p-value</i>, associated with a one-sample, two-tailed t-test
+ * comparing the mean of the dataset described by <code>sampleStats</code>
+ * with the constant <code>mu</code>.
+ * <p>
+ * The number returned is the smallest significance level
+ * at which one can reject the null hypothesis that the mean equals
+ * <code>mu</code> in favor of the two-sided alternative that the mean
+ * is different from <code>mu</code>. For a one-sided test, divide the
+ * returned value by 2.</p>
+ * <p>
+ * <strong>Usage Note:</strong><br>
+ * The validity of the test depends on the assumptions of the parametric
+ * t-test procedure, as discussed
+ * <a href="http://www.basic.nwu.edu/statguidefiles/ttest_unpaired_ass_viol.html">
+ * here</a></p>
+ * <p>
+ * <strong>Preconditions</strong>: <ul>
+ * <li>The sample must contain at least 2 observations.
+ * </li></ul></p>
+ *
+ * @param mu constant value to compare sample mean against
+ * @param sampleStats StatisticalSummary describing sample data
+ * @return p-value
+ * @throws NullArgumentException if <code>sampleStats</code> is <code>null</code>
+ * @throws NumberIsTooSmallException if the number of samples is &lt; 2
+ * @throws MaxCountExceededException if an error occurs computing the p-value
+ */
+ public double tTest(final double mu, final StatisticalSummary sampleStats)
+ throws NullArgumentException, NumberIsTooSmallException,
+ MaxCountExceededException {
+
+ checkSampleData(sampleStats);
+ return tTest(sampleStats.getMean(), mu, sampleStats.getVariance(),
+ sampleStats.getN());
+
+ }
+
+ /**
+ * Performs a <a href="http://www.itl.nist.gov/div898/handbook/eda/section3/eda353.htm">
+ * two-sided t-test</a> evaluating the null hypothesis that the mean of the
+ * population from which the dataset described by <code>stats</code> is
+ * drawn equals <code>mu</code>.
+ * <p>
+ * Returns <code>true</code> iff the null hypothesis can be rejected with
+ * confidence <code>1 - alpha</code>. To perform a 1-sided test, use
+ * <code>alpha * 2.</code></p>
+ * <p>
+ * <strong>Examples:</strong><br><ol>
+ * <li>To test the (2-sided) hypothesis <code>sample mean = mu </code> at
+ * the 95% level, use <br><code>tTest(mu, sampleStats, 0.05) </code>
+ * </li>
+ * <li>To test the (one-sided) hypothesis <code> sample mean < mu </code>
+ * at the 99% level, first verify that the measured sample mean is less
+ * than <code>mu</code> and then use
+ * <br><code>tTest(mu, sampleStats, 0.02) </code>
+ * </li></ol></p>
+ * <p>
+ * <strong>Usage Note:</strong><br>
+ * The validity of the test depends on the assumptions of the one-sample
+ * parametric t-test procedure, as discussed
+ * <a href="http://www.basic.nwu.edu/statguidefiles/sg_glos.html#one-sample">here</a>
+ * </p><p>
+ * <strong>Preconditions</strong>: <ul>
+ * <li>The sample must include at least 2 observations.
+ * </li></ul></p>
+ *
+ * @param mu constant value to compare sample mean against
+ * @param sampleStats StatisticalSummary describing sample data values
+ * @param alpha significance level of the test
+ * @return p-value
+ * @throws NullArgumentException if <code>sampleStats</code> is <code>null</code>
+ * @throws NumberIsTooSmallException if the number of samples is &lt; 2
+ * @throws OutOfRangeException if <code>alpha</code> is not in the range (0, 0.5]
+ * @throws MaxCountExceededException if an error occurs computing the p-value
+ */
+ public boolean tTest(final double mu, final StatisticalSummary sampleStats,
+ final double alpha)
+ throws NullArgumentException, NumberIsTooSmallException,
+ OutOfRangeException, MaxCountExceededException {
+
+ checkSignificanceLevel(alpha);
+ return tTest(mu, sampleStats) < alpha;
+
+ }
+
+ /**
+ * Returns the <i>observed significance level</i>, or
+ * <i>p-value</i>, associated with a two-sample, two-tailed t-test
+ * comparing the means of the input arrays.
+ * <p>
+ * The number returned is the smallest significance level
+ * at which one can reject the null hypothesis that the two means are
+ * equal in favor of the two-sided alternative that they are different.
+ * For a one-sided test, divide the returned value by 2.</p>
+ * <p>
+ * The test does not assume that the underlying popuation variances are
+ * equal and it uses approximated degrees of freedom computed from the
+ * sample data to compute the p-value. The t-statistic used is as defined in
+ * {@link #t(double[], double[])} and the Welch-Satterthwaite approximation
+ * to the degrees of freedom is used,
+ * as described
+ * <a href="http://www.itl.nist.gov/div898/handbook/prc/section3/prc31.htm">
+ * here.</a> To perform the test under the assumption of equal subpopulation
+ * variances, use {@link #homoscedasticTTest(double[], double[])}.</p>
+ * <p>
+ * <strong>Usage Note:</strong><br>
+ * The validity of the p-value depends on the assumptions of the parametric
+ * t-test procedure, as discussed
+ * <a href="http://www.basic.nwu.edu/statguidefiles/ttest_unpaired_ass_viol.html">
+ * here</a></p>
+ * <p>
+ * <strong>Preconditions</strong>: <ul>
+ * <li>The observed array lengths must both be at least 2.
+ * </li></ul></p>
+ *
+ * @param sample1 array of sample data values
+ * @param sample2 array of sample data values
+ * @return p-value for t-test
+ * @throws NullArgumentException if the arrays are <code>null</code>
+ * @throws NumberIsTooSmallException if the length of the arrays is &lt; 2
+ * @throws MaxCountExceededException if an error occurs computing the p-value
+ */
+ public double tTest(final double[] sample1, final double[] sample2)
+ throws NullArgumentException, NumberIsTooSmallException,
+ MaxCountExceededException {
+
+ checkSampleData(sample1);
+ checkSampleData(sample2);
+ // No try-catch or advertised exception because args have just been checked
+ return tTest(StatUtils.mean(sample1), StatUtils.mean(sample2),
+ StatUtils.variance(sample1), StatUtils.variance(sample2),
+ sample1.length, sample2.length);
+
+ }
+
+ /**
+ * Returns the <i>observed significance level</i>, or
+ * <i>p-value</i>, associated with a two-sample, two-tailed t-test
+ * comparing the means of the input arrays, under the assumption that
+ * the two samples are drawn from subpopulations with equal variances.
+ * To perform the test without the equal variances assumption, use
+ * {@link #tTest(double[], double[])}.</p>
+ * <p>
+ * The number returned is the smallest significance level
+ * at which one can reject the null hypothesis that the two means are
+ * equal in favor of the two-sided alternative that they are different.
+ * For a one-sided test, divide the returned value by 2.</p>
+ * <p>
+ * A pooled variance estimate is used to compute the t-statistic. See
+ * {@link #homoscedasticT(double[], double[])}. The sum of the sample sizes
+ * minus 2 is used as the degrees of freedom.</p>
+ * <p>
+ * <strong>Usage Note:</strong><br>
+ * The validity of the p-value depends on the assumptions of the parametric
+ * t-test procedure, as discussed
+ * <a href="http://www.basic.nwu.edu/statguidefiles/ttest_unpaired_ass_viol.html">
+ * here</a></p>
+ * <p>
+ * <strong>Preconditions</strong>: <ul>
+ * <li>The observed array lengths must both be at least 2.
+ * </li></ul></p>
+ *
+ * @param sample1 array of sample data values
+ * @param sample2 array of sample data values
+ * @return p-value for t-test
+ * @throws NullArgumentException if the arrays are <code>null</code>
+ * @throws NumberIsTooSmallException if the length of the arrays is &lt; 2
+ * @throws MaxCountExceededException if an error occurs computing the p-value
+ */
+ public double homoscedasticTTest(final double[] sample1, final double[] sample2)
+ throws NullArgumentException, NumberIsTooSmallException,
+ MaxCountExceededException {
+
+ checkSampleData(sample1);
+ checkSampleData(sample2);
+ // No try-catch or advertised exception because args have just been checked
+ return homoscedasticTTest(StatUtils.mean(sample1),
+ StatUtils.mean(sample2),
+ StatUtils.variance(sample1),
+ StatUtils.variance(sample2),
+ sample1.length, sample2.length);
+
+ }
+
+ /**
+ * Performs a
+ * <a href="http://www.itl.nist.gov/div898/handbook/eda/section3/eda353.htm">
+ * two-sided t-test</a> evaluating the null hypothesis that <code>sample1</code>
+ * and <code>sample2</code> are drawn from populations with the same mean,
+ * with significance level <code>alpha</code>. This test does not assume
+ * that the subpopulation variances are equal. To perform the test assuming
+ * equal variances, use
+ * {@link #homoscedasticTTest(double[], double[], double)}.
+ * <p>
+ * Returns <code>true</code> iff the null hypothesis that the means are
+ * equal can be rejected with confidence <code>1 - alpha</code>. To
+ * perform a 1-sided test, use <code>alpha * 2</code></p>
+ * <p>
+ * See {@link #t(double[], double[])} for the formula used to compute the
+ * t-statistic. Degrees of freedom are approximated using the
+ * <a href="http://www.itl.nist.gov/div898/handbook/prc/section3/prc31.htm">
+ * Welch-Satterthwaite approximation.</a></p>
+ * <p>
+ * <strong>Examples:</strong><br><ol>
+ * <li>To test the (2-sided) hypothesis <code>mean 1 = mean 2 </code> at
+ * the 95% level, use
+ * <br><code>tTest(sample1, sample2, 0.05). </code>
+ * </li>
+ * <li>To test the (one-sided) hypothesis <code> mean 1 < mean 2 </code>,
+ * at the 99% level, first verify that the measured mean of <code>sample 1</code>
+ * is less than the mean of <code>sample 2</code> and then use
+ * <br><code>tTest(sample1, sample2, 0.02) </code>
+ * </li></ol></p>
+ * <p>
+ * <strong>Usage Note:</strong><br>
+ * The validity of the test depends on the assumptions of the parametric
+ * t-test procedure, as discussed
+ * <a href="http://www.basic.nwu.edu/statguidefiles/ttest_unpaired_ass_viol.html">
+ * here</a></p>
+ * <p>
+ * <strong>Preconditions</strong>: <ul>
+ * <li>The observed array lengths must both be at least 2.
+ * </li>
+ * <li> <code> 0 < alpha < 0.5 </code>
+ * </li></ul></p>
+ *
+ * @param sample1 array of sample data values
+ * @param sample2 array of sample data values
+ * @param alpha significance level of the test
+ * @return true if the null hypothesis can be rejected with
+ * confidence 1 - alpha
+ * @throws NullArgumentException if the arrays are <code>null</code>
+ * @throws NumberIsTooSmallException if the length of the arrays is &lt; 2
+ * @throws OutOfRangeException if <code>alpha</code> is not in the range (0, 0.5]
+ * @throws MaxCountExceededException if an error occurs computing the p-value
+ */
+ public boolean tTest(final double[] sample1, final double[] sample2,
+ final double alpha)
+ throws NullArgumentException, NumberIsTooSmallException,
+ OutOfRangeException, MaxCountExceededException {
+
+ checkSignificanceLevel(alpha);
+ return tTest(sample1, sample2) < alpha;
+
+ }
+
+ /**
+ * Performs a
+ * <a href="http://www.itl.nist.gov/div898/handbook/eda/section3/eda353.htm">
+ * two-sided t-test</a> evaluating the null hypothesis that <code>sample1</code>
+ * and <code>sample2</code> are drawn from populations with the same mean,
+ * with significance level <code>alpha</code>, assuming that the
+ * subpopulation variances are equal. Use
+ * {@link #tTest(double[], double[], double)} to perform the test without
+ * the assumption of equal variances.
+ * <p>
+ * Returns <code>true</code> iff the null hypothesis that the means are
+ * equal can be rejected with confidence <code>1 - alpha</code>. To
+ * perform a 1-sided test, use <code>alpha * 2.</code> To perform the test
+ * without the assumption of equal subpopulation variances, use
+ * {@link #tTest(double[], double[], double)}.</p>
+ * <p>
+ * A pooled variance estimate is used to compute the t-statistic. See
+ * {@link #t(double[], double[])} for the formula. The sum of the sample
+ * sizes minus 2 is used as the degrees of freedom.</p>
+ * <p>
+ * <strong>Examples:</strong><br><ol>
+ * <li>To test the (2-sided) hypothesis <code>mean 1 = mean 2 </code> at
+ * the 95% level, use <br><code>tTest(sample1, sample2, 0.05). </code>
+ * </li>
+ * <li>To test the (one-sided) hypothesis <code> mean 1 < mean 2, </code>
+ * at the 99% level, first verify that the measured mean of
+ * <code>sample 1</code> is less than the mean of <code>sample 2</code>
+ * and then use
+ * <br><code>tTest(sample1, sample2, 0.02) </code>
+ * </li></ol></p>
+ * <p>
+ * <strong>Usage Note:</strong><br>
+ * The validity of the test depends on the assumptions of the parametric
+ * t-test procedure, as discussed
+ * <a href="http://www.basic.nwu.edu/statguidefiles/ttest_unpaired_ass_viol.html">
+ * here</a></p>
+ * <p>
+ * <strong>Preconditions</strong>: <ul>
+ * <li>The observed array lengths must both be at least 2.
+ * </li>
+ * <li> <code> 0 < alpha < 0.5 </code>
+ * </li></ul></p>
+ *
+ * @param sample1 array of sample data values
+ * @param sample2 array of sample data values
+ * @param alpha significance level of the test
+ * @return true if the null hypothesis can be rejected with
+ * confidence 1 - alpha
+ * @throws NullArgumentException if the arrays are <code>null</code>
+ * @throws NumberIsTooSmallException if the length of the arrays is &lt; 2
+ * @throws OutOfRangeException if <code>alpha</code> is not in the range (0, 0.5]
+ * @throws MaxCountExceededException if an error occurs computing the p-value
+ */
+ public boolean homoscedasticTTest(final double[] sample1, final double[] sample2,
+ final double alpha)
+ throws NullArgumentException, NumberIsTooSmallException,
+ OutOfRangeException, MaxCountExceededException {
+
+ checkSignificanceLevel(alpha);
+ return homoscedasticTTest(sample1, sample2) < alpha;
+
+ }
+
+ /**
+ * Returns the <i>observed significance level</i>, or
+ * <i>p-value</i>, associated with a two-sample, two-tailed t-test
+ * comparing the means of the datasets described by two StatisticalSummary
+ * instances.
+ * <p>
+ * The number returned is the smallest significance level
+ * at which one can reject the null hypothesis that the two means are
+ * equal in favor of the two-sided alternative that they are different.
+ * For a one-sided test, divide the returned value by 2.</p>
+ * <p>
+ * The test does not assume that the underlying population variances are
+ * equal and it uses approximated degrees of freedom computed from the
+ * sample data to compute the p-value. To perform the test assuming
+ * equal variances, use
+ * {@link #homoscedasticTTest(StatisticalSummary, StatisticalSummary)}.</p>
+ * <p>
+ * <strong>Usage Note:</strong><br>
+ * The validity of the p-value depends on the assumptions of the parametric
+ * t-test procedure, as discussed
+ * <a href="http://www.basic.nwu.edu/statguidefiles/ttest_unpaired_ass_viol.html">
+ * here</a></p>
+ * <p>
+ * <strong>Preconditions</strong>: <ul>
+ * <li>The datasets described by the two Univariates must each contain
+ * at least 2 observations.
+ * </li></ul></p>
+ *
+ * @param sampleStats1 StatisticalSummary describing data from the first sample
+ * @param sampleStats2 StatisticalSummary describing data from the second sample
+ * @return p-value for t-test
+ * @throws NullArgumentException if the sample statistics are <code>null</code>
+ * @throws NumberIsTooSmallException if the number of samples is &lt; 2
+ * @throws MaxCountExceededException if an error occurs computing the p-value
+ */
+ public double tTest(final StatisticalSummary sampleStats1,
+ final StatisticalSummary sampleStats2)
+ throws NullArgumentException, NumberIsTooSmallException,
+ MaxCountExceededException {
+
+ checkSampleData(sampleStats1);
+ checkSampleData(sampleStats2);
+ return tTest(sampleStats1.getMean(), sampleStats2.getMean(),
+ sampleStats1.getVariance(), sampleStats2.getVariance(),
+ sampleStats1.getN(), sampleStats2.getN());
+
+ }
+
+ /**
+ * Returns the <i>observed significance level</i>, or
+ * <i>p-value</i>, associated with a two-sample, two-tailed t-test
+ * comparing the means of the datasets described by two StatisticalSummary
+ * instances, under the hypothesis of equal subpopulation variances. To
+ * perform a test without the equal variances assumption, use
+ * {@link #tTest(StatisticalSummary, StatisticalSummary)}.
+ * <p>
+ * The number returned is the smallest significance level
+ * at which one can reject the null hypothesis that the two means are
+ * equal in favor of the two-sided alternative that they are different.
+ * For a one-sided test, divide the returned value by 2.</p>
+ * <p>
+ * See {@link #homoscedasticT(double[], double[])} for the formula used to
+ * compute the t-statistic. The sum of the sample sizes minus 2 is used as
+ * the degrees of freedom.</p>
+ * <p>
+ * <strong>Usage Note:</strong><br>
+ * The validity of the p-value depends on the assumptions of the parametric
+ * t-test procedure, as discussed
+ * <a href="http://www.basic.nwu.edu/statguidefiles/ttest_unpaired_ass_viol.html">here</a>
+ * </p><p>
+ * <strong>Preconditions</strong>: <ul>
+ * <li>The datasets described by the two Univariates must each contain
+ * at least 2 observations.
+ * </li></ul></p>
+ *
+ * @param sampleStats1 StatisticalSummary describing data from the first sample
+ * @param sampleStats2 StatisticalSummary describing data from the second sample
+ * @return p-value for t-test
+ * @throws NullArgumentException if the sample statistics are <code>null</code>
+ * @throws NumberIsTooSmallException if the number of samples is &lt; 2
+ * @throws MaxCountExceededException if an error occurs computing the p-value
+ */
+ public double homoscedasticTTest(final StatisticalSummary sampleStats1,
+ final StatisticalSummary sampleStats2)
+ throws NullArgumentException, NumberIsTooSmallException,
+ MaxCountExceededException {
+
+ checkSampleData(sampleStats1);
+ checkSampleData(sampleStats2);
+ return homoscedasticTTest(sampleStats1.getMean(),
+ sampleStats2.getMean(),
+ sampleStats1.getVariance(),
+ sampleStats2.getVariance(),
+ sampleStats1.getN(), sampleStats2.getN());
+
+ }
+
+ /**
+ * Performs a
+ * <a href="http://www.itl.nist.gov/div898/handbook/eda/section3/eda353.htm">
+ * two-sided t-test</a> evaluating the null hypothesis that
+ * <code>sampleStats1</code> and <code>sampleStats2</code> describe
+ * datasets drawn from populations with the same mean, with significance
+ * level <code>alpha</code>. This test does not assume that the
+ * subpopulation variances are equal. To perform the test under the equal
+ * variances assumption, use
+ * {@link #homoscedasticTTest(StatisticalSummary, StatisticalSummary)}.
+ * <p>
+ * Returns <code>true</code> iff the null hypothesis that the means are
+ * equal can be rejected with confidence <code>1 - alpha</code>. To
+ * perform a 1-sided test, use <code>alpha * 2</code></p>
+ * <p>
+ * See {@link #t(double[], double[])} for the formula used to compute the
+ * t-statistic. Degrees of freedom are approximated using the
+ * <a href="http://www.itl.nist.gov/div898/handbook/prc/section3/prc31.htm">
+ * Welch-Satterthwaite approximation.</a></p>
+ * <p>
+ * <strong>Examples:</strong><br><ol>
+ * <li>To test the (2-sided) hypothesis <code>mean 1 = mean 2 </code> at
+ * the 95%, use
+ * <br><code>tTest(sampleStats1, sampleStats2, 0.05) </code>
+ * </li>
+ * <li>To test the (one-sided) hypothesis <code> mean 1 < mean 2 </code>
+ * at the 99% level, first verify that the measured mean of
+ * <code>sample 1</code> is less than the mean of <code>sample 2</code>
+ * and then use
+ * <br><code>tTest(sampleStats1, sampleStats2, 0.02) </code>
+ * </li></ol></p>
+ * <p>
+ * <strong>Usage Note:</strong><br>
+ * The validity of the test depends on the assumptions of the parametric
+ * t-test procedure, as discussed
+ * <a href="http://www.basic.nwu.edu/statguidefiles/ttest_unpaired_ass_viol.html">
+ * here</a></p>
+ * <p>
+ * <strong>Preconditions</strong>: <ul>
+ * <li>The datasets described by the two Univariates must each contain
+ * at least 2 observations.
+ * </li>
+ * <li> <code> 0 < alpha < 0.5 </code>
+ * </li></ul></p>
+ *
+ * @param sampleStats1 StatisticalSummary describing sample data values
+ * @param sampleStats2 StatisticalSummary describing sample data values
+ * @param alpha significance level of the test
+ * @return true if the null hypothesis can be rejected with
+ * confidence 1 - alpha
+ * @throws NullArgumentException if the sample statistics are <code>null</code>
+ * @throws NumberIsTooSmallException if the number of samples is &lt; 2
+ * @throws OutOfRangeException if <code>alpha</code> is not in the range (0, 0.5]
+ * @throws MaxCountExceededException if an error occurs computing the p-value
+ */
+ public boolean tTest(final StatisticalSummary sampleStats1,
+ final StatisticalSummary sampleStats2,
+ final double alpha)
+ throws NullArgumentException, NumberIsTooSmallException,
+ OutOfRangeException, MaxCountExceededException {
+
+ checkSignificanceLevel(alpha);
+ return tTest(sampleStats1, sampleStats2) < alpha;
+
+ }
+
+ //----------------------------------------------- Protected methods
+
+ /**
+ * Computes approximate degrees of freedom for 2-sample t-test.
+ *
+ * @param v1 first sample variance
+ * @param v2 second sample variance
+ * @param n1 first sample n
+ * @param n2 second sample n
+ * @return approximate degrees of freedom
+ */
+ protected double df(double v1, double v2, double n1, double n2) {
+ return (((v1 / n1) + (v2 / n2)) * ((v1 / n1) + (v2 / n2))) /
+ ((v1 * v1) / (n1 * n1 * (n1 - 1d)) + (v2 * v2) /
+ (n2 * n2 * (n2 - 1d)));
+ }
+
+ /**
+ * Computes t test statistic for 1-sample t-test.
+ *
+ * @param m sample mean
+ * @param mu constant to test against
+ * @param v sample variance
+ * @param n sample n
+ * @return t test statistic
+ */
+ protected double t(final double m, final double mu,
+ final double v, final double n) {
+ return (m - mu) / FastMath.sqrt(v / n);
+ }
+
+ /**
+ * Computes t test statistic for 2-sample t-test.
+ * <p>
+ * Does not assume that subpopulation variances are equal.</p>
+ *
+ * @param m1 first sample mean
+ * @param m2 second sample mean
+ * @param v1 first sample variance
+ * @param v2 second sample variance
+ * @param n1 first sample n
+ * @param n2 second sample n
+ * @return t test statistic
+ */
+ protected double t(final double m1, final double m2,
+ final double v1, final double v2,
+ final double n1, final double n2) {
+ return (m1 - m2) / FastMath.sqrt((v1 / n1) + (v2 / n2));
+ }
+
+ /**
+ * Computes t test statistic for 2-sample t-test under the hypothesis
+ * of equal subpopulation variances.
+ *
+ * @param m1 first sample mean
+ * @param m2 second sample mean
+ * @param v1 first sample variance
+ * @param v2 second sample variance
+ * @param n1 first sample n
+ * @param n2 second sample n
+ * @return t test statistic
+ */
+ protected double homoscedasticT(final double m1, final double m2,
+ final double v1, final double v2,
+ final double n1, final double n2) {
+ final double pooledVariance = ((n1 - 1) * v1 + (n2 -1) * v2 ) / (n1 + n2 - 2);
+ return (m1 - m2) / FastMath.sqrt(pooledVariance * (1d / n1 + 1d / n2));
+ }
+
+ /**
+ * Computes p-value for 2-sided, 1-sample t-test.
+ *
+ * @param m sample mean
+ * @param mu constant to test against
+ * @param v sample variance
+ * @param n sample n
+ * @return p-value
+ * @throws MaxCountExceededException if an error occurs computing the p-value
+ * @throws MathIllegalArgumentException if n is not greater than 1
+ */
+ protected double tTest(final double m, final double mu,
+ final double v, final double n)
+ throws MaxCountExceededException, MathIllegalArgumentException {
+
+ final double t = FastMath.abs(t(m, mu, v, n));
+ // pass a null rng to avoid unneeded overhead as we will not sample from this distribution
+ final TDistribution distribution = new TDistribution(null, n - 1);
+ return 2.0 * distribution.cumulativeProbability(-t);
+
+ }
+
+ /**
+ * Computes p-value for 2-sided, 2-sample t-test.
+ * <p>
+ * Does not assume subpopulation variances are equal. Degrees of freedom
+ * are estimated from the data.</p>
+ *
+ * @param m1 first sample mean
+ * @param m2 second sample mean
+ * @param v1 first sample variance
+ * @param v2 second sample variance
+ * @param n1 first sample n
+ * @param n2 second sample n
+ * @return p-value
+ * @throws MaxCountExceededException if an error occurs computing the p-value
+ * @throws NotStrictlyPositiveException if the estimated degrees of freedom is not
+ * strictly positive
+ */
+ protected double tTest(final double m1, final double m2,
+ final double v1, final double v2,
+ final double n1, final double n2)
+ throws MaxCountExceededException, NotStrictlyPositiveException {
+
+ final double t = FastMath.abs(t(m1, m2, v1, v2, n1, n2));
+ final double degreesOfFreedom = df(v1, v2, n1, n2);
+ // pass a null rng to avoid unneeded overhead as we will not sample from this distribution
+ final TDistribution distribution = new TDistribution(null, degreesOfFreedom);
+ return 2.0 * distribution.cumulativeProbability(-t);
+
+ }
+
+ /**
+ * Computes p-value for 2-sided, 2-sample t-test, under the assumption
+ * of equal subpopulation variances.
+ * <p>
+ * The sum of the sample sizes minus 2 is used as degrees of freedom.</p>
+ *
+ * @param m1 first sample mean
+ * @param m2 second sample mean
+ * @param v1 first sample variance
+ * @param v2 second sample variance
+ * @param n1 first sample n
+ * @param n2 second sample n
+ * @return p-value
+ * @throws MaxCountExceededException if an error occurs computing the p-value
+ * @throws NotStrictlyPositiveException if the estimated degrees of freedom is not
+ * strictly positive
+ */
+ protected double homoscedasticTTest(double m1, double m2,
+ double v1, double v2,
+ double n1, double n2)
+ throws MaxCountExceededException, NotStrictlyPositiveException {
+
+ final double t = FastMath.abs(homoscedasticT(m1, m2, v1, v2, n1, n2));
+ final double degreesOfFreedom = n1 + n2 - 2;
+ // pass a null rng to avoid unneeded overhead as we will not sample from this distribution
+ final TDistribution distribution = new TDistribution(null, degreesOfFreedom);
+ return 2.0 * distribution.cumulativeProbability(-t);
+
+ }
+
+ /**
+ * Check significance level.
+ *
+ * @param alpha significance level
+ * @throws OutOfRangeException if the significance level is out of bounds.
+ */
+ private void checkSignificanceLevel(final double alpha)
+ throws OutOfRangeException {
+
+ if (alpha <= 0 || alpha > 0.5) {
+ throw new OutOfRangeException(LocalizedFormats.SIGNIFICANCE_LEVEL,
+ alpha, 0.0, 0.5);
+ }
+
+ }
+
+ /**
+ * Check sample data.
+ *
+ * @param data Sample data.
+ * @throws NullArgumentException if {@code data} is {@code null}.
+ * @throws NumberIsTooSmallException if there is not enough sample data.
+ */
+ private void checkSampleData(final double[] data)
+ throws NullArgumentException, NumberIsTooSmallException {
+
+ if (data == null) {
+ throw new NullArgumentException();
+ }
+ if (data.length < 2) {
+ throw new NumberIsTooSmallException(
+ LocalizedFormats.INSUFFICIENT_DATA_FOR_T_STATISTIC,
+ data.length, 2, true);
+ }
+
+ }
+
+ /**
+ * Check sample data.
+ *
+ * @param stat Statistical summary.
+ * @throws NullArgumentException if {@code data} is {@code null}.
+ * @throws NumberIsTooSmallException if there is not enough sample data.
+ */
+ private void checkSampleData(final StatisticalSummary stat)
+ throws NullArgumentException, NumberIsTooSmallException {
+
+ if (stat == null) {
+ throw new NullArgumentException();
+ }
+ if (stat.getN() < 2) {
+ throw new NumberIsTooSmallException(
+ LocalizedFormats.INSUFFICIENT_DATA_FOR_T_STATISTIC,
+ stat.getN(), 2, true);
+ }
+
+ }
+
+}
diff --git a/src/main/java/org/apache/commons/math3/stat/inference/TestUtils.java b/src/main/java/org/apache/commons/math3/stat/inference/TestUtils.java
new file mode 100644
index 0000000..a92fb19
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/stat/inference/TestUtils.java
@@ -0,0 +1,547 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math3.stat.inference;
+
+import java.util.Collection;
+
+import org.apache.commons.math3.distribution.RealDistribution;
+import org.apache.commons.math3.exception.ConvergenceException;
+import org.apache.commons.math3.exception.DimensionMismatchException;
+import org.apache.commons.math3.exception.InsufficientDataException;
+import org.apache.commons.math3.exception.MaxCountExceededException;
+import org.apache.commons.math3.exception.NoDataException;
+import org.apache.commons.math3.exception.NotPositiveException;
+import org.apache.commons.math3.exception.NotStrictlyPositiveException;
+import org.apache.commons.math3.exception.NullArgumentException;
+import org.apache.commons.math3.exception.NumberIsTooSmallException;
+import org.apache.commons.math3.exception.OutOfRangeException;
+import org.apache.commons.math3.exception.ZeroException;
+import org.apache.commons.math3.stat.descriptive.StatisticalSummary;
+
+/**
+ * A collection of static methods to create inference test instances or to
+ * perform inference tests.
+ *
+ * @since 1.1
+ */
+public class TestUtils {
+
+ /** Singleton TTest instance. */
+ private static final TTest T_TEST = new TTest();
+
+ /** Singleton ChiSquareTest instance. */
+ private static final ChiSquareTest CHI_SQUARE_TEST = new ChiSquareTest();
+
+ /** Singleton OneWayAnova instance. */
+ private static final OneWayAnova ONE_WAY_ANANOVA = new OneWayAnova();
+
+ /** Singleton G-Test instance. */
+ private static final GTest G_TEST = new GTest();
+
+ /** Singleton K-S test instance */
+ private static final KolmogorovSmirnovTest KS_TEST = new KolmogorovSmirnovTest();
+
+ /**
+ * Prevent instantiation.
+ */
+ private TestUtils() {
+ super();
+ }
+
+ // CHECKSTYLE: stop JavadocMethodCheck
+
+ /**
+ * @see org.apache.commons.math3.stat.inference.TTest#homoscedasticT(double[], double[])
+ */
+ public static double homoscedasticT(final double[] sample1, final double[] sample2)
+ throws NullArgumentException, NumberIsTooSmallException {
+ return T_TEST.homoscedasticT(sample1, sample2);
+ }
+
+ /**
+ * @see org.apache.commons.math3.stat.inference.TTest#homoscedasticT(org.apache.commons.math3.stat.descriptive.StatisticalSummary, org.apache.commons.math3.stat.descriptive.StatisticalSummary)
+ */
+ public static double homoscedasticT(final StatisticalSummary sampleStats1,
+ final StatisticalSummary sampleStats2)
+ throws NullArgumentException, NumberIsTooSmallException {
+ return T_TEST.homoscedasticT(sampleStats1, sampleStats2);
+ }
+
+ /**
+ * @see org.apache.commons.math3.stat.inference.TTest#homoscedasticTTest(double[], double[], double)
+ */
+ public static boolean homoscedasticTTest(final double[] sample1, final double[] sample2,
+ final double alpha)
+ throws NullArgumentException, NumberIsTooSmallException,
+ OutOfRangeException, MaxCountExceededException {
+ return T_TEST.homoscedasticTTest(sample1, sample2, alpha);
+ }
+
+ /**
+ * @see org.apache.commons.math3.stat.inference.TTest#homoscedasticTTest(double[], double[])
+ */
+ public static double homoscedasticTTest(final double[] sample1, final double[] sample2)
+ throws NullArgumentException, NumberIsTooSmallException, MaxCountExceededException {
+ return T_TEST.homoscedasticTTest(sample1, sample2);
+ }
+
+ /**
+ * @see org.apache.commons.math3.stat.inference.TTest#homoscedasticTTest(org.apache.commons.math3.stat.descriptive.StatisticalSummary, org.apache.commons.math3.stat.descriptive.StatisticalSummary)
+ */
+ public static double homoscedasticTTest(final StatisticalSummary sampleStats1,
+ final StatisticalSummary sampleStats2)
+ throws NullArgumentException, NumberIsTooSmallException, MaxCountExceededException {
+ return T_TEST.homoscedasticTTest(sampleStats1, sampleStats2);
+ }
+
+ /**
+ * @see org.apache.commons.math3.stat.inference.TTest#pairedT(double[], double[])
+ */
+ public static double pairedT(final double[] sample1, final double[] sample2)
+ throws NullArgumentException, NoDataException,
+ DimensionMismatchException, NumberIsTooSmallException {
+ return T_TEST.pairedT(sample1, sample2);
+ }
+
+ /**
+ * @see org.apache.commons.math3.stat.inference.TTest#pairedTTest(double[], double[], double)
+ */
+ public static boolean pairedTTest(final double[] sample1, final double[] sample2,
+ final double alpha)
+ throws NullArgumentException, NoDataException, DimensionMismatchException,
+ NumberIsTooSmallException, OutOfRangeException, MaxCountExceededException {
+ return T_TEST.pairedTTest(sample1, sample2, alpha);
+ }
+
+ /**
+ * @see org.apache.commons.math3.stat.inference.TTest#pairedTTest(double[], double[])
+ */
+ public static double pairedTTest(final double[] sample1, final double[] sample2)
+ throws NullArgumentException, NoDataException, DimensionMismatchException,
+ NumberIsTooSmallException, MaxCountExceededException {
+ return T_TEST.pairedTTest(sample1, sample2);
+ }
+
+ /**
+ * @see org.apache.commons.math3.stat.inference.TTest#t(double, double[])
+ */
+ public static double t(final double mu, final double[] observed)
+ throws NullArgumentException, NumberIsTooSmallException {
+ return T_TEST.t(mu, observed);
+ }
+
+ /**
+ * @see org.apache.commons.math3.stat.inference.TTest#t(double, org.apache.commons.math3.stat.descriptive.StatisticalSummary)
+ */
+ public static double t(final double mu, final StatisticalSummary sampleStats)
+ throws NullArgumentException, NumberIsTooSmallException {
+ return T_TEST.t(mu, sampleStats);
+ }
+
+ /**
+ * @see org.apache.commons.math3.stat.inference.TTest#t(double[], double[])
+ */
+ public static double t(final double[] sample1, final double[] sample2)
+ throws NullArgumentException, NumberIsTooSmallException {
+ return T_TEST.t(sample1, sample2);
+ }
+
+ /**
+ * @see org.apache.commons.math3.stat.inference.TTest#t(org.apache.commons.math3.stat.descriptive.StatisticalSummary, org.apache.commons.math3.stat.descriptive.StatisticalSummary)
+ */
+ public static double t(final StatisticalSummary sampleStats1,
+ final StatisticalSummary sampleStats2)
+ throws NullArgumentException, NumberIsTooSmallException {
+ return T_TEST.t(sampleStats1, sampleStats2);
+ }
+
+ /**
+ * @see org.apache.commons.math3.stat.inference.TTest#tTest(double, double[], double)
+ */
+ public static boolean tTest(final double mu, final double[] sample, final double alpha)
+ throws NullArgumentException, NumberIsTooSmallException,
+ OutOfRangeException, MaxCountExceededException {
+ return T_TEST.tTest(mu, sample, alpha);
+ }
+
+ /**
+ * @see org.apache.commons.math3.stat.inference.TTest#tTest(double, double[])
+ */
+ public static double tTest(final double mu, final double[] sample)
+ throws NullArgumentException, NumberIsTooSmallException,
+ MaxCountExceededException {
+ return T_TEST.tTest(mu, sample);
+ }
+
+ /**
+ * @see org.apache.commons.math3.stat.inference.TTest#tTest(double, org.apache.commons.math3.stat.descriptive.StatisticalSummary, double)
+ */
+ public static boolean tTest(final double mu, final StatisticalSummary sampleStats,
+ final double alpha)
+ throws NullArgumentException, NumberIsTooSmallException,
+ OutOfRangeException, MaxCountExceededException {
+ return T_TEST.tTest(mu, sampleStats, alpha);
+ }
+
+ /**
+ * @see org.apache.commons.math3.stat.inference.TTest#tTest(double, org.apache.commons.math3.stat.descriptive.StatisticalSummary)
+ */
+ public static double tTest(final double mu, final StatisticalSummary sampleStats)
+ throws NullArgumentException, NumberIsTooSmallException,
+ MaxCountExceededException {
+ return T_TEST.tTest(mu, sampleStats);
+ }
+
+ /**
+ * @see org.apache.commons.math3.stat.inference.TTest#tTest(double[], double[], double)
+ */
+ public static boolean tTest(final double[] sample1, final double[] sample2,
+ final double alpha)
+ throws NullArgumentException, NumberIsTooSmallException,
+ OutOfRangeException, MaxCountExceededException {
+ return T_TEST.tTest(sample1, sample2, alpha);
+ }
+
+ /**
+ * @see org.apache.commons.math3.stat.inference.TTest#tTest(double[], double[])
+ */
+ public static double tTest(final double[] sample1, final double[] sample2)
+ throws NullArgumentException, NumberIsTooSmallException,
+ MaxCountExceededException {
+ return T_TEST.tTest(sample1, sample2);
+ }
+
+ /**
+ * @see org.apache.commons.math3.stat.inference.TTest#tTest(org.apache.commons.math3.stat.descriptive.StatisticalSummary, org.apache.commons.math3.stat.descriptive.StatisticalSummary, double)
+ */
+ public static boolean tTest(final StatisticalSummary sampleStats1,
+ final StatisticalSummary sampleStats2,
+ final double alpha)
+ throws NullArgumentException, NumberIsTooSmallException,
+ OutOfRangeException, MaxCountExceededException {
+ return T_TEST.tTest(sampleStats1, sampleStats2, alpha);
+ }
+
+ /**
+ * @see org.apache.commons.math3.stat.inference.TTest#tTest(org.apache.commons.math3.stat.descriptive.StatisticalSummary, org.apache.commons.math3.stat.descriptive.StatisticalSummary)
+ */
+ public static double tTest(final StatisticalSummary sampleStats1,
+ final StatisticalSummary sampleStats2)
+ throws NullArgumentException, NumberIsTooSmallException,
+ MaxCountExceededException {
+ return T_TEST.tTest(sampleStats1, sampleStats2);
+ }
+
+ /**
+ * @see org.apache.commons.math3.stat.inference.ChiSquareTest#chiSquare(double[], long[])
+ */
+ public static double chiSquare(final double[] expected, final long[] observed)
+ throws NotPositiveException, NotStrictlyPositiveException,
+ DimensionMismatchException {
+ return CHI_SQUARE_TEST.chiSquare(expected, observed);
+ }
+
+ /**
+ * @see org.apache.commons.math3.stat.inference.ChiSquareTest#chiSquare(long[][])
+ */
+ public static double chiSquare(final long[][] counts)
+ throws NullArgumentException, NotPositiveException,
+ DimensionMismatchException {
+ return CHI_SQUARE_TEST.chiSquare(counts);
+ }
+
+ /**
+ * @see org.apache.commons.math3.stat.inference.ChiSquareTest#chiSquareTest(double[], long[], double)
+ */
+ public static boolean chiSquareTest(final double[] expected, final long[] observed,
+ final double alpha)
+ throws NotPositiveException, NotStrictlyPositiveException,
+ DimensionMismatchException, OutOfRangeException, MaxCountExceededException {
+ return CHI_SQUARE_TEST.chiSquareTest(expected, observed, alpha);
+ }
+
+ /**
+ * @see org.apache.commons.math3.stat.inference.ChiSquareTest#chiSquareTest(double[], long[])
+ */
+ public static double chiSquareTest(final double[] expected, final long[] observed)
+ throws NotPositiveException, NotStrictlyPositiveException,
+ DimensionMismatchException, MaxCountExceededException {
+ return CHI_SQUARE_TEST.chiSquareTest(expected, observed);
+ }
+
+ /**
+ * @see org.apache.commons.math3.stat.inference.ChiSquareTest#chiSquareTest(long[][], double)
+ */
+ public static boolean chiSquareTest(final long[][] counts, final double alpha)
+ throws NullArgumentException, DimensionMismatchException,
+ NotPositiveException, OutOfRangeException, MaxCountExceededException {
+ return CHI_SQUARE_TEST.chiSquareTest(counts, alpha);
+ }
+
+ /**
+ * @see org.apache.commons.math3.stat.inference.ChiSquareTest#chiSquareTest(long[][])
+ */
+ public static double chiSquareTest(final long[][] counts)
+ throws NullArgumentException, DimensionMismatchException,
+ NotPositiveException, MaxCountExceededException {
+ return CHI_SQUARE_TEST.chiSquareTest(counts);
+ }
+
+ /**
+ * @see org.apache.commons.math3.stat.inference.ChiSquareTest#chiSquareDataSetsComparison(long[], long[])
+ *
+ * @since 1.2
+ */
+ public static double chiSquareDataSetsComparison(final long[] observed1,
+ final long[] observed2)
+ throws DimensionMismatchException, NotPositiveException, ZeroException {
+ return CHI_SQUARE_TEST.chiSquareDataSetsComparison(observed1, observed2);
+ }
+
+ /**
+ * @see org.apache.commons.math3.stat.inference.ChiSquareTest#chiSquareTestDataSetsComparison(long[], long[])
+ *
+ * @since 1.2
+ */
+ public static double chiSquareTestDataSetsComparison(final long[] observed1,
+ final long[] observed2)
+ throws DimensionMismatchException, NotPositiveException, ZeroException,
+ MaxCountExceededException {
+ return CHI_SQUARE_TEST.chiSquareTestDataSetsComparison(observed1, observed2);
+ }
+
+ /**
+ * @see org.apache.commons.math3.stat.inference.ChiSquareTest#chiSquareTestDataSetsComparison(long[], long[], double)
+ *
+ * @since 1.2
+ */
+ public static boolean chiSquareTestDataSetsComparison(final long[] observed1,
+ final long[] observed2,
+ final double alpha)
+ throws DimensionMismatchException, NotPositiveException,
+ ZeroException, OutOfRangeException, MaxCountExceededException {
+ return CHI_SQUARE_TEST.chiSquareTestDataSetsComparison(observed1, observed2, alpha);
+ }
+
+ /**
+ * @see org.apache.commons.math3.stat.inference.OneWayAnova#anovaFValue(Collection)
+ *
+ * @since 1.2
+ */
+ public static double oneWayAnovaFValue(final Collection<double[]> categoryData)
+ throws NullArgumentException, DimensionMismatchException {
+ return ONE_WAY_ANANOVA.anovaFValue(categoryData);
+ }
+
+ /**
+ * @see org.apache.commons.math3.stat.inference.OneWayAnova#anovaPValue(Collection)
+ *
+ * @since 1.2
+ */
+ public static double oneWayAnovaPValue(final Collection<double[]> categoryData)
+ throws NullArgumentException, DimensionMismatchException,
+ ConvergenceException, MaxCountExceededException {
+ return ONE_WAY_ANANOVA.anovaPValue(categoryData);
+ }
+
+ /**
+ * @see org.apache.commons.math3.stat.inference.OneWayAnova#anovaTest(Collection,double)
+ *
+ * @since 1.2
+ */
+ public static boolean oneWayAnovaTest(final Collection<double[]> categoryData,
+ final double alpha)
+ throws NullArgumentException, DimensionMismatchException,
+ OutOfRangeException, ConvergenceException, MaxCountExceededException {
+ return ONE_WAY_ANANOVA.anovaTest(categoryData, alpha);
+ }
+
+ /**
+ * @see org.apache.commons.math3.stat.inference.GTest#g(double[], long[])
+ * @since 3.1
+ */
+ public static double g(final double[] expected, final long[] observed)
+ throws NotPositiveException, NotStrictlyPositiveException,
+ DimensionMismatchException {
+ return G_TEST.g(expected, observed);
+ }
+
+ /**
+ * @see org.apache.commons.math3.stat.inference.GTest#gTest( double[], long[] )
+ * @since 3.1
+ */
+ public static double gTest(final double[] expected, final long[] observed)
+ throws NotPositiveException, NotStrictlyPositiveException,
+ DimensionMismatchException, MaxCountExceededException {
+ return G_TEST.gTest(expected, observed);
+ }
+
+ /**
+ * @see org.apache.commons.math3.stat.inference.GTest#gTestIntrinsic(double[], long[] )
+ * @since 3.1
+ */
+ public static double gTestIntrinsic(final double[] expected, final long[] observed)
+ throws NotPositiveException, NotStrictlyPositiveException,
+ DimensionMismatchException, MaxCountExceededException {
+ return G_TEST.gTestIntrinsic(expected, observed);
+ }
+
+ /**
+ * @see org.apache.commons.math3.stat.inference.GTest#gTest( double[],long[],double)
+ * @since 3.1
+ */
+ public static boolean gTest(final double[] expected, final long[] observed,
+ final double alpha)
+ throws NotPositiveException, NotStrictlyPositiveException,
+ DimensionMismatchException, OutOfRangeException, MaxCountExceededException {
+ return G_TEST.gTest(expected, observed, alpha);
+ }
+
+ /**
+ * @see org.apache.commons.math3.stat.inference.GTest#gDataSetsComparison(long[], long[])
+ * @since 3.1
+ */
+ public static double gDataSetsComparison(final long[] observed1,
+ final long[] observed2)
+ throws DimensionMismatchException, NotPositiveException, ZeroException {
+ return G_TEST.gDataSetsComparison(observed1, observed2);
+ }
+
+ /**
+ * @see org.apache.commons.math3.stat.inference.GTest#rootLogLikelihoodRatio(long, long, long, long)
+ * @since 3.1
+ */
+ public static double rootLogLikelihoodRatio(final long k11, final long k12, final long k21, final long k22)
+ throws DimensionMismatchException, NotPositiveException, ZeroException {
+ return G_TEST.rootLogLikelihoodRatio(k11, k12, k21, k22);
+ }
+
+
+ /**
+ * @see org.apache.commons.math3.stat.inference.GTest#gTestDataSetsComparison(long[], long[])
+ * @since 3.1
+ */
+ public static double gTestDataSetsComparison(final long[] observed1,
+ final long[] observed2)
+ throws DimensionMismatchException, NotPositiveException, ZeroException,
+ MaxCountExceededException {
+ return G_TEST.gTestDataSetsComparison(observed1, observed2);
+ }
+
+ /**
+ * @see org.apache.commons.math3.stat.inference.GTest#gTestDataSetsComparison(long[],long[],double)
+ * @since 3.1
+ */
+ public static boolean gTestDataSetsComparison(final long[] observed1,
+ final long[] observed2,
+ final double alpha)
+ throws DimensionMismatchException, NotPositiveException,
+ ZeroException, OutOfRangeException, MaxCountExceededException {
+ return G_TEST.gTestDataSetsComparison(observed1, observed2, alpha);
+ }
+
+ /**
+ * @see org.apache.commons.math3.stat.inference.KolmogorovSmirnovTest#kolmogorovSmirnovStatistic(RealDistribution, double[])
+ * @since 3.3
+ */
+ public static double kolmogorovSmirnovStatistic(RealDistribution dist, double[] data)
+ throws InsufficientDataException, NullArgumentException {
+ return KS_TEST.kolmogorovSmirnovStatistic(dist, data);
+ }
+
+ /**
+ * @see org.apache.commons.math3.stat.inference.KolmogorovSmirnovTest#kolmogorovSmirnovTest(RealDistribution, double[])
+ * @since 3.3
+ */
+ public static double kolmogorovSmirnovTest(RealDistribution dist, double[] data)
+ throws InsufficientDataException, NullArgumentException {
+ return KS_TEST.kolmogorovSmirnovTest(dist, data);
+ }
+
+ /**
+ * @see org.apache.commons.math3.stat.inference.KolmogorovSmirnovTest#kolmogorovSmirnovTest(RealDistribution, double[], boolean)
+ * @since 3.3
+ */
+ public static double kolmogorovSmirnovTest(RealDistribution dist, double[] data, boolean strict)
+ throws InsufficientDataException, NullArgumentException {
+ return KS_TEST.kolmogorovSmirnovTest(dist, data, strict);
+ }
+
+ /**
+ * @see org.apache.commons.math3.stat.inference.KolmogorovSmirnovTest#kolmogorovSmirnovTest(RealDistribution, double[], double)
+ * @since 3.3
+ */
+ public static boolean kolmogorovSmirnovTest(RealDistribution dist, double[] data, double alpha)
+ throws InsufficientDataException, NullArgumentException {
+ return KS_TEST.kolmogorovSmirnovTest(dist, data, alpha);
+ }
+
+ /**
+ * @see org.apache.commons.math3.stat.inference.KolmogorovSmirnovTest#kolmogorovSmirnovStatistic(double[], double[])
+ * @since 3.3
+ */
+ public static double kolmogorovSmirnovStatistic(double[] x, double[] y)
+ throws InsufficientDataException, NullArgumentException {
+ return KS_TEST.kolmogorovSmirnovStatistic(x, y);
+ }
+
+ /**
+ * @see org.apache.commons.math3.stat.inference.KolmogorovSmirnovTest#kolmogorovSmirnovTest(double[], double[])
+ * @since 3.3
+ */
+ public static double kolmogorovSmirnovTest(double[] x, double[] y)
+ throws InsufficientDataException, NullArgumentException {
+ return KS_TEST.kolmogorovSmirnovTest(x, y);
+ }
+
+ /**
+ * @see org.apache.commons.math3.stat.inference.KolmogorovSmirnovTest#kolmogorovSmirnovTest(double[], double[], boolean)
+ * @since 3.3
+ */
+ public static double kolmogorovSmirnovTest(double[] x, double[] y, boolean strict)
+ throws InsufficientDataException, NullArgumentException {
+ return KS_TEST.kolmogorovSmirnovTest(x, y, strict);
+ }
+
+ /**
+ * @see org.apache.commons.math3.stat.inference.KolmogorovSmirnovTest#exactP(double, int, int, boolean)
+ * @since 3.3
+ */
+ public static double exactP(double d, int m, int n, boolean strict) {
+ return KS_TEST.exactP(d, n, m, strict);
+ }
+
+ /**
+ * @see org.apache.commons.math3.stat.inference.KolmogorovSmirnovTest#approximateP(double, int, int)
+ * @since 3.3
+ */
+ public static double approximateP(double d, int n, int m) {
+ return KS_TEST.approximateP(d, n, m);
+ }
+
+ /**
+ * @see org.apache.commons.math3.stat.inference.KolmogorovSmirnovTest#monteCarloP(double, int, int, boolean, int)
+ * @since 3.3
+ */
+ public static double monteCarloP(double d, int n, int m, boolean strict, int iterations) {
+ return KS_TEST.monteCarloP(d, n, m, strict, iterations);
+ }
+
+
+ // CHECKSTYLE: resume JavadocMethodCheck
+
+}
diff --git a/src/main/java/org/apache/commons/math3/stat/inference/WilcoxonSignedRankTest.java b/src/main/java/org/apache/commons/math3/stat/inference/WilcoxonSignedRankTest.java
new file mode 100644
index 0000000..bd4d7e2
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/stat/inference/WilcoxonSignedRankTest.java
@@ -0,0 +1,325 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math3.stat.inference;
+
+import org.apache.commons.math3.distribution.NormalDistribution;
+import org.apache.commons.math3.exception.ConvergenceException;
+import org.apache.commons.math3.exception.DimensionMismatchException;
+import org.apache.commons.math3.exception.MaxCountExceededException;
+import org.apache.commons.math3.exception.NoDataException;
+import org.apache.commons.math3.exception.NullArgumentException;
+import org.apache.commons.math3.exception.NumberIsTooLargeException;
+import org.apache.commons.math3.stat.ranking.NaNStrategy;
+import org.apache.commons.math3.stat.ranking.NaturalRanking;
+import org.apache.commons.math3.stat.ranking.TiesStrategy;
+import org.apache.commons.math3.util.FastMath;
+
+/**
+ * An implementation of the Wilcoxon signed-rank test.
+ *
+ */
+public class WilcoxonSignedRankTest {
+
+ /** Ranking algorithm. */
+ private NaturalRanking naturalRanking;
+
+ /**
+ * Create a test instance where NaN's are left in place and ties get
+ * the average of applicable ranks. Use this unless you are very sure
+ * of what you are doing.
+ */
+ public WilcoxonSignedRankTest() {
+ naturalRanking = new NaturalRanking(NaNStrategy.FIXED,
+ TiesStrategy.AVERAGE);
+ }
+
+ /**
+ * Create a test instance using the given strategies for NaN's and ties.
+ * Only use this if you are sure of what you are doing.
+ *
+ * @param nanStrategy
+ * specifies the strategy that should be used for Double.NaN's
+ * @param tiesStrategy
+ * specifies the strategy that should be used for ties
+ */
+ public WilcoxonSignedRankTest(final NaNStrategy nanStrategy,
+ final TiesStrategy tiesStrategy) {
+ naturalRanking = new NaturalRanking(nanStrategy, tiesStrategy);
+ }
+
+ /**
+ * Ensures that the provided arrays fulfills the assumptions.
+ *
+ * @param x first sample
+ * @param y second sample
+ * @throws NullArgumentException if {@code x} or {@code y} are {@code null}.
+ * @throws NoDataException if {@code x} or {@code y} are zero-length.
+ * @throws DimensionMismatchException if {@code x} and {@code y} do not
+ * have the same length.
+ */
+ private void ensureDataConformance(final double[] x, final double[] y)
+ throws NullArgumentException, NoDataException, DimensionMismatchException {
+
+ if (x == null ||
+ y == null) {
+ throw new NullArgumentException();
+ }
+ if (x.length == 0 ||
+ y.length == 0) {
+ throw new NoDataException();
+ }
+ if (y.length != x.length) {
+ throw new DimensionMismatchException(y.length, x.length);
+ }
+ }
+
+ /**
+ * Calculates y[i] - x[i] for all i
+ *
+ * @param x first sample
+ * @param y second sample
+ * @return z = y - x
+ */
+ private double[] calculateDifferences(final double[] x, final double[] y) {
+
+ final double[] z = new double[x.length];
+
+ for (int i = 0; i < x.length; ++i) {
+ z[i] = y[i] - x[i];
+ }
+
+ return z;
+ }
+
+ /**
+ * Calculates |z[i]| for all i
+ *
+ * @param z sample
+ * @return |z|
+ * @throws NullArgumentException if {@code z} is {@code null}
+ * @throws NoDataException if {@code z} is zero-length.
+ */
+ private double[] calculateAbsoluteDifferences(final double[] z)
+ throws NullArgumentException, NoDataException {
+
+ if (z == null) {
+ throw new NullArgumentException();
+ }
+
+ if (z.length == 0) {
+ throw new NoDataException();
+ }
+
+ final double[] zAbs = new double[z.length];
+
+ for (int i = 0; i < z.length; ++i) {
+ zAbs[i] = FastMath.abs(z[i]);
+ }
+
+ return zAbs;
+ }
+
+ /**
+ * Computes the <a
+ * href="http://en.wikipedia.org/wiki/Wilcoxon_signed-rank_test">
+ * Wilcoxon signed ranked statistic</a> comparing mean for two related
+ * samples or repeated measurements on a single sample.
+ * <p>
+ * This statistic can be used to perform a Wilcoxon signed ranked test
+ * evaluating the null hypothesis that the two related samples or repeated
+ * measurements on a single sample has equal mean.
+ * </p>
+ * <p>
+ * Let X<sub>i</sub> denote the i'th individual of the first sample and
+ * Y<sub>i</sub> the related i'th individual in the second sample. Let
+ * Z<sub>i</sub> = Y<sub>i</sub> - X<sub>i</sub>.
+ * </p>
+ * <p>
+ * <strong>Preconditions</strong>:
+ * <ul>
+ * <li>The differences Z<sub>i</sub> must be independent.</li>
+ * <li>Each Z<sub>i</sub> comes from a continuous population (they must be
+ * identical) and is symmetric about a common median.</li>
+ * <li>The values that X<sub>i</sub> and Y<sub>i</sub> represent are
+ * ordered, so the comparisons greater than, less than, and equal to are
+ * meaningful.</li>
+ * </ul>
+ * </p>
+ *
+ * @param x the first sample
+ * @param y the second sample
+ * @return wilcoxonSignedRank statistic (the larger of W+ and W-)
+ * @throws NullArgumentException if {@code x} or {@code y} are {@code null}.
+ * @throws NoDataException if {@code x} or {@code y} are zero-length.
+ * @throws DimensionMismatchException if {@code x} and {@code y} do not
+ * have the same length.
+ */
+ public double wilcoxonSignedRank(final double[] x, final double[] y)
+ throws NullArgumentException, NoDataException, DimensionMismatchException {
+
+ ensureDataConformance(x, y);
+
+ // throws IllegalArgumentException if x and y are not correctly
+ // specified
+ final double[] z = calculateDifferences(x, y);
+ final double[] zAbs = calculateAbsoluteDifferences(z);
+
+ final double[] ranks = naturalRanking.rank(zAbs);
+
+ double Wplus = 0;
+
+ for (int i = 0; i < z.length; ++i) {
+ if (z[i] > 0) {
+ Wplus += ranks[i];
+ }
+ }
+
+ final int N = x.length;
+ final double Wminus = (((double) (N * (N + 1))) / 2.0) - Wplus;
+
+ return FastMath.max(Wplus, Wminus);
+ }
+
+ /**
+ * Algorithm inspired by
+ * http://www.fon.hum.uva.nl/Service/Statistics/Signed_Rank_Algorihms.html#C
+ * by Rob van Son, Institute of Phonetic Sciences & IFOTT,
+ * University of Amsterdam
+ *
+ * @param Wmax largest Wilcoxon signed rank value
+ * @param N number of subjects (corresponding to x.length)
+ * @return two-sided exact p-value
+ */
+ private double calculateExactPValue(final double Wmax, final int N) {
+
+ // Total number of outcomes (equal to 2^N but a lot faster)
+ final int m = 1 << N;
+
+ int largerRankSums = 0;
+
+ for (int i = 0; i < m; ++i) {
+ int rankSum = 0;
+
+ // Generate all possible rank sums
+ for (int j = 0; j < N; ++j) {
+
+ // (i >> j) & 1 extract i's j-th bit from the right
+ if (((i >> j) & 1) == 1) {
+ rankSum += j + 1;
+ }
+ }
+
+ if (rankSum >= Wmax) {
+ ++largerRankSums;
+ }
+ }
+
+ /*
+ * largerRankSums / m gives the one-sided p-value, so it's multiplied
+ * with 2 to get the two-sided p-value
+ */
+ return 2 * ((double) largerRankSums) / ((double) m);
+ }
+
+ /**
+ * @param Wmin smallest Wilcoxon signed rank value
+ * @param N number of subjects (corresponding to x.length)
+ * @return two-sided asymptotic p-value
+ */
+ private double calculateAsymptoticPValue(final double Wmin, final int N) {
+
+ final double ES = (double) (N * (N + 1)) / 4.0;
+
+ /* Same as (but saves computations):
+ * final double VarW = ((double) (N * (N + 1) * (2*N + 1))) / 24;
+ */
+ final double VarS = ES * ((double) (2 * N + 1) / 6.0);
+
+ // - 0.5 is a continuity correction
+ final double z = (Wmin - ES - 0.5) / FastMath.sqrt(VarS);
+
+ // No try-catch or advertised exception because args are valid
+ // pass a null rng to avoid unneeded overhead as we will not sample from this distribution
+ final NormalDistribution standardNormal = new NormalDistribution(null, 0, 1);
+
+ return 2*standardNormal.cumulativeProbability(z);
+ }
+
+ /**
+ * Returns the <i>observed significance level</i>, or <a href=
+ * "http://www.cas.lancs.ac.uk/glossary_v1.1/hyptest.html#pvalue">
+ * p-value</a>, associated with a <a
+ * href="http://en.wikipedia.org/wiki/Wilcoxon_signed-rank_test">
+ * Wilcoxon signed ranked statistic</a> comparing mean for two related
+ * samples or repeated measurements on a single sample.
+ * <p>
+ * Let X<sub>i</sub> denote the i'th individual of the first sample and
+ * Y<sub>i</sub> the related i'th individual in the second sample. Let
+ * Z<sub>i</sub> = Y<sub>i</sub> - X<sub>i</sub>.
+ * </p>
+ * <p>
+ * <strong>Preconditions</strong>:
+ * <ul>
+ * <li>The differences Z<sub>i</sub> must be independent.</li>
+ * <li>Each Z<sub>i</sub> comes from a continuous population (they must be
+ * identical) and is symmetric about a common median.</li>
+ * <li>The values that X<sub>i</sub> and Y<sub>i</sub> represent are
+ * ordered, so the comparisons greater than, less than, and equal to are
+ * meaningful.</li>
+ * </ul>
+ * </p>
+ *
+ * @param x the first sample
+ * @param y the second sample
+ * @param exactPValue
+ * if the exact p-value is wanted (only works for x.length <= 30,
+ * if true and x.length > 30, this is ignored because
+ * calculations may take too long)
+ * @return p-value
+ * @throws NullArgumentException if {@code x} or {@code y} are {@code null}.
+ * @throws NoDataException if {@code x} or {@code y} are zero-length.
+ * @throws DimensionMismatchException if {@code x} and {@code y} do not
+ * have the same length.
+ * @throws NumberIsTooLargeException if {@code exactPValue} is {@code true}
+ * and {@code x.length} > 30
+ * @throws ConvergenceException if the p-value can not be computed due to
+ * a convergence error
+ * @throws MaxCountExceededException if the maximum number of iterations
+ * is exceeded
+ */
+ public double wilcoxonSignedRankTest(final double[] x, final double[] y,
+ final boolean exactPValue)
+ throws NullArgumentException, NoDataException, DimensionMismatchException,
+ NumberIsTooLargeException, ConvergenceException, MaxCountExceededException {
+
+ ensureDataConformance(x, y);
+
+ final int N = x.length;
+ final double Wmax = wilcoxonSignedRank(x, y);
+
+ if (exactPValue && N > 30) {
+ throw new NumberIsTooLargeException(N, 30, true);
+ }
+
+ if (exactPValue) {
+ return calculateExactPValue(Wmax, N);
+ } else {
+ final double Wmin = ( (double)(N*(N+1)) / 2.0 ) - Wmax;
+ return calculateAsymptoticPValue(Wmin, N);
+ }
+ }
+}
diff --git a/src/main/java/org/apache/commons/math3/stat/inference/package-info.java b/src/main/java/org/apache/commons/math3/stat/inference/package-info.java
new file mode 100644
index 0000000..a36a080
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/stat/inference/package-info.java
@@ -0,0 +1,22 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/**
+ *
+ * Classes providing hypothesis testing.
+ *
+ */
+package org.apache.commons.math3.stat.inference;
diff --git a/src/main/java/org/apache/commons/math3/stat/interval/AgrestiCoullInterval.java b/src/main/java/org/apache/commons/math3/stat/interval/AgrestiCoullInterval.java
new file mode 100644
index 0000000..b71c718
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/stat/interval/AgrestiCoullInterval.java
@@ -0,0 +1,48 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math3.stat.interval;
+
+import org.apache.commons.math3.distribution.NormalDistribution;
+import org.apache.commons.math3.util.FastMath;
+
+/**
+ * Implements the Agresti-Coull method for creating a binomial proportion confidence interval.
+ *
+ * @see <a
+ * href="http://en.wikipedia.org/wiki/Binomial_proportion_confidence_interval#Agresti-Coull_Interval">
+ * Agresti-Coull interval (Wikipedia)</a>
+ * @since 3.3
+ */
+public class AgrestiCoullInterval implements BinomialConfidenceInterval {
+
+ /** {@inheritDoc} */
+ public ConfidenceInterval createInterval(int numberOfTrials, int numberOfSuccesses, double confidenceLevel) {
+ IntervalUtils.checkParameters(numberOfTrials, numberOfSuccesses, confidenceLevel);
+ final double alpha = (1.0 - confidenceLevel) / 2;
+ final NormalDistribution normalDistribution = new NormalDistribution();
+ final double z = normalDistribution.inverseCumulativeProbability(1 - alpha);
+ final double zSquared = FastMath.pow(z, 2);
+ final double modifiedNumberOfTrials = numberOfTrials + zSquared;
+ final double modifiedSuccessesRatio = (1.0 / modifiedNumberOfTrials) * (numberOfSuccesses + 0.5 * zSquared);
+ final double difference = z *
+ FastMath.sqrt(1.0 / modifiedNumberOfTrials * modifiedSuccessesRatio *
+ (1 - modifiedSuccessesRatio));
+ return new ConfidenceInterval(modifiedSuccessesRatio - difference, modifiedSuccessesRatio + difference,
+ confidenceLevel);
+ }
+
+}
diff --git a/src/main/java/org/apache/commons/math3/stat/interval/BinomialConfidenceInterval.java b/src/main/java/org/apache/commons/math3/stat/interval/BinomialConfidenceInterval.java
new file mode 100644
index 0000000..532679a
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/stat/interval/BinomialConfidenceInterval.java
@@ -0,0 +1,62 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math3.stat.interval;
+
+import org.apache.commons.math3.exception.NumberIsTooLargeException;
+import org.apache.commons.math3.exception.OutOfRangeException;
+import org.apache.commons.math3.exception.NotPositiveException;
+import org.apache.commons.math3.exception.NotStrictlyPositiveException;
+
+/**
+ * Interface to generate confidence intervals for a binomial proportion.
+ *
+ * @see <a
+ * href="http://en.wikipedia.org/wiki/Binomial_proportion_confidence_interval">Binomial
+ * proportion confidence interval (Wikipedia)</a>
+ * @since 3.3
+ */
+public interface BinomialConfidenceInterval {
+
+ /**
+ * Create a confidence interval for the true probability of success
+ * of an unknown binomial distribution with the given observed number
+ * of trials, successes and confidence level.
+ * <p>
+ * Preconditions:
+ * <ul>
+ * <li>{@code numberOfTrials} must be positive</li>
+ * <li>{@code numberOfSuccesses} may not exceed {@code numberOfTrials}</li>
+ * <li>{@code confidenceLevel} must be strictly between 0 and 1 (exclusive)</li>
+ * </ul>
+ * </p>
+ *
+ * @param numberOfTrials number of trials
+ * @param numberOfSuccesses number of successes
+ * @param confidenceLevel desired probability that the true probability of
+ * success falls within the returned interval
+ * @return Confidence interval containing the probability of success with
+ * probability {@code confidenceLevel}
+ * @throws NotStrictlyPositiveException if {@code numberOfTrials <= 0}.
+ * @throws NotPositiveException if {@code numberOfSuccesses < 0}.
+ * @throws NumberIsTooLargeException if {@code numberOfSuccesses > numberOfTrials}.
+ * @throws OutOfRangeException if {@code confidenceLevel} is not in the interval {@code (0, 1)}.
+ */
+ ConfidenceInterval createInterval(int numberOfTrials, int numberOfSuccesses, double confidenceLevel)
+ throws NotStrictlyPositiveException, NotPositiveException,
+ NumberIsTooLargeException, OutOfRangeException;
+
+}
diff --git a/src/main/java/org/apache/commons/math3/stat/interval/ClopperPearsonInterval.java b/src/main/java/org/apache/commons/math3/stat/interval/ClopperPearsonInterval.java
new file mode 100644
index 0000000..34a0d57
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/stat/interval/ClopperPearsonInterval.java
@@ -0,0 +1,58 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math3.stat.interval;
+
+import org.apache.commons.math3.distribution.FDistribution;
+
+/**
+ * Implements the Clopper-Pearson method for creating a binomial proportion confidence interval.
+ *
+ * @see <a
+ * href="http://en.wikipedia.org/wiki/Binomial_proportion_confidence_interval#Clopper-Pearson_interval">
+ * Clopper-Pearson interval (Wikipedia)</a>
+ * @since 3.3
+ */
+public class ClopperPearsonInterval implements BinomialConfidenceInterval {
+
+ /** {@inheritDoc} */
+ public ConfidenceInterval createInterval(int numberOfTrials, int numberOfSuccesses,
+ double confidenceLevel) {
+ IntervalUtils.checkParameters(numberOfTrials, numberOfSuccesses, confidenceLevel);
+ double lowerBound = 0;
+ double upperBound = 0;
+ final double alpha = (1.0 - confidenceLevel) / 2.0;
+
+ final FDistribution distributionLowerBound = new FDistribution(2 * (numberOfTrials - numberOfSuccesses + 1),
+ 2 * numberOfSuccesses);
+ final double fValueLowerBound = distributionLowerBound.inverseCumulativeProbability(1 - alpha);
+ if (numberOfSuccesses > 0) {
+ lowerBound = numberOfSuccesses /
+ (numberOfSuccesses + (numberOfTrials - numberOfSuccesses + 1) * fValueLowerBound);
+ }
+
+ final FDistribution distributionUpperBound = new FDistribution(2 * (numberOfSuccesses + 1),
+ 2 * (numberOfTrials - numberOfSuccesses));
+ final double fValueUpperBound = distributionUpperBound.inverseCumulativeProbability(1 - alpha);
+ if (numberOfSuccesses > 0) {
+ upperBound = (numberOfSuccesses + 1) * fValueUpperBound /
+ (numberOfTrials - numberOfSuccesses + (numberOfSuccesses + 1) * fValueUpperBound);
+ }
+
+ return new ConfidenceInterval(lowerBound, upperBound, confidenceLevel);
+ }
+
+}
diff --git a/src/main/java/org/apache/commons/math3/stat/interval/ConfidenceInterval.java b/src/main/java/org/apache/commons/math3/stat/interval/ConfidenceInterval.java
new file mode 100644
index 0000000..0147c8c
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/stat/interval/ConfidenceInterval.java
@@ -0,0 +1,109 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math3.stat.interval;
+
+import org.apache.commons.math3.exception.MathIllegalArgumentException;
+import org.apache.commons.math3.exception.util.LocalizedFormats;
+
+/**
+ * Represents an interval estimate of a population parameter.
+ *
+ * @since 3.3
+ */
+public class ConfidenceInterval {
+
+ /** Lower endpoint of the interval */
+ private double lowerBound;
+
+ /** Upper endpoint of the interval */
+ private double upperBound;
+
+ /**
+ * The asserted probability that the interval contains the population
+ * parameter
+ */
+ private double confidenceLevel;
+
+ /**
+ * Create a confidence interval with the given bounds and confidence level.
+ * <p>
+ * Preconditions:
+ * <ul>
+ * <li>{@code lower} must be strictly less than {@code upper}</li>
+ * <li>{@code confidenceLevel} must be strictly between 0 and 1 (exclusive)</li>
+ * </ul>
+ * </p>
+ *
+ * @param lowerBound lower endpoint of the interval
+ * @param upperBound upper endpoint of the interval
+ * @param confidenceLevel coverage probability
+ * @throws MathIllegalArgumentException if the preconditions are not met
+ */
+ public ConfidenceInterval(double lowerBound, double upperBound, double confidenceLevel) {
+ checkParameters(lowerBound, upperBound, confidenceLevel);
+ this.lowerBound = lowerBound;
+ this.upperBound = upperBound;
+ this.confidenceLevel = confidenceLevel;
+ }
+
+ /**
+ * @return the lower endpoint of the interval
+ */
+ public double getLowerBound() {
+ return lowerBound;
+ }
+
+ /**
+ * @return the upper endpoint of the interval
+ */
+ public double getUpperBound() {
+ return upperBound;
+ }
+
+ /**
+ * @return the asserted probability that the interval contains the
+ * population parameter
+ */
+ public double getConfidenceLevel() {
+ return confidenceLevel;
+ }
+
+ /**
+ * @return String representation of the confidence interval
+ */
+ @Override
+ public String toString() {
+ return "[" + lowerBound + ";" + upperBound + "] (confidence level:" + confidenceLevel + ")";
+ }
+
+ /**
+ * Verifies that (lower, upper) is a valid non-empty interval and confidence
+ * is strictly between 0 and 1.
+ *
+ * @param lower lower endpoint
+ * @param upper upper endpoint
+ * @param confidence confidence level
+ */
+ private void checkParameters(double lower, double upper, double confidence) {
+ if (lower >= upper) {
+ throw new MathIllegalArgumentException(LocalizedFormats.LOWER_BOUND_NOT_BELOW_UPPER_BOUND, lower, upper);
+ }
+ if (confidence <= 0 || confidence >= 1) {
+ throw new MathIllegalArgumentException(LocalizedFormats.OUT_OF_BOUNDS_CONFIDENCE_LEVEL, confidence, 0, 1);
+ }
+ }
+}
diff --git a/src/main/java/org/apache/commons/math3/stat/interval/IntervalUtils.java b/src/main/java/org/apache/commons/math3/stat/interval/IntervalUtils.java
new file mode 100644
index 0000000..0613c99
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/stat/interval/IntervalUtils.java
@@ -0,0 +1,174 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math3.stat.interval;
+
+import org.apache.commons.math3.exception.NotPositiveException;
+import org.apache.commons.math3.exception.NotStrictlyPositiveException;
+import org.apache.commons.math3.exception.NumberIsTooLargeException;
+import org.apache.commons.math3.exception.OutOfRangeException;
+import org.apache.commons.math3.exception.util.LocalizedFormats;
+
+/**
+ * Factory methods to generate confidence intervals for a binomial proportion.
+ * The supported methods are:
+ * <ul>
+ * <li>Agresti-Coull interval</li>
+ * <li>Clopper-Pearson method (exact method)</li>
+ * <li>Normal approximation (based on central limit theorem)</li>
+ * <li>Wilson score interval</li>
+ * </ul>
+ *
+ * @since 3.3
+ */
+public final class IntervalUtils {
+
+ /** Singleton Agresti-Coull instance. */
+ private static final BinomialConfidenceInterval AGRESTI_COULL = new AgrestiCoullInterval();
+
+ /** Singleton Clopper-Pearson instance. */
+ private static final BinomialConfidenceInterval CLOPPER_PEARSON = new ClopperPearsonInterval();
+
+ /** Singleton NormalApproximation instance. */
+ private static final BinomialConfidenceInterval NORMAL_APPROXIMATION = new NormalApproximationInterval();
+
+ /** Singleton Wilson score instance. */
+ private static final BinomialConfidenceInterval WILSON_SCORE = new WilsonScoreInterval();
+
+ /**
+ * Prevent instantiation.
+ */
+ private IntervalUtils() {
+ }
+
+ /**
+ * Create an Agresti-Coull binomial confidence interval for the true
+ * probability of success of an unknown binomial distribution with the given
+ * observed number of trials, successes and confidence level.
+ *
+ * @param numberOfTrials number of trials
+ * @param numberOfSuccesses number of successes
+ * @param confidenceLevel desired probability that the true probability of
+ * success falls within the returned interval
+ * @return Confidence interval containing the probability of success with
+ * probability {@code confidenceLevel}
+ * @throws NotStrictlyPositiveException if {@code numberOfTrials <= 0}.
+ * @throws NotPositiveException if {@code numberOfSuccesses < 0}.
+ * @throws NumberIsTooLargeException if {@code numberOfSuccesses > numberOfTrials}.
+ * @throws OutOfRangeException if {@code confidenceLevel} is not in the interval {@code (0, 1)}.
+ */
+ public static ConfidenceInterval getAgrestiCoullInterval(int numberOfTrials, int numberOfSuccesses,
+ double confidenceLevel) {
+ return AGRESTI_COULL.createInterval(numberOfTrials, numberOfSuccesses, confidenceLevel);
+ }
+
+ /**
+ * Create a Clopper-Pearson binomial confidence interval for the true
+ * probability of success of an unknown binomial distribution with the given
+ * observed number of trials, successes and confidence level.
+ * <p>
+ * Preconditions:
+ * <ul>
+ * <li>{@code numberOfTrials} must be positive</li>
+ * <li>{@code numberOfSuccesses} may not exceed {@code numberOfTrials}</li>
+ * <li>{@code confidenceLevel} must be strictly between 0 and 1 (exclusive)</li>
+ * </ul>
+ * </p>
+ *
+ * @param numberOfTrials number of trials
+ * @param numberOfSuccesses number of successes
+ * @param confidenceLevel desired probability that the true probability of
+ * success falls within the returned interval
+ * @return Confidence interval containing the probability of success with
+ * probability {@code confidenceLevel}
+ * @throws NotStrictlyPositiveException if {@code numberOfTrials <= 0}.
+ * @throws NotPositiveException if {@code numberOfSuccesses < 0}.
+ * @throws NumberIsTooLargeException if {@code numberOfSuccesses > numberOfTrials}.
+ * @throws OutOfRangeException if {@code confidenceLevel} is not in the interval {@code (0, 1)}.
+ */
+ public static ConfidenceInterval getClopperPearsonInterval(int numberOfTrials, int numberOfSuccesses,
+ double confidenceLevel) {
+ return CLOPPER_PEARSON.createInterval(numberOfTrials, numberOfSuccesses, confidenceLevel);
+ }
+
+ /**
+ * Create a binomial confidence interval for the true probability of success
+ * of an unknown binomial distribution with the given observed number of
+ * trials, successes and confidence level using the Normal approximation to
+ * the binomial distribution.
+ *
+ * @param numberOfTrials number of trials
+ * @param numberOfSuccesses number of successes
+ * @param confidenceLevel desired probability that the true probability of
+ * success falls within the interval
+ * @return Confidence interval containing the probability of success with
+ * probability {@code confidenceLevel}
+ */
+ public static ConfidenceInterval getNormalApproximationInterval(int numberOfTrials, int numberOfSuccesses,
+ double confidenceLevel) {
+ return NORMAL_APPROXIMATION.createInterval(numberOfTrials, numberOfSuccesses, confidenceLevel);
+ }
+
+ /**
+ * Create a Wilson score binomial confidence interval for the true
+ * probability of success of an unknown binomial distribution with the given
+ * observed number of trials, successes and confidence level.
+ *
+ * @param numberOfTrials number of trials
+ * @param numberOfSuccesses number of successes
+ * @param confidenceLevel desired probability that the true probability of
+ * success falls within the returned interval
+ * @return Confidence interval containing the probability of success with
+ * probability {@code confidenceLevel}
+ * @throws NotStrictlyPositiveException if {@code numberOfTrials <= 0}.
+ * @throws NotPositiveException if {@code numberOfSuccesses < 0}.
+ * @throws NumberIsTooLargeException if {@code numberOfSuccesses > numberOfTrials}.
+ * @throws OutOfRangeException if {@code confidenceLevel} is not in the interval {@code (0, 1)}.
+ */
+ public static ConfidenceInterval getWilsonScoreInterval(int numberOfTrials, int numberOfSuccesses,
+ double confidenceLevel) {
+ return WILSON_SCORE.createInterval(numberOfTrials, numberOfSuccesses, confidenceLevel);
+ }
+
+ /**
+ * Verifies that parameters satisfy preconditions.
+ *
+ * @param numberOfTrials number of trials (must be positive)
+ * @param numberOfSuccesses number of successes (must not exceed numberOfTrials)
+ * @param confidenceLevel confidence level (must be strictly between 0 and 1)
+ * @throws NotStrictlyPositiveException if {@code numberOfTrials <= 0}.
+ * @throws NotPositiveException if {@code numberOfSuccesses < 0}.
+ * @throws NumberIsTooLargeException if {@code numberOfSuccesses > numberOfTrials}.
+ * @throws OutOfRangeException if {@code confidenceLevel} is not in the interval {@code (0, 1)}.
+ */
+ static void checkParameters(int numberOfTrials, int numberOfSuccesses, double confidenceLevel) {
+ if (numberOfTrials <= 0) {
+ throw new NotStrictlyPositiveException(LocalizedFormats.NUMBER_OF_TRIALS, numberOfTrials);
+ }
+ if (numberOfSuccesses < 0) {
+ throw new NotPositiveException(LocalizedFormats.NEGATIVE_NUMBER_OF_SUCCESSES, numberOfSuccesses);
+ }
+ if (numberOfSuccesses > numberOfTrials) {
+ throw new NumberIsTooLargeException(LocalizedFormats.NUMBER_OF_SUCCESS_LARGER_THAN_POPULATION_SIZE,
+ numberOfSuccesses, numberOfTrials, true);
+ }
+ if (confidenceLevel <= 0 || confidenceLevel >= 1) {
+ throw new OutOfRangeException(LocalizedFormats.OUT_OF_BOUNDS_CONFIDENCE_LEVEL,
+ confidenceLevel, 0, 1);
+ }
+ }
+
+}
diff --git a/src/main/java/org/apache/commons/math3/stat/interval/NormalApproximationInterval.java b/src/main/java/org/apache/commons/math3/stat/interval/NormalApproximationInterval.java
new file mode 100644
index 0000000..25a213a
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/stat/interval/NormalApproximationInterval.java
@@ -0,0 +1,44 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math3.stat.interval;
+
+import org.apache.commons.math3.distribution.NormalDistribution;
+import org.apache.commons.math3.util.FastMath;
+
+/**
+ * Implements the normal approximation method for creating a binomial proportion confidence interval.
+ *
+ * @see <a
+ * href="http://en.wikipedia.org/wiki/Binomial_proportion_confidence_interval#Normal_approximation_interval">
+ * Normal approximation interval (Wikipedia)</a>
+ * @since 3.3
+ */
+public class NormalApproximationInterval implements BinomialConfidenceInterval {
+
+ /** {@inheritDoc} */
+ public ConfidenceInterval createInterval(int numberOfTrials, int numberOfSuccesses,
+ double confidenceLevel) {
+ IntervalUtils.checkParameters(numberOfTrials, numberOfSuccesses, confidenceLevel);
+ final double mean = (double) numberOfSuccesses / (double) numberOfTrials;
+ final double alpha = (1.0 - confidenceLevel) / 2;
+ final NormalDistribution normalDistribution = new NormalDistribution();
+ final double difference = normalDistribution.inverseCumulativeProbability(1 - alpha) *
+ FastMath.sqrt(1.0 / numberOfTrials * mean * (1 - mean));
+ return new ConfidenceInterval(mean - difference, mean + difference, confidenceLevel);
+ }
+
+}
diff --git a/src/main/java/org/apache/commons/math3/stat/interval/WilsonScoreInterval.java b/src/main/java/org/apache/commons/math3/stat/interval/WilsonScoreInterval.java
new file mode 100644
index 0000000..9932835
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/stat/interval/WilsonScoreInterval.java
@@ -0,0 +1,52 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math3.stat.interval;
+
+import org.apache.commons.math3.distribution.NormalDistribution;
+import org.apache.commons.math3.util.FastMath;
+
+/**
+ * Implements the Wilson score method for creating a binomial proportion confidence interval.
+ *
+ * @see <a
+ * href="http://en.wikipedia.org/wiki/Binomial_proportion_confidence_interval#Wilson_score_interval">
+ * Wilson score interval (Wikipedia)</a>
+ * @since 3.3
+ */
+public class WilsonScoreInterval implements BinomialConfidenceInterval {
+
+ /** {@inheritDoc} */
+ public ConfidenceInterval createInterval(int numberOfTrials, int numberOfSuccesses, double confidenceLevel) {
+ IntervalUtils.checkParameters(numberOfTrials, numberOfSuccesses, confidenceLevel);
+ final double alpha = (1.0 - confidenceLevel) / 2;
+ final NormalDistribution normalDistribution = new NormalDistribution();
+ final double z = normalDistribution.inverseCumulativeProbability(1 - alpha);
+ final double zSquared = FastMath.pow(z, 2);
+ final double mean = (double) numberOfSuccesses / (double) numberOfTrials;
+
+ final double factor = 1.0 / (1 + (1.0 / numberOfTrials) * zSquared);
+ final double modifiedSuccessRatio = mean + (1.0 / (2 * numberOfTrials)) * zSquared;
+ final double difference = z *
+ FastMath.sqrt(1.0 / numberOfTrials * mean * (1 - mean) +
+ (1.0 / (4 * FastMath.pow(numberOfTrials, 2)) * zSquared));
+
+ final double lowerBound = factor * (modifiedSuccessRatio - difference);
+ final double upperBound = factor * (modifiedSuccessRatio + difference);
+ return new ConfidenceInterval(lowerBound, upperBound, confidenceLevel);
+ }
+
+}
diff --git a/src/main/java/org/apache/commons/math3/stat/interval/package-info.java b/src/main/java/org/apache/commons/math3/stat/interval/package-info.java
new file mode 100644
index 0000000..34f43d9
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/stat/interval/package-info.java
@@ -0,0 +1,22 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/**
+ *
+ * Classes providing binomial proportion confidence interval construction.
+ *
+ */
+package org.apache.commons.math3.stat.interval;
diff --git a/src/main/java/org/apache/commons/math3/stat/package-info.java b/src/main/java/org/apache/commons/math3/stat/package-info.java
new file mode 100644
index 0000000..1df9698
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/stat/package-info.java
@@ -0,0 +1,18 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/** Data storage, manipulation and summary routines. */
+package org.apache.commons.math3.stat;
diff --git a/src/main/java/org/apache/commons/math3/stat/ranking/NaNStrategy.java b/src/main/java/org/apache/commons/math3/stat/ranking/NaNStrategy.java
new file mode 100644
index 0000000..1a916ef
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/stat/ranking/NaNStrategy.java
@@ -0,0 +1,55 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math3.stat.ranking;
+
+/**
+ * Strategies for handling NaN values in rank transformations.
+ * <ul>
+ * <li>MINIMAL - NaNs are treated as minimal in the ordering, equivalent to
+ * (that is, tied with) <code>Double.NEGATIVE_INFINITY</code>.</li>
+ * <li>MAXIMAL - NaNs are treated as maximal in the ordering, equivalent to
+ * <code>Double.POSITIVE_INFINITY</code></li>
+ * <li>REMOVED - NaNs are removed before the rank transform is applied</li>
+ * <li>FIXED - NaNs are left "in place," that is the rank transformation is
+ * applied to the other elements in the input array, but the NaN elements
+ * are returned unchanged.</li>
+ * <li>FAILED - If any NaN is encountered in the input array, an appropriate
+ * exception is thrown</li>
+ * </ul>
+ *
+ * @since 2.0
+ */
+public enum NaNStrategy {
+
+ /** NaNs are considered minimal in the ordering */
+ MINIMAL,
+
+ /** NaNs are considered maximal in the ordering */
+ MAXIMAL,
+
+ /** NaNs are removed before computing ranks */
+ REMOVED,
+
+ /** NaNs are left in place */
+ FIXED,
+
+ /** NaNs result in an exception
+ * @since 3.1
+ */
+ FAILED
+}
diff --git a/src/main/java/org/apache/commons/math3/stat/ranking/NaturalRanking.java b/src/main/java/org/apache/commons/math3/stat/ranking/NaturalRanking.java
new file mode 100644
index 0000000..6107c46
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/stat/ranking/NaturalRanking.java
@@ -0,0 +1,474 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math3.stat.ranking;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Iterator;
+import java.util.List;
+
+import org.apache.commons.math3.exception.MathInternalError;
+import org.apache.commons.math3.exception.NotANumberException;
+import org.apache.commons.math3.random.RandomDataGenerator;
+import org.apache.commons.math3.random.RandomGenerator;
+import org.apache.commons.math3.util.FastMath;
+
+
+/**
+ * <p> Ranking based on the natural ordering on doubles.</p>
+ * <p>NaNs are treated according to the configured {@link NaNStrategy} and ties
+ * are handled using the selected {@link TiesStrategy}.
+ * Configuration settings are supplied in optional constructor arguments.
+ * Defaults are {@link NaNStrategy#FAILED} and {@link TiesStrategy#AVERAGE},
+ * respectively. When using {@link TiesStrategy#RANDOM}, a
+ * {@link RandomGenerator} may be supplied as a constructor argument.</p>
+ * <p>Examples:
+ * <table border="1" cellpadding="3">
+ * <tr><th colspan="3">
+ * Input data: (20, 17, 30, 42.3, 17, 50, Double.NaN, Double.NEGATIVE_INFINITY, 17)
+ * </th></tr>
+ * <tr><th>NaNStrategy</th><th>TiesStrategy</th>
+ * <th><code>rank(data)</code></th>
+ * <tr>
+ * <td>default (NaNs maximal)</td>
+ * <td>default (ties averaged)</td>
+ * <td>(5, 3, 6, 7, 3, 8, 9, 1, 3)</td></tr>
+ * <tr>
+ * <td>default (NaNs maximal)</td>
+ * <td>MINIMUM</td>
+ * <td>(5, 2, 6, 7, 2, 8, 9, 1, 2)</td></tr>
+ * <tr>
+ * <td>MINIMAL</td>
+ * <td>default (ties averaged)</td>
+ * <td>(6, 4, 7, 8, 4, 9, 1.5, 1.5, 4)</td></tr>
+ * <tr>
+ * <td>REMOVED</td>
+ * <td>SEQUENTIAL</td>
+ * <td>(5, 2, 6, 7, 3, 8, 1, 4)</td></tr>
+ * <tr>
+ * <td>MINIMAL</td>
+ * <td>MAXIMUM</td>
+ * <td>(6, 5, 7, 8, 5, 9, 2, 2, 5)</td></tr></table></p>
+ *
+ * @since 2.0
+ */
+public class NaturalRanking implements RankingAlgorithm {
+
+ /** default NaN strategy */
+ public static final NaNStrategy DEFAULT_NAN_STRATEGY = NaNStrategy.FAILED;
+
+ /** default ties strategy */
+ public static final TiesStrategy DEFAULT_TIES_STRATEGY = TiesStrategy.AVERAGE;
+
+ /** NaN strategy - defaults to NaNs maximal */
+ private final NaNStrategy nanStrategy;
+
+ /** Ties strategy - defaults to ties averaged */
+ private final TiesStrategy tiesStrategy;
+
+ /** Source of random data - used only when ties strategy is RANDOM */
+ private final RandomDataGenerator randomData;
+
+ /**
+ * Create a NaturalRanking with default strategies for handling ties and NaNs.
+ */
+ public NaturalRanking() {
+ super();
+ tiesStrategy = DEFAULT_TIES_STRATEGY;
+ nanStrategy = DEFAULT_NAN_STRATEGY;
+ randomData = null;
+ }
+
+ /**
+ * Create a NaturalRanking with the given TiesStrategy.
+ *
+ * @param tiesStrategy the TiesStrategy to use
+ */
+ public NaturalRanking(TiesStrategy tiesStrategy) {
+ super();
+ this.tiesStrategy = tiesStrategy;
+ nanStrategy = DEFAULT_NAN_STRATEGY;
+ randomData = new RandomDataGenerator();
+ }
+
+ /**
+ * Create a NaturalRanking with the given NaNStrategy.
+ *
+ * @param nanStrategy the NaNStrategy to use
+ */
+ public NaturalRanking(NaNStrategy nanStrategy) {
+ super();
+ this.nanStrategy = nanStrategy;
+ tiesStrategy = DEFAULT_TIES_STRATEGY;
+ randomData = null;
+ }
+
+ /**
+ * Create a NaturalRanking with the given NaNStrategy and TiesStrategy.
+ *
+ * @param nanStrategy NaNStrategy to use
+ * @param tiesStrategy TiesStrategy to use
+ */
+ public NaturalRanking(NaNStrategy nanStrategy, TiesStrategy tiesStrategy) {
+ super();
+ this.nanStrategy = nanStrategy;
+ this.tiesStrategy = tiesStrategy;
+ randomData = new RandomDataGenerator();
+ }
+
+ /**
+ * Create a NaturalRanking with TiesStrategy.RANDOM and the given
+ * RandomGenerator as the source of random data.
+ *
+ * @param randomGenerator source of random data
+ */
+ public NaturalRanking(RandomGenerator randomGenerator) {
+ super();
+ this.tiesStrategy = TiesStrategy.RANDOM;
+ nanStrategy = DEFAULT_NAN_STRATEGY;
+ randomData = new RandomDataGenerator(randomGenerator);
+ }
+
+
+ /**
+ * Create a NaturalRanking with the given NaNStrategy, TiesStrategy.RANDOM
+ * and the given source of random data.
+ *
+ * @param nanStrategy NaNStrategy to use
+ * @param randomGenerator source of random data
+ */
+ public NaturalRanking(NaNStrategy nanStrategy,
+ RandomGenerator randomGenerator) {
+ super();
+ this.nanStrategy = nanStrategy;
+ this.tiesStrategy = TiesStrategy.RANDOM;
+ randomData = new RandomDataGenerator(randomGenerator);
+ }
+
+ /**
+ * Return the NaNStrategy
+ *
+ * @return returns the NaNStrategy
+ */
+ public NaNStrategy getNanStrategy() {
+ return nanStrategy;
+ }
+
+ /**
+ * Return the TiesStrategy
+ *
+ * @return the TiesStrategy
+ */
+ public TiesStrategy getTiesStrategy() {
+ return tiesStrategy;
+ }
+
+ /**
+ * Rank <code>data</code> using the natural ordering on Doubles, with
+ * NaN values handled according to <code>nanStrategy</code> and ties
+ * resolved using <code>tiesStrategy.</code>
+ *
+ * @param data array to be ranked
+ * @return array of ranks
+ * @throws NotANumberException if the selected {@link NaNStrategy} is {@code FAILED}
+ * and a {@link Double#NaN} is encountered in the input data
+ */
+ public double[] rank(double[] data) {
+
+ // Array recording initial positions of data to be ranked
+ IntDoublePair[] ranks = new IntDoublePair[data.length];
+ for (int i = 0; i < data.length; i++) {
+ ranks[i] = new IntDoublePair(data[i], i);
+ }
+
+ // Recode, remove or record positions of NaNs
+ List<Integer> nanPositions = null;
+ switch (nanStrategy) {
+ case MAXIMAL: // Replace NaNs with +INFs
+ recodeNaNs(ranks, Double.POSITIVE_INFINITY);
+ break;
+ case MINIMAL: // Replace NaNs with -INFs
+ recodeNaNs(ranks, Double.NEGATIVE_INFINITY);
+ break;
+ case REMOVED: // Drop NaNs from data
+ ranks = removeNaNs(ranks);
+ break;
+ case FIXED: // Record positions of NaNs
+ nanPositions = getNanPositions(ranks);
+ break;
+ case FAILED:
+ nanPositions = getNanPositions(ranks);
+ if (nanPositions.size() > 0) {
+ throw new NotANumberException();
+ }
+ break;
+ default: // this should not happen unless NaNStrategy enum is changed
+ throw new MathInternalError();
+ }
+
+ // Sort the IntDoublePairs
+ Arrays.sort(ranks);
+
+ // Walk the sorted array, filling output array using sorted positions,
+ // resolving ties as we go
+ double[] out = new double[ranks.length];
+ int pos = 1; // position in sorted array
+ out[ranks[0].getPosition()] = pos;
+ List<Integer> tiesTrace = new ArrayList<Integer>();
+ tiesTrace.add(ranks[0].getPosition());
+ for (int i = 1; i < ranks.length; i++) {
+ if (Double.compare(ranks[i].getValue(), ranks[i - 1].getValue()) > 0) {
+ // tie sequence has ended (or had length 1)
+ pos = i + 1;
+ if (tiesTrace.size() > 1) { // if seq is nontrivial, resolve
+ resolveTie(out, tiesTrace);
+ }
+ tiesTrace = new ArrayList<Integer>();
+ tiesTrace.add(ranks[i].getPosition());
+ } else {
+ // tie sequence continues
+ tiesTrace.add(ranks[i].getPosition());
+ }
+ out[ranks[i].getPosition()] = pos;
+ }
+ if (tiesTrace.size() > 1) { // handle tie sequence at end
+ resolveTie(out, tiesTrace);
+ }
+ if (nanStrategy == NaNStrategy.FIXED) {
+ restoreNaNs(out, nanPositions);
+ }
+ return out;
+ }
+
+ /**
+ * Returns an array that is a copy of the input array with IntDoublePairs
+ * having NaN values removed.
+ *
+ * @param ranks input array
+ * @return array with NaN-valued entries removed
+ */
+ private IntDoublePair[] removeNaNs(IntDoublePair[] ranks) {
+ if (!containsNaNs(ranks)) {
+ return ranks;
+ }
+ IntDoublePair[] outRanks = new IntDoublePair[ranks.length];
+ int j = 0;
+ for (int i = 0; i < ranks.length; i++) {
+ if (Double.isNaN(ranks[i].getValue())) {
+ // drop, but adjust original ranks of later elements
+ for (int k = i + 1; k < ranks.length; k++) {
+ ranks[k] = new IntDoublePair(
+ ranks[k].getValue(), ranks[k].getPosition() - 1);
+ }
+ } else {
+ outRanks[j] = new IntDoublePair(
+ ranks[i].getValue(), ranks[i].getPosition());
+ j++;
+ }
+ }
+ IntDoublePair[] returnRanks = new IntDoublePair[j];
+ System.arraycopy(outRanks, 0, returnRanks, 0, j);
+ return returnRanks;
+ }
+
+ /**
+ * Recodes NaN values to the given value.
+ *
+ * @param ranks array to recode
+ * @param value the value to replace NaNs with
+ */
+ private void recodeNaNs(IntDoublePair[] ranks, double value) {
+ for (int i = 0; i < ranks.length; i++) {
+ if (Double.isNaN(ranks[i].getValue())) {
+ ranks[i] = new IntDoublePair(
+ value, ranks[i].getPosition());
+ }
+ }
+ }
+
+ /**
+ * Checks for presence of NaNs in <code>ranks.</code>
+ *
+ * @param ranks array to be searched for NaNs
+ * @return true iff ranks contains one or more NaNs
+ */
+ private boolean containsNaNs(IntDoublePair[] ranks) {
+ for (int i = 0; i < ranks.length; i++) {
+ if (Double.isNaN(ranks[i].getValue())) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Resolve a sequence of ties, using the configured {@link TiesStrategy}.
+ * The input <code>ranks</code> array is expected to take the same value
+ * for all indices in <code>tiesTrace</code>. The common value is recoded
+ * according to the tiesStrategy. For example, if ranks = <5,8,2,6,2,7,1,2>,
+ * tiesTrace = <2,4,7> and tiesStrategy is MINIMUM, ranks will be unchanged.
+ * The same array and trace with tiesStrategy AVERAGE will come out
+ * <5,8,3,6,3,7,1,3>.
+ *
+ * @param ranks array of ranks
+ * @param tiesTrace list of indices where <code>ranks</code> is constant
+ * -- that is, for any i and j in TiesTrace, <code> ranks[i] == ranks[j]
+ * </code>
+ */
+ private void resolveTie(double[] ranks, List<Integer> tiesTrace) {
+
+ // constant value of ranks over tiesTrace
+ final double c = ranks[tiesTrace.get(0)];
+
+ // length of sequence of tied ranks
+ final int length = tiesTrace.size();
+
+ switch (tiesStrategy) {
+ case AVERAGE: // Replace ranks with average
+ fill(ranks, tiesTrace, (2 * c + length - 1) / 2d);
+ break;
+ case MAXIMUM: // Replace ranks with maximum values
+ fill(ranks, tiesTrace, c + length - 1);
+ break;
+ case MINIMUM: // Replace ties with minimum
+ fill(ranks, tiesTrace, c);
+ break;
+ case RANDOM: // Fill with random integral values in [c, c + length - 1]
+ Iterator<Integer> iterator = tiesTrace.iterator();
+ long f = FastMath.round(c);
+ while (iterator.hasNext()) {
+ // No advertised exception because args are guaranteed valid
+ ranks[iterator.next()] =
+ randomData.nextLong(f, f + length - 1);
+ }
+ break;
+ case SEQUENTIAL: // Fill sequentially from c to c + length - 1
+ // walk and fill
+ iterator = tiesTrace.iterator();
+ f = FastMath.round(c);
+ int i = 0;
+ while (iterator.hasNext()) {
+ ranks[iterator.next()] = f + i++;
+ }
+ break;
+ default: // this should not happen unless TiesStrategy enum is changed
+ throw new MathInternalError();
+ }
+ }
+
+ /**
+ * Sets<code>data[i] = value</code> for each i in <code>tiesTrace.</code>
+ *
+ * @param data array to modify
+ * @param tiesTrace list of index values to set
+ * @param value value to set
+ */
+ private void fill(double[] data, List<Integer> tiesTrace, double value) {
+ Iterator<Integer> iterator = tiesTrace.iterator();
+ while (iterator.hasNext()) {
+ data[iterator.next()] = value;
+ }
+ }
+
+ /**
+ * Set <code>ranks[i] = Double.NaN</code> for each i in <code>nanPositions.</code>
+ *
+ * @param ranks array to modify
+ * @param nanPositions list of index values to set to <code>Double.NaN</code>
+ */
+ private void restoreNaNs(double[] ranks, List<Integer> nanPositions) {
+ if (nanPositions.size() == 0) {
+ return;
+ }
+ Iterator<Integer> iterator = nanPositions.iterator();
+ while (iterator.hasNext()) {
+ ranks[iterator.next().intValue()] = Double.NaN;
+ }
+
+ }
+
+ /**
+ * Returns a list of indexes where <code>ranks</code> is <code>NaN.</code>
+ *
+ * @param ranks array to search for <code>NaNs</code>
+ * @return list of indexes i such that <code>ranks[i] = NaN</code>
+ */
+ private List<Integer> getNanPositions(IntDoublePair[] ranks) {
+ ArrayList<Integer> out = new ArrayList<Integer>();
+ for (int i = 0; i < ranks.length; i++) {
+ if (Double.isNaN(ranks[i].getValue())) {
+ out.add(Integer.valueOf(i));
+ }
+ }
+ return out;
+ }
+
+ /**
+ * Represents the position of a double value in an ordering.
+ * Comparable interface is implemented so Arrays.sort can be used
+ * to sort an array of IntDoublePairs by value. Note that the
+ * implicitly defined natural ordering is NOT consistent with equals.
+ */
+ private static class IntDoublePair implements Comparable<IntDoublePair> {
+
+ /** Value of the pair */
+ private final double value;
+
+ /** Original position of the pair */
+ private final int position;
+
+ /**
+ * Construct an IntDoublePair with the given value and position.
+ * @param value the value of the pair
+ * @param position the original position
+ */
+ IntDoublePair(double value, int position) {
+ this.value = value;
+ this.position = position;
+ }
+
+ /**
+ * Compare this IntDoublePair to another pair.
+ * Only the <strong>values</strong> are compared.
+ *
+ * @param other the other pair to compare this to
+ * @return result of <code>Double.compare(value, other.value)</code>
+ */
+ public int compareTo(IntDoublePair other) {
+ return Double.compare(value, other.value);
+ }
+
+ // N.B. equals() and hashCode() are not implemented; see MATH-610 for discussion.
+
+ /**
+ * Returns the value of the pair.
+ * @return value
+ */
+ public double getValue() {
+ return value;
+ }
+
+ /**
+ * Returns the original position of the pair.
+ * @return position
+ */
+ public int getPosition() {
+ return position;
+ }
+ }
+}
diff --git a/src/main/java/org/apache/commons/math3/stat/ranking/RankingAlgorithm.java b/src/main/java/org/apache/commons/math3/stat/ranking/RankingAlgorithm.java
new file mode 100644
index 0000000..188bc99
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/stat/ranking/RankingAlgorithm.java
@@ -0,0 +1,40 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math3.stat.ranking;
+
+/**
+ * Interface representing a rank transformation.
+ *
+ * @since 2.0
+ */
+public interface RankingAlgorithm {
+ /**
+ * <p>Performs a rank transformation on the input data, returning an array
+ * of ranks.</p>
+ *
+ * <p>Ranks should be 1-based - that is, the smallest value
+ * returned in an array of ranks should be greater than or equal to one,
+ * rather than 0. Ranks should in general take integer values, though
+ * implementations may return averages or other floating point values
+ * to resolve ties in the input data.</p>
+ *
+ * @param data array of data to be ranked
+ * @return an array of ranks corresponding to the elements of the input array
+ */
+ double[] rank (double[] data);
+}
diff --git a/src/main/java/org/apache/commons/math3/stat/ranking/TiesStrategy.java b/src/main/java/org/apache/commons/math3/stat/ranking/TiesStrategy.java
new file mode 100644
index 0000000..08ab99a
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/stat/ranking/TiesStrategy.java
@@ -0,0 +1,54 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math3.stat.ranking;
+
+/**
+ * Strategies for handling tied values in rank transformations.
+ * <ul>
+ * <li>SEQUENTIAL - Ties are assigned ranks in order of occurrence in the original array,
+ * for example (1,3,4,3) is ranked as (1,2,4,3)</li>
+ * <li>MINIMUM - Tied values are assigned the minimum applicable rank, or the rank
+ * of the first occurrence. For example, (1,3,4,3) is ranked as (1,2,4,2)</li>
+ * <li>MAXIMUM - Tied values are assigned the maximum applicable rank, or the rank
+ * of the last occurrence. For example, (1,3,4,3) is ranked as (1,3,4,3)</li>
+ * <li>AVERAGE - Tied values are assigned the average of the applicable ranks.
+ * For example, (1,3,4,3) is ranked as (1,2.5,4,2.5)</li>
+ * <li>RANDOM - Tied values are assigned a random integer rank from among the
+ * applicable values. The assigned rank will always be an integer, (inclusively)
+ * between the values returned by the MINIMUM and MAXIMUM strategies.</li>
+ * </ul>
+ *
+ * @since 2.0
+ */
+public enum TiesStrategy {
+
+ /** Ties assigned sequential ranks in order of occurrence */
+ SEQUENTIAL,
+
+ /** Ties get the minimum applicable rank */
+ MINIMUM,
+
+ /** Ties get the maximum applicable rank */
+ MAXIMUM,
+
+ /** Ties get the average of applicable ranks */
+ AVERAGE,
+
+ /** Ties get a random integral value from among applicable ranks */
+ RANDOM
+}
diff --git a/src/main/java/org/apache/commons/math3/stat/ranking/package-info.java b/src/main/java/org/apache/commons/math3/stat/ranking/package-info.java
new file mode 100644
index 0000000..b86575b
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/stat/ranking/package-info.java
@@ -0,0 +1,22 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/**
+ *
+ * Classes providing rank transformations.
+ *
+ */
+package org.apache.commons.math3.stat.ranking;
diff --git a/src/main/java/org/apache/commons/math3/stat/regression/AbstractMultipleLinearRegression.java b/src/main/java/org/apache/commons/math3/stat/regression/AbstractMultipleLinearRegression.java
new file mode 100644
index 0000000..9b7c40a
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/stat/regression/AbstractMultipleLinearRegression.java
@@ -0,0 +1,383 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math3.stat.regression;
+
+import org.apache.commons.math3.exception.DimensionMismatchException;
+import org.apache.commons.math3.exception.InsufficientDataException;
+import org.apache.commons.math3.exception.MathIllegalArgumentException;
+import org.apache.commons.math3.exception.NoDataException;
+import org.apache.commons.math3.exception.NullArgumentException;
+import org.apache.commons.math3.exception.util.LocalizedFormats;
+import org.apache.commons.math3.linear.NonSquareMatrixException;
+import org.apache.commons.math3.linear.RealMatrix;
+import org.apache.commons.math3.linear.Array2DRowRealMatrix;
+import org.apache.commons.math3.linear.RealVector;
+import org.apache.commons.math3.linear.ArrayRealVector;
+import org.apache.commons.math3.stat.descriptive.moment.Variance;
+import org.apache.commons.math3.util.FastMath;
+
+/**
+ * Abstract base class for implementations of MultipleLinearRegression.
+ * @since 2.0
+ */
+public abstract class AbstractMultipleLinearRegression implements
+ MultipleLinearRegression {
+
+ /** X sample data. */
+ private RealMatrix xMatrix;
+
+ /** Y sample data. */
+ private RealVector yVector;
+
+ /** Whether or not the regression model includes an intercept. True means no intercept. */
+ private boolean noIntercept = false;
+
+ /**
+ * @return the X sample data.
+ */
+ protected RealMatrix getX() {
+ return xMatrix;
+ }
+
+ /**
+ * @return the Y sample data.
+ */
+ protected RealVector getY() {
+ return yVector;
+ }
+
+ /**
+ * @return true if the model has no intercept term; false otherwise
+ * @since 2.2
+ */
+ public boolean isNoIntercept() {
+ return noIntercept;
+ }
+
+ /**
+ * @param noIntercept true means the model is to be estimated without an intercept term
+ * @since 2.2
+ */
+ public void setNoIntercept(boolean noIntercept) {
+ this.noIntercept = noIntercept;
+ }
+
+ /**
+ * <p>Loads model x and y sample data from a flat input array, overriding any previous sample.
+ * </p>
+ * <p>Assumes that rows are concatenated with y values first in each row. For example, an input
+ * <code>data</code> array containing the sequence of values (1, 2, 3, 4, 5, 6, 7, 8, 9) with
+ * <code>nobs = 3</code> and <code>nvars = 2</code> creates a regression dataset with two
+ * independent variables, as below:
+ * <pre>
+ * y x[0] x[1]
+ * --------------
+ * 1 2 3
+ * 4 5 6
+ * 7 8 9
+ * </pre>
+ * </p>
+ * <p>Note that there is no need to add an initial unitary column (column of 1's) when
+ * specifying a model including an intercept term. If {@link #isNoIntercept()} is <code>true</code>,
+ * the X matrix will be created without an initial column of "1"s; otherwise this column will
+ * be added.
+ * </p>
+ * <p>Throws IllegalArgumentException if any of the following preconditions fail:
+ * <ul><li><code>data</code> cannot be null</li>
+ * <li><code>data.length = nobs * (nvars + 1)</li>
+ * <li><code>nobs > nvars</code></li></ul>
+ * </p>
+ *
+ * @param data input data array
+ * @param nobs number of observations (rows)
+ * @param nvars number of independent variables (columns, not counting y)
+ * @throws NullArgumentException if the data array is null
+ * @throws DimensionMismatchException if the length of the data array is not equal
+ * to <code>nobs * (nvars + 1)</code>
+ * @throws InsufficientDataException if <code>nobs</code> is less than
+ * <code>nvars + 1</code>
+ */
+ public void newSampleData(double[] data, int nobs, int nvars) {
+ if (data == null) {
+ throw new NullArgumentException();
+ }
+ if (data.length != nobs * (nvars + 1)) {
+ throw new DimensionMismatchException(data.length, nobs * (nvars + 1));
+ }
+ if (nobs <= nvars) {
+ throw new InsufficientDataException(LocalizedFormats.INSUFFICIENT_OBSERVED_POINTS_IN_SAMPLE, nobs, nvars + 1);
+ }
+ double[] y = new double[nobs];
+ final int cols = noIntercept ? nvars: nvars + 1;
+ double[][] x = new double[nobs][cols];
+ int pointer = 0;
+ for (int i = 0; i < nobs; i++) {
+ y[i] = data[pointer++];
+ if (!noIntercept) {
+ x[i][0] = 1.0d;
+ }
+ for (int j = noIntercept ? 0 : 1; j < cols; j++) {
+ x[i][j] = data[pointer++];
+ }
+ }
+ this.xMatrix = new Array2DRowRealMatrix(x);
+ this.yVector = new ArrayRealVector(y);
+ }
+
+ /**
+ * Loads new y sample data, overriding any previous data.
+ *
+ * @param y the array representing the y sample
+ * @throws NullArgumentException if y is null
+ * @throws NoDataException if y is empty
+ */
+ protected void newYSampleData(double[] y) {
+ if (y == null) {
+ throw new NullArgumentException();
+ }
+ if (y.length == 0) {
+ throw new NoDataException();
+ }
+ this.yVector = new ArrayRealVector(y);
+ }
+
+ /**
+ * <p>Loads new x sample data, overriding any previous data.
+ * </p>
+ * The input <code>x</code> array should have one row for each sample
+ * observation, with columns corresponding to independent variables.
+ * For example, if <pre>
+ * <code> x = new double[][] {{1, 2}, {3, 4}, {5, 6}} </code></pre>
+ * then <code>setXSampleData(x) </code> results in a model with two independent
+ * variables and 3 observations:
+ * <pre>
+ * x[0] x[1]
+ * ----------
+ * 1 2
+ * 3 4
+ * 5 6
+ * </pre>
+ * </p>
+ * <p>Note that there is no need to add an initial unitary column (column of 1's) when
+ * specifying a model including an intercept term.
+ * </p>
+ * @param x the rectangular array representing the x sample
+ * @throws NullArgumentException if x is null
+ * @throws NoDataException if x is empty
+ * @throws DimensionMismatchException if x is not rectangular
+ */
+ protected void newXSampleData(double[][] x) {
+ if (x == null) {
+ throw new NullArgumentException();
+ }
+ if (x.length == 0) {
+ throw new NoDataException();
+ }
+ if (noIntercept) {
+ this.xMatrix = new Array2DRowRealMatrix(x, true);
+ } else { // Augment design matrix with initial unitary column
+ final int nVars = x[0].length;
+ final double[][] xAug = new double[x.length][nVars + 1];
+ for (int i = 0; i < x.length; i++) {
+ if (x[i].length != nVars) {
+ throw new DimensionMismatchException(x[i].length, nVars);
+ }
+ xAug[i][0] = 1.0d;
+ System.arraycopy(x[i], 0, xAug[i], 1, nVars);
+ }
+ this.xMatrix = new Array2DRowRealMatrix(xAug, false);
+ }
+ }
+
+ /**
+ * Validates sample data. Checks that
+ * <ul><li>Neither x nor y is null or empty;</li>
+ * <li>The length (i.e. number of rows) of x equals the length of y</li>
+ * <li>x has at least one more row than it has columns (i.e. there is
+ * sufficient data to estimate regression coefficients for each of the
+ * columns in x plus an intercept.</li>
+ * </ul>
+ *
+ * @param x the [n,k] array representing the x data
+ * @param y the [n,1] array representing the y data
+ * @throws NullArgumentException if {@code x} or {@code y} is null
+ * @throws DimensionMismatchException if {@code x} and {@code y} do not
+ * have the same length
+ * @throws NoDataException if {@code x} or {@code y} are zero-length
+ * @throws MathIllegalArgumentException if the number of rows of {@code x}
+ * is not larger than the number of columns + 1
+ */
+ protected void validateSampleData(double[][] x, double[] y) throws MathIllegalArgumentException {
+ if ((x == null) || (y == null)) {
+ throw new NullArgumentException();
+ }
+ if (x.length != y.length) {
+ throw new DimensionMismatchException(y.length, x.length);
+ }
+ if (x.length == 0) { // Must be no y data either
+ throw new NoDataException();
+ }
+ if (x[0].length + 1 > x.length) {
+ throw new MathIllegalArgumentException(
+ LocalizedFormats.NOT_ENOUGH_DATA_FOR_NUMBER_OF_PREDICTORS,
+ x.length, x[0].length);
+ }
+ }
+
+ /**
+ * Validates that the x data and covariance matrix have the same
+ * number of rows and that the covariance matrix is square.
+ *
+ * @param x the [n,k] array representing the x sample
+ * @param covariance the [n,n] array representing the covariance matrix
+ * @throws DimensionMismatchException if the number of rows in x is not equal
+ * to the number of rows in covariance
+ * @throws NonSquareMatrixException if the covariance matrix is not square
+ */
+ protected void validateCovarianceData(double[][] x, double[][] covariance) {
+ if (x.length != covariance.length) {
+ throw new DimensionMismatchException(x.length, covariance.length);
+ }
+ if (covariance.length > 0 && covariance.length != covariance[0].length) {
+ throw new NonSquareMatrixException(covariance.length, covariance[0].length);
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public double[] estimateRegressionParameters() {
+ RealVector b = calculateBeta();
+ return b.toArray();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public double[] estimateResiduals() {
+ RealVector b = calculateBeta();
+ RealVector e = yVector.subtract(xMatrix.operate(b));
+ return e.toArray();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public double[][] estimateRegressionParametersVariance() {
+ return calculateBetaVariance().getData();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public double[] estimateRegressionParametersStandardErrors() {
+ double[][] betaVariance = estimateRegressionParametersVariance();
+ double sigma = calculateErrorVariance();
+ int length = betaVariance[0].length;
+ double[] result = new double[length];
+ for (int i = 0; i < length; i++) {
+ result[i] = FastMath.sqrt(sigma * betaVariance[i][i]);
+ }
+ return result;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public double estimateRegressandVariance() {
+ return calculateYVariance();
+ }
+
+ /**
+ * Estimates the variance of the error.
+ *
+ * @return estimate of the error variance
+ * @since 2.2
+ */
+ public double estimateErrorVariance() {
+ return calculateErrorVariance();
+
+ }
+
+ /**
+ * Estimates the standard error of the regression.
+ *
+ * @return regression standard error
+ * @since 2.2
+ */
+ public double estimateRegressionStandardError() {
+ return FastMath.sqrt(estimateErrorVariance());
+ }
+
+ /**
+ * Calculates the beta of multiple linear regression in matrix notation.
+ *
+ * @return beta
+ */
+ protected abstract RealVector calculateBeta();
+
+ /**
+ * Calculates the beta variance of multiple linear regression in matrix
+ * notation.
+ *
+ * @return beta variance
+ */
+ protected abstract RealMatrix calculateBetaVariance();
+
+
+ /**
+ * Calculates the variance of the y values.
+ *
+ * @return Y variance
+ */
+ protected double calculateYVariance() {
+ return new Variance().evaluate(yVector.toArray());
+ }
+
+ /**
+ * <p>Calculates the variance of the error term.</p>
+ * Uses the formula <pre>
+ * var(u) = u &middot; u / (n - k)
+ * </pre>
+ * where n and k are the row and column dimensions of the design
+ * matrix X.
+ *
+ * @return error variance estimate
+ * @since 2.2
+ */
+ protected double calculateErrorVariance() {
+ RealVector residuals = calculateResiduals();
+ return residuals.dotProduct(residuals) /
+ (xMatrix.getRowDimension() - xMatrix.getColumnDimension());
+ }
+
+ /**
+ * Calculates the residuals of multiple linear regression in matrix
+ * notation.
+ *
+ * <pre>
+ * u = y - X * b
+ * </pre>
+ *
+ * @return The residuals [n,1] matrix
+ */
+ protected RealVector calculateResiduals() {
+ RealVector b = calculateBeta();
+ return yVector.subtract(xMatrix.operate(b));
+ }
+
+}
diff --git a/src/main/java/org/apache/commons/math3/stat/regression/GLSMultipleLinearRegression.java b/src/main/java/org/apache/commons/math3/stat/regression/GLSMultipleLinearRegression.java
new file mode 100644
index 0000000..1644e6d
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/stat/regression/GLSMultipleLinearRegression.java
@@ -0,0 +1,135 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math3.stat.regression;
+
+import org.apache.commons.math3.linear.LUDecomposition;
+import org.apache.commons.math3.linear.RealMatrix;
+import org.apache.commons.math3.linear.Array2DRowRealMatrix;
+import org.apache.commons.math3.linear.RealVector;
+
+/**
+ * The GLS implementation of multiple linear regression.
+ *
+ * GLS assumes a general covariance matrix Omega of the error
+ * <pre>
+ * u ~ N(0, Omega)
+ * </pre>
+ *
+ * Estimated by GLS,
+ * <pre>
+ * b=(X' Omega^-1 X)^-1X'Omega^-1 y
+ * </pre>
+ * whose variance is
+ * <pre>
+ * Var(b)=(X' Omega^-1 X)^-1
+ * </pre>
+ * @since 2.0
+ */
+public class GLSMultipleLinearRegression extends AbstractMultipleLinearRegression {
+
+ /** Covariance matrix. */
+ private RealMatrix Omega;
+
+ /** Inverse of covariance matrix. */
+ private RealMatrix OmegaInverse;
+
+ /** Replace sample data, overriding any previous sample.
+ * @param y y values of the sample
+ * @param x x values of the sample
+ * @param covariance array representing the covariance matrix
+ */
+ public void newSampleData(double[] y, double[][] x, double[][] covariance) {
+ validateSampleData(x, y);
+ newYSampleData(y);
+ newXSampleData(x);
+ validateCovarianceData(x, covariance);
+ newCovarianceData(covariance);
+ }
+
+ /**
+ * Add the covariance data.
+ *
+ * @param omega the [n,n] array representing the covariance
+ */
+ protected void newCovarianceData(double[][] omega){
+ this.Omega = new Array2DRowRealMatrix(omega);
+ this.OmegaInverse = null;
+ }
+
+ /**
+ * Get the inverse of the covariance.
+ * <p>The inverse of the covariance matrix is lazily evaluated and cached.</p>
+ * @return inverse of the covariance
+ */
+ protected RealMatrix getOmegaInverse() {
+ if (OmegaInverse == null) {
+ OmegaInverse = new LUDecomposition(Omega).getSolver().getInverse();
+ }
+ return OmegaInverse;
+ }
+
+ /**
+ * Calculates beta by GLS.
+ * <pre>
+ * b=(X' Omega^-1 X)^-1X'Omega^-1 y
+ * </pre>
+ * @return beta
+ */
+ @Override
+ protected RealVector calculateBeta() {
+ RealMatrix OI = getOmegaInverse();
+ RealMatrix XT = getX().transpose();
+ RealMatrix XTOIX = XT.multiply(OI).multiply(getX());
+ RealMatrix inverse = new LUDecomposition(XTOIX).getSolver().getInverse();
+ return inverse.multiply(XT).multiply(OI).operate(getY());
+ }
+
+ /**
+ * Calculates the variance on the beta.
+ * <pre>
+ * Var(b)=(X' Omega^-1 X)^-1
+ * </pre>
+ * @return The beta variance matrix
+ */
+ @Override
+ protected RealMatrix calculateBetaVariance() {
+ RealMatrix OI = getOmegaInverse();
+ RealMatrix XTOIX = getX().transpose().multiply(OI).multiply(getX());
+ return new LUDecomposition(XTOIX).getSolver().getInverse();
+ }
+
+
+ /**
+ * Calculates the estimated variance of the error term using the formula
+ * <pre>
+ * Var(u) = Tr(u' Omega^-1 u)/(n-k)
+ * </pre>
+ * where n and k are the row and column dimensions of the design
+ * matrix X.
+ *
+ * @return error variance
+ * @since 2.2
+ */
+ @Override
+ protected double calculateErrorVariance() {
+ RealVector residuals = calculateResiduals();
+ double t = residuals.dotProduct(getOmegaInverse().operate(residuals));
+ return t / (getX().getRowDimension() - getX().getColumnDimension());
+
+ }
+
+}
diff --git a/src/main/java/org/apache/commons/math3/stat/regression/MillerUpdatingRegression.java b/src/main/java/org/apache/commons/math3/stat/regression/MillerUpdatingRegression.java
new file mode 100644
index 0000000..3fe3c03
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/stat/regression/MillerUpdatingRegression.java
@@ -0,0 +1,1101 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math3.stat.regression;
+
+import java.util.Arrays;
+import org.apache.commons.math3.exception.util.LocalizedFormats;
+import org.apache.commons.math3.util.FastMath;
+import org.apache.commons.math3.util.Precision;
+import org.apache.commons.math3.util.MathArrays;
+
+/**
+ * This class is a concrete implementation of the {@link UpdatingMultipleLinearRegression} interface.
+ *
+ * <p>The algorithm is described in: <pre>
+ * Algorithm AS 274: Least Squares Routines to Supplement Those of Gentleman
+ * Author(s): Alan J. Miller
+ * Source: Journal of the Royal Statistical Society.
+ * Series C (Applied Statistics), Vol. 41, No. 2
+ * (1992), pp. 458-478
+ * Published by: Blackwell Publishing for the Royal Statistical Society
+ * Stable URL: http://www.jstor.org/stable/2347583 </pre></p>
+ *
+ * <p>This method for multiple regression forms the solution to the OLS problem
+ * by updating the QR decomposition as described by Gentleman.</p>
+ *
+ * @since 3.0
+ */
+public class MillerUpdatingRegression implements UpdatingMultipleLinearRegression {
+
+ /** number of variables in regression */
+ private final int nvars;
+ /** diagonals of cross products matrix */
+ private final double[] d;
+ /** the elements of the R`Y */
+ private final double[] rhs;
+ /** the off diagonal portion of the R matrix */
+ private final double[] r;
+ /** the tolerance for each of the variables */
+ private final double[] tol;
+ /** residual sum of squares for all nested regressions */
+ private final double[] rss;
+ /** order of the regressors */
+ private final int[] vorder;
+ /** scratch space for tolerance calc */
+ private final double[] work_tolset;
+ /** number of observations entered */
+ private long nobs = 0;
+ /** sum of squared errors of largest regression */
+ private double sserr = 0.0;
+ /** has rss been called? */
+ private boolean rss_set = false;
+ /** has the tolerance setting method been called */
+ private boolean tol_set = false;
+ /** flags for variables with linear dependency problems */
+ private final boolean[] lindep;
+ /** singular x values */
+ private final double[] x_sing;
+ /** workspace for singularity method */
+ private final double[] work_sing;
+ /** summation of Y variable */
+ private double sumy = 0.0;
+ /** summation of squared Y values */
+ private double sumsqy = 0.0;
+ /** boolean flag whether a regression constant is added */
+ private boolean hasIntercept;
+ /** zero tolerance */
+ private final double epsilon;
+ /**
+ * Set the default constructor to private access
+ * to prevent inadvertent instantiation
+ */
+ @SuppressWarnings("unused")
+ private MillerUpdatingRegression() {
+ this(-1, false, Double.NaN);
+ }
+
+ /**
+ * This is the augmented constructor for the MillerUpdatingRegression class.
+ *
+ * @param numberOfVariables number of regressors to expect, not including constant
+ * @param includeConstant include a constant automatically
+ * @param errorTolerance zero tolerance, how machine zero is determined
+ * @throws ModelSpecificationException if {@code numberOfVariables is less than 1}
+ */
+ public MillerUpdatingRegression(int numberOfVariables, boolean includeConstant, double errorTolerance)
+ throws ModelSpecificationException {
+ if (numberOfVariables < 1) {
+ throw new ModelSpecificationException(LocalizedFormats.NO_REGRESSORS);
+ }
+ if (includeConstant) {
+ this.nvars = numberOfVariables + 1;
+ } else {
+ this.nvars = numberOfVariables;
+ }
+ this.hasIntercept = includeConstant;
+ this.nobs = 0;
+ this.d = new double[this.nvars];
+ this.rhs = new double[this.nvars];
+ this.r = new double[this.nvars * (this.nvars - 1) / 2];
+ this.tol = new double[this.nvars];
+ this.rss = new double[this.nvars];
+ this.vorder = new int[this.nvars];
+ this.x_sing = new double[this.nvars];
+ this.work_sing = new double[this.nvars];
+ this.work_tolset = new double[this.nvars];
+ this.lindep = new boolean[this.nvars];
+ for (int i = 0; i < this.nvars; i++) {
+ vorder[i] = i;
+ }
+ if (errorTolerance > 0) {
+ this.epsilon = errorTolerance;
+ } else {
+ this.epsilon = -errorTolerance;
+ }
+ }
+
+ /**
+ * Primary constructor for the MillerUpdatingRegression.
+ *
+ * @param numberOfVariables maximum number of potential regressors
+ * @param includeConstant include a constant automatically
+ * @throws ModelSpecificationException if {@code numberOfVariables is less than 1}
+ */
+ public MillerUpdatingRegression(int numberOfVariables, boolean includeConstant)
+ throws ModelSpecificationException {
+ this(numberOfVariables, includeConstant, Precision.EPSILON);
+ }
+
+ /**
+ * A getter method which determines whether a constant is included.
+ * @return true regression has an intercept, false no intercept
+ */
+ public boolean hasIntercept() {
+ return this.hasIntercept;
+ }
+
+ /**
+ * Gets the number of observations added to the regression model.
+ * @return number of observations
+ */
+ public long getN() {
+ return this.nobs;
+ }
+
+ /**
+ * Adds an observation to the regression model.
+ * @param x the array with regressor values
+ * @param y the value of dependent variable given these regressors
+ * @exception ModelSpecificationException if the length of {@code x} does not equal
+ * the number of independent variables in the model
+ */
+ public void addObservation(final double[] x, final double y)
+ throws ModelSpecificationException {
+
+ if ((!this.hasIntercept && x.length != nvars) ||
+ (this.hasIntercept && x.length + 1 != nvars)) {
+ throw new ModelSpecificationException(LocalizedFormats.INVALID_REGRESSION_OBSERVATION,
+ x.length, nvars);
+ }
+ if (!this.hasIntercept) {
+ include(MathArrays.copyOf(x, x.length), 1.0, y);
+ } else {
+ final double[] tmp = new double[x.length + 1];
+ System.arraycopy(x, 0, tmp, 1, x.length);
+ tmp[0] = 1.0;
+ include(tmp, 1.0, y);
+ }
+ ++nobs;
+
+ }
+
+ /**
+ * Adds multiple observations to the model.
+ * @param x observations on the regressors
+ * @param y observations on the regressand
+ * @throws ModelSpecificationException if {@code x} is not rectangular, does not match
+ * the length of {@code y} or does not contain sufficient data to estimate the model
+ */
+ public void addObservations(double[][] x, double[] y) throws ModelSpecificationException {
+ if ((x == null) || (y == null) || (x.length != y.length)) {
+ throw new ModelSpecificationException(
+ LocalizedFormats.DIMENSIONS_MISMATCH_SIMPLE,
+ (x == null) ? 0 : x.length,
+ (y == null) ? 0 : y.length);
+ }
+ if (x.length == 0) { // Must be no y data either
+ throw new ModelSpecificationException(
+ LocalizedFormats.NO_DATA);
+ }
+ if (x[0].length + 1 > x.length) {
+ throw new ModelSpecificationException(
+ LocalizedFormats.NOT_ENOUGH_DATA_FOR_NUMBER_OF_PREDICTORS,
+ x.length, x[0].length);
+ }
+ for (int i = 0; i < x.length; i++) {
+ addObservation(x[i], y[i]);
+ }
+ }
+
+ /**
+ * The include method is where the QR decomposition occurs. This statement forms all
+ * intermediate data which will be used for all derivative measures.
+ * According to the miller paper, note that in the original implementation the x vector
+ * is overwritten. In this implementation, the include method is passed a copy of the
+ * original data vector so that there is no contamination of the data. Additionally,
+ * this method differs slightly from Gentleman's method, in that the assumption is
+ * of dense design matrices, there is some advantage in using the original gentleman algorithm
+ * on sparse matrices.
+ *
+ * @param x observations on the regressors
+ * @param wi weight of the this observation (-1,1)
+ * @param yi observation on the regressand
+ */
+ private void include(final double[] x, final double wi, final double yi) {
+ int nextr = 0;
+ double w = wi;
+ double y = yi;
+ double xi;
+ double di;
+ double wxi;
+ double dpi;
+ double xk;
+ double _w;
+ this.rss_set = false;
+ sumy = smartAdd(yi, sumy);
+ sumsqy = smartAdd(sumsqy, yi * yi);
+ for (int i = 0; i < x.length; i++) {
+ if (w == 0.0) {
+ return;
+ }
+ xi = x[i];
+
+ if (xi == 0.0) {
+ nextr += nvars - i - 1;
+ continue;
+ }
+ di = d[i];
+ wxi = w * xi;
+ _w = w;
+ if (di != 0.0) {
+ dpi = smartAdd(di, wxi * xi);
+ final double tmp = wxi * xi / di;
+ if (FastMath.abs(tmp) > Precision.EPSILON) {
+ w = (di * w) / dpi;
+ }
+ } else {
+ dpi = wxi * xi;
+ w = 0.0;
+ }
+ d[i] = dpi;
+ for (int k = i + 1; k < nvars; k++) {
+ xk = x[k];
+ x[k] = smartAdd(xk, -xi * r[nextr]);
+ if (di != 0.0) {
+ r[nextr] = smartAdd(di * r[nextr], (_w * xi) * xk) / dpi;
+ } else {
+ r[nextr] = xk / xi;
+ }
+ ++nextr;
+ }
+ xk = y;
+ y = smartAdd(xk, -xi * rhs[i]);
+ if (di != 0.0) {
+ rhs[i] = smartAdd(di * rhs[i], wxi * xk) / dpi;
+ } else {
+ rhs[i] = xk / xi;
+ }
+ }
+ sserr = smartAdd(sserr, w * y * y);
+ }
+
+ /**
+ * Adds to number a and b such that the contamination due to
+ * numerical smallness of one addend does not corrupt the sum.
+ * @param a - an addend
+ * @param b - an addend
+ * @return the sum of the a and b
+ */
+ private double smartAdd(double a, double b) {
+ final double _a = FastMath.abs(a);
+ final double _b = FastMath.abs(b);
+ if (_a > _b) {
+ final double eps = _a * Precision.EPSILON;
+ if (_b > eps) {
+ return a + b;
+ }
+ return a;
+ } else {
+ final double eps = _b * Precision.EPSILON;
+ if (_a > eps) {
+ return a + b;
+ }
+ return b;
+ }
+ }
+
+ /**
+ * As the name suggests, clear wipes the internals and reorders everything in the
+ * canonical order.
+ */
+ public void clear() {
+ Arrays.fill(this.d, 0.0);
+ Arrays.fill(this.rhs, 0.0);
+ Arrays.fill(this.r, 0.0);
+ Arrays.fill(this.tol, 0.0);
+ Arrays.fill(this.rss, 0.0);
+ Arrays.fill(this.work_tolset, 0.0);
+ Arrays.fill(this.work_sing, 0.0);
+ Arrays.fill(this.x_sing, 0.0);
+ Arrays.fill(this.lindep, false);
+ for (int i = 0; i < nvars; i++) {
+ this.vorder[i] = i;
+ }
+ this.nobs = 0;
+ this.sserr = 0.0;
+ this.sumy = 0.0;
+ this.sumsqy = 0.0;
+ this.rss_set = false;
+ this.tol_set = false;
+ }
+
+ /**
+ * This sets up tolerances for singularity testing.
+ */
+ private void tolset() {
+ int pos;
+ double total;
+ final double eps = this.epsilon;
+ for (int i = 0; i < nvars; i++) {
+ this.work_tolset[i] = FastMath.sqrt(d[i]);
+ }
+ tol[0] = eps * this.work_tolset[0];
+ for (int col = 1; col < nvars; col++) {
+ pos = col - 1;
+ total = work_tolset[col];
+ for (int row = 0; row < col; row++) {
+ total += FastMath.abs(r[pos]) * work_tolset[row];
+ pos += nvars - row - 2;
+ }
+ tol[col] = eps * total;
+ }
+ tol_set = true;
+ }
+
+ /**
+ * The regcf method conducts the linear regression and extracts the
+ * parameter vector. Notice that the algorithm can do subset regression
+ * with no alteration.
+ *
+ * @param nreq how many of the regressors to include (either in canonical
+ * order, or in the current reordered state)
+ * @return an array with the estimated slope coefficients
+ * @throws ModelSpecificationException if {@code nreq} is less than 1
+ * or greater than the number of independent variables
+ */
+ private double[] regcf(int nreq) throws ModelSpecificationException {
+ int nextr;
+ if (nreq < 1) {
+ throw new ModelSpecificationException(LocalizedFormats.NO_REGRESSORS);
+ }
+ if (nreq > this.nvars) {
+ throw new ModelSpecificationException(
+ LocalizedFormats.TOO_MANY_REGRESSORS, nreq, this.nvars);
+ }
+ if (!this.tol_set) {
+ tolset();
+ }
+ final double[] ret = new double[nreq];
+ boolean rankProblem = false;
+ for (int i = nreq - 1; i > -1; i--) {
+ if (FastMath.sqrt(d[i]) < tol[i]) {
+ ret[i] = 0.0;
+ d[i] = 0.0;
+ rankProblem = true;
+ } else {
+ ret[i] = rhs[i];
+ nextr = i * (nvars + nvars - i - 1) / 2;
+ for (int j = i + 1; j < nreq; j++) {
+ ret[i] = smartAdd(ret[i], -r[nextr] * ret[j]);
+ ++nextr;
+ }
+ }
+ }
+ if (rankProblem) {
+ for (int i = 0; i < nreq; i++) {
+ if (this.lindep[i]) {
+ ret[i] = Double.NaN;
+ }
+ }
+ }
+ return ret;
+ }
+
+ /**
+ * The method which checks for singularities and then eliminates the offending
+ * columns.
+ */
+ private void singcheck() {
+ int pos;
+ for (int i = 0; i < nvars; i++) {
+ work_sing[i] = FastMath.sqrt(d[i]);
+ }
+ for (int col = 0; col < nvars; col++) {
+ // Set elements within R to zero if they are less than tol(col) in
+ // absolute value after being scaled by the square root of their row
+ // multiplier
+ final double temp = tol[col];
+ pos = col - 1;
+ for (int row = 0; row < col - 1; row++) {
+ if (FastMath.abs(r[pos]) * work_sing[row] < temp) {
+ r[pos] = 0.0;
+ }
+ pos += nvars - row - 2;
+ }
+ // If diagonal element is near zero, set it to zero, set appropriate
+ // element of LINDEP, and use INCLUD to augment the projections in
+ // the lower rows of the orthogonalization.
+ lindep[col] = false;
+ if (work_sing[col] < temp) {
+ lindep[col] = true;
+ if (col < nvars - 1) {
+ Arrays.fill(x_sing, 0.0);
+ int _pi = col * (nvars + nvars - col - 1) / 2;
+ for (int _xi = col + 1; _xi < nvars; _xi++, _pi++) {
+ x_sing[_xi] = r[_pi];
+ r[_pi] = 0.0;
+ }
+ final double y = rhs[col];
+ final double weight = d[col];
+ d[col] = 0.0;
+ rhs[col] = 0.0;
+ this.include(x_sing, weight, y);
+ } else {
+ sserr += d[col] * rhs[col] * rhs[col];
+ }
+ }
+ }
+ }
+
+ /**
+ * Calculates the sum of squared errors for the full regression
+ * and all subsets in the following manner: <pre>
+ * rss[] ={
+ * ResidualSumOfSquares_allNvars,
+ * ResidualSumOfSquares_FirstNvars-1,
+ * ResidualSumOfSquares_FirstNvars-2,
+ * ..., ResidualSumOfSquares_FirstVariable} </pre>
+ */
+ private void ss() {
+ double total = sserr;
+ rss[nvars - 1] = sserr;
+ for (int i = nvars - 1; i > 0; i--) {
+ total += d[i] * rhs[i] * rhs[i];
+ rss[i - 1] = total;
+ }
+ rss_set = true;
+ }
+
+ /**
+ * Calculates the cov matrix assuming only the first nreq variables are
+ * included in the calculation. The returned array contains a symmetric
+ * matrix stored in lower triangular form. The matrix will have
+ * ( nreq + 1 ) * nreq / 2 elements. For illustration <pre>
+ * cov =
+ * {
+ * cov_00,
+ * cov_10, cov_11,
+ * cov_20, cov_21, cov22,
+ * ...
+ * } </pre>
+ *
+ * @param nreq how many of the regressors to include (either in canonical
+ * order, or in the current reordered state)
+ * @return an array with the variance covariance of the included
+ * regressors in lower triangular form
+ */
+ private double[] cov(int nreq) {
+ if (this.nobs <= nreq) {
+ return null;
+ }
+ double rnk = 0.0;
+ for (int i = 0; i < nreq; i++) {
+ if (!this.lindep[i]) {
+ rnk += 1.0;
+ }
+ }
+ final double var = rss[nreq - 1] / (nobs - rnk);
+ final double[] rinv = new double[nreq * (nreq - 1) / 2];
+ inverse(rinv, nreq);
+ final double[] covmat = new double[nreq * (nreq + 1) / 2];
+ Arrays.fill(covmat, Double.NaN);
+ int pos2;
+ int pos1;
+ int start = 0;
+ double total = 0;
+ for (int row = 0; row < nreq; row++) {
+ pos2 = start;
+ if (!this.lindep[row]) {
+ for (int col = row; col < nreq; col++) {
+ if (!this.lindep[col]) {
+ pos1 = start + col - row;
+ if (row == col) {
+ total = 1.0 / d[col];
+ } else {
+ total = rinv[pos1 - 1] / d[col];
+ }
+ for (int k = col + 1; k < nreq; k++) {
+ if (!this.lindep[k]) {
+ total += rinv[pos1] * rinv[pos2] / d[k];
+ }
+ ++pos1;
+ ++pos2;
+ }
+ covmat[ (col + 1) * col / 2 + row] = total * var;
+ } else {
+ pos2 += nreq - col - 1;
+ }
+ }
+ }
+ start += nreq - row - 1;
+ }
+ return covmat;
+ }
+
+ /**
+ * This internal method calculates the inverse of the upper-triangular portion
+ * of the R matrix.
+ * @param rinv the storage for the inverse of r
+ * @param nreq how many of the regressors to include (either in canonical
+ * order, or in the current reordered state)
+ */
+ private void inverse(double[] rinv, int nreq) {
+ int pos = nreq * (nreq - 1) / 2 - 1;
+ int pos1 = -1;
+ int pos2 = -1;
+ double total = 0.0;
+ Arrays.fill(rinv, Double.NaN);
+ for (int row = nreq - 1; row > 0; --row) {
+ if (!this.lindep[row]) {
+ final int start = (row - 1) * (nvars + nvars - row) / 2;
+ for (int col = nreq; col > row; --col) {
+ pos1 = start;
+ pos2 = pos;
+ total = 0.0;
+ for (int k = row; k < col - 1; k++) {
+ pos2 += nreq - k - 1;
+ if (!this.lindep[k]) {
+ total += -r[pos1] * rinv[pos2];
+ }
+ ++pos1;
+ }
+ rinv[pos] = total - r[pos1];
+ --pos;
+ }
+ } else {
+ pos -= nreq - row;
+ }
+ }
+ }
+
+ /**
+ * In the original algorithm only the partial correlations of the regressors
+ * is returned to the user. In this implementation, we have <pre>
+ * corr =
+ * {
+ * corrxx - lower triangular
+ * corrxy - bottom row of the matrix
+ * }
+ * Replaces subroutines PCORR and COR of:
+ * ALGORITHM AS274 APPL. STATIST. (1992) VOL.41, NO. 2 </pre>
+ *
+ * <p>Calculate partial correlations after the variables in rows
+ * 1, 2, ..., IN have been forced into the regression.
+ * If IN = 1, and the first row of R represents a constant in the
+ * model, then the usual simple correlations are returned.</p>
+ *
+ * <p>If IN = 0, the value returned in array CORMAT for the correlation
+ * of variables Xi & Xj is: <pre>
+ * sum ( Xi.Xj ) / Sqrt ( sum (Xi^2) . sum (Xj^2) )</pre></p>
+ *
+ * <p>On return, array CORMAT contains the upper triangle of the matrix of
+ * partial correlations stored by rows, excluding the 1's on the diagonal.
+ * e.g. if IN = 2, the consecutive elements returned are:
+ * (3,4) (3,5) ... (3,ncol), (4,5) (4,6) ... (4,ncol), etc.
+ * Array YCORR stores the partial correlations with the Y-variable
+ * starting with YCORR(IN+1) = partial correlation with the variable in
+ * position (IN+1). </p>
+ *
+ * @param in how many of the regressors to include (either in canonical
+ * order, or in the current reordered state)
+ * @return an array with the partial correlations of the remainder of
+ * regressors with each other and the regressand, in lower triangular form
+ */
+ public double[] getPartialCorrelations(int in) {
+ final double[] output = new double[(nvars - in + 1) * (nvars - in) / 2];
+ int pos;
+ int pos1;
+ int pos2;
+ final int rms_off = -in;
+ final int wrk_off = -(in + 1);
+ final double[] rms = new double[nvars - in];
+ final double[] work = new double[nvars - in - 1];
+ double sumxx;
+ double sumxy;
+ double sumyy;
+ final int offXX = (nvars - in) * (nvars - in - 1) / 2;
+ if (in < -1 || in >= nvars) {
+ return null;
+ }
+ final int nvm = nvars - 1;
+ final int base_pos = r.length - (nvm - in) * (nvm - in + 1) / 2;
+ if (d[in] > 0.0) {
+ rms[in + rms_off] = 1.0 / FastMath.sqrt(d[in]);
+ }
+ for (int col = in + 1; col < nvars; col++) {
+ pos = base_pos + col - 1 - in;
+ sumxx = d[col];
+ for (int row = in; row < col; row++) {
+ sumxx += d[row] * r[pos] * r[pos];
+ pos += nvars - row - 2;
+ }
+ if (sumxx > 0.0) {
+ rms[col + rms_off] = 1.0 / FastMath.sqrt(sumxx);
+ } else {
+ rms[col + rms_off] = 0.0;
+ }
+ }
+ sumyy = sserr;
+ for (int row = in; row < nvars; row++) {
+ sumyy += d[row] * rhs[row] * rhs[row];
+ }
+ if (sumyy > 0.0) {
+ sumyy = 1.0 / FastMath.sqrt(sumyy);
+ }
+ pos = 0;
+ for (int col1 = in; col1 < nvars; col1++) {
+ sumxy = 0.0;
+ Arrays.fill(work, 0.0);
+ pos1 = base_pos + col1 - in - 1;
+ for (int row = in; row < col1; row++) {
+ pos2 = pos1 + 1;
+ for (int col2 = col1 + 1; col2 < nvars; col2++) {
+ work[col2 + wrk_off] += d[row] * r[pos1] * r[pos2];
+ pos2++;
+ }
+ sumxy += d[row] * r[pos1] * rhs[row];
+ pos1 += nvars - row - 2;
+ }
+ pos2 = pos1 + 1;
+ for (int col2 = col1 + 1; col2 < nvars; col2++) {
+ work[col2 + wrk_off] += d[col1] * r[pos2];
+ ++pos2;
+ output[ (col2 - 1 - in) * (col2 - in) / 2 + col1 - in] =
+ work[col2 + wrk_off] * rms[col1 + rms_off] * rms[col2 + rms_off];
+ ++pos;
+ }
+ sumxy += d[col1] * rhs[col1];
+ output[col1 + rms_off + offXX] = sumxy * rms[col1 + rms_off] * sumyy;
+ }
+
+ return output;
+ }
+
+ /**
+ * ALGORITHM AS274 APPL. STATIST. (1992) VOL.41, NO. 2.
+ * Move variable from position FROM to position TO in an
+ * orthogonal reduction produced by AS75.1.
+ *
+ * @param from initial position
+ * @param to destination
+ */
+ private void vmove(int from, int to) {
+ double d1;
+ double d2;
+ double X;
+ double d1new;
+ double d2new;
+ double cbar;
+ double sbar;
+ double Y;
+ int first;
+ int inc;
+ int m1;
+ int m2;
+ int mp1;
+ int pos;
+ boolean bSkipTo40 = false;
+ if (from == to) {
+ return;
+ }
+ if (!this.rss_set) {
+ ss();
+ }
+ int count = 0;
+ if (from < to) {
+ first = from;
+ inc = 1;
+ count = to - from;
+ } else {
+ first = from - 1;
+ inc = -1;
+ count = from - to;
+ }
+
+ int m = first;
+ int idx = 0;
+ while (idx < count) {
+ m1 = m * (nvars + nvars - m - 1) / 2;
+ m2 = m1 + nvars - m - 1;
+ mp1 = m + 1;
+
+ d1 = d[m];
+ d2 = d[mp1];
+ // Special cases.
+ if (d1 > this.epsilon || d2 > this.epsilon) {
+ X = r[m1];
+ if (FastMath.abs(X) * FastMath.sqrt(d1) < tol[mp1]) {
+ X = 0.0;
+ }
+ if (d1 < this.epsilon || FastMath.abs(X) < this.epsilon) {
+ d[m] = d2;
+ d[mp1] = d1;
+ r[m1] = 0.0;
+ for (int col = m + 2; col < nvars; col++) {
+ ++m1;
+ X = r[m1];
+ r[m1] = r[m2];
+ r[m2] = X;
+ ++m2;
+ }
+ X = rhs[m];
+ rhs[m] = rhs[mp1];
+ rhs[mp1] = X;
+ bSkipTo40 = true;
+ //break;
+ } else if (d2 < this.epsilon) {
+ d[m] = d1 * X * X;
+ r[m1] = 1.0 / X;
+ for (int _i = m1 + 1; _i < m1 + nvars - m - 1; _i++) {
+ r[_i] /= X;
+ }
+ rhs[m] /= X;
+ bSkipTo40 = true;
+ //break;
+ }
+ if (!bSkipTo40) {
+ d1new = d2 + d1 * X * X;
+ cbar = d2 / d1new;
+ sbar = X * d1 / d1new;
+ d2new = d1 * cbar;
+ d[m] = d1new;
+ d[mp1] = d2new;
+ r[m1] = sbar;
+ for (int col = m + 2; col < nvars; col++) {
+ ++m1;
+ Y = r[m1];
+ r[m1] = cbar * r[m2] + sbar * Y;
+ r[m2] = Y - X * r[m2];
+ ++m2;
+ }
+ Y = rhs[m];
+ rhs[m] = cbar * rhs[mp1] + sbar * Y;
+ rhs[mp1] = Y - X * rhs[mp1];
+ }
+ }
+ if (m > 0) {
+ pos = m;
+ for (int row = 0; row < m; row++) {
+ X = r[pos];
+ r[pos] = r[pos - 1];
+ r[pos - 1] = X;
+ pos += nvars - row - 2;
+ }
+ }
+ // Adjust variable order (VORDER), the tolerances (TOL) and
+ // the vector of residual sums of squares (RSS).
+ m1 = vorder[m];
+ vorder[m] = vorder[mp1];
+ vorder[mp1] = m1;
+ X = tol[m];
+ tol[m] = tol[mp1];
+ tol[mp1] = X;
+ rss[m] = rss[mp1] + d[mp1] * rhs[mp1] * rhs[mp1];
+
+ m += inc;
+ ++idx;
+ }
+ }
+
+ /**
+ * ALGORITHM AS274 APPL. STATIST. (1992) VOL.41, NO. 2
+ *
+ * <p> Re-order the variables in an orthogonal reduction produced by
+ * AS75.1 so that the N variables in LIST start at position POS1,
+ * though will not necessarily be in the same order as in LIST.
+ * Any variables in VORDER before position POS1 are not moved.
+ * Auxiliary routine called: VMOVE. </p>
+ *
+ * <p>This internal method reorders the regressors.</p>
+ *
+ * @param list the regressors to move
+ * @param pos1 where the list will be placed
+ * @return -1 error, 0 everything ok
+ */
+ private int reorderRegressors(int[] list, int pos1) {
+ int next;
+ int i;
+ int l;
+ if (list.length < 1 || list.length > nvars + 1 - pos1) {
+ return -1;
+ }
+ next = pos1;
+ i = pos1;
+ while (i < nvars) {
+ l = vorder[i];
+ for (int j = 0; j < list.length; j++) {
+ if (l == list[j] && i > next) {
+ this.vmove(i, next);
+ ++next;
+ if (next >= list.length + pos1) {
+ return 0;
+ } else {
+ break;
+ }
+ }
+ }
+ ++i;
+ }
+ return 0;
+ }
+
+ /**
+ * Gets the diagonal of the Hat matrix also known as the leverage matrix.
+ *
+ * @param row_data returns the diagonal of the hat matrix for this observation
+ * @return the diagonal element of the hatmatrix
+ */
+ public double getDiagonalOfHatMatrix(double[] row_data) {
+ double[] wk = new double[this.nvars];
+ int pos;
+ double total;
+
+ if (row_data.length > nvars) {
+ return Double.NaN;
+ }
+ double[] xrow;
+ if (this.hasIntercept) {
+ xrow = new double[row_data.length + 1];
+ xrow[0] = 1.0;
+ System.arraycopy(row_data, 0, xrow, 1, row_data.length);
+ } else {
+ xrow = row_data;
+ }
+ double hii = 0.0;
+ for (int col = 0; col < xrow.length; col++) {
+ if (FastMath.sqrt(d[col]) < tol[col]) {
+ wk[col] = 0.0;
+ } else {
+ pos = col - 1;
+ total = xrow[col];
+ for (int row = 0; row < col; row++) {
+ total = smartAdd(total, -wk[row] * r[pos]);
+ pos += nvars - row - 2;
+ }
+ wk[col] = total;
+ hii = smartAdd(hii, (total * total) / d[col]);
+ }
+ }
+ return hii;
+ }
+
+ /**
+ * Gets the order of the regressors, useful if some type of reordering
+ * has been called. Calling regress with int[]{} args will trigger
+ * a reordering.
+ *
+ * @return int[] with the current order of the regressors
+ */
+ public int[] getOrderOfRegressors(){
+ return MathArrays.copyOf(vorder);
+ }
+
+ /**
+ * Conducts a regression on the data in the model, using all regressors.
+ *
+ * @return RegressionResults the structure holding all regression results
+ * @exception ModelSpecificationException - thrown if number of observations is
+ * less than the number of variables
+ */
+ public RegressionResults regress() throws ModelSpecificationException {
+ return regress(this.nvars);
+ }
+
+ /**
+ * Conducts a regression on the data in the model, using a subset of regressors.
+ *
+ * @param numberOfRegressors many of the regressors to include (either in canonical
+ * order, or in the current reordered state)
+ * @return RegressionResults the structure holding all regression results
+ * @exception ModelSpecificationException - thrown if number of observations is
+ * less than the number of variables or number of regressors requested
+ * is greater than the regressors in the model
+ */
+ public RegressionResults regress(int numberOfRegressors) throws ModelSpecificationException {
+ if (this.nobs <= numberOfRegressors) {
+ throw new ModelSpecificationException(
+ LocalizedFormats.NOT_ENOUGH_DATA_FOR_NUMBER_OF_PREDICTORS,
+ this.nobs, numberOfRegressors);
+ }
+ if( numberOfRegressors > this.nvars ){
+ throw new ModelSpecificationException(
+ LocalizedFormats.TOO_MANY_REGRESSORS, numberOfRegressors, this.nvars);
+ }
+
+ tolset();
+ singcheck();
+
+ double[] beta = this.regcf(numberOfRegressors);
+
+ ss();
+
+ double[] cov = this.cov(numberOfRegressors);
+
+ int rnk = 0;
+ for (int i = 0; i < this.lindep.length; i++) {
+ if (!this.lindep[i]) {
+ ++rnk;
+ }
+ }
+
+ boolean needsReorder = false;
+ for (int i = 0; i < numberOfRegressors; i++) {
+ if (this.vorder[i] != i) {
+ needsReorder = true;
+ break;
+ }
+ }
+ if (!needsReorder) {
+ return new RegressionResults(
+ beta, new double[][]{cov}, true, this.nobs, rnk,
+ this.sumy, this.sumsqy, this.sserr, this.hasIntercept, false);
+ } else {
+ double[] betaNew = new double[beta.length];
+ double[] covNew = new double[cov.length];
+
+ int[] newIndices = new int[beta.length];
+ for (int i = 0; i < nvars; i++) {
+ for (int j = 0; j < numberOfRegressors; j++) {
+ if (this.vorder[j] == i) {
+ betaNew[i] = beta[ j];
+ newIndices[i] = j;
+ }
+ }
+ }
+
+ int idx1 = 0;
+ int idx2;
+ int _i;
+ int _j;
+ for (int i = 0; i < beta.length; i++) {
+ _i = newIndices[i];
+ for (int j = 0; j <= i; j++, idx1++) {
+ _j = newIndices[j];
+ if (_i > _j) {
+ idx2 = _i * (_i + 1) / 2 + _j;
+ } else {
+ idx2 = _j * (_j + 1) / 2 + _i;
+ }
+ covNew[idx1] = cov[idx2];
+ }
+ }
+ return new RegressionResults(
+ betaNew, new double[][]{covNew}, true, this.nobs, rnk,
+ this.sumy, this.sumsqy, this.sserr, this.hasIntercept, false);
+ }
+ }
+
+ /**
+ * Conducts a regression on the data in the model, using regressors in array
+ * Calling this method will change the internal order of the regressors
+ * and care is required in interpreting the hatmatrix.
+ *
+ * @param variablesToInclude array of variables to include in regression
+ * @return RegressionResults the structure holding all regression results
+ * @exception ModelSpecificationException - thrown if number of observations is
+ * less than the number of variables, the number of regressors requested
+ * is greater than the regressors in the model or a regressor index in
+ * regressor array does not exist
+ */
+ public RegressionResults regress(int[] variablesToInclude) throws ModelSpecificationException {
+ if (variablesToInclude.length > this.nvars) {
+ throw new ModelSpecificationException(
+ LocalizedFormats.TOO_MANY_REGRESSORS, variablesToInclude.length, this.nvars);
+ }
+ if (this.nobs <= this.nvars) {
+ throw new ModelSpecificationException(
+ LocalizedFormats.NOT_ENOUGH_DATA_FOR_NUMBER_OF_PREDICTORS,
+ this.nobs, this.nvars);
+ }
+ Arrays.sort(variablesToInclude);
+ int iExclude = 0;
+ for (int i = 0; i < variablesToInclude.length; i++) {
+ if (i >= this.nvars) {
+ throw new ModelSpecificationException(
+ LocalizedFormats.INDEX_LARGER_THAN_MAX, i, this.nvars);
+ }
+ if (i > 0 && variablesToInclude[i] == variablesToInclude[i - 1]) {
+ variablesToInclude[i] = -1;
+ ++iExclude;
+ }
+ }
+ int[] series;
+ if (iExclude > 0) {
+ int j = 0;
+ series = new int[variablesToInclude.length - iExclude];
+ for (int i = 0; i < variablesToInclude.length; i++) {
+ if (variablesToInclude[i] > -1) {
+ series[j] = variablesToInclude[i];
+ ++j;
+ }
+ }
+ } else {
+ series = variablesToInclude;
+ }
+
+ reorderRegressors(series, 0);
+ tolset();
+ singcheck();
+
+ double[] beta = this.regcf(series.length);
+
+ ss();
+
+ double[] cov = this.cov(series.length);
+
+ int rnk = 0;
+ for (int i = 0; i < this.lindep.length; i++) {
+ if (!this.lindep[i]) {
+ ++rnk;
+ }
+ }
+
+ boolean needsReorder = false;
+ for (int i = 0; i < this.nvars; i++) {
+ if (this.vorder[i] != series[i]) {
+ needsReorder = true;
+ break;
+ }
+ }
+ if (!needsReorder) {
+ return new RegressionResults(
+ beta, new double[][]{cov}, true, this.nobs, rnk,
+ this.sumy, this.sumsqy, this.sserr, this.hasIntercept, false);
+ } else {
+ double[] betaNew = new double[beta.length];
+ int[] newIndices = new int[beta.length];
+ for (int i = 0; i < series.length; i++) {
+ for (int j = 0; j < this.vorder.length; j++) {
+ if (this.vorder[j] == series[i]) {
+ betaNew[i] = beta[ j];
+ newIndices[i] = j;
+ }
+ }
+ }
+ double[] covNew = new double[cov.length];
+ int idx1 = 0;
+ int idx2;
+ int _i;
+ int _j;
+ for (int i = 0; i < beta.length; i++) {
+ _i = newIndices[i];
+ for (int j = 0; j <= i; j++, idx1++) {
+ _j = newIndices[j];
+ if (_i > _j) {
+ idx2 = _i * (_i + 1) / 2 + _j;
+ } else {
+ idx2 = _j * (_j + 1) / 2 + _i;
+ }
+ covNew[idx1] = cov[idx2];
+ }
+ }
+ return new RegressionResults(
+ betaNew, new double[][]{covNew}, true, this.nobs, rnk,
+ this.sumy, this.sumsqy, this.sserr, this.hasIntercept, false);
+ }
+ }
+}
diff --git a/src/main/java/org/apache/commons/math3/stat/regression/ModelSpecificationException.java b/src/main/java/org/apache/commons/math3/stat/regression/ModelSpecificationException.java
new file mode 100644
index 0000000..f3804db
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/stat/regression/ModelSpecificationException.java
@@ -0,0 +1,40 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math3.stat.regression;
+
+import org.apache.commons.math3.exception.MathIllegalArgumentException;
+import org.apache.commons.math3.exception.util.Localizable;
+
+/**
+ * Exception thrown when a regression model is not correctly specified.
+ *
+ * @since 3.0
+ */
+public class ModelSpecificationException extends MathIllegalArgumentException {
+ /** Serializable version Id. */
+ private static final long serialVersionUID = 4206514456095401070L;
+
+ /**
+ * @param pattern message pattern describing the specification error.
+ *
+ * @param args arguments.
+ */
+ public ModelSpecificationException(Localizable pattern,
+ Object ... args) {
+ super(pattern, args);
+ }
+}
diff --git a/src/main/java/org/apache/commons/math3/stat/regression/MultipleLinearRegression.java b/src/main/java/org/apache/commons/math3/stat/regression/MultipleLinearRegression.java
new file mode 100644
index 0000000..866214f
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/stat/regression/MultipleLinearRegression.java
@@ -0,0 +1,69 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math3.stat.regression;
+
+/**
+ * The multiple linear regression can be represented in matrix-notation.
+ * <pre>
+ * y=X*b+u
+ * </pre>
+ * where y is an <code>n-vector</code> <b>regressand</b>, X is a <code>[n,k]</code> matrix whose <code>k</code> columns are called
+ * <b>regressors</b>, b is <code>k-vector</code> of <b>regression parameters</b> and <code>u</code> is an <code>n-vector</code>
+ * of <b>error terms</b> or <b>residuals</b>.
+ *
+ * The notation is quite standard in literature,
+ * cf eg <a href="http://www.econ.queensu.ca/ETM">Davidson and MacKinnon, Econometrics Theory and Methods, 2004</a>.
+ * @since 2.0
+ */
+public interface MultipleLinearRegression {
+
+ /**
+ * Estimates the regression parameters b.
+ *
+ * @return The [k,1] array representing b
+ */
+ double[] estimateRegressionParameters();
+
+ /**
+ * Estimates the variance of the regression parameters, ie Var(b).
+ *
+ * @return The [k,k] array representing the variance of b
+ */
+ double[][] estimateRegressionParametersVariance();
+
+ /**
+ * Estimates the residuals, ie u = y - X*b.
+ *
+ * @return The [n,1] array representing the residuals
+ */
+ double[] estimateResiduals();
+
+ /**
+ * Returns the variance of the regressand, ie Var(y).
+ *
+ * @return The double representing the variance of y
+ */
+ double estimateRegressandVariance();
+
+ /**
+ * Returns the standard errors of the regression parameters.
+ *
+ * @return standard errors of estimated regression parameters
+ */
+ double[] estimateRegressionParametersStandardErrors();
+
+}
diff --git a/src/main/java/org/apache/commons/math3/stat/regression/OLSMultipleLinearRegression.java b/src/main/java/org/apache/commons/math3/stat/regression/OLSMultipleLinearRegression.java
new file mode 100644
index 0000000..7fff940
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/stat/regression/OLSMultipleLinearRegression.java
@@ -0,0 +1,285 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math3.stat.regression;
+
+import org.apache.commons.math3.exception.MathIllegalArgumentException;
+import org.apache.commons.math3.linear.Array2DRowRealMatrix;
+import org.apache.commons.math3.linear.LUDecomposition;
+import org.apache.commons.math3.linear.QRDecomposition;
+import org.apache.commons.math3.linear.RealMatrix;
+import org.apache.commons.math3.linear.RealVector;
+import org.apache.commons.math3.stat.StatUtils;
+import org.apache.commons.math3.stat.descriptive.moment.SecondMoment;
+
+/**
+ * <p>Implements ordinary least squares (OLS) to estimate the parameters of a
+ * multiple linear regression model.</p>
+ *
+ * <p>The regression coefficients, <code>b</code>, satisfy the normal equations:
+ * <pre><code> X<sup>T</sup> X b = X<sup>T</sup> y </code></pre></p>
+ *
+ * <p>To solve the normal equations, this implementation uses QR decomposition
+ * of the <code>X</code> matrix. (See {@link QRDecomposition} for details on the
+ * decomposition algorithm.) The <code>X</code> matrix, also known as the <i>design matrix,</i>
+ * has rows corresponding to sample observations and columns corresponding to independent
+ * variables. When the model is estimated using an intercept term (i.e. when
+ * {@link #isNoIntercept() isNoIntercept} is false as it is by default), the <code>X</code>
+ * matrix includes an initial column identically equal to 1. We solve the normal equations
+ * as follows:
+ * <pre><code> X<sup>T</sup>X b = X<sup>T</sup> y
+ * (QR)<sup>T</sup> (QR) b = (QR)<sup>T</sup>y
+ * R<sup>T</sup> (Q<sup>T</sup>Q) R b = R<sup>T</sup> Q<sup>T</sup> y
+ * R<sup>T</sup> R b = R<sup>T</sup> Q<sup>T</sup> y
+ * (R<sup>T</sup>)<sup>-1</sup> R<sup>T</sup> R b = (R<sup>T</sup>)<sup>-1</sup> R<sup>T</sup> Q<sup>T</sup> y
+ * R b = Q<sup>T</sup> y </code></pre></p>
+ *
+ * <p>Given <code>Q</code> and <code>R</code>, the last equation is solved by back-substitution.</p>
+ *
+ * @since 2.0
+ */
+public class OLSMultipleLinearRegression extends AbstractMultipleLinearRegression {
+
+ /** Cached QR decomposition of X matrix */
+ private QRDecomposition qr = null;
+
+ /** Singularity threshold for QR decomposition */
+ private final double threshold;
+
+ /**
+ * Create an empty OLSMultipleLinearRegression instance.
+ */
+ public OLSMultipleLinearRegression() {
+ this(0d);
+ }
+
+ /**
+ * Create an empty OLSMultipleLinearRegression instance, using the given
+ * singularity threshold for the QR decomposition.
+ *
+ * @param threshold the singularity threshold
+ * @since 3.3
+ */
+ public OLSMultipleLinearRegression(final double threshold) {
+ this.threshold = threshold;
+ }
+
+ /**
+ * Loads model x and y sample data, overriding any previous sample.
+ *
+ * Computes and caches QR decomposition of the X matrix.
+ * @param y the [n,1] array representing the y sample
+ * @param x the [n,k] array representing the x sample
+ * @throws MathIllegalArgumentException if the x and y array data are not
+ * compatible for the regression
+ */
+ public void newSampleData(double[] y, double[][] x) throws MathIllegalArgumentException {
+ validateSampleData(x, y);
+ newYSampleData(y);
+ newXSampleData(x);
+ }
+
+ /**
+ * {@inheritDoc}
+ * <p>This implementation computes and caches the QR decomposition of the X matrix.</p>
+ */
+ @Override
+ public void newSampleData(double[] data, int nobs, int nvars) {
+ super.newSampleData(data, nobs, nvars);
+ qr = new QRDecomposition(getX(), threshold);
+ }
+
+ /**
+ * <p>Compute the "hat" matrix.
+ * </p>
+ * <p>The hat matrix is defined in terms of the design matrix X
+ * by X(X<sup>T</sup>X)<sup>-1</sup>X<sup>T</sup>
+ * </p>
+ * <p>The implementation here uses the QR decomposition to compute the
+ * hat matrix as Q I<sub>p</sub>Q<sup>T</sup> where I<sub>p</sub> is the
+ * p-dimensional identity matrix augmented by 0's. This computational
+ * formula is from "The Hat Matrix in Regression and ANOVA",
+ * David C. Hoaglin and Roy E. Welsch,
+ * <i>The American Statistician</i>, Vol. 32, No. 1 (Feb., 1978), pp. 17-22.
+ * </p>
+ * <p>Data for the model must have been successfully loaded using one of
+ * the {@code newSampleData} methods before invoking this method; otherwise
+ * a {@code NullPointerException} will be thrown.</p>
+ *
+ * @return the hat matrix
+ * @throws NullPointerException unless method {@code newSampleData} has been
+ * called beforehand.
+ */
+ public RealMatrix calculateHat() {
+ // Create augmented identity matrix
+ RealMatrix Q = qr.getQ();
+ final int p = qr.getR().getColumnDimension();
+ final int n = Q.getColumnDimension();
+ // No try-catch or advertised NotStrictlyPositiveException - NPE above if n < 3
+ Array2DRowRealMatrix augI = new Array2DRowRealMatrix(n, n);
+ double[][] augIData = augI.getDataRef();
+ for (int i = 0; i < n; i++) {
+ for (int j =0; j < n; j++) {
+ if (i == j && i < p) {
+ augIData[i][j] = 1d;
+ } else {
+ augIData[i][j] = 0d;
+ }
+ }
+ }
+
+ // Compute and return Hat matrix
+ // No DME advertised - args valid if we get here
+ return Q.multiply(augI).multiply(Q.transpose());
+ }
+
+ /**
+ * <p>Returns the sum of squared deviations of Y from its mean.</p>
+ *
+ * <p>If the model has no intercept term, <code>0</code> is used for the
+ * mean of Y - i.e., what is returned is the sum of the squared Y values.</p>
+ *
+ * <p>The value returned by this method is the SSTO value used in
+ * the {@link #calculateRSquared() R-squared} computation.</p>
+ *
+ * @return SSTO - the total sum of squares
+ * @throws NullPointerException if the sample has not been set
+ * @see #isNoIntercept()
+ * @since 2.2
+ */
+ public double calculateTotalSumOfSquares() {
+ if (isNoIntercept()) {
+ return StatUtils.sumSq(getY().toArray());
+ } else {
+ return new SecondMoment().evaluate(getY().toArray());
+ }
+ }
+
+ /**
+ * Returns the sum of squared residuals.
+ *
+ * @return residual sum of squares
+ * @since 2.2
+ * @throws org.apache.commons.math3.linear.SingularMatrixException if the design matrix is singular
+ * @throws NullPointerException if the data for the model have not been loaded
+ */
+ public double calculateResidualSumOfSquares() {
+ final RealVector residuals = calculateResiduals();
+ // No advertised DME, args are valid
+ return residuals.dotProduct(residuals);
+ }
+
+ /**
+ * Returns the R-Squared statistic, defined by the formula <pre>
+ * R<sup>2</sup> = 1 - SSR / SSTO
+ * </pre>
+ * where SSR is the {@link #calculateResidualSumOfSquares() sum of squared residuals}
+ * and SSTO is the {@link #calculateTotalSumOfSquares() total sum of squares}
+ *
+ * <p>If there is no variance in y, i.e., SSTO = 0, NaN is returned.</p>
+ *
+ * @return R-square statistic
+ * @throws NullPointerException if the sample has not been set
+ * @throws org.apache.commons.math3.linear.SingularMatrixException if the design matrix is singular
+ * @since 2.2
+ */
+ public double calculateRSquared() {
+ return 1 - calculateResidualSumOfSquares() / calculateTotalSumOfSquares();
+ }
+
+ /**
+ * <p>Returns the adjusted R-squared statistic, defined by the formula <pre>
+ * R<sup>2</sup><sub>adj</sub> = 1 - [SSR (n - 1)] / [SSTO (n - p)]
+ * </pre>
+ * where SSR is the {@link #calculateResidualSumOfSquares() sum of squared residuals},
+ * SSTO is the {@link #calculateTotalSumOfSquares() total sum of squares}, n is the number
+ * of observations and p is the number of parameters estimated (including the intercept).</p>
+ *
+ * <p>If the regression is estimated without an intercept term, what is returned is <pre>
+ * <code> 1 - (1 - {@link #calculateRSquared()}) * (n / (n - p)) </code>
+ * </pre></p>
+ *
+ * <p>If there is no variance in y, i.e., SSTO = 0, NaN is returned.</p>
+ *
+ * @return adjusted R-Squared statistic
+ * @throws NullPointerException if the sample has not been set
+ * @throws org.apache.commons.math3.linear.SingularMatrixException if the design matrix is singular
+ * @see #isNoIntercept()
+ * @since 2.2
+ */
+ public double calculateAdjustedRSquared() {
+ final double n = getX().getRowDimension();
+ if (isNoIntercept()) {
+ return 1 - (1 - calculateRSquared()) * (n / (n - getX().getColumnDimension()));
+ } else {
+ return 1 - (calculateResidualSumOfSquares() * (n - 1)) /
+ (calculateTotalSumOfSquares() * (n - getX().getColumnDimension()));
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ * <p>This implementation computes and caches the QR decomposition of the X matrix
+ * once it is successfully loaded.</p>
+ */
+ @Override
+ protected void newXSampleData(double[][] x) {
+ super.newXSampleData(x);
+ qr = new QRDecomposition(getX(), threshold);
+ }
+
+ /**
+ * Calculates the regression coefficients using OLS.
+ *
+ * <p>Data for the model must have been successfully loaded using one of
+ * the {@code newSampleData} methods before invoking this method; otherwise
+ * a {@code NullPointerException} will be thrown.</p>
+ *
+ * @return beta
+ * @throws org.apache.commons.math3.linear.SingularMatrixException if the design matrix is singular
+ * @throws NullPointerException if the data for the model have not been loaded
+ */
+ @Override
+ protected RealVector calculateBeta() {
+ return qr.getSolver().solve(getY());
+ }
+
+ /**
+ * <p>Calculates the variance-covariance matrix of the regression parameters.
+ * </p>
+ * <p>Var(b) = (X<sup>T</sup>X)<sup>-1</sup>
+ * </p>
+ * <p>Uses QR decomposition to reduce (X<sup>T</sup>X)<sup>-1</sup>
+ * to (R<sup>T</sup>R)<sup>-1</sup>, with only the top p rows of
+ * R included, where p = the length of the beta vector.</p>
+ *
+ * <p>Data for the model must have been successfully loaded using one of
+ * the {@code newSampleData} methods before invoking this method; otherwise
+ * a {@code NullPointerException} will be thrown.</p>
+ *
+ * @return The beta variance-covariance matrix
+ * @throws org.apache.commons.math3.linear.SingularMatrixException if the design matrix is singular
+ * @throws NullPointerException if the data for the model have not been loaded
+ */
+ @Override
+ protected RealMatrix calculateBetaVariance() {
+ int p = getX().getColumnDimension();
+ RealMatrix Raug = qr.getR().getSubMatrix(0, p - 1 , 0, p - 1);
+ RealMatrix Rinv = new LUDecomposition(Raug).getSolver().getInverse();
+ return Rinv.multiply(Rinv.transpose());
+ }
+
+}
diff --git a/src/main/java/org/apache/commons/math3/stat/regression/RegressionResults.java b/src/main/java/org/apache/commons/math3/stat/regression/RegressionResults.java
new file mode 100644
index 0000000..70faeac
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/stat/regression/RegressionResults.java
@@ -0,0 +1,421 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math3.stat.regression;
+
+import java.io.Serializable;
+import java.util.Arrays;
+import org.apache.commons.math3.util.FastMath;
+import org.apache.commons.math3.util.MathArrays;
+import org.apache.commons.math3.exception.OutOfRangeException;
+
+/**
+ * Results of a Multiple Linear Regression model fit.
+ *
+ * @since 3.0
+ */
+public class RegressionResults implements Serializable {
+
+ /** INDEX of Sum of Squared Errors */
+ private static final int SSE_IDX = 0;
+ /** INDEX of Sum of Squares of Model */
+ private static final int SST_IDX = 1;
+ /** INDEX of R-Squared of regression */
+ private static final int RSQ_IDX = 2;
+ /** INDEX of Mean Squared Error */
+ private static final int MSE_IDX = 3;
+ /** INDEX of Adjusted R Squared */
+ private static final int ADJRSQ_IDX = 4;
+ /** UID */
+ private static final long serialVersionUID = 1l;
+ /** regression slope parameters */
+ private final double[] parameters;
+ /** variance covariance matrix of parameters */
+ private final double[][] varCovData;
+ /** boolean flag for variance covariance matrix in symm compressed storage */
+ private final boolean isSymmetricVCD;
+ /** rank of the solution */
+ @SuppressWarnings("unused")
+ private final int rank;
+ /** number of observations on which results are based */
+ private final long nobs;
+ /** boolean flag indicator of whether a constant was included*/
+ private final boolean containsConstant;
+ /** array storing global results, SSE, MSE, RSQ, adjRSQ */
+ private final double[] globalFitInfo;
+
+ /**
+ * Set the default constructor to private access
+ * to prevent inadvertent instantiation
+ */
+ @SuppressWarnings("unused")
+ private RegressionResults() {
+ this.parameters = null;
+ this.varCovData = null;
+ this.rank = -1;
+ this.nobs = -1;
+ this.containsConstant = false;
+ this.isSymmetricVCD = false;
+ this.globalFitInfo = null;
+ }
+
+ /**
+ * Constructor for Regression Results.
+ *
+ * @param parameters a double array with the regression slope estimates
+ * @param varcov the variance covariance matrix, stored either in a square matrix
+ * or as a compressed
+ * @param isSymmetricCompressed a flag which denotes that the variance covariance
+ * matrix is in symmetric compressed format
+ * @param nobs the number of observations of the regression estimation
+ * @param rank the number of independent variables in the regression
+ * @param sumy the sum of the independent variable
+ * @param sumysq the sum of the squared independent variable
+ * @param sse sum of squared errors
+ * @param containsConstant true model has constant, false model does not have constant
+ * @param copyData if true a deep copy of all input data is made, if false only references
+ * are copied and the RegressionResults become mutable
+ */
+ public RegressionResults(
+ final double[] parameters, final double[][] varcov,
+ final boolean isSymmetricCompressed,
+ final long nobs, final int rank,
+ final double sumy, final double sumysq, final double sse,
+ final boolean containsConstant,
+ final boolean copyData) {
+ if (copyData) {
+ this.parameters = MathArrays.copyOf(parameters);
+ this.varCovData = new double[varcov.length][];
+ for (int i = 0; i < varcov.length; i++) {
+ this.varCovData[i] = MathArrays.copyOf(varcov[i]);
+ }
+ } else {
+ this.parameters = parameters;
+ this.varCovData = varcov;
+ }
+ this.isSymmetricVCD = isSymmetricCompressed;
+ this.nobs = nobs;
+ this.rank = rank;
+ this.containsConstant = containsConstant;
+ this.globalFitInfo = new double[5];
+ Arrays.fill(this.globalFitInfo, Double.NaN);
+
+ if (rank > 0) {
+ this.globalFitInfo[SST_IDX] = containsConstant ?
+ (sumysq - sumy * sumy / nobs) : sumysq;
+ }
+
+ this.globalFitInfo[SSE_IDX] = sse;
+ this.globalFitInfo[MSE_IDX] = this.globalFitInfo[SSE_IDX] /
+ (nobs - rank);
+ this.globalFitInfo[RSQ_IDX] = 1.0 -
+ this.globalFitInfo[SSE_IDX] /
+ this.globalFitInfo[SST_IDX];
+
+ if (!containsConstant) {
+ this.globalFitInfo[ADJRSQ_IDX] = 1.0-
+ (1.0 - this.globalFitInfo[RSQ_IDX]) *
+ ( (double) nobs / ( (double) (nobs - rank)));
+ } else {
+ this.globalFitInfo[ADJRSQ_IDX] = 1.0 - (sse * (nobs - 1.0)) /
+ (globalFitInfo[SST_IDX] * (nobs - rank));
+ }
+ }
+
+ /**
+ * <p>Returns the parameter estimate for the regressor at the given index.</p>
+ *
+ * <p>A redundant regressor will have its redundancy flag set, as well as
+ * a parameters estimated equal to {@code Double.NaN}</p>
+ *
+ * @param index Index.
+ * @return the parameters estimated for regressor at index.
+ * @throws OutOfRangeException if {@code index} is not in the interval
+ * {@code [0, number of parameters)}.
+ */
+ public double getParameterEstimate(int index) throws OutOfRangeException {
+ if (parameters == null) {
+ return Double.NaN;
+ }
+ if (index < 0 || index >= this.parameters.length) {
+ throw new OutOfRangeException(index, 0, this.parameters.length - 1);
+ }
+ return this.parameters[index];
+ }
+
+ /**
+ * <p>Returns a copy of the regression parameters estimates.</p>
+ *
+ * <p>The parameter estimates are returned in the natural order of the data.</p>
+ *
+ * <p>A redundant regressor will have its redundancy flag set, as will
+ * a parameter estimate equal to {@code Double.NaN}.</p>
+ *
+ * @return array of parameter estimates, null if no estimation occurred
+ */
+ public double[] getParameterEstimates() {
+ if (this.parameters == null) {
+ return null;
+ }
+ return MathArrays.copyOf(parameters);
+ }
+
+ /**
+ * Returns the <a href="http://www.xycoon.com/standerrorb(1).htm">standard
+ * error of the parameter estimate at index</a>,
+ * usually denoted s(b<sub>index</sub>).
+ *
+ * @param index Index.
+ * @return the standard errors associated with parameters estimated at index.
+ * @throws OutOfRangeException if {@code index} is not in the interval
+ * {@code [0, number of parameters)}.
+ */
+ public double getStdErrorOfEstimate(int index) throws OutOfRangeException {
+ if (parameters == null) {
+ return Double.NaN;
+ }
+ if (index < 0 || index >= this.parameters.length) {
+ throw new OutOfRangeException(index, 0, this.parameters.length - 1);
+ }
+ double var = this.getVcvElement(index, index);
+ if (!Double.isNaN(var) && var > Double.MIN_VALUE) {
+ return FastMath.sqrt(var);
+ }
+ return Double.NaN;
+ }
+
+ /**
+ * <p>Returns the <a href="http://www.xycoon.com/standerrorb(1).htm">standard
+ * error of the parameter estimates</a>,
+ * usually denoted s(b<sub>i</sub>).</p>
+ *
+ * <p>If there are problems with an ill conditioned design matrix then the regressor
+ * which is redundant will be assigned <code>Double.NaN</code>. </p>
+ *
+ * @return an array standard errors associated with parameters estimates,
+ * null if no estimation occurred
+ */
+ public double[] getStdErrorOfEstimates() {
+ if (parameters == null) {
+ return null;
+ }
+ double[] se = new double[this.parameters.length];
+ for (int i = 0; i < this.parameters.length; i++) {
+ double var = this.getVcvElement(i, i);
+ if (!Double.isNaN(var) && var > Double.MIN_VALUE) {
+ se[i] = FastMath.sqrt(var);
+ continue;
+ }
+ se[i] = Double.NaN;
+ }
+ return se;
+ }
+
+ /**
+ * <p>Returns the covariance between regression parameters i and j.</p>
+ *
+ * <p>If there are problems with an ill conditioned design matrix then the covariance
+ * which involves redundant columns will be assigned {@code Double.NaN}. </p>
+ *
+ * @param i {@code i}th regression parameter.
+ * @param j {@code j}th regression parameter.
+ * @return the covariance of the parameter estimates.
+ * @throws OutOfRangeException if {@code i} or {@code j} is not in the
+ * interval {@code [0, number of parameters)}.
+ */
+ public double getCovarianceOfParameters(int i, int j) throws OutOfRangeException {
+ if (parameters == null) {
+ return Double.NaN;
+ }
+ if (i < 0 || i >= this.parameters.length) {
+ throw new OutOfRangeException(i, 0, this.parameters.length - 1);
+ }
+ if (j < 0 || j >= this.parameters.length) {
+ throw new OutOfRangeException(j, 0, this.parameters.length - 1);
+ }
+ return this.getVcvElement(i, j);
+ }
+
+ /**
+ * <p>Returns the number of parameters estimated in the model.</p>
+ *
+ * <p>This is the maximum number of regressors, some techniques may drop
+ * redundant parameters</p>
+ *
+ * @return number of regressors, -1 if not estimated
+ */
+ public int getNumberOfParameters() {
+ if (this.parameters == null) {
+ return -1;
+ }
+ return this.parameters.length;
+ }
+
+ /**
+ * Returns the number of observations added to the regression model.
+ *
+ * @return Number of observations, -1 if an error condition prevents estimation
+ */
+ public long getN() {
+ return this.nobs;
+ }
+
+ /**
+ * <p>Returns the sum of squared deviations of the y values about their mean.</p>
+ *
+ * <p>This is defined as SSTO
+ * <a href="http://www.xycoon.com/SumOfSquares.htm">here</a>.</p>
+ *
+ * <p>If {@code n < 2}, this returns {@code Double.NaN}.</p>
+ *
+ * @return sum of squared deviations of y values
+ */
+ public double getTotalSumSquares() {
+ return this.globalFitInfo[SST_IDX];
+ }
+
+ /**
+ * <p>Returns the sum of squared deviations of the predicted y values about
+ * their mean (which equals the mean of y).</p>
+ *
+ * <p>This is usually abbreviated SSR or SSM. It is defined as SSM
+ * <a href="http://www.xycoon.com/SumOfSquares.htm">here</a></p>
+ *
+ * <p><strong>Preconditions</strong>: <ul>
+ * <li>At least two observations (with at least two different x values)
+ * must have been added before invoking this method. If this method is
+ * invoked before a model can be estimated, <code>Double.NaN</code> is
+ * returned.
+ * </li></ul></p>
+ *
+ * @return sum of squared deviations of predicted y values
+ */
+ public double getRegressionSumSquares() {
+ return this.globalFitInfo[SST_IDX] - this.globalFitInfo[SSE_IDX];
+ }
+
+ /**
+ * <p>Returns the <a href="http://www.xycoon.com/SumOfSquares.htm">
+ * sum of squared errors</a> (SSE) associated with the regression
+ * model.</p>
+ *
+ * <p>The return value is constrained to be non-negative - i.e., if due to
+ * rounding errors the computational formula returns a negative result,
+ * 0 is returned.</p>
+ *
+ * <p><strong>Preconditions</strong>: <ul>
+ * <li>numberOfParameters data pairs
+ * must have been added before invoking this method. If this method is
+ * invoked before a model can be estimated, <code>Double,NaN</code> is
+ * returned.
+ * </li></ul></p>
+ *
+ * @return sum of squared errors associated with the regression model
+ */
+ public double getErrorSumSquares() {
+ return this.globalFitInfo[ SSE_IDX];
+ }
+
+ /**
+ * <p>Returns the sum of squared errors divided by the degrees of freedom,
+ * usually abbreviated MSE.</p>
+ *
+ * <p>If there are fewer than <strong>numberOfParameters + 1</strong> data pairs in the model,
+ * or if there is no variation in <code>x</code>, this returns
+ * <code>Double.NaN</code>.</p>
+ *
+ * @return sum of squared deviations of y values
+ */
+ public double getMeanSquareError() {
+ return this.globalFitInfo[ MSE_IDX];
+ }
+
+ /**
+ * <p>Returns the <a href="http://www.xycoon.com/coefficient1.htm">
+ * coefficient of multiple determination</a>,
+ * usually denoted r-square.</p>
+ *
+ * <p><strong>Preconditions</strong>: <ul>
+ * <li>At least numberOfParameters observations (with at least numberOfParameters different x values)
+ * must have been added before invoking this method. If this method is
+ * invoked before a model can be estimated, {@code Double,NaN} is
+ * returned.
+ * </li></ul></p>
+ *
+ * @return r-square, a double in the interval [0, 1]
+ */
+ public double getRSquared() {
+ return this.globalFitInfo[ RSQ_IDX];
+ }
+
+ /**
+ * <p>Returns the adjusted R-squared statistic, defined by the formula <pre>
+ * R<sup>2</sup><sub>adj</sub> = 1 - [SSR (n - 1)] / [SSTO (n - p)]
+ * </pre>
+ * where SSR is the sum of squared residuals},
+ * SSTO is the total sum of squares}, n is the number
+ * of observations and p is the number of parameters estimated (including the intercept).</p>
+ *
+ * <p>If the regression is estimated without an intercept term, what is returned is <pre>
+ * <code> 1 - (1 - {@link #getRSquared()} ) * (n / (n - p)) </code>
+ * </pre></p>
+ *
+ * @return adjusted R-Squared statistic
+ */
+ public double getAdjustedRSquared() {
+ return this.globalFitInfo[ ADJRSQ_IDX];
+ }
+
+ /**
+ * Returns true if the regression model has been computed including an intercept.
+ * In this case, the coefficient of the intercept is the first element of the
+ * {@link #getParameterEstimates() parameter estimates}.
+ * @return true if the model has an intercept term
+ */
+ public boolean hasIntercept() {
+ return this.containsConstant;
+ }
+
+ /**
+ * Gets the i-jth element of the variance-covariance matrix.
+ *
+ * @param i first variable index
+ * @param j second variable index
+ * @return the requested variance-covariance matrix entry
+ */
+ private double getVcvElement(int i, int j) {
+ if (this.isSymmetricVCD) {
+ if (this.varCovData.length > 1) {
+ //could be stored in upper or lower triangular
+ if (i == j) {
+ return varCovData[i][i];
+ } else if (i >= varCovData[j].length) {
+ return varCovData[i][j];
+ } else {
+ return varCovData[j][i];
+ }
+ } else {//could be in single array
+ if (i > j) {
+ return varCovData[0][(i + 1) * i / 2 + j];
+ } else {
+ return varCovData[0][(j + 1) * j / 2 + i];
+ }
+ }
+ } else {
+ return this.varCovData[i][j];
+ }
+ }
+}
diff --git a/src/main/java/org/apache/commons/math3/stat/regression/SimpleRegression.java b/src/main/java/org/apache/commons/math3/stat/regression/SimpleRegression.java
new file mode 100644
index 0000000..02bf8f4
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/stat/regression/SimpleRegression.java
@@ -0,0 +1,881 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math3.stat.regression;
+import java.io.Serializable;
+
+import org.apache.commons.math3.distribution.TDistribution;
+import org.apache.commons.math3.exception.MathIllegalArgumentException;
+import org.apache.commons.math3.exception.NoDataException;
+import org.apache.commons.math3.exception.OutOfRangeException;
+import org.apache.commons.math3.exception.util.LocalizedFormats;
+import org.apache.commons.math3.util.FastMath;
+import org.apache.commons.math3.util.Precision;
+
+/**
+ * Estimates an ordinary least squares regression model
+ * with one independent variable.
+ * <p>
+ * <code> y = intercept + slope * x </code></p>
+ * <p>
+ * Standard errors for <code>intercept</code> and <code>slope</code> are
+ * available as well as ANOVA, r-square and Pearson's r statistics.</p>
+ * <p>
+ * Observations (x,y pairs) can be added to the model one at a time or they
+ * can be provided in a 2-dimensional array. The observations are not stored
+ * in memory, so there is no limit to the number of observations that can be
+ * added to the model.</p>
+ * <p>
+ * <strong>Usage Notes</strong>: <ul>
+ * <li> When there are fewer than two observations in the model, or when
+ * there is no variation in the x values (i.e. all x values are the same)
+ * all statistics return <code>NaN</code>. At least two observations with
+ * different x coordinates are required to estimate a bivariate regression
+ * model.
+ * </li>
+ * <li> Getters for the statistics always compute values based on the current
+ * set of observations -- i.e., you can get statistics, then add more data
+ * and get updated statistics without using a new instance. There is no
+ * "compute" method that updates all statistics. Each of the getters performs
+ * the necessary computations to return the requested statistic.
+ * </li>
+ * <li> The intercept term may be suppressed by passing {@code false} to
+ * the {@link #SimpleRegression(boolean)} constructor. When the
+ * {@code hasIntercept} property is false, the model is estimated without a
+ * constant term and {@link #getIntercept()} returns {@code 0}.</li>
+ * </ul></p>
+ *
+ */
+public class SimpleRegression implements Serializable, UpdatingMultipleLinearRegression {
+
+ /** Serializable version identifier */
+ private static final long serialVersionUID = -3004689053607543335L;
+
+ /** sum of x values */
+ private double sumX = 0d;
+
+ /** total variation in x (sum of squared deviations from xbar) */
+ private double sumXX = 0d;
+
+ /** sum of y values */
+ private double sumY = 0d;
+
+ /** total variation in y (sum of squared deviations from ybar) */
+ private double sumYY = 0d;
+
+ /** sum of products */
+ private double sumXY = 0d;
+
+ /** number of observations */
+ private long n = 0;
+
+ /** mean of accumulated x values, used in updating formulas */
+ private double xbar = 0;
+
+ /** mean of accumulated y values, used in updating formulas */
+ private double ybar = 0;
+
+ /** include an intercept or not */
+ private final boolean hasIntercept;
+ // ---------------------Public methods--------------------------------------
+
+ /**
+ * Create an empty SimpleRegression instance
+ */
+ public SimpleRegression() {
+ this(true);
+ }
+ /**
+ * Create a SimpleRegression instance, specifying whether or not to estimate
+ * an intercept.
+ *
+ * <p>Use {@code false} to estimate a model with no intercept. When the
+ * {@code hasIntercept} property is false, the model is estimated without a
+ * constant term and {@link #getIntercept()} returns {@code 0}.</p>
+ *
+ * @param includeIntercept whether or not to include an intercept term in
+ * the regression model
+ */
+ public SimpleRegression(boolean includeIntercept) {
+ super();
+ hasIntercept = includeIntercept;
+ }
+
+ /**
+ * Adds the observation (x,y) to the regression data set.
+ * <p>
+ * Uses updating formulas for means and sums of squares defined in
+ * "Algorithms for Computing the Sample Variance: Analysis and
+ * Recommendations", Chan, T.F., Golub, G.H., and LeVeque, R.J.
+ * 1983, American Statistician, vol. 37, pp. 242-247, referenced in
+ * Weisberg, S. "Applied Linear Regression". 2nd Ed. 1985.</p>
+ *
+ *
+ * @param x independent variable value
+ * @param y dependent variable value
+ */
+ public void addData(final double x,final double y) {
+ if (n == 0) {
+ xbar = x;
+ ybar = y;
+ } else {
+ if( hasIntercept ){
+ final double fact1 = 1.0 + n;
+ final double fact2 = n / (1.0 + n);
+ final double dx = x - xbar;
+ final double dy = y - ybar;
+ sumXX += dx * dx * fact2;
+ sumYY += dy * dy * fact2;
+ sumXY += dx * dy * fact2;
+ xbar += dx / fact1;
+ ybar += dy / fact1;
+ }
+ }
+ if( !hasIntercept ){
+ sumXX += x * x ;
+ sumYY += y * y ;
+ sumXY += x * y ;
+ }
+ sumX += x;
+ sumY += y;
+ n++;
+ }
+
+ /**
+ * Appends data from another regression calculation to this one.
+ *
+ * <p>The mean update formulae are based on a paper written by Philippe
+ * P&eacute;bay:
+ * <a
+ * href="http://prod.sandia.gov/techlib/access-control.cgi/2008/086212.pdf">
+ * Formulas for Robust, One-Pass Parallel Computation of Covariances and
+ * Arbitrary-Order Statistical Moments</a>, 2008, Technical Report
+ * SAND2008-6212, Sandia National Laboratories.</p>
+ *
+ * @param reg model to append data from
+ * @since 3.3
+ */
+ public void append(SimpleRegression reg) {
+ if (n == 0) {
+ xbar = reg.xbar;
+ ybar = reg.ybar;
+ sumXX = reg.sumXX;
+ sumYY = reg.sumYY;
+ sumXY = reg.sumXY;
+ } else {
+ if (hasIntercept) {
+ final double fact1 = reg.n / (double) (reg.n + n);
+ final double fact2 = n * reg.n / (double) (reg.n + n);
+ final double dx = reg.xbar - xbar;
+ final double dy = reg.ybar - ybar;
+ sumXX += reg.sumXX + dx * dx * fact2;
+ sumYY += reg.sumYY + dy * dy * fact2;
+ sumXY += reg.sumXY + dx * dy * fact2;
+ xbar += dx * fact1;
+ ybar += dy * fact1;
+ }else{
+ sumXX += reg.sumXX;
+ sumYY += reg.sumYY;
+ sumXY += reg.sumXY;
+ }
+ }
+ sumX += reg.sumX;
+ sumY += reg.sumY;
+ n += reg.n;
+ }
+
+ /**
+ * Removes the observation (x,y) from the regression data set.
+ * <p>
+ * Mirrors the addData method. This method permits the use of
+ * SimpleRegression instances in streaming mode where the regression
+ * is applied to a sliding "window" of observations, however the caller is
+ * responsible for maintaining the set of observations in the window.</p>
+ *
+ * The method has no effect if there are no points of data (i.e. n=0)
+ *
+ * @param x independent variable value
+ * @param y dependent variable value
+ */
+ public void removeData(final double x,final double y) {
+ if (n > 0) {
+ if (hasIntercept) {
+ final double fact1 = n - 1.0;
+ final double fact2 = n / (n - 1.0);
+ final double dx = x - xbar;
+ final double dy = y - ybar;
+ sumXX -= dx * dx * fact2;
+ sumYY -= dy * dy * fact2;
+ sumXY -= dx * dy * fact2;
+ xbar -= dx / fact1;
+ ybar -= dy / fact1;
+ } else {
+ final double fact1 = n - 1.0;
+ sumXX -= x * x;
+ sumYY -= y * y;
+ sumXY -= x * y;
+ xbar -= x / fact1;
+ ybar -= y / fact1;
+ }
+ sumX -= x;
+ sumY -= y;
+ n--;
+ }
+ }
+
+ /**
+ * Adds the observations represented by the elements in
+ * <code>data</code>.
+ * <p>
+ * <code>(data[0][0],data[0][1])</code> will be the first observation, then
+ * <code>(data[1][0],data[1][1])</code>, etc.</p>
+ * <p>
+ * This method does not replace data that has already been added. The
+ * observations represented by <code>data</code> are added to the existing
+ * dataset.</p>
+ * <p>
+ * To replace all data, use <code>clear()</code> before adding the new
+ * data.</p>
+ *
+ * @param data array of observations to be added
+ * @throws ModelSpecificationException if the length of {@code data[i]} is not
+ * greater than or equal to 2
+ */
+ public void addData(final double[][] data) throws ModelSpecificationException {
+ for (int i = 0; i < data.length; i++) {
+ if( data[i].length < 2 ){
+ throw new ModelSpecificationException(LocalizedFormats.INVALID_REGRESSION_OBSERVATION,
+ data[i].length, 2);
+ }
+ addData(data[i][0], data[i][1]);
+ }
+ }
+
+ /**
+ * Adds one observation to the regression model.
+ *
+ * @param x the independent variables which form the design matrix
+ * @param y the dependent or response variable
+ * @throws ModelSpecificationException if the length of {@code x} does not equal
+ * the number of independent variables in the model
+ */
+ public void addObservation(final double[] x,final double y)
+ throws ModelSpecificationException {
+ if( x == null || x.length == 0 ){
+ throw new ModelSpecificationException(LocalizedFormats.INVALID_REGRESSION_OBSERVATION,x!=null?x.length:0, 1);
+ }
+ addData( x[0], y );
+ }
+
+ /**
+ * Adds a series of observations to the regression model. The lengths of
+ * x and y must be the same and x must be rectangular.
+ *
+ * @param x a series of observations on the independent variables
+ * @param y a series of observations on the dependent variable
+ * The length of x and y must be the same
+ * @throws ModelSpecificationException if {@code x} is not rectangular, does not match
+ * the length of {@code y} or does not contain sufficient data to estimate the model
+ */
+ public void addObservations(final double[][] x,final double[] y) throws ModelSpecificationException {
+ if ((x == null) || (y == null) || (x.length != y.length)) {
+ throw new ModelSpecificationException(
+ LocalizedFormats.DIMENSIONS_MISMATCH_SIMPLE,
+ (x == null) ? 0 : x.length,
+ (y == null) ? 0 : y.length);
+ }
+ boolean obsOk=true;
+ for( int i = 0 ; i < x.length; i++){
+ if( x[i] == null || x[i].length == 0 ){
+ obsOk = false;
+ }
+ }
+ if( !obsOk ){
+ throw new ModelSpecificationException(
+ LocalizedFormats.NOT_ENOUGH_DATA_FOR_NUMBER_OF_PREDICTORS,
+ 0, 1);
+ }
+ for( int i = 0 ; i < x.length ; i++){
+ addData( x[i][0], y[i] );
+ }
+ }
+
+ /**
+ * Removes observations represented by the elements in <code>data</code>.
+ * <p>
+ * If the array is larger than the current n, only the first n elements are
+ * processed. This method permits the use of SimpleRegression instances in
+ * streaming mode where the regression is applied to a sliding "window" of
+ * observations, however the caller is responsible for maintaining the set
+ * of observations in the window.</p>
+ * <p>
+ * To remove all data, use <code>clear()</code>.</p>
+ *
+ * @param data array of observations to be removed
+ */
+ public void removeData(double[][] data) {
+ for (int i = 0; i < data.length && n > 0; i++) {
+ removeData(data[i][0], data[i][1]);
+ }
+ }
+
+ /**
+ * Clears all data from the model.
+ */
+ public void clear() {
+ sumX = 0d;
+ sumXX = 0d;
+ sumY = 0d;
+ sumYY = 0d;
+ sumXY = 0d;
+ n = 0;
+ }
+
+ /**
+ * Returns the number of observations that have been added to the model.
+ *
+ * @return n number of observations that have been added.
+ */
+ public long getN() {
+ return n;
+ }
+
+ /**
+ * Returns the "predicted" <code>y</code> value associated with the
+ * supplied <code>x</code> value, based on the data that has been
+ * added to the model when this method is activated.
+ * <p>
+ * <code> predict(x) = intercept + slope * x </code></p>
+ * <p>
+ * <strong>Preconditions</strong>: <ul>
+ * <li>At least two observations (with at least two different x values)
+ * must have been added before invoking this method. If this method is
+ * invoked before a model can be estimated, <code>Double,NaN</code> is
+ * returned.
+ * </li></ul></p>
+ *
+ * @param x input <code>x</code> value
+ * @return predicted <code>y</code> value
+ */
+ public double predict(final double x) {
+ final double b1 = getSlope();
+ if (hasIntercept) {
+ return getIntercept(b1) + b1 * x;
+ }
+ return b1 * x;
+ }
+
+ /**
+ * Returns the intercept of the estimated regression line, if
+ * {@link #hasIntercept()} is true; otherwise 0.
+ * <p>
+ * The least squares estimate of the intercept is computed using the
+ * <a href="http://www.xycoon.com/estimation4.htm">normal equations</a>.
+ * The intercept is sometimes denoted b0.</p>
+ * <p>
+ * <strong>Preconditions</strong>: <ul>
+ * <li>At least two observations (with at least two different x values)
+ * must have been added before invoking this method. If this method is
+ * invoked before a model can be estimated, <code>Double,NaN</code> is
+ * returned.
+ * </li></ul></p>
+ *
+ * @return the intercept of the regression line if the model includes an
+ * intercept; 0 otherwise
+ * @see #SimpleRegression(boolean)
+ */
+ public double getIntercept() {
+ return hasIntercept ? getIntercept(getSlope()) : 0.0;
+ }
+
+ /**
+ * Returns true if the model includes an intercept term.
+ *
+ * @return true if the regression includes an intercept; false otherwise
+ * @see #SimpleRegression(boolean)
+ */
+ public boolean hasIntercept() {
+ return hasIntercept;
+ }
+
+ /**
+ * Returns the slope of the estimated regression line.
+ * <p>
+ * The least squares estimate of the slope is computed using the
+ * <a href="http://www.xycoon.com/estimation4.htm">normal equations</a>.
+ * The slope is sometimes denoted b1.</p>
+ * <p>
+ * <strong>Preconditions</strong>: <ul>
+ * <li>At least two observations (with at least two different x values)
+ * must have been added before invoking this method. If this method is
+ * invoked before a model can be estimated, <code>Double.NaN</code> is
+ * returned.
+ * </li></ul></p>
+ *
+ * @return the slope of the regression line
+ */
+ public double getSlope() {
+ if (n < 2) {
+ return Double.NaN; //not enough data
+ }
+ if (FastMath.abs(sumXX) < 10 * Double.MIN_VALUE) {
+ return Double.NaN; //not enough variation in x
+ }
+ return sumXY / sumXX;
+ }
+
+ /**
+ * Returns the <a href="http://www.xycoon.com/SumOfSquares.htm">
+ * sum of squared errors</a> (SSE) associated with the regression
+ * model.
+ * <p>
+ * The sum is computed using the computational formula</p>
+ * <p>
+ * <code>SSE = SYY - (SXY * SXY / SXX)</code></p>
+ * <p>
+ * where <code>SYY</code> is the sum of the squared deviations of the y
+ * values about their mean, <code>SXX</code> is similarly defined and
+ * <code>SXY</code> is the sum of the products of x and y mean deviations.
+ * </p><p>
+ * The sums are accumulated using the updating algorithm referenced in
+ * {@link #addData}.</p>
+ * <p>
+ * The return value is constrained to be non-negative - i.e., if due to
+ * rounding errors the computational formula returns a negative result,
+ * 0 is returned.</p>
+ * <p>
+ * <strong>Preconditions</strong>: <ul>
+ * <li>At least two observations (with at least two different x values)
+ * must have been added before invoking this method. If this method is
+ * invoked before a model can be estimated, <code>Double,NaN</code> is
+ * returned.
+ * </li></ul></p>
+ *
+ * @return sum of squared errors associated with the regression model
+ */
+ public double getSumSquaredErrors() {
+ return FastMath.max(0d, sumYY - sumXY * sumXY / sumXX);
+ }
+
+ /**
+ * Returns the sum of squared deviations of the y values about their mean.
+ * <p>
+ * This is defined as SSTO
+ * <a href="http://www.xycoon.com/SumOfSquares.htm">here</a>.</p>
+ * <p>
+ * If <code>n < 2</code>, this returns <code>Double.NaN</code>.</p>
+ *
+ * @return sum of squared deviations of y values
+ */
+ public double getTotalSumSquares() {
+ if (n < 2) {
+ return Double.NaN;
+ }
+ return sumYY;
+ }
+
+ /**
+ * Returns the sum of squared deviations of the x values about their mean.
+ *
+ * If <code>n < 2</code>, this returns <code>Double.NaN</code>.</p>
+ *
+ * @return sum of squared deviations of x values
+ */
+ public double getXSumSquares() {
+ if (n < 2) {
+ return Double.NaN;
+ }
+ return sumXX;
+ }
+
+ /**
+ * Returns the sum of crossproducts, x<sub>i</sub>*y<sub>i</sub>.
+ *
+ * @return sum of cross products
+ */
+ public double getSumOfCrossProducts() {
+ return sumXY;
+ }
+
+ /**
+ * Returns the sum of squared deviations of the predicted y values about
+ * their mean (which equals the mean of y).
+ * <p>
+ * This is usually abbreviated SSR or SSM. It is defined as SSM
+ * <a href="http://www.xycoon.com/SumOfSquares.htm">here</a></p>
+ * <p>
+ * <strong>Preconditions</strong>: <ul>
+ * <li>At least two observations (with at least two different x values)
+ * must have been added before invoking this method. If this method is
+ * invoked before a model can be estimated, <code>Double.NaN</code> is
+ * returned.
+ * </li></ul></p>
+ *
+ * @return sum of squared deviations of predicted y values
+ */
+ public double getRegressionSumSquares() {
+ return getRegressionSumSquares(getSlope());
+ }
+
+ /**
+ * Returns the sum of squared errors divided by the degrees of freedom,
+ * usually abbreviated MSE.
+ * <p>
+ * If there are fewer than <strong>three</strong> data pairs in the model,
+ * or if there is no variation in <code>x</code>, this returns
+ * <code>Double.NaN</code>.</p>
+ *
+ * @return sum of squared deviations of y values
+ */
+ public double getMeanSquareError() {
+ if (n < 3) {
+ return Double.NaN;
+ }
+ return hasIntercept ? (getSumSquaredErrors() / (n - 2)) : (getSumSquaredErrors() / (n - 1));
+ }
+
+ /**
+ * Returns <a href="http://mathworld.wolfram.com/CorrelationCoefficient.html">
+ * Pearson's product moment correlation coefficient</a>,
+ * usually denoted r.
+ * <p>
+ * <strong>Preconditions</strong>: <ul>
+ * <li>At least two observations (with at least two different x values)
+ * must have been added before invoking this method. If this method is
+ * invoked before a model can be estimated, <code>Double,NaN</code> is
+ * returned.
+ * </li></ul></p>
+ *
+ * @return Pearson's r
+ */
+ public double getR() {
+ double b1 = getSlope();
+ double result = FastMath.sqrt(getRSquare());
+ if (b1 < 0) {
+ result = -result;
+ }
+ return result;
+ }
+
+ /**
+ * Returns the <a href="http://www.xycoon.com/coefficient1.htm">
+ * coefficient of determination</a>,
+ * usually denoted r-square.
+ * <p>
+ * <strong>Preconditions</strong>: <ul>
+ * <li>At least two observations (with at least two different x values)
+ * must have been added before invoking this method. If this method is
+ * invoked before a model can be estimated, <code>Double,NaN</code> is
+ * returned.
+ * </li></ul></p>
+ *
+ * @return r-square
+ */
+ public double getRSquare() {
+ double ssto = getTotalSumSquares();
+ return (ssto - getSumSquaredErrors()) / ssto;
+ }
+
+ /**
+ * Returns the <a href="http://www.xycoon.com/standarderrorb0.htm">
+ * standard error of the intercept estimate</a>,
+ * usually denoted s(b0).
+ * <p>
+ * If there are fewer that <strong>three</strong> observations in the
+ * model, or if there is no variation in x, this returns
+ * <code>Double.NaN</code>.</p> Additionally, a <code>Double.NaN</code> is
+ * returned when the intercept is constrained to be zero
+ *
+ * @return standard error associated with intercept estimate
+ */
+ public double getInterceptStdErr() {
+ if( !hasIntercept ){
+ return Double.NaN;
+ }
+ return FastMath.sqrt(
+ getMeanSquareError() * ((1d / n) + (xbar * xbar) / sumXX));
+ }
+
+ /**
+ * Returns the <a href="http://www.xycoon.com/standerrorb(1).htm">standard
+ * error of the slope estimate</a>,
+ * usually denoted s(b1).
+ * <p>
+ * If there are fewer that <strong>three</strong> data pairs in the model,
+ * or if there is no variation in x, this returns <code>Double.NaN</code>.
+ * </p>
+ *
+ * @return standard error associated with slope estimate
+ */
+ public double getSlopeStdErr() {
+ return FastMath.sqrt(getMeanSquareError() / sumXX);
+ }
+
+ /**
+ * Returns the half-width of a 95% confidence interval for the slope
+ * estimate.
+ * <p>
+ * The 95% confidence interval is</p>
+ * <p>
+ * <code>(getSlope() - getSlopeConfidenceInterval(),
+ * getSlope() + getSlopeConfidenceInterval())</code></p>
+ * <p>
+ * If there are fewer that <strong>three</strong> observations in the
+ * model, or if there is no variation in x, this returns
+ * <code>Double.NaN</code>.</p>
+ * <p>
+ * <strong>Usage Note</strong>:<br>
+ * The validity of this statistic depends on the assumption that the
+ * observations included in the model are drawn from a
+ * <a href="http://mathworld.wolfram.com/BivariateNormalDistribution.html">
+ * Bivariate Normal Distribution</a>.</p>
+ *
+ * @return half-width of 95% confidence interval for the slope estimate
+ * @throws OutOfRangeException if the confidence interval can not be computed.
+ */
+ public double getSlopeConfidenceInterval() throws OutOfRangeException {
+ return getSlopeConfidenceInterval(0.05d);
+ }
+
+ /**
+ * Returns the half-width of a (100-100*alpha)% confidence interval for
+ * the slope estimate.
+ * <p>
+ * The (100-100*alpha)% confidence interval is </p>
+ * <p>
+ * <code>(getSlope() - getSlopeConfidenceInterval(),
+ * getSlope() + getSlopeConfidenceInterval())</code></p>
+ * <p>
+ * To request, for example, a 99% confidence interval, use
+ * <code>alpha = .01</code></p>
+ * <p>
+ * <strong>Usage Note</strong>:<br>
+ * The validity of this statistic depends on the assumption that the
+ * observations included in the model are drawn from a
+ * <a href="http://mathworld.wolfram.com/BivariateNormalDistribution.html">
+ * Bivariate Normal Distribution</a>.</p>
+ * <p>
+ * <strong> Preconditions:</strong><ul>
+ * <li>If there are fewer that <strong>three</strong> observations in the
+ * model, or if there is no variation in x, this returns
+ * <code>Double.NaN</code>.
+ * </li>
+ * <li><code>(0 < alpha < 1)</code>; otherwise an
+ * <code>OutOfRangeException</code> is thrown.
+ * </li></ul></p>
+ *
+ * @param alpha the desired significance level
+ * @return half-width of 95% confidence interval for the slope estimate
+ * @throws OutOfRangeException if the confidence interval can not be computed.
+ */
+ public double getSlopeConfidenceInterval(final double alpha)
+ throws OutOfRangeException {
+ if (n < 3) {
+ return Double.NaN;
+ }
+ if (alpha >= 1 || alpha <= 0) {
+ throw new OutOfRangeException(LocalizedFormats.SIGNIFICANCE_LEVEL,
+ alpha, 0, 1);
+ }
+ // No advertised NotStrictlyPositiveException here - will return NaN above
+ TDistribution distribution = new TDistribution(n - 2);
+ return getSlopeStdErr() *
+ distribution.inverseCumulativeProbability(1d - alpha / 2d);
+ }
+
+ /**
+ * Returns the significance level of the slope (equiv) correlation.
+ * <p>
+ * Specifically, the returned value is the smallest <code>alpha</code>
+ * such that the slope confidence interval with significance level
+ * equal to <code>alpha</code> does not include <code>0</code>.
+ * On regression output, this is often denoted <code>Prob(|t| > 0)</code>
+ * </p><p>
+ * <strong>Usage Note</strong>:<br>
+ * The validity of this statistic depends on the assumption that the
+ * observations included in the model are drawn from a
+ * <a href="http://mathworld.wolfram.com/BivariateNormalDistribution.html">
+ * Bivariate Normal Distribution</a>.</p>
+ * <p>
+ * If there are fewer that <strong>three</strong> observations in the
+ * model, or if there is no variation in x, this returns
+ * <code>Double.NaN</code>.</p>
+ *
+ * @return significance level for slope/correlation
+ * @throws org.apache.commons.math3.exception.MaxCountExceededException
+ * if the significance level can not be computed.
+ */
+ public double getSignificance() {
+ if (n < 3) {
+ return Double.NaN;
+ }
+ // No advertised NotStrictlyPositiveException here - will return NaN above
+ TDistribution distribution = new TDistribution(n - 2);
+ return 2d * (1.0 - distribution.cumulativeProbability(
+ FastMath.abs(getSlope()) / getSlopeStdErr()));
+ }
+
+ // ---------------------Private methods-----------------------------------
+
+ /**
+ * Returns the intercept of the estimated regression line, given the slope.
+ * <p>
+ * Will return <code>NaN</code> if slope is <code>NaN</code>.</p>
+ *
+ * @param slope current slope
+ * @return the intercept of the regression line
+ */
+ private double getIntercept(final double slope) {
+ if( hasIntercept){
+ return (sumY - slope * sumX) / n;
+ }
+ return 0.0;
+ }
+
+ /**
+ * Computes SSR from b1.
+ *
+ * @param slope regression slope estimate
+ * @return sum of squared deviations of predicted y values
+ */
+ private double getRegressionSumSquares(final double slope) {
+ return slope * slope * sumXX;
+ }
+
+ /**
+ * Performs a regression on data present in buffers and outputs a RegressionResults object.
+ *
+ * <p>If there are fewer than 3 observations in the model and {@code hasIntercept} is true
+ * a {@code NoDataException} is thrown. If there is no intercept term, the model must
+ * contain at least 2 observations.</p>
+ *
+ * @return RegressionResults acts as a container of regression output
+ * @throws ModelSpecificationException if the model is not correctly specified
+ * @throws NoDataException if there is not sufficient data in the model to
+ * estimate the regression parameters
+ */
+ public RegressionResults regress() throws ModelSpecificationException, NoDataException {
+ if (hasIntercept) {
+ if (n < 3) {
+ throw new NoDataException(LocalizedFormats.NOT_ENOUGH_DATA_REGRESSION);
+ }
+ if (FastMath.abs(sumXX) > Precision.SAFE_MIN) {
+ final double[] params = new double[] { getIntercept(), getSlope() };
+ final double mse = getMeanSquareError();
+ final double _syy = sumYY + sumY * sumY / n;
+ final double[] vcv = new double[] { mse * (xbar * xbar / sumXX + 1.0 / n), -xbar * mse / sumXX, mse / sumXX };
+ return new RegressionResults(params, new double[][] { vcv }, true, n, 2, sumY, _syy, getSumSquaredErrors(), true,
+ false);
+ } else {
+ final double[] params = new double[] { sumY / n, Double.NaN };
+ // final double mse = getMeanSquareError();
+ final double[] vcv = new double[] { ybar / (n - 1.0), Double.NaN, Double.NaN };
+ return new RegressionResults(params, new double[][] { vcv }, true, n, 1, sumY, sumYY, getSumSquaredErrors(), true,
+ false);
+ }
+ } else {
+ if (n < 2) {
+ throw new NoDataException(LocalizedFormats.NOT_ENOUGH_DATA_REGRESSION);
+ }
+ if (!Double.isNaN(sumXX)) {
+ final double[] vcv = new double[] { getMeanSquareError() / sumXX };
+ final double[] params = new double[] { sumXY / sumXX };
+ return new RegressionResults(params, new double[][] { vcv }, true, n, 1, sumY, sumYY, getSumSquaredErrors(), false,
+ false);
+ } else {
+ final double[] vcv = new double[] { Double.NaN };
+ final double[] params = new double[] { Double.NaN };
+ return new RegressionResults(params, new double[][] { vcv }, true, n, 1, Double.NaN, Double.NaN, Double.NaN, false,
+ false);
+ }
+ }
+ }
+
+ /**
+ * Performs a regression on data present in buffers including only regressors
+ * indexed in variablesToInclude and outputs a RegressionResults object
+ * @param variablesToInclude an array of indices of regressors to include
+ * @return RegressionResults acts as a container of regression output
+ * @throws MathIllegalArgumentException if the variablesToInclude array is null or zero length
+ * @throws OutOfRangeException if a requested variable is not present in model
+ */
+ public RegressionResults regress(int[] variablesToInclude) throws MathIllegalArgumentException{
+ if( variablesToInclude == null || variablesToInclude.length == 0){
+ throw new MathIllegalArgumentException(LocalizedFormats.ARRAY_ZERO_LENGTH_OR_NULL_NOT_ALLOWED);
+ }
+ if( variablesToInclude.length > 2 || (variablesToInclude.length > 1 && !hasIntercept) ){
+ throw new ModelSpecificationException(
+ LocalizedFormats.ARRAY_SIZE_EXCEEDS_MAX_VARIABLES,
+ (variablesToInclude.length > 1 && !hasIntercept) ? 1 : 2);
+ }
+
+ if( hasIntercept ){
+ if( variablesToInclude.length == 2 ){
+ if( variablesToInclude[0] == 1 ){
+ throw new ModelSpecificationException(LocalizedFormats.NOT_INCREASING_SEQUENCE);
+ }else if( variablesToInclude[0] != 0 ){
+ throw new OutOfRangeException( variablesToInclude[0], 0,1 );
+ }
+ if( variablesToInclude[1] != 1){
+ throw new OutOfRangeException( variablesToInclude[0], 0,1 );
+ }
+ return regress();
+ }else{
+ if( variablesToInclude[0] != 1 && variablesToInclude[0] != 0 ){
+ throw new OutOfRangeException( variablesToInclude[0],0,1 );
+ }
+ final double _mean = sumY * sumY / n;
+ final double _syy = sumYY + _mean;
+ if( variablesToInclude[0] == 0 ){
+ //just the mean
+ final double[] vcv = new double[]{ sumYY/(((n-1)*n)) };
+ final double[] params = new double[]{ ybar };
+ return new RegressionResults(
+ params, new double[][]{vcv}, true, n, 1,
+ sumY, _syy+_mean, sumYY,true,false);
+
+ }else if( variablesToInclude[0] == 1){
+ //final double _syy = sumYY + sumY * sumY / ((double) n);
+ final double _sxx = sumXX + sumX * sumX / n;
+ final double _sxy = sumXY + sumX * sumY / n;
+ final double _sse = FastMath.max(0d, _syy - _sxy * _sxy / _sxx);
+ final double _mse = _sse/((n-1));
+ if( !Double.isNaN(_sxx) ){
+ final double[] vcv = new double[]{ _mse / _sxx };
+ final double[] params = new double[]{ _sxy/_sxx };
+ return new RegressionResults(
+ params, new double[][]{vcv}, true, n, 1,
+ sumY, _syy, _sse,false,false);
+ }else{
+ final double[] vcv = new double[]{Double.NaN };
+ final double[] params = new double[]{ Double.NaN };
+ return new RegressionResults(
+ params, new double[][]{vcv}, true, n, 1,
+ Double.NaN, Double.NaN, Double.NaN,false,false);
+ }
+ }
+ }
+ }else{
+ if( variablesToInclude[0] != 0 ){
+ throw new OutOfRangeException(variablesToInclude[0],0,0);
+ }
+ return regress();
+ }
+
+ return null;
+ }
+}
diff --git a/src/main/java/org/apache/commons/math3/stat/regression/UpdatingMultipleLinearRegression.java b/src/main/java/org/apache/commons/math3/stat/regression/UpdatingMultipleLinearRegression.java
new file mode 100644
index 0000000..ebefc31
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/stat/regression/UpdatingMultipleLinearRegression.java
@@ -0,0 +1,93 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math3.stat.regression;
+
+import org.apache.commons.math3.exception.MathIllegalArgumentException;
+import org.apache.commons.math3.exception.NoDataException;
+
+/**
+ * An interface for regression models allowing for dynamic updating of the data.
+ * That is, the entire data set need not be loaded into memory. As observations
+ * become available, they can be added to the regression model and an updated
+ * estimate regression statistics can be calculated.
+ *
+ * @since 3.0
+ */
+public interface UpdatingMultipleLinearRegression {
+
+ /**
+ * Returns true if a constant has been included false otherwise.
+ *
+ * @return true if constant exists, false otherwise
+ */
+ boolean hasIntercept();
+
+ /**
+ * Returns the number of observations added to the regression model.
+ *
+ * @return Number of observations
+ */
+ long getN();
+
+ /**
+ * Adds one observation to the regression model.
+ *
+ * @param x the independent variables which form the design matrix
+ * @param y the dependent or response variable
+ * @throws ModelSpecificationException if the length of {@code x} does not equal
+ * the number of independent variables in the model
+ */
+ void addObservation(double[] x, double y) throws ModelSpecificationException;
+
+ /**
+ * Adds a series of observations to the regression model. The lengths of
+ * x and y must be the same and x must be rectangular.
+ *
+ * @param x a series of observations on the independent variables
+ * @param y a series of observations on the dependent variable
+ * The length of x and y must be the same
+ * @throws ModelSpecificationException if {@code x} is not rectangular, does not match
+ * the length of {@code y} or does not contain sufficient data to estimate the model
+ */
+ void addObservations(double[][] x, double[] y) throws ModelSpecificationException;
+
+ /**
+ * Clears internal buffers and resets the regression model. This means all
+ * data and derived values are initialized
+ */
+ void clear();
+
+
+ /**
+ * Performs a regression on data present in buffers and outputs a RegressionResults object
+ * @return RegressionResults acts as a container of regression output
+ * @throws ModelSpecificationException if the model is not correctly specified
+ * @throws NoDataException if there is not sufficient data in the model to
+ * estimate the regression parameters
+ */
+ RegressionResults regress() throws ModelSpecificationException, NoDataException;
+
+ /**
+ * Performs a regression on data present in buffers including only regressors
+ * indexed in variablesToInclude and outputs a RegressionResults object
+ * @param variablesToInclude an array of indices of regressors to include
+ * @return RegressionResults acts as a container of regression output
+ * @throws ModelSpecificationException if the model is not correctly specified
+ * @throws MathIllegalArgumentException if the variablesToInclude array is null or zero length
+ */
+ RegressionResults regress(int[] variablesToInclude) throws ModelSpecificationException, MathIllegalArgumentException;
+}
diff --git a/src/main/java/org/apache/commons/math3/stat/regression/package-info.java b/src/main/java/org/apache/commons/math3/stat/regression/package-info.java
new file mode 100644
index 0000000..fbc0e12
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/stat/regression/package-info.java
@@ -0,0 +1,22 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/**
+ *
+ * Statistical routines involving multivariate data.
+ *
+ */
+package org.apache.commons.math3.stat.regression;
diff --git a/src/main/java/org/apache/commons/math3/transform/DctNormalization.java b/src/main/java/org/apache/commons/math3/transform/DctNormalization.java
new file mode 100644
index 0000000..2f38d04
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/transform/DctNormalization.java
@@ -0,0 +1,62 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math3.transform;
+
+/**
+ * This enumeration defines the various types of normalizations that can be applied to discrete
+ * cosine transforms (DCT). The exact definition of these normalizations is detailed below.
+ *
+ * @see FastCosineTransformer
+ * @since 3.0
+ */
+public enum DctNormalization {
+ /**
+ * Should be passed to the constructor of {@link FastCosineTransformer} to use the
+ * <em>standard</em> normalization convention. The standard DCT-I normalization convention is
+ * defined as follows
+ *
+ * <ul>
+ * <li>forward transform: y<sub>n</sub> = (1/2) [x<sub>0</sub> +
+ * (-1)<sup>n</sup>x<sub>N-1</sub>] + &sum;<sub>k=1</sub><sup>N-2</sup> x<sub>k</sub>
+ * cos[&pi; nk / (N - 1)],
+ * <li>inverse transform: x<sub>k</sub> = [1 / (N - 1)] [y<sub>0</sub> +
+ * (-1)<sup>k</sup>y<sub>N-1</sub>] + [2 / (N - 1)] &sum;<sub>n=1</sub><sup>N-2</sup>
+ * y<sub>n</sub> cos[&pi; nk / (N - 1)],
+ * </ul>
+ *
+ * where N is the size of the data sample.
+ */
+ STANDARD_DCT_I,
+
+ /**
+ * Should be passed to the constructor of {@link FastCosineTransformer} to use the
+ * <em>orthogonal</em> normalization convention. The orthogonal DCT-I normalization convention
+ * is defined as follows
+ *
+ * <ul>
+ * <li>forward transform: y<sub>n</sub> = [2(N - 1)]<sup>-1/2</sup> [x<sub>0</sub> +
+ * (-1)<sup>n</sup>x<sub>N-1</sub>] + [2 / (N - 1)]<sup>1/2</sup>
+ * &sum;<sub>k=1</sub><sup>N-2</sup> x<sub>k</sub> cos[&pi; nk / (N - 1)],
+ * <li>inverse transform: x<sub>k</sub> = [2(N - 1)]<sup>-1/2</sup> [y<sub>0</sub> +
+ * (-1)<sup>k</sup>y<sub>N-1</sub>] + [2 / (N - 1)]<sup>1/2</sup>
+ * &sum;<sub>n=1</sub><sup>N-2</sup> y<sub>n</sub> cos[&pi; nk / (N - 1)],
+ * </ul>
+ *
+ * which makes the transform orthogonal. N is the size of the data sample.
+ */
+ ORTHOGONAL_DCT_I;
+}
diff --git a/src/main/java/org/apache/commons/math3/transform/DftNormalization.java b/src/main/java/org/apache/commons/math3/transform/DftNormalization.java
new file mode 100644
index 0000000..db0eddb
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/transform/DftNormalization.java
@@ -0,0 +1,58 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math3.transform;
+
+/**
+ * This enumeration defines the various types of normalizations that can be applied to discrete
+ * Fourier transforms (DFT). The exact definition of these normalizations is detailed below.
+ *
+ * @see FastFourierTransformer
+ * @since 3.0
+ */
+public enum DftNormalization {
+ /**
+ * Should be passed to the constructor of {@link FastFourierTransformer} to use the
+ * <em>standard</em> normalization convention. This normalization convention is defined as
+ * follows
+ *
+ * <ul>
+ * <li>forward transform: y<sub>n</sub> = &sum;<sub>k=0</sub><sup>N-1</sup> x<sub>k</sub>
+ * exp(-2&pi;i n k / N),
+ * <li>inverse transform: x<sub>k</sub> = N<sup>-1</sup> &sum;<sub>n=0</sub><sup>N-1</sup>
+ * y<sub>n</sub> exp(2&pi;i n k / N),
+ * </ul>
+ *
+ * where N is the size of the data sample.
+ */
+ STANDARD,
+
+ /**
+ * Should be passed to the constructor of {@link FastFourierTransformer} to use the
+ * <em>unitary</em> normalization convention. This normalization convention is defined as
+ * follows
+ *
+ * <ul>
+ * <li>forward transform: y<sub>n</sub> = (1 / &radic;N) &sum;<sub>k=0</sub><sup>N-1</sup>
+ * x<sub>k</sub> exp(-2&pi;i n k / N),
+ * <li>inverse transform: x<sub>k</sub> = (1 / &radic;N) &sum;<sub>n=0</sub><sup>N-1</sup>
+ * y<sub>n</sub> exp(2&pi;i n k / N),
+ * </ul>
+ *
+ * which makes the transform unitary. N is the size of the data sample.
+ */
+ UNITARY;
+}
diff --git a/src/main/java/org/apache/commons/math3/transform/DstNormalization.java b/src/main/java/org/apache/commons/math3/transform/DstNormalization.java
new file mode 100644
index 0000000..0aba09e
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/transform/DstNormalization.java
@@ -0,0 +1,59 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math3.transform;
+
+/**
+ * This enumeration defines the various types of normalizations that can be applied to discrete sine
+ * transforms (DST). The exact definition of these normalizations is detailed below.
+ *
+ * @see FastSineTransformer
+ * @since 3.0
+ */
+public enum DstNormalization {
+ /**
+ * Should be passed to the constructor of {@link FastSineTransformer} to use the
+ * <em>standard</em> normalization convention. The standard DST-I normalization convention is
+ * defined as follows
+ *
+ * <ul>
+ * <li>forward transform: y<sub>n</sub> = &sum;<sub>k=0</sub><sup>N-1</sup> x<sub>k</sub>
+ * sin(&pi; nk / N),
+ * <li>inverse transform: x<sub>k</sub> = (2 / N) &sum;<sub>n=0</sub><sup>N-1</sup>
+ * y<sub>n</sub> sin(&pi; nk / N),
+ * </ul>
+ *
+ * where N is the size of the data sample, and x<sub>0</sub> = 0.
+ */
+ STANDARD_DST_I,
+
+ /**
+ * Should be passed to the constructor of {@link FastSineTransformer} to use the
+ * <em>orthogonal</em> normalization convention. The orthogonal DCT-I normalization convention
+ * is defined as follows
+ *
+ * <ul>
+ * <li>Forward transform: y<sub>n</sub> = &radic;(2 / N) &sum;<sub>k=0</sub><sup>N-1</sup>
+ * x<sub>k</sub> sin(&pi; nk / N),
+ * <li>Inverse transform: x<sub>k</sub> = &radic;(2 / N) &sum;<sub>n=0</sub><sup>N-1</sup>
+ * y<sub>n</sub> sin(&pi; nk / N),
+ * </ul>
+ *
+ * which makes the transform orthogonal. N is the size of the data sample, and x<sub>0</sub> =
+ * 0.
+ */
+ ORTHOGONAL_DST_I
+}
diff --git a/src/main/java/org/apache/commons/math3/transform/FastCosineTransformer.java b/src/main/java/org/apache/commons/math3/transform/FastCosineTransformer.java
new file mode 100644
index 0000000..1e73187
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/transform/FastCosineTransformer.java
@@ -0,0 +1,177 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math3.transform;
+
+import org.apache.commons.math3.analysis.FunctionUtils;
+import org.apache.commons.math3.analysis.UnivariateFunction;
+import org.apache.commons.math3.complex.Complex;
+import org.apache.commons.math3.exception.MathIllegalArgumentException;
+import org.apache.commons.math3.exception.util.LocalizedFormats;
+import org.apache.commons.math3.util.ArithmeticUtils;
+import org.apache.commons.math3.util.FastMath;
+
+import java.io.Serializable;
+
+/**
+ * Implements the Fast Cosine Transform for transformation of one-dimensional real data sets. For
+ * reference, see James S. Walker, <em>Fast Fourier Transforms</em>, chapter 3 (ISBN 0849371635).
+ *
+ * <p>There are several variants of the discrete cosine transform. The present implementation
+ * corresponds to DCT-I, with various normalization conventions, which are specified by the
+ * parameter {@link DctNormalization}.
+ *
+ * <p>DCT-I is equivalent to DFT of an <em>even extension</em> of the data series. More precisely,
+ * if x<sub>0</sub>, &hellip;, x<sub>N-1</sub> is the data set to be cosine transformed, the
+ * extended data set x<sub>0</sub><sup>&#35;</sup>, &hellip;, x<sub>2N-3</sub><sup>&#35;</sup> is
+ * defined as follows
+ *
+ * <ul>
+ * <li>x<sub>k</sub><sup>&#35;</sup> = x<sub>k</sub> if 0 &le; k &lt; N,
+ * <li>x<sub>k</sub><sup>&#35;</sup> = x<sub>2N-2-k</sub> if N &le; k &lt; 2N - 2.
+ * </ul>
+ *
+ * <p>Then, the standard DCT-I y<sub>0</sub>, &hellip;, y<sub>N-1</sub> of the real data set
+ * x<sub>0</sub>, &hellip;, x<sub>N-1</sub> is equal to <em>half</em> of the N first elements of the
+ * DFT of the extended data set x<sub>0</sub><sup>&#35;</sup>, &hellip;,
+ * x<sub>2N-3</sub><sup>&#35;</sup> <br>
+ * y<sub>n</sub> = (1 / 2) &sum;<sub>k=0</sub><sup>2N-3</sup> x<sub>k</sub><sup>&#35;</sup>
+ * exp[-2&pi;i nk / (2N - 2)] &nbsp;&nbsp;&nbsp;&nbsp;k = 0, &hellip;, N-1.
+ *
+ * <p>The present implementation of the discrete cosine transform as a fast cosine transform
+ * requires the length of the data set to be a power of two plus one
+ * (N&nbsp;=&nbsp;2<sup>n</sup>&nbsp;+&nbsp;1). Besides, it implicitly assumes that the sampled
+ * function is even.
+ *
+ * @since 1.2
+ */
+public class FastCosineTransformer implements RealTransformer, Serializable {
+
+ /** Serializable version identifier. */
+ static final long serialVersionUID = 20120212L;
+
+ /** The type of DCT to be performed. */
+ private final DctNormalization normalization;
+
+ /**
+ * Creates a new instance of this class, with various normalization conventions.
+ *
+ * @param normalization the type of normalization to be applied to the transformed data
+ */
+ public FastCosineTransformer(final DctNormalization normalization) {
+ this.normalization = normalization;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @throws MathIllegalArgumentException if the length of the data array is not a power of two
+ * plus one
+ */
+ public double[] transform(final double[] f, final TransformType type)
+ throws MathIllegalArgumentException {
+ if (type == TransformType.FORWARD) {
+ if (normalization == DctNormalization.ORTHOGONAL_DCT_I) {
+ final double s = FastMath.sqrt(2.0 / (f.length - 1));
+ return TransformUtils.scaleArray(fct(f), s);
+ }
+ return fct(f);
+ }
+ final double s2 = 2.0 / (f.length - 1);
+ final double s1;
+ if (normalization == DctNormalization.ORTHOGONAL_DCT_I) {
+ s1 = FastMath.sqrt(s2);
+ } else {
+ s1 = s2;
+ }
+ return TransformUtils.scaleArray(fct(f), s1);
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @throws org.apache.commons.math3.exception.NonMonotonicSequenceException if the lower bound
+ * is greater than, or equal to the upper bound
+ * @throws org.apache.commons.math3.exception.NotStrictlyPositiveException if the number of
+ * sample points is negative
+ * @throws MathIllegalArgumentException if the number of sample points is not a power of two
+ * plus one
+ */
+ public double[] transform(
+ final UnivariateFunction f,
+ final double min,
+ final double max,
+ final int n,
+ final TransformType type)
+ throws MathIllegalArgumentException {
+
+ final double[] data = FunctionUtils.sample(f, min, max, n);
+ return transform(data, type);
+ }
+
+ /**
+ * Perform the FCT algorithm (including inverse).
+ *
+ * @param f the real data array to be transformed
+ * @return the real transformed array
+ * @throws MathIllegalArgumentException if the length of the data array is not a power of two
+ * plus one
+ */
+ protected double[] fct(double[] f) throws MathIllegalArgumentException {
+
+ final double[] transformed = new double[f.length];
+
+ final int n = f.length - 1;
+ if (!ArithmeticUtils.isPowerOfTwo(n)) {
+ throw new MathIllegalArgumentException(
+ LocalizedFormats.NOT_POWER_OF_TWO_PLUS_ONE, Integer.valueOf(f.length));
+ }
+ if (n == 1) { // trivial case
+ transformed[0] = 0.5 * (f[0] + f[1]);
+ transformed[1] = 0.5 * (f[0] - f[1]);
+ return transformed;
+ }
+
+ // construct a new array and perform FFT on it
+ final double[] x = new double[n];
+ x[0] = 0.5 * (f[0] + f[n]);
+ x[n >> 1] = f[n >> 1];
+ // temporary variable for transformed[1]
+ double t1 = 0.5 * (f[0] - f[n]);
+ for (int i = 1; i < (n >> 1); i++) {
+ final double a = 0.5 * (f[i] + f[n - i]);
+ final double b = FastMath.sin(i * FastMath.PI / n) * (f[i] - f[n - i]);
+ final double c = FastMath.cos(i * FastMath.PI / n) * (f[i] - f[n - i]);
+ x[i] = a - b;
+ x[n - i] = a + b;
+ t1 += c;
+ }
+ FastFourierTransformer transformer;
+ transformer = new FastFourierTransformer(DftNormalization.STANDARD);
+ Complex[] y = transformer.transform(x, TransformType.FORWARD);
+
+ // reconstruct the FCT result for the original array
+ transformed[0] = y[0].getReal();
+ transformed[1] = t1;
+ for (int i = 1; i < (n >> 1); i++) {
+ transformed[2 * i] = y[i].getReal();
+ transformed[2 * i + 1] = transformed[2 * i - 1] - y[i].getImaginary();
+ }
+ transformed[n] = y[n >> 1].getReal();
+
+ return transformed;
+ }
+}
diff --git a/src/main/java/org/apache/commons/math3/transform/FastFourierTransformer.java b/src/main/java/org/apache/commons/math3/transform/FastFourierTransformer.java
new file mode 100644
index 0000000..116b21d
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/transform/FastFourierTransformer.java
@@ -0,0 +1,749 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math3.transform;
+
+import org.apache.commons.math3.analysis.FunctionUtils;
+import org.apache.commons.math3.analysis.UnivariateFunction;
+import org.apache.commons.math3.complex.Complex;
+import org.apache.commons.math3.exception.DimensionMismatchException;
+import org.apache.commons.math3.exception.MathIllegalArgumentException;
+import org.apache.commons.math3.exception.MathIllegalStateException;
+import org.apache.commons.math3.exception.util.LocalizedFormats;
+import org.apache.commons.math3.util.ArithmeticUtils;
+import org.apache.commons.math3.util.FastMath;
+import org.apache.commons.math3.util.MathArrays;
+
+import java.io.Serializable;
+import java.lang.reflect.Array;
+
+/**
+ * Implements the Fast Fourier Transform for transformation of one-dimensional real or complex data
+ * sets. For reference, see <em>Applied Numerical Linear Algebra</em>, ISBN 0898713897, chapter 6.
+ *
+ * <p>There are several variants of the discrete Fourier transform, with various normalization
+ * conventions, which are specified by the parameter {@link DftNormalization}.
+ *
+ * <p>The current implementation of the discrete Fourier transform as a fast Fourier transform
+ * requires the length of the data set to be a power of 2. This greatly simplifies and speeds up the
+ * code. Users can pad the data with zeros to meet this requirement. There are other flavors of FFT,
+ * for reference, see S. Winograd, <i>On computing the discrete Fourier transform</i>, Mathematics
+ * of Computation, 32 (1978), 175 - 199.
+ *
+ * @see DftNormalization
+ * @since 1.2
+ */
+public class FastFourierTransformer implements Serializable {
+
+ /** Serializable version identifier. */
+ static final long serialVersionUID = 20120210L;
+
+ /**
+ * {@code W_SUB_N_R[i]} is the real part of {@code exp(- 2 * i * pi / n)}: {@code W_SUB_N_R[i] =
+ * cos(2 * pi/ n)}, where {@code n = 2^i}.
+ */
+ private static final double[] W_SUB_N_R = {
+ 0x1.0p0,
+ -0x1.0p0,
+ 0x1.1a62633145c07p-54,
+ 0x1.6a09e667f3bcdp-1,
+ 0x1.d906bcf328d46p-1,
+ 0x1.f6297cff75cbp-1,
+ 0x1.fd88da3d12526p-1,
+ 0x1.ff621e3796d7ep-1,
+ 0x1.ffd886084cd0dp-1,
+ 0x1.fff62169b92dbp-1,
+ 0x1.fffd8858e8a92p-1,
+ 0x1.ffff621621d02p-1,
+ 0x1.ffffd88586ee6p-1,
+ 0x1.fffff62161a34p-1,
+ 0x1.fffffd8858675p-1,
+ 0x1.ffffff621619cp-1,
+ 0x1.ffffffd885867p-1,
+ 0x1.fffffff62161ap-1,
+ 0x1.fffffffd88586p-1,
+ 0x1.ffffffff62162p-1,
+ 0x1.ffffffffd8858p-1,
+ 0x1.fffffffff6216p-1,
+ 0x1.fffffffffd886p-1,
+ 0x1.ffffffffff621p-1,
+ 0x1.ffffffffffd88p-1,
+ 0x1.fffffffffff62p-1,
+ 0x1.fffffffffffd9p-1,
+ 0x1.ffffffffffff6p-1,
+ 0x1.ffffffffffffep-1,
+ 0x1.fffffffffffffp-1,
+ 0x1.0p0,
+ 0x1.0p0,
+ 0x1.0p0,
+ 0x1.0p0,
+ 0x1.0p0,
+ 0x1.0p0,
+ 0x1.0p0,
+ 0x1.0p0,
+ 0x1.0p0,
+ 0x1.0p0,
+ 0x1.0p0,
+ 0x1.0p0,
+ 0x1.0p0,
+ 0x1.0p0,
+ 0x1.0p0,
+ 0x1.0p0,
+ 0x1.0p0,
+ 0x1.0p0,
+ 0x1.0p0,
+ 0x1.0p0,
+ 0x1.0p0,
+ 0x1.0p0,
+ 0x1.0p0,
+ 0x1.0p0,
+ 0x1.0p0,
+ 0x1.0p0,
+ 0x1.0p0,
+ 0x1.0p0,
+ 0x1.0p0,
+ 0x1.0p0,
+ 0x1.0p0,
+ 0x1.0p0,
+ 0x1.0p0
+ };
+
+ /**
+ * {@code W_SUB_N_I[i]} is the imaginary part of {@code exp(- 2 * i * pi / n)}: {@code
+ * W_SUB_N_I[i] = -sin(2 * pi/ n)}, where {@code n = 2^i}.
+ */
+ private static final double[] W_SUB_N_I = {
+ 0x1.1a62633145c07p-52,
+ -0x1.1a62633145c07p-53,
+ -0x1.0p0,
+ -0x1.6a09e667f3bccp-1,
+ -0x1.87de2a6aea963p-2,
+ -0x1.8f8b83c69a60ap-3,
+ -0x1.917a6bc29b42cp-4,
+ -0x1.91f65f10dd814p-5,
+ -0x1.92155f7a3667ep-6,
+ -0x1.921d1fcdec784p-7,
+ -0x1.921f0fe670071p-8,
+ -0x1.921f8becca4bap-9,
+ -0x1.921faaee6472dp-10,
+ -0x1.921fb2aecb36p-11,
+ -0x1.921fb49ee4ea6p-12,
+ -0x1.921fb51aeb57bp-13,
+ -0x1.921fb539ecf31p-14,
+ -0x1.921fb541ad59ep-15,
+ -0x1.921fb5439d73ap-16,
+ -0x1.921fb544197ap-17,
+ -0x1.921fb544387bap-18,
+ -0x1.921fb544403c1p-19,
+ -0x1.921fb544422c2p-20,
+ -0x1.921fb54442a83p-21,
+ -0x1.921fb54442c73p-22,
+ -0x1.921fb54442cefp-23,
+ -0x1.921fb54442d0ep-24,
+ -0x1.921fb54442d15p-25,
+ -0x1.921fb54442d17p-26,
+ -0x1.921fb54442d18p-27,
+ -0x1.921fb54442d18p-28,
+ -0x1.921fb54442d18p-29,
+ -0x1.921fb54442d18p-30,
+ -0x1.921fb54442d18p-31,
+ -0x1.921fb54442d18p-32,
+ -0x1.921fb54442d18p-33,
+ -0x1.921fb54442d18p-34,
+ -0x1.921fb54442d18p-35,
+ -0x1.921fb54442d18p-36,
+ -0x1.921fb54442d18p-37,
+ -0x1.921fb54442d18p-38,
+ -0x1.921fb54442d18p-39,
+ -0x1.921fb54442d18p-40,
+ -0x1.921fb54442d18p-41,
+ -0x1.921fb54442d18p-42,
+ -0x1.921fb54442d18p-43,
+ -0x1.921fb54442d18p-44,
+ -0x1.921fb54442d18p-45,
+ -0x1.921fb54442d18p-46,
+ -0x1.921fb54442d18p-47,
+ -0x1.921fb54442d18p-48,
+ -0x1.921fb54442d18p-49,
+ -0x1.921fb54442d18p-50,
+ -0x1.921fb54442d18p-51,
+ -0x1.921fb54442d18p-52,
+ -0x1.921fb54442d18p-53,
+ -0x1.921fb54442d18p-54,
+ -0x1.921fb54442d18p-55,
+ -0x1.921fb54442d18p-56,
+ -0x1.921fb54442d18p-57,
+ -0x1.921fb54442d18p-58,
+ -0x1.921fb54442d18p-59,
+ -0x1.921fb54442d18p-60
+ };
+
+ /** The type of DFT to be performed. */
+ private final DftNormalization normalization;
+
+ /**
+ * Creates a new instance of this class, with various normalization conventions.
+ *
+ * @param normalization the type of normalization to be applied to the transformed data
+ */
+ public FastFourierTransformer(final DftNormalization normalization) {
+ this.normalization = normalization;
+ }
+
+ /**
+ * Performs identical index bit reversal shuffles on two arrays of identical size. Each element
+ * in the array is swapped with another element based on the bit-reversal of the index. For
+ * example, in an array with length 16, item at binary index 0011 (decimal 3) would be swapped
+ * with the item at binary index 1100 (decimal 12).
+ *
+ * @param a the first array to be shuffled
+ * @param b the second array to be shuffled
+ */
+ private static void bitReversalShuffle2(double[] a, double[] b) {
+ final int n = a.length;
+ assert b.length == n;
+ final int halfOfN = n >> 1;
+
+ int j = 0;
+ for (int i = 0; i < n; i++) {
+ if (i < j) {
+ // swap indices i & j
+ double temp = a[i];
+ a[i] = a[j];
+ a[j] = temp;
+
+ temp = b[i];
+ b[i] = b[j];
+ b[j] = temp;
+ }
+
+ int k = halfOfN;
+ while (k <= j && k > 0) {
+ j -= k;
+ k >>= 1;
+ }
+ j += k;
+ }
+ }
+
+ /**
+ * Applies the proper normalization to the specified transformed data.
+ *
+ * @param dataRI the unscaled transformed data
+ * @param normalization the normalization to be applied
+ * @param type the type of transform (forward, inverse) which resulted in the specified data
+ */
+ private static void normalizeTransformedData(
+ final double[][] dataRI,
+ final DftNormalization normalization,
+ final TransformType type) {
+
+ final double[] dataR = dataRI[0];
+ final double[] dataI = dataRI[1];
+ final int n = dataR.length;
+ assert dataI.length == n;
+
+ switch (normalization) {
+ case STANDARD:
+ if (type == TransformType.INVERSE) {
+ final double scaleFactor = 1.0 / ((double) n);
+ for (int i = 0; i < n; i++) {
+ dataR[i] *= scaleFactor;
+ dataI[i] *= scaleFactor;
+ }
+ }
+ break;
+ case UNITARY:
+ final double scaleFactor = 1.0 / FastMath.sqrt(n);
+ for (int i = 0; i < n; i++) {
+ dataR[i] *= scaleFactor;
+ dataI[i] *= scaleFactor;
+ }
+ break;
+ default:
+ /*
+ * This should never occur in normal conditions. However this
+ * clause has been added as a safeguard if other types of
+ * normalizations are ever implemented, and the corresponding
+ * test is forgotten in the present switch.
+ */
+ throw new MathIllegalStateException();
+ }
+ }
+
+ /**
+ * Computes the standard transform of the specified complex data. The computation is done in
+ * place. The input data is laid out as follows
+ *
+ * <ul>
+ * <li>{@code dataRI[0][i]} is the real part of the {@code i}-th data point,
+ * <li>{@code dataRI[1][i]} is the imaginary part of the {@code i}-th data point.
+ * </ul>
+ *
+ * @param dataRI the two dimensional array of real and imaginary parts of the data
+ * @param normalization the normalization to be applied to the transformed data
+ * @param type the type of transform (forward, inverse) to be performed
+ * @throws DimensionMismatchException if the number of rows of the specified array is not two,
+ * or the array is not rectangular
+ * @throws MathIllegalArgumentException if the number of data points is not a power of two
+ */
+ public static void transformInPlace(
+ final double[][] dataRI,
+ final DftNormalization normalization,
+ final TransformType type) {
+
+ if (dataRI.length != 2) {
+ throw new DimensionMismatchException(dataRI.length, 2);
+ }
+ final double[] dataR = dataRI[0];
+ final double[] dataI = dataRI[1];
+ if (dataR.length != dataI.length) {
+ throw new DimensionMismatchException(dataI.length, dataR.length);
+ }
+
+ final int n = dataR.length;
+ if (!ArithmeticUtils.isPowerOfTwo(n)) {
+ throw new MathIllegalArgumentException(
+ LocalizedFormats.NOT_POWER_OF_TWO_CONSIDER_PADDING, Integer.valueOf(n));
+ }
+
+ if (n == 1) {
+ return;
+ } else if (n == 2) {
+ final double srcR0 = dataR[0];
+ final double srcI0 = dataI[0];
+ final double srcR1 = dataR[1];
+ final double srcI1 = dataI[1];
+
+ // X_0 = x_0 + x_1
+ dataR[0] = srcR0 + srcR1;
+ dataI[0] = srcI0 + srcI1;
+ // X_1 = x_0 - x_1
+ dataR[1] = srcR0 - srcR1;
+ dataI[1] = srcI0 - srcI1;
+
+ normalizeTransformedData(dataRI, normalization, type);
+ return;
+ }
+
+ bitReversalShuffle2(dataR, dataI);
+
+ // Do 4-term DFT.
+ if (type == TransformType.INVERSE) {
+ for (int i0 = 0; i0 < n; i0 += 4) {
+ final int i1 = i0 + 1;
+ final int i2 = i0 + 2;
+ final int i3 = i0 + 3;
+
+ final double srcR0 = dataR[i0];
+ final double srcI0 = dataI[i0];
+ final double srcR1 = dataR[i2];
+ final double srcI1 = dataI[i2];
+ final double srcR2 = dataR[i1];
+ final double srcI2 = dataI[i1];
+ final double srcR3 = dataR[i3];
+ final double srcI3 = dataI[i3];
+
+ // 4-term DFT
+ // X_0 = x_0 + x_1 + x_2 + x_3
+ dataR[i0] = srcR0 + srcR1 + srcR2 + srcR3;
+ dataI[i0] = srcI0 + srcI1 + srcI2 + srcI3;
+ // X_1 = x_0 - x_2 + j * (x_3 - x_1)
+ dataR[i1] = srcR0 - srcR2 + (srcI3 - srcI1);
+ dataI[i1] = srcI0 - srcI2 + (srcR1 - srcR3);
+ // X_2 = x_0 - x_1 + x_2 - x_3
+ dataR[i2] = srcR0 - srcR1 + srcR2 - srcR3;
+ dataI[i2] = srcI0 - srcI1 + srcI2 - srcI3;
+ // X_3 = x_0 - x_2 + j * (x_1 - x_3)
+ dataR[i3] = srcR0 - srcR2 + (srcI1 - srcI3);
+ dataI[i3] = srcI0 - srcI2 + (srcR3 - srcR1);
+ }
+ } else {
+ for (int i0 = 0; i0 < n; i0 += 4) {
+ final int i1 = i0 + 1;
+ final int i2 = i0 + 2;
+ final int i3 = i0 + 3;
+
+ final double srcR0 = dataR[i0];
+ final double srcI0 = dataI[i0];
+ final double srcR1 = dataR[i2];
+ final double srcI1 = dataI[i2];
+ final double srcR2 = dataR[i1];
+ final double srcI2 = dataI[i1];
+ final double srcR3 = dataR[i3];
+ final double srcI3 = dataI[i3];
+
+ // 4-term DFT
+ // X_0 = x_0 + x_1 + x_2 + x_3
+ dataR[i0] = srcR0 + srcR1 + srcR2 + srcR3;
+ dataI[i0] = srcI0 + srcI1 + srcI2 + srcI3;
+ // X_1 = x_0 - x_2 + j * (x_3 - x_1)
+ dataR[i1] = srcR0 - srcR2 + (srcI1 - srcI3);
+ dataI[i1] = srcI0 - srcI2 + (srcR3 - srcR1);
+ // X_2 = x_0 - x_1 + x_2 - x_3
+ dataR[i2] = srcR0 - srcR1 + srcR2 - srcR3;
+ dataI[i2] = srcI0 - srcI1 + srcI2 - srcI3;
+ // X_3 = x_0 - x_2 + j * (x_1 - x_3)
+ dataR[i3] = srcR0 - srcR2 + (srcI3 - srcI1);
+ dataI[i3] = srcI0 - srcI2 + (srcR1 - srcR3);
+ }
+ }
+
+ int lastN0 = 4;
+ int lastLogN0 = 2;
+ while (lastN0 < n) {
+ int n0 = lastN0 << 1;
+ int logN0 = lastLogN0 + 1;
+ double wSubN0R = W_SUB_N_R[logN0];
+ double wSubN0I = W_SUB_N_I[logN0];
+ if (type == TransformType.INVERSE) {
+ wSubN0I = -wSubN0I;
+ }
+
+ // Combine even/odd transforms of size lastN0 into a transform of size N0 (lastN0 * 2).
+ for (int destEvenStartIndex = 0; destEvenStartIndex < n; destEvenStartIndex += n0) {
+ int destOddStartIndex = destEvenStartIndex + lastN0;
+
+ double wSubN0ToRR = 1;
+ double wSubN0ToRI = 0;
+
+ for (int r = 0; r < lastN0; r++) {
+ double grR = dataR[destEvenStartIndex + r];
+ double grI = dataI[destEvenStartIndex + r];
+ double hrR = dataR[destOddStartIndex + r];
+ double hrI = dataI[destOddStartIndex + r];
+
+ // dest[destEvenStartIndex + r] = Gr + WsubN0ToR * Hr
+ dataR[destEvenStartIndex + r] = grR + wSubN0ToRR * hrR - wSubN0ToRI * hrI;
+ dataI[destEvenStartIndex + r] = grI + wSubN0ToRR * hrI + wSubN0ToRI * hrR;
+ // dest[destOddStartIndex + r] = Gr - WsubN0ToR * Hr
+ dataR[destOddStartIndex + r] = grR - (wSubN0ToRR * hrR - wSubN0ToRI * hrI);
+ dataI[destOddStartIndex + r] = grI - (wSubN0ToRR * hrI + wSubN0ToRI * hrR);
+
+ // WsubN0ToR *= WsubN0R
+ double nextWsubN0ToRR = wSubN0ToRR * wSubN0R - wSubN0ToRI * wSubN0I;
+ double nextWsubN0ToRI = wSubN0ToRR * wSubN0I + wSubN0ToRI * wSubN0R;
+ wSubN0ToRR = nextWsubN0ToRR;
+ wSubN0ToRI = nextWsubN0ToRI;
+ }
+ }
+
+ lastN0 = n0;
+ lastLogN0 = logN0;
+ }
+
+ normalizeTransformedData(dataRI, normalization, type);
+ }
+
+ /**
+ * Returns the (forward, inverse) transform of the specified real data set.
+ *
+ * @param f the real data array to be transformed
+ * @param type the type of transform (forward, inverse) to be performed
+ * @return the complex transformed array
+ * @throws MathIllegalArgumentException if the length of the data array is not a power of two
+ */
+ public Complex[] transform(final double[] f, final TransformType type) {
+ final double[][] dataRI =
+ new double[][] {MathArrays.copyOf(f, f.length), new double[f.length]};
+
+ transformInPlace(dataRI, normalization, type);
+
+ return TransformUtils.createComplexArray(dataRI);
+ }
+
+ /**
+ * Returns the (forward, inverse) transform of the specified real function, sampled on the
+ * specified interval.
+ *
+ * @param f the function to be sampled and transformed
+ * @param min the (inclusive) lower bound for the interval
+ * @param max the (exclusive) upper bound for the interval
+ * @param n the number of sample points
+ * @param type the type of transform (forward, inverse) to be performed
+ * @return the complex transformed array
+ * @throws org.apache.commons.math3.exception.NumberIsTooLargeException if the lower bound is
+ * greater than, or equal to the upper bound
+ * @throws org.apache.commons.math3.exception.NotStrictlyPositiveException if the number of
+ * sample points {@code n} is negative
+ * @throws MathIllegalArgumentException if the number of sample points {@code n} is not a power
+ * of two
+ */
+ public Complex[] transform(
+ final UnivariateFunction f,
+ final double min,
+ final double max,
+ final int n,
+ final TransformType type) {
+
+ final double[] data = FunctionUtils.sample(f, min, max, n);
+ return transform(data, type);
+ }
+
+ /**
+ * Returns the (forward, inverse) transform of the specified complex data set.
+ *
+ * @param f the complex data array to be transformed
+ * @param type the type of transform (forward, inverse) to be performed
+ * @return the complex transformed array
+ * @throws MathIllegalArgumentException if the length of the data array is not a power of two
+ */
+ public Complex[] transform(final Complex[] f, final TransformType type) {
+ final double[][] dataRI = TransformUtils.createRealImaginaryArray(f);
+
+ transformInPlace(dataRI, normalization, type);
+
+ return TransformUtils.createComplexArray(dataRI);
+ }
+
+ /**
+ * Performs a multi-dimensional Fourier transform on a given array. Use {@link
+ * #transform(Complex[], TransformType)} in a row-column implementation in any number of
+ * dimensions with O(N&times;log(N)) complexity with N = n<sub>1</sub> &times; n<sub>2</sub>
+ * &times;n<sub>3</sub> &times; ... &times; n<sub>d</sub>, where n<sub>k</sub> is the number of
+ * elements in dimension k, and d is the total number of dimensions.
+ *
+ * @param mdca Multi-Dimensional Complex Array, i.e. {@code Complex[][][][]}
+ * @param type the type of transform (forward, inverse) to be performed
+ * @return transform of {@code mdca} as a Multi-Dimensional Complex Array, i.e. {@code
+ * Complex[][][][]}
+ * @throws IllegalArgumentException if any dimension is not a power of two
+ * @deprecated see MATH-736
+ */
+ @Deprecated
+ public Object mdfft(Object mdca, TransformType type) {
+ MultiDimensionalComplexMatrix mdcm =
+ (MultiDimensionalComplexMatrix) new MultiDimensionalComplexMatrix(mdca).clone();
+ int[] dimensionSize = mdcm.getDimensionSizes();
+ // cycle through each dimension
+ for (int i = 0; i < dimensionSize.length; i++) {
+ mdfft(mdcm, type, i, new int[0]);
+ }
+ return mdcm.getArray();
+ }
+
+ /**
+ * Performs one dimension of a multi-dimensional Fourier transform.
+ *
+ * @param mdcm input matrix
+ * @param type the type of transform (forward, inverse) to be performed
+ * @param d index of the dimension to process
+ * @param subVector recursion subvector
+ * @throws IllegalArgumentException if any dimension is not a power of two
+ * @deprecated see MATH-736
+ */
+ @Deprecated
+ private void mdfft(
+ MultiDimensionalComplexMatrix mdcm, TransformType type, int d, int[] subVector) {
+
+ int[] dimensionSize = mdcm.getDimensionSizes();
+ // if done
+ if (subVector.length == dimensionSize.length) {
+ Complex[] temp = new Complex[dimensionSize[d]];
+ for (int i = 0; i < dimensionSize[d]; i++) {
+ // fft along dimension d
+ subVector[d] = i;
+ temp[i] = mdcm.get(subVector);
+ }
+
+ temp = transform(temp, type);
+
+ for (int i = 0; i < dimensionSize[d]; i++) {
+ subVector[d] = i;
+ mdcm.set(temp[i], subVector);
+ }
+ } else {
+ int[] vector = new int[subVector.length + 1];
+ System.arraycopy(subVector, 0, vector, 0, subVector.length);
+ if (subVector.length == d) {
+ // value is not important once the recursion is done.
+ // then an fft will be applied along the dimension d.
+ vector[d] = 0;
+ mdfft(mdcm, type, d, vector);
+ } else {
+ for (int i = 0; i < dimensionSize[subVector.length]; i++) {
+ vector[subVector.length] = i;
+ // further split along the next dimension
+ mdfft(mdcm, type, d, vector);
+ }
+ }
+ }
+ }
+
+ /**
+ * Complex matrix implementation. Not designed for synchronized access may eventually be
+ * replaced by jsr-83 of the java community process http://jcp.org/en/jsr/detail?id=83 may
+ * require additional exception throws for other basic requirements.
+ *
+ * @deprecated see MATH-736
+ */
+ @Deprecated
+ private static class MultiDimensionalComplexMatrix implements Cloneable {
+
+ /** Size in all dimensions. */
+ protected int[] dimensionSize;
+
+ /** Storage array. */
+ protected Object multiDimensionalComplexArray;
+
+ /**
+ * Simple constructor.
+ *
+ * @param multiDimensionalComplexArray array containing the matrix elements
+ */
+ MultiDimensionalComplexMatrix(Object multiDimensionalComplexArray) {
+
+ this.multiDimensionalComplexArray = multiDimensionalComplexArray;
+
+ // count dimensions
+ int numOfDimensions = 0;
+ for (Object lastDimension = multiDimensionalComplexArray;
+ lastDimension instanceof Object[]; ) {
+ final Object[] array = (Object[]) lastDimension;
+ numOfDimensions++;
+ lastDimension = array[0];
+ }
+
+ // allocate array with exact count
+ dimensionSize = new int[numOfDimensions];
+
+ // fill array
+ numOfDimensions = 0;
+ for (Object lastDimension = multiDimensionalComplexArray;
+ lastDimension instanceof Object[]; ) {
+ final Object[] array = (Object[]) lastDimension;
+ dimensionSize[numOfDimensions++] = array.length;
+ lastDimension = array[0];
+ }
+ }
+
+ /**
+ * Get a matrix element.
+ *
+ * @param vector indices of the element
+ * @return matrix element
+ * @exception DimensionMismatchException if dimensions do not match
+ */
+ public Complex get(int... vector) throws DimensionMismatchException {
+
+ if (vector == null) {
+ if (dimensionSize.length > 0) {
+ throw new DimensionMismatchException(0, dimensionSize.length);
+ }
+ return null;
+ }
+ if (vector.length != dimensionSize.length) {
+ throw new DimensionMismatchException(vector.length, dimensionSize.length);
+ }
+
+ Object lastDimension = multiDimensionalComplexArray;
+
+ for (int i = 0; i < dimensionSize.length; i++) {
+ lastDimension = ((Object[]) lastDimension)[vector[i]];
+ }
+ return (Complex) lastDimension;
+ }
+
+ /**
+ * Set a matrix element.
+ *
+ * @param magnitude magnitude of the element
+ * @param vector indices of the element
+ * @return the previous value
+ * @exception DimensionMismatchException if dimensions do not match
+ */
+ public Complex set(Complex magnitude, int... vector) throws DimensionMismatchException {
+
+ if (vector == null) {
+ if (dimensionSize.length > 0) {
+ throw new DimensionMismatchException(0, dimensionSize.length);
+ }
+ return null;
+ }
+ if (vector.length != dimensionSize.length) {
+ throw new DimensionMismatchException(vector.length, dimensionSize.length);
+ }
+
+ Object[] lastDimension = (Object[]) multiDimensionalComplexArray;
+ for (int i = 0; i < dimensionSize.length - 1; i++) {
+ lastDimension = (Object[]) lastDimension[vector[i]];
+ }
+
+ Complex lastValue = (Complex) lastDimension[vector[dimensionSize.length - 1]];
+ lastDimension[vector[dimensionSize.length - 1]] = magnitude;
+
+ return lastValue;
+ }
+
+ /**
+ * Get the size in all dimensions.
+ *
+ * @return size in all dimensions
+ */
+ public int[] getDimensionSizes() {
+ return dimensionSize.clone();
+ }
+
+ /**
+ * Get the underlying storage array.
+ *
+ * @return underlying storage array
+ */
+ public Object getArray() {
+ return multiDimensionalComplexArray;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public Object clone() {
+ MultiDimensionalComplexMatrix mdcm =
+ new MultiDimensionalComplexMatrix(
+ Array.newInstance(Complex.class, dimensionSize));
+ clone(mdcm);
+ return mdcm;
+ }
+
+ /**
+ * Copy contents of current array into mdcm.
+ *
+ * @param mdcm array where to copy data
+ */
+ private void clone(MultiDimensionalComplexMatrix mdcm) {
+
+ int[] vector = new int[dimensionSize.length];
+ int size = 1;
+ for (int i = 0; i < dimensionSize.length; i++) {
+ size *= dimensionSize[i];
+ }
+ int[][] vectorList = new int[size][dimensionSize.length];
+ for (int[] nextVector : vectorList) {
+ System.arraycopy(vector, 0, nextVector, 0, dimensionSize.length);
+ for (int i = 0; i < dimensionSize.length; i++) {
+ vector[i]++;
+ if (vector[i] < dimensionSize[i]) {
+ break;
+ } else {
+ vector[i] = 0;
+ }
+ }
+ }
+
+ for (int[] nextVector : vectorList) {
+ mdcm.set(get(nextVector), nextVector);
+ }
+ }
+ }
+}
diff --git a/src/main/java/org/apache/commons/math3/transform/FastHadamardTransformer.java b/src/main/java/org/apache/commons/math3/transform/FastHadamardTransformer.java
new file mode 100644
index 0000000..5f7e6a2
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/transform/FastHadamardTransformer.java
@@ -0,0 +1,316 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math3.transform;
+
+import org.apache.commons.math3.analysis.FunctionUtils;
+import org.apache.commons.math3.analysis.UnivariateFunction;
+import org.apache.commons.math3.exception.MathIllegalArgumentException;
+import org.apache.commons.math3.exception.util.LocalizedFormats;
+import org.apache.commons.math3.util.ArithmeticUtils;
+
+import java.io.Serializable;
+
+/**
+ * Implements the <a href="http://www.archive.chipcenter.com/dsp/DSP000517F1.html">Fast Hadamard
+ * Transform</a> (FHT). Transformation of an input vector x to the output vector y.
+ *
+ * <p>In addition to transformation of real vectors, the Hadamard transform can transform integer
+ * vectors into integer vectors. However, this integer transform cannot be inverted directly. Due to
+ * a scaling factor it may lead to rational results. As an example, the inverse transform of integer
+ * vector (0, 1, 0, 1) is rational vector (1/2, -1/2, 0, 0).
+ *
+ * @since 2.0
+ */
+public class FastHadamardTransformer implements RealTransformer, Serializable {
+
+ /** Serializable version identifier. */
+ static final long serialVersionUID = 20120211L;
+
+ /**
+ * {@inheritDoc}
+ *
+ * @throws MathIllegalArgumentException if the length of the data array is not a power of two
+ */
+ public double[] transform(final double[] f, final TransformType type) {
+ if (type == TransformType.FORWARD) {
+ return fht(f);
+ }
+ return TransformUtils.scaleArray(fht(f), 1.0 / f.length);
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @throws org.apache.commons.math3.exception.NonMonotonicSequenceException if the lower bound
+ * is greater than, or equal to the upper bound
+ * @throws org.apache.commons.math3.exception.NotStrictlyPositiveException if the number of
+ * sample points is negative
+ * @throws MathIllegalArgumentException if the number of sample points is not a power of two
+ */
+ public double[] transform(
+ final UnivariateFunction f,
+ final double min,
+ final double max,
+ final int n,
+ final TransformType type) {
+
+ return transform(FunctionUtils.sample(f, min, max, n), type);
+ }
+
+ /**
+ * Returns the forward transform of the specified integer data set.The integer transform cannot
+ * be inverted directly, due to a scaling factor which may lead to double results.
+ *
+ * @param f the integer data array to be transformed (signal)
+ * @return the integer transformed array (spectrum)
+ * @throws MathIllegalArgumentException if the length of the data array is not a power of two
+ */
+ public int[] transform(final int[] f) {
+ return fht(f);
+ }
+
+ /**
+ * The FHT (Fast Hadamard Transformation) which uses only subtraction and addition. Requires
+ * {@code N * log2(N) = n * 2^n} additions.
+ *
+ * <h3>Short Table of manual calculation for N=8</h3>
+ *
+ * <ol>
+ * <li><b>x</b> is the input vector to be transformed,
+ * <li><b>y</b> is the output vector (Fast Hadamard transform of <b>x</b>),
+ * <li>a and b are helper rows.
+ * </ol>
+ *
+ * <table align="center" border="1" cellpadding="3">
+ * <tbody align="center">
+ * <tr>
+ * <th>x</th>
+ * <th>a</th>
+ * <th>b</th>
+ * <th>y</th>
+ * </tr>
+ * <tr>
+ * <th>x<sub>0</sub></th>
+ * <td>a<sub>0</sub> = x<sub>0</sub> + x<sub>1</sub></td>
+ * <td>b<sub>0</sub> = a<sub>0</sub> + a<sub>1</sub></td>
+ * <td>y<sub>0</sub> = b<sub>0</sub >+ b<sub>1</sub></td>
+ * </tr>
+ * <tr>
+ * <th>x<sub>1</sub></th>
+ * <td>a<sub>1</sub> = x<sub>2</sub> + x<sub>3</sub></td>
+ * <td>b<sub>0</sub> = a<sub>2</sub> + a<sub>3</sub></td>
+ * <td>y<sub>0</sub> = b<sub>2</sub> + b<sub>3</sub></td>
+ * </tr>
+ * <tr>
+ * <th>x<sub>2</sub></th>
+ * <td>a<sub>2</sub> = x<sub>4</sub> + x<sub>5</sub></td>
+ * <td>b<sub>0</sub> = a<sub>4</sub> + a<sub>5</sub></td>
+ * <td>y<sub>0</sub> = b<sub>4</sub> + b<sub>5</sub></td>
+ * </tr>
+ * <tr>
+ * <th>x<sub>3</sub></th>
+ * <td>a<sub>3</sub> = x<sub>6</sub> + x<sub>7</sub></td>
+ * <td>b<sub>0</sub> = a<sub>6</sub> + a<sub>7</sub></td>
+ * <td>y<sub>0</sub> = b<sub>6</sub> + b<sub>7</sub></td>
+ * </tr>
+ * <tr>
+ * <th>x<sub>4</sub></th>
+ * <td>a<sub>0</sub> = x<sub>0</sub> - x<sub>1</sub></td>
+ * <td>b<sub>0</sub> = a<sub>0</sub> - a<sub>1</sub></td>
+ * <td>y<sub>0</sub> = b<sub>0</sub> - b<sub>1</sub></td>
+ * </tr>
+ * <tr>
+ * <th>x<sub>5</sub></th>
+ * <td>a<sub>1</sub> = x<sub>2</sub> - x<sub>3</sub></td>
+ * <td>b<sub>0</sub> = a<sub>2</sub> - a<sub>3</sub></td>
+ * <td>y<sub>0</sub> = b<sub>2</sub> - b<sub>3</sub></td>
+ * </tr>
+ * <tr>
+ * <th>x<sub>6</sub></th>
+ * <td>a<sub>2</sub> = x<sub>4</sub> - x<sub>5</sub></td>
+ * <td>b<sub>0</sub> = a<sub>4</sub> - a<sub>5</sub></td>
+ * <td>y<sub>0</sub> = b<sub>4</sub> - b<sub>5</sub></td>
+ * </tr>
+ * <tr>
+ * <th>x<sub>7</sub></th>
+ * <td>a<sub>3</sub> = x<sub>6</sub> - x<sub>7</sub></td>
+ * <td>b<sub>0</sub> = a<sub>6</sub> - a<sub>7</sub></td>
+ * <td>y<sub>0</sub> = b<sub>6</sub> - b<sub>7</sub></td>
+ * </tr>
+ * </tbody>
+ * </table>
+ *
+ * <h3>How it works</h3>
+ *
+ * <ol>
+ * <li>Construct a matrix with {@code N} rows and {@code n + 1} columns, {@code hadm[n+1][N]}.
+ * <br>
+ * <em>(If I use [x][y] it always means [row-offset][column-offset] of a Matrix with n
+ * rows and m columns. Its entries go from M[0][0] to M[n][N])</em>
+ * <li>Place the input vector {@code x[N]} in the first column of the matrix {@code hadm}.
+ * <li>The entries of the submatrix {@code D_top} are calculated as follows
+ * <ul>
+ * <li>{@code D_top} goes from entry {@code [0][1]} to {@code [N / 2 - 1][n + 1]},
+ * <li>the columns of {@code D_top} are the pairwise mutually exclusive sums of the
+ * previous column.
+ * </ul>
+ * <li>The entries of the submatrix {@code D_bottom} are calculated as follows
+ * <ul>
+ * <li>{@code D_bottom} goes from entry {@code [N / 2][1]} to {@code [N][n + 1]},
+ * <li>the columns of {@code D_bottom} are the pairwise differences of the previous
+ * column.
+ * </ul>
+ * <li>The consputation of {@code D_top} and {@code D_bottom} are best understood with the
+ * above example (for {@code N = 8}).
+ * <li>The output vector {@code y} is now in the last column of {@code hadm}.
+ * <li><em>Algorithm from <a
+ * href="http://www.archive.chipcenter.com/dsp/DSP000517F1.html">chipcenter</a>.</em>
+ * </ol>
+ *
+ * <h3>Visually</h3>
+ *
+ * <table border="1" align="center" cellpadding="3">
+ * <tbody align="center">
+ * <tr>
+ * <td></td><th>0</th><th>1</th><th>2</th><th>3</th>
+ * <th>&hellip;</th>
+ * <th>n + 1</th>
+ * </tr>
+ * <tr>
+ * <th>0</th>
+ * <td>x<sub>0</sub></td>
+ * <td colspan="5" rowspan="5" align="center" valign="middle">
+ * &uarr;<br/>
+ * &larr; D<sub>top</sub> &rarr;<br/>
+ * &darr;
+ * </td>
+ * </tr>
+ * <tr><th>1</th><td>x<sub>1</sub></td></tr>
+ * <tr><th>2</th><td>x<sub>2</sub></td></tr>
+ * <tr><th>&hellip;</th><td>&hellip;</td></tr>
+ * <tr><th>N / 2 - 1</th><td>x<sub>N/2-1</sub></td></tr>
+ * <tr>
+ * <th>N / 2</th>
+ * <td>x<sub>N/2</sub></td>
+ * <td colspan="5" rowspan="5" align="center" valign="middle">
+ * &uarr;<br/>
+ * &larr; D<sub>bottom</sub> &rarr;<br/>
+ * &darr;
+ * </td>
+ * </tr>
+ * <tr><th>N / 2 + 1</th><td>x<sub>N/2+1</sub></td></tr>
+ * <tr><th>N / 2 + 2</th><td>x<sub>N/2+2</sub></td></tr>
+ * <tr><th>&hellip;</th><td>&hellip;</td></tr>
+ * <tr><th>N</th><td>x<sub>N</sub></td></tr>
+ * </tbody>
+ * </table>
+ *
+ * @param x the real data array to be transformed
+ * @return the real transformed array, {@code y}
+ * @throws MathIllegalArgumentException if the length of the data array is not a power of two
+ */
+ protected double[] fht(double[] x) throws MathIllegalArgumentException {
+
+ final int n = x.length;
+ final int halfN = n / 2;
+
+ if (!ArithmeticUtils.isPowerOfTwo(n)) {
+ throw new MathIllegalArgumentException(
+ LocalizedFormats.NOT_POWER_OF_TWO, Integer.valueOf(n));
+ }
+
+ /*
+ * Instead of creating a matrix with p+1 columns and n rows, we use two
+ * one dimension arrays which we are used in an alternating way.
+ */
+ double[] yPrevious = new double[n];
+ double[] yCurrent = x.clone();
+
+ // iterate from left to right (column)
+ for (int j = 1; j < n; j <<= 1) {
+
+ // switch columns
+ final double[] yTmp = yCurrent;
+ yCurrent = yPrevious;
+ yPrevious = yTmp;
+
+ // iterate from top to bottom (row)
+ for (int i = 0; i < halfN; ++i) {
+ // Dtop: the top part works with addition
+ final int twoI = 2 * i;
+ yCurrent[i] = yPrevious[twoI] + yPrevious[twoI + 1];
+ }
+ for (int i = halfN; i < n; ++i) {
+ // Dbottom: the bottom part works with subtraction
+ final int twoI = 2 * i;
+ yCurrent[i] = yPrevious[twoI - n] - yPrevious[twoI - n + 1];
+ }
+ }
+
+ return yCurrent;
+ }
+
+ /**
+ * Returns the forward transform of the specified integer data set. The FHT (Fast Hadamard
+ * Transform) uses only subtraction and addition.
+ *
+ * @param x the integer data array to be transformed
+ * @return the integer transformed array, {@code y}
+ * @throws MathIllegalArgumentException if the length of the data array is not a power of two
+ */
+ protected int[] fht(int[] x) throws MathIllegalArgumentException {
+
+ final int n = x.length;
+ final int halfN = n / 2;
+
+ if (!ArithmeticUtils.isPowerOfTwo(n)) {
+ throw new MathIllegalArgumentException(
+ LocalizedFormats.NOT_POWER_OF_TWO, Integer.valueOf(n));
+ }
+
+ /*
+ * Instead of creating a matrix with p+1 columns and n rows, we use two
+ * one dimension arrays which we are used in an alternating way.
+ */
+ int[] yPrevious = new int[n];
+ int[] yCurrent = x.clone();
+
+ // iterate from left to right (column)
+ for (int j = 1; j < n; j <<= 1) {
+
+ // switch columns
+ final int[] yTmp = yCurrent;
+ yCurrent = yPrevious;
+ yPrevious = yTmp;
+
+ // iterate from top to bottom (row)
+ for (int i = 0; i < halfN; ++i) {
+ // Dtop: the top part works with addition
+ final int twoI = 2 * i;
+ yCurrent[i] = yPrevious[twoI] + yPrevious[twoI + 1];
+ }
+ for (int i = halfN; i < n; ++i) {
+ // Dbottom: the bottom part works with subtraction
+ final int twoI = 2 * i;
+ yCurrent[i] = yPrevious[twoI - n] - yPrevious[twoI - n + 1];
+ }
+ }
+
+ // return the last computed output vector y
+ return yCurrent;
+ }
+}
diff --git a/src/main/java/org/apache/commons/math3/transform/FastSineTransformer.java b/src/main/java/org/apache/commons/math3/transform/FastSineTransformer.java
new file mode 100644
index 0000000..b33b226
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/transform/FastSineTransformer.java
@@ -0,0 +1,175 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math3.transform;
+
+import org.apache.commons.math3.analysis.FunctionUtils;
+import org.apache.commons.math3.analysis.UnivariateFunction;
+import org.apache.commons.math3.complex.Complex;
+import org.apache.commons.math3.exception.MathIllegalArgumentException;
+import org.apache.commons.math3.exception.util.LocalizedFormats;
+import org.apache.commons.math3.util.ArithmeticUtils;
+import org.apache.commons.math3.util.FastMath;
+
+import java.io.Serializable;
+
+/**
+ * Implements the Fast Sine Transform for transformation of one-dimensional real data sets. For
+ * reference, see James S. Walker, <em>Fast Fourier Transforms</em>, chapter 3 (ISBN 0849371635).
+ *
+ * <p>There are several variants of the discrete sine transform. The present implementation
+ * corresponds to DST-I, with various normalization conventions, which are specified by the
+ * parameter {@link DstNormalization}. <strong>It should be noted that regardless to the convention,
+ * the first element of the dataset to be transformed must be zero.</strong>
+ *
+ * <p>DST-I is equivalent to DFT of an <em>odd extension</em> of the data series. More precisely, if
+ * x<sub>0</sub>, &hellip;, x<sub>N-1</sub> is the data set to be sine transformed, the extended
+ * data set x<sub>0</sub><sup>&#35;</sup>, &hellip;, x<sub>2N-1</sub><sup>&#35;</sup> is defined as
+ * follows
+ *
+ * <ul>
+ * <li>x<sub>0</sub><sup>&#35;</sup> = x<sub>0</sub> = 0,
+ * <li>x<sub>k</sub><sup>&#35;</sup> = x<sub>k</sub> if 1 &le; k &lt; N,
+ * <li>x<sub>N</sub><sup>&#35;</sup> = 0,
+ * <li>x<sub>k</sub><sup>&#35;</sup> = -x<sub>2N-k</sub> if N + 1 &le; k &lt; 2N.
+ * </ul>
+ *
+ * <p>Then, the standard DST-I y<sub>0</sub>, &hellip;, y<sub>N-1</sub> of the real data set
+ * x<sub>0</sub>, &hellip;, x<sub>N-1</sub> is equal to <em>half</em> of i (the pure imaginary
+ * number) times the N first elements of the DFT of the extended data set
+ * x<sub>0</sub><sup>&#35;</sup>, &hellip;, x<sub>2N-1</sub><sup>&#35;</sup> <br>
+ * y<sub>n</sub> = (i / 2) &sum;<sub>k=0</sub><sup>2N-1</sup> x<sub>k</sub><sup>&#35;</sup>
+ * exp[-2&pi;i nk / (2N)] &nbsp;&nbsp;&nbsp;&nbsp;k = 0, &hellip;, N-1.
+ *
+ * <p>The present implementation of the discrete sine transform as a fast sine transform requires
+ * the length of the data to be a power of two. Besides, it implicitly assumes that the sampled
+ * function is odd. In particular, the first element of the data set must be 0, which is enforced in
+ * {@link #transform(UnivariateFunction, double, double, int, TransformType)}, after sampling.
+ *
+ * @since 1.2
+ */
+public class FastSineTransformer implements RealTransformer, Serializable {
+
+ /** Serializable version identifier. */
+ static final long serialVersionUID = 20120211L;
+
+ /** The type of DST to be performed. */
+ private final DstNormalization normalization;
+
+ /**
+ * Creates a new instance of this class, with various normalization conventions.
+ *
+ * @param normalization the type of normalization to be applied to the transformed data
+ */
+ public FastSineTransformer(final DstNormalization normalization) {
+ this.normalization = normalization;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * <p>The first element of the specified data set is required to be {@code 0}.
+ *
+ * @throws MathIllegalArgumentException if the length of the data array is not a power of two,
+ * or the first element of the data array is not zero
+ */
+ public double[] transform(final double[] f, final TransformType type) {
+ if (normalization == DstNormalization.ORTHOGONAL_DST_I) {
+ final double s = FastMath.sqrt(2.0 / f.length);
+ return TransformUtils.scaleArray(fst(f), s);
+ }
+ if (type == TransformType.FORWARD) {
+ return fst(f);
+ }
+ final double s = 2.0 / f.length;
+ return TransformUtils.scaleArray(fst(f), s);
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * <p>This implementation enforces {@code f(x) = 0.0} at {@code x = 0.0}.
+ *
+ * @throws org.apache.commons.math3.exception.NonMonotonicSequenceException if the lower bound
+ * is greater than, or equal to the upper bound
+ * @throws org.apache.commons.math3.exception.NotStrictlyPositiveException if the number of
+ * sample points is negative
+ * @throws MathIllegalArgumentException if the number of sample points is not a power of two
+ */
+ public double[] transform(
+ final UnivariateFunction f,
+ final double min,
+ final double max,
+ final int n,
+ final TransformType type) {
+
+ final double[] data = FunctionUtils.sample(f, min, max, n);
+ data[0] = 0.0;
+ return transform(data, type);
+ }
+
+ /**
+ * Perform the FST algorithm (including inverse). The first element of the data set is required
+ * to be {@code 0}.
+ *
+ * @param f the real data array to be transformed
+ * @return the real transformed array
+ * @throws MathIllegalArgumentException if the length of the data array is not a power of two,
+ * or the first element of the data array is not zero
+ */
+ protected double[] fst(double[] f) throws MathIllegalArgumentException {
+
+ final double[] transformed = new double[f.length];
+
+ if (!ArithmeticUtils.isPowerOfTwo(f.length)) {
+ throw new MathIllegalArgumentException(
+ LocalizedFormats.NOT_POWER_OF_TWO_CONSIDER_PADDING, Integer.valueOf(f.length));
+ }
+ if (f[0] != 0.0) {
+ throw new MathIllegalArgumentException(
+ LocalizedFormats.FIRST_ELEMENT_NOT_ZERO, Double.valueOf(f[0]));
+ }
+ final int n = f.length;
+ if (n == 1) { // trivial case
+ transformed[0] = 0.0;
+ return transformed;
+ }
+
+ // construct a new array and perform FFT on it
+ final double[] x = new double[n];
+ x[0] = 0.0;
+ x[n >> 1] = 2.0 * f[n >> 1];
+ for (int i = 1; i < (n >> 1); i++) {
+ final double a = FastMath.sin(i * FastMath.PI / n) * (f[i] + f[n - i]);
+ final double b = 0.5 * (f[i] - f[n - i]);
+ x[i] = a + b;
+ x[n - i] = a - b;
+ }
+ FastFourierTransformer transformer;
+ transformer = new FastFourierTransformer(DftNormalization.STANDARD);
+ Complex[] y = transformer.transform(x, TransformType.FORWARD);
+
+ // reconstruct the FST result for the original array
+ transformed[0] = 0.0;
+ transformed[1] = 0.5 * y[0].getReal();
+ for (int i = 1; i < (n >> 1); i++) {
+ transformed[2 * i] = -y[i].getImaginary();
+ transformed[2 * i + 1] = y[i].getReal() + transformed[2 * i - 1];
+ }
+
+ return transformed;
+ }
+}
diff --git a/src/main/java/org/apache/commons/math3/transform/RealTransformer.java b/src/main/java/org/apache/commons/math3/transform/RealTransformer.java
new file mode 100644
index 0000000..b5a105b
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/transform/RealTransformer.java
@@ -0,0 +1,68 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math3.transform;
+
+import org.apache.commons.math3.analysis.UnivariateFunction;
+import org.apache.commons.math3.exception.MathIllegalArgumentException;
+import org.apache.commons.math3.exception.NonMonotonicSequenceException;
+import org.apache.commons.math3.exception.NotStrictlyPositiveException;
+
+/**
+ * Interface for one-dimensional data sets transformations producing real results.
+ *
+ * <p>Such transforms include {@link FastSineTransformer sine transform}, {@link
+ * FastCosineTransformer cosine transform} or {@link FastHadamardTransformer Hadamard transform}.
+ * {@link FastFourierTransformer Fourier transform} is of a different kind and does not implement
+ * this interface since it produces {@link org.apache.commons.math3.complex.Complex} results instead
+ * of real ones.
+ *
+ * @since 2.0
+ */
+public interface RealTransformer {
+
+ /**
+ * Returns the (forward, inverse) transform of the specified real data set.
+ *
+ * @param f the real data array to be transformed (signal)
+ * @param type the type of transform (forward, inverse) to be performed
+ * @return the real transformed array (spectrum)
+ * @throws MathIllegalArgumentException if the array cannot be transformed with the given type
+ * (this may be for example due to array size, which is constrained in some transforms)
+ */
+ double[] transform(double[] f, TransformType type) throws MathIllegalArgumentException;
+
+ /**
+ * Returns the (forward, inverse) transform of the specified real function, sampled on the
+ * specified interval.
+ *
+ * @param f the function to be sampled and transformed
+ * @param min the (inclusive) lower bound for the interval
+ * @param max the (exclusive) upper bound for the interval
+ * @param n the number of sample points
+ * @param type the type of transform (forward, inverse) to be performed
+ * @return the real transformed array
+ * @throws NonMonotonicSequenceException if the lower bound is greater than, or equal to the
+ * upper bound
+ * @throws NotStrictlyPositiveException if the number of sample points is negative
+ * @throws MathIllegalArgumentException if the sample cannot be transformed with the given type
+ * (this may be for example due to sample size, which is constrained in some transforms)
+ */
+ double[] transform(UnivariateFunction f, double min, double max, int n, TransformType type)
+ throws NonMonotonicSequenceException,
+ NotStrictlyPositiveException,
+ MathIllegalArgumentException;
+}
diff --git a/src/main/java/org/apache/commons/math3/transform/TransformType.java b/src/main/java/org/apache/commons/math3/transform/TransformType.java
new file mode 100644
index 0000000..ae1a6c5
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/transform/TransformType.java
@@ -0,0 +1,30 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math3.transform;
+
+/**
+ * This enumeration defines the type of transform which is to be computed.
+ *
+ * @since 3.0
+ */
+public enum TransformType {
+ /** The type to be specified for forward transforms. */
+ FORWARD,
+
+ /** The type to be specified for inverse transforms. */
+ INVERSE;
+}
diff --git a/src/main/java/org/apache/commons/math3/transform/TransformUtils.java b/src/main/java/org/apache/commons/math3/transform/TransformUtils.java
new file mode 100644
index 0000000..5cf83de
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/transform/TransformUtils.java
@@ -0,0 +1,160 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math3.transform;
+
+import org.apache.commons.math3.complex.Complex;
+import org.apache.commons.math3.exception.DimensionMismatchException;
+import org.apache.commons.math3.exception.MathIllegalArgumentException;
+import org.apache.commons.math3.exception.util.LocalizedFormats;
+
+import java.util.Arrays;
+
+/**
+ * Useful functions for the implementation of various transforms.
+ *
+ * @since 3.0
+ */
+public class TransformUtils {
+ /**
+ * Table of the powers of 2 to facilitate binary search lookup.
+ *
+ * @see #exactLog2(int)
+ */
+ private static final int[] POWERS_OF_TWO = {
+ 0x00000001, 0x00000002, 0x00000004, 0x00000008, 0x00000010, 0x00000020,
+ 0x00000040, 0x00000080, 0x00000100, 0x00000200, 0x00000400, 0x00000800,
+ 0x00001000, 0x00002000, 0x00004000, 0x00008000, 0x00010000, 0x00020000,
+ 0x00040000, 0x00080000, 0x00100000, 0x00200000, 0x00400000, 0x00800000,
+ 0x01000000, 0x02000000, 0x04000000, 0x08000000, 0x10000000, 0x20000000,
+ 0x40000000
+ };
+
+ /** Private constructor. */
+ private TransformUtils() {
+ super();
+ }
+
+ /**
+ * Multiply every component in the given real array by the given real number. The change is made
+ * in place.
+ *
+ * @param f the real array to be scaled
+ * @param d the real scaling coefficient
+ * @return a reference to the scaled array
+ */
+ public static double[] scaleArray(double[] f, double d) {
+
+ for (int i = 0; i < f.length; i++) {
+ f[i] *= d;
+ }
+ return f;
+ }
+
+ /**
+ * Multiply every component in the given complex array by the given real number. The change is
+ * made in place.
+ *
+ * @param f the complex array to be scaled
+ * @param d the real scaling coefficient
+ * @return a reference to the scaled array
+ */
+ public static Complex[] scaleArray(Complex[] f, double d) {
+
+ for (int i = 0; i < f.length; i++) {
+ f[i] = new Complex(d * f[i].getReal(), d * f[i].getImaginary());
+ }
+ return f;
+ }
+
+ /**
+ * Builds a new two dimensional array of {@code double} filled with the real and imaginary parts
+ * of the specified {@link Complex} numbers. In the returned array {@code dataRI}, the data is
+ * laid out as follows
+ *
+ * <ul>
+ * <li>{@code dataRI[0][i] = dataC[i].getReal()},
+ * <li>{@code dataRI[1][i] = dataC[i].getImaginary()}.
+ * </ul>
+ *
+ * @param dataC the array of {@link Complex} data to be transformed
+ * @return a two dimensional array filled with the real and imaginary parts of the specified
+ * complex input
+ */
+ public static double[][] createRealImaginaryArray(final Complex[] dataC) {
+ final double[][] dataRI = new double[2][dataC.length];
+ final double[] dataR = dataRI[0];
+ final double[] dataI = dataRI[1];
+ for (int i = 0; i < dataC.length; i++) {
+ final Complex c = dataC[i];
+ dataR[i] = c.getReal();
+ dataI[i] = c.getImaginary();
+ }
+ return dataRI;
+ }
+
+ /**
+ * Builds a new array of {@link Complex} from the specified two dimensional array of real and
+ * imaginary parts. In the returned array {@code dataC}, the data is laid out as follows
+ *
+ * <ul>
+ * <li>{@code dataC[i].getReal() = dataRI[0][i]},
+ * <li>{@code dataC[i].getImaginary() = dataRI[1][i]}.
+ * </ul>
+ *
+ * @param dataRI the array of real and imaginary parts to be transformed
+ * @return an array of {@link Complex} with specified real and imaginary parts.
+ * @throws DimensionMismatchException if the number of rows of the specified array is not two,
+ * or the array is not rectangular
+ */
+ public static Complex[] createComplexArray(final double[][] dataRI)
+ throws DimensionMismatchException {
+
+ if (dataRI.length != 2) {
+ throw new DimensionMismatchException(dataRI.length, 2);
+ }
+ final double[] dataR = dataRI[0];
+ final double[] dataI = dataRI[1];
+ if (dataR.length != dataI.length) {
+ throw new DimensionMismatchException(dataI.length, dataR.length);
+ }
+
+ final int n = dataR.length;
+ final Complex[] c = new Complex[n];
+ for (int i = 0; i < n; i++) {
+ c[i] = new Complex(dataR[i], dataI[i]);
+ }
+ return c;
+ }
+
+ /**
+ * Returns the base-2 logarithm of the specified {@code int}. Throws an exception if {@code n}
+ * is not a power of two.
+ *
+ * @param n the {@code int} whose base-2 logarithm is to be evaluated
+ * @return the base-2 logarithm of {@code n}
+ * @throws MathIllegalArgumentException if {@code n} is not a power of two
+ */
+ public static int exactLog2(final int n) throws MathIllegalArgumentException {
+
+ int index = Arrays.binarySearch(TransformUtils.POWERS_OF_TWO, n);
+ if (index < 0) {
+ throw new MathIllegalArgumentException(
+ LocalizedFormats.NOT_POWER_OF_TWO_CONSIDER_PADDING, Integer.valueOf(n));
+ }
+ return index;
+ }
+}
diff --git a/src/main/java/org/apache/commons/math3/transform/package-info.java b/src/main/java/org/apache/commons/math3/transform/package-info.java
new file mode 100644
index 0000000..728c7c4
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/transform/package-info.java
@@ -0,0 +1,18 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/** Implementations of transform methods, including Fast Fourier transforms. */
+package org.apache.commons.math3.transform;
diff --git a/src/main/java/org/apache/commons/math3/util/ArithmeticUtils.java b/src/main/java/org/apache/commons/math3/util/ArithmeticUtils.java
new file mode 100644
index 0000000..8f07818
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/util/ArithmeticUtils.java
@@ -0,0 +1,845 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math3.util;
+
+import org.apache.commons.math3.exception.MathArithmeticException;
+import org.apache.commons.math3.exception.NotPositiveException;
+import org.apache.commons.math3.exception.NumberIsTooLargeException;
+import org.apache.commons.math3.exception.util.Localizable;
+import org.apache.commons.math3.exception.util.LocalizedFormats;
+
+import java.math.BigInteger;
+
+/** Some useful, arithmetics related, additions to the built-in functions in {@link Math}. */
+public final class ArithmeticUtils {
+
+ /** Private constructor. */
+ private ArithmeticUtils() {
+ super();
+ }
+
+ /**
+ * Add two integers, checking for overflow.
+ *
+ * @param x an addend
+ * @param y an addend
+ * @return the sum {@code x+y}
+ * @throws MathArithmeticException if the result can not be represented as an {@code int}.
+ * @since 1.1
+ */
+ public static int addAndCheck(int x, int y) throws MathArithmeticException {
+ long s = (long) x + (long) y;
+ if (s < Integer.MIN_VALUE || s > Integer.MAX_VALUE) {
+ throw new MathArithmeticException(LocalizedFormats.OVERFLOW_IN_ADDITION, x, y);
+ }
+ return (int) s;
+ }
+
+ /**
+ * Add two long integers, checking for overflow.
+ *
+ * @param a an addend
+ * @param b an addend
+ * @return the sum {@code a+b}
+ * @throws MathArithmeticException if the result can not be represented as an long
+ * @since 1.2
+ */
+ public static long addAndCheck(long a, long b) throws MathArithmeticException {
+ return addAndCheck(a, b, LocalizedFormats.OVERFLOW_IN_ADDITION);
+ }
+
+ /**
+ * Returns an exact representation of the <a
+ * href="http://mathworld.wolfram.com/BinomialCoefficient.html">Binomial Coefficient</a>,
+ * "{@code n choose k}", the number of {@code k}-element subsets that can be selected from an
+ * {@code n}-element set.
+ *
+ * <p><Strong>Preconditions</strong>:
+ *
+ * <ul>
+ * <li>{@code 0 <= k <= n } (otherwise {@code IllegalArgumentException} is thrown)
+ * <li>The result is small enough to fit into a {@code long}. The largest value of {@code n}
+ * for which all coefficients are {@code < Long.MAX_VALUE} is 66. If the computed value
+ * exceeds {@code Long.MAX_VALUE} an {@code ArithMeticException} is thrown.
+ * </ul>
+ *
+ * @param n the size of the set
+ * @param k the size of the subsets to be counted
+ * @return {@code n choose k}
+ * @throws NotPositiveException if {@code n < 0}.
+ * @throws NumberIsTooLargeException if {@code k > n}.
+ * @throws MathArithmeticException if the result is too large to be represented by a long
+ * integer.
+ * @deprecated use {@link CombinatoricsUtils#binomialCoefficient(int, int)}
+ */
+ @Deprecated
+ public static long binomialCoefficient(final int n, final int k)
+ throws NotPositiveException, NumberIsTooLargeException, MathArithmeticException {
+ return CombinatoricsUtils.binomialCoefficient(n, k);
+ }
+
+ /**
+ * Returns a {@code double} representation of the <a
+ * href="http://mathworld.wolfram.com/BinomialCoefficient.html">Binomial Coefficient</a>,
+ * "{@code n choose k}", the number of {@code k}-element subsets that can be selected from an
+ * {@code n}-element set.
+ *
+ * <p><Strong>Preconditions</strong>:
+ *
+ * <ul>
+ * <li>{@code 0 <= k <= n } (otherwise {@code IllegalArgumentException} is thrown)
+ * <li>The result is small enough to fit into a {@code double}. The largest value of {@code n}
+ * for which all coefficients are < Double.MAX_VALUE is 1029. If the computed value
+ * exceeds Double.MAX_VALUE, Double.POSITIVE_INFINITY is returned
+ * </ul>
+ *
+ * @param n the size of the set
+ * @param k the size of the subsets to be counted
+ * @return {@code n choose k}
+ * @throws NotPositiveException if {@code n < 0}.
+ * @throws NumberIsTooLargeException if {@code k > n}.
+ * @throws MathArithmeticException if the result is too large to be represented by a long
+ * integer.
+ * @deprecated use {@link CombinatoricsUtils#binomialCoefficientDouble(int, int)}
+ */
+ @Deprecated
+ public static double binomialCoefficientDouble(final int n, final int k)
+ throws NotPositiveException, NumberIsTooLargeException, MathArithmeticException {
+ return CombinatoricsUtils.binomialCoefficientDouble(n, k);
+ }
+
+ /**
+ * Returns the natural {@code log} of the <a
+ * href="http://mathworld.wolfram.com/BinomialCoefficient.html">Binomial Coefficient</a>,
+ * "{@code n choose k}", the number of {@code k}-element subsets that can be selected from an
+ * {@code n}-element set.
+ *
+ * <p><Strong>Preconditions</strong>:
+ *
+ * <ul>
+ * <li>{@code 0 <= k <= n } (otherwise {@code IllegalArgumentException} is thrown)
+ * </ul>
+ *
+ * @param n the size of the set
+ * @param k the size of the subsets to be counted
+ * @return {@code n choose k}
+ * @throws NotPositiveException if {@code n < 0}.
+ * @throws NumberIsTooLargeException if {@code k > n}.
+ * @throws MathArithmeticException if the result is too large to be represented by a long
+ * integer.
+ * @deprecated use {@link CombinatoricsUtils#binomialCoefficientLog(int, int)}
+ */
+ @Deprecated
+ public static double binomialCoefficientLog(final int n, final int k)
+ throws NotPositiveException, NumberIsTooLargeException, MathArithmeticException {
+ return CombinatoricsUtils.binomialCoefficientLog(n, k);
+ }
+
+ /**
+ * Returns n!. Shorthand for {@code n} <a href="http://mathworld.wolfram.com/Factorial.html">
+ * Factorial</a>, the product of the numbers {@code 1,...,n}.
+ *
+ * <p><Strong>Preconditions</strong>:
+ *
+ * <ul>
+ * <li>{@code n >= 0} (otherwise {@code IllegalArgumentException} is thrown)
+ * <li>The result is small enough to fit into a {@code long}. The largest value of {@code n}
+ * for which {@code n!} < Long.MAX_VALUE} is 20. If the computed value exceeds {@code
+ * Long.MAX_VALUE} an {@code ArithMeticException } is thrown.
+ * </ul>
+ *
+ * @param n argument
+ * @return {@code n!}
+ * @throws MathArithmeticException if the result is too large to be represented by a {@code
+ * long}.
+ * @throws NotPositiveException if {@code n < 0}.
+ * @throws MathArithmeticException if {@code n > 20}: The factorial value is too large to fit in
+ * a {@code long}.
+ * @deprecated use {@link CombinatoricsUtils#factorial(int)}
+ */
+ @Deprecated
+ public static long factorial(final int n) throws NotPositiveException, MathArithmeticException {
+ return CombinatoricsUtils.factorial(n);
+ }
+
+ /**
+ * Compute n!, the<a href="http://mathworld.wolfram.com/Factorial.html">factorial</a> of {@code
+ * n} (the product of the numbers 1 to n), as a {@code double}. The result should be small
+ * enough to fit into a {@code double}: The largest {@code n} for which {@code n! <
+ * Double.MAX_VALUE} is 170. If the computed value exceeds {@code Double.MAX_VALUE}, {@code
+ * Double.POSITIVE_INFINITY} is returned.
+ *
+ * @param n Argument.
+ * @return {@code n!}
+ * @throws NotPositiveException if {@code n < 0}.
+ * @deprecated use {@link CombinatoricsUtils#factorialDouble(int)}
+ */
+ @Deprecated
+ public static double factorialDouble(final int n) throws NotPositiveException {
+ return CombinatoricsUtils.factorialDouble(n);
+ }
+
+ /**
+ * Compute the natural logarithm of the factorial of {@code n}.
+ *
+ * @param n Argument.
+ * @return {@code n!}
+ * @throws NotPositiveException if {@code n < 0}.
+ * @deprecated use {@link CombinatoricsUtils#factorialLog(int)}
+ */
+ @Deprecated
+ public static double factorialLog(final int n) throws NotPositiveException {
+ return CombinatoricsUtils.factorialLog(n);
+ }
+
+ /**
+ * Computes the greatest common divisor of the absolute value of two numbers, using a modified
+ * version of the "binary gcd" method. See Knuth 4.5.2 algorithm B. The algorithm is due to
+ * Josef Stein (1961). <br>
+ * Special cases:
+ *
+ * <ul>
+ * <li>The invocations {@code gcd(Integer.MIN_VALUE, Integer.MIN_VALUE)}, {@code
+ * gcd(Integer.MIN_VALUE, 0)} and {@code gcd(0, Integer.MIN_VALUE)} throw an {@code
+ * ArithmeticException}, because the result would be 2^31, which is too large for an int
+ * value.
+ * <li>The result of {@code gcd(x, x)}, {@code gcd(0, x)} and {@code gcd(x, 0)} is the
+ * absolute value of {@code x}, except for the special cases above.
+ * <li>The invocation {@code gcd(0, 0)} is the only one which returns {@code 0}.
+ * </ul>
+ *
+ * @param p Number.
+ * @param q Number.
+ * @return the greatest common divisor (never negative).
+ * @throws MathArithmeticException if the result cannot be represented as a non-negative {@code
+ * int} value.
+ * @since 1.1
+ */
+ public static int gcd(int p, int q) throws MathArithmeticException {
+ int a = p;
+ int b = q;
+ if (a == 0 || b == 0) {
+ if (a == Integer.MIN_VALUE || b == Integer.MIN_VALUE) {
+ throw new MathArithmeticException(LocalizedFormats.GCD_OVERFLOW_32_BITS, p, q);
+ }
+ return FastMath.abs(a + b);
+ }
+
+ long al = a;
+ long bl = b;
+ boolean useLong = false;
+ if (a < 0) {
+ if (Integer.MIN_VALUE == a) {
+ useLong = true;
+ } else {
+ a = -a;
+ }
+ al = -al;
+ }
+ if (b < 0) {
+ if (Integer.MIN_VALUE == b) {
+ useLong = true;
+ } else {
+ b = -b;
+ }
+ bl = -bl;
+ }
+ if (useLong) {
+ if (al == bl) {
+ throw new MathArithmeticException(LocalizedFormats.GCD_OVERFLOW_32_BITS, p, q);
+ }
+ long blbu = bl;
+ bl = al;
+ al = blbu % al;
+ if (al == 0) {
+ if (bl > Integer.MAX_VALUE) {
+ throw new MathArithmeticException(LocalizedFormats.GCD_OVERFLOW_32_BITS, p, q);
+ }
+ return (int) bl;
+ }
+ blbu = bl;
+
+ // Now "al" and "bl" fit in an "int".
+ b = (int) al;
+ a = (int) (blbu % al);
+ }
+
+ return gcdPositive(a, b);
+ }
+
+ /**
+ * Computes the greatest common divisor of two <em>positive</em> numbers (this precondition is
+ * <em>not</em> checked and the result is undefined if not fulfilled) using the "binary gcd"
+ * method which avoids division and modulo operations. See Knuth 4.5.2 algorithm B. The
+ * algorithm is due to Josef Stein (1961). <br>
+ * Special cases:
+ *
+ * <ul>
+ * <li>The result of {@code gcd(x, x)}, {@code gcd(0, x)} and {@code gcd(x, 0)} is the value
+ * of {@code x}.
+ * <li>The invocation {@code gcd(0, 0)} is the only one which returns {@code 0}.
+ * </ul>
+ *
+ * @param a Positive number.
+ * @param b Positive number.
+ * @return the greatest common divisor.
+ */
+ private static int gcdPositive(int a, int b) {
+ if (a == 0) {
+ return b;
+ } else if (b == 0) {
+ return a;
+ }
+
+ // Make "a" and "b" odd, keeping track of common power of 2.
+ final int aTwos = Integer.numberOfTrailingZeros(a);
+ a >>= aTwos;
+ final int bTwos = Integer.numberOfTrailingZeros(b);
+ b >>= bTwos;
+ final int shift = FastMath.min(aTwos, bTwos);
+
+ // "a" and "b" are positive.
+ // If a > b then "gdc(a, b)" is equal to "gcd(a - b, b)".
+ // If a < b then "gcd(a, b)" is equal to "gcd(b - a, a)".
+ // Hence, in the successive iterations:
+ // "a" becomes the absolute difference of the current values,
+ // "b" becomes the minimum of the current values.
+ while (a != b) {
+ final int delta = a - b;
+ b = Math.min(a, b);
+ a = Math.abs(delta);
+
+ // Remove any power of 2 in "a" ("b" is guaranteed to be odd).
+ a >>= Integer.numberOfTrailingZeros(a);
+ }
+
+ // Recover the common power of 2.
+ return a << shift;
+ }
+
+ /**
+ * Gets the greatest common divisor of the absolute value of two numbers, using the "binary gcd"
+ * method which avoids division and modulo operations. See Knuth 4.5.2 algorithm B. This
+ * algorithm is due to Josef Stein (1961). Special cases:
+ *
+ * <ul>
+ * <li>The invocations {@code gcd(Long.MIN_VALUE, Long.MIN_VALUE)}, {@code gcd(Long.MIN_VALUE,
+ * 0L)} and {@code gcd(0L, Long.MIN_VALUE)} throw an {@code ArithmeticException}, because
+ * the result would be 2^63, which is too large for a long value.
+ * <li>The result of {@code gcd(x, x)}, {@code gcd(0L, x)} and {@code gcd(x, 0L)} is the
+ * absolute value of {@code x}, except for the special cases above.
+ * <li>The invocation {@code gcd(0L, 0L)} is the only one which returns {@code 0L}.
+ * </ul>
+ *
+ * @param p Number.
+ * @param q Number.
+ * @return the greatest common divisor, never negative.
+ * @throws MathArithmeticException if the result cannot be represented as a non-negative {@code
+ * long} value.
+ * @since 2.1
+ */
+ public static long gcd(final long p, final long q) throws MathArithmeticException {
+ long u = p;
+ long v = q;
+ if ((u == 0) || (v == 0)) {
+ if ((u == Long.MIN_VALUE) || (v == Long.MIN_VALUE)) {
+ throw new MathArithmeticException(LocalizedFormats.GCD_OVERFLOW_64_BITS, p, q);
+ }
+ return FastMath.abs(u) + FastMath.abs(v);
+ }
+ // keep u and v negative, as negative integers range down to
+ // -2^63, while positive numbers can only be as large as 2^63-1
+ // (i.e. we can't necessarily negate a negative number without
+ // overflow)
+ /* assert u!=0 && v!=0; */
+ if (u > 0) {
+ u = -u;
+ } // make u negative
+ if (v > 0) {
+ v = -v;
+ } // make v negative
+ // B1. [Find power of 2]
+ int k = 0;
+ while ((u & 1) == 0 && (v & 1) == 0 && k < 63) { // while u and v are
+ // both even...
+ u /= 2;
+ v /= 2;
+ k++; // cast out twos.
+ }
+ if (k == 63) {
+ throw new MathArithmeticException(LocalizedFormats.GCD_OVERFLOW_64_BITS, p, q);
+ }
+ // B2. Initialize: u and v have been divided by 2^k and at least
+ // one is odd.
+ long t = ((u & 1) == 1) ? v : -(u / 2) /* B3 */;
+ // t negative: u was odd, v may be even (t replaces v)
+ // t positive: u was even, v is odd (t replaces u)
+ do {
+ /* assert u<0 && v<0; */
+ // B4/B3: cast out twos from t.
+ while ((t & 1) == 0) { // while t is even..
+ t /= 2; // cast out twos
+ }
+ // B5 [reset max(u,v)]
+ if (t > 0) {
+ u = -t;
+ } else {
+ v = t;
+ }
+ // B6/B3. at this point both u and v should be odd.
+ t = (v - u) / 2;
+ // |u| larger: t positive (replace u)
+ // |v| larger: t negative (replace v)
+ } while (t != 0);
+ return -u * (1L << k); // gcd is u*2^k
+ }
+
+ /**
+ * Returns the least common multiple of the absolute value of two numbers, using the formula
+ * {@code lcm(a,b) = (a / gcd(a,b)) * b}. Special cases:
+ *
+ * <ul>
+ * <li>The invocations {@code lcm(Integer.MIN_VALUE, n)} and {@code lcm(n,
+ * Integer.MIN_VALUE)}, where {@code abs(n)} is a power of 2, throw an {@code
+ * ArithmeticException}, because the result would be 2^31, which is too large for an int
+ * value.
+ * <li>The result of {@code lcm(0, x)} and {@code lcm(x, 0)} is {@code 0} for any {@code x}.
+ * </ul>
+ *
+ * @param a Number.
+ * @param b Number.
+ * @return the least common multiple, never negative.
+ * @throws MathArithmeticException if the result cannot be represented as a non-negative {@code
+ * int} value.
+ * @since 1.1
+ */
+ public static int lcm(int a, int b) throws MathArithmeticException {
+ if (a == 0 || b == 0) {
+ return 0;
+ }
+ int lcm = FastMath.abs(ArithmeticUtils.mulAndCheck(a / gcd(a, b), b));
+ if (lcm == Integer.MIN_VALUE) {
+ throw new MathArithmeticException(LocalizedFormats.LCM_OVERFLOW_32_BITS, a, b);
+ }
+ return lcm;
+ }
+
+ /**
+ * Returns the least common multiple of the absolute value of two numbers, using the formula
+ * {@code lcm(a,b) = (a / gcd(a,b)) * b}. Special cases:
+ *
+ * <ul>
+ * <li>The invocations {@code lcm(Long.MIN_VALUE, n)} and {@code lcm(n, Long.MIN_VALUE)},
+ * where {@code abs(n)} is a power of 2, throw an {@code ArithmeticException}, because the
+ * result would be 2^63, which is too large for an int value.
+ * <li>The result of {@code lcm(0L, x)} and {@code lcm(x, 0L)} is {@code 0L} for any {@code
+ * x}.
+ * </ul>
+ *
+ * @param a Number.
+ * @param b Number.
+ * @return the least common multiple, never negative.
+ * @throws MathArithmeticException if the result cannot be represented as a non-negative {@code
+ * long} value.
+ * @since 2.1
+ */
+ public static long lcm(long a, long b) throws MathArithmeticException {
+ if (a == 0 || b == 0) {
+ return 0;
+ }
+ long lcm = FastMath.abs(ArithmeticUtils.mulAndCheck(a / gcd(a, b), b));
+ if (lcm == Long.MIN_VALUE) {
+ throw new MathArithmeticException(LocalizedFormats.LCM_OVERFLOW_64_BITS, a, b);
+ }
+ return lcm;
+ }
+
+ /**
+ * Multiply two integers, checking for overflow.
+ *
+ * @param x Factor.
+ * @param y Factor.
+ * @return the product {@code x * y}.
+ * @throws MathArithmeticException if the result can not be represented as an {@code int}.
+ * @since 1.1
+ */
+ public static int mulAndCheck(int x, int y) throws MathArithmeticException {
+ long m = ((long) x) * ((long) y);
+ if (m < Integer.MIN_VALUE || m > Integer.MAX_VALUE) {
+ throw new MathArithmeticException();
+ }
+ return (int) m;
+ }
+
+ /**
+ * Multiply two long integers, checking for overflow.
+ *
+ * @param a Factor.
+ * @param b Factor.
+ * @return the product {@code a * b}.
+ * @throws MathArithmeticException if the result can not be represented as a {@code long}.
+ * @since 1.2
+ */
+ public static long mulAndCheck(long a, long b) throws MathArithmeticException {
+ long ret;
+ if (a > b) {
+ // use symmetry to reduce boundary cases
+ ret = mulAndCheck(b, a);
+ } else {
+ if (a < 0) {
+ if (b < 0) {
+ // check for positive overflow with negative a, negative b
+ if (a >= Long.MAX_VALUE / b) {
+ ret = a * b;
+ } else {
+ throw new MathArithmeticException();
+ }
+ } else if (b > 0) {
+ // check for negative overflow with negative a, positive b
+ if (Long.MIN_VALUE / b <= a) {
+ ret = a * b;
+ } else {
+ throw new MathArithmeticException();
+ }
+ } else {
+ // assert b == 0
+ ret = 0;
+ }
+ } else if (a > 0) {
+ // assert a > 0
+ // assert b > 0
+
+ // check for positive overflow with positive a, positive b
+ if (a <= Long.MAX_VALUE / b) {
+ ret = a * b;
+ } else {
+ throw new MathArithmeticException();
+ }
+ } else {
+ // assert a == 0
+ ret = 0;
+ }
+ }
+ return ret;
+ }
+
+ /**
+ * Subtract two integers, checking for overflow.
+ *
+ * @param x Minuend.
+ * @param y Subtrahend.
+ * @return the difference {@code x - y}.
+ * @throws MathArithmeticException if the result can not be represented as an {@code int}.
+ * @since 1.1
+ */
+ public static int subAndCheck(int x, int y) throws MathArithmeticException {
+ long s = (long) x - (long) y;
+ if (s < Integer.MIN_VALUE || s > Integer.MAX_VALUE) {
+ throw new MathArithmeticException(LocalizedFormats.OVERFLOW_IN_SUBTRACTION, x, y);
+ }
+ return (int) s;
+ }
+
+ /**
+ * Subtract two long integers, checking for overflow.
+ *
+ * @param a Value.
+ * @param b Value.
+ * @return the difference {@code a - b}.
+ * @throws MathArithmeticException if the result can not be represented as a {@code long}.
+ * @since 1.2
+ */
+ public static long subAndCheck(long a, long b) throws MathArithmeticException {
+ long ret;
+ if (b == Long.MIN_VALUE) {
+ if (a < 0) {
+ ret = a - b;
+ } else {
+ throw new MathArithmeticException(LocalizedFormats.OVERFLOW_IN_ADDITION, a, -b);
+ }
+ } else {
+ // use additive inverse
+ ret = addAndCheck(a, -b, LocalizedFormats.OVERFLOW_IN_ADDITION);
+ }
+ return ret;
+ }
+
+ /**
+ * Raise an int to an int power.
+ *
+ * @param k Number to raise.
+ * @param e Exponent (must be positive or zero).
+ * @return \( k^e \)
+ * @throws NotPositiveException if {@code e < 0}.
+ * @throws MathArithmeticException if the result would overflow.
+ */
+ public static int pow(final int k, final int e)
+ throws NotPositiveException, MathArithmeticException {
+ if (e < 0) {
+ throw new NotPositiveException(LocalizedFormats.EXPONENT, e);
+ }
+
+ try {
+ int exp = e;
+ int result = 1;
+ int k2p = k;
+ while (true) {
+ if ((exp & 0x1) != 0) {
+ result = mulAndCheck(result, k2p);
+ }
+
+ exp >>= 1;
+ if (exp == 0) {
+ break;
+ }
+
+ k2p = mulAndCheck(k2p, k2p);
+ }
+
+ return result;
+ } catch (MathArithmeticException mae) {
+ // Add context information.
+ mae.getContext().addMessage(LocalizedFormats.OVERFLOW);
+ mae.getContext().addMessage(LocalizedFormats.BASE, k);
+ mae.getContext().addMessage(LocalizedFormats.EXPONENT, e);
+
+ // Rethrow.
+ throw mae;
+ }
+ }
+
+ /**
+ * Raise an int to a long power.
+ *
+ * @param k Number to raise.
+ * @param e Exponent (must be positive or zero).
+ * @return k<sup>e</sup>
+ * @throws NotPositiveException if {@code e < 0}.
+ * @deprecated As of 3.3. Please use {@link #pow(int,int)} instead.
+ */
+ @Deprecated
+ public static int pow(final int k, long e) throws NotPositiveException {
+ if (e < 0) {
+ throw new NotPositiveException(LocalizedFormats.EXPONENT, e);
+ }
+
+ int result = 1;
+ int k2p = k;
+ while (e != 0) {
+ if ((e & 0x1) != 0) {
+ result *= k2p;
+ }
+ k2p *= k2p;
+ e >>= 1;
+ }
+
+ return result;
+ }
+
+ /**
+ * Raise a long to an int power.
+ *
+ * @param k Number to raise.
+ * @param e Exponent (must be positive or zero).
+ * @return \( k^e \)
+ * @throws NotPositiveException if {@code e < 0}.
+ * @throws MathArithmeticException if the result would overflow.
+ */
+ public static long pow(final long k, final int e)
+ throws NotPositiveException, MathArithmeticException {
+ if (e < 0) {
+ throw new NotPositiveException(LocalizedFormats.EXPONENT, e);
+ }
+
+ try {
+ int exp = e;
+ long result = 1;
+ long k2p = k;
+ while (true) {
+ if ((exp & 0x1) != 0) {
+ result = mulAndCheck(result, k2p);
+ }
+
+ exp >>= 1;
+ if (exp == 0) {
+ break;
+ }
+
+ k2p = mulAndCheck(k2p, k2p);
+ }
+
+ return result;
+ } catch (MathArithmeticException mae) {
+ // Add context information.
+ mae.getContext().addMessage(LocalizedFormats.OVERFLOW);
+ mae.getContext().addMessage(LocalizedFormats.BASE, k);
+ mae.getContext().addMessage(LocalizedFormats.EXPONENT, e);
+
+ // Rethrow.
+ throw mae;
+ }
+ }
+
+ /**
+ * Raise a long to a long power.
+ *
+ * @param k Number to raise.
+ * @param e Exponent (must be positive or zero).
+ * @return k<sup>e</sup>
+ * @throws NotPositiveException if {@code e < 0}.
+ * @deprecated As of 3.3. Please use {@link #pow(long,int)} instead.
+ */
+ @Deprecated
+ public static long pow(final long k, long e) throws NotPositiveException {
+ if (e < 0) {
+ throw new NotPositiveException(LocalizedFormats.EXPONENT, e);
+ }
+
+ long result = 1l;
+ long k2p = k;
+ while (e != 0) {
+ if ((e & 0x1) != 0) {
+ result *= k2p;
+ }
+ k2p *= k2p;
+ e >>= 1;
+ }
+
+ return result;
+ }
+
+ /**
+ * Raise a BigInteger to an int power.
+ *
+ * @param k Number to raise.
+ * @param e Exponent (must be positive or zero).
+ * @return k<sup>e</sup>
+ * @throws NotPositiveException if {@code e < 0}.
+ */
+ public static BigInteger pow(final BigInteger k, int e) throws NotPositiveException {
+ if (e < 0) {
+ throw new NotPositiveException(LocalizedFormats.EXPONENT, e);
+ }
+
+ return k.pow(e);
+ }
+
+ /**
+ * Raise a BigInteger to a long power.
+ *
+ * @param k Number to raise.
+ * @param e Exponent (must be positive or zero).
+ * @return k<sup>e</sup>
+ * @throws NotPositiveException if {@code e < 0}.
+ */
+ public static BigInteger pow(final BigInteger k, long e) throws NotPositiveException {
+ if (e < 0) {
+ throw new NotPositiveException(LocalizedFormats.EXPONENT, e);
+ }
+
+ BigInteger result = BigInteger.ONE;
+ BigInteger k2p = k;
+ while (e != 0) {
+ if ((e & 0x1) != 0) {
+ result = result.multiply(k2p);
+ }
+ k2p = k2p.multiply(k2p);
+ e >>= 1;
+ }
+
+ return result;
+ }
+
+ /**
+ * Raise a BigInteger to a BigInteger power.
+ *
+ * @param k Number to raise.
+ * @param e Exponent (must be positive or zero).
+ * @return k<sup>e</sup>
+ * @throws NotPositiveException if {@code e < 0}.
+ */
+ public static BigInteger pow(final BigInteger k, BigInteger e) throws NotPositiveException {
+ if (e.compareTo(BigInteger.ZERO) < 0) {
+ throw new NotPositiveException(LocalizedFormats.EXPONENT, e);
+ }
+
+ BigInteger result = BigInteger.ONE;
+ BigInteger k2p = k;
+ while (!BigInteger.ZERO.equals(e)) {
+ if (e.testBit(0)) {
+ result = result.multiply(k2p);
+ }
+ k2p = k2p.multiply(k2p);
+ e = e.shiftRight(1);
+ }
+
+ return result;
+ }
+
+ /**
+ * Returns the <a href="http://mathworld.wolfram.com/StirlingNumberoftheSecondKind.html">
+ * Stirling number of the second kind</a>, "{@code S(n,k)}", the number of ways of partitioning
+ * an {@code n}-element set into {@code k} non-empty subsets.
+ *
+ * <p>The preconditions are {@code 0 <= k <= n } (otherwise {@code NotPositiveException} is
+ * thrown)
+ *
+ * @param n the size of the set
+ * @param k the number of non-empty subsets
+ * @return {@code S(n,k)}
+ * @throws NotPositiveException if {@code k < 0}.
+ * @throws NumberIsTooLargeException if {@code k > n}.
+ * @throws MathArithmeticException if some overflow happens, typically for n exceeding 25 and k
+ * between 20 and n-2 (S(n,n-1) is handled specifically and does not overflow)
+ * @since 3.1
+ * @deprecated use {@link CombinatoricsUtils#stirlingS2(int, int)}
+ */
+ @Deprecated
+ public static long stirlingS2(final int n, final int k)
+ throws NotPositiveException, NumberIsTooLargeException, MathArithmeticException {
+ return CombinatoricsUtils.stirlingS2(n, k);
+ }
+
+ /**
+ * Add two long integers, checking for overflow.
+ *
+ * @param a Addend.
+ * @param b Addend.
+ * @param pattern Pattern to use for any thrown exception.
+ * @return the sum {@code a + b}.
+ * @throws MathArithmeticException if the result cannot be represented as a {@code long}.
+ * @since 1.2
+ */
+ private static long addAndCheck(long a, long b, Localizable pattern)
+ throws MathArithmeticException {
+ final long result = a + b;
+ if (!((a ^ b) < 0 | (a ^ result) >= 0)) {
+ throw new MathArithmeticException(pattern, a, b);
+ }
+ return result;
+ }
+
+ /**
+ * Returns true if the argument is a power of two.
+ *
+ * @param n the number to test
+ * @return true if the argument is a power of two
+ */
+ public static boolean isPowerOfTwo(long n) {
+ return (n > 0) && ((n & (n - 1)) == 0);
+ }
+}
diff --git a/src/main/java/org/apache/commons/math3/util/BigReal.java b/src/main/java/org/apache/commons/math3/util/BigReal.java
new file mode 100644
index 0000000..54c59e8
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/util/BigReal.java
@@ -0,0 +1,362 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math3.util;
+
+import org.apache.commons.math3.Field;
+import org.apache.commons.math3.FieldElement;
+import org.apache.commons.math3.exception.MathArithmeticException;
+import org.apache.commons.math3.exception.util.LocalizedFormats;
+
+import java.io.Serializable;
+import java.math.BigDecimal;
+import java.math.BigInteger;
+import java.math.MathContext;
+import java.math.RoundingMode;
+
+/**
+ * Arbitrary precision decimal number.
+ *
+ * <p>This class is a simple wrapper around the standard <code>BigDecimal</code> in order to
+ * implement the {@link FieldElement} interface.
+ *
+ * @since 2.0
+ */
+public class BigReal implements FieldElement<BigReal>, Comparable<BigReal>, Serializable {
+
+ /** A big real representing 0. */
+ public static final BigReal ZERO = new BigReal(BigDecimal.ZERO);
+
+ /** A big real representing 1. */
+ public static final BigReal ONE = new BigReal(BigDecimal.ONE);
+
+ /** Serializable version identifier. */
+ private static final long serialVersionUID = 4984534880991310382L;
+
+ /** Underlying BigDecimal. */
+ private final BigDecimal d;
+
+ /** Rounding mode for divisions. * */
+ private RoundingMode roundingMode = RoundingMode.HALF_UP;
+
+ /*** BigDecimal scale ***/
+ private int scale = 64;
+
+ /**
+ * Build an instance from a BigDecimal.
+ *
+ * @param val value of the instance
+ */
+ public BigReal(BigDecimal val) {
+ d = val;
+ }
+
+ /**
+ * Build an instance from a BigInteger.
+ *
+ * @param val value of the instance
+ */
+ public BigReal(BigInteger val) {
+ d = new BigDecimal(val);
+ }
+
+ /**
+ * Build an instance from an unscaled BigInteger.
+ *
+ * @param unscaledVal unscaled value
+ * @param scale scale to use
+ */
+ public BigReal(BigInteger unscaledVal, int scale) {
+ d = new BigDecimal(unscaledVal, scale);
+ }
+
+ /**
+ * Build an instance from an unscaled BigInteger.
+ *
+ * @param unscaledVal unscaled value
+ * @param scale scale to use
+ * @param mc to used
+ */
+ public BigReal(BigInteger unscaledVal, int scale, MathContext mc) {
+ d = new BigDecimal(unscaledVal, scale, mc);
+ }
+
+ /**
+ * Build an instance from a BigInteger.
+ *
+ * @param val value of the instance
+ * @param mc context to use
+ */
+ public BigReal(BigInteger val, MathContext mc) {
+ d = new BigDecimal(val, mc);
+ }
+
+ /**
+ * Build an instance from a characters representation.
+ *
+ * @param in character representation of the value
+ */
+ public BigReal(char[] in) {
+ d = new BigDecimal(in);
+ }
+
+ /**
+ * Build an instance from a characters representation.
+ *
+ * @param in character representation of the value
+ * @param offset offset of the first character to analyze
+ * @param len length of the array slice to analyze
+ */
+ public BigReal(char[] in, int offset, int len) {
+ d = new BigDecimal(in, offset, len);
+ }
+
+ /**
+ * Build an instance from a characters representation.
+ *
+ * @param in character representation of the value
+ * @param offset offset of the first character to analyze
+ * @param len length of the array slice to analyze
+ * @param mc context to use
+ */
+ public BigReal(char[] in, int offset, int len, MathContext mc) {
+ d = new BigDecimal(in, offset, len, mc);
+ }
+
+ /**
+ * Build an instance from a characters representation.
+ *
+ * @param in character representation of the value
+ * @param mc context to use
+ */
+ public BigReal(char[] in, MathContext mc) {
+ d = new BigDecimal(in, mc);
+ }
+
+ /**
+ * Build an instance from a double.
+ *
+ * @param val value of the instance
+ */
+ public BigReal(double val) {
+ d = new BigDecimal(val);
+ }
+
+ /**
+ * Build an instance from a double.
+ *
+ * @param val value of the instance
+ * @param mc context to use
+ */
+ public BigReal(double val, MathContext mc) {
+ d = new BigDecimal(val, mc);
+ }
+
+ /**
+ * Build an instance from an int.
+ *
+ * @param val value of the instance
+ */
+ public BigReal(int val) {
+ d = new BigDecimal(val);
+ }
+
+ /**
+ * Build an instance from an int.
+ *
+ * @param val value of the instance
+ * @param mc context to use
+ */
+ public BigReal(int val, MathContext mc) {
+ d = new BigDecimal(val, mc);
+ }
+
+ /**
+ * Build an instance from a long.
+ *
+ * @param val value of the instance
+ */
+ public BigReal(long val) {
+ d = new BigDecimal(val);
+ }
+
+ /**
+ * Build an instance from a long.
+ *
+ * @param val value of the instance
+ * @param mc context to use
+ */
+ public BigReal(long val, MathContext mc) {
+ d = new BigDecimal(val, mc);
+ }
+
+ /**
+ * Build an instance from a String representation.
+ *
+ * @param val character representation of the value
+ */
+ public BigReal(String val) {
+ d = new BigDecimal(val);
+ }
+
+ /**
+ * Build an instance from a String representation.
+ *
+ * @param val character representation of the value
+ * @param mc context to use
+ */
+ public BigReal(String val, MathContext mc) {
+ d = new BigDecimal(val, mc);
+ }
+
+ /***
+ * Gets the rounding mode for division operations
+ * The default is {@code RoundingMode.HALF_UP}
+ * @return the rounding mode.
+ * @since 2.1
+ */
+ public RoundingMode getRoundingMode() {
+ return roundingMode;
+ }
+
+ /***
+ * Sets the rounding mode for decimal divisions.
+ * @param roundingMode rounding mode for decimal divisions
+ * @since 2.1
+ */
+ public void setRoundingMode(RoundingMode roundingMode) {
+ this.roundingMode = roundingMode;
+ }
+
+ /***
+ * Sets the scale for division operations.
+ * The default is 64
+ * @return the scale
+ * @since 2.1
+ */
+ public int getScale() {
+ return scale;
+ }
+
+ /***
+ * Sets the scale for division operations.
+ * @param scale scale for division operations
+ * @since 2.1
+ */
+ public void setScale(int scale) {
+ this.scale = scale;
+ }
+
+ /** {@inheritDoc} */
+ public BigReal add(BigReal a) {
+ return new BigReal(d.add(a.d));
+ }
+
+ /** {@inheritDoc} */
+ public BigReal subtract(BigReal a) {
+ return new BigReal(d.subtract(a.d));
+ }
+
+ /** {@inheritDoc} */
+ public BigReal negate() {
+ return new BigReal(d.negate());
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @throws MathArithmeticException if {@code a} is zero
+ */
+ public BigReal divide(BigReal a) throws MathArithmeticException {
+ try {
+ return new BigReal(d.divide(a.d, scale, roundingMode));
+ } catch (ArithmeticException e) {
+ // Division by zero has occurred
+ throw new MathArithmeticException(LocalizedFormats.ZERO_NOT_ALLOWED);
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @throws MathArithmeticException if {@code this} is zero
+ */
+ public BigReal reciprocal() throws MathArithmeticException {
+ try {
+ return new BigReal(BigDecimal.ONE.divide(d, scale, roundingMode));
+ } catch (ArithmeticException e) {
+ // Division by zero has occurred
+ throw new MathArithmeticException(LocalizedFormats.ZERO_NOT_ALLOWED);
+ }
+ }
+
+ /** {@inheritDoc} */
+ public BigReal multiply(BigReal a) {
+ return new BigReal(d.multiply(a.d));
+ }
+
+ /** {@inheritDoc} */
+ public BigReal multiply(final int n) {
+ return new BigReal(d.multiply(new BigDecimal(n)));
+ }
+
+ /** {@inheritDoc} */
+ public int compareTo(BigReal a) {
+ return d.compareTo(a.d);
+ }
+
+ /**
+ * Get the double value corresponding to the instance.
+ *
+ * @return double value corresponding to the instance
+ */
+ public double doubleValue() {
+ return d.doubleValue();
+ }
+
+ /**
+ * Get the BigDecimal value corresponding to the instance.
+ *
+ * @return BigDecimal value corresponding to the instance
+ */
+ public BigDecimal bigDecimalValue() {
+ return d;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public boolean equals(Object other) {
+ if (this == other) {
+ return true;
+ }
+
+ if (other instanceof BigReal) {
+ return d.equals(((BigReal) other).d);
+ }
+ return false;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public int hashCode() {
+ return d.hashCode();
+ }
+
+ /** {@inheritDoc} */
+ public Field<BigReal> getField() {
+ return BigRealField.getInstance();
+ }
+}
diff --git a/src/main/java/org/apache/commons/math3/util/BigRealField.java b/src/main/java/org/apache/commons/math3/util/BigRealField.java
new file mode 100644
index 0000000..cde8d34
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/util/BigRealField.java
@@ -0,0 +1,87 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math3.util;
+
+import org.apache.commons.math3.Field;
+import org.apache.commons.math3.FieldElement;
+
+import java.io.Serializable;
+
+/**
+ * Representation of real numbers with arbitrary precision field.
+ *
+ * <p>This class is a singleton.
+ *
+ * @see BigReal
+ * @since 2.0
+ */
+public class BigRealField implements Field<BigReal>, Serializable {
+
+ /** Serializable version identifier */
+ private static final long serialVersionUID = 4756431066541037559L;
+
+ /** Private constructor for the singleton. */
+ private BigRealField() {}
+
+ /**
+ * Get the unique instance.
+ *
+ * @return the unique instance
+ */
+ public static BigRealField getInstance() {
+ return LazyHolder.INSTANCE;
+ }
+
+ /** {@inheritDoc} */
+ public BigReal getOne() {
+ return BigReal.ONE;
+ }
+
+ /** {@inheritDoc} */
+ public BigReal getZero() {
+ return BigReal.ZERO;
+ }
+
+ /** {@inheritDoc} */
+ public Class<? extends FieldElement<BigReal>> getRuntimeClass() {
+ return BigReal.class;
+ }
+
+ // CHECKSTYLE: stop HideUtilityClassConstructor
+ /**
+ * Holder for the instance.
+ *
+ * <p>We use here the Initialization On Demand Holder Idiom.
+ */
+ private static class LazyHolder {
+ /** Cached field instance. */
+ private static final BigRealField INSTANCE = new BigRealField();
+ }
+
+ // CHECKSTYLE: resume HideUtilityClassConstructor
+
+ /**
+ * Handle deserialization of the singleton.
+ *
+ * @return the singleton instance
+ */
+ private Object readResolve() {
+ // return the singleton instance
+ return LazyHolder.INSTANCE;
+ }
+}
diff --git a/src/main/java/org/apache/commons/math3/util/CentralPivotingStrategy.java b/src/main/java/org/apache/commons/math3/util/CentralPivotingStrategy.java
new file mode 100644
index 0000000..3762b8e
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/util/CentralPivotingStrategy.java
@@ -0,0 +1,45 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math3.util;
+
+import org.apache.commons.math3.exception.MathIllegalArgumentException;
+
+import java.io.Serializable;
+
+/**
+ * A mid point strategy based on the average of begin and end indices.
+ *
+ * @since 3.4
+ */
+public class CentralPivotingStrategy implements PivotingStrategyInterface, Serializable {
+
+ /** Serializable UID. */
+ private static final long serialVersionUID = 20140713L;
+
+ /**
+ * {@inheritDoc} This in particular picks a average of begin and end indices
+ *
+ * @return The index corresponding to a simple average of the first and the last element indices
+ * of the array slice
+ * @throws MathIllegalArgumentException when indices exceeds range
+ */
+ public int pivotIndex(final double[] work, final int begin, final int end)
+ throws MathIllegalArgumentException {
+ MathArrays.verifyValues(work, begin, end - begin);
+ return begin + (end - begin) / 2;
+ }
+}
diff --git a/src/main/java/org/apache/commons/math3/util/Combinations.java b/src/main/java/org/apache/commons/math3/util/Combinations.java
new file mode 100644
index 0000000..8721dae
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/util/Combinations.java
@@ -0,0 +1,384 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math3.util;
+
+import org.apache.commons.math3.exception.DimensionMismatchException;
+import org.apache.commons.math3.exception.MathInternalError;
+import org.apache.commons.math3.exception.OutOfRangeException;
+
+import java.io.Serializable;
+import java.util.Arrays;
+import java.util.Comparator;
+import java.util.Iterator;
+import java.util.NoSuchElementException;
+
+/**
+ * Utility to create <a href="http://en.wikipedia.org/wiki/Combination">combinations</a> {@code (n,
+ * k)} of {@code k} elements in a set of {@code n} elements.
+ *
+ * @since 3.3
+ */
+public class Combinations implements Iterable<int[]> {
+ /** Size of the set from which combinations are drawn. */
+ private final int n;
+
+ /** Number of elements in each combination. */
+ private final int k;
+
+ /** Iteration order. */
+ private final IterationOrder iterationOrder;
+
+ /** Describes the type of iteration performed by the {@link #iterator() iterator}. */
+ private enum IterationOrder {
+ /** Lexicographic order. */
+ LEXICOGRAPHIC
+ }
+
+ /**
+ * Creates an instance whose range is the k-element subsets of {0, ..., n - 1} represented as
+ * {@code int[]} arrays.
+ *
+ * <p>The iteration order is lexicographic: the arrays returned by the {@link #iterator()
+ * iterator} are sorted in descending order and they are visited in lexicographic order with
+ * significance from right to left. For example, {@code new Combinations(4, 2).iterator()}
+ * returns an iterator that will generate the following sequence of arrays on successive calls
+ * to {@code next()}:<br>
+ * {@code [0, 1], [0, 2], [1, 2], [0, 3], [1, 3], [2, 3]} If {@code k == 0} an iterator
+ * containing an empty array is returned; if {@code k == n} an iterator containing [0, ..., n -
+ * 1] is returned.
+ *
+ * @param n Size of the set from which subsets are selected.
+ * @param k Size of the subsets to be enumerated.
+ * @throws org.apache.commons.math3.exception.NotPositiveException if {@code n < 0}.
+ * @throws org.apache.commons.math3.exception.NumberIsTooLargeException if {@code k > n}.
+ */
+ public Combinations(int n, int k) {
+ this(n, k, IterationOrder.LEXICOGRAPHIC);
+ }
+
+ /**
+ * Creates an instance whose range is the k-element subsets of {0, ..., n - 1} represented as
+ * {@code int[]} arrays.
+ *
+ * <p>If the {@code iterationOrder} argument is set to {@link IterationOrder#LEXICOGRAPHIC}, the
+ * arrays returned by the {@link #iterator() iterator} are sorted in descending order and they
+ * are visited in lexicographic order with significance from right to left. For example, {@code
+ * new Combinations(4, 2).iterator()} returns an iterator that will generate the following
+ * sequence of arrays on successive calls to {@code next()}:<br>
+ * {@code [0, 1], [0, 2], [1, 2], [0, 3], [1, 3], [2, 3]} If {@code k == 0} an iterator
+ * containing an empty array is returned; if {@code k == n} an iterator containing [0, ..., n -
+ * 1] is returned.
+ *
+ * @param n Size of the set from which subsets are selected.
+ * @param k Size of the subsets to be enumerated.
+ * @param iterationOrder Specifies the {@link #iterator() iteration order}.
+ * @throws org.apache.commons.math3.exception.NotPositiveException if {@code n < 0}.
+ * @throws org.apache.commons.math3.exception.NumberIsTooLargeException if {@code k > n}.
+ */
+ private Combinations(int n, int k, IterationOrder iterationOrder) {
+ CombinatoricsUtils.checkBinomial(n, k);
+ this.n = n;
+ this.k = k;
+ this.iterationOrder = iterationOrder;
+ }
+
+ /**
+ * Gets the size of the set from which combinations are drawn.
+ *
+ * @return the size of the universe.
+ */
+ public int getN() {
+ return n;
+ }
+
+ /**
+ * Gets the number of elements in each combination.
+ *
+ * @return the size of the subsets to be enumerated.
+ */
+ public int getK() {
+ return k;
+ }
+
+ /** {@inheritDoc} */
+ public Iterator<int[]> iterator() {
+ if (k == 0 || k == n) {
+ return new SingletonIterator(MathArrays.natural(k));
+ }
+
+ switch (iterationOrder) {
+ case LEXICOGRAPHIC:
+ return new LexicographicIterator(n, k);
+ default:
+ throw new MathInternalError(); // Should never happen.
+ }
+ }
+
+ /**
+ * Defines a lexicographic ordering of combinations. The returned comparator allows to compare
+ * any two combinations that can be produced by this instance's {@link #iterator() iterator}.
+ * Its {@code compare(int[],int[])} method will throw exceptions if passed combinations that are
+ * inconsistent with this instance:
+ *
+ * <ul>
+ * <li>{@code DimensionMismatchException} if the array lengths are not equal to {@code k},
+ * <li>{@code OutOfRangeException} if an element of the array is not within the interval [0,
+ * {@code n}).
+ * </ul>
+ *
+ * @return a lexicographic comparator.
+ */
+ public Comparator<int[]> comparator() {
+ return new LexicographicComparator(n, k);
+ }
+
+ /**
+ * Lexicographic combinations iterator.
+ *
+ * <p>Implementation follows Algorithm T in <i>The Art of Computer Programming</i> Internet
+ * Draft (PRE-FASCICLE 3A), "A Draft of Section 7.2.1.3 Generating All Combinations</a>, D.
+ * Knuth, 2004.
+ *
+ * <p>The degenerate cases {@code k == 0} and {@code k == n} are NOT handled by this
+ * implementation. If constructor arguments satisfy {@code k == 0} or {@code k >= n}, no
+ * exception is generated, but the iterator is empty.
+ */
+ private static class LexicographicIterator implements Iterator<int[]> {
+ /** Size of subsets returned by the iterator */
+ private final int k;
+
+ /**
+ * c[1], ..., c[k] stores the next combination; c[k + 1], c[k + 2] are sentinels.
+ *
+ * <p>Note that c[0] is "wasted" but this makes it a little easier to follow the code.
+ */
+ private final int[] c;
+
+ /** Return value for {@link #hasNext()} */
+ private boolean more = true;
+
+ /** Marker: smallest index such that c[j + 1] > j */
+ private int j;
+
+ /**
+ * Construct a CombinationIterator to enumerate k-sets from n.
+ *
+ * <p>NOTE: If {@code k === 0} or {@code k >= n}, the Iterator will be empty (that is,
+ * {@link #hasNext()} will return {@code false} immediately.
+ *
+ * @param n size of the set from which subsets are enumerated
+ * @param k size of the subsets to enumerate
+ */
+ LexicographicIterator(int n, int k) {
+ this.k = k;
+ c = new int[k + 3];
+ if (k == 0 || k >= n) {
+ more = false;
+ return;
+ }
+ // Initialize c to start with lexicographically first k-set
+ for (int i = 1; i <= k; i++) {
+ c[i] = i - 1;
+ }
+ // Initialize sentinels
+ c[k + 1] = n;
+ c[k + 2] = 0;
+ j = k; // Set up invariant: j is smallest index such that c[j + 1] > j
+ }
+
+ /** {@inheritDoc} */
+ public boolean hasNext() {
+ return more;
+ }
+
+ /** {@inheritDoc} */
+ public int[] next() {
+ if (!more) {
+ throw new NoSuchElementException();
+ }
+ // Copy return value (prepared by last activation)
+ final int[] ret = new int[k];
+ System.arraycopy(c, 1, ret, 0, k);
+
+ // Prepare next iteration
+ // T2 and T6 loop
+ int x = 0;
+ if (j > 0) {
+ x = j;
+ c[j] = x;
+ j--;
+ return ret;
+ }
+ // T3
+ if (c[1] + 1 < c[2]) {
+ c[1]++;
+ return ret;
+ } else {
+ j = 2;
+ }
+ // T4
+ boolean stepDone = false;
+ while (!stepDone) {
+ c[j - 1] = j - 2;
+ x = c[j] + 1;
+ if (x == c[j + 1]) {
+ j++;
+ } else {
+ stepDone = true;
+ }
+ }
+ // T5
+ if (j > k) {
+ more = false;
+ return ret;
+ }
+ // T6
+ c[j] = x;
+ j--;
+ return ret;
+ }
+
+ /** Not supported. */
+ public void remove() {
+ throw new UnsupportedOperationException();
+ }
+ }
+
+ /**
+ * Iterator with just one element to handle degenerate cases (full array, empty array) for
+ * combination iterator.
+ */
+ private static class SingletonIterator implements Iterator<int[]> {
+ /** Singleton array */
+ private final int[] singleton;
+
+ /** True on initialization, false after first call to next */
+ private boolean more = true;
+
+ /**
+ * Create a singleton iterator providing the given array.
+ *
+ * @param singleton array returned by the iterator
+ */
+ SingletonIterator(final int[] singleton) {
+ this.singleton = singleton;
+ }
+
+ /**
+ * @return True until next is called the first time, then false
+ */
+ public boolean hasNext() {
+ return more;
+ }
+
+ /**
+ * @return the singleton in first activation; throws NSEE thereafter
+ */
+ public int[] next() {
+ if (more) {
+ more = false;
+ return singleton;
+ } else {
+ throw new NoSuchElementException();
+ }
+ }
+
+ /** Not supported */
+ public void remove() {
+ throw new UnsupportedOperationException();
+ }
+ }
+
+ /**
+ * Defines the lexicographic ordering of combinations, using the {@link #lexNorm(int[])} method.
+ */
+ private static class LexicographicComparator implements Comparator<int[]>, Serializable {
+ /** Serializable version identifier. */
+ private static final long serialVersionUID = 20130906L;
+
+ /** Size of the set from which combinations are drawn. */
+ private final int n;
+
+ /** Number of elements in each combination. */
+ private final int k;
+
+ /**
+ * @param n Size of the set from which subsets are selected.
+ * @param k Size of the subsets to be enumerated.
+ */
+ LexicographicComparator(int n, int k) {
+ this.n = n;
+ this.k = k;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @throws DimensionMismatchException if the array lengths are not equal to {@code k}.
+ * @throws OutOfRangeException if an element of the array is not within the interval [0,
+ * {@code n}).
+ */
+ public int compare(int[] c1, int[] c2) {
+ if (c1.length != k) {
+ throw new DimensionMismatchException(c1.length, k);
+ }
+ if (c2.length != k) {
+ throw new DimensionMismatchException(c2.length, k);
+ }
+
+ // Method "lexNorm" works with ordered arrays.
+ final int[] c1s = MathArrays.copyOf(c1);
+ Arrays.sort(c1s);
+ final int[] c2s = MathArrays.copyOf(c2);
+ Arrays.sort(c2s);
+
+ final long v1 = lexNorm(c1s);
+ final long v2 = lexNorm(c2s);
+
+ if (v1 < v2) {
+ return -1;
+ } else if (v1 > v2) {
+ return 1;
+ } else {
+ return 0;
+ }
+ }
+
+ /**
+ * Computes the value (in base 10) represented by the digit (interpreted in base {@code n})
+ * in the input array in reverse order. For example if {@code c} is {@code {3, 2, 1}}, and
+ * {@code n} is 3, the method will return 18.
+ *
+ * @param c Input array.
+ * @return the lexicographic norm.
+ * @throws OutOfRangeException if an element of the array is not within the interval [0,
+ * {@code n}).
+ */
+ private long lexNorm(int[] c) {
+ long ret = 0;
+ for (int i = 0; i < c.length; i++) {
+ final int digit = c[i];
+ if (digit < 0 || digit >= n) {
+ throw new OutOfRangeException(digit, 0, n - 1);
+ }
+
+ ret += c[i] * ArithmeticUtils.pow(n, i);
+ }
+ return ret;
+ }
+ }
+}
diff --git a/src/main/java/org/apache/commons/math3/util/CombinatoricsUtils.java b/src/main/java/org/apache/commons/math3/util/CombinatoricsUtils.java
new file mode 100644
index 0000000..9743a6b
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/util/CombinatoricsUtils.java
@@ -0,0 +1,460 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math3.util;
+
+import org.apache.commons.math3.exception.MathArithmeticException;
+import org.apache.commons.math3.exception.NotPositiveException;
+import org.apache.commons.math3.exception.NumberIsTooLargeException;
+import org.apache.commons.math3.exception.util.LocalizedFormats;
+
+import java.util.Iterator;
+import java.util.concurrent.atomic.AtomicReference;
+
+/**
+ * Combinatorial utilities.
+ *
+ * @since 3.3
+ */
+public final class CombinatoricsUtils {
+
+ /** All long-representable factorials */
+ static final long[] FACTORIALS =
+ new long[] {
+ 1l,
+ 1l,
+ 2l,
+ 6l,
+ 24l,
+ 120l,
+ 720l,
+ 5040l,
+ 40320l,
+ 362880l,
+ 3628800l,
+ 39916800l,
+ 479001600l,
+ 6227020800l,
+ 87178291200l,
+ 1307674368000l,
+ 20922789888000l,
+ 355687428096000l,
+ 6402373705728000l,
+ 121645100408832000l,
+ 2432902008176640000l
+ };
+
+ /** Stirling numbers of the second kind. */
+ static final AtomicReference<long[][]> STIRLING_S2 = new AtomicReference<long[][]>(null);
+
+ /** Private constructor (class contains only static methods). */
+ private CombinatoricsUtils() {}
+
+ /**
+ * Returns an exact representation of the <a
+ * href="http://mathworld.wolfram.com/BinomialCoefficient.html">Binomial Coefficient</a>,
+ * "{@code n choose k}", the number of {@code k}-element subsets that can be selected from an
+ * {@code n}-element set.
+ *
+ * <p><Strong>Preconditions</strong>:
+ *
+ * <ul>
+ * <li>{@code 0 <= k <= n } (otherwise {@code MathIllegalArgumentException} is thrown)
+ * <li>The result is small enough to fit into a {@code long}. The largest value of {@code n}
+ * for which all coefficients are {@code < Long.MAX_VALUE} is 66. If the computed value
+ * exceeds {@code Long.MAX_VALUE} a {@code MathArithMeticException} is thrown.
+ * </ul>
+ *
+ * @param n the size of the set
+ * @param k the size of the subsets to be counted
+ * @return {@code n choose k}
+ * @throws NotPositiveException if {@code n < 0}.
+ * @throws NumberIsTooLargeException if {@code k > n}.
+ * @throws MathArithmeticException if the result is too large to be represented by a long
+ * integer.
+ */
+ public static long binomialCoefficient(final int n, final int k)
+ throws NotPositiveException, NumberIsTooLargeException, MathArithmeticException {
+ CombinatoricsUtils.checkBinomial(n, k);
+ if ((n == k) || (k == 0)) {
+ return 1;
+ }
+ if ((k == 1) || (k == n - 1)) {
+ return n;
+ }
+ // Use symmetry for large k
+ if (k > n / 2) {
+ return binomialCoefficient(n, n - k);
+ }
+
+ // We use the formula
+ // (n choose k) = n! / (n-k)! / k!
+ // (n choose k) == ((n-k+1)*...*n) / (1*...*k)
+ // which could be written
+ // (n choose k) == (n-1 choose k-1) * n / k
+ long result = 1;
+ if (n <= 61) {
+ // For n <= 61, the naive implementation cannot overflow.
+ int i = n - k + 1;
+ for (int j = 1; j <= k; j++) {
+ result = result * i / j;
+ i++;
+ }
+ } else if (n <= 66) {
+ // For n > 61 but n <= 66, the result cannot overflow,
+ // but we must take care not to overflow intermediate values.
+ int i = n - k + 1;
+ for (int j = 1; j <= k; j++) {
+ // We know that (result * i) is divisible by j,
+ // but (result * i) may overflow, so we split j:
+ // Filter out the gcd, d, so j/d and i/d are integer.
+ // result is divisible by (j/d) because (j/d)
+ // is relative prime to (i/d) and is a divisor of
+ // result * (i/d).
+ final long d = ArithmeticUtils.gcd(i, j);
+ result = (result / (j / d)) * (i / d);
+ i++;
+ }
+ } else {
+ // For n > 66, a result overflow might occur, so we check
+ // the multiplication, taking care to not overflow
+ // unnecessary.
+ int i = n - k + 1;
+ for (int j = 1; j <= k; j++) {
+ final long d = ArithmeticUtils.gcd(i, j);
+ result = ArithmeticUtils.mulAndCheck(result / (j / d), i / d);
+ i++;
+ }
+ }
+ return result;
+ }
+
+ /**
+ * Returns a {@code double} representation of the <a
+ * href="http://mathworld.wolfram.com/BinomialCoefficient.html">Binomial Coefficient</a>,
+ * "{@code n choose k}", the number of {@code k}-element subsets that can be selected from an
+ * {@code n}-element set.
+ *
+ * <p><Strong>Preconditions</strong>:
+ *
+ * <ul>
+ * <li>{@code 0 <= k <= n } (otherwise {@code IllegalArgumentException} is thrown)
+ * <li>The result is small enough to fit into a {@code double}. The largest value of {@code n}
+ * for which all coefficients are less than Double.MAX_VALUE is 1029. If the computed
+ * value exceeds Double.MAX_VALUE, Double.POSITIVE_INFINITY is returned
+ * </ul>
+ *
+ * @param n the size of the set
+ * @param k the size of the subsets to be counted
+ * @return {@code n choose k}
+ * @throws NotPositiveException if {@code n < 0}.
+ * @throws NumberIsTooLargeException if {@code k > n}.
+ * @throws MathArithmeticException if the result is too large to be represented by a long
+ * integer.
+ */
+ public static double binomialCoefficientDouble(final int n, final int k)
+ throws NotPositiveException, NumberIsTooLargeException, MathArithmeticException {
+ CombinatoricsUtils.checkBinomial(n, k);
+ if ((n == k) || (k == 0)) {
+ return 1d;
+ }
+ if ((k == 1) || (k == n - 1)) {
+ return n;
+ }
+ if (k > n / 2) {
+ return binomialCoefficientDouble(n, n - k);
+ }
+ if (n < 67) {
+ return binomialCoefficient(n, k);
+ }
+
+ double result = 1d;
+ for (int i = 1; i <= k; i++) {
+ result *= (double) (n - k + i) / (double) i;
+ }
+
+ return FastMath.floor(result + 0.5);
+ }
+
+ /**
+ * Returns the natural {@code log} of the <a
+ * href="http://mathworld.wolfram.com/BinomialCoefficient.html">Binomial Coefficient</a>,
+ * "{@code n choose k}", the number of {@code k}-element subsets that can be selected from an
+ * {@code n}-element set.
+ *
+ * <p><Strong>Preconditions</strong>:
+ *
+ * <ul>
+ * <li>{@code 0 <= k <= n } (otherwise {@code MathIllegalArgumentException} is thrown)
+ * </ul>
+ *
+ * @param n the size of the set
+ * @param k the size of the subsets to be counted
+ * @return {@code n choose k}
+ * @throws NotPositiveException if {@code n < 0}.
+ * @throws NumberIsTooLargeException if {@code k > n}.
+ * @throws MathArithmeticException if the result is too large to be represented by a long
+ * integer.
+ */
+ public static double binomialCoefficientLog(final int n, final int k)
+ throws NotPositiveException, NumberIsTooLargeException, MathArithmeticException {
+ CombinatoricsUtils.checkBinomial(n, k);
+ if ((n == k) || (k == 0)) {
+ return 0;
+ }
+ if ((k == 1) || (k == n - 1)) {
+ return FastMath.log(n);
+ }
+
+ /*
+ * For values small enough to do exact integer computation,
+ * return the log of the exact value
+ */
+ if (n < 67) {
+ return FastMath.log(binomialCoefficient(n, k));
+ }
+
+ /*
+ * Return the log of binomialCoefficientDouble for values that will not
+ * overflow binomialCoefficientDouble
+ */
+ if (n < 1030) {
+ return FastMath.log(binomialCoefficientDouble(n, k));
+ }
+
+ if (k > n / 2) {
+ return binomialCoefficientLog(n, n - k);
+ }
+
+ /*
+ * Sum logs for values that could overflow
+ */
+ double logSum = 0;
+
+ // n!/(n-k)!
+ for (int i = n - k + 1; i <= n; i++) {
+ logSum += FastMath.log(i);
+ }
+
+ // divide by k!
+ for (int i = 2; i <= k; i++) {
+ logSum -= FastMath.log(i);
+ }
+
+ return logSum;
+ }
+
+ /**
+ * Returns n!. Shorthand for {@code n} <a href="http://mathworld.wolfram.com/Factorial.html">
+ * Factorial</a>, the product of the numbers {@code 1,...,n}.
+ *
+ * <p><Strong>Preconditions</strong>:
+ *
+ * <ul>
+ * <li>{@code n >= 0} (otherwise {@code MathIllegalArgumentException} is thrown)
+ * <li>The result is small enough to fit into a {@code long}. The largest value of {@code n}
+ * for which {@code n!} does not exceed Long.MAX_VALUE} is 20. If the computed value
+ * exceeds {@code Long.MAX_VALUE} an {@code MathArithMeticException } is thrown.
+ * </ul>
+ *
+ * @param n argument
+ * @return {@code n!}
+ * @throws MathArithmeticException if the result is too large to be represented by a {@code
+ * long}.
+ * @throws NotPositiveException if {@code n < 0}.
+ * @throws MathArithmeticException if {@code n > 20}: The factorial value is too large to fit in
+ * a {@code long}.
+ */
+ public static long factorial(final int n) throws NotPositiveException, MathArithmeticException {
+ if (n < 0) {
+ throw new NotPositiveException(LocalizedFormats.FACTORIAL_NEGATIVE_PARAMETER, n);
+ }
+ if (n > 20) {
+ throw new MathArithmeticException();
+ }
+ return FACTORIALS[n];
+ }
+
+ /**
+ * Compute n!, the<a href="http://mathworld.wolfram.com/Factorial.html">factorial</a> of {@code
+ * n} (the product of the numbers 1 to n), as a {@code double}. The result should be small
+ * enough to fit into a {@code double}: The largest {@code n} for which {@code n!} does not
+ * exceed {@code Double.MAX_VALUE} is 170. If the computed value exceeds {@code
+ * Double.MAX_VALUE}, {@code Double.POSITIVE_INFINITY} is returned.
+ *
+ * @param n Argument.
+ * @return {@code n!}
+ * @throws NotPositiveException if {@code n < 0}.
+ */
+ public static double factorialDouble(final int n) throws NotPositiveException {
+ if (n < 0) {
+ throw new NotPositiveException(LocalizedFormats.FACTORIAL_NEGATIVE_PARAMETER, n);
+ }
+ if (n < 21) {
+ return FACTORIALS[n];
+ }
+ return FastMath.floor(FastMath.exp(CombinatoricsUtils.factorialLog(n)) + 0.5);
+ }
+
+ /**
+ * Compute the natural logarithm of the factorial of {@code n}.
+ *
+ * @param n Argument.
+ * @return {@code n!}
+ * @throws NotPositiveException if {@code n < 0}.
+ */
+ public static double factorialLog(final int n) throws NotPositiveException {
+ if (n < 0) {
+ throw new NotPositiveException(LocalizedFormats.FACTORIAL_NEGATIVE_PARAMETER, n);
+ }
+ if (n < 21) {
+ return FastMath.log(FACTORIALS[n]);
+ }
+ double logSum = 0;
+ for (int i = 2; i <= n; i++) {
+ logSum += FastMath.log(i);
+ }
+ return logSum;
+ }
+
+ /**
+ * Returns the <a href="http://mathworld.wolfram.com/StirlingNumberoftheSecondKind.html">
+ * Stirling number of the second kind</a>, "{@code S(n,k)}", the number of ways of partitioning
+ * an {@code n}-element set into {@code k} non-empty subsets.
+ *
+ * <p>The preconditions are {@code 0 <= k <= n } (otherwise {@code NotPositiveException} is
+ * thrown)
+ *
+ * @param n the size of the set
+ * @param k the number of non-empty subsets
+ * @return {@code S(n,k)}
+ * @throws NotPositiveException if {@code k < 0}.
+ * @throws NumberIsTooLargeException if {@code k > n}.
+ * @throws MathArithmeticException if some overflow happens, typically for n exceeding 25 and k
+ * between 20 and n-2 (S(n,n-1) is handled specifically and does not overflow)
+ * @since 3.1
+ */
+ public static long stirlingS2(final int n, final int k)
+ throws NotPositiveException, NumberIsTooLargeException, MathArithmeticException {
+ if (k < 0) {
+ throw new NotPositiveException(k);
+ }
+ if (k > n) {
+ throw new NumberIsTooLargeException(k, n, true);
+ }
+
+ long[][] stirlingS2 = STIRLING_S2.get();
+
+ if (stirlingS2 == null) {
+ // the cache has never been initialized, compute the first numbers
+ // by direct recurrence relation
+
+ // as S(26,9) = 11201516780955125625 is larger than Long.MAX_VALUE
+ // we must stop computation at row 26
+ final int maxIndex = 26;
+ stirlingS2 = new long[maxIndex][];
+ stirlingS2[0] = new long[] {1l};
+ for (int i = 1; i < stirlingS2.length; ++i) {
+ stirlingS2[i] = new long[i + 1];
+ stirlingS2[i][0] = 0;
+ stirlingS2[i][1] = 1;
+ stirlingS2[i][i] = 1;
+ for (int j = 2; j < i; ++j) {
+ stirlingS2[i][j] = j * stirlingS2[i - 1][j] + stirlingS2[i - 1][j - 1];
+ }
+ }
+
+ // atomically save the cache
+ STIRLING_S2.compareAndSet(null, stirlingS2);
+ }
+
+ if (n < stirlingS2.length) {
+ // the number is in the small cache
+ return stirlingS2[n][k];
+ } else {
+ // use explicit formula to compute the number without caching it
+ if (k == 0) {
+ return 0;
+ } else if (k == 1 || k == n) {
+ return 1;
+ } else if (k == 2) {
+ return (1l << (n - 1)) - 1l;
+ } else if (k == n - 1) {
+ return binomialCoefficient(n, 2);
+ } else {
+ // definition formula: note that this may trigger some overflow
+ long sum = 0;
+ long sign = ((k & 0x1) == 0) ? 1 : -1;
+ for (int j = 1; j <= k; ++j) {
+ sign = -sign;
+ sum += sign * binomialCoefficient(k, j) * ArithmeticUtils.pow(j, n);
+ if (sum < 0) {
+ // there was an overflow somewhere
+ throw new MathArithmeticException(
+ LocalizedFormats.ARGUMENT_OUTSIDE_DOMAIN,
+ n,
+ 0,
+ stirlingS2.length - 1);
+ }
+ }
+ return sum / factorial(k);
+ }
+ }
+ }
+
+ /**
+ * Returns an iterator whose range is the k-element subsets of {0, ..., n - 1} represented as
+ * {@code int[]} arrays.
+ *
+ * <p>The arrays returned by the iterator are sorted in descending order and they are visited in
+ * lexicographic order with significance from right to left. For example,
+ * combinationsIterator(4, 2) returns an Iterator that will generate the following sequence of
+ * arrays on successive calls to {@code next()}:
+ *
+ * <p>{@code [0, 1], [0, 2], [1, 2], [0, 3], [1, 3], [2, 3]}
+ *
+ * <p>If {@code k == 0} an Iterator containing an empty array is returned and if {@code k == n}
+ * an Iterator containing [0, ..., n -1] is returned.
+ *
+ * @param n Size of the set from which subsets are selected.
+ * @param k Size of the subsets to be enumerated.
+ * @return an {@link Iterator iterator} over the k-sets in n.
+ * @throws NotPositiveException if {@code n < 0}.
+ * @throws NumberIsTooLargeException if {@code k > n}.
+ */
+ public static Iterator<int[]> combinationsIterator(int n, int k) {
+ return new Combinations(n, k).iterator();
+ }
+
+ /**
+ * Check binomial preconditions.
+ *
+ * @param n Size of the set.
+ * @param k Size of the subsets to be counted.
+ * @throws NotPositiveException if {@code n < 0}.
+ * @throws NumberIsTooLargeException if {@code k > n}.
+ */
+ public static void checkBinomial(final int n, final int k)
+ throws NumberIsTooLargeException, NotPositiveException {
+ if (n < k) {
+ throw new NumberIsTooLargeException(
+ LocalizedFormats.BINOMIAL_INVALID_PARAMETERS_ORDER, k, n, true);
+ }
+ if (n < 0) {
+ throw new NotPositiveException(LocalizedFormats.BINOMIAL_NEGATIVE_PARAMETER, n);
+ }
+ }
+}
diff --git a/src/main/java/org/apache/commons/math3/util/CompositeFormat.java b/src/main/java/org/apache/commons/math3/util/CompositeFormat.java
new file mode 100644
index 0000000..63dfd59
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/util/CompositeFormat.java
@@ -0,0 +1,215 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math3.util;
+
+import java.text.FieldPosition;
+import java.text.NumberFormat;
+import java.text.ParsePosition;
+import java.util.Locale;
+
+/** Base class for formatters of composite objects (complex numbers, vectors ...). */
+public class CompositeFormat {
+
+ /** Class contains only static methods. */
+ private CompositeFormat() {}
+
+ /**
+ * Create a default number format. The default number format is based on {@link
+ * NumberFormat#getInstance()} with the only customizing that the maximum number of fraction
+ * digits is set to 10.
+ *
+ * @return the default number format.
+ */
+ public static NumberFormat getDefaultNumberFormat() {
+ return getDefaultNumberFormat(Locale.getDefault());
+ }
+
+ /**
+ * Create a default number format. The default number format is based on {@link
+ * NumberFormat#getInstance(java.util.Locale)} with the only customizing that the maximum number
+ * of fraction digits is set to 10.
+ *
+ * @param locale the specific locale used by the format.
+ * @return the default number format specific to the given locale.
+ */
+ public static NumberFormat getDefaultNumberFormat(final Locale locale) {
+ final NumberFormat nf = NumberFormat.getInstance(locale);
+ nf.setMaximumFractionDigits(10);
+ return nf;
+ }
+
+ /**
+ * Parses <code>source</code> until a non-whitespace character is found.
+ *
+ * @param source the string to parse
+ * @param pos input/output parsing parameter. On output, <code>pos</code> holds the index of the
+ * next non-whitespace character.
+ */
+ public static void parseAndIgnoreWhitespace(final String source, final ParsePosition pos) {
+ parseNextCharacter(source, pos);
+ pos.setIndex(pos.getIndex() - 1);
+ }
+
+ /**
+ * Parses <code>source</code> until a non-whitespace character is found.
+ *
+ * @param source the string to parse
+ * @param pos input/output parsing parameter.
+ * @return the first non-whitespace character.
+ */
+ public static char parseNextCharacter(final String source, final ParsePosition pos) {
+ int index = pos.getIndex();
+ final int n = source.length();
+ char ret = 0;
+
+ if (index < n) {
+ char c;
+ do {
+ c = source.charAt(index++);
+ } while (Character.isWhitespace(c) && index < n);
+ pos.setIndex(index);
+
+ if (index < n) {
+ ret = c;
+ }
+ }
+
+ return ret;
+ }
+
+ /**
+ * Parses <code>source</code> for special double values. These values include Double.NaN,
+ * Double.POSITIVE_INFINITY, Double.NEGATIVE_INFINITY.
+ *
+ * @param source the string to parse
+ * @param value the special value to parse.
+ * @param pos input/output parsing parameter.
+ * @return the special number.
+ */
+ private static Number parseNumber(
+ final String source, final double value, final ParsePosition pos) {
+ Number ret = null;
+
+ StringBuilder sb = new StringBuilder();
+ sb.append('(');
+ sb.append(value);
+ sb.append(')');
+
+ final int n = sb.length();
+ final int startIndex = pos.getIndex();
+ final int endIndex = startIndex + n;
+ if (endIndex < source.length()
+ && source.substring(startIndex, endIndex).compareTo(sb.toString()) == 0) {
+ ret = Double.valueOf(value);
+ pos.setIndex(endIndex);
+ }
+
+ return ret;
+ }
+
+ /**
+ * Parses <code>source</code> for a number. This method can parse normal, numeric values as well
+ * as special values. These special values include Double.NaN, Double.POSITIVE_INFINITY,
+ * Double.NEGATIVE_INFINITY.
+ *
+ * @param source the string to parse
+ * @param format the number format used to parse normal, numeric values.
+ * @param pos input/output parsing parameter.
+ * @return the parsed number.
+ */
+ public static Number parseNumber(
+ final String source, final NumberFormat format, final ParsePosition pos) {
+ final int startIndex = pos.getIndex();
+ Number number = format.parse(source, pos);
+ final int endIndex = pos.getIndex();
+
+ // check for error parsing number
+ if (startIndex == endIndex) {
+ // try parsing special numbers
+ final double[] special = {
+ Double.NaN, Double.POSITIVE_INFINITY, Double.NEGATIVE_INFINITY
+ };
+ for (int i = 0; i < special.length; ++i) {
+ number = parseNumber(source, special[i], pos);
+ if (number != null) {
+ break;
+ }
+ }
+ }
+
+ return number;
+ }
+
+ /**
+ * Parse <code>source</code> for an expected fixed string.
+ *
+ * @param source the string to parse
+ * @param expected expected string
+ * @param pos input/output parsing parameter.
+ * @return true if the expected string was there
+ */
+ public static boolean parseFixedstring(
+ final String source, final String expected, final ParsePosition pos) {
+
+ final int startIndex = pos.getIndex();
+ final int endIndex = startIndex + expected.length();
+ if ((startIndex >= source.length())
+ || (endIndex > source.length())
+ || (source.substring(startIndex, endIndex).compareTo(expected) != 0)) {
+ // set index back to start, error index should be the start index
+ pos.setIndex(startIndex);
+ pos.setErrorIndex(startIndex);
+ return false;
+ }
+
+ // the string was here
+ pos.setIndex(endIndex);
+ return true;
+ }
+
+ /**
+ * Formats a double value to produce a string. In general, the value is formatted using the
+ * formatting rules of <code>format</code>. There are three exceptions to this:
+ *
+ * <ol>
+ * <li>NaN is formatted as '(NaN)'
+ * <li>Positive infinity is formatted as '(Infinity)'
+ * <li>Negative infinity is formatted as '(-Infinity)'
+ * </ol>
+ *
+ * @param value the double to format.
+ * @param format the format used.
+ * @param toAppendTo where the text is to be appended
+ * @param pos On input: an alignment field, if desired. On output: the offsets of the alignment
+ * field
+ * @return the value passed in as toAppendTo.
+ */
+ public static StringBuffer formatDouble(
+ final double value,
+ final NumberFormat format,
+ final StringBuffer toAppendTo,
+ final FieldPosition pos) {
+ if (Double.isNaN(value) || Double.isInfinite(value)) {
+ toAppendTo.append('(');
+ toAppendTo.append(value);
+ toAppendTo.append(')');
+ } else {
+ format.format(value, toAppendTo, pos);
+ }
+ return toAppendTo;
+ }
+}
diff --git a/src/main/java/org/apache/commons/math3/util/ContinuedFraction.java b/src/main/java/org/apache/commons/math3/util/ContinuedFraction.java
new file mode 100644
index 0000000..69b4964
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/util/ContinuedFraction.java
@@ -0,0 +1,181 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math3.util;
+
+import org.apache.commons.math3.exception.ConvergenceException;
+import org.apache.commons.math3.exception.MaxCountExceededException;
+import org.apache.commons.math3.exception.util.LocalizedFormats;
+
+/**
+ * Provides a generic means to evaluate continued fractions. Subclasses simply provided the a and b
+ * coefficients to evaluate the continued fraction.
+ *
+ * <p>References:
+ *
+ * <ul>
+ * <li><a href="http://mathworld.wolfram.com/ContinuedFraction.html">Continued Fraction</a>
+ * </ul>
+ */
+public abstract class ContinuedFraction {
+ /** Maximum allowed numerical error. */
+ private static final double DEFAULT_EPSILON = 10e-9;
+
+ /** Default constructor. */
+ protected ContinuedFraction() {
+ super();
+ }
+
+ /**
+ * Access the n-th a coefficient of the continued fraction. Since a can be a function of the
+ * evaluation point, x, that is passed in as well.
+ *
+ * @param n the coefficient index to retrieve.
+ * @param x the evaluation point.
+ * @return the n-th a coefficient.
+ */
+ protected abstract double getA(int n, double x);
+
+ /**
+ * Access the n-th b coefficient of the continued fraction. Since b can be a function of the
+ * evaluation point, x, that is passed in as well.
+ *
+ * @param n the coefficient index to retrieve.
+ * @param x the evaluation point.
+ * @return the n-th b coefficient.
+ */
+ protected abstract double getB(int n, double x);
+
+ /**
+ * Evaluates the continued fraction at the value x.
+ *
+ * @param x the evaluation point.
+ * @return the value of the continued fraction evaluated at x.
+ * @throws ConvergenceException if the algorithm fails to converge.
+ */
+ public double evaluate(double x) throws ConvergenceException {
+ return evaluate(x, DEFAULT_EPSILON, Integer.MAX_VALUE);
+ }
+
+ /**
+ * Evaluates the continued fraction at the value x.
+ *
+ * @param x the evaluation point.
+ * @param epsilon maximum error allowed.
+ * @return the value of the continued fraction evaluated at x.
+ * @throws ConvergenceException if the algorithm fails to converge.
+ */
+ public double evaluate(double x, double epsilon) throws ConvergenceException {
+ return evaluate(x, epsilon, Integer.MAX_VALUE);
+ }
+
+ /**
+ * Evaluates the continued fraction at the value x.
+ *
+ * @param x the evaluation point.
+ * @param maxIterations maximum number of convergents
+ * @return the value of the continued fraction evaluated at x.
+ * @throws ConvergenceException if the algorithm fails to converge.
+ * @throws MaxCountExceededException if maximal number of iterations is reached
+ */
+ public double evaluate(double x, int maxIterations)
+ throws ConvergenceException, MaxCountExceededException {
+ return evaluate(x, DEFAULT_EPSILON, maxIterations);
+ }
+
+ /**
+ * Evaluates the continued fraction at the value x.
+ *
+ * <p>The implementation of this method is based on the modified Lentz algorithm as described on
+ * page 18 ff. in:
+ *
+ * <ul>
+ * <li>I. J. Thompson, A. R. Barnett. "Coulomb and Bessel Functions of Complex Arguments and
+ * Order." <a target="_blank"
+ * href="http://www.fresco.org.uk/papers/Thompson-JCP64p490.pdf">
+ * http://www.fresco.org.uk/papers/Thompson-JCP64p490.pdf</a>
+ * </ul>
+ *
+ * <b>Note:</b> the implementation uses the terms a<sub>i</sub> and b<sub>i</sub> as defined in
+ * <a href="http://mathworld.wolfram.com/ContinuedFraction.html">Continued Fraction @
+ * MathWorld</a>.
+ *
+ * @param x the evaluation point.
+ * @param epsilon maximum error allowed.
+ * @param maxIterations maximum number of convergents
+ * @return the value of the continued fraction evaluated at x.
+ * @throws ConvergenceException if the algorithm fails to converge.
+ * @throws MaxCountExceededException if maximal number of iterations is reached
+ */
+ public double evaluate(double x, double epsilon, int maxIterations)
+ throws ConvergenceException, MaxCountExceededException {
+ final double small = 1e-50;
+ double hPrev = getA(0, x);
+
+ // use the value of small as epsilon criteria for zero checks
+ if (Precision.equals(hPrev, 0.0, small)) {
+ hPrev = small;
+ }
+
+ int n = 1;
+ double dPrev = 0.0;
+ double cPrev = hPrev;
+ double hN = hPrev;
+
+ while (n < maxIterations) {
+ final double a = getA(n, x);
+ final double b = getB(n, x);
+
+ double dN = a + b * dPrev;
+ if (Precision.equals(dN, 0.0, small)) {
+ dN = small;
+ }
+ double cN = a + b / cPrev;
+ if (Precision.equals(cN, 0.0, small)) {
+ cN = small;
+ }
+
+ dN = 1 / dN;
+ final double deltaN = cN * dN;
+ hN = hPrev * deltaN;
+
+ if (Double.isInfinite(hN)) {
+ throw new ConvergenceException(
+ LocalizedFormats.CONTINUED_FRACTION_INFINITY_DIVERGENCE, x);
+ }
+ if (Double.isNaN(hN)) {
+ throw new ConvergenceException(
+ LocalizedFormats.CONTINUED_FRACTION_NAN_DIVERGENCE, x);
+ }
+
+ if (FastMath.abs(deltaN - 1.0) < epsilon) {
+ break;
+ }
+
+ dPrev = dN;
+ cPrev = cN;
+ hPrev = hN;
+ n++;
+ }
+
+ if (n >= maxIterations) {
+ throw new MaxCountExceededException(
+ LocalizedFormats.NON_CONVERGENT_CONTINUED_FRACTION, maxIterations, x);
+ }
+
+ return hN;
+ }
+}
diff --git a/src/main/java/org/apache/commons/math3/util/Decimal64.java b/src/main/java/org/apache/commons/math3/util/Decimal64.java
new file mode 100644
index 0000000..4d25339
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/util/Decimal64.java
@@ -0,0 +1,806 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math3.util;
+
+import org.apache.commons.math3.Field;
+import org.apache.commons.math3.RealFieldElement;
+import org.apache.commons.math3.exception.DimensionMismatchException;
+
+/**
+ * This class wraps a {@code double} value in an object. It is similar to the standard class {@link
+ * Double}, while also implementing the {@link RealFieldElement} interface.
+ *
+ * @since 3.1
+ */
+public class Decimal64 extends Number
+ implements RealFieldElement<Decimal64>, Comparable<Decimal64> {
+
+ /** The constant value of {@code 0d} as a {@code Decimal64}. */
+ public static final Decimal64 ZERO;
+
+ /** The constant value of {@code 1d} as a {@code Decimal64}. */
+ public static final Decimal64 ONE;
+
+ /** The constant value of {@link Double#NEGATIVE_INFINITY} as a {@code Decimal64}. */
+ public static final Decimal64 NEGATIVE_INFINITY;
+
+ /** The constant value of {@link Double#POSITIVE_INFINITY} as a {@code Decimal64}. */
+ public static final Decimal64 POSITIVE_INFINITY;
+
+ /** The constant value of {@link Double#NaN} as a {@code Decimal64}. */
+ public static final Decimal64 NAN;
+
+ /** */
+ private static final long serialVersionUID = 20120227L;
+
+ static {
+ ZERO = new Decimal64(0d);
+ ONE = new Decimal64(1d);
+ NEGATIVE_INFINITY = new Decimal64(Double.NEGATIVE_INFINITY);
+ POSITIVE_INFINITY = new Decimal64(Double.POSITIVE_INFINITY);
+ NAN = new Decimal64(Double.NaN);
+ }
+
+ /** The primitive {@code double} value of this object. */
+ private final double value;
+
+ /**
+ * Creates a new instance of this class.
+ *
+ * @param x the primitive {@code double} value of the object to be created
+ */
+ public Decimal64(final double x) {
+ this.value = x;
+ }
+
+ /*
+ * Methods from the FieldElement interface.
+ */
+
+ /** {@inheritDoc} */
+ public Field<Decimal64> getField() {
+ return Decimal64Field.getInstance();
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * <p>The current implementation strictly enforces {@code this.add(a).equals(new
+ * Decimal64(this.doubleValue() + a.doubleValue()))}.
+ */
+ public Decimal64 add(final Decimal64 a) {
+ return new Decimal64(this.value + a.value);
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * <p>The current implementation strictly enforces {@code this.subtract(a).equals(new
+ * Decimal64(this.doubleValue() - a.doubleValue()))}.
+ */
+ public Decimal64 subtract(final Decimal64 a) {
+ return new Decimal64(this.value - a.value);
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * <p>The current implementation strictly enforces {@code this.negate().equals(new
+ * Decimal64(-this.doubleValue()))}.
+ */
+ public Decimal64 negate() {
+ return new Decimal64(-this.value);
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * <p>The current implementation strictly enforces {@code this.multiply(a).equals(new
+ * Decimal64(this.doubleValue() * a.doubleValue()))}.
+ */
+ public Decimal64 multiply(final Decimal64 a) {
+ return new Decimal64(this.value * a.value);
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * <p>The current implementation strictly enforces {@code this.multiply(n).equals(new
+ * Decimal64(n * this.doubleValue()))}.
+ */
+ public Decimal64 multiply(final int n) {
+ return new Decimal64(n * this.value);
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * <p>The current implementation strictly enforces {@code this.divide(a).equals(new
+ * Decimal64(this.doubleValue() / a.doubleValue()))}.
+ */
+ public Decimal64 divide(final Decimal64 a) {
+ return new Decimal64(this.value / a.value);
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * <p>The current implementation strictly enforces {@code this.reciprocal().equals(new
+ * Decimal64(1.0 / this.doubleValue()))}.
+ */
+ public Decimal64 reciprocal() {
+ return new Decimal64(1.0 / this.value);
+ }
+
+ /*
+ * Methods from the Number abstract class
+ */
+
+ /**
+ * {@inheritDoc}
+ *
+ * <p>The current implementation performs casting to a {@code byte}.
+ */
+ @Override
+ public byte byteValue() {
+ return (byte) value;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * <p>The current implementation performs casting to a {@code short}.
+ */
+ @Override
+ public short shortValue() {
+ return (short) value;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * <p>The current implementation performs casting to a {@code int}.
+ */
+ @Override
+ public int intValue() {
+ return (int) value;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * <p>The current implementation performs casting to a {@code long}.
+ */
+ @Override
+ public long longValue() {
+ return (long) value;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * <p>The current implementation performs casting to a {@code float}.
+ */
+ @Override
+ public float floatValue() {
+ return (float) value;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public double doubleValue() {
+ return value;
+ }
+
+ /*
+ * Methods from the Comparable interface.
+ */
+
+ /**
+ * {@inheritDoc}
+ *
+ * <p>The current implementation returns the same value as <center> {@code new
+ * Double(this.doubleValue()).compareTo(new Double(o.doubleValue()))} </center>
+ *
+ * @see Double#compareTo(Double)
+ */
+ public int compareTo(final Decimal64 o) {
+ return Double.compare(this.value, o.value);
+ }
+
+ /*
+ * Methods from the Object abstract class.
+ */
+
+ /** {@inheritDoc} */
+ @Override
+ public boolean equals(final Object obj) {
+ if (obj instanceof Decimal64) {
+ final Decimal64 that = (Decimal64) obj;
+ return Double.doubleToLongBits(this.value) == Double.doubleToLongBits(that.value);
+ }
+ return false;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * <p>The current implementation returns the same value as {@code new
+ * Double(this.doubleValue()).hashCode()}
+ *
+ * @see Double#hashCode()
+ */
+ @Override
+ public int hashCode() {
+ long v = Double.doubleToLongBits(value);
+ return (int) (v ^ (v >>> 32));
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * <p>The returned {@code String} is equal to {@code Double.toString(this.doubleValue())}
+ *
+ * @see Double#toString(double)
+ */
+ @Override
+ public String toString() {
+ return Double.toString(value);
+ }
+
+ /*
+ * Methods inspired by the Double class.
+ */
+
+ /**
+ * Returns {@code true} if {@code this} double precision number is infinite ({@link
+ * Double#POSITIVE_INFINITY} or {@link Double#NEGATIVE_INFINITY}).
+ *
+ * @return {@code true} if {@code this} number is infinite
+ */
+ public boolean isInfinite() {
+ return Double.isInfinite(value);
+ }
+
+ /**
+ * Returns {@code true} if {@code this} double precision number is Not-a-Number ({@code NaN}),
+ * false otherwise.
+ *
+ * @return {@code true} if {@code this} is {@code NaN}
+ */
+ public boolean isNaN() {
+ return Double.isNaN(value);
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @since 3.2
+ */
+ public double getReal() {
+ return value;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @since 3.2
+ */
+ public Decimal64 add(final double a) {
+ return new Decimal64(value + a);
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @since 3.2
+ */
+ public Decimal64 subtract(final double a) {
+ return new Decimal64(value - a);
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @since 3.2
+ */
+ public Decimal64 multiply(final double a) {
+ return new Decimal64(value * a);
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @since 3.2
+ */
+ public Decimal64 divide(final double a) {
+ return new Decimal64(value / a);
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @since 3.2
+ */
+ public Decimal64 remainder(final double a) {
+ return new Decimal64(FastMath.IEEEremainder(value, a));
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @since 3.2
+ */
+ public Decimal64 remainder(final Decimal64 a) {
+ return new Decimal64(FastMath.IEEEremainder(value, a.value));
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @since 3.2
+ */
+ public Decimal64 abs() {
+ return new Decimal64(FastMath.abs(value));
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @since 3.2
+ */
+ public Decimal64 ceil() {
+ return new Decimal64(FastMath.ceil(value));
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @since 3.2
+ */
+ public Decimal64 floor() {
+ return new Decimal64(FastMath.floor(value));
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @since 3.2
+ */
+ public Decimal64 rint() {
+ return new Decimal64(FastMath.rint(value));
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @since 3.2
+ */
+ public long round() {
+ return FastMath.round(value);
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @since 3.2
+ */
+ public Decimal64 signum() {
+ return new Decimal64(FastMath.signum(value));
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @since 3.2
+ */
+ public Decimal64 copySign(final Decimal64 sign) {
+ return new Decimal64(FastMath.copySign(value, sign.value));
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @since 3.2
+ */
+ public Decimal64 copySign(final double sign) {
+ return new Decimal64(FastMath.copySign(value, sign));
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @since 3.2
+ */
+ public Decimal64 scalb(final int n) {
+ return new Decimal64(FastMath.scalb(value, n));
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @since 3.2
+ */
+ public Decimal64 hypot(final Decimal64 y) {
+ return new Decimal64(FastMath.hypot(value, y.value));
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @since 3.2
+ */
+ public Decimal64 sqrt() {
+ return new Decimal64(FastMath.sqrt(value));
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @since 3.2
+ */
+ public Decimal64 cbrt() {
+ return new Decimal64(FastMath.cbrt(value));
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @since 3.2
+ */
+ public Decimal64 rootN(final int n) {
+ if (value < 0) {
+ return new Decimal64(-FastMath.pow(-value, 1.0 / n));
+ } else {
+ return new Decimal64(FastMath.pow(value, 1.0 / n));
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @since 3.2
+ */
+ public Decimal64 pow(final double p) {
+ return new Decimal64(FastMath.pow(value, p));
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @since 3.2
+ */
+ public Decimal64 pow(final int n) {
+ return new Decimal64(FastMath.pow(value, n));
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @since 3.2
+ */
+ public Decimal64 pow(final Decimal64 e) {
+ return new Decimal64(FastMath.pow(value, e.value));
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @since 3.2
+ */
+ public Decimal64 exp() {
+ return new Decimal64(FastMath.exp(value));
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @since 3.2
+ */
+ public Decimal64 expm1() {
+ return new Decimal64(FastMath.expm1(value));
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @since 3.2
+ */
+ public Decimal64 log() {
+ return new Decimal64(FastMath.log(value));
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @since 3.2
+ */
+ public Decimal64 log1p() {
+ return new Decimal64(FastMath.log1p(value));
+ }
+
+ /**
+ * Base 10 logarithm.
+ *
+ * @return base 10 logarithm of the instance
+ * @since 3.2
+ */
+ public Decimal64 log10() {
+ return new Decimal64(FastMath.log10(value));
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @since 3.2
+ */
+ public Decimal64 cos() {
+ return new Decimal64(FastMath.cos(value));
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @since 3.2
+ */
+ public Decimal64 sin() {
+ return new Decimal64(FastMath.sin(value));
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @since 3.2
+ */
+ public Decimal64 tan() {
+ return new Decimal64(FastMath.tan(value));
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @since 3.2
+ */
+ public Decimal64 acos() {
+ return new Decimal64(FastMath.acos(value));
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @since 3.2
+ */
+ public Decimal64 asin() {
+ return new Decimal64(FastMath.asin(value));
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @since 3.2
+ */
+ public Decimal64 atan() {
+ return new Decimal64(FastMath.atan(value));
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @since 3.2
+ */
+ public Decimal64 atan2(final Decimal64 x) {
+ return new Decimal64(FastMath.atan2(value, x.value));
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @since 3.2
+ */
+ public Decimal64 cosh() {
+ return new Decimal64(FastMath.cosh(value));
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @since 3.2
+ */
+ public Decimal64 sinh() {
+ return new Decimal64(FastMath.sinh(value));
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @since 3.2
+ */
+ public Decimal64 tanh() {
+ return new Decimal64(FastMath.tanh(value));
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @since 3.2
+ */
+ public Decimal64 acosh() {
+ return new Decimal64(FastMath.acosh(value));
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @since 3.2
+ */
+ public Decimal64 asinh() {
+ return new Decimal64(FastMath.asinh(value));
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @since 3.2
+ */
+ public Decimal64 atanh() {
+ return new Decimal64(FastMath.atanh(value));
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @since 3.2
+ */
+ public Decimal64 linearCombination(final Decimal64[] a, final Decimal64[] b)
+ throws DimensionMismatchException {
+ if (a.length != b.length) {
+ throw new DimensionMismatchException(a.length, b.length);
+ }
+ final double[] aDouble = new double[a.length];
+ final double[] bDouble = new double[b.length];
+ for (int i = 0; i < a.length; ++i) {
+ aDouble[i] = a[i].value;
+ bDouble[i] = b[i].value;
+ }
+ return new Decimal64(MathArrays.linearCombination(aDouble, bDouble));
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @since 3.2
+ */
+ public Decimal64 linearCombination(final double[] a, final Decimal64[] b)
+ throws DimensionMismatchException {
+ if (a.length != b.length) {
+ throw new DimensionMismatchException(a.length, b.length);
+ }
+ final double[] bDouble = new double[b.length];
+ for (int i = 0; i < a.length; ++i) {
+ bDouble[i] = b[i].value;
+ }
+ return new Decimal64(MathArrays.linearCombination(a, bDouble));
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @since 3.2
+ */
+ public Decimal64 linearCombination(
+ final Decimal64 a1, final Decimal64 b1, final Decimal64 a2, final Decimal64 b2) {
+ return new Decimal64(
+ MathArrays.linearCombination(
+ a1.value, b1.value,
+ a2.value, b2.value));
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @since 3.2
+ */
+ public Decimal64 linearCombination(
+ final double a1, final Decimal64 b1, final double a2, final Decimal64 b2) {
+ return new Decimal64(
+ MathArrays.linearCombination(
+ a1, b1.value,
+ a2, b2.value));
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @since 3.2
+ */
+ public Decimal64 linearCombination(
+ final Decimal64 a1,
+ final Decimal64 b1,
+ final Decimal64 a2,
+ final Decimal64 b2,
+ final Decimal64 a3,
+ final Decimal64 b3) {
+ return new Decimal64(
+ MathArrays.linearCombination(
+ a1.value, b1.value,
+ a2.value, b2.value,
+ a3.value, b3.value));
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @since 3.2
+ */
+ public Decimal64 linearCombination(
+ final double a1,
+ final Decimal64 b1,
+ final double a2,
+ final Decimal64 b2,
+ final double a3,
+ final Decimal64 b3) {
+ return new Decimal64(
+ MathArrays.linearCombination(
+ a1, b1.value,
+ a2, b2.value,
+ a3, b3.value));
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @since 3.2
+ */
+ public Decimal64 linearCombination(
+ final Decimal64 a1,
+ final Decimal64 b1,
+ final Decimal64 a2,
+ final Decimal64 b2,
+ final Decimal64 a3,
+ final Decimal64 b3,
+ final Decimal64 a4,
+ final Decimal64 b4) {
+ return new Decimal64(
+ MathArrays.linearCombination(
+ a1.value, b1.value,
+ a2.value, b2.value,
+ a3.value, b3.value,
+ a4.value, b4.value));
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @since 3.2
+ */
+ public Decimal64 linearCombination(
+ final double a1,
+ final Decimal64 b1,
+ final double a2,
+ final Decimal64 b2,
+ final double a3,
+ final Decimal64 b3,
+ final double a4,
+ final Decimal64 b4) {
+ return new Decimal64(
+ MathArrays.linearCombination(
+ a1, b1.value,
+ a2, b2.value,
+ a3, b3.value,
+ a4, b4.value));
+ }
+}
diff --git a/src/main/java/org/apache/commons/math3/util/Decimal64Field.java b/src/main/java/org/apache/commons/math3/util/Decimal64Field.java
new file mode 100644
index 0000000..2b07826
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/util/Decimal64Field.java
@@ -0,0 +1,61 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math3.util;
+
+import org.apache.commons.math3.Field;
+import org.apache.commons.math3.FieldElement;
+
+/**
+ * The field of double precision floating-point numbers.
+ *
+ * @since 3.1
+ * @see Decimal64
+ */
+public class Decimal64Field implements Field<Decimal64> {
+
+ /** The unique instance of this class. */
+ private static final Decimal64Field INSTANCE = new Decimal64Field();
+
+ /** Default constructor. */
+ private Decimal64Field() {
+ // Do nothing
+ }
+
+ /**
+ * Returns the unique instance of this class.
+ *
+ * @return the unique instance of this class
+ */
+ public static final Decimal64Field getInstance() {
+ return INSTANCE;
+ }
+
+ /** {@inheritDoc} */
+ public Decimal64 getZero() {
+ return Decimal64.ZERO;
+ }
+
+ /** {@inheritDoc} */
+ public Decimal64 getOne() {
+ return Decimal64.ONE;
+ }
+
+ /** {@inheritDoc} */
+ public Class<? extends FieldElement<Decimal64>> getRuntimeClass() {
+ return Decimal64.class;
+ }
+}
diff --git a/src/main/java/org/apache/commons/math3/util/DefaultTransformer.java b/src/main/java/org/apache/commons/math3/util/DefaultTransformer.java
new file mode 100644
index 0000000..ba92d1b
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/util/DefaultTransformer.java
@@ -0,0 +1,79 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math3.util;
+
+import org.apache.commons.math3.exception.MathIllegalArgumentException;
+import org.apache.commons.math3.exception.NullArgumentException;
+import org.apache.commons.math3.exception.util.LocalizedFormats;
+
+import java.io.Serializable;
+
+/**
+ * A Default NumberTransformer for java.lang.Numbers and Numeric Strings. This provides some simple
+ * conversion capabilities to turn any java.lang.Number into a primitive double or to turn a String
+ * representation of a Number into a double.
+ */
+public class DefaultTransformer implements NumberTransformer, Serializable {
+
+ /** Serializable version identifier */
+ private static final long serialVersionUID = 4019938025047800455L;
+
+ /**
+ * @param o the object that gets transformed.
+ * @return a double primitive representation of the Object o.
+ * @throws NullArgumentException if Object <code>o</code> is {@code null}.
+ * @throws MathIllegalArgumentException if Object <code>o</code> cannot successfully be
+ * transformed
+ * @see <a
+ * href="http://commons.apache.org/collections/api-release/org/apache/commons/collections/Transformer.html">Commons
+ * Collections Transformer</a>
+ */
+ public double transform(Object o) throws NullArgumentException, MathIllegalArgumentException {
+
+ if (o == null) {
+ throw new NullArgumentException(LocalizedFormats.OBJECT_TRANSFORMATION);
+ }
+
+ if (o instanceof Number) {
+ return ((Number) o).doubleValue();
+ }
+
+ try {
+ return Double.parseDouble(o.toString());
+ } catch (NumberFormatException e) {
+ throw new MathIllegalArgumentException(
+ LocalizedFormats.CANNOT_TRANSFORM_TO_DOUBLE, o.toString());
+ }
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public boolean equals(Object other) {
+ if (this == other) {
+ return true;
+ }
+ return other instanceof DefaultTransformer;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public int hashCode() {
+ // some arbitrary number ...
+ return 401993047;
+ }
+}
diff --git a/src/main/java/org/apache/commons/math3/util/DoubleArray.java b/src/main/java/org/apache/commons/math3/util/DoubleArray.java
new file mode 100644
index 0000000..f5762a9
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/util/DoubleArray.java
@@ -0,0 +1,96 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math3.util;
+
+/**
+ * Provides a standard interface for double arrays. Allows different array implementations to
+ * support various storage mechanisms such as automatic expansion, contraction, and array "rolling".
+ */
+public interface DoubleArray {
+
+ /**
+ * Returns the number of elements currently in the array. Please note that this may be different
+ * from the length of the internal storage array.
+ *
+ * @return number of elements
+ */
+ int getNumElements();
+
+ /**
+ * Returns the element at the specified index. Note that if an out of bounds index is supplied a
+ * ArrayIndexOutOfBoundsException will be thrown.
+ *
+ * @param index index to fetch a value from
+ * @return value stored at the specified index
+ * @throws ArrayIndexOutOfBoundsException if <code>index</code> is less than zero or is greater
+ * than <code>getNumElements() - 1</code>.
+ */
+ double getElement(int index);
+
+ /**
+ * Sets the element at the specified index. If the specified index is greater than <code>
+ * getNumElements() - 1</code>, the <code>numElements</code> property is increased to <code>
+ * index +1</code> and additional storage is allocated (if necessary) for the new element and
+ * all (uninitialized) elements between the new element and the previous end of the array).
+ *
+ * @param index index to store a value in
+ * @param value value to store at the specified index
+ * @throws ArrayIndexOutOfBoundsException if <code>index</code> is less than zero.
+ */
+ void setElement(int index, double value);
+
+ /**
+ * Adds an element to the end of this expandable array
+ *
+ * @param value to be added to end of array
+ */
+ void addElement(double value);
+
+ /**
+ * Adds elements to the end of this expandable array
+ *
+ * @param values to be added to end of array
+ */
+ void addElements(double[] values);
+
+ /**
+ * Adds an element to the end of the array and removes the first element in the array. Returns
+ * the discarded first element. The effect is similar to a push operation in a FIFO queue.
+ *
+ * <p>Example: If the array contains the elements 1, 2, 3, 4 (in that order) and
+ * addElementRolling(5) is invoked, the result is an array containing the entries 2, 3, 4, 5 and
+ * the value returned is 1.
+ *
+ * @param value the value to be added to the array
+ * @return the value which has been discarded or "pushed" out of the array by this rolling
+ * insert
+ */
+ double addElementRolling(double value);
+
+ /**
+ * Returns a double[] array containing the elements of this <code>DoubleArray</code>. If the
+ * underlying implementation is array-based, this method should always return a copy, rather
+ * than a reference to the underlying array so that changes made to the returned array have no
+ * effect on the <code>DoubleArray.</code>
+ *
+ * @return all elements added to the array
+ */
+ double[] getElements();
+
+ /** Clear the double array */
+ void clear();
+}
diff --git a/src/main/java/org/apache/commons/math3/util/FastMath.java b/src/main/java/org/apache/commons/math3/util/FastMath.java
new file mode 100644
index 0000000..f40961e
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/util/FastMath.java
@@ -0,0 +1,4508 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math3.util;
+
+import org.apache.commons.math3.exception.MathArithmeticException;
+import org.apache.commons.math3.exception.util.LocalizedFormats;
+
+import java.io.PrintStream;
+
+/**
+ * Faster, more accurate, portable alternative to {@link Math} and {@link StrictMath} for large
+ * scale computation.
+ *
+ * <p>FastMath is a drop-in replacement for both Math and StrictMath. This means that for any method
+ * in Math (say {@code Math.sin(x)} or {@code Math.cbrt(y)}), user can directly change the class and
+ * use the methods as is (using {@code FastMath.sin(x)} or {@code FastMath.cbrt(y)} in the previous
+ * example).
+ *
+ * <p>FastMath speed is achieved by relying heavily on optimizing compilers to native code present
+ * in many JVMs today and use of large tables. The larger tables are lazily initialised on first
+ * use, so that the setup time does not penalise methods that don't need them.
+ *
+ * <p>Note that FastMath is extensively used inside Apache Commons Math, so by calling some
+ * algorithms, the overhead when the the tables need to be intialised will occur regardless of the
+ * end-user calling FastMath methods directly or not. Performance figures for a specific JVM and
+ * hardware can be evaluated by running the FastMathTestPerformance tests in the test directory of
+ * the source distribution.
+ *
+ * <p>FastMath accuracy should be mostly independent of the JVM as it relies only on IEEE-754 basic
+ * operations and on embedded tables. Almost all operations are accurate to about 0.5 ulp throughout
+ * the domain range. This statement, of course is only a rough global observed behavior, it is
+ * <em>not</em> a guarantee for <em>every</em> double numbers input (see William Kahan's <a
+ * href="http://en.wikipedia.org/wiki/Rounding#The_table-maker.27s_dilemma">Table Maker's
+ * Dilemma</a>).
+ *
+ * <p>FastMath additionally implements the following methods not found in Math/StrictMath:
+ *
+ * <ul>
+ * <li>{@link #asinh(double)}
+ * <li>{@link #acosh(double)}
+ * <li>{@link #atanh(double)}
+ * </ul>
+ *
+ * The following methods are found in Math/StrictMath since 1.6 only, they are provided by FastMath
+ * even in 1.5 Java virtual machines
+ *
+ * <ul>
+ * <li>{@link #copySign(double, double)}
+ * <li>{@link #getExponent(double)}
+ * <li>{@link #nextAfter(double,double)}
+ * <li>{@link #nextUp(double)}
+ * <li>{@link #scalb(double, int)}
+ * <li>{@link #copySign(float, float)}
+ * <li>{@link #getExponent(float)}
+ * <li>{@link #nextAfter(float,double)}
+ * <li>{@link #nextUp(float)}
+ * <li>{@link #scalb(float, int)}
+ * </ul>
+ *
+ * @since 2.2
+ */
+public class FastMath {
+ /** Archimede's constant PI, ratio of circle circumference to diameter. */
+ public static final double PI = 105414357.0 / 33554432.0 + 1.984187159361080883e-9;
+
+ /** Napier's constant e, base of the natural logarithm. */
+ public static final double E = 2850325.0 / 1048576.0 + 8.254840070411028747e-8;
+
+ /** Index of exp(0) in the array of integer exponentials. */
+ static final int EXP_INT_TABLE_MAX_INDEX = 750;
+
+ /** Length of the array of integer exponentials. */
+ static final int EXP_INT_TABLE_LEN = EXP_INT_TABLE_MAX_INDEX * 2;
+
+ /** Logarithm table length. */
+ static final int LN_MANT_LEN = 1024;
+
+ /** Exponential fractions table length. */
+ static final int EXP_FRAC_TABLE_LEN = 1025; // 0, 1/1024, ... 1024/1024
+
+ /** StrictMath.log(Double.MAX_VALUE): {@value} */
+ private static final double LOG_MAX_VALUE = StrictMath.log(Double.MAX_VALUE);
+
+ /**
+ * Indicator for tables initialization.
+ *
+ * <p>This compile-time constant should be set to true only if one explicitly wants to compute
+ * the tables at class loading time instead of using the already computed ones provided as
+ * literal arrays below.
+ */
+ private static final boolean RECOMPUTE_TABLES_AT_RUNTIME = false;
+
+ /** log(2) (high bits). */
+ private static final double LN_2_A = 0.693147063255310059;
+
+ /** log(2) (low bits). */
+ private static final double LN_2_B = 1.17304635250823482e-7;
+
+ /** Coefficients for log, when input 0.99 < x < 1.01. */
+ private static final double LN_QUICK_COEF[][] = {
+ {1.0, 5.669184079525E-24},
+ {-0.25, -0.25},
+ {0.3333333134651184, 1.986821492305628E-8},
+ {-0.25, -6.663542893624021E-14},
+ {0.19999998807907104, 1.1921056801463227E-8},
+ {-0.1666666567325592, -7.800414592973399E-9},
+ {0.1428571343421936, 5.650007086920087E-9},
+ {-0.12502530217170715, -7.44321345601866E-11},
+ {0.11113807559013367, 9.219544613762692E-9},
+ };
+
+ /** Coefficients for log in the range of 1.0 < x < 1.0 + 2^-10. */
+ private static final double LN_HI_PREC_COEF[][] = {
+ {1.0, -6.032174644509064E-23},
+ {-0.25, -0.25},
+ {0.3333333134651184, 1.9868161777724352E-8},
+ {-0.2499999701976776, -2.957007209750105E-8},
+ {0.19999954104423523, 1.5830993332061267E-10},
+ {-0.16624879837036133, -2.6033824355191673E-8}
+ };
+
+ /** Sine, Cosine, Tangent tables are for 0, 1/8, 2/8, ... 13/8 = PI/2 approx. */
+ private static final int SINE_TABLE_LEN = 14;
+
+ /** Sine table (high bits). */
+ private static final double SINE_TABLE_A[] = {
+ +0.0d,
+ +0.1246747374534607d,
+ +0.24740394949913025d,
+ +0.366272509098053d,
+ +0.4794255495071411d,
+ +0.5850973129272461d,
+ +0.6816387176513672d,
+ +0.7675435543060303d,
+ +0.8414709568023682d,
+ +0.902267575263977d,
+ +0.9489846229553223d,
+ +0.9808930158615112d,
+ +0.9974949359893799d,
+ +0.9985313415527344d,
+ };
+
+ /** Sine table (low bits). */
+ private static final double SINE_TABLE_B[] = {
+ +0.0d,
+ -4.068233003401932E-9d,
+ +9.755392680573412E-9d,
+ +1.9987994582857286E-8d,
+ -1.0902938113007961E-8d,
+ -3.9986783938944604E-8d,
+ +4.23719669792332E-8d,
+ -5.207000323380292E-8d,
+ +2.800552834259E-8d,
+ +1.883511811213715E-8d,
+ -3.5997360512765566E-9d,
+ +4.116164446561962E-8d,
+ +5.0614674548127384E-8d,
+ -1.0129027912496858E-9d,
+ };
+
+ /** Cosine table (high bits). */
+ private static final double COSINE_TABLE_A[] = {
+ +1.0d,
+ +0.9921976327896118d,
+ +0.9689123630523682d,
+ +0.9305076599121094d,
+ +0.8775825500488281d,
+ +0.8109631538391113d,
+ +0.7316888570785522d,
+ +0.6409968137741089d,
+ +0.5403022766113281d,
+ +0.4311765432357788d,
+ +0.3153223395347595d,
+ +0.19454771280288696d,
+ +0.07073719799518585d,
+ -0.05417713522911072d,
+ };
+
+ /** Cosine table (low bits). */
+ private static final double COSINE_TABLE_B[] = {
+ +0.0d,
+ +3.4439717236742845E-8d,
+ +5.865827662008209E-8d,
+ -3.7999795083850525E-8d,
+ +1.184154459111628E-8d,
+ -3.43338934259355E-8d,
+ +1.1795268640216787E-8d,
+ +4.438921624363781E-8d,
+ +2.925681159240093E-8d,
+ -2.6437112632041807E-8d,
+ +2.2860509143963117E-8d,
+ -4.813899778443457E-9d,
+ +3.6725170580355583E-9d,
+ +2.0217439756338078E-10d,
+ };
+
+ /** Tangent table, used by atan() (high bits). */
+ private static final double TANGENT_TABLE_A[] = {
+ +0.0d,
+ +0.1256551444530487d,
+ +0.25534194707870483d,
+ +0.3936265707015991d,
+ +0.5463024377822876d,
+ +0.7214844226837158d,
+ +0.9315965175628662d,
+ +1.1974215507507324d,
+ +1.5574076175689697d,
+ +2.092571258544922d,
+ +3.0095696449279785d,
+ +5.041914939880371d,
+ +14.101419448852539d,
+ -18.430862426757812d,
+ };
+
+ /** Tangent table, used by atan() (low bits). */
+ private static final double TANGENT_TABLE_B[] = {
+ +0.0d,
+ -7.877917738262007E-9d,
+ -2.5857668567479893E-8d,
+ +5.2240336371356666E-9d,
+ +5.206150291559893E-8d,
+ +1.8307188599677033E-8d,
+ -5.7618793749770706E-8d,
+ +7.848361555046424E-8d,
+ +1.0708593250394448E-7d,
+ +1.7827257129423813E-8d,
+ +2.893485277253286E-8d,
+ +3.1660099222737955E-7d,
+ +4.983191803254889E-7d,
+ -3.356118100840571E-7d,
+ };
+
+ /** Bits of 1/(2*pi), need for reducePayneHanek(). */
+ private static final long RECIP_2PI[] =
+ new long[] {
+ (0x28be60dbL << 32) | 0x9391054aL,
+ (0x7f09d5f4L << 32) | 0x7d4d3770L,
+ (0x36d8a566L << 32) | 0x4f10e410L,
+ (0x7f9458eaL << 32) | 0xf7aef158L,
+ (0x6dc91b8eL << 32) | 0x909374b8L,
+ (0x01924bbaL << 32) | 0x82746487L,
+ (0x3f877ac7L << 32) | 0x2c4a69cfL,
+ (0xba208d7dL << 32) | 0x4baed121L,
+ (0x3a671c09L << 32) | 0xad17df90L,
+ (0x4e64758eL << 32) | 0x60d4ce7dL,
+ (0x272117e2L << 32) | 0xef7e4a0eL,
+ (0xc7fe25ffL << 32) | 0xf7816603L,
+ (0xfbcbc462L << 32) | 0xd6829b47L,
+ (0xdb4d9fb3L << 32) | 0xc9f2c26dL,
+ (0xd3d18fd9L << 32) | 0xa797fa8bL,
+ (0x5d49eeb1L << 32) | 0xfaf97c5eL,
+ (0xcf41ce7dL << 32) | 0xe294a4baL,
+ 0x9afed7ecL << 32
+ };
+
+ /** Bits of pi/4, need for reducePayneHanek(). */
+ private static final long PI_O_4_BITS[] =
+ new long[] {(0xc90fdaa2L << 32) | 0x2168c234L, (0xc4c6628bL << 32) | 0x80dc1cd1L};
+
+ /**
+ * Eighths. This is used by sinQ, because its faster to do a table lookup than a multiply in
+ * this time-critical routine
+ */
+ private static final double EIGHTHS[] = {
+ 0, 0.125, 0.25, 0.375, 0.5, 0.625, 0.75, 0.875, 1.0, 1.125, 1.25, 1.375, 1.5, 1.625
+ };
+
+ /** Table of 2^((n+2)/3) */
+ private static final double CBRTTWO[] = {
+ 0.6299605249474366, 0.7937005259840998, 1.0, 1.2599210498948732, 1.5874010519681994
+ };
+
+ /*
+ * There are 52 bits in the mantissa of a double.
+ * For additional precision, the code splits double numbers into two parts,
+ * by clearing the low order 30 bits if possible, and then performs the arithmetic
+ * on each half separately.
+ */
+
+ /**
+ * 0x40000000 - used to split a double into two parts, both with the low order bits cleared.
+ * Equivalent to 2^30.
+ */
+ private static final long HEX_40000000 = 0x40000000L; // 1073741824L
+
+ /** Mask used to clear low order 30 bits */
+ private static final long MASK_30BITS = -1L - (HEX_40000000 - 1); // 0xFFFFFFFFC0000000L;
+
+ /** Mask used to clear the non-sign part of an int. */
+ private static final int MASK_NON_SIGN_INT = 0x7fffffff;
+
+ /** Mask used to clear the non-sign part of a long. */
+ private static final long MASK_NON_SIGN_LONG = 0x7fffffffffffffffl;
+
+ /** Mask used to extract exponent from double bits. */
+ private static final long MASK_DOUBLE_EXPONENT = 0x7ff0000000000000L;
+
+ /** Mask used to extract mantissa from double bits. */
+ private static final long MASK_DOUBLE_MANTISSA = 0x000fffffffffffffL;
+
+ /** Mask used to add implicit high order bit for normalized double. */
+ private static final long IMPLICIT_HIGH_BIT = 0x0010000000000000L;
+
+ /** 2^52 - double numbers this large must be integral (no fraction) or NaN or Infinite */
+ private static final double TWO_POWER_52 = 4503599627370496.0;
+
+ /** Constant: {@value}. */
+ private static final double F_1_3 = 1d / 3d;
+
+ /** Constant: {@value}. */
+ private static final double F_1_5 = 1d / 5d;
+
+ /** Constant: {@value}. */
+ private static final double F_1_7 = 1d / 7d;
+
+ /** Constant: {@value}. */
+ private static final double F_1_9 = 1d / 9d;
+
+ /** Constant: {@value}. */
+ private static final double F_1_11 = 1d / 11d;
+
+ /** Constant: {@value}. */
+ private static final double F_1_13 = 1d / 13d;
+
+ /** Constant: {@value}. */
+ private static final double F_1_15 = 1d / 15d;
+
+ /** Constant: {@value}. */
+ private static final double F_1_17 = 1d / 17d;
+
+ /** Constant: {@value}. */
+ private static final double F_3_4 = 3d / 4d;
+
+ /** Constant: {@value}. */
+ private static final double F_15_16 = 15d / 16d;
+
+ /** Constant: {@value}. */
+ private static final double F_13_14 = 13d / 14d;
+
+ /** Constant: {@value}. */
+ private static final double F_11_12 = 11d / 12d;
+
+ /** Constant: {@value}. */
+ private static final double F_9_10 = 9d / 10d;
+
+ /** Constant: {@value}. */
+ private static final double F_7_8 = 7d / 8d;
+
+ /** Constant: {@value}. */
+ private static final double F_5_6 = 5d / 6d;
+
+ /** Constant: {@value}. */
+ private static final double F_1_2 = 1d / 2d;
+
+ /** Constant: {@value}. */
+ private static final double F_1_4 = 1d / 4d;
+
+ /** Private Constructor */
+ private FastMath() {}
+
+ // Generic helper methods
+
+ /**
+ * Get the high order bits from the mantissa. Equivalent to adding and subtracting HEX_40000 but
+ * also works for very large numbers
+ *
+ * @param d the value to split
+ * @return the high order part of the mantissa
+ */
+ private static double doubleHighPart(double d) {
+ if (d > -Precision.SAFE_MIN && d < Precision.SAFE_MIN) {
+ return d; // These are un-normalised - don't try to convert
+ }
+ long xl =
+ Double.doubleToRawLongBits(
+ d); // can take raw bits because just gonna convert it back
+ xl &= MASK_30BITS; // Drop low order bits
+ return Double.longBitsToDouble(xl);
+ }
+
+ /**
+ * Compute the square root of a number.
+ *
+ * <p><b>Note:</b> this implementation currently delegates to {@link Math#sqrt}
+ *
+ * @param a number on which evaluation is done
+ * @return square root of a
+ */
+ public static double sqrt(final double a) {
+ return Math.sqrt(a);
+ }
+
+ /**
+ * Compute the hyperbolic cosine of a number.
+ *
+ * @param x number on which evaluation is done
+ * @return hyperbolic cosine of x
+ */
+ public static double cosh(double x) {
+ if (x != x) {
+ return x;
+ }
+
+ // cosh[z] = (exp(z) + exp(-z))/2
+
+ // for numbers with magnitude 20 or so,
+ // exp(-z) can be ignored in comparison with exp(z)
+
+ if (x > 20) {
+ if (x >= LOG_MAX_VALUE) {
+ // Avoid overflow (MATH-905).
+ final double t = exp(0.5 * x);
+ return (0.5 * t) * t;
+ } else {
+ return 0.5 * exp(x);
+ }
+ } else if (x < -20) {
+ if (x <= -LOG_MAX_VALUE) {
+ // Avoid overflow (MATH-905).
+ final double t = exp(-0.5 * x);
+ return (0.5 * t) * t;
+ } else {
+ return 0.5 * exp(-x);
+ }
+ }
+
+ final double hiPrec[] = new double[2];
+ if (x < 0.0) {
+ x = -x;
+ }
+ exp(x, 0.0, hiPrec);
+
+ double ya = hiPrec[0] + hiPrec[1];
+ double yb = -(ya - hiPrec[0] - hiPrec[1]);
+
+ double temp = ya * HEX_40000000;
+ double yaa = ya + temp - temp;
+ double yab = ya - yaa;
+
+ // recip = 1/y
+ double recip = 1.0 / ya;
+ temp = recip * HEX_40000000;
+ double recipa = recip + temp - temp;
+ double recipb = recip - recipa;
+
+ // Correct for rounding in division
+ recipb += (1.0 - yaa * recipa - yaa * recipb - yab * recipa - yab * recipb) * recip;
+ // Account for yb
+ recipb += -yb * recip * recip;
+
+ // y = y + 1/y
+ temp = ya + recipa;
+ yb += -(temp - ya - recipa);
+ ya = temp;
+ temp = ya + recipb;
+ yb += -(temp - ya - recipb);
+ ya = temp;
+
+ double result = ya + yb;
+ result *= 0.5;
+ return result;
+ }
+
+ /**
+ * Compute the hyperbolic sine of a number.
+ *
+ * @param x number on which evaluation is done
+ * @return hyperbolic sine of x
+ */
+ public static double sinh(double x) {
+ boolean negate = false;
+ if (x != x) {
+ return x;
+ }
+
+ // sinh[z] = (exp(z) - exp(-z) / 2
+
+ // for values of z larger than about 20,
+ // exp(-z) can be ignored in comparison with exp(z)
+
+ if (x > 20) {
+ if (x >= LOG_MAX_VALUE) {
+ // Avoid overflow (MATH-905).
+ final double t = exp(0.5 * x);
+ return (0.5 * t) * t;
+ } else {
+ return 0.5 * exp(x);
+ }
+ } else if (x < -20) {
+ if (x <= -LOG_MAX_VALUE) {
+ // Avoid overflow (MATH-905).
+ final double t = exp(-0.5 * x);
+ return (-0.5 * t) * t;
+ } else {
+ return -0.5 * exp(-x);
+ }
+ }
+
+ if (x == 0) {
+ return x;
+ }
+
+ if (x < 0.0) {
+ x = -x;
+ negate = true;
+ }
+
+ double result;
+
+ if (x > 0.25) {
+ double hiPrec[] = new double[2];
+ exp(x, 0.0, hiPrec);
+
+ double ya = hiPrec[0] + hiPrec[1];
+ double yb = -(ya - hiPrec[0] - hiPrec[1]);
+
+ double temp = ya * HEX_40000000;
+ double yaa = ya + temp - temp;
+ double yab = ya - yaa;
+
+ // recip = 1/y
+ double recip = 1.0 / ya;
+ temp = recip * HEX_40000000;
+ double recipa = recip + temp - temp;
+ double recipb = recip - recipa;
+
+ // Correct for rounding in division
+ recipb += (1.0 - yaa * recipa - yaa * recipb - yab * recipa - yab * recipb) * recip;
+ // Account for yb
+ recipb += -yb * recip * recip;
+
+ recipa = -recipa;
+ recipb = -recipb;
+
+ // y = y + 1/y
+ temp = ya + recipa;
+ yb += -(temp - ya - recipa);
+ ya = temp;
+ temp = ya + recipb;
+ yb += -(temp - ya - recipb);
+ ya = temp;
+
+ result = ya + yb;
+ result *= 0.5;
+ } else {
+ double hiPrec[] = new double[2];
+ expm1(x, hiPrec);
+
+ double ya = hiPrec[0] + hiPrec[1];
+ double yb = -(ya - hiPrec[0] - hiPrec[1]);
+
+ /* Compute expm1(-x) = -expm1(x) / (expm1(x) + 1) */
+ double denom = 1.0 + ya;
+ double denomr = 1.0 / denom;
+ double denomb = -(denom - 1.0 - ya) + yb;
+ double ratio = ya * denomr;
+ double temp = ratio * HEX_40000000;
+ double ra = ratio + temp - temp;
+ double rb = ratio - ra;
+
+ temp = denom * HEX_40000000;
+ double za = denom + temp - temp;
+ double zb = denom - za;
+
+ rb += (ya - za * ra - za * rb - zb * ra - zb * rb) * denomr;
+
+ // Adjust for yb
+ rb += yb * denomr; // numerator
+ rb += -ya * denomb * denomr * denomr; // denominator
+
+ // y = y - 1/y
+ temp = ya + ra;
+ yb += -(temp - ya - ra);
+ ya = temp;
+ temp = ya + rb;
+ yb += -(temp - ya - rb);
+ ya = temp;
+
+ result = ya + yb;
+ result *= 0.5;
+ }
+
+ if (negate) {
+ result = -result;
+ }
+
+ return result;
+ }
+
+ /**
+ * Compute the hyperbolic tangent of a number.
+ *
+ * @param x number on which evaluation is done
+ * @return hyperbolic tangent of x
+ */
+ public static double tanh(double x) {
+ boolean negate = false;
+
+ if (x != x) {
+ return x;
+ }
+
+ // tanh[z] = sinh[z] / cosh[z]
+ // = (exp(z) - exp(-z)) / (exp(z) + exp(-z))
+ // = (exp(2x) - 1) / (exp(2x) + 1)
+
+ // for magnitude > 20, sinh[z] == cosh[z] in double precision
+
+ if (x > 20.0) {
+ return 1.0;
+ }
+
+ if (x < -20) {
+ return -1.0;
+ }
+
+ if (x == 0) {
+ return x;
+ }
+
+ if (x < 0.0) {
+ x = -x;
+ negate = true;
+ }
+
+ double result;
+ if (x >= 0.5) {
+ double hiPrec[] = new double[2];
+ // tanh(x) = (exp(2x) - 1) / (exp(2x) + 1)
+ exp(x * 2.0, 0.0, hiPrec);
+
+ double ya = hiPrec[0] + hiPrec[1];
+ double yb = -(ya - hiPrec[0] - hiPrec[1]);
+
+ /* Numerator */
+ double na = -1.0 + ya;
+ double nb = -(na + 1.0 - ya);
+ double temp = na + yb;
+ nb += -(temp - na - yb);
+ na = temp;
+
+ /* Denominator */
+ double da = 1.0 + ya;
+ double db = -(da - 1.0 - ya);
+ temp = da + yb;
+ db += -(temp - da - yb);
+ da = temp;
+
+ temp = da * HEX_40000000;
+ double daa = da + temp - temp;
+ double dab = da - daa;
+
+ // ratio = na/da
+ double ratio = na / da;
+ temp = ratio * HEX_40000000;
+ double ratioa = ratio + temp - temp;
+ double ratiob = ratio - ratioa;
+
+ // Correct for rounding in division
+ ratiob += (na - daa * ratioa - daa * ratiob - dab * ratioa - dab * ratiob) / da;
+
+ // Account for nb
+ ratiob += nb / da;
+ // Account for db
+ ratiob += -db * na / da / da;
+
+ result = ratioa + ratiob;
+ } else {
+ double hiPrec[] = new double[2];
+ // tanh(x) = expm1(2x) / (expm1(2x) + 2)
+ expm1(x * 2.0, hiPrec);
+
+ double ya = hiPrec[0] + hiPrec[1];
+ double yb = -(ya - hiPrec[0] - hiPrec[1]);
+
+ /* Numerator */
+ double na = ya;
+ double nb = yb;
+
+ /* Denominator */
+ double da = 2.0 + ya;
+ double db = -(da - 2.0 - ya);
+ double temp = da + yb;
+ db += -(temp - da - yb);
+ da = temp;
+
+ temp = da * HEX_40000000;
+ double daa = da + temp - temp;
+ double dab = da - daa;
+
+ // ratio = na/da
+ double ratio = na / da;
+ temp = ratio * HEX_40000000;
+ double ratioa = ratio + temp - temp;
+ double ratiob = ratio - ratioa;
+
+ // Correct for rounding in division
+ ratiob += (na - daa * ratioa - daa * ratiob - dab * ratioa - dab * ratiob) / da;
+
+ // Account for nb
+ ratiob += nb / da;
+ // Account for db
+ ratiob += -db * na / da / da;
+
+ result = ratioa + ratiob;
+ }
+
+ if (negate) {
+ result = -result;
+ }
+
+ return result;
+ }
+
+ /**
+ * Compute the inverse hyperbolic cosine of a number.
+ *
+ * @param a number on which evaluation is done
+ * @return inverse hyperbolic cosine of a
+ */
+ public static double acosh(final double a) {
+ return FastMath.log(a + FastMath.sqrt(a * a - 1));
+ }
+
+ /**
+ * Compute the inverse hyperbolic sine of a number.
+ *
+ * @param a number on which evaluation is done
+ * @return inverse hyperbolic sine of a
+ */
+ public static double asinh(double a) {
+ boolean negative = false;
+ if (a < 0) {
+ negative = true;
+ a = -a;
+ }
+
+ double absAsinh;
+ if (a > 0.167) {
+ absAsinh = FastMath.log(FastMath.sqrt(a * a + 1) + a);
+ } else {
+ final double a2 = a * a;
+ if (a > 0.097) {
+ absAsinh =
+ a
+ * (1
+ - a2
+ * (F_1_3
+ - a2
+ * (F_1_5
+ - a2
+ * (F_1_7
+ - a2
+ * (F_1_9
+ - a2
+ * (F_1_11
+ - a2
+ * (F_1_13
+ - a2
+ * (F_1_15
+ - a2
+ * F_1_17
+ * F_15_16)
+ * F_13_14)
+ * F_11_12)
+ * F_9_10)
+ * F_7_8)
+ * F_5_6)
+ * F_3_4)
+ * F_1_2);
+ } else if (a > 0.036) {
+ absAsinh =
+ a
+ * (1
+ - a2
+ * (F_1_3
+ - a2
+ * (F_1_5
+ - a2
+ * (F_1_7
+ - a2
+ * (F_1_9
+ - a2
+ * (F_1_11
+ - a2
+ * F_1_13
+ * F_11_12)
+ * F_9_10)
+ * F_7_8)
+ * F_5_6)
+ * F_3_4)
+ * F_1_2);
+ } else if (a > 0.0036) {
+ absAsinh =
+ a
+ * (1
+ - a2
+ * (F_1_3
+ - a2
+ * (F_1_5
+ - a2
+ * (F_1_7
+ - a2 * F_1_9
+ * F_7_8)
+ * F_5_6)
+ * F_3_4)
+ * F_1_2);
+ } else {
+ absAsinh = a * (1 - a2 * (F_1_3 - a2 * F_1_5 * F_3_4) * F_1_2);
+ }
+ }
+
+ return negative ? -absAsinh : absAsinh;
+ }
+
+ /**
+ * Compute the inverse hyperbolic tangent of a number.
+ *
+ * @param a number on which evaluation is done
+ * @return inverse hyperbolic tangent of a
+ */
+ public static double atanh(double a) {
+ boolean negative = false;
+ if (a < 0) {
+ negative = true;
+ a = -a;
+ }
+
+ double absAtanh;
+ if (a > 0.15) {
+ absAtanh = 0.5 * FastMath.log((1 + a) / (1 - a));
+ } else {
+ final double a2 = a * a;
+ if (a > 0.087) {
+ absAtanh =
+ a
+ * (1
+ + a2
+ * (F_1_3
+ + a2
+ * (F_1_5
+ + a2
+ * (F_1_7
+ + a2
+ * (F_1_9
+ + a2
+ * (F_1_11
+ + a2
+ * (F_1_13
+ + a2
+ * (F_1_15
+ + a2
+ * F_1_17))))))));
+ } else if (a > 0.031) {
+ absAtanh =
+ a
+ * (1
+ + a2
+ * (F_1_3
+ + a2
+ * (F_1_5
+ + a2
+ * (F_1_7
+ + a2
+ * (F_1_9
+ + a2
+ * (F_1_11
+ + a2
+ * F_1_13))))));
+ } else if (a > 0.003) {
+ absAtanh = a * (1 + a2 * (F_1_3 + a2 * (F_1_5 + a2 * (F_1_7 + a2 * F_1_9))));
+ } else {
+ absAtanh = a * (1 + a2 * (F_1_3 + a2 * F_1_5));
+ }
+ }
+
+ return negative ? -absAtanh : absAtanh;
+ }
+
+ /**
+ * Compute the signum of a number. The signum is -1 for negative numbers, +1 for positive
+ * numbers and 0 otherwise
+ *
+ * @param a number on which evaluation is done
+ * @return -1.0, -0.0, +0.0, +1.0 or NaN depending on sign of a
+ */
+ public static double signum(final double a) {
+ return (a < 0.0) ? -1.0 : ((a > 0.0) ? 1.0 : a); // return +0.0/-0.0/NaN depending on a
+ }
+
+ /**
+ * Compute the signum of a number. The signum is -1 for negative numbers, +1 for positive
+ * numbers and 0 otherwise
+ *
+ * @param a number on which evaluation is done
+ * @return -1.0, -0.0, +0.0, +1.0 or NaN depending on sign of a
+ */
+ public static float signum(final float a) {
+ return (a < 0.0f) ? -1.0f : ((a > 0.0f) ? 1.0f : a); // return +0.0/-0.0/NaN depending on a
+ }
+
+ /**
+ * Compute next number towards positive infinity.
+ *
+ * @param a number to which neighbor should be computed
+ * @return neighbor of a towards positive infinity
+ */
+ public static double nextUp(final double a) {
+ return nextAfter(a, Double.POSITIVE_INFINITY);
+ }
+
+ /**
+ * Compute next number towards positive infinity.
+ *
+ * @param a number to which neighbor should be computed
+ * @return neighbor of a towards positive infinity
+ */
+ public static float nextUp(final float a) {
+ return nextAfter(a, Float.POSITIVE_INFINITY);
+ }
+
+ /**
+ * Compute next number towards negative infinity.
+ *
+ * @param a number to which neighbor should be computed
+ * @return neighbor of a towards negative infinity
+ * @since 3.4
+ */
+ public static double nextDown(final double a) {
+ return nextAfter(a, Double.NEGATIVE_INFINITY);
+ }
+
+ /**
+ * Compute next number towards negative infinity.
+ *
+ * @param a number to which neighbor should be computed
+ * @return neighbor of a towards negative infinity
+ * @since 3.4
+ */
+ public static float nextDown(final float a) {
+ return nextAfter(a, Float.NEGATIVE_INFINITY);
+ }
+
+ /**
+ * Returns a pseudo-random number between 0.0 and 1.0.
+ *
+ * <p><b>Note:</b> this implementation currently delegates to {@link Math#random}
+ *
+ * @return a random number between 0.0 and 1.0
+ */
+ public static double random() {
+ return Math.random();
+ }
+
+ /**
+ * Exponential function.
+ *
+ * <p>Computes exp(x), function result is nearly rounded. It will be correctly rounded to the
+ * theoretical value for 99.9% of input values, otherwise it will have a 1 ULP error.
+ *
+ * <p>Method: Lookup intVal = exp(int(x)) Lookup fracVal = exp(int(x-int(x) / 1024.0) * 1024.0
+ * ); Compute z as the exponential of the remaining bits by a polynomial minus one exp(x) =
+ * intVal * fracVal * (1 + z)
+ *
+ * <p>Accuracy: Calculation is done with 63 bits of precision, so result should be correctly
+ * rounded for 99.9% of input values, with less than 1 ULP error otherwise.
+ *
+ * @param x a double
+ * @return double e<sup>x</sup>
+ */
+ public static double exp(double x) {
+ return exp(x, 0.0, null);
+ }
+
+ /**
+ * Internal helper method for exponential function.
+ *
+ * @param x original argument of the exponential function
+ * @param extra extra bits of precision on input (To Be Confirmed)
+ * @param hiPrec extra bits of precision on output (To Be Confirmed)
+ * @return exp(x)
+ */
+ private static double exp(double x, double extra, double[] hiPrec) {
+ double intPartA;
+ double intPartB;
+ int intVal = (int) x;
+
+ /* Lookup exp(floor(x)).
+ * intPartA will have the upper 22 bits, intPartB will have the lower
+ * 52 bits.
+ */
+ if (x < 0.0) {
+
+ // We don't check against intVal here as conversion of large negative double values
+ // may be affected by a JIT bug. Subsequent comparisons can safely use intVal
+ if (x < -746d) {
+ if (hiPrec != null) {
+ hiPrec[0] = 0.0;
+ hiPrec[1] = 0.0;
+ }
+ return 0.0;
+ }
+
+ if (intVal < -709) {
+ /* This will produce a subnormal output */
+ final double result = exp(x + 40.19140625, extra, hiPrec) / 285040095144011776.0;
+ if (hiPrec != null) {
+ hiPrec[0] /= 285040095144011776.0;
+ hiPrec[1] /= 285040095144011776.0;
+ }
+ return result;
+ }
+
+ if (intVal == -709) {
+ /* exp(1.494140625) is nearly a machine number... */
+ final double result = exp(x + 1.494140625, extra, hiPrec) / 4.455505956692756620;
+ if (hiPrec != null) {
+ hiPrec[0] /= 4.455505956692756620;
+ hiPrec[1] /= 4.455505956692756620;
+ }
+ return result;
+ }
+
+ intVal--;
+
+ } else {
+ if (intVal > 709) {
+ if (hiPrec != null) {
+ hiPrec[0] = Double.POSITIVE_INFINITY;
+ hiPrec[1] = 0.0;
+ }
+ return Double.POSITIVE_INFINITY;
+ }
+ }
+
+ intPartA = ExpIntTable.EXP_INT_TABLE_A[EXP_INT_TABLE_MAX_INDEX + intVal];
+ intPartB = ExpIntTable.EXP_INT_TABLE_B[EXP_INT_TABLE_MAX_INDEX + intVal];
+
+ /* Get the fractional part of x, find the greatest multiple of 2^-10 less than
+ * x and look up the exp function of it.
+ * fracPartA will have the upper 22 bits, fracPartB the lower 52 bits.
+ */
+ final int intFrac = (int) ((x - intVal) * 1024.0);
+ final double fracPartA = ExpFracTable.EXP_FRAC_TABLE_A[intFrac];
+ final double fracPartB = ExpFracTable.EXP_FRAC_TABLE_B[intFrac];
+
+ /* epsilon is the difference in x from the nearest multiple of 2^-10. It
+ * has a value in the range 0 <= epsilon < 2^-10.
+ * Do the subtraction from x as the last step to avoid possible loss of precision.
+ */
+ final double epsilon = x - (intVal + intFrac / 1024.0);
+
+ /* Compute z = exp(epsilon) - 1.0 via a minimax polynomial. z has
+ full double precision (52 bits). Since z < 2^-10, we will have
+ 62 bits of precision when combined with the constant 1. This will be
+ used in the last addition below to get proper rounding. */
+
+ /* Remez generated polynomial. Converges on the interval [0, 2^-10], error
+ is less than 0.5 ULP */
+ double z = 0.04168701738764507;
+ z = z * epsilon + 0.1666666505023083;
+ z = z * epsilon + 0.5000000000042687;
+ z = z * epsilon + 1.0;
+ z = z * epsilon + -3.940510424527919E-20;
+
+ /* Compute (intPartA+intPartB) * (fracPartA+fracPartB) by binomial
+ expansion.
+ tempA is exact since intPartA and intPartB only have 22 bits each.
+ tempB will have 52 bits of precision.
+ */
+ double tempA = intPartA * fracPartA;
+ double tempB = intPartA * fracPartB + intPartB * fracPartA + intPartB * fracPartB;
+
+ /* Compute the result. (1+z)(tempA+tempB). Order of operations is
+ important. For accuracy add by increasing size. tempA is exact and
+ much larger than the others. If there are extra bits specified from the
+ pow() function, use them. */
+ final double tempC = tempB + tempA;
+
+ // If tempC is positive infinite, the evaluation below could result in NaN,
+ // because z could be negative at the same time.
+ if (tempC == Double.POSITIVE_INFINITY) {
+ return Double.POSITIVE_INFINITY;
+ }
+
+ final double result;
+ if (extra != 0.0) {
+ result = tempC * extra * z + tempC * extra + tempC * z + tempB + tempA;
+ } else {
+ result = tempC * z + tempB + tempA;
+ }
+
+ if (hiPrec != null) {
+ // If requesting high precision
+ hiPrec[0] = tempA;
+ hiPrec[1] = tempC * extra * z + tempC * extra + tempC * z + tempB;
+ }
+
+ return result;
+ }
+
+ /**
+ * Compute exp(x) - 1
+ *
+ * @param x number to compute shifted exponential
+ * @return exp(x) - 1
+ */
+ public static double expm1(double x) {
+ return expm1(x, null);
+ }
+
+ /**
+ * Internal helper method for expm1
+ *
+ * @param x number to compute shifted exponential
+ * @param hiPrecOut receive high precision result for -1.0 < x < 1.0
+ * @return exp(x) - 1
+ */
+ private static double expm1(double x, double hiPrecOut[]) {
+ if (x != x || x == 0.0) { // NaN or zero
+ return x;
+ }
+
+ if (x <= -1.0 || x >= 1.0) {
+ // If not between +/- 1.0
+ // return exp(x) - 1.0;
+ double hiPrec[] = new double[2];
+ exp(x, 0.0, hiPrec);
+ if (x > 0.0) {
+ return -1.0 + hiPrec[0] + hiPrec[1];
+ } else {
+ final double ra = -1.0 + hiPrec[0];
+ double rb = -(ra + 1.0 - hiPrec[0]);
+ rb += hiPrec[1];
+ return ra + rb;
+ }
+ }
+
+ double baseA;
+ double baseB;
+ double epsilon;
+ boolean negative = false;
+
+ if (x < 0.0) {
+ x = -x;
+ negative = true;
+ }
+
+ {
+ int intFrac = (int) (x * 1024.0);
+ double tempA = ExpFracTable.EXP_FRAC_TABLE_A[intFrac] - 1.0;
+ double tempB = ExpFracTable.EXP_FRAC_TABLE_B[intFrac];
+
+ double temp = tempA + tempB;
+ tempB = -(temp - tempA - tempB);
+ tempA = temp;
+
+ temp = tempA * HEX_40000000;
+ baseA = tempA + temp - temp;
+ baseB = tempB + (tempA - baseA);
+
+ epsilon = x - intFrac / 1024.0;
+ }
+
+ /* Compute expm1(epsilon) */
+ double zb = 0.008336750013465571;
+ zb = zb * epsilon + 0.041666663879186654;
+ zb = zb * epsilon + 0.16666666666745392;
+ zb = zb * epsilon + 0.49999999999999994;
+ zb *= epsilon;
+ zb *= epsilon;
+
+ double za = epsilon;
+ double temp = za + zb;
+ zb = -(temp - za - zb);
+ za = temp;
+
+ temp = za * HEX_40000000;
+ temp = za + temp - temp;
+ zb += za - temp;
+ za = temp;
+
+ /* Combine the parts. expm1(a+b) = expm1(a) + expm1(b) + expm1(a)*expm1(b) */
+ double ya = za * baseA;
+ // double yb = za*baseB + zb*baseA + zb*baseB;
+ temp = ya + za * baseB;
+ double yb = -(temp - ya - za * baseB);
+ ya = temp;
+
+ temp = ya + zb * baseA;
+ yb += -(temp - ya - zb * baseA);
+ ya = temp;
+
+ temp = ya + zb * baseB;
+ yb += -(temp - ya - zb * baseB);
+ ya = temp;
+
+ // ya = ya + za + baseA;
+ // yb = yb + zb + baseB;
+ temp = ya + baseA;
+ yb += -(temp - baseA - ya);
+ ya = temp;
+
+ temp = ya + za;
+ // yb += (ya > za) ? -(temp - ya - za) : -(temp - za - ya);
+ yb += -(temp - ya - za);
+ ya = temp;
+
+ temp = ya + baseB;
+ // yb += (ya > baseB) ? -(temp - ya - baseB) : -(temp - baseB - ya);
+ yb += -(temp - ya - baseB);
+ ya = temp;
+
+ temp = ya + zb;
+ // yb += (ya > zb) ? -(temp - ya - zb) : -(temp - zb - ya);
+ yb += -(temp - ya - zb);
+ ya = temp;
+
+ if (negative) {
+ /* Compute expm1(-x) = -expm1(x) / (expm1(x) + 1) */
+ double denom = 1.0 + ya;
+ double denomr = 1.0 / denom;
+ double denomb = -(denom - 1.0 - ya) + yb;
+ double ratio = ya * denomr;
+ temp = ratio * HEX_40000000;
+ final double ra = ratio + temp - temp;
+ double rb = ratio - ra;
+
+ temp = denom * HEX_40000000;
+ za = denom + temp - temp;
+ zb = denom - za;
+
+ rb += (ya - za * ra - za * rb - zb * ra - zb * rb) * denomr;
+
+ // f(x) = x/1+x
+ // Compute f'(x)
+ // Product rule: d(uv) = du*v + u*dv
+ // Chain rule: d(f(g(x)) = f'(g(x))*f(g'(x))
+ // d(1/x) = -1/(x*x)
+ // d(1/1+x) = -1/( (1+x)^2) * 1 = -1/((1+x)*(1+x))
+ // d(x/1+x) = -x/((1+x)(1+x)) + 1/1+x = 1 / ((1+x)(1+x))
+
+ // Adjust for yb
+ rb += yb * denomr; // numerator
+ rb += -ya * denomb * denomr * denomr; // denominator
+
+ // negate
+ ya = -ra;
+ yb = -rb;
+ }
+
+ if (hiPrecOut != null) {
+ hiPrecOut[0] = ya;
+ hiPrecOut[1] = yb;
+ }
+
+ return ya + yb;
+ }
+
+ /**
+ * Natural logarithm.
+ *
+ * @param x a double
+ * @return log(x)
+ */
+ public static double log(final double x) {
+ return log(x, null);
+ }
+
+ /**
+ * Internal helper method for natural logarithm function.
+ *
+ * @param x original argument of the natural logarithm function
+ * @param hiPrec extra bits of precision on output (To Be Confirmed)
+ * @return log(x)
+ */
+ private static double log(final double x, final double[] hiPrec) {
+ if (x == 0) { // Handle special case of +0/-0
+ return Double.NEGATIVE_INFINITY;
+ }
+ long bits = Double.doubleToRawLongBits(x);
+
+ /* Handle special cases of negative input, and NaN */
+ if (((bits & 0x8000000000000000L) != 0 || x != x) && x != 0.0) {
+ if (hiPrec != null) {
+ hiPrec[0] = Double.NaN;
+ }
+
+ return Double.NaN;
+ }
+
+ /* Handle special cases of Positive infinity. */
+ if (x == Double.POSITIVE_INFINITY) {
+ if (hiPrec != null) {
+ hiPrec[0] = Double.POSITIVE_INFINITY;
+ }
+
+ return Double.POSITIVE_INFINITY;
+ }
+
+ /* Extract the exponent */
+ int exp = (int) (bits >> 52) - 1023;
+
+ if ((bits & 0x7ff0000000000000L) == 0) {
+ // Subnormal!
+ if (x == 0) {
+ // Zero
+ if (hiPrec != null) {
+ hiPrec[0] = Double.NEGATIVE_INFINITY;
+ }
+
+ return Double.NEGATIVE_INFINITY;
+ }
+
+ /* Normalize the subnormal number. */
+ bits <<= 1;
+ while ((bits & 0x0010000000000000L) == 0) {
+ --exp;
+ bits <<= 1;
+ }
+ }
+
+ if ((exp == -1 || exp == 0) && x < 1.01 && x > 0.99 && hiPrec == null) {
+ /* The normal method doesn't work well in the range [0.99, 1.01], so call do a straight
+ polynomial expansion in higer precision. */
+
+ /* Compute x - 1.0 and split it */
+ double xa = x - 1.0;
+ double xb = xa - x + 1.0;
+ double tmp = xa * HEX_40000000;
+ double aa = xa + tmp - tmp;
+ double ab = xa - aa;
+ xa = aa;
+ xb = ab;
+
+ final double[] lnCoef_last = LN_QUICK_COEF[LN_QUICK_COEF.length - 1];
+ double ya = lnCoef_last[0];
+ double yb = lnCoef_last[1];
+
+ for (int i = LN_QUICK_COEF.length - 2; i >= 0; i--) {
+ /* Multiply a = y * x */
+ aa = ya * xa;
+ ab = ya * xb + yb * xa + yb * xb;
+ /* split, so now y = a */
+ tmp = aa * HEX_40000000;
+ ya = aa + tmp - tmp;
+ yb = aa - ya + ab;
+
+ /* Add a = y + lnQuickCoef */
+ final double[] lnCoef_i = LN_QUICK_COEF[i];
+ aa = ya + lnCoef_i[0];
+ ab = yb + lnCoef_i[1];
+ /* Split y = a */
+ tmp = aa * HEX_40000000;
+ ya = aa + tmp - tmp;
+ yb = aa - ya + ab;
+ }
+
+ /* Multiply a = y * x */
+ aa = ya * xa;
+ ab = ya * xb + yb * xa + yb * xb;
+ /* split, so now y = a */
+ tmp = aa * HEX_40000000;
+ ya = aa + tmp - tmp;
+ yb = aa - ya + ab;
+
+ return ya + yb;
+ }
+
+ // lnm is a log of a number in the range of 1.0 - 2.0, so 0 <= lnm < ln(2)
+ final double[] lnm = lnMant.LN_MANT[(int) ((bits & 0x000ffc0000000000L) >> 42)];
+
+ /*
+ double epsilon = x / Double.longBitsToDouble(bits & 0xfffffc0000000000L);
+
+ epsilon -= 1.0;
+ */
+
+ // y is the most significant 10 bits of the mantissa
+ // double y = Double.longBitsToDouble(bits & 0xfffffc0000000000L);
+ // double epsilon = (x - y) / y;
+ final double epsilon =
+ (bits & 0x3ffffffffffL) / (TWO_POWER_52 + (bits & 0x000ffc0000000000L));
+
+ double lnza = 0.0;
+ double lnzb = 0.0;
+
+ if (hiPrec != null) {
+ /* split epsilon -> x */
+ double tmp = epsilon * HEX_40000000;
+ double aa = epsilon + tmp - tmp;
+ double ab = epsilon - aa;
+ double xa = aa;
+ double xb = ab;
+
+ /* Need a more accurate epsilon, so adjust the division. */
+ final double numer = bits & 0x3ffffffffffL;
+ final double denom = TWO_POWER_52 + (bits & 0x000ffc0000000000L);
+ aa = numer - xa * denom - xb * denom;
+ xb += aa / denom;
+
+ /* Remez polynomial evaluation */
+ final double[] lnCoef_last = LN_HI_PREC_COEF[LN_HI_PREC_COEF.length - 1];
+ double ya = lnCoef_last[0];
+ double yb = lnCoef_last[1];
+
+ for (int i = LN_HI_PREC_COEF.length - 2; i >= 0; i--) {
+ /* Multiply a = y * x */
+ aa = ya * xa;
+ ab = ya * xb + yb * xa + yb * xb;
+ /* split, so now y = a */
+ tmp = aa * HEX_40000000;
+ ya = aa + tmp - tmp;
+ yb = aa - ya + ab;
+
+ /* Add a = y + lnHiPrecCoef */
+ final double[] lnCoef_i = LN_HI_PREC_COEF[i];
+ aa = ya + lnCoef_i[0];
+ ab = yb + lnCoef_i[1];
+ /* Split y = a */
+ tmp = aa * HEX_40000000;
+ ya = aa + tmp - tmp;
+ yb = aa - ya + ab;
+ }
+
+ /* Multiply a = y * x */
+ aa = ya * xa;
+ ab = ya * xb + yb * xa + yb * xb;
+
+ /* split, so now lnz = a */
+ /*
+ tmp = aa * 1073741824.0;
+ lnza = aa + tmp - tmp;
+ lnzb = aa - lnza + ab;
+ */
+ lnza = aa + ab;
+ lnzb = -(lnza - aa - ab);
+ } else {
+ /* High precision not required. Eval Remez polynomial
+ using standard double precision */
+ lnza = -0.16624882440418567;
+ lnza = lnza * epsilon + 0.19999954120254515;
+ lnza = lnza * epsilon + -0.2499999997677497;
+ lnza = lnza * epsilon + 0.3333333333332802;
+ lnza = lnza * epsilon + -0.5;
+ lnza = lnza * epsilon + 1.0;
+ lnza *= epsilon;
+ }
+
+ /* Relative sizes:
+ * lnzb [0, 2.33E-10]
+ * lnm[1] [0, 1.17E-7]
+ * ln2B*exp [0, 1.12E-4]
+ * lnza [0, 9.7E-4]
+ * lnm[0] [0, 0.692]
+ * ln2A*exp [0, 709]
+ */
+
+ /* Compute the following sum:
+ * lnzb + lnm[1] + ln2B*exp + lnza + lnm[0] + ln2A*exp;
+ */
+
+ // return lnzb + lnm[1] + ln2B*exp + lnza + lnm[0] + ln2A*exp;
+ double a = LN_2_A * exp;
+ double b = 0.0;
+ double c = a + lnm[0];
+ double d = -(c - a - lnm[0]);
+ a = c;
+ b += d;
+
+ c = a + lnza;
+ d = -(c - a - lnza);
+ a = c;
+ b += d;
+
+ c = a + LN_2_B * exp;
+ d = -(c - a - LN_2_B * exp);
+ a = c;
+ b += d;
+
+ c = a + lnm[1];
+ d = -(c - a - lnm[1]);
+ a = c;
+ b += d;
+
+ c = a + lnzb;
+ d = -(c - a - lnzb);
+ a = c;
+ b += d;
+
+ if (hiPrec != null) {
+ hiPrec[0] = a;
+ hiPrec[1] = b;
+ }
+
+ return a + b;
+ }
+
+ /**
+ * Computes log(1 + x).
+ *
+ * @param x Number.
+ * @return {@code log(1 + x)}.
+ */
+ public static double log1p(final double x) {
+ if (x == -1) {
+ return Double.NEGATIVE_INFINITY;
+ }
+
+ if (x == Double.POSITIVE_INFINITY) {
+ return Double.POSITIVE_INFINITY;
+ }
+
+ if (x > 1e-6 || x < -1e-6) {
+ final double xpa = 1 + x;
+ final double xpb = -(xpa - 1 - x);
+
+ final double[] hiPrec = new double[2];
+ final double lores = log(xpa, hiPrec);
+ if (Double.isInfinite(lores)) { // Don't allow this to be converted to NaN
+ return lores;
+ }
+
+ // Do a taylor series expansion around xpa:
+ // f(x+y) = f(x) + f'(x) y + f''(x)/2 y^2
+ final double fx1 = xpb / xpa;
+ final double epsilon = 0.5 * fx1 + 1;
+ return epsilon * fx1 + hiPrec[1] + hiPrec[0];
+ } else {
+ // Value is small |x| < 1e6, do a Taylor series centered on 1.
+ final double y = (x * F_1_3 - F_1_2) * x + 1;
+ return y * x;
+ }
+ }
+
+ /**
+ * Compute the base 10 logarithm.
+ *
+ * @param x a number
+ * @return log10(x)
+ */
+ public static double log10(final double x) {
+ final double hiPrec[] = new double[2];
+
+ final double lores = log(x, hiPrec);
+ if (Double.isInfinite(lores)) { // don't allow this to be converted to NaN
+ return lores;
+ }
+
+ final double tmp = hiPrec[0] * HEX_40000000;
+ final double lna = hiPrec[0] + tmp - tmp;
+ final double lnb = hiPrec[0] - lna + hiPrec[1];
+
+ final double rln10a = 0.4342944622039795;
+ final double rln10b = 1.9699272335463627E-8;
+
+ return rln10b * lnb + rln10b * lna + rln10a * lnb + rln10a * lna;
+ }
+
+ /**
+ * Computes the <a href="http://mathworld.wolfram.com/Logarithm.html">logarithm</a> in a given
+ * base.
+ *
+ * <p>Returns {@code NaN} if either argument is negative. If {@code base} is 0 and {@code x} is
+ * positive, 0 is returned. If {@code base} is positive and {@code x} is 0, {@code
+ * Double.NEGATIVE_INFINITY} is returned. If both arguments are 0, the result is {@code NaN}.
+ *
+ * @param base Base of the logarithm, must be greater than 0.
+ * @param x Argument, must be greater than 0.
+ * @return the value of the logarithm, i.e. the number {@code y} such that <code>
+ * base<sup>y</sup> = x</code>.
+ * @since 1.2 (previously in {@code MathUtils}, moved as of version 3.0)
+ */
+ public static double log(double base, double x) {
+ return log(x) / log(base);
+ }
+
+ /**
+ * Power function. Compute x^y.
+ *
+ * @param x a double
+ * @param y a double
+ * @return double
+ */
+ public static double pow(final double x, final double y) {
+
+ if (y == 0) {
+ // y = -0 or y = +0
+ return 1.0;
+ } else {
+
+ final long yBits = Double.doubleToRawLongBits(y);
+ final int yRawExp = (int) ((yBits & MASK_DOUBLE_EXPONENT) >> 52);
+ final long yRawMantissa = yBits & MASK_DOUBLE_MANTISSA;
+ final long xBits = Double.doubleToRawLongBits(x);
+ final int xRawExp = (int) ((xBits & MASK_DOUBLE_EXPONENT) >> 52);
+ final long xRawMantissa = xBits & MASK_DOUBLE_MANTISSA;
+
+ if (yRawExp > 1085) {
+ // y is either a very large integral value that does not fit in a long or it is a
+ // special number
+
+ if ((yRawExp == 2047 && yRawMantissa != 0)
+ || (xRawExp == 2047 && xRawMantissa != 0)) {
+ // NaN
+ return Double.NaN;
+ } else if (xRawExp == 1023 && xRawMantissa == 0) {
+ // x = -1.0 or x = +1.0
+ if (yRawExp == 2047) {
+ // y is infinite
+ return Double.NaN;
+ } else {
+ // y is a large even integer
+ return 1.0;
+ }
+ } else {
+ // the absolute value of x is either greater or smaller than 1.0
+
+ // if yRawExp == 2047 and mantissa is 0, y = -infinity or y = +infinity
+ // if 1085 < yRawExp < 2047, y is simply a large number, however, due to limited
+ // accuracy, at this magnitude it behaves just like infinity with regards to x
+ if ((y > 0) ^ (xRawExp < 1023)) {
+ // either y = +infinity (or large engouh) and abs(x) > 1.0
+ // or y = -infinity (or large engouh) and abs(x) < 1.0
+ return Double.POSITIVE_INFINITY;
+ } else {
+ // either y = +infinity (or large engouh) and abs(x) < 1.0
+ // or y = -infinity (or large engouh) and abs(x) > 1.0
+ return +0.0;
+ }
+ }
+
+ } else {
+ // y is a regular non-zero number
+
+ if (yRawExp >= 1023) {
+ // y may be an integral value, which should be handled specifically
+ final long yFullMantissa = IMPLICIT_HIGH_BIT | yRawMantissa;
+ if (yRawExp < 1075) {
+ // normal number with negative shift that may have a fractional part
+ final long integralMask = (-1L) << (1075 - yRawExp);
+ if ((yFullMantissa & integralMask) == yFullMantissa) {
+ // all fractional bits are 0, the number is really integral
+ final long l = yFullMantissa >> (1075 - yRawExp);
+ return FastMath.pow(x, (y < 0) ? -l : l);
+ }
+ } else {
+ // normal number with positive shift, always an integral value
+ // we know it fits in a primitive long because yRawExp > 1085 has been
+ // handled above
+ final long l = yFullMantissa << (yRawExp - 1075);
+ return FastMath.pow(x, (y < 0) ? -l : l);
+ }
+ }
+
+ // y is a non-integral value
+
+ if (x == 0) {
+ // x = -0 or x = +0
+ // the integer powers have already been handled above
+ return y < 0 ? Double.POSITIVE_INFINITY : +0.0;
+ } else if (xRawExp == 2047) {
+ if (xRawMantissa == 0) {
+ // x = -infinity or x = +infinity
+ return (y < 0) ? +0.0 : Double.POSITIVE_INFINITY;
+ } else {
+ // NaN
+ return Double.NaN;
+ }
+ } else if (x < 0) {
+ // the integer powers have already been handled above
+ return Double.NaN;
+ } else {
+
+ // this is the general case, for regular fractional numbers x and y
+
+ // Split y into ya and yb such that y = ya+yb
+ final double tmp = y * HEX_40000000;
+ final double ya = (y + tmp) - tmp;
+ final double yb = y - ya;
+
+ /* Compute ln(x) */
+ final double lns[] = new double[2];
+ final double lores = log(x, lns);
+ if (Double.isInfinite(lores)) { // don't allow this to be converted to NaN
+ return lores;
+ }
+
+ double lna = lns[0];
+ double lnb = lns[1];
+
+ /* resplit lns */
+ final double tmp1 = lna * HEX_40000000;
+ final double tmp2 = (lna + tmp1) - tmp1;
+ lnb += lna - tmp2;
+ lna = tmp2;
+
+ // y*ln(x) = (aa+ab)
+ final double aa = lna * ya;
+ final double ab = lna * yb + lnb * ya + lnb * yb;
+
+ lna = aa + ab;
+ lnb = -(lna - aa - ab);
+
+ double z = 1.0 / 120.0;
+ z = z * lnb + (1.0 / 24.0);
+ z = z * lnb + (1.0 / 6.0);
+ z = z * lnb + 0.5;
+ z = z * lnb + 1.0;
+ z *= lnb;
+
+ final double result = exp(lna, z, null);
+ // result = result + result * z;
+ return result;
+ }
+ }
+ }
+ }
+
+ /**
+ * Raise a double to an int power.
+ *
+ * @param d Number to raise.
+ * @param e Exponent.
+ * @return d<sup>e</sup>
+ * @since 3.1
+ */
+ public static double pow(double d, int e) {
+ return pow(d, (long) e);
+ }
+
+ /**
+ * Raise a double to a long power.
+ *
+ * @param d Number to raise.
+ * @param e Exponent.
+ * @return d<sup>e</sup>
+ * @since 3.6
+ */
+ public static double pow(double d, long e) {
+ if (e == 0) {
+ return 1.0;
+ } else if (e > 0) {
+ return new Split(d).pow(e).full;
+ } else {
+ return new Split(d).reciprocal().pow(-e).full;
+ }
+ }
+
+ /** Class operator on double numbers split into one 26 bits number and one 27 bits number. */
+ private static class Split {
+
+ /** Split version of NaN. */
+ public static final Split NAN = new Split(Double.NaN, 0);
+
+ /** Split version of positive infinity. */
+ public static final Split POSITIVE_INFINITY = new Split(Double.POSITIVE_INFINITY, 0);
+
+ /** Split version of negative infinity. */
+ public static final Split NEGATIVE_INFINITY = new Split(Double.NEGATIVE_INFINITY, 0);
+
+ /** Full number. */
+ private final double full;
+
+ /** High order bits. */
+ private final double high;
+
+ /** Low order bits. */
+ private final double low;
+
+ /**
+ * Simple constructor.
+ *
+ * @param x number to split
+ */
+ Split(final double x) {
+ full = x;
+ high = Double.longBitsToDouble(Double.doubleToRawLongBits(x) & ((-1L) << 27));
+ low = x - high;
+ }
+
+ /**
+ * Simple constructor.
+ *
+ * @param high high order bits
+ * @param low low order bits
+ */
+ Split(final double high, final double low) {
+ this(
+ high == 0.0
+ ? (low == 0.0
+ && Double.doubleToRawLongBits(high)
+ == Long.MIN_VALUE /* negative zero */
+ ? -0.0
+ : low)
+ : high + low,
+ high,
+ low);
+ }
+
+ /**
+ * Simple constructor.
+ *
+ * @param full full number
+ * @param high high order bits
+ * @param low low order bits
+ */
+ Split(final double full, final double high, final double low) {
+ this.full = full;
+ this.high = high;
+ this.low = low;
+ }
+
+ /**
+ * Multiply the instance by another one.
+ *
+ * @param b other instance to multiply by
+ * @return product
+ */
+ public Split multiply(final Split b) {
+ // beware the following expressions must NOT be simplified, they rely on floating point
+ // arithmetic properties
+ final Split mulBasic = new Split(full * b.full);
+ final double mulError =
+ low * b.low - (((mulBasic.full - high * b.high) - low * b.high) - high * b.low);
+ return new Split(mulBasic.high, mulBasic.low + mulError);
+ }
+
+ /**
+ * Compute the reciprocal of the instance.
+ *
+ * @return reciprocal of the instance
+ */
+ public Split reciprocal() {
+
+ final double approximateInv = 1.0 / full;
+ final Split splitInv = new Split(approximateInv);
+
+ // if 1.0/d were computed perfectly, remultiplying it by d should give 1.0
+ // we want to estimate the error so we can fix the low order bits of approximateInvLow
+ // beware the following expressions must NOT be simplified, they rely on floating point
+ // arithmetic properties
+ final Split product = multiply(splitInv);
+ final double error = (product.high - 1) + product.low;
+
+ // better accuracy estimate of reciprocal
+ return Double.isNaN(error)
+ ? splitInv
+ : new Split(splitInv.high, splitInv.low - error / full);
+ }
+
+ /**
+ * Computes this^e.
+ *
+ * @param e exponent (beware, here it MUST be > 0; the only exclusion is Long.MIN_VALUE)
+ * @return d^e, split in high and low bits
+ * @since 3.6
+ */
+ private Split pow(final long e) {
+
+ // prepare result
+ Split result = new Split(1);
+
+ // d^(2p)
+ Split d2p = new Split(full, high, low);
+
+ for (long p = e; p != 0; p >>>= 1) {
+
+ if ((p & 0x1) != 0) {
+ // accurate multiplication result = result * d^(2p) using Veltkamp TwoProduct
+ // algorithm
+ result = result.multiply(d2p);
+ }
+
+ // accurate squaring d^(2(p+1)) = d^(2p) * d^(2p) using Veltkamp TwoProduct
+ // algorithm
+ d2p = d2p.multiply(d2p);
+ }
+
+ if (Double.isNaN(result.full)) {
+ if (Double.isNaN(full)) {
+ return Split.NAN;
+ } else {
+ // some intermediate numbers exceeded capacity,
+ // and the low order bits became NaN (because infinity - infinity = NaN)
+ if (FastMath.abs(full) < 1) {
+ return new Split(FastMath.copySign(0.0, full), 0.0);
+ } else if (full < 0 && (e & 0x1) == 1) {
+ return Split.NEGATIVE_INFINITY;
+ } else {
+ return Split.POSITIVE_INFINITY;
+ }
+ }
+ } else {
+ return result;
+ }
+ }
+ }
+
+ /**
+ * Computes sin(x) - x, where |x| < 1/16. Use a Remez polynomial approximation.
+ *
+ * @param x a number smaller than 1/16
+ * @return sin(x) - x
+ */
+ private static double polySine(final double x) {
+ double x2 = x * x;
+
+ double p = 2.7553817452272217E-6;
+ p = p * x2 + -1.9841269659586505E-4;
+ p = p * x2 + 0.008333333333329196;
+ p = p * x2 + -0.16666666666666666;
+ // p *= x2;
+ // p *= x;
+ p = p * x2 * x;
+
+ return p;
+ }
+
+ /**
+ * Computes cos(x) - 1, where |x| < 1/16. Use a Remez polynomial approximation.
+ *
+ * @param x a number smaller than 1/16
+ * @return cos(x) - 1
+ */
+ private static double polyCosine(double x) {
+ double x2 = x * x;
+
+ double p = 2.479773539153719E-5;
+ p = p * x2 + -0.0013888888689039883;
+ p = p * x2 + 0.041666666666621166;
+ p = p * x2 + -0.49999999999999994;
+ p *= x2;
+
+ return p;
+ }
+
+ /**
+ * Compute sine over the first quadrant (0 < x < pi/2). Use combination of table lookup and
+ * rational polynomial expansion.
+ *
+ * @param xa number from which sine is requested
+ * @param xb extra bits for x (may be 0.0)
+ * @return sin(xa + xb)
+ */
+ private static double sinQ(double xa, double xb) {
+ int idx = (int) ((xa * 8.0) + 0.5);
+ final double epsilon = xa - EIGHTHS[idx]; // idx*0.125;
+
+ // Table lookups
+ final double sintA = SINE_TABLE_A[idx];
+ final double sintB = SINE_TABLE_B[idx];
+ final double costA = COSINE_TABLE_A[idx];
+ final double costB = COSINE_TABLE_B[idx];
+
+ // Polynomial eval of sin(epsilon), cos(epsilon)
+ double sinEpsA = epsilon;
+ double sinEpsB = polySine(epsilon);
+ final double cosEpsA = 1.0;
+ final double cosEpsB = polyCosine(epsilon);
+
+ // Split epsilon xa + xb = x
+ final double temp = sinEpsA * HEX_40000000;
+ double temp2 = (sinEpsA + temp) - temp;
+ sinEpsB += sinEpsA - temp2;
+ sinEpsA = temp2;
+
+ /* Compute sin(x) by angle addition formula */
+ double result;
+
+ /* Compute the following sum:
+ *
+ * result = sintA + costA*sinEpsA + sintA*cosEpsB + costA*sinEpsB +
+ * sintB + costB*sinEpsA + sintB*cosEpsB + costB*sinEpsB;
+ *
+ * Ranges of elements
+ *
+ * xxxtA 0 PI/2
+ * xxxtB -1.5e-9 1.5e-9
+ * sinEpsA -0.0625 0.0625
+ * sinEpsB -6e-11 6e-11
+ * cosEpsA 1.0
+ * cosEpsB 0 -0.0625
+ *
+ */
+
+ // result = sintA + costA*sinEpsA + sintA*cosEpsB + costA*sinEpsB +
+ // sintB + costB*sinEpsA + sintB*cosEpsB + costB*sinEpsB;
+
+ // result = sintA + sintA*cosEpsB + sintB + sintB * cosEpsB;
+ // result += costA*sinEpsA + costA*sinEpsB + costB*sinEpsA + costB * sinEpsB;
+ double a = 0;
+ double b = 0;
+
+ double t = sintA;
+ double c = a + t;
+ double d = -(c - a - t);
+ a = c;
+ b += d;
+
+ t = costA * sinEpsA;
+ c = a + t;
+ d = -(c - a - t);
+ a = c;
+ b += d;
+
+ b = b + sintA * cosEpsB + costA * sinEpsB;
+ /*
+ t = sintA*cosEpsB;
+ c = a + t;
+ d = -(c - a - t);
+ a = c;
+ b = b + d;
+
+ t = costA*sinEpsB;
+ c = a + t;
+ d = -(c - a - t);
+ a = c;
+ b = b + d;
+ */
+
+ b = b + sintB + costB * sinEpsA + sintB * cosEpsB + costB * sinEpsB;
+ /*
+ t = sintB;
+ c = a + t;
+ d = -(c - a - t);
+ a = c;
+ b = b + d;
+
+ t = costB*sinEpsA;
+ c = a + t;
+ d = -(c - a - t);
+ a = c;
+ b = b + d;
+
+ t = sintB*cosEpsB;
+ c = a + t;
+ d = -(c - a - t);
+ a = c;
+ b = b + d;
+
+ t = costB*sinEpsB;
+ c = a + t;
+ d = -(c - a - t);
+ a = c;
+ b = b + d;
+ */
+
+ if (xb != 0.0) {
+ t =
+ ((costA + costB) * (cosEpsA + cosEpsB) - (sintA + sintB) * (sinEpsA + sinEpsB))
+ * xb; // approximate cosine*xb
+ c = a + t;
+ d = -(c - a - t);
+ a = c;
+ b += d;
+ }
+
+ result = a + b;
+
+ return result;
+ }
+
+ /**
+ * Compute cosine in the first quadrant by subtracting input from PI/2 and then calling sinQ.
+ * This is more accurate as the input approaches PI/2.
+ *
+ * @param xa number from which cosine is requested
+ * @param xb extra bits for x (may be 0.0)
+ * @return cos(xa + xb)
+ */
+ private static double cosQ(double xa, double xb) {
+ final double pi2a = 1.5707963267948966;
+ final double pi2b = 6.123233995736766E-17;
+
+ final double a = pi2a - xa;
+ double b = -(a - pi2a + xa);
+ b += pi2b - xb;
+
+ return sinQ(a, b);
+ }
+
+ /**
+ * Compute tangent (or cotangent) over the first quadrant. 0 < x < pi/2 Use combination of table
+ * lookup and rational polynomial expansion.
+ *
+ * @param xa number from which sine is requested
+ * @param xb extra bits for x (may be 0.0)
+ * @param cotanFlag if true, compute the cotangent instead of the tangent
+ * @return tan(xa+xb) (or cotangent, depending on cotanFlag)
+ */
+ private static double tanQ(double xa, double xb, boolean cotanFlag) {
+
+ int idx = (int) ((xa * 8.0) + 0.5);
+ final double epsilon = xa - EIGHTHS[idx]; // idx*0.125;
+
+ // Table lookups
+ final double sintA = SINE_TABLE_A[idx];
+ final double sintB = SINE_TABLE_B[idx];
+ final double costA = COSINE_TABLE_A[idx];
+ final double costB = COSINE_TABLE_B[idx];
+
+ // Polynomial eval of sin(epsilon), cos(epsilon)
+ double sinEpsA = epsilon;
+ double sinEpsB = polySine(epsilon);
+ final double cosEpsA = 1.0;
+ final double cosEpsB = polyCosine(epsilon);
+
+ // Split epsilon xa + xb = x
+ double temp = sinEpsA * HEX_40000000;
+ double temp2 = (sinEpsA + temp) - temp;
+ sinEpsB += sinEpsA - temp2;
+ sinEpsA = temp2;
+
+ /* Compute sin(x) by angle addition formula */
+
+ /* Compute the following sum:
+ *
+ * result = sintA + costA*sinEpsA + sintA*cosEpsB + costA*sinEpsB +
+ * sintB + costB*sinEpsA + sintB*cosEpsB + costB*sinEpsB;
+ *
+ * Ranges of elements
+ *
+ * xxxtA 0 PI/2
+ * xxxtB -1.5e-9 1.5e-9
+ * sinEpsA -0.0625 0.0625
+ * sinEpsB -6e-11 6e-11
+ * cosEpsA 1.0
+ * cosEpsB 0 -0.0625
+ *
+ */
+
+ // result = sintA + costA*sinEpsA + sintA*cosEpsB + costA*sinEpsB +
+ // sintB + costB*sinEpsA + sintB*cosEpsB + costB*sinEpsB;
+
+ // result = sintA + sintA*cosEpsB + sintB + sintB * cosEpsB;
+ // result += costA*sinEpsA + costA*sinEpsB + costB*sinEpsA + costB * sinEpsB;
+ double a = 0;
+ double b = 0;
+
+ // Compute sine
+ double t = sintA;
+ double c = a + t;
+ double d = -(c - a - t);
+ a = c;
+ b += d;
+
+ t = costA * sinEpsA;
+ c = a + t;
+ d = -(c - a - t);
+ a = c;
+ b += d;
+
+ b += sintA * cosEpsB + costA * sinEpsB;
+ b += sintB + costB * sinEpsA + sintB * cosEpsB + costB * sinEpsB;
+
+ double sina = a + b;
+ double sinb = -(sina - a - b);
+
+ // Compute cosine
+
+ a = b = c = d = 0.0;
+
+ t = costA * cosEpsA;
+ c = a + t;
+ d = -(c - a - t);
+ a = c;
+ b += d;
+
+ t = -sintA * sinEpsA;
+ c = a + t;
+ d = -(c - a - t);
+ a = c;
+ b += d;
+
+ b += costB * cosEpsA + costA * cosEpsB + costB * cosEpsB;
+ b -= sintB * sinEpsA + sintA * sinEpsB + sintB * sinEpsB;
+
+ double cosa = a + b;
+ double cosb = -(cosa - a - b);
+
+ if (cotanFlag) {
+ double tmp;
+ tmp = cosa;
+ cosa = sina;
+ sina = tmp;
+ tmp = cosb;
+ cosb = sinb;
+ sinb = tmp;
+ }
+
+ /* estimate and correct, compute 1.0/(cosa+cosb) */
+ /*
+ double est = (sina+sinb)/(cosa+cosb);
+ double err = (sina - cosa*est) + (sinb - cosb*est);
+ est += err/(cosa+cosb);
+ err = (sina - cosa*est) + (sinb - cosb*est);
+ */
+
+ // f(x) = 1/x, f'(x) = -1/x^2
+
+ double est = sina / cosa;
+
+ /* Split the estimate to get more accurate read on division rounding */
+ temp = est * HEX_40000000;
+ double esta = (est + temp) - temp;
+ double estb = est - esta;
+
+ temp = cosa * HEX_40000000;
+ double cosaa = (cosa + temp) - temp;
+ double cosab = cosa - cosaa;
+
+ // double err = (sina - est*cosa)/cosa; // Correction for division rounding
+ double err =
+ (sina - esta * cosaa - esta * cosab - estb * cosaa - estb * cosab)
+ / cosa; // Correction for division rounding
+ err += sinb / cosa; // Change in est due to sinb
+ err += -sina * cosb / cosa / cosa; // Change in est due to cosb
+
+ if (xb != 0.0) {
+ // tan' = 1 + tan^2 cot' = -(1 + cot^2)
+ // Approximate impact of xb
+ double xbadj = xb + est * est * xb;
+ if (cotanFlag) {
+ xbadj = -xbadj;
+ }
+
+ err += xbadj;
+ }
+
+ return est + err;
+ }
+
+ /**
+ * Reduce the input argument using the Payne and Hanek method. This is good for all inputs 0.0 <
+ * x < inf Output is remainder after dividing by PI/2 The result array should contain 3 numbers.
+ * result[0] is the integer portion, so mod 4 this gives the quadrant. result[1] is the upper
+ * bits of the remainder result[2] is the lower bits of the remainder
+ *
+ * @param x number to reduce
+ * @param result placeholder where to put the result
+ */
+ private static void reducePayneHanek(double x, double result[]) {
+ /* Convert input double to bits */
+ long inbits = Double.doubleToRawLongBits(x);
+ int exponent = (int) ((inbits >> 52) & 0x7ff) - 1023;
+
+ /* Convert to fixed point representation */
+ inbits &= 0x000fffffffffffffL;
+ inbits |= 0x0010000000000000L;
+
+ /* Normalize input to be between 0.5 and 1.0 */
+ exponent++;
+ inbits <<= 11;
+
+ /* Based on the exponent, get a shifted copy of recip2pi */
+ long shpi0;
+ long shpiA;
+ long shpiB;
+ int idx = exponent >> 6;
+ int shift = exponent - (idx << 6);
+
+ if (shift != 0) {
+ shpi0 = (idx == 0) ? 0 : (RECIP_2PI[idx - 1] << shift);
+ shpi0 |= RECIP_2PI[idx] >>> (64 - shift);
+ shpiA = (RECIP_2PI[idx] << shift) | (RECIP_2PI[idx + 1] >>> (64 - shift));
+ shpiB = (RECIP_2PI[idx + 1] << shift) | (RECIP_2PI[idx + 2] >>> (64 - shift));
+ } else {
+ shpi0 = (idx == 0) ? 0 : RECIP_2PI[idx - 1];
+ shpiA = RECIP_2PI[idx];
+ shpiB = RECIP_2PI[idx + 1];
+ }
+
+ /* Multiply input by shpiA */
+ long a = inbits >>> 32;
+ long b = inbits & 0xffffffffL;
+
+ long c = shpiA >>> 32;
+ long d = shpiA & 0xffffffffL;
+
+ long ac = a * c;
+ long bd = b * d;
+ long bc = b * c;
+ long ad = a * d;
+
+ long prodB = bd + (ad << 32);
+ long prodA = ac + (ad >>> 32);
+
+ boolean bita = (bd & 0x8000000000000000L) != 0;
+ boolean bitb = (ad & 0x80000000L) != 0;
+ boolean bitsum = (prodB & 0x8000000000000000L) != 0;
+
+ /* Carry */
+ if ((bita && bitb) || ((bita || bitb) && !bitsum)) {
+ prodA++;
+ }
+
+ bita = (prodB & 0x8000000000000000L) != 0;
+ bitb = (bc & 0x80000000L) != 0;
+
+ prodB += bc << 32;
+ prodA += bc >>> 32;
+
+ bitsum = (prodB & 0x8000000000000000L) != 0;
+
+ /* Carry */
+ if ((bita && bitb) || ((bita || bitb) && !bitsum)) {
+ prodA++;
+ }
+
+ /* Multiply input by shpiB */
+ c = shpiB >>> 32;
+ d = shpiB & 0xffffffffL;
+ ac = a * c;
+ bc = b * c;
+ ad = a * d;
+
+ /* Collect terms */
+ ac += (bc + ad) >>> 32;
+
+ bita = (prodB & 0x8000000000000000L) != 0;
+ bitb = (ac & 0x8000000000000000L) != 0;
+ prodB += ac;
+ bitsum = (prodB & 0x8000000000000000L) != 0;
+ /* Carry */
+ if ((bita && bitb) || ((bita || bitb) && !bitsum)) {
+ prodA++;
+ }
+
+ /* Multiply by shpi0 */
+ c = shpi0 >>> 32;
+ d = shpi0 & 0xffffffffL;
+
+ bd = b * d;
+ bc = b * c;
+ ad = a * d;
+
+ prodA += bd + ((bc + ad) << 32);
+
+ /*
+ * prodA, prodB now contain the remainder as a fraction of PI. We want this as a fraction of
+ * PI/2, so use the following steps:
+ * 1.) multiply by 4.
+ * 2.) do a fixed point muliply by PI/4.
+ * 3.) Convert to floating point.
+ * 4.) Multiply by 2
+ */
+
+ /* This identifies the quadrant */
+ int intPart = (int) (prodA >>> 62);
+
+ /* Multiply by 4 */
+ prodA <<= 2;
+ prodA |= prodB >>> 62;
+ prodB <<= 2;
+
+ /* Multiply by PI/4 */
+ a = prodA >>> 32;
+ b = prodA & 0xffffffffL;
+
+ c = PI_O_4_BITS[0] >>> 32;
+ d = PI_O_4_BITS[0] & 0xffffffffL;
+
+ ac = a * c;
+ bd = b * d;
+ bc = b * c;
+ ad = a * d;
+
+ long prod2B = bd + (ad << 32);
+ long prod2A = ac + (ad >>> 32);
+
+ bita = (bd & 0x8000000000000000L) != 0;
+ bitb = (ad & 0x80000000L) != 0;
+ bitsum = (prod2B & 0x8000000000000000L) != 0;
+
+ /* Carry */
+ if ((bita && bitb) || ((bita || bitb) && !bitsum)) {
+ prod2A++;
+ }
+
+ bita = (prod2B & 0x8000000000000000L) != 0;
+ bitb = (bc & 0x80000000L) != 0;
+
+ prod2B += bc << 32;
+ prod2A += bc >>> 32;
+
+ bitsum = (prod2B & 0x8000000000000000L) != 0;
+
+ /* Carry */
+ if ((bita && bitb) || ((bita || bitb) && !bitsum)) {
+ prod2A++;
+ }
+
+ /* Multiply input by pio4bits[1] */
+ c = PI_O_4_BITS[1] >>> 32;
+ d = PI_O_4_BITS[1] & 0xffffffffL;
+ ac = a * c;
+ bc = b * c;
+ ad = a * d;
+
+ /* Collect terms */
+ ac += (bc + ad) >>> 32;
+
+ bita = (prod2B & 0x8000000000000000L) != 0;
+ bitb = (ac & 0x8000000000000000L) != 0;
+ prod2B += ac;
+ bitsum = (prod2B & 0x8000000000000000L) != 0;
+ /* Carry */
+ if ((bita && bitb) || ((bita || bitb) && !bitsum)) {
+ prod2A++;
+ }
+
+ /* Multiply inputB by pio4bits[0] */
+ a = prodB >>> 32;
+ b = prodB & 0xffffffffL;
+ c = PI_O_4_BITS[0] >>> 32;
+ d = PI_O_4_BITS[0] & 0xffffffffL;
+ ac = a * c;
+ bc = b * c;
+ ad = a * d;
+
+ /* Collect terms */
+ ac += (bc + ad) >>> 32;
+
+ bita = (prod2B & 0x8000000000000000L) != 0;
+ bitb = (ac & 0x8000000000000000L) != 0;
+ prod2B += ac;
+ bitsum = (prod2B & 0x8000000000000000L) != 0;
+ /* Carry */
+ if ((bita && bitb) || ((bita || bitb) && !bitsum)) {
+ prod2A++;
+ }
+
+ /* Convert to double */
+ double tmpA = (prod2A >>> 12) / TWO_POWER_52; // High order 52 bits
+ double tmpB =
+ (((prod2A & 0xfffL) << 40) + (prod2B >>> 24))
+ / TWO_POWER_52
+ / TWO_POWER_52; // Low bits
+
+ double sumA = tmpA + tmpB;
+ double sumB = -(sumA - tmpA - tmpB);
+
+ /* Multiply by PI/2 and return */
+ result[0] = intPart;
+ result[1] = sumA * 2.0;
+ result[2] = sumB * 2.0;
+ }
+
+ /**
+ * Sine function.
+ *
+ * @param x Argument.
+ * @return sin(x)
+ */
+ public static double sin(double x) {
+ boolean negative = false;
+ int quadrant = 0;
+ double xa;
+ double xb = 0.0;
+
+ /* Take absolute value of the input */
+ xa = x;
+ if (x < 0) {
+ negative = true;
+ xa = -xa;
+ }
+
+ /* Check for zero and negative zero */
+ if (xa == 0.0) {
+ long bits = Double.doubleToRawLongBits(x);
+ if (bits < 0) {
+ return -0.0;
+ }
+ return 0.0;
+ }
+
+ if (xa != xa || xa == Double.POSITIVE_INFINITY) {
+ return Double.NaN;
+ }
+
+ /* Perform any argument reduction */
+ if (xa > 3294198.0) {
+ // PI * (2**20)
+ // Argument too big for CodyWaite reduction. Must use
+ // PayneHanek.
+ double reduceResults[] = new double[3];
+ reducePayneHanek(xa, reduceResults);
+ quadrant = ((int) reduceResults[0]) & 3;
+ xa = reduceResults[1];
+ xb = reduceResults[2];
+ } else if (xa > 1.5707963267948966) {
+ final CodyWaite cw = new CodyWaite(xa);
+ quadrant = cw.getK() & 3;
+ xa = cw.getRemA();
+ xb = cw.getRemB();
+ }
+
+ if (negative) {
+ quadrant ^= 2; // Flip bit 1
+ }
+
+ switch (quadrant) {
+ case 0:
+ return sinQ(xa, xb);
+ case 1:
+ return cosQ(xa, xb);
+ case 2:
+ return -sinQ(xa, xb);
+ case 3:
+ return -cosQ(xa, xb);
+ default:
+ return Double.NaN;
+ }
+ }
+
+ /**
+ * Cosine function.
+ *
+ * @param x Argument.
+ * @return cos(x)
+ */
+ public static double cos(double x) {
+ int quadrant = 0;
+
+ /* Take absolute value of the input */
+ double xa = x;
+ if (x < 0) {
+ xa = -xa;
+ }
+
+ if (xa != xa || xa == Double.POSITIVE_INFINITY) {
+ return Double.NaN;
+ }
+
+ /* Perform any argument reduction */
+ double xb = 0;
+ if (xa > 3294198.0) {
+ // PI * (2**20)
+ // Argument too big for CodyWaite reduction. Must use
+ // PayneHanek.
+ double reduceResults[] = new double[3];
+ reducePayneHanek(xa, reduceResults);
+ quadrant = ((int) reduceResults[0]) & 3;
+ xa = reduceResults[1];
+ xb = reduceResults[2];
+ } else if (xa > 1.5707963267948966) {
+ final CodyWaite cw = new CodyWaite(xa);
+ quadrant = cw.getK() & 3;
+ xa = cw.getRemA();
+ xb = cw.getRemB();
+ }
+
+ // if (negative)
+ // quadrant = (quadrant + 2) % 4;
+
+ switch (quadrant) {
+ case 0:
+ return cosQ(xa, xb);
+ case 1:
+ return -sinQ(xa, xb);
+ case 2:
+ return -cosQ(xa, xb);
+ case 3:
+ return sinQ(xa, xb);
+ default:
+ return Double.NaN;
+ }
+ }
+
+ /**
+ * Tangent function.
+ *
+ * @param x Argument.
+ * @return tan(x)
+ */
+ public static double tan(double x) {
+ boolean negative = false;
+ int quadrant = 0;
+
+ /* Take absolute value of the input */
+ double xa = x;
+ if (x < 0) {
+ negative = true;
+ xa = -xa;
+ }
+
+ /* Check for zero and negative zero */
+ if (xa == 0.0) {
+ long bits = Double.doubleToRawLongBits(x);
+ if (bits < 0) {
+ return -0.0;
+ }
+ return 0.0;
+ }
+
+ if (xa != xa || xa == Double.POSITIVE_INFINITY) {
+ return Double.NaN;
+ }
+
+ /* Perform any argument reduction */
+ double xb = 0;
+ if (xa > 3294198.0) {
+ // PI * (2**20)
+ // Argument too big for CodyWaite reduction. Must use
+ // PayneHanek.
+ double reduceResults[] = new double[3];
+ reducePayneHanek(xa, reduceResults);
+ quadrant = ((int) reduceResults[0]) & 3;
+ xa = reduceResults[1];
+ xb = reduceResults[2];
+ } else if (xa > 1.5707963267948966) {
+ final CodyWaite cw = new CodyWaite(xa);
+ quadrant = cw.getK() & 3;
+ xa = cw.getRemA();
+ xb = cw.getRemB();
+ }
+
+ if (xa > 1.5) {
+ // Accuracy suffers between 1.5 and PI/2
+ final double pi2a = 1.5707963267948966;
+ final double pi2b = 6.123233995736766E-17;
+
+ final double a = pi2a - xa;
+ double b = -(a - pi2a + xa);
+ b += pi2b - xb;
+
+ xa = a + b;
+ xb = -(xa - a - b);
+ quadrant ^= 1;
+ negative ^= true;
+ }
+
+ double result;
+ if ((quadrant & 1) == 0) {
+ result = tanQ(xa, xb, false);
+ } else {
+ result = -tanQ(xa, xb, true);
+ }
+
+ if (negative) {
+ result = -result;
+ }
+
+ return result;
+ }
+
+ /**
+ * Arctangent function
+ *
+ * @param x a number
+ * @return atan(x)
+ */
+ public static double atan(double x) {
+ return atan(x, 0.0, false);
+ }
+
+ /**
+ * Internal helper function to compute arctangent.
+ *
+ * @param xa number from which arctangent is requested
+ * @param xb extra bits for x (may be 0.0)
+ * @param leftPlane if true, result angle must be put in the left half plane
+ * @return atan(xa + xb) (or angle shifted by {@code PI} if leftPlane is true)
+ */
+ private static double atan(double xa, double xb, boolean leftPlane) {
+ if (xa == 0.0) { // Matches +/- 0.0; return correct sign
+ return leftPlane ? copySign(Math.PI, xa) : xa;
+ }
+
+ final boolean negate;
+ if (xa < 0) {
+ // negative
+ xa = -xa;
+ xb = -xb;
+ negate = true;
+ } else {
+ negate = false;
+ }
+
+ if (xa > 1.633123935319537E16) { // Very large input
+ return (negate ^ leftPlane) ? (-Math.PI * F_1_2) : (Math.PI * F_1_2);
+ }
+
+ /* Estimate the closest tabulated arctan value, compute eps = xa-tangentTable */
+ final int idx;
+ if (xa < 1) {
+ idx = (int) (((-1.7168146928204136 * xa * xa + 8.0) * xa) + 0.5);
+ } else {
+ final double oneOverXa = 1 / xa;
+ idx =
+ (int)
+ (-((-1.7168146928204136 * oneOverXa * oneOverXa + 8.0) * oneOverXa)
+ + 13.07);
+ }
+
+ final double ttA = TANGENT_TABLE_A[idx];
+ final double ttB = TANGENT_TABLE_B[idx];
+
+ double epsA = xa - ttA;
+ double epsB = -(epsA - xa + ttA);
+ epsB += xb - ttB;
+
+ double temp = epsA + epsB;
+ epsB = -(temp - epsA - epsB);
+ epsA = temp;
+
+ /* Compute eps = eps / (1.0 + xa*tangent) */
+ temp = xa * HEX_40000000;
+ double ya = xa + temp - temp;
+ double yb = xb + xa - ya;
+ xa = ya;
+ xb += yb;
+
+ // if (idx > 8 || idx == 0)
+ if (idx == 0) {
+ /* If the slope of the arctan is gentle enough (< 0.45), this approximation will suffice */
+ // double denom = 1.0 / (1.0 + xa*tangentTableA[idx] + xb*tangentTableA[idx] +
+ // xa*tangentTableB[idx] + xb*tangentTableB[idx]);
+ final double denom = 1d / (1d + (xa + xb) * (ttA + ttB));
+ // double denom = 1.0 / (1.0 + xa*tangentTableA[idx]);
+ ya = epsA * denom;
+ yb = epsB * denom;
+ } else {
+ double temp2 = xa * ttA;
+ double za = 1d + temp2;
+ double zb = -(za - 1d - temp2);
+ temp2 = xb * ttA + xa * ttB;
+ temp = za + temp2;
+ zb += -(temp - za - temp2);
+ za = temp;
+
+ zb += xb * ttB;
+ ya = epsA / za;
+
+ temp = ya * HEX_40000000;
+ final double yaa = (ya + temp) - temp;
+ final double yab = ya - yaa;
+
+ temp = za * HEX_40000000;
+ final double zaa = (za + temp) - temp;
+ final double zab = za - zaa;
+
+ /* Correct for rounding in division */
+ yb = (epsA - yaa * zaa - yaa * zab - yab * zaa - yab * zab) / za;
+
+ yb += -epsA * zb / za / za;
+ yb += epsB / za;
+ }
+
+ epsA = ya;
+ epsB = yb;
+
+ /* Evaluate polynomial */
+ final double epsA2 = epsA * epsA;
+
+ /*
+ yb = -0.09001346640161823;
+ yb = yb * epsA2 + 0.11110718400605211;
+ yb = yb * epsA2 + -0.1428571349122913;
+ yb = yb * epsA2 + 0.19999999999273194;
+ yb = yb * epsA2 + -0.33333333333333093;
+ yb = yb * epsA2 * epsA;
+ */
+
+ yb = 0.07490822288864472;
+ yb = yb * epsA2 - 0.09088450866185192;
+ yb = yb * epsA2 + 0.11111095942313305;
+ yb = yb * epsA2 - 0.1428571423679182;
+ yb = yb * epsA2 + 0.19999999999923582;
+ yb = yb * epsA2 - 0.33333333333333287;
+ yb = yb * epsA2 * epsA;
+
+ ya = epsA;
+
+ temp = ya + yb;
+ yb = -(temp - ya - yb);
+ ya = temp;
+
+ /* Add in effect of epsB. atan'(x) = 1/(1+x^2) */
+ yb += epsB / (1d + epsA * epsA);
+
+ final double eighths = EIGHTHS[idx];
+
+ // result = yb + eighths[idx] + ya;
+ double za = eighths + ya;
+ double zb = -(za - eighths - ya);
+ temp = za + yb;
+ zb += -(temp - za - yb);
+ za = temp;
+
+ double result = za + zb;
+
+ if (leftPlane) {
+ // Result is in the left plane
+ final double resultb = -(result - za - zb);
+ final double pia = 1.5707963267948966 * 2;
+ final double pib = 6.123233995736766E-17 * 2;
+
+ za = pia - result;
+ zb = -(za - pia + result);
+ zb += pib - resultb;
+
+ result = za + zb;
+ }
+
+ if (negate ^ leftPlane) {
+ result = -result;
+ }
+
+ return result;
+ }
+
+ /**
+ * Two arguments arctangent function
+ *
+ * @param y ordinate
+ * @param x abscissa
+ * @return phase angle of point (x,y) between {@code -PI} and {@code PI}
+ */
+ public static double atan2(double y, double x) {
+ if (x != x || y != y) {
+ return Double.NaN;
+ }
+
+ if (y == 0) {
+ final double result = x * y;
+ final double invx = 1d / x;
+ final double invy = 1d / y;
+
+ if (invx == 0) { // X is infinite
+ if (x > 0) {
+ return y; // return +/- 0.0
+ } else {
+ return copySign(Math.PI, y);
+ }
+ }
+
+ if (x < 0 || invx < 0) {
+ if (y < 0 || invy < 0) {
+ return -Math.PI;
+ } else {
+ return Math.PI;
+ }
+ } else {
+ return result;
+ }
+ }
+
+ // y cannot now be zero
+
+ if (y == Double.POSITIVE_INFINITY) {
+ if (x == Double.POSITIVE_INFINITY) {
+ return Math.PI * F_1_4;
+ }
+
+ if (x == Double.NEGATIVE_INFINITY) {
+ return Math.PI * F_3_4;
+ }
+
+ return Math.PI * F_1_2;
+ }
+
+ if (y == Double.NEGATIVE_INFINITY) {
+ if (x == Double.POSITIVE_INFINITY) {
+ return -Math.PI * F_1_4;
+ }
+
+ if (x == Double.NEGATIVE_INFINITY) {
+ return -Math.PI * F_3_4;
+ }
+
+ return -Math.PI * F_1_2;
+ }
+
+ if (x == Double.POSITIVE_INFINITY) {
+ if (y > 0 || 1 / y > 0) {
+ return 0d;
+ }
+
+ if (y < 0 || 1 / y < 0) {
+ return -0d;
+ }
+ }
+
+ if (x == Double.NEGATIVE_INFINITY) {
+ if (y > 0.0 || 1 / y > 0.0) {
+ return Math.PI;
+ }
+
+ if (y < 0 || 1 / y < 0) {
+ return -Math.PI;
+ }
+ }
+
+ // Neither y nor x can be infinite or NAN here
+
+ if (x == 0) {
+ if (y > 0 || 1 / y > 0) {
+ return Math.PI * F_1_2;
+ }
+
+ if (y < 0 || 1 / y < 0) {
+ return -Math.PI * F_1_2;
+ }
+ }
+
+ // Compute ratio r = y/x
+ final double r = y / x;
+ if (Double.isInfinite(r)) { // bypass calculations that can create NaN
+ return atan(r, 0, x < 0);
+ }
+
+ double ra = doubleHighPart(r);
+ double rb = r - ra;
+
+ // Split x
+ final double xa = doubleHighPart(x);
+ final double xb = x - xa;
+
+ rb += (y - ra * xa - ra * xb - rb * xa - rb * xb) / x;
+
+ final double temp = ra + rb;
+ rb = -(temp - ra - rb);
+ ra = temp;
+
+ if (ra == 0) { // Fix up the sign so atan works correctly
+ ra = copySign(0d, y);
+ }
+
+ // Call atan
+ final double result = atan(ra, rb, x < 0);
+
+ return result;
+ }
+
+ /**
+ * Compute the arc sine of a number.
+ *
+ * @param x number on which evaluation is done
+ * @return arc sine of x
+ */
+ public static double asin(double x) {
+ if (x != x) {
+ return Double.NaN;
+ }
+
+ if (x > 1.0 || x < -1.0) {
+ return Double.NaN;
+ }
+
+ if (x == 1.0) {
+ return Math.PI / 2.0;
+ }
+
+ if (x == -1.0) {
+ return -Math.PI / 2.0;
+ }
+
+ if (x == 0.0) { // Matches +/- 0.0; return correct sign
+ return x;
+ }
+
+ /* Compute asin(x) = atan(x/sqrt(1-x*x)) */
+
+ /* Split x */
+ double temp = x * HEX_40000000;
+ final double xa = x + temp - temp;
+ final double xb = x - xa;
+
+ /* Square it */
+ double ya = xa * xa;
+ double yb = xa * xb * 2.0 + xb * xb;
+
+ /* Subtract from 1 */
+ ya = -ya;
+ yb = -yb;
+
+ double za = 1.0 + ya;
+ double zb = -(za - 1.0 - ya);
+
+ temp = za + yb;
+ zb += -(temp - za - yb);
+ za = temp;
+
+ /* Square root */
+ double y;
+ y = sqrt(za);
+ temp = y * HEX_40000000;
+ ya = y + temp - temp;
+ yb = y - ya;
+
+ /* Extend precision of sqrt */
+ yb += (za - ya * ya - 2 * ya * yb - yb * yb) / (2.0 * y);
+
+ /* Contribution of zb to sqrt */
+ double dx = zb / (2.0 * y);
+
+ // Compute ratio r = x/y
+ double r = x / y;
+ temp = r * HEX_40000000;
+ double ra = r + temp - temp;
+ double rb = r - ra;
+
+ rb += (x - ra * ya - ra * yb - rb * ya - rb * yb) / y; // Correct for rounding in division
+ rb += -x * dx / y / y; // Add in effect additional bits of sqrt.
+
+ temp = ra + rb;
+ rb = -(temp - ra - rb);
+ ra = temp;
+
+ return atan(ra, rb, false);
+ }
+
+ /**
+ * Compute the arc cosine of a number.
+ *
+ * @param x number on which evaluation is done
+ * @return arc cosine of x
+ */
+ public static double acos(double x) {
+ if (x != x) {
+ return Double.NaN;
+ }
+
+ if (x > 1.0 || x < -1.0) {
+ return Double.NaN;
+ }
+
+ if (x == -1.0) {
+ return Math.PI;
+ }
+
+ if (x == 1.0) {
+ return 0.0;
+ }
+
+ if (x == 0) {
+ return Math.PI / 2.0;
+ }
+
+ /* Compute acos(x) = atan(sqrt(1-x*x)/x) */
+
+ /* Split x */
+ double temp = x * HEX_40000000;
+ final double xa = x + temp - temp;
+ final double xb = x - xa;
+
+ /* Square it */
+ double ya = xa * xa;
+ double yb = xa * xb * 2.0 + xb * xb;
+
+ /* Subtract from 1 */
+ ya = -ya;
+ yb = -yb;
+
+ double za = 1.0 + ya;
+ double zb = -(za - 1.0 - ya);
+
+ temp = za + yb;
+ zb += -(temp - za - yb);
+ za = temp;
+
+ /* Square root */
+ double y = sqrt(za);
+ temp = y * HEX_40000000;
+ ya = y + temp - temp;
+ yb = y - ya;
+
+ /* Extend precision of sqrt */
+ yb += (za - ya * ya - 2 * ya * yb - yb * yb) / (2.0 * y);
+
+ /* Contribution of zb to sqrt */
+ yb += zb / (2.0 * y);
+ y = ya + yb;
+ yb = -(y - ya - yb);
+
+ // Compute ratio r = y/x
+ double r = y / x;
+
+ // Did r overflow?
+ if (Double.isInfinite(r)) { // x is effectively zero
+ return Math.PI / 2; // so return the appropriate value
+ }
+
+ double ra = doubleHighPart(r);
+ double rb = r - ra;
+
+ rb += (y - ra * xa - ra * xb - rb * xa - rb * xb) / x; // Correct for rounding in division
+ rb += yb / x; // Add in effect additional bits of sqrt.
+
+ temp = ra + rb;
+ rb = -(temp - ra - rb);
+ ra = temp;
+
+ return atan(ra, rb, x < 0);
+ }
+
+ /**
+ * Compute the cubic root of a number.
+ *
+ * @param x number on which evaluation is done
+ * @return cubic root of x
+ */
+ public static double cbrt(double x) {
+ /* Convert input double to bits */
+ long inbits = Double.doubleToRawLongBits(x);
+ int exponent = (int) ((inbits >> 52) & 0x7ff) - 1023;
+ boolean subnormal = false;
+
+ if (exponent == -1023) {
+ if (x == 0) {
+ return x;
+ }
+
+ /* Subnormal, so normalize */
+ subnormal = true;
+ x *= 1.8014398509481984E16; // 2^54
+ inbits = Double.doubleToRawLongBits(x);
+ exponent = (int) ((inbits >> 52) & 0x7ff) - 1023;
+ }
+
+ if (exponent == 1024) {
+ // Nan or infinity. Don't care which.
+ return x;
+ }
+
+ /* Divide the exponent by 3 */
+ int exp3 = exponent / 3;
+
+ /* p2 will be the nearest power of 2 to x with its exponent divided by 3 */
+ double p2 =
+ Double.longBitsToDouble(
+ (inbits & 0x8000000000000000L) | (long) (((exp3 + 1023) & 0x7ff)) << 52);
+
+ /* This will be a number between 1 and 2 */
+ final double mant =
+ Double.longBitsToDouble((inbits & 0x000fffffffffffffL) | 0x3ff0000000000000L);
+
+ /* Estimate the cube root of mant by polynomial */
+ double est = -0.010714690733195933;
+ est = est * mant + 0.0875862700108075;
+ est = est * mant + -0.3058015757857271;
+ est = est * mant + 0.7249995199969751;
+ est = est * mant + 0.5039018405998233;
+
+ est *= CBRTTWO[exponent % 3 + 2];
+
+ // est should now be good to about 15 bits of precision. Do 2 rounds of
+ // Newton's method to get closer, this should get us full double precision
+ // Scale down x for the purpose of doing newtons method. This avoids over/under flows.
+ final double xs = x / (p2 * p2 * p2);
+ est += (xs - est * est * est) / (3 * est * est);
+ est += (xs - est * est * est) / (3 * est * est);
+
+ // Do one round of Newton's method in extended precision to get the last bit right.
+ double temp = est * HEX_40000000;
+ double ya = est + temp - temp;
+ double yb = est - ya;
+
+ double za = ya * ya;
+ double zb = ya * yb * 2.0 + yb * yb;
+ temp = za * HEX_40000000;
+ double temp2 = za + temp - temp;
+ zb += za - temp2;
+ za = temp2;
+
+ zb = za * yb + ya * zb + zb * yb;
+ za *= ya;
+
+ double na = xs - za;
+ double nb = -(na - xs + za);
+ nb -= zb;
+
+ est += (na + nb) / (3 * est * est);
+
+ /* Scale by a power of two, so this is exact. */
+ est *= p2;
+
+ if (subnormal) {
+ est *= 3.814697265625E-6; // 2^-18
+ }
+
+ return est;
+ }
+
+ /**
+ * Convert degrees to radians, with error of less than 0.5 ULP
+ *
+ * @param x angle in degrees
+ * @return x converted into radians
+ */
+ public static double toRadians(double x) {
+ if (Double.isInfinite(x) || x == 0.0) { // Matches +/- 0.0; return correct sign
+ return x;
+ }
+
+ // These are PI/180 split into high and low order bits
+ final double facta = 0.01745329052209854;
+ final double factb = 1.997844754509471E-9;
+
+ double xa = doubleHighPart(x);
+ double xb = x - xa;
+
+ double result = xb * factb + xb * facta + xa * factb + xa * facta;
+ if (result == 0) {
+ result *= x; // ensure correct sign if calculation underflows
+ }
+ return result;
+ }
+
+ /**
+ * Convert radians to degrees, with error of less than 0.5 ULP
+ *
+ * @param x angle in radians
+ * @return x converted into degrees
+ */
+ public static double toDegrees(double x) {
+ if (Double.isInfinite(x) || x == 0.0) { // Matches +/- 0.0; return correct sign
+ return x;
+ }
+
+ // These are 180/PI split into high and low order bits
+ final double facta = 57.2957763671875;
+ final double factb = 3.145894820876798E-6;
+
+ double xa = doubleHighPart(x);
+ double xb = x - xa;
+
+ return xb * factb + xb * facta + xa * factb + xa * facta;
+ }
+
+ /**
+ * Absolute value.
+ *
+ * @param x number from which absolute value is requested
+ * @return abs(x)
+ */
+ public static int abs(final int x) {
+ final int i = x >>> 31;
+ return (x ^ (~i + 1)) + i;
+ }
+
+ /**
+ * Absolute value.
+ *
+ * @param x number from which absolute value is requested
+ * @return abs(x)
+ */
+ public static long abs(final long x) {
+ final long l = x >>> 63;
+ // l is one if x negative zero else
+ // ~l+1 is zero if x is positive, -1 if x is negative
+ // x^(~l+1) is x is x is positive, ~x if x is negative
+ // add around
+ return (x ^ (~l + 1)) + l;
+ }
+
+ /**
+ * Absolute value.
+ *
+ * @param x number from which absolute value is requested
+ * @return abs(x)
+ */
+ public static float abs(final float x) {
+ return Float.intBitsToFloat(MASK_NON_SIGN_INT & Float.floatToRawIntBits(x));
+ }
+
+ /**
+ * Absolute value.
+ *
+ * @param x number from which absolute value is requested
+ * @return abs(x)
+ */
+ public static double abs(double x) {
+ return Double.longBitsToDouble(MASK_NON_SIGN_LONG & Double.doubleToRawLongBits(x));
+ }
+
+ /**
+ * Compute least significant bit (Unit in Last Position) for a number.
+ *
+ * @param x number from which ulp is requested
+ * @return ulp(x)
+ */
+ public static double ulp(double x) {
+ if (Double.isInfinite(x)) {
+ return Double.POSITIVE_INFINITY;
+ }
+ return abs(x - Double.longBitsToDouble(Double.doubleToRawLongBits(x) ^ 1));
+ }
+
+ /**
+ * Compute least significant bit (Unit in Last Position) for a number.
+ *
+ * @param x number from which ulp is requested
+ * @return ulp(x)
+ */
+ public static float ulp(float x) {
+ if (Float.isInfinite(x)) {
+ return Float.POSITIVE_INFINITY;
+ }
+ return abs(x - Float.intBitsToFloat(Float.floatToIntBits(x) ^ 1));
+ }
+
+ /**
+ * Multiply a double number by a power of 2.
+ *
+ * @param d number to multiply
+ * @param n power of 2
+ * @return d &times; 2<sup>n</sup>
+ */
+ public static double scalb(final double d, final int n) {
+
+ // first simple and fast handling when 2^n can be represented using normal numbers
+ if ((n > -1023) && (n < 1024)) {
+ return d * Double.longBitsToDouble(((long) (n + 1023)) << 52);
+ }
+
+ // handle special cases
+ if (Double.isNaN(d) || Double.isInfinite(d) || (d == 0)) {
+ return d;
+ }
+ if (n < -2098) {
+ return (d > 0) ? 0.0 : -0.0;
+ }
+ if (n > 2097) {
+ return (d > 0) ? Double.POSITIVE_INFINITY : Double.NEGATIVE_INFINITY;
+ }
+
+ // decompose d
+ final long bits = Double.doubleToRawLongBits(d);
+ final long sign = bits & 0x8000000000000000L;
+ int exponent = ((int) (bits >>> 52)) & 0x7ff;
+ long mantissa = bits & 0x000fffffffffffffL;
+
+ // compute scaled exponent
+ int scaledExponent = exponent + n;
+
+ if (n < 0) {
+ // we are really in the case n <= -1023
+ if (scaledExponent > 0) {
+ // both the input and the result are normal numbers, we only adjust the exponent
+ return Double.longBitsToDouble(sign | (((long) scaledExponent) << 52) | mantissa);
+ } else if (scaledExponent > -53) {
+ // the input is a normal number and the result is a subnormal number
+
+ // recover the hidden mantissa bit
+ mantissa |= 1L << 52;
+
+ // scales down complete mantissa, hence losing least significant bits
+ final long mostSignificantLostBit = mantissa & (1L << (-scaledExponent));
+ mantissa >>>= 1 - scaledExponent;
+ if (mostSignificantLostBit != 0) {
+ // we need to add 1 bit to round up the result
+ mantissa++;
+ }
+ return Double.longBitsToDouble(sign | mantissa);
+
+ } else {
+ // no need to compute the mantissa, the number scales down to 0
+ return (sign == 0L) ? 0.0 : -0.0;
+ }
+ } else {
+ // we are really in the case n >= 1024
+ if (exponent == 0) {
+
+ // the input number is subnormal, normalize it
+ while ((mantissa >>> 52) != 1) {
+ mantissa <<= 1;
+ --scaledExponent;
+ }
+ ++scaledExponent;
+ mantissa &= 0x000fffffffffffffL;
+
+ if (scaledExponent < 2047) {
+ return Double.longBitsToDouble(
+ sign | (((long) scaledExponent) << 52) | mantissa);
+ } else {
+ return (sign == 0L) ? Double.POSITIVE_INFINITY : Double.NEGATIVE_INFINITY;
+ }
+
+ } else if (scaledExponent < 2047) {
+ return Double.longBitsToDouble(sign | (((long) scaledExponent) << 52) | mantissa);
+ } else {
+ return (sign == 0L) ? Double.POSITIVE_INFINITY : Double.NEGATIVE_INFINITY;
+ }
+ }
+ }
+
+ /**
+ * Multiply a float number by a power of 2.
+ *
+ * @param f number to multiply
+ * @param n power of 2
+ * @return f &times; 2<sup>n</sup>
+ */
+ public static float scalb(final float f, final int n) {
+
+ // first simple and fast handling when 2^n can be represented using normal numbers
+ if ((n > -127) && (n < 128)) {
+ return f * Float.intBitsToFloat((n + 127) << 23);
+ }
+
+ // handle special cases
+ if (Float.isNaN(f) || Float.isInfinite(f) || (f == 0f)) {
+ return f;
+ }
+ if (n < -277) {
+ return (f > 0) ? 0.0f : -0.0f;
+ }
+ if (n > 276) {
+ return (f > 0) ? Float.POSITIVE_INFINITY : Float.NEGATIVE_INFINITY;
+ }
+
+ // decompose f
+ final int bits = Float.floatToIntBits(f);
+ final int sign = bits & 0x80000000;
+ int exponent = (bits >>> 23) & 0xff;
+ int mantissa = bits & 0x007fffff;
+
+ // compute scaled exponent
+ int scaledExponent = exponent + n;
+
+ if (n < 0) {
+ // we are really in the case n <= -127
+ if (scaledExponent > 0) {
+ // both the input and the result are normal numbers, we only adjust the exponent
+ return Float.intBitsToFloat(sign | (scaledExponent << 23) | mantissa);
+ } else if (scaledExponent > -24) {
+ // the input is a normal number and the result is a subnormal number
+
+ // recover the hidden mantissa bit
+ mantissa |= 1 << 23;
+
+ // scales down complete mantissa, hence losing least significant bits
+ final int mostSignificantLostBit = mantissa & (1 << (-scaledExponent));
+ mantissa >>>= 1 - scaledExponent;
+ if (mostSignificantLostBit != 0) {
+ // we need to add 1 bit to round up the result
+ mantissa++;
+ }
+ return Float.intBitsToFloat(sign | mantissa);
+
+ } else {
+ // no need to compute the mantissa, the number scales down to 0
+ return (sign == 0) ? 0.0f : -0.0f;
+ }
+ } else {
+ // we are really in the case n >= 128
+ if (exponent == 0) {
+
+ // the input number is subnormal, normalize it
+ while ((mantissa >>> 23) != 1) {
+ mantissa <<= 1;
+ --scaledExponent;
+ }
+ ++scaledExponent;
+ mantissa &= 0x007fffff;
+
+ if (scaledExponent < 255) {
+ return Float.intBitsToFloat(sign | (scaledExponent << 23) | mantissa);
+ } else {
+ return (sign == 0) ? Float.POSITIVE_INFINITY : Float.NEGATIVE_INFINITY;
+ }
+
+ } else if (scaledExponent < 255) {
+ return Float.intBitsToFloat(sign | (scaledExponent << 23) | mantissa);
+ } else {
+ return (sign == 0) ? Float.POSITIVE_INFINITY : Float.NEGATIVE_INFINITY;
+ }
+ }
+ }
+
+ /**
+ * Get the next machine representable number after a number, moving in the direction of another
+ * number.
+ *
+ * <p>The ordering is as follows (increasing):
+ *
+ * <ul>
+ * <li>-INFINITY
+ * <li>-MAX_VALUE
+ * <li>-MIN_VALUE
+ * <li>-0.0
+ * <li>+0.0
+ * <li>+MIN_VALUE
+ * <li>+MAX_VALUE
+ * <li>+INFINITY
+ * <li>
+ * <p>If arguments compare equal, then the second argument is returned.
+ * <p>If {@code direction} is greater than {@code d}, the smallest machine representable
+ * number strictly greater than {@code d} is returned; if less, then the largest
+ * representable number strictly less than {@code d} is returned.
+ * <p>If {@code d} is infinite and direction does not bring it back to finite numbers, it
+ * is returned unchanged.
+ *
+ * @param d base number
+ * @param direction (the only important thing is whether {@code direction} is greater or smaller
+ * than {@code d})
+ * @return the next machine representable number in the specified direction
+ */
+ public static double nextAfter(double d, double direction) {
+
+ // handling of some important special cases
+ if (Double.isNaN(d) || Double.isNaN(direction)) {
+ return Double.NaN;
+ } else if (d == direction) {
+ return direction;
+ } else if (Double.isInfinite(d)) {
+ return (d < 0) ? -Double.MAX_VALUE : Double.MAX_VALUE;
+ } else if (d == 0) {
+ return (direction < 0) ? -Double.MIN_VALUE : Double.MIN_VALUE;
+ }
+ // special cases MAX_VALUE to infinity and MIN_VALUE to 0
+ // are handled just as normal numbers
+ // can use raw bits since already dealt with infinity and NaN
+ final long bits = Double.doubleToRawLongBits(d);
+ final long sign = bits & 0x8000000000000000L;
+ if ((direction < d) ^ (sign == 0L)) {
+ return Double.longBitsToDouble(sign | ((bits & 0x7fffffffffffffffL) + 1));
+ } else {
+ return Double.longBitsToDouble(sign | ((bits & 0x7fffffffffffffffL) - 1));
+ }
+ }
+
+ /**
+ * Get the next machine representable number after a number, moving in the direction of another
+ * number.
+ *
+ * <p>The ordering is as follows (increasing):
+ *
+ * <ul>
+ * <li>-INFINITY
+ * <li>-MAX_VALUE
+ * <li>-MIN_VALUE
+ * <li>-0.0
+ * <li>+0.0
+ * <li>+MIN_VALUE
+ * <li>+MAX_VALUE
+ * <li>+INFINITY
+ * <li>
+ * <p>If arguments compare equal, then the second argument is returned.
+ * <p>If {@code direction} is greater than {@code f}, the smallest machine representable
+ * number strictly greater than {@code f} is returned; if less, then the largest
+ * representable number strictly less than {@code f} is returned.
+ * <p>If {@code f} is infinite and direction does not bring it back to finite numbers, it
+ * is returned unchanged.
+ *
+ * @param f base number
+ * @param direction (the only important thing is whether {@code direction} is greater or smaller
+ * than {@code f})
+ * @return the next machine representable number in the specified direction
+ */
+ public static float nextAfter(final float f, final double direction) {
+
+ // handling of some important special cases
+ if (Double.isNaN(f) || Double.isNaN(direction)) {
+ return Float.NaN;
+ } else if (f == direction) {
+ return (float) direction;
+ } else if (Float.isInfinite(f)) {
+ return (f < 0f) ? -Float.MAX_VALUE : Float.MAX_VALUE;
+ } else if (f == 0f) {
+ return (direction < 0) ? -Float.MIN_VALUE : Float.MIN_VALUE;
+ }
+ // special cases MAX_VALUE to infinity and MIN_VALUE to 0
+ // are handled just as normal numbers
+
+ final int bits = Float.floatToIntBits(f);
+ final int sign = bits & 0x80000000;
+ if ((direction < f) ^ (sign == 0)) {
+ return Float.intBitsToFloat(sign | ((bits & 0x7fffffff) + 1));
+ } else {
+ return Float.intBitsToFloat(sign | ((bits & 0x7fffffff) - 1));
+ }
+ }
+
+ /**
+ * Get the largest whole number smaller than x.
+ *
+ * @param x number from which floor is requested
+ * @return a double number f such that f is an integer f <= x < f + 1.0
+ */
+ public static double floor(double x) {
+ long y;
+
+ if (x != x) { // NaN
+ return x;
+ }
+
+ if (x >= TWO_POWER_52 || x <= -TWO_POWER_52) {
+ return x;
+ }
+
+ y = (long) x;
+ if (x < 0 && y != x) {
+ y--;
+ }
+
+ if (y == 0) {
+ return x * y;
+ }
+
+ return y;
+ }
+
+ /**
+ * Get the smallest whole number larger than x.
+ *
+ * @param x number from which ceil is requested
+ * @return a double number c such that c is an integer c - 1.0 < x <= c
+ */
+ public static double ceil(double x) {
+ double y;
+
+ if (x != x) { // NaN
+ return x;
+ }
+
+ y = floor(x);
+ if (y == x) {
+ return y;
+ }
+
+ y += 1.0;
+
+ if (y == 0) {
+ return x * y;
+ }
+
+ return y;
+ }
+
+ /**
+ * Get the whole number that is the nearest to x, or the even one if x is exactly half way
+ * between two integers.
+ *
+ * @param x number from which nearest whole number is requested
+ * @return a double number r such that r is an integer r - 0.5 <= x <= r + 0.5
+ */
+ public static double rint(double x) {
+ double y = floor(x);
+ double d = x - y;
+
+ if (d > 0.5) {
+ if (y == -1.0) {
+ return -0.0; // Preserve sign of operand
+ }
+ return y + 1.0;
+ }
+ if (d < 0.5) {
+ return y;
+ }
+
+ /* half way, round to even */
+ long z = (long) y;
+ return (z & 1) == 0 ? y : y + 1.0;
+ }
+
+ /**
+ * Get the closest long to x.
+ *
+ * @param x number from which closest long is requested
+ * @return closest long to x
+ */
+ public static long round(double x) {
+ return (long) floor(x + 0.5);
+ }
+
+ /**
+ * Get the closest int to x.
+ *
+ * @param x number from which closest int is requested
+ * @return closest int to x
+ */
+ public static int round(final float x) {
+ return (int) floor(x + 0.5f);
+ }
+
+ /**
+ * Compute the minimum of two values
+ *
+ * @param a first value
+ * @param b second value
+ * @return a if a is lesser or equal to b, b otherwise
+ */
+ public static int min(final int a, final int b) {
+ return (a <= b) ? a : b;
+ }
+
+ /**
+ * Compute the minimum of two values
+ *
+ * @param a first value
+ * @param b second value
+ * @return a if a is lesser or equal to b, b otherwise
+ */
+ public static long min(final long a, final long b) {
+ return (a <= b) ? a : b;
+ }
+
+ /**
+ * Compute the minimum of two values
+ *
+ * @param a first value
+ * @param b second value
+ * @return a if a is lesser or equal to b, b otherwise
+ */
+ public static float min(final float a, final float b) {
+ if (a > b) {
+ return b;
+ }
+ if (a < b) {
+ return a;
+ }
+ /* if either arg is NaN, return NaN */
+ if (a != b) {
+ return Float.NaN;
+ }
+ /* min(+0.0,-0.0) == -0.0 */
+ /* 0x80000000 == Float.floatToRawIntBits(-0.0d) */
+ int bits = Float.floatToRawIntBits(a);
+ if (bits == 0x80000000) {
+ return a;
+ }
+ return b;
+ }
+
+ /**
+ * Compute the minimum of two values
+ *
+ * @param a first value
+ * @param b second value
+ * @return a if a is lesser or equal to b, b otherwise
+ */
+ public static double min(final double a, final double b) {
+ if (a > b) {
+ return b;
+ }
+ if (a < b) {
+ return a;
+ }
+ /* if either arg is NaN, return NaN */
+ if (a != b) {
+ return Double.NaN;
+ }
+ /* min(+0.0,-0.0) == -0.0 */
+ /* 0x8000000000000000L == Double.doubleToRawLongBits(-0.0d) */
+ long bits = Double.doubleToRawLongBits(a);
+ if (bits == 0x8000000000000000L) {
+ return a;
+ }
+ return b;
+ }
+
+ /**
+ * Compute the maximum of two values
+ *
+ * @param a first value
+ * @param b second value
+ * @return b if a is lesser or equal to b, a otherwise
+ */
+ public static int max(final int a, final int b) {
+ return (a <= b) ? b : a;
+ }
+
+ /**
+ * Compute the maximum of two values
+ *
+ * @param a first value
+ * @param b second value
+ * @return b if a is lesser or equal to b, a otherwise
+ */
+ public static long max(final long a, final long b) {
+ return (a <= b) ? b : a;
+ }
+
+ /**
+ * Compute the maximum of two values
+ *
+ * @param a first value
+ * @param b second value
+ * @return b if a is lesser or equal to b, a otherwise
+ */
+ public static float max(final float a, final float b) {
+ if (a > b) {
+ return a;
+ }
+ if (a < b) {
+ return b;
+ }
+ /* if either arg is NaN, return NaN */
+ if (a != b) {
+ return Float.NaN;
+ }
+ /* min(+0.0,-0.0) == -0.0 */
+ /* 0x80000000 == Float.floatToRawIntBits(-0.0d) */
+ int bits = Float.floatToRawIntBits(a);
+ if (bits == 0x80000000) {
+ return b;
+ }
+ return a;
+ }
+
+ /**
+ * Compute the maximum of two values
+ *
+ * @param a first value
+ * @param b second value
+ * @return b if a is lesser or equal to b, a otherwise
+ */
+ public static double max(final double a, final double b) {
+ if (a > b) {
+ return a;
+ }
+ if (a < b) {
+ return b;
+ }
+ /* if either arg is NaN, return NaN */
+ if (a != b) {
+ return Double.NaN;
+ }
+ /* min(+0.0,-0.0) == -0.0 */
+ /* 0x8000000000000000L == Double.doubleToRawLongBits(-0.0d) */
+ long bits = Double.doubleToRawLongBits(a);
+ if (bits == 0x8000000000000000L) {
+ return b;
+ }
+ return a;
+ }
+
+ /**
+ * Returns the hypotenuse of a triangle with sides {@code x} and {@code y} -
+ * sqrt(<i>x</i><sup>2</sup>&nbsp;+<i>y</i><sup>2</sup>)<br>
+ * avoiding intermediate overflow or underflow.
+ *
+ * <ul>
+ * <li>If either argument is infinite, then the result is positive infinity.
+ * <li>else, if either argument is NaN then the result is NaN.
+ * </ul>
+ *
+ * @param x a value
+ * @param y a value
+ * @return sqrt(<i>x</i><sup>2</sup>&nbsp;+<i>y</i><sup>2</sup>)
+ */
+ public static double hypot(final double x, final double y) {
+ if (Double.isInfinite(x) || Double.isInfinite(y)) {
+ return Double.POSITIVE_INFINITY;
+ } else if (Double.isNaN(x) || Double.isNaN(y)) {
+ return Double.NaN;
+ } else {
+
+ final int expX = getExponent(x);
+ final int expY = getExponent(y);
+ if (expX > expY + 27) {
+ // y is neglectible with respect to x
+ return abs(x);
+ } else if (expY > expX + 27) {
+ // x is neglectible with respect to y
+ return abs(y);
+ } else {
+
+ // find an intermediate scale to avoid both overflow and underflow
+ final int middleExp = (expX + expY) / 2;
+
+ // scale parameters without losing precision
+ final double scaledX = scalb(x, -middleExp);
+ final double scaledY = scalb(y, -middleExp);
+
+ // compute scaled hypotenuse
+ final double scaledH = sqrt(scaledX * scaledX + scaledY * scaledY);
+
+ // remove scaling
+ return scalb(scaledH, middleExp);
+ }
+ }
+ }
+
+ /**
+ * Computes the remainder as prescribed by the IEEE 754 standard. The remainder value is
+ * mathematically equal to {@code x - y*n} where {@code n} is the mathematical integer closest
+ * to the exact mathematical value of the quotient {@code x/y}. If two mathematical integers are
+ * equally close to {@code x/y} then {@code n} is the integer that is even.
+ *
+ * <p>
+ *
+ * <ul>
+ * <li>If either operand is NaN, the result is NaN.
+ * <li>If the result is not NaN, the sign of the result equals the sign of the dividend.
+ * <li>If the dividend is an infinity, or the divisor is a zero, or both, the result is NaN.
+ * <li>If the dividend is finite and the divisor is an infinity, the result equals the
+ * dividend.
+ * <li>If the dividend is a zero and the divisor is finite, the result equals the dividend.
+ * </ul>
+ *
+ * <p><b>Note:</b> this implementation currently delegates to {@link StrictMath#IEEEremainder}
+ *
+ * @param dividend the number to be divided
+ * @param divisor the number by which to divide
+ * @return the remainder, rounded
+ */
+ public static double IEEEremainder(double dividend, double divisor) {
+ return StrictMath.IEEEremainder(dividend, divisor); // TODO provide our own implementation
+ }
+
+ /**
+ * Convert a long to interger, detecting overflows
+ *
+ * @param n number to convert to int
+ * @return integer with same valie as n if no overflows occur
+ * @exception MathArithmeticException if n cannot fit into an int
+ * @since 3.4
+ */
+ public static int toIntExact(final long n) throws MathArithmeticException {
+ if (n < Integer.MIN_VALUE || n > Integer.MAX_VALUE) {
+ throw new MathArithmeticException(LocalizedFormats.OVERFLOW);
+ }
+ return (int) n;
+ }
+
+ /**
+ * Increment a number, detecting overflows.
+ *
+ * @param n number to increment
+ * @return n+1 if no overflows occur
+ * @exception MathArithmeticException if an overflow occurs
+ * @since 3.4
+ */
+ public static int incrementExact(final int n) throws MathArithmeticException {
+
+ if (n == Integer.MAX_VALUE) {
+ throw new MathArithmeticException(LocalizedFormats.OVERFLOW_IN_ADDITION, n, 1);
+ }
+
+ return n + 1;
+ }
+
+ /**
+ * Increment a number, detecting overflows.
+ *
+ * @param n number to increment
+ * @return n+1 if no overflows occur
+ * @exception MathArithmeticException if an overflow occurs
+ * @since 3.4
+ */
+ public static long incrementExact(final long n) throws MathArithmeticException {
+
+ if (n == Long.MAX_VALUE) {
+ throw new MathArithmeticException(LocalizedFormats.OVERFLOW_IN_ADDITION, n, 1);
+ }
+
+ return n + 1;
+ }
+
+ /**
+ * Decrement a number, detecting overflows.
+ *
+ * @param n number to decrement
+ * @return n-1 if no overflows occur
+ * @exception MathArithmeticException if an overflow occurs
+ * @since 3.4
+ */
+ public static int decrementExact(final int n) throws MathArithmeticException {
+
+ if (n == Integer.MIN_VALUE) {
+ throw new MathArithmeticException(LocalizedFormats.OVERFLOW_IN_SUBTRACTION, n, 1);
+ }
+
+ return n - 1;
+ }
+
+ /**
+ * Decrement a number, detecting overflows.
+ *
+ * @param n number to decrement
+ * @return n-1 if no overflows occur
+ * @exception MathArithmeticException if an overflow occurs
+ * @since 3.4
+ */
+ public static long decrementExact(final long n) throws MathArithmeticException {
+
+ if (n == Long.MIN_VALUE) {
+ throw new MathArithmeticException(LocalizedFormats.OVERFLOW_IN_SUBTRACTION, n, 1);
+ }
+
+ return n - 1;
+ }
+
+ /**
+ * Add two numbers, detecting overflows.
+ *
+ * @param a first number to add
+ * @param b second number to add
+ * @return a+b if no overflows occur
+ * @exception MathArithmeticException if an overflow occurs
+ * @since 3.4
+ */
+ public static int addExact(final int a, final int b) throws MathArithmeticException {
+
+ // compute sum
+ final int sum = a + b;
+
+ // check for overflow
+ if ((a ^ b) >= 0 && (sum ^ b) < 0) {
+ throw new MathArithmeticException(LocalizedFormats.OVERFLOW_IN_ADDITION, a, b);
+ }
+
+ return sum;
+ }
+
+ /**
+ * Add two numbers, detecting overflows.
+ *
+ * @param a first number to add
+ * @param b second number to add
+ * @return a+b if no overflows occur
+ * @exception MathArithmeticException if an overflow occurs
+ * @since 3.4
+ */
+ public static long addExact(final long a, final long b) throws MathArithmeticException {
+
+ // compute sum
+ final long sum = a + b;
+
+ // check for overflow
+ if ((a ^ b) >= 0 && (sum ^ b) < 0) {
+ throw new MathArithmeticException(LocalizedFormats.OVERFLOW_IN_ADDITION, a, b);
+ }
+
+ return sum;
+ }
+
+ /**
+ * Subtract two numbers, detecting overflows.
+ *
+ * @param a first number
+ * @param b second number to subtract from a
+ * @return a-b if no overflows occur
+ * @exception MathArithmeticException if an overflow occurs
+ * @since 3.4
+ */
+ public static int subtractExact(final int a, final int b) {
+
+ // compute subtraction
+ final int sub = a - b;
+
+ // check for overflow
+ if ((a ^ b) < 0 && (sub ^ b) >= 0) {
+ throw new MathArithmeticException(LocalizedFormats.OVERFLOW_IN_SUBTRACTION, a, b);
+ }
+
+ return sub;
+ }
+
+ /**
+ * Subtract two numbers, detecting overflows.
+ *
+ * @param a first number
+ * @param b second number to subtract from a
+ * @return a-b if no overflows occur
+ * @exception MathArithmeticException if an overflow occurs
+ * @since 3.4
+ */
+ public static long subtractExact(final long a, final long b) {
+
+ // compute subtraction
+ final long sub = a - b;
+
+ // check for overflow
+ if ((a ^ b) < 0 && (sub ^ b) >= 0) {
+ throw new MathArithmeticException(LocalizedFormats.OVERFLOW_IN_SUBTRACTION, a, b);
+ }
+
+ return sub;
+ }
+
+ /**
+ * Multiply two numbers, detecting overflows.
+ *
+ * @param a first number to multiply
+ * @param b second number to multiply
+ * @return a*b if no overflows occur
+ * @exception MathArithmeticException if an overflow occurs
+ * @since 3.4
+ */
+ public static int multiplyExact(final int a, final int b) {
+ if (((b > 0) && (a > Integer.MAX_VALUE / b || a < Integer.MIN_VALUE / b))
+ || ((b < -1) && (a > Integer.MIN_VALUE / b || a < Integer.MAX_VALUE / b))
+ || ((b == -1) && (a == Integer.MIN_VALUE))) {
+ throw new MathArithmeticException(LocalizedFormats.OVERFLOW_IN_MULTIPLICATION, a, b);
+ }
+ return a * b;
+ }
+
+ /**
+ * Multiply two numbers, detecting overflows.
+ *
+ * @param a first number to multiply
+ * @param b second number to multiply
+ * @return a*b if no overflows occur
+ * @exception MathArithmeticException if an overflow occurs
+ * @since 3.4
+ */
+ public static long multiplyExact(final long a, final long b) {
+ if (((b > 0l) && (a > Long.MAX_VALUE / b || a < Long.MIN_VALUE / b))
+ || ((b < -1l) && (a > Long.MIN_VALUE / b || a < Long.MAX_VALUE / b))
+ || ((b == -1l) && (a == Long.MIN_VALUE))) {
+ throw new MathArithmeticException(LocalizedFormats.OVERFLOW_IN_MULTIPLICATION, a, b);
+ }
+ return a * b;
+ }
+
+ /**
+ * Finds q such that a = q b + r with 0 <= r < b if b > 0 and b < r <= 0 if b < 0.
+ *
+ * <p>This methods returns the same value as integer division when a and b are same signs, but
+ * returns a different value when they are opposite (i.e. q is negative).
+ *
+ * @param a dividend
+ * @param b divisor
+ * @return q such that a = q b + r with 0 <= r < b if b > 0 and b < r <= 0 if b < 0
+ * @exception MathArithmeticException if b == 0
+ * @see #floorMod(int, int)
+ * @since 3.4
+ */
+ public static int floorDiv(final int a, final int b) throws MathArithmeticException {
+
+ if (b == 0) {
+ throw new MathArithmeticException(LocalizedFormats.ZERO_DENOMINATOR);
+ }
+
+ final int m = a % b;
+ if ((a ^ b) >= 0 || m == 0) {
+ // a an b have same sign, or division is exact
+ return a / b;
+ } else {
+ // a and b have opposite signs and division is not exact
+ return (a / b) - 1;
+ }
+ }
+
+ /**
+ * Finds q such that a = q b + r with 0 <= r < b if b > 0 and b < r <= 0 if b < 0.
+ *
+ * <p>This methods returns the same value as integer division when a and b are same signs, but
+ * returns a different value when they are opposite (i.e. q is negative).
+ *
+ * @param a dividend
+ * @param b divisor
+ * @return q such that a = q b + r with 0 <= r < b if b > 0 and b < r <= 0 if b < 0
+ * @exception MathArithmeticException if b == 0
+ * @see #floorMod(long, long)
+ * @since 3.4
+ */
+ public static long floorDiv(final long a, final long b) throws MathArithmeticException {
+
+ if (b == 0l) {
+ throw new MathArithmeticException(LocalizedFormats.ZERO_DENOMINATOR);
+ }
+
+ final long m = a % b;
+ if ((a ^ b) >= 0l || m == 0l) {
+ // a an b have same sign, or division is exact
+ return a / b;
+ } else {
+ // a and b have opposite signs and division is not exact
+ return (a / b) - 1l;
+ }
+ }
+
+ /**
+ * Finds r such that a = q b + r with 0 <= r < b if b > 0 and b < r <= 0 if b < 0.
+ *
+ * <p>This methods returns the same value as integer modulo when a and b are same signs, but
+ * returns a different value when they are opposite (i.e. q is negative).
+ *
+ * @param a dividend
+ * @param b divisor
+ * @return r such that a = q b + r with 0 <= r < b if b > 0 and b < r <= 0 if b < 0
+ * @exception MathArithmeticException if b == 0
+ * @see #floorDiv(int, int)
+ * @since 3.4
+ */
+ public static int floorMod(final int a, final int b) throws MathArithmeticException {
+
+ if (b == 0) {
+ throw new MathArithmeticException(LocalizedFormats.ZERO_DENOMINATOR);
+ }
+
+ final int m = a % b;
+ if ((a ^ b) >= 0 || m == 0) {
+ // a an b have same sign, or division is exact
+ return m;
+ } else {
+ // a and b have opposite signs and division is not exact
+ return b + m;
+ }
+ }
+
+ /**
+ * Finds r such that a = q b + r with 0 <= r < b if b > 0 and b < r <= 0 if b < 0.
+ *
+ * <p>This methods returns the same value as integer modulo when a and b are same signs, but
+ * returns a different value when they are opposite (i.e. q is negative).
+ *
+ * @param a dividend
+ * @param b divisor
+ * @return r such that a = q b + r with 0 <= r < b if b > 0 and b < r <= 0 if b < 0
+ * @exception MathArithmeticException if b == 0
+ * @see #floorDiv(long, long)
+ * @since 3.4
+ */
+ public static long floorMod(final long a, final long b) {
+
+ if (b == 0l) {
+ throw new MathArithmeticException(LocalizedFormats.ZERO_DENOMINATOR);
+ }
+
+ final long m = a % b;
+ if ((a ^ b) >= 0l || m == 0l) {
+ // a an b have same sign, or division is exact
+ return m;
+ } else {
+ // a and b have opposite signs and division is not exact
+ return b + m;
+ }
+ }
+
+ /**
+ * Returns the first argument with the sign of the second argument. A NaN {@code sign} argument
+ * is treated as positive.
+ *
+ * @param magnitude the value to return
+ * @param sign the sign for the returned value
+ * @return the magnitude with the same sign as the {@code sign} argument
+ */
+ public static double copySign(double magnitude, double sign) {
+ // The highest order bit is going to be zero if the
+ // highest order bit of m and s is the same and one otherwise.
+ // So (m^s) will be positive if both m and s have the same sign
+ // and negative otherwise.
+ final long m = Double.doubleToRawLongBits(magnitude); // don't care about NaN
+ final long s = Double.doubleToRawLongBits(sign);
+ if ((m ^ s) >= 0) {
+ return magnitude;
+ }
+ return -magnitude; // flip sign
+ }
+
+ /**
+ * Returns the first argument with the sign of the second argument. A NaN {@code sign} argument
+ * is treated as positive.
+ *
+ * @param magnitude the value to return
+ * @param sign the sign for the returned value
+ * @return the magnitude with the same sign as the {@code sign} argument
+ */
+ public static float copySign(float magnitude, float sign) {
+ // The highest order bit is going to be zero if the
+ // highest order bit of m and s is the same and one otherwise.
+ // So (m^s) will be positive if both m and s have the same sign
+ // and negative otherwise.
+ final int m = Float.floatToRawIntBits(magnitude);
+ final int s = Float.floatToRawIntBits(sign);
+ if ((m ^ s) >= 0) {
+ return magnitude;
+ }
+ return -magnitude; // flip sign
+ }
+
+ /**
+ * Return the exponent of a double number, removing the bias.
+ *
+ * <p>For double numbers of the form 2<sup>x</sup>, the unbiased exponent is exactly x.
+ *
+ * @param d number from which exponent is requested
+ * @return exponent for d in IEEE754 representation, without bias
+ */
+ public static int getExponent(final double d) {
+ // NaN and Infinite will return 1024 anywho so can use raw bits
+ return (int) ((Double.doubleToRawLongBits(d) >>> 52) & 0x7ff) - 1023;
+ }
+
+ /**
+ * Return the exponent of a float number, removing the bias.
+ *
+ * <p>For float numbers of the form 2<sup>x</sup>, the unbiased exponent is exactly x.
+ *
+ * @param f number from which exponent is requested
+ * @return exponent for d in IEEE754 representation, without bias
+ */
+ public static int getExponent(final float f) {
+ // NaN and Infinite will return the same exponent anywho so can use raw bits
+ return ((Float.floatToRawIntBits(f) >>> 23) & 0xff) - 127;
+ }
+
+ /**
+ * Print out contents of arrays, and check the length.
+ *
+ * <p>used to generate the preset arrays originally.
+ *
+ * @param a unused
+ */
+ public static void main(String[] a) {
+ PrintStream out = System.out;
+ FastMathCalc.printarray(
+ out, "EXP_INT_TABLE_A", EXP_INT_TABLE_LEN, ExpIntTable.EXP_INT_TABLE_A);
+ FastMathCalc.printarray(
+ out, "EXP_INT_TABLE_B", EXP_INT_TABLE_LEN, ExpIntTable.EXP_INT_TABLE_B);
+ FastMathCalc.printarray(
+ out, "EXP_FRAC_TABLE_A", EXP_FRAC_TABLE_LEN, ExpFracTable.EXP_FRAC_TABLE_A);
+ FastMathCalc.printarray(
+ out, "EXP_FRAC_TABLE_B", EXP_FRAC_TABLE_LEN, ExpFracTable.EXP_FRAC_TABLE_B);
+ FastMathCalc.printarray(out, "LN_MANT", LN_MANT_LEN, lnMant.LN_MANT);
+ FastMathCalc.printarray(out, "SINE_TABLE_A", SINE_TABLE_LEN, SINE_TABLE_A);
+ FastMathCalc.printarray(out, "SINE_TABLE_B", SINE_TABLE_LEN, SINE_TABLE_B);
+ FastMathCalc.printarray(out, "COSINE_TABLE_A", SINE_TABLE_LEN, COSINE_TABLE_A);
+ FastMathCalc.printarray(out, "COSINE_TABLE_B", SINE_TABLE_LEN, COSINE_TABLE_B);
+ FastMathCalc.printarray(out, "TANGENT_TABLE_A", SINE_TABLE_LEN, TANGENT_TABLE_A);
+ FastMathCalc.printarray(out, "TANGENT_TABLE_B", SINE_TABLE_LEN, TANGENT_TABLE_B);
+ }
+
+ /** Enclose large data table in nested static class so it's only loaded on first access. */
+ private static class ExpIntTable {
+ /**
+ * Exponential evaluated at integer values, exp(x) = expIntTableA[x +
+ * EXP_INT_TABLE_MAX_INDEX] + expIntTableB[x+EXP_INT_TABLE_MAX_INDEX].
+ */
+ private static final double[] EXP_INT_TABLE_A;
+
+ /**
+ * Exponential evaluated at integer values, exp(x) = expIntTableA[x +
+ * EXP_INT_TABLE_MAX_INDEX] + expIntTableB[x+EXP_INT_TABLE_MAX_INDEX]
+ */
+ private static final double[] EXP_INT_TABLE_B;
+
+ static {
+ if (RECOMPUTE_TABLES_AT_RUNTIME) {
+ EXP_INT_TABLE_A = new double[FastMath.EXP_INT_TABLE_LEN];
+ EXP_INT_TABLE_B = new double[FastMath.EXP_INT_TABLE_LEN];
+
+ final double tmp[] = new double[2];
+ final double recip[] = new double[2];
+
+ // Populate expIntTable
+ for (int i = 0; i < FastMath.EXP_INT_TABLE_MAX_INDEX; i++) {
+ FastMathCalc.expint(i, tmp);
+ EXP_INT_TABLE_A[i + FastMath.EXP_INT_TABLE_MAX_INDEX] = tmp[0];
+ EXP_INT_TABLE_B[i + FastMath.EXP_INT_TABLE_MAX_INDEX] = tmp[1];
+
+ if (i != 0) {
+ // Negative integer powers
+ FastMathCalc.splitReciprocal(tmp, recip);
+ EXP_INT_TABLE_A[FastMath.EXP_INT_TABLE_MAX_INDEX - i] = recip[0];
+ EXP_INT_TABLE_B[FastMath.EXP_INT_TABLE_MAX_INDEX - i] = recip[1];
+ }
+ }
+ } else {
+ EXP_INT_TABLE_A = FastMathLiteralArrays.loadExpIntA();
+ EXP_INT_TABLE_B = FastMathLiteralArrays.loadExpIntB();
+ }
+ }
+ }
+
+ /** Enclose large data table in nested static class so it's only loaded on first access. */
+ private static class ExpFracTable {
+ /**
+ * Exponential over the range of 0 - 1 in increments of 2^-10 exp(x/1024) = expFracTableA[x]
+ * + expFracTableB[x]. 1024 = 2^10
+ */
+ private static final double[] EXP_FRAC_TABLE_A;
+
+ /**
+ * Exponential over the range of 0 - 1 in increments of 2^-10 exp(x/1024) = expFracTableA[x]
+ * + expFracTableB[x].
+ */
+ private static final double[] EXP_FRAC_TABLE_B;
+
+ static {
+ if (RECOMPUTE_TABLES_AT_RUNTIME) {
+ EXP_FRAC_TABLE_A = new double[FastMath.EXP_FRAC_TABLE_LEN];
+ EXP_FRAC_TABLE_B = new double[FastMath.EXP_FRAC_TABLE_LEN];
+
+ final double tmp[] = new double[2];
+
+ // Populate expFracTable
+ final double factor = 1d / (EXP_FRAC_TABLE_LEN - 1);
+ for (int i = 0; i < EXP_FRAC_TABLE_A.length; i++) {
+ FastMathCalc.slowexp(i * factor, tmp);
+ EXP_FRAC_TABLE_A[i] = tmp[0];
+ EXP_FRAC_TABLE_B[i] = tmp[1];
+ }
+ } else {
+ EXP_FRAC_TABLE_A = FastMathLiteralArrays.loadExpFracA();
+ EXP_FRAC_TABLE_B = FastMathLiteralArrays.loadExpFracB();
+ }
+ }
+ }
+
+ /** Enclose large data table in nested static class so it's only loaded on first access. */
+ private static class lnMant {
+ /** Extended precision logarithm table over the range 1 - 2 in increments of 2^-10. */
+ private static final double[][] LN_MANT;
+
+ static {
+ if (RECOMPUTE_TABLES_AT_RUNTIME) {
+ LN_MANT = new double[FastMath.LN_MANT_LEN][];
+
+ // Populate lnMant table
+ for (int i = 0; i < LN_MANT.length; i++) {
+ final double d =
+ Double.longBitsToDouble((((long) i) << 42) | 0x3ff0000000000000L);
+ LN_MANT[i] = FastMathCalc.slowLog(d);
+ }
+ } else {
+ LN_MANT = FastMathLiteralArrays.loadLnMant();
+ }
+ }
+ }
+
+ /** Enclose the Cody/Waite reduction (used in "sin", "cos" and "tan"). */
+ private static class CodyWaite {
+ /** k */
+ private final int finalK;
+
+ /** remA */
+ private final double finalRemA;
+
+ /** remB */
+ private final double finalRemB;
+
+ /**
+ * @param xa Argument.
+ */
+ CodyWaite(double xa) {
+ // Estimate k.
+ // k = (int)(xa / 1.5707963267948966);
+ int k = (int) (xa * 0.6366197723675814);
+
+ // Compute remainder.
+ double remA;
+ double remB;
+ while (true) {
+ double a = -k * 1.570796251296997;
+ remA = xa + a;
+ remB = -(remA - xa - a);
+
+ a = -k * 7.549789948768648E-8;
+ double b = remA;
+ remA = a + b;
+ remB += -(remA - b - a);
+
+ a = -k * 6.123233995736766E-17;
+ b = remA;
+ remA = a + b;
+ remB += -(remA - b - a);
+
+ if (remA > 0) {
+ break;
+ }
+
+ // Remainder is negative, so decrement k and try again.
+ // This should only happen if the input is very close
+ // to an even multiple of pi/2.
+ --k;
+ }
+
+ this.finalK = k;
+ this.finalRemA = remA;
+ this.finalRemB = remB;
+ }
+
+ /**
+ * @return k
+ */
+ int getK() {
+ return finalK;
+ }
+
+ /**
+ * @return remA
+ */
+ double getRemA() {
+ return finalRemA;
+ }
+
+ /**
+ * @return remB
+ */
+ double getRemB() {
+ return finalRemB;
+ }
+ }
+}
diff --git a/src/main/java/org/apache/commons/math3/util/FastMathCalc.java b/src/main/java/org/apache/commons/math3/util/FastMathCalc.java
new file mode 100644
index 0000000..cf0f27c
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/util/FastMathCalc.java
@@ -0,0 +1,678 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math3.util;
+
+import org.apache.commons.math3.exception.DimensionMismatchException;
+
+import java.io.PrintStream;
+
+/**
+ * Class used to compute the classical functions tables.
+ *
+ * @since 3.0
+ */
+class FastMathCalc {
+
+ /**
+ * 0x40000000 - used to split a double into two parts, both with the low order bits cleared.
+ * Equivalent to 2^30.
+ */
+ private static final long HEX_40000000 = 0x40000000L; // 1073741824L
+
+ /** Factorial table, for Taylor series expansions. 0!, 1!, 2!, ... 19! */
+ private static final double FACT[] =
+ new double[] {
+ +1.0d, // 0
+ +1.0d, // 1
+ +2.0d, // 2
+ +6.0d, // 3
+ +24.0d, // 4
+ +120.0d, // 5
+ +720.0d, // 6
+ +5040.0d, // 7
+ +40320.0d, // 8
+ +362880.0d, // 9
+ +3628800.0d, // 10
+ +39916800.0d, // 11
+ +479001600.0d, // 12
+ +6227020800.0d, // 13
+ +87178291200.0d, // 14
+ +1307674368000.0d, // 15
+ +20922789888000.0d, // 16
+ +355687428096000.0d, // 17
+ +6402373705728000.0d, // 18
+ +121645100408832000.0d, // 19
+ };
+
+ /** Coefficients for slowLog. */
+ private static final double LN_SPLIT_COEF[][] = {
+ {2.0, 0.0},
+ {0.6666666269302368, 3.9736429850260626E-8},
+ {0.3999999761581421, 2.3841857910019882E-8},
+ {0.2857142686843872, 1.7029898543501842E-8},
+ {0.2222222089767456, 1.3245471311735498E-8},
+ {0.1818181574344635, 2.4384203044354907E-8},
+ {0.1538461446762085, 9.140260083262505E-9},
+ {0.13333332538604736, 9.220590270857665E-9},
+ {0.11764700710773468, 1.2393345855018391E-8},
+ {0.10526403784751892, 8.251545029714408E-9},
+ {0.0952233225107193, 1.2675934823758863E-8},
+ {0.08713622391223907, 1.1430250008909141E-8},
+ {0.07842259109020233, 2.404307984052299E-9},
+ {0.08371849358081818, 1.176342548272881E-8},
+ {0.030589580535888672, 1.2958646899018938E-9},
+ {0.14982303977012634, 1.225743062930824E-8},
+ };
+
+ /** Table start declaration. */
+ private static final String TABLE_START_DECL = " {";
+
+ /** Table end declaration. */
+ private static final String TABLE_END_DECL = " };";
+
+ /** Private Constructor. */
+ private FastMathCalc() {}
+
+ /**
+ * Build the sine and cosine tables.
+ *
+ * @param SINE_TABLE_A table of the most significant part of the sines
+ * @param SINE_TABLE_B table of the least significant part of the sines
+ * @param COSINE_TABLE_A table of the most significant part of the cosines
+ * @param COSINE_TABLE_B table of the most significant part of the cosines
+ * @param SINE_TABLE_LEN length of the tables
+ * @param TANGENT_TABLE_A table of the most significant part of the tangents
+ * @param TANGENT_TABLE_B table of the most significant part of the tangents
+ */
+ @SuppressWarnings("unused")
+ private static void buildSinCosTables(
+ double[] SINE_TABLE_A,
+ double[] SINE_TABLE_B,
+ double[] COSINE_TABLE_A,
+ double[] COSINE_TABLE_B,
+ int SINE_TABLE_LEN,
+ double[] TANGENT_TABLE_A,
+ double[] TANGENT_TABLE_B) {
+ final double result[] = new double[2];
+
+ /* Use taylor series for 0 <= x <= 6/8 */
+ for (int i = 0; i < 7; i++) {
+ double x = i / 8.0;
+
+ slowSin(x, result);
+ SINE_TABLE_A[i] = result[0];
+ SINE_TABLE_B[i] = result[1];
+
+ slowCos(x, result);
+ COSINE_TABLE_A[i] = result[0];
+ COSINE_TABLE_B[i] = result[1];
+ }
+
+ /* Use angle addition formula to complete table to 13/8, just beyond pi/2 */
+ for (int i = 7; i < SINE_TABLE_LEN; i++) {
+ double xs[] = new double[2];
+ double ys[] = new double[2];
+ double as[] = new double[2];
+ double bs[] = new double[2];
+ double temps[] = new double[2];
+
+ if ((i & 1) == 0) {
+ // Even, use double angle
+ xs[0] = SINE_TABLE_A[i / 2];
+ xs[1] = SINE_TABLE_B[i / 2];
+ ys[0] = COSINE_TABLE_A[i / 2];
+ ys[1] = COSINE_TABLE_B[i / 2];
+
+ /* compute sine */
+ splitMult(xs, ys, result);
+ SINE_TABLE_A[i] = result[0] * 2.0;
+ SINE_TABLE_B[i] = result[1] * 2.0;
+
+ /* Compute cosine */
+ splitMult(ys, ys, as);
+ splitMult(xs, xs, temps);
+ temps[0] = -temps[0];
+ temps[1] = -temps[1];
+ splitAdd(as, temps, result);
+ COSINE_TABLE_A[i] = result[0];
+ COSINE_TABLE_B[i] = result[1];
+ } else {
+ xs[0] = SINE_TABLE_A[i / 2];
+ xs[1] = SINE_TABLE_B[i / 2];
+ ys[0] = COSINE_TABLE_A[i / 2];
+ ys[1] = COSINE_TABLE_B[i / 2];
+ as[0] = SINE_TABLE_A[i / 2 + 1];
+ as[1] = SINE_TABLE_B[i / 2 + 1];
+ bs[0] = COSINE_TABLE_A[i / 2 + 1];
+ bs[1] = COSINE_TABLE_B[i / 2 + 1];
+
+ /* compute sine */
+ splitMult(xs, bs, temps);
+ splitMult(ys, as, result);
+ splitAdd(result, temps, result);
+ SINE_TABLE_A[i] = result[0];
+ SINE_TABLE_B[i] = result[1];
+
+ /* Compute cosine */
+ splitMult(ys, bs, result);
+ splitMult(xs, as, temps);
+ temps[0] = -temps[0];
+ temps[1] = -temps[1];
+ splitAdd(result, temps, result);
+ COSINE_TABLE_A[i] = result[0];
+ COSINE_TABLE_B[i] = result[1];
+ }
+ }
+
+ /* Compute tangent = sine/cosine */
+ for (int i = 0; i < SINE_TABLE_LEN; i++) {
+ double xs[] = new double[2];
+ double ys[] = new double[2];
+ double as[] = new double[2];
+
+ as[0] = COSINE_TABLE_A[i];
+ as[1] = COSINE_TABLE_B[i];
+
+ splitReciprocal(as, ys);
+
+ xs[0] = SINE_TABLE_A[i];
+ xs[1] = SINE_TABLE_B[i];
+
+ splitMult(xs, ys, as);
+
+ TANGENT_TABLE_A[i] = as[0];
+ TANGENT_TABLE_B[i] = as[1];
+ }
+ }
+
+ /**
+ * For x between 0 and pi/4 compute cosine using Talor series cos(x) = 1 - x^2/2! + x^4/4! ...
+ *
+ * @param x number from which cosine is requested
+ * @param result placeholder where to put the result in extended precision (may be null)
+ * @return cos(x)
+ */
+ static double slowCos(final double x, final double result[]) {
+
+ final double xs[] = new double[2];
+ final double ys[] = new double[2];
+ final double facts[] = new double[2];
+ final double as[] = new double[2];
+ split(x, xs);
+ ys[0] = ys[1] = 0.0;
+
+ for (int i = FACT.length - 1; i >= 0; i--) {
+ splitMult(xs, ys, as);
+ ys[0] = as[0];
+ ys[1] = as[1];
+
+ if ((i & 1) != 0) { // skip odd entries
+ continue;
+ }
+
+ split(FACT[i], as);
+ splitReciprocal(as, facts);
+
+ if ((i & 2) != 0) { // alternate terms are negative
+ facts[0] = -facts[0];
+ facts[1] = -facts[1];
+ }
+
+ splitAdd(ys, facts, as);
+ ys[0] = as[0];
+ ys[1] = as[1];
+ }
+
+ if (result != null) {
+ result[0] = ys[0];
+ result[1] = ys[1];
+ }
+
+ return ys[0] + ys[1];
+ }
+
+ /**
+ * For x between 0 and pi/4 compute sine using Taylor expansion: sin(x) = x - x^3/3! + x^5/5! -
+ * x^7/7! ...
+ *
+ * @param x number from which sine is requested
+ * @param result placeholder where to put the result in extended precision (may be null)
+ * @return sin(x)
+ */
+ static double slowSin(final double x, final double result[]) {
+ final double xs[] = new double[2];
+ final double ys[] = new double[2];
+ final double facts[] = new double[2];
+ final double as[] = new double[2];
+ split(x, xs);
+ ys[0] = ys[1] = 0.0;
+
+ for (int i = FACT.length - 1; i >= 0; i--) {
+ splitMult(xs, ys, as);
+ ys[0] = as[0];
+ ys[1] = as[1];
+
+ if ((i & 1) == 0) { // Ignore even numbers
+ continue;
+ }
+
+ split(FACT[i], as);
+ splitReciprocal(as, facts);
+
+ if ((i & 2) != 0) { // alternate terms are negative
+ facts[0] = -facts[0];
+ facts[1] = -facts[1];
+ }
+
+ splitAdd(ys, facts, as);
+ ys[0] = as[0];
+ ys[1] = as[1];
+ }
+
+ if (result != null) {
+ result[0] = ys[0];
+ result[1] = ys[1];
+ }
+
+ return ys[0] + ys[1];
+ }
+
+ /**
+ * For x between 0 and 1, returns exp(x), uses extended precision
+ *
+ * @param x argument of exponential
+ * @param result placeholder where to place exp(x) split in two terms for extra precision (i.e.
+ * exp(x) = result[0] + result[1]
+ * @return exp(x)
+ */
+ static double slowexp(final double x, final double result[]) {
+ final double xs[] = new double[2];
+ final double ys[] = new double[2];
+ final double facts[] = new double[2];
+ final double as[] = new double[2];
+ split(x, xs);
+ ys[0] = ys[1] = 0.0;
+
+ for (int i = FACT.length - 1; i >= 0; i--) {
+ splitMult(xs, ys, as);
+ ys[0] = as[0];
+ ys[1] = as[1];
+
+ split(FACT[i], as);
+ splitReciprocal(as, facts);
+
+ splitAdd(ys, facts, as);
+ ys[0] = as[0];
+ ys[1] = as[1];
+ }
+
+ if (result != null) {
+ result[0] = ys[0];
+ result[1] = ys[1];
+ }
+
+ return ys[0] + ys[1];
+ }
+
+ /**
+ * Compute split[0], split[1] such that their sum is equal to d, and split[0] has its 30 least
+ * significant bits as zero.
+ *
+ * @param d number to split
+ * @param split placeholder where to place the result
+ */
+ private static void split(final double d, final double split[]) {
+ if (d < 8e298 && d > -8e298) {
+ final double a = d * HEX_40000000;
+ split[0] = (d + a) - a;
+ split[1] = d - split[0];
+ } else {
+ final double a = d * 9.31322574615478515625E-10;
+ split[0] = (d + a - d) * HEX_40000000;
+ split[1] = d - split[0];
+ }
+ }
+
+ /**
+ * Recompute a split.
+ *
+ * @param a input/out array containing the split, changed on output
+ */
+ private static void resplit(final double a[]) {
+ final double c = a[0] + a[1];
+ final double d = -(c - a[0] - a[1]);
+
+ if (c < 8e298 && c > -8e298) { // MAGIC NUMBER
+ double z = c * HEX_40000000;
+ a[0] = (c + z) - z;
+ a[1] = c - a[0] + d;
+ } else {
+ double z = c * 9.31322574615478515625E-10;
+ a[0] = (c + z - c) * HEX_40000000;
+ a[1] = c - a[0] + d;
+ }
+ }
+
+ /**
+ * Multiply two numbers in split form.
+ *
+ * @param a first term of multiplication
+ * @param b second term of multiplication
+ * @param ans placeholder where to put the result
+ */
+ private static void splitMult(double a[], double b[], double ans[]) {
+ ans[0] = a[0] * b[0];
+ ans[1] = a[0] * b[1] + a[1] * b[0] + a[1] * b[1];
+
+ /* Resplit */
+ resplit(ans);
+ }
+
+ /**
+ * Add two numbers in split form.
+ *
+ * @param a first term of addition
+ * @param b second term of addition
+ * @param ans placeholder where to put the result
+ */
+ private static void splitAdd(final double a[], final double b[], final double ans[]) {
+ ans[0] = a[0] + b[0];
+ ans[1] = a[1] + b[1];
+
+ resplit(ans);
+ }
+
+ /**
+ * Compute the reciprocal of in. Use the following algorithm. in = c + d. want to find x + y
+ * such that x+y = 1/(c+d) and x is much larger than y and x has several zero bits on the right.
+ *
+ * <p>Set b = 1/(2^22), a = 1 - b. Thus (a+b) = 1. Use following identity to compute (a+b)/(c+d)
+ *
+ * <p>(a+b)/(c+d) = a/c + (bc - ad) / (c^2 + cd) set x = a/c and y = (bc - ad) / (c^2 + cd) This
+ * will be close to the right answer, but there will be some rounding in the calculation of X.
+ * So by carefully computing 1 - (c+d)(x+y) we can compute an error and add that back in. This
+ * is done carefully so that terms of similar size are subtracted first.
+ *
+ * @param in initial number, in split form
+ * @param result placeholder where to put the result
+ */
+ static void splitReciprocal(final double in[], final double result[]) {
+ final double b = 1.0 / 4194304.0;
+ final double a = 1.0 - b;
+
+ if (in[0] == 0.0) {
+ in[0] = in[1];
+ in[1] = 0.0;
+ }
+
+ result[0] = a / in[0];
+ result[1] = (b * in[0] - a * in[1]) / (in[0] * in[0] + in[0] * in[1]);
+
+ if (result[1] != result[1]) { // can happen if result[1] is NAN
+ result[1] = 0.0;
+ }
+
+ /* Resplit */
+ resplit(result);
+
+ for (int i = 0; i < 2; i++) {
+ /* this may be overkill, probably once is enough */
+ double err =
+ 1.0
+ - result[0] * in[0]
+ - result[0] * in[1]
+ - result[1] * in[0]
+ - result[1] * in[1];
+ /*err = 1.0 - err; */
+ err *= result[0] + result[1];
+ /*printf("err = %16e\n", err); */
+ result[1] += err;
+ }
+ }
+
+ /**
+ * Compute (a[0] + a[1]) * (b[0] + b[1]) in extended precision.
+ *
+ * @param a first term of the multiplication
+ * @param b second term of the multiplication
+ * @param result placeholder where to put the result
+ */
+ private static void quadMult(final double a[], final double b[], final double result[]) {
+ final double xs[] = new double[2];
+ final double ys[] = new double[2];
+ final double zs[] = new double[2];
+
+ /* a[0] * b[0] */
+ split(a[0], xs);
+ split(b[0], ys);
+ splitMult(xs, ys, zs);
+
+ result[0] = zs[0];
+ result[1] = zs[1];
+
+ /* a[0] * b[1] */
+ split(b[1], ys);
+ splitMult(xs, ys, zs);
+
+ double tmp = result[0] + zs[0];
+ result[1] -= tmp - result[0] - zs[0];
+ result[0] = tmp;
+ tmp = result[0] + zs[1];
+ result[1] -= tmp - result[0] - zs[1];
+ result[0] = tmp;
+
+ /* a[1] * b[0] */
+ split(a[1], xs);
+ split(b[0], ys);
+ splitMult(xs, ys, zs);
+
+ tmp = result[0] + zs[0];
+ result[1] -= tmp - result[0] - zs[0];
+ result[0] = tmp;
+ tmp = result[0] + zs[1];
+ result[1] -= tmp - result[0] - zs[1];
+ result[0] = tmp;
+
+ /* a[1] * b[0] */
+ split(a[1], xs);
+ split(b[1], ys);
+ splitMult(xs, ys, zs);
+
+ tmp = result[0] + zs[0];
+ result[1] -= tmp - result[0] - zs[0];
+ result[0] = tmp;
+ tmp = result[0] + zs[1];
+ result[1] -= tmp - result[0] - zs[1];
+ result[0] = tmp;
+ }
+
+ /**
+ * Compute exp(p) for a integer p in extended precision.
+ *
+ * @param p integer whose exponential is requested
+ * @param result placeholder where to put the result in extended precision
+ * @return exp(p) in standard precision (equal to result[0] + result[1])
+ */
+ static double expint(int p, final double result[]) {
+ // double x = M_E;
+ final double xs[] = new double[2];
+ final double as[] = new double[2];
+ final double ys[] = new double[2];
+ // split(x, xs);
+ // xs[1] = (double)(2.7182818284590452353602874713526625L - xs[0]);
+ // xs[0] = 2.71827697753906250000;
+ // xs[1] = 4.85091998273542816811e-06;
+ // xs[0] = Double.longBitsToDouble(0x4005bf0800000000L);
+ // xs[1] = Double.longBitsToDouble(0x3ed458a2bb4a9b00L);
+
+ /* E */
+ xs[0] = 2.718281828459045;
+ xs[1] = 1.4456468917292502E-16;
+
+ split(1.0, ys);
+
+ while (p > 0) {
+ if ((p & 1) != 0) {
+ quadMult(ys, xs, as);
+ ys[0] = as[0];
+ ys[1] = as[1];
+ }
+
+ quadMult(xs, xs, as);
+ xs[0] = as[0];
+ xs[1] = as[1];
+
+ p >>= 1;
+ }
+
+ if (result != null) {
+ result[0] = ys[0];
+ result[1] = ys[1];
+
+ resplit(result);
+ }
+
+ return ys[0] + ys[1];
+ }
+
+ /**
+ * xi in the range of [1, 2]. 3 5 7 x+1 / x x x \ ln ----- = 2 * | x + ---- + ---- + ---- + ...
+ * | 1-x \ 3 5 7 /
+ *
+ * <p>So, compute a Remez approximation of the following function
+ *
+ * <p>ln ((sqrt(x)+1)/(1-sqrt(x))) / x
+ *
+ * <p>This will be an even function with only positive coefficents. x is in the range [0 - 1/3].
+ *
+ * <p>Transform xi for input to the above function by setting x = (xi-1)/(xi+1). Input to the
+ * polynomial is x^2, then the result is multiplied by x.
+ *
+ * @param xi number from which log is requested
+ * @return log(xi)
+ */
+ static double[] slowLog(double xi) {
+ double x[] = new double[2];
+ double x2[] = new double[2];
+ double y[] = new double[2];
+ double a[] = new double[2];
+
+ split(xi, x);
+
+ /* Set X = (x-1)/(x+1) */
+ x[0] += 1.0;
+ resplit(x);
+ splitReciprocal(x, a);
+ x[0] -= 2.0;
+ resplit(x);
+ splitMult(x, a, y);
+ x[0] = y[0];
+ x[1] = y[1];
+
+ /* Square X -> X2*/
+ splitMult(x, x, x2);
+
+ // x[0] -= 1.0;
+ // resplit(x);
+
+ y[0] = LN_SPLIT_COEF[LN_SPLIT_COEF.length - 1][0];
+ y[1] = LN_SPLIT_COEF[LN_SPLIT_COEF.length - 1][1];
+
+ for (int i = LN_SPLIT_COEF.length - 2; i >= 0; i--) {
+ splitMult(y, x2, a);
+ y[0] = a[0];
+ y[1] = a[1];
+ splitAdd(y, LN_SPLIT_COEF[i], a);
+ y[0] = a[0];
+ y[1] = a[1];
+ }
+
+ splitMult(y, x, a);
+ y[0] = a[0];
+ y[1] = a[1];
+
+ return y;
+ }
+
+ /**
+ * Print an array.
+ *
+ * @param out text output stream where output should be printed
+ * @param name array name
+ * @param expectedLen expected length of the array
+ * @param array2d array data
+ */
+ static void printarray(PrintStream out, String name, int expectedLen, double[][] array2d) {
+ out.println(name);
+ checkLen(expectedLen, array2d.length);
+ out.println(TABLE_START_DECL + " ");
+ int i = 0;
+ for (double[] array : array2d) { // "double array[]" causes PMD parsing error
+ out.print(" {");
+ for (double d : array) { // assume inner array has very few entries
+ out.printf("%-25.25s", format(d)); // multiple entries per line
+ }
+ out.println("}, // " + i++);
+ }
+ out.println(TABLE_END_DECL);
+ }
+
+ /**
+ * Print an array.
+ *
+ * @param out text output stream where output should be printed
+ * @param name array name
+ * @param expectedLen expected length of the array
+ * @param array array data
+ */
+ static void printarray(PrintStream out, String name, int expectedLen, double[] array) {
+ out.println(name + "=");
+ checkLen(expectedLen, array.length);
+ out.println(TABLE_START_DECL);
+ for (double d : array) {
+ out.printf(" %s%n", format(d)); // one entry per line
+ }
+ out.println(TABLE_END_DECL);
+ }
+
+ /**
+ * Format a double.
+ *
+ * @param d double number to format
+ * @return formatted number
+ */
+ static String format(double d) {
+ if (d != d) {
+ return "Double.NaN,";
+ } else {
+ return ((d >= 0) ? "+" : "") + Double.toString(d) + "d,";
+ }
+ }
+
+ /**
+ * Check two lengths are equal.
+ *
+ * @param expectedLen expected length
+ * @param actual actual length
+ * @exception DimensionMismatchException if the two lengths are not equal
+ */
+ private static void checkLen(int expectedLen, int actual) throws DimensionMismatchException {
+ if (expectedLen != actual) {
+ throw new DimensionMismatchException(actual, expectedLen);
+ }
+ }
+}
diff --git a/src/main/java/org/apache/commons/math3/util/FastMathLiteralArrays.java b/src/main/java/org/apache/commons/math3/util/FastMathLiteralArrays.java
new file mode 100644
index 0000000..1092b32
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/util/FastMathLiteralArrays.java
@@ -0,0 +1,8228 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math3.util;
+
+/** Utility class for loading tabulated data used by {@link FastMath}. */
+class FastMathLiteralArrays {
+ /**
+ * Exponential evaluated at integer values, exp(x) = expIntTableA[x + EXP_INT_TABLE_MAX_INDEX] +
+ * expIntTableB[x+EXP_INT_TABLE_MAX_INDEX].
+ */
+ private static final double[] EXP_INT_A =
+ new double[] {
+ +0.0d,
+ Double.NaN,
+ Double.NaN,
+ Double.NaN,
+ Double.NaN,
+ Double.NaN,
+ Double.NaN,
+ Double.NaN,
+ Double.NaN,
+ Double.NaN,
+ Double.NaN,
+ Double.NaN,
+ Double.NaN,
+ Double.NaN,
+ Double.NaN,
+ Double.NaN,
+ Double.NaN,
+ Double.NaN,
+ Double.NaN,
+ Double.NaN,
+ Double.NaN,
+ Double.NaN,
+ Double.NaN,
+ Double.NaN,
+ Double.NaN,
+ Double.NaN,
+ Double.NaN,
+ Double.NaN,
+ Double.NaN,
+ Double.NaN,
+ Double.NaN,
+ Double.NaN,
+ Double.NaN,
+ Double.NaN,
+ Double.NaN,
+ Double.NaN,
+ Double.NaN,
+ Double.NaN,
+ Double.NaN,
+ Double.NaN,
+ Double.NaN,
+ +1.2167807682331913E-308d,
+ +3.3075532478807267E-308d,
+ +8.990862214387203E-308d,
+ +2.4439696075216986E-307d,
+ +6.64339758024534E-307d,
+ +1.8058628951432254E-306d,
+ +4.908843759498681E-306d,
+ +1.334362017065677E-305d,
+ +3.627172425759641E-305d,
+ +9.85967600992008E-305d,
+ +2.680137967689915E-304d,
+ +7.285370725133842E-304d,
+ +1.9803689272433392E-303d,
+ +5.3832011494782624E-303d,
+ +1.463305638201413E-302d,
+ +3.9776772027043775E-302d,
+ +1.0812448255518705E-301d,
+ +2.9391280956327795E-301d,
+ +7.989378677301346E-301d,
+ +2.1717383041010577E-300d,
+ +5.903396499766243E-300d,
+ +1.604709595901607E-299d,
+ +4.3620527352131126E-299d,
+ +1.1857289715706991E-298d,
+ +3.2231452986239366E-298d,
+ +8.761416875971053E-298d,
+ +2.381600167287677E-297d,
+ +6.473860152384321E-297d,
+ +1.7597776278732318E-296d,
+ +4.7835721669653157E-296d,
+ +1.3003096668152053E-295d,
+ +3.5346080979652066E-295d,
+ +9.608060944124859E-295d,
+ +2.6117415961302846E-294d,
+ +7.099449830809996E-294d,
+ +1.9298305829106006E-293d,
+ +5.245823134132673E-293d,
+ +1.4259627797225802E-292d,
+ +3.8761686729764145E-292d,
+ +1.0536518897078156E-291d,
+ +2.864122672853628E-291d,
+ +7.785491934690374E-291d,
+ +2.116316283183901E-290d,
+ +5.7527436249968E-290d,
+ +1.5637579898345352E-289d,
+ +4.250734424415339E-289d,
+ +1.1554696041977512E-288d,
+ +3.1408919441362495E-288d,
+ +8.537829238438662E-288d,
+ +2.320822576772103E-287d,
+ +6.308649765138419E-287d,
+ +1.7148689119310826E-286d,
+ +4.66149719271323E-286d,
+ +1.267126226441217E-285d,
+ +3.444406231880653E-285d,
+ +9.362866914115166E-285d,
+ +2.5450911557068313E-284d,
+ +6.918275021321188E-284d,
+ +1.880582039589629E-283d,
+ +5.111952261540649E-283d,
+ +1.3895726688907995E-282d,
+ +3.7772500667438066E-282d,
+ +1.026763015362553E-281d,
+ +2.791031173360063E-281d,
+ +7.586808748646825E-281d,
+ +2.0623086887184633E-280d,
+ +5.605936171588964E-280d,
+ +1.5238514098804918E-279d,
+ +4.1422578754033235E-279d,
+ +1.1259823210174452E-278d,
+ +3.060737220976933E-278d,
+ +8.319947089683576E-278d,
+ +2.2615958035357106E-277d,
+ +6.147655179898435E-277d,
+ +1.6711060014400145E-276d,
+ +4.542536646012133E-276d,
+ +1.2347896500246374E-275d,
+ +3.3565057475434694E-275d,
+ +9.123929070778758E-275d,
+ +2.4801413921885483E-274d,
+ +6.741722283079056E-274d,
+ +1.8325902719086093E-273d,
+ +4.981496462621207E-273d,
+ +1.3541112064618357E-272d,
+ +3.68085620656127E-272d,
+ +1.0005602916630382E-271d,
+ +2.719805132368625E-271d,
+ +7.393196131284108E-271d,
+ +2.0096791226867E-270d,
+ +5.462874707256208E-270d,
+ +1.4849631831943512E-269d,
+ +4.036548930895323E-269d,
+ +1.0972476870931676E-268d,
+ +2.9826282194717127E-268d,
+ +8.107624153838987E-268d,
+ +2.2038806519542315E-267d,
+ +5.990769236615968E-267d,
+ +1.628459873440512E-266d,
+ +4.4266130556431266E-266d,
+ +1.203278237867575E-265d,
+ +3.270849446965521E-265d,
+ +8.891090288030614E-265d,
+ +2.4168487931443637E-264d,
+ +6.569676185250389E-264d,
+ +1.7858231429575898E-263d,
+ +4.85437090269903E-263d,
+ +1.3195548295785448E-262d,
+ +3.5869215528816054E-262d,
+ +9.750264097807267E-262d,
+ +2.650396454019762E-261d,
+ +7.204525142098426E-261d,
+ +1.958392846081373E-260d,
+ +5.32346341339996E-260d,
+ +1.4470673509275515E-259d,
+ +3.9335373658569176E-259d,
+ +1.0692462289051038E-258d,
+ +2.9065128598079075E-258d,
+ +7.900720862969045E-258d,
+ +2.147638465376883E-257d,
+ +5.8378869339035456E-257d,
+ +1.5869022483809747E-256d,
+ +4.3136475849391444E-256d,
+ +1.1725710340687719E-255d,
+ +3.1873780814410126E-255d,
+ +8.66419234315257E-255d,
+ +2.35517168886351E-254d,
+ +6.402020300783889E-254d,
+ +1.740249660600677E-253d,
+ +4.7304887145310405E-253d,
+ +1.2858802448614707E-252d,
+ +3.495384792953975E-252d,
+ +9.501439740542955E-252d,
+ +2.582759362004277E-251d,
+ +7.020668578160457E-251d,
+ +1.908415302517694E-250d,
+ +5.1876107490791666E-250d,
+ +1.4101386971763257E-249d,
+ +3.8331545111676784E-249d,
+ +1.0419594359065132E-248d,
+ +2.8323395451363237E-248d,
+ +7.699097067385825E-248d,
+ +2.0928317096428755E-247d,
+ +5.688906371296133E-247d,
+ +1.5464049837965422E-246d,
+ +4.2035646586788297E-246d,
+ +1.1426473877336358E-245d,
+ +3.106037603716254E-245d,
+ +8.443084996839363E-245d,
+ +2.2950686306677644E-244d,
+ +6.238642390386363E-244d,
+ +1.695838923802857E-243d,
+ +4.6097680405580995E-243d,
+ +1.2530649392922358E-242d,
+ +3.4061835424180075E-242d,
+ +9.25896798127602E-242d,
+ +2.5168480541429286E-241d,
+ +6.841502859109196E-241d,
+ +1.8597132378953187E-240d,
+ +5.055224959032211E-240d,
+ +1.374152583940637E-239d,
+ +3.735333866258403E-239d,
+ +1.0153690688015855E-238d,
+ +2.7600590782738726E-238d,
+ +7.502618487550056E-238d,
+ +2.0394233446495043E-237d,
+ +5.543727690168612E-237d,
+ +1.5069412868172555E-236d,
+ +4.0962906236847E-236d,
+ +1.1134873918971586E-235d,
+ +3.026772467749944E-235d,
+ +8.227620163729258E-235d,
+ +2.2364990583200056E-234d,
+ +6.079434951446575E-234d,
+ +1.6525617499662284E-233d,
+ +4.4921289690525345E-233d,
+ +1.2210872189854344E-232d,
+ +3.3192593301633E-232d,
+ +9.02268127425393E-232d,
+ +2.4526190464373087E-231d,
+ +6.666909874218774E-231d,
+ +1.8122539547625083E-230d,
+ +4.926216840507529E-230d,
+ +1.3390847149416908E-229d,
+ +3.6400093808551196E-229d,
+ +9.894571625944288E-229d,
+ +2.689623698321582E-228d,
+ +7.31115423069187E-228d,
+ +1.9873779569310022E-227d,
+ +5.402252865260326E-227d,
+ +1.4684846983789053E-226d,
+ +3.991755413823315E-226d,
+ +1.0850715739509136E-225d,
+ +2.9495302004590423E-225d,
+ +8.017654713159388E-225d,
+ +2.179424521221378E-224d,
+ +5.924290380648597E-224d,
+ +1.6103890140790331E-223d,
+ +4.377491272857675E-223d,
+ +1.1899254154663847E-222d,
+ +3.2345523990372546E-222d,
+ +8.792425221770645E-222d,
+ +2.3900289095512176E-221d,
+ +6.496772856703278E-221d,
+ +1.7660059778220905E-220d,
+ +4.800501435803201E-220d,
+ +1.3049116216750674E-219d,
+ +3.5471180281159325E-219d,
+ +9.642065709892252E-219d,
+ +2.6209850274990846E-218d,
+ +7.124574366530717E-218d,
+ +1.9366601417010147E-217d,
+ +5.264388476949737E-217d,
+ +1.431009021985696E-216d,
+ +3.889885799962507E-216d,
+ +1.057380684430436E-215d,
+ +2.8742587656021775E-215d,
+ +7.813044552050569E-215d,
+ +2.1238058974550874E-214d,
+ +5.773102661099307E-214d,
+ +1.5692921723471877E-213d,
+ +4.2657777816050375E-213d,
+ +1.1595585743839232E-212d,
+ +3.1520070828798975E-212d,
+ +8.568043768122183E-212d,
+ +2.329035966595791E-211d,
+ +6.33097561889469E-211d,
+ +1.720937714565362E-210d,
+ +4.677993239821998E-210d,
+ +1.2716105485691878E-209d,
+ +3.456595573934475E-209d,
+ +9.396000024637834E-209d,
+ +2.55409795397022E-208d,
+ +6.942757623821567E-208d,
+ +1.887237361505784E-207d,
+ +5.13004286606108E-207d,
+ +1.3944901709366118E-206d,
+ +3.7906173667738715E-206d,
+ +1.0303966192973381E-205d,
+ +2.8009086220877197E-205d,
+ +7.613657850210907E-205d,
+ +2.0696069842597556E-204d,
+ +5.6257755605305175E-204d,
+ +1.5292444435954893E-203d,
+ +4.156916476922876E-203d,
+ +1.12996721591364E-202d,
+ +3.071569248856111E-202d,
+ +8.349390727162016E-202d,
+ +2.2695999828608633E-201d,
+ +6.1694117899971836E-201d,
+ +1.677020107827128E-200d,
+ +4.558612479525779E-200d,
+ +1.2391595516612638E-199d,
+ +3.3683846288580648E-199d,
+ +9.156218120779494E-199d,
+ +2.4889182184335247E-198d,
+ +6.765580431441772E-198d,
+ +1.839075686473352E-197d,
+ +4.999126524757713E-197d,
+ +1.3589033107846643E-196d,
+ +3.6938826366068014E-196d,
+ +1.0041012794280992E-195d,
+ +2.7294301888986675E-195d,
+ +7.419361045185406E-195d,
+ +2.016791373353671E-194d,
+ +5.482208065983983E-194d,
+ +1.490218341008089E-193d,
+ +4.050833763855709E-193d,
+ +1.101130773265179E-192d,
+ +2.993183789477209E-192d,
+ +8.136316299122392E-192d,
+ +2.2116799789922265E-191d,
+ +6.011969568315371E-191d,
+ +1.6342228966392253E-190d,
+ +4.4422779589171113E-190d,
+ +1.2075364784547675E-189d,
+ +3.282424571107068E-189d,
+ +8.92255448602772E-189d,
+ +2.425402115319395E-188d,
+ +6.592926904915355E-188d,
+ +1.79214305133496E-187d,
+ +4.871550528055661E-187d,
+ +1.3242245776666673E-186d,
+ +3.599615946028287E-186d,
+ +9.78476998200719E-186d,
+ +2.659776075359514E-185d,
+ +7.230020851688713E-185d,
+ +1.9653234116333892E-184d,
+ +5.34230278107224E-184d,
+ +1.4521887058451231E-183d,
+ +3.947457923821984E-183d,
+ +1.0730302255093144E-182d,
+ +2.9167986204137332E-182d,
+ +7.928680793406766E-182d,
+ +2.1552386987482013E-181d,
+ +5.858546779607288E-181d,
+ +1.5925182066949723E-180d,
+ +4.328913614497258E-180d,
+ +1.1767205227552116E-179d,
+ +3.198658219194836E-179d,
+ +8.694853785564504E-179d,
+ +2.363506255864984E-178d,
+ +6.42467573615509E-178d,
+ +1.746408207555959E-177d,
+ +4.747229597770176E-177d,
+ +1.2904307529671472E-176d,
+ +3.507754341050756E-176d,
+ +9.535066345267336E-176d,
+ +2.591899541396432E-175d,
+ +7.045512786902009E-175d,
+ +1.9151693415969248E-174d,
+ +5.205969622575851E-174d,
+ +1.4151292367806538E-173d,
+ +3.846720258072078E-173d,
+ +1.045647032279984E-172d,
+ +2.8423629805010285E-172d,
+ +7.726344058192276E-172d,
+ +2.1002377128928765E-171d,
+ +5.709039546124285E-171d,
+ +1.5518778128928824E-170d,
+ +4.218440703602533E-170d,
+ +1.1466910691560932E-169d,
+ +3.1170298734336303E-169d,
+ +8.472965161251656E-169d,
+ +2.303190374523956E-168d,
+ +6.260720440258473E-168d,
+ +1.701840523821621E-167d,
+ +4.62608152166211E-167d,
+ +1.2574995962791943E-166d,
+ +3.418237608335161E-166d,
+ +9.29173407843235E-166d,
+ +2.5257552661512635E-165d,
+ +6.865714679174435E-165d,
+ +1.866294830116931E-164d,
+ +5.073114566291778E-164d,
+ +1.3790154522394582E-163d,
+ +3.7485528226129495E-163d,
+ +1.0189624503698769E-162d,
+ +2.7698267293941856E-162d,
+ +7.529170882336924E-162d,
+ +2.0466404088178596E-161d,
+ +5.56334611651382E-161d,
+ +1.512274346576166E-160d,
+ +4.110787043867721E-160d,
+ +1.1174279267498045E-159d,
+ +3.0374839443564585E-159d,
+ +8.25673801176584E-159d,
+ +2.244414150254963E-158d,
+ +6.1009492034592176E-158d,
+ +1.6584100275603453E-157d,
+ +4.50802633729044E-157d,
+ +1.2254085656601853E-156d,
+ +3.3310057014599044E-156d,
+ +9.054612259832416E-156d,
+ +2.4612985502035675E-155d,
+ +6.690503835950083E-155d,
+ +1.8186679660152888E-154d,
+ +4.9436516047443576E-154d,
+ +1.3438240331106108E-153d,
+ +3.652892398145774E-153d,
+ +9.92958982547828E-153d,
+ +2.6991427376823027E-152d,
+ +7.3370297995122135E-152d,
+ +1.994411660450821E-151d,
+ +5.421372463189529E-151d,
+ +1.4736818914204564E-150d,
+ +4.005882964287806E-150d,
+ +1.088911919926534E-149d,
+ +2.9599693109692324E-149d,
+ +8.046030012041041E-149d,
+ +2.18713790898745E-148d,
+ +5.945256705384597E-148d,
+ +1.6160884846515524E-147d,
+ +4.392983574030969E-147d,
+ +1.1941366764543551E-146d,
+ +3.2460001983475855E-146d,
+ +8.8235440586675E-146d,
+ +2.3984878190403553E-145d,
+ +6.519765758635405E-145d,
+ +1.772256261139753E-144d,
+ +4.817491674217065E-144d,
+ +1.3095299991573769E-143d,
+ +3.559671483107555E-143d,
+ +9.676190774054103E-143d,
+ +2.630261301303634E-142d,
+ +7.149792225695347E-142d,
+ +1.943514969662872E-141d,
+ +5.283020542151163E-141d,
+ +1.4360739330834996E-140d,
+ +3.9036541111764032E-140d,
+ +1.0611230602364477E-139d,
+ +2.8844319473099593E-139d,
+ +7.84069876400596E-139d,
+ +2.1313228444765414E-138d,
+ +5.793536445518422E-138d,
+ +1.5748463788034308E-137d,
+ +4.2808762411845363E-137d,
+ +1.1636629220608724E-136d,
+ +3.163163464591171E-136d,
+ +8.598369704466743E-136d,
+ +2.337279322276433E-135d,
+ +6.353384093665193E-135d,
+ +1.7270287031459572E-134d,
+ +4.694550492773212E-134d,
+ +1.2761111606368036E-133d,
+ +3.4688299108856403E-133d,
+ +9.429257929713919E-133d,
+ +2.5631381141873417E-132d,
+ +6.967331001069377E-132d,
+ +1.8939170679975288E-131d,
+ +5.148199748336684E-131d,
+ +1.3994258162094293E-130d,
+ +3.804034213613942E-130d,
+ +1.0340436948077763E-129d,
+ +2.8108219632627907E-129d,
+ +7.640606938467665E-129d,
+ +2.0769322678328357E-128d,
+ +5.645687086879944E-128d,
+ +1.5346568127351796E-127d,
+ +4.171630237420918E-127d,
+ +1.1339665711932977E-126d,
+ +3.0824406750909563E-126d,
+ +8.37894218404787E-126d,
+ +2.2776327994966818E-125d,
+ +6.191247522703296E-125d,
+ +1.6829556040859853E-124d,
+ +4.5747479502862494E-124d,
+ +1.2435453481209945E-123d,
+ +3.3803067202247166E-123d,
+ +9.188625696750548E-123d,
+ +2.4977273040076145E-122d,
+ +6.789527378582775E-122d,
+ +1.845584943222965E-121d,
+ +5.016820182185716E-121d,
+ +1.3637129731022491E-120d,
+ +3.706956710275979E-120d,
+ +1.0076552294433743E-119d,
+ +2.739090595934893E-119d,
+ +7.445620503219039E-119d,
+ +2.023929422267303E-118d,
+ +5.501611507503037E-118d,
+ +1.4954928881576769E-117d,
+ +4.0651709187617596E-117d,
+ +1.1050280679513555E-116d,
+ +3.003777734030334E-116d,
+ +8.165114384910189E-116d,
+ +2.219508285637377E-115d,
+ +6.033249389304709E-115d,
+ +1.6400070480930697E-114d,
+ +4.458001565878111E-114d,
+ +1.2118105325725891E-113d,
+ +3.2940421731384895E-113d,
+ +8.954135150208654E-113d,
+ +2.433986351722258E-112d,
+ +6.616260705434716E-112d,
+ +1.7984863104885375E-111d,
+ +4.888792154132158E-111d,
+ +1.3289115531074511E-110d,
+ +3.612356038181234E-110d,
+ +9.819402293160495E-110d,
+ +2.6691899766673256E-109d,
+ +7.255611264437603E-109d,
+ +1.9722796756250217E-108d,
+ +5.361211684173837E-108d,
+ +1.4573285967670963E-107d,
+ +3.961429477016909E-107d,
+ +1.0768281419102595E-106d,
+ +2.9271223293841774E-106d,
+ +7.956744351476403E-106d,
+ +2.1628672925745152E-105d,
+ +5.879282834821692E-105d,
+ +1.5981547034872092E-104d,
+ +4.344234755347641E-104d,
+ +1.1808855501885005E-103d,
+ +3.2099795870407646E-103d,
+ +8.725629524586503E-103d,
+ +2.3718718327094683E-102d,
+ +6.44741641521183E-102d,
+ +1.7525895549820557E-101d,
+ +4.7640323331013947E-101d,
+ +1.2949980563724296E-100d,
+ +3.5201699899499525E-100d,
+ +9.56881327374431E-100d,
+ +2.6010732940533088E-99d,
+ +7.070450309820548E-99d,
+ +1.9219478787856753E-98d,
+ +5.2243955659975294E-98d,
+ +1.4201378353978042E-97d,
+ +3.8603349913851996E-97d,
+ +1.0493479260117497E-96d,
+ +2.8524232604238555E-96d,
+ +7.753690709912764E-96d,
+ +2.1076716069929933E-95d,
+ +5.72924572981599E-95d,
+ +1.5573703263204683E-94d,
+ +4.233371554108682E-94d,
+ +1.1507496472539512E-93d,
+ +3.1280620563875923E-93d,
+ +8.5029538631631E-93d,
+ +2.3113425190436427E-92d,
+ +6.28287989314225E-92d,
+ +1.7078641226055994E-91d,
+ +4.6424556110307644E-91d,
+ +1.261950308999819E-90d,
+ +3.430336362898836E-90d,
+ +9.324622137237299E-90d,
+ +2.5346947846365435E-89d,
+ +6.890014851450124E-89d,
+ +1.8729003560057785E-88d,
+ +5.091070300111434E-88d,
+ +1.3838964592430477E-87d,
+ +3.761820584522275E-87d,
+ +1.0225689628581036E-86d,
+ +2.7796303536272215E-86d,
+ +7.555818934379333E-86d,
+ +2.053884626293416E-85d,
+ +5.583037134407759E-85d,
+ +1.5176268538776042E-84d,
+ +4.125337057189083E-84d,
+ +1.121383042095528E-83d,
+ +3.0482348236054953E-83d,
+ +8.285962249116636E-83d,
+ +2.2523580600947705E-82d,
+ +6.122543452787843E-82d,
+ +1.664279766968299E-81d,
+ +4.523982262003404E-81d,
+ +1.2297456769063303E-80d,
+ +3.342795345742034E-80d,
+ +9.086660081726823E-80d,
+ +2.4700104681773258E-79d,
+ +6.714184569587689E-79d,
+ +1.8251046352720517E-78d,
+ +4.961148056969105E-78d,
+ +1.3485799924445315E-77d,
+ +3.665820371396835E-77d,
+ +9.964732578705785E-77d,
+ +2.708695208461993E-76d,
+ +7.362996533913695E-76d,
+ +2.0014700145557332E-75d,
+ +5.440559532453721E-75d,
+ +1.4788974793889734E-74d,
+ +4.020060558571273E-74d,
+ +1.092765612182012E-73d,
+ +2.970445258959489E-73d,
+ +8.074507236705857E-73d,
+ +2.1948784599535102E-72d,
+ +5.966298125808066E-72d,
+ +1.6218081151910012E-71d,
+ +4.408531734441582E-71d,
+ +1.198363039426718E-70d,
+ +3.257488853378793E-70d,
+ +8.854771398921902E-70d,
+ +2.406976727302894E-69d,
+ +6.542840888268955E-69d,
+ +1.778528517418201E-68d,
+ +4.834541417183388E-68d,
+ +1.3141647465063647E-67d,
+ +3.572270133517001E-67d,
+ +9.710435805122717E-67d,
+ +2.63957027915428E-66d,
+ +7.175096392165733E-66d,
+ +1.9503931430716318E-65d,
+ +5.3017188565638215E-65d,
+ +1.4411566290936352E-64d,
+ +3.9174693825966044E-64d,
+ +1.0648786018364265E-63d,
+ +2.8946401383311E-63d,
+ +7.868447965383903E-63d,
+ +2.1388659707647114E-62d,
+ +5.814040618670345E-62d,
+ +1.5804200403673568E-61d,
+ +4.296027044486766E-61d,
+ +1.1677812418806031E-60d,
+ +3.174358801839755E-60d,
+ +8.62880163941313E-60d,
+ +2.345551464945955E-59d,
+ +6.3758692300917355E-59d,
+ +1.733140900346534E-58d,
+ +4.711165925070571E-58d,
+ +1.2806275683797178E-57d,
+ +3.481106736845E-57d,
+ +9.462629520363307E-57d,
+ +2.5722094667974783E-56d,
+ +6.9919903587080315E-56d,
+ +1.9006201022568844E-55d,
+ +5.166420404109835E-55d,
+ +1.4043786616805493E-54d,
+ +3.8174968984748894E-54d,
+ +1.03770335512154E-53d,
+ +2.820769858672565E-53d,
+ +7.667647949477605E-53d,
+ +2.0842827711783212E-52d,
+ +5.6656680900216754E-52d,
+ +1.5400881501571645E-51d,
+ +4.1863938339341257E-51d,
+ +1.1379799629071911E-50d,
+ +3.093350150840571E-50d,
+ +8.408597060399334E-50d,
+ +2.2856938448387544E-49d,
+ +6.2131591878042886E-49d,
+ +1.688911928929718E-48d,
+ +4.5909386437919143E-48d,
+ +1.2479464696643861E-47d,
+ +3.3922703599272275E-47d,
+ +9.221146830884422E-47d,
+ +2.5065676066043174E-46d,
+ +6.8135571305481364E-46d,
+ +1.8521166948363666E-45d,
+ +5.0345752964740226E-45d,
+ +1.368539456379101E-44d,
+ +3.720075801577098E-44d,
+ +1.0112214979786464E-43d,
+ +2.7487849807248755E-43d,
+ +7.47197247068667E-43d,
+ +2.0310928323153876E-42d,
+ +5.521082422279256E-42d,
+ +1.5007857288519654E-41d,
+ +4.0795586181406803E-41d,
+ +1.108938997126179E-40d,
+ +3.0144088843073416E-40d,
+ +8.194012195477669E-40d,
+ +2.2273635587196807E-39d,
+ +6.054601485195952E-39d,
+ +1.6458113136245473E-38d,
+ +4.473779311490168E-38d,
+ +1.2160992719555806E-37d,
+ +3.3057007442449645E-37d,
+ +8.985825281444118E-37d,
+ +2.442600707513088E-36d,
+ +6.639677673630215E-36d,
+ +1.8048513285848406E-35d,
+ +4.906094420881007E-35d,
+ +1.3336148713971936E-34d,
+ +3.625141007634431E-34d,
+ +9.854154449263851E-34d,
+ +2.6786368134431636E-33d,
+ +7.28128971953363E-33d,
+ +1.9792597720953414E-32d,
+ +5.380185921962174E-32d,
+ +1.4624861244004054E-31d,
+ +3.975449484028966E-31d,
+ +1.080639291795678E-30d,
+ +2.9374821418009058E-30d,
+ +7.984904044796711E-30d,
+ +2.1705221445447534E-29d,
+ +5.900089995748943E-29d,
+ +1.6038109389511792E-28d,
+ +4.359610133382778E-28d,
+ +1.185064946717304E-27d,
+ +3.221340469489223E-27d,
+ +8.756510122348782E-27d,
+ +2.380266370880709E-26d,
+ +6.47023467943241E-26d,
+ +1.75879225876483E-25d,
+ +4.780892502168074E-25d,
+ +1.2995814853898995E-24d,
+ +3.5326287852455166E-24d,
+ +9.602680736954162E-24d,
+ +2.6102792042257208E-23d,
+ +7.095474414148981E-23d,
+ +1.9287497671359936E-22d,
+ +5.242885191553114E-22d,
+ +1.4251641388208515E-21d,
+ +3.873997809109103E-21d,
+ +1.0530616658562386E-20d,
+ +2.862518609581133E-20d,
+ +7.78113163345177E-20d,
+ +2.1151310700892382E-19d,
+ +5.74952254077566E-19d,
+ +1.5628822871880503E-18d,
+ +4.24835413113866E-18d,
+ +1.1548223864099742E-17d,
+ +3.139132557537509E-17d,
+ +8.533046968331264E-17d,
+ +2.3195229636950566E-16d,
+ +6.305116324200775E-16d,
+ +1.71390848833098E-15d,
+ +4.6588861918718874E-15d,
+ +1.2664165777252073E-14d,
+ +3.442477422913037E-14d,
+ +9.357622912219837E-14d,
+ +2.5436656904062604E-13d,
+ +6.914399608426436E-13d,
+ +1.879528650772233E-12d,
+ +5.1090893668503945E-12d,
+ +1.3887944613766301E-11d,
+ +3.775134371775124E-11d,
+ +1.0261880234452292E-10d,
+ +2.789468100949932E-10d,
+ +7.582560135332983E-10d,
+ +2.061153470123145E-9d,
+ +5.602796449011294E-9d,
+ +1.5229979055675358E-8d,
+ +4.139937459513021E-8d,
+ +1.1253517584464134E-7d,
+ +3.059023470086686E-7d,
+ +8.315287232107949E-7d,
+ +2.260329438286135E-6d,
+ +6.1442124206223525E-6d,
+ +1.670170240686275E-5d,
+ +4.539993096841499E-5d,
+ +1.2340981629677117E-4d,
+ +3.35462624207139E-4d,
+ +9.118819143623114E-4d,
+ +0.0024787522852420807d,
+ +0.006737947463989258d,
+ +0.018315639346837997d,
+ +0.049787066876888275d,
+ +0.1353352963924408d,
+ +0.3678794503211975d,
+ +1.0d,
+ +2.7182817459106445d,
+ +7.389056205749512d,
+ +20.08553695678711d,
+ +54.59815216064453d,
+ +148.41314697265625d,
+ +403.42877197265625d,
+ +1096.633056640625d,
+ +2980.9580078125d,
+ +8103.083984375d,
+ +22026.46484375d,
+ +59874.140625d,
+ +162754.78125d,
+ +442413.375d,
+ +1202604.25d,
+ +3269017.5d,
+ +8886110.0d,
+ +2.4154952E7d,
+ +6.5659968E7d,
+ +1.78482304E8d,
+ +4.85165184E8d,
+ +1.318815744E9d,
+ +3.584912896E9d,
+ +9.74480384E9d,
+ +2.6489122816E10d,
+ +7.200489472E10d,
+ +1.95729620992E11d,
+ +5.32048248832E11d,
+ +1.446257098752E12d,
+ +3.9313342464E12d,
+ +1.0686474223616E13d,
+ +2.904884772864E13d,
+ +7.8962956959744E13d,
+ +2.14643574308864E14d,
+ +5.83461777702912E14d,
+ +1.586013579247616E15d,
+ +4.31123180027904E15d,
+ +1.1719142537166848E16d,
+ +3.1855931348221952E16d,
+ +8.6593395455164416E16d,
+ +2.35385270340419584E17d,
+ +6.3984347447610573E17d,
+ +1.73927483790327808E18d,
+ +4.7278395262972723E18d,
+ +1.285159987981792E19d,
+ +3.493427277593156E19d,
+ +9.496119530068797E19d,
+ +2.581312717296228E20d,
+ +7.016736290557636E20d,
+ +1.907346499785443E21d,
+ +5.1847060206155E21d,
+ +1.4093490364499379E22d,
+ +3.831007739580998E22d,
+ +1.0413759887481643E23d,
+ +2.8307533984544136E23d,
+ +7.694785471490595E23d,
+ +2.0916595931561093E24d,
+ +5.685720022003016E24d,
+ +1.545539007875769E25d,
+ +4.201209991636407E25d,
+ +1.142007304008196E26d,
+ +3.104297782658242E26d,
+ +8.43835682327257E26d,
+ +2.2937832658080656E27d,
+ +6.23514943204966E27d,
+ +1.694889206675675E28d,
+ +4.607187019879158E28d,
+ +1.2523630909973607E29d,
+ +3.4042761729010895E29d,
+ +9.253781621373885E29d,
+ +2.5154385492401904E30d,
+ +6.837671137556327E30d,
+ +1.8586717056324128E31d,
+ +5.05239404378821E31d,
+ +1.3733830589835937E32d,
+ +3.733241849647479E32d,
+ +1.014800418749161E33d,
+ +2.758513549969986E33d,
+ +7.498416981578345E33d,
+ +2.0382811492597872E34d,
+ +5.540622484676759E34d,
+ +1.5060972626944096E35d,
+ +4.0939972479624634E35d,
+ +1.1128638067747114E36d,
+ +3.0250770246136387E36d,
+ +8.223012393018281E36d,
+ +2.2352467822017166E37d,
+ +6.076029840339376E37d,
+ +1.6516361647240826E38d,
+ +4.4896127778163155E38d,
+ +1.2204032949639917E39d,
+ +3.3174000012927697E39d,
+ +9.017628107716908E39d,
+ +2.451245443147225E40d,
+ +6.663175904917432E40d,
+ +1.8112388823726723E41d,
+ +4.923458004084836E41d,
+ +1.3383347029375378E42d,
+ +3.637970747803715E42d,
+ +9.889030935681123E42d,
+ +2.6881169167589747E43d,
+ +7.307059786371152E43d,
+ +1.986264756071962E44d,
+ +5.399227989109673E44d,
+ +1.467662348860426E45d,
+ +3.989519470441919E45d,
+ +1.0844638420493122E46d,
+ +2.9478781225754055E46d,
+ +8.013164089994031E46d,
+ +2.1782039447564253E47d,
+ +5.920972420778763E47d,
+ +1.609486943324346E48d,
+ +4.3750396394525074E48d,
+ +1.1892591576149107E49d,
+ +3.2327411123173475E49d,
+ +8.787501601904039E49d,
+ +2.3886908001521312E50d,
+ +6.493134033643613E50d,
+ +1.7650169203544438E51d,
+ +4.7978130078372714E51d,
+ +1.3041809768060802E52d,
+ +3.5451314095271004E52d,
+ +9.636666808527841E52d,
+ +2.6195174357581655E53d,
+ +7.120586694432509E53d,
+ +1.9355758655647052E54d,
+ +5.2614409704305464E54d,
+ +1.4302079642723736E55d,
+ +3.8877083524279136E55d,
+ +1.0567886837680406E56d,
+ +2.872649515690124E56d,
+ +7.808670894670738E56d,
+ +2.1226166967029073E57d,
+ +5.769871153180574E57d,
+ +1.568413405104933E58d,
+ +4.263390023436419E58d,
+ +1.1589095247718807E59d,
+ +3.150242850860434E59d,
+ +8.563247933339596E59d,
+ +2.3277319969498524E60d,
+ +6.327431953939798E60d,
+ +1.719974302355042E61d,
+ +4.675374788964851E61d,
+ +1.2708985520400816E62d,
+ +3.454660807101683E62d,
+ +9.390740355567705E62d,
+ +2.5526681615684215E63d,
+ +6.938871462941557E63d,
+ +1.8861808782043154E64d,
+ +5.1271712215233855E64d,
+ +1.3937096689052236E65d,
+ +3.7884955399150257E65d,
+ +1.0298199046367501E66d,
+ +2.799340708992666E66d,
+ +7.609396391563323E66d,
+ +2.0684484008569103E67d,
+ +5.622626080395226E67d,
+ +1.528388084444653E68d,
+ +4.1545899609113734E68d,
+ +1.1293346659459732E69d,
+ +3.069849599753188E69d,
+ +8.344717266683004E69d,
+ +2.268329019570017E70d,
+ +6.165958325782564E70d,
+ +1.676081191364984E71d,
+ +4.556060380835955E71d,
+ +1.2384658100355657E72d,
+ +3.3664990715562672E72d,
+ +9.15109220707761E72d,
+ +2.4875248571153216E73d,
+ +6.761793219649385E73d,
+ +1.8380461271305958E74d,
+ +4.996327312938759E74d,
+ +1.3581426848077408E75d,
+ +3.691814001080034E75d,
+ +1.0035391101975138E76d,
+ +2.7279024753382288E76d,
+ +7.415207287657125E76d,
+ +2.0156621983963848E77d,
+ +5.479138512760614E77d,
+ +1.4893842728520671E78d,
+ +4.048565732162643E78d,
+ +1.1005142643914475E79d,
+ +2.991508131437659E79d,
+ +8.131762373533769E79d,
+ +2.210442148596269E80d,
+ +6.008604166110734E80d,
+ +1.633308028614055E81d,
+ +4.439791652732591E81d,
+ +1.206860599814453E82d,
+ +3.280586734644871E82d,
+ +8.917559854082513E82d,
+ +2.4240442814945802E83d,
+ +6.589235682116406E83d,
+ +1.7911398904871E84d,
+ +4.86882298924053E84d,
+ +1.3234832005748183E85d,
+ +3.597600556519039E85d,
+ +9.77929222446451E85d,
+ +2.658286976862848E86d,
+ +7.225974166887662E86d,
+ +1.9642232209552433E87d,
+ +5.3393125705958075E87d,
+ +1.4513757076459615E88d,
+ +3.945247871835613E88d,
+ +1.0724295693252266E89d,
+ +2.915165904253785E89d,
+ +7.924242330665303E89d,
+ +2.1540322390343345E90d,
+ +5.855267177907345E90d,
+ +1.5916266807316476E91d,
+ +4.326489915443873E91d,
+ +1.1760619079592718E92d,
+ +3.1968677404735245E92d,
+ +8.689987517871135E92d,
+ +2.3621834216830225E93d,
+ +6.421080550439423E93d,
+ +1.7454306955949023E94d,
+ +4.744571892885607E94d,
+ +1.2897084285532175E95d,
+ +3.505791114318544E95d,
+ +9.529727908157224E95d,
+ +2.5904487437231458E96d,
+ +7.041568925985714E96d,
+ +1.9140971884979424E97d,
+ +5.203055142575272E97d,
+ +1.4143368931719686E98d,
+ +3.8445667684706366E98d,
+ +1.0450615121235744E99d,
+ +2.8407720200442806E99d,
+ +7.722018663521402E99d,
+ +2.0990624115923312E100d,
+ +5.705842978547001E100d,
+ +1.5510089388648915E101d,
+ +4.216079296087462E101d,
+ +1.1460491592124923E102d,
+ +3.1152847602082673E102d,
+ +8.468222063292654E102d,
+ +2.3019011105282883E103d,
+ +6.257216813084462E103d,
+ +1.7008878437355237E104d,
+ +4.62349260394851E104d,
+ +1.2567956334920216E105d,
+ +3.416324322370112E105d,
+ +9.286532888251822E105d,
+ +2.5243410574836706E106d,
+ +6.861870970598542E106d,
+ +1.8652499723625443E107d,
+ +5.070274654122399E107d,
+ +1.3782437251846782E108d,
+ +3.746454626411946E108d,
+ +1.0183920005400422E109d,
+ +2.768276122845335E109d,
+ +7.524954624697075E109d,
+ +2.0454950851007314E110d,
+ +5.56023190218245E110d,
+ +1.511427628805191E111d,
+ +4.1084862677372065E111d,
+ +1.1168024085164686E112d,
+ +3.0357834799588566E112d,
+ +8.252116273466952E112d,
+ +2.2431576057283144E113d,
+ +6.097534318207731E113d,
+ +1.65748157925005E114d,
+ +4.5055022172222453E114d,
+ +1.2247224482958058E115d,
+ +3.329140840363789E115d,
+ +9.049543313665034E115d,
+ +2.4599209935197392E116d,
+ +6.686758417135634E116d,
+ +1.817649308779104E117d,
+ +4.940883275207154E117d,
+ +1.3430713954289087E118d,
+ +3.6508464654683645E118d,
+ +9.924030156169606E118d,
+ +2.697631034485758E119d,
+ +7.332921137166064E119d,
+ +1.9932945470297703E120d,
+ +5.418336099279846E120d,
+ +1.472856595860236E121d,
+ +4.0036393271908754E121d,
+ +1.0883019300873278E122d,
+ +2.9583112936666607E122d,
+ +8.041523923017192E122d,
+ +2.1859129781586158E123d,
+ +5.941927186144745E123d,
+ +1.6151834292371802E124d,
+ +4.390523815859274E124d,
+ +1.1934680816813702E125d,
+ +3.2441826014060764E125d,
+ +8.81860282490643E125d,
+ +2.3971445233885962E126d,
+ +6.516115189736396E126d,
+ +1.7712635751001657E127d,
+ +4.814793918384117E127d,
+ +1.3087966177291396E128d,
+ +3.557678449715009E128d,
+ +9.670771210463886E128d,
+ +2.628788218289742E129d,
+ +7.145787619369324E129d,
+ +1.9424264981694277E130d,
+ +5.280062387569078E130d,
+ +1.4352697002457768E131d,
+ +3.901467289560222E131d,
+ +1.0605288965077546E132d,
+ +2.882816299252225E132d,
+ +7.836307815186044E132d,
+ +2.1301292155181736E133d,
+ +5.790291758828013E133d,
+ +1.573964437869041E134d,
+ +4.278478878300888E134d,
+ +1.1630112062985817E135d,
+ +3.1613917467297413E135d,
+ +8.593554223894477E135d,
+ +2.335970335559215E136d,
+ +6.349826172787151E136d,
+ +1.7260616357651607E137d,
+ +4.691921416188566E137d,
+ +1.2753966504932798E138d,
+ +3.466887271843006E138d,
+ +9.423976538577447E138d,
+ +2.561702766944378E139d,
+ +6.963429563637273E139d,
+ +1.892856346657855E140d,
+ +5.1453167686439515E140d,
+ +1.3986421289359558E141d,
+ +3.8019036618832785E141d,
+ +1.033464507572145E142d,
+ +2.809247950589945E142d,
+ +7.636326960498012E142d,
+ +2.075769060297565E143d,
+ +5.64252553828769E143d,
+ +1.5337974510118784E144d,
+ +4.169293918423203E144d,
+ +1.1333315586787883E145d,
+ +3.080714152600695E145d,
+ +8.374250298636991E145d,
+ +2.276357074042286E146d,
+ +6.187780443461367E146d,
+ +1.6820131331794073E147d,
+ +4.572185635487065E147d,
+ +1.2428488853188662E148d,
+ +3.378413594504258E148d,
+ +9.183480622172801E148d,
+ +2.4963286658278886E149d,
+ +6.785725312893433E149d,
+ +1.8445514681108982E150d,
+ +5.014010481958507E150d,
+ +1.3629491735708616E151d,
+ +3.7048805655699485E151d,
+ +1.0070909418550386E152d,
+ +2.7375567044077912E152d,
+ +7.441451374243517E152d,
+ +2.022795961737854E153d,
+ +5.4985298195094216E153d,
+ +1.494655405262451E154d,
+ +4.062894701808608E154d,
+ +1.1044092571980793E155d,
+ +3.002095574584687E155d,
+ +8.160542326793782E155d,
+ +2.218265110516721E156d,
+ +6.02987028472758E156d,
+ +1.6390888071605646E157d,
+ +4.455504920700703E157d,
+ +1.2111317421229415E158d,
+ +3.2921976772303727E158d,
+ +8.94912101169977E158d,
+ +2.432623425087251E159d,
+ +6.612555731556604E159d,
+ +1.7974788874847574E160d,
+ +4.8860545948985793E160d,
+ +1.328167263606087E161d,
+ +3.610333312791256E161d,
+ +9.813901863427107E161d,
+ +2.667695552814763E162d,
+ +7.251548346906463E162d,
+ +1.9711751621240536E163d,
+ +5.3582093498119173E163d,
+ +1.4565123573071036E164d,
+ +3.959211091077107E164d,
+ +1.0762251933089556E165d,
+ +2.9254832789181E165d,
+ +7.952287052787358E165d,
+ +2.161656025361765E166d,
+ +5.8759898326913254E166d,
+ +1.597259768214821E167d,
+ +4.3418021646459346E167d,
+ +1.1802241249113175E168d,
+ +3.2081817253680657E168d,
+ +8.720743087611513E168d,
+ +2.3705435424427623E169d,
+ +6.443805025317327E169d,
+ +1.7516078165936552E170d,
+ +4.7613641572445654E170d,
+ +1.2942728582966776E171d,
+ +3.518198614137319E171d,
+ +9.563454814394247E171d,
+ +2.5996166206245285E172d,
+ +7.066491077377918E172d,
+ +1.920871394985668E173d,
+ +5.221469250951617E173d,
+ +1.4193426880442385E174d,
+ +3.8581732071331E174d,
+ +1.0487601931965087E175d,
+ +2.850825930161946E175d,
+ +7.749348772180658E175d,
+ +2.1064911705560668E176d,
+ +5.726036941135634E176d,
+ +1.5564982816556894E177d,
+ +4.231000988846797E177d,
+ +1.1501053030837989E178d,
+ +3.1263099916916113E178d,
+ +8.498192212235393E178d,
+ +2.3100480183046895E179d,
+ +6.279361500971995E179d,
+ +1.7069074829463731E180d,
+ +4.63985600437427E180d,
+ +1.2612435745231905E181d,
+ +3.4284156709489884E181d,
+ +9.319400030019162E181d,
+ +2.5332752658571312E182d,
+ +6.88615578404537E182d,
+ +1.8718514371423056E183d,
+ +5.088219872370737E183d,
+ +1.3831214731781958E184d,
+ +3.759713966511158E184d,
+ +1.021996184153141E185d,
+ +2.778073442169904E185d,
+ +7.55158797540476E185d,
+ +2.0527342305586606E186d,
+ +5.579910641313343E186d,
+ +1.5167767828844167E187d,
+ +4.123026721295484E187d,
+ +1.1207549425651513E188d,
+ +3.0465278560980536E188d,
+ +8.281321669236493E188d,
+ +2.251096660331649E189d,
+ +6.119114404399683E189d,
+ +1.6633478556884994E190d,
+ +4.521448560089285E190d,
+ +1.2290570545894685E191d,
+ +3.340923580982338E191d,
+ +9.081571104550255E191d,
+ +2.468626868232408E192d,
+ +6.710424255583952E192d,
+ +1.8240823171621646E193d,
+ +4.958369974640573E193d,
+ +1.3478247120462365E194d,
+ +3.6637673548790206E194d,
+ +9.959152908532152E194d,
+ +2.707178052117959E195d,
+ +7.358873642076596E195d,
+ +2.0003490682463053E196d,
+ +5.4375131636754E196d,
+ +1.4780692924846082E197d,
+ +4.01780853635105E197d,
+ +1.0921536132159379E198d,
+ +2.968781250496917E198d,
+ +8.069984512111955E198d,
+ +2.193649279840519E199d,
+ +5.962956589227457E199d,
+ +1.620899738203635E200d,
+ +4.406062052965071E200d,
+ +1.1976919074588434E201d,
+ +3.2556641859513496E201d,
+ +8.849812639395597E201d,
+ +2.40562867677584E202d,
+ +6.539175932653188E202d,
+ +1.7775323307944624E203d,
+ +4.831833881898182E203d,
+ +1.3134287685114547E204d,
+ +3.5702693195009266E204d,
+ +9.704997606668411E204d,
+ +2.63809219778715E205d,
+ +7.171077244202293E205d,
+ +1.949300880034352E206d,
+ +5.298749302736127E206d,
+ +1.4403494631058154E207d,
+ +3.91527572177694E207d,
+ +1.0642823992403076E208d,
+ +2.8930193727937684E208d,
+ +7.8640411896421955E208d,
+ +2.1376680994038112E209d,
+ +5.8107841809216616E209d,
+ +1.5795351101531684E210d,
+ +4.293620869258453E210d,
+ +1.1671272667059652E211d,
+ +3.172580666390786E211d,
+ +8.623968972387222E211d,
+ +2.3442378838418366E212d,
+ +6.372298757235201E212d,
+ +1.7321703934464356E213d,
+ +4.708527306855985E213d,
+ +1.279910496643312E214d,
+ +3.479157135998568E214d,
+ +9.45732984079136E214d,
+ +2.5707689593428096E215d,
+ +6.988074107282322E215d,
+ +1.8995553996578656E216d,
+ +5.1635269305465607E216d,
+ +1.4035923083915864E217d,
+ +3.815359096108819E217d,
+ +1.0371220592190472E218d,
+ +2.819190456167585E218d,
+ +7.663353127378024E218d,
+ +2.083115484919861E219d,
+ +5.662495731848751E219d,
+ +1.5392257142577226E220d,
+ +4.184049381430498E220d,
+ +1.1373425785132867E221d,
+ +3.091617462831603E221d,
+ +8.403887374207366E221d,
+ +2.2844135610697528E222d,
+ +6.209679892802781E222d,
+ +1.6879660933816274E223d,
+ +4.588367423411997E223d,
+ +1.2472476068464461E224d,
+ +3.3903703993793316E224d,
+ +9.215982463319503E224d,
+ +2.5051637206758385E225d,
+ +6.809741127603255E225d,
+ +1.8510795864289367E226d,
+ +5.031755776868959E226d,
+ +1.3677729802316034E227d,
+ +3.7179924024793253E227d,
+ +1.0106552237522032E228d,
+ +2.7472456017809066E228d,
+ +7.467788172398272E228d,
+ +2.029955237703202E229d,
+ +5.517990469846618E229d,
+ +1.4999452522236406E230d,
+ +4.0772734783595525E230d,
+ +1.1083180046837618E231d,
+ +3.012720614547867E231d,
+ +8.18942426109357E231d,
+ +2.2261161215322043E232d,
+ +6.051211457626543E232d,
+ +1.6448897917725177E233d,
+ +4.471273900208441E233d,
+ +1.2154183152078517E234d,
+ +3.3038494682728794E234d,
+ +8.98079409878202E234d,
+ +2.4412328161430576E235d,
+ +6.63595840453991E235d,
+ +1.8038406914061554E236d,
+ +4.90334700062756E236d,
+ +1.3328680266667662E237d,
+ +3.623110695743118E237d,
+ +9.848636053777669E237d,
+ +2.677136737066629E238d,
+ +7.277212447141125E238d,
+ +1.978151484427976E239d,
+ +5.377173488599035E239d,
+ +1.4616672175682191E240d,
+ +3.973222981713661E240d,
+ +1.0800340064859439E241d,
+ +2.935837009891444E241d,
+ +7.980432566722885E241d,
+ +2.169306470354036E242d,
+ +5.896786161387733E242d,
+ +1.6029126916635028E243d,
+ +4.357168123448786E243d,
+ +1.1844011798406507E244d,
+ +3.2195361624179725E244d,
+ +8.751606149833694E244d,
+ +2.3789334438756013E245d,
+ +6.466611224443739E245d,
+ +1.7578073785142153E246d,
+ +4.7782149589194885E246d,
+ +1.2988535295611824E247d,
+ +3.5306502960727705E247d,
+ +9.597302512507479E247d,
+ +2.608817438130718E248d,
+ +7.091500562953208E248d,
+ +1.9276698418065647E249d,
+ +5.239949786641934E249d,
+ +1.42436589329759E250d,
+ +3.8718282216768776E250d,
+ +1.0524719896550007E251d,
+ +2.860915548426704E251d,
+ +7.77677492833005E251d,
+ +2.113946677051906E252d,
+ +5.7463023795153145E252d,
+ +1.56200679236425E253d,
+ +4.2459748085663055E253d,
+ +1.1541756557557508E254d,
+ +3.137374584307575E254d,
+ +8.528268445871411E254d,
+ +2.3182239583484444E255d,
+ +6.301585387776819E255d,
+ +1.7129486892266285E256d,
+ +4.6562769567905925E256d,
+ +1.26570724146049E257d,
+ +3.4405490416979487E257d,
+ +9.352382323649647E257d,
+ +2.54224113415832E258d,
+ +6.910528108396216E258d,
+ +1.8784760208391767E259d,
+ +5.106228040084293E259d,
+ +1.3880166914480165E260d,
+ +3.7730204737910044E260d,
+ +1.0256131352582533E261d,
+ +2.787906051540986E261d,
+ +7.578313650939932E261d,
+ +2.0599991793068063E262d,
+ +5.5996586041611455E262d,
+ +1.522145133131402E263d,
+ +4.137618951061827E263d,
+ +1.1247213964487372E264d,
+ +3.0573102223682595E264d,
+ +8.310629417537063E264d,
+ +2.2590636576955473E265d,
+ +6.1407711078356886E265d,
+ +1.6692346202615142E266d,
+ +4.5374504961394207E266d,
+ +1.2334070098307164E267d,
+ +3.3527476928456816E267d,
+ +9.113713162029408E267d,
+ +2.4773638527240193E268d,
+ +6.734172833429278E268d,
+ +1.8305382378470305E269d,
+ +4.9759187284770303E269d,
+ +1.352594940263854E270d,
+ +3.6767339705169146E270d,
+ +9.994400500679653E270d,
+ +2.716759624268743E271d,
+ +7.384918458508588E271d,
+ +2.007428933605617E272d,
+ +5.456757565532369E272d,
+ +1.4833003969415539E273d,
+ +4.0320284712983994E273d,
+ +1.096019026243815E274d,
+ +2.979288529962515E274d,
+ +8.098545495417704E274d,
+ +2.201412886580694E275d,
+ +5.984060832462728E275d,
+ +1.6266362950862408E276d,
+ +4.4216561713555547E276d,
+ +1.2019307065458128E277d,
+ +3.2671863888979078E277d,
+ +8.881133159512924E277d,
+ +2.4141423627760256E278d,
+ +6.562319473965767E278d,
+ +1.7838233889223407E279d,
+ +4.848934634563382E279d,
+ +1.3180771991576186E280d,
+ +3.5829049382293792E280d,
+ +9.739345931419228E280d,
+ +2.6474285478041252E281d,
+ +7.196457718729758E281d,
+ +1.956199868121249E282d,
+ +5.31750271790054E282d,
+ +1.4454470027638629E283d,
+ +3.929132560365955E283d,
+ +1.0680488848057261E284d,
+ +2.9032581477488686E284d,
+ +7.89187408872514E284d,
+ +2.1452336456259667E285d,
+ +5.831349876080173E285d,
+ +1.5851251724785243E286d,
+ +4.308816643345461E286d,
+ +1.1712579802975851E287d,
+ +3.1838092090922606E287d,
+ +8.654490685278886E287d,
+ +2.3525345191912968E288d,
+ +6.39485115791896E288d,
+ +1.7383009254496851E289d,
+ +4.725191397657393E289d,
+ +1.2844402232816276E290d,
+ +3.491470347090126E290d,
+ +9.490800658395667E290d,
+ +2.579867270991543E291d,
+ +7.012806239173502E291d,
+ +1.906278351789277E292d,
+ +5.181801397059486E292d,
+ +1.408559707497606E293d,
+ +3.8288623079292884E293d,
+ +1.0407926842436056E294d,
+ +2.829168201470791E294d,
+ +7.690475570840264E294d,
+ +2.0904882610105383E295d,
+ +5.68253547942899E295d,
+ +1.544673396032028E296d,
+ +4.1988574190754736E296d,
+ +1.1413677466646359E297d,
+ +3.102559332875688E297d,
+ +8.433630296371073E297d,
+ +2.292498520423419E298d,
+ +6.23165710486722E298d,
+ +1.6939399242810123E299d,
+ +4.604606371472047E299d,
+ +1.2516618713553432E300d,
+ +3.402369329874797E300d,
+ +9.248598815279678E300d,
+ +2.51402968559859E301d,
+ +6.833842035076675E301d,
+ +1.8576309291617257E302d,
+ +5.049564425991982E302d,
+ +1.3726137091534984E303d,
+ +3.7311513682845094E303d,
+ +1.0142320772726397E304d,
+ +2.7569686255975333E304d,
+ +7.494218049456063E304d,
+ +2.037139607241041E305d,
+ +5.5375196488302575E305d,
+ +1.5052539519895093E306d,
+ +4.091704288360009E306d,
+ +1.1122405335641184E307d,
+ +3.023383151402969E307d,
+ +8.218407798110846E307d,
+ Double.NaN,
+ Double.NaN,
+ Double.NaN,
+ Double.NaN,
+ Double.NaN,
+ Double.NaN,
+ Double.NaN,
+ Double.NaN,
+ Double.NaN,
+ Double.NaN,
+ Double.NaN,
+ Double.NaN,
+ Double.NaN,
+ Double.NaN,
+ Double.NaN,
+ Double.NaN,
+ Double.NaN,
+ Double.NaN,
+ Double.NaN,
+ Double.NaN,
+ Double.NaN,
+ Double.NaN,
+ Double.NaN,
+ Double.NaN,
+ Double.NaN,
+ Double.NaN,
+ Double.NaN,
+ Double.NaN,
+ Double.NaN,
+ Double.NaN,
+ Double.NaN,
+ Double.NaN,
+ Double.NaN,
+ Double.NaN,
+ Double.NaN,
+ Double.NaN,
+ Double.NaN,
+ Double.NaN,
+ Double.NaN,
+ Double.NaN,
+ };
+
+ /**
+ * Exponential evaluated at integer values, exp(x) = expIntTableA[x + EXP_INT_TABLE_MAX_INDEX] +
+ * expIntTableB[x+EXP_INT_TABLE_MAX_INDEX]
+ */
+ private static final double[] EXP_INT_B =
+ new double[] {
+ +0.0d,
+ Double.NaN,
+ Double.NaN,
+ Double.NaN,
+ Double.NaN,
+ Double.NaN,
+ Double.NaN,
+ Double.NaN,
+ Double.NaN,
+ Double.NaN,
+ Double.NaN,
+ Double.NaN,
+ Double.NaN,
+ Double.NaN,
+ Double.NaN,
+ Double.NaN,
+ Double.NaN,
+ Double.NaN,
+ Double.NaN,
+ Double.NaN,
+ Double.NaN,
+ Double.NaN,
+ Double.NaN,
+ Double.NaN,
+ Double.NaN,
+ Double.NaN,
+ Double.NaN,
+ Double.NaN,
+ Double.NaN,
+ Double.NaN,
+ Double.NaN,
+ Double.NaN,
+ Double.NaN,
+ Double.NaN,
+ Double.NaN,
+ Double.NaN,
+ Double.NaN,
+ Double.NaN,
+ Double.NaN,
+ Double.NaN,
+ Double.NaN,
+ -1.76097684E-316d,
+ -2.44242319E-315d,
+ -9.879317845E-315d,
+ -1.3811462167E-314d,
+ +2.1775261204E-314d,
+ -1.4379095864E-313d,
+ +1.4219324087E-313d,
+ +1.00605438061E-312d,
+ -1.287101187097E-312d,
+ +5.33839690397E-312d,
+ -9.35130825405E-313d,
+ -4.15218681073E-311d,
+ +4.546040329134E-311d,
+ -1.57333572310673E-310d,
+ +1.05387548454467E-309d,
+ +2.095732474644446E-309d,
+ -2.62524392470767E-310d,
+ +5.86440876259637E-309d,
+ -2.401816502004675E-309d,
+ -2.2711230715729753E-308d,
+ +2.0670460065057715E-307d,
+ +3.436860020483706E-308d,
+ +2.0862243734177337E-306d,
+ -4.637025318037353E-306d,
+ +9.222671009756424E-306d,
+ +6.704597874020559E-305d,
+ +4.351284159444109E-305d,
+ +4.232889602759328E-304d,
+ +1.2840977763293412E-303d,
+ -2.6993478083348727E-303d,
+ -1.053265874779237E-303d,
+ +1.207746682843556E-303d,
+ +5.21281096513035E-303d,
+ +1.6515377082609677E-301d,
+ +3.3951607353932444E-301d,
+ +5.609418227003629E-301d,
+ +4.238775357914848E-300d,
+ -9.441842771290538E-300d,
+ -2.1745347282493023E-299d,
+ -6.203839803215248E-299d,
+ -5.617718879466363E-299d,
+ +5.2869976233132615E-298d,
+ -1.4300075619643524E-298d,
+ +4.3198234936686506E-297d,
+ -2.6448316331572387E-297d,
+ +4.315655444002347E-296d,
+ -7.253671992213344E-296d,
+ -1.1288398461391523E-295d,
+ -4.83901764243093E-296d,
+ +1.7407497662694827E-295d,
+ +1.1969717029666017E-294d,
+ -7.752519943329177E-294d,
+ -4.019569741253664E-293d,
+ -2.4467928392518484E-293d,
+ -1.0269233640424235E-292d,
+ -3.2330960700986594E-292d,
+ -1.440995270758115E-291d,
+ -3.726946038150935E-291d,
+ -1.3424576100819801E-291d,
+ -3.128894928199484E-290d,
+ -5.989337506920005E-290d,
+ -9.438168176533759E-290d,
+ -1.9220613500411237E-289d,
+ +2.1186736024949195E-289d,
+ +6.3015208029537436E-288d,
+ -8.168129112703755E-288d,
+ -1.6040513288090055E-287d,
+ -1.0809972724404233E-287d,
+ -3.080380385962424E-286d,
+ +2.6399157174374624E-286d,
+ +1.3317127674213423E-285d,
+ -3.5821668044872306E-285d,
+ +1.978536584535392E-284d,
+ +1.3399392455370071E-284d,
+ -2.870168560029448E-284d,
+ +3.5311184272663063E-283d,
+ -7.204247881190918E-283d,
+ +3.2425604548983798E-282d,
+ +3.913063150326019E-282d,
+ -2.260957518848075E-281d,
+ +3.807242187736102E-281d,
+ -5.095591405025083E-281d,
+ +2.3400625068490396E-280d,
+ -1.1564717694090882E-280d,
+ -3.517594695450786E-279d,
+ +6.666544384808297E-279d,
+ -9.204784113858607E-279d,
+ +4.8677119923665573E-278d,
+ +7.942176091555472E-278d,
+ -2.5113270522478854E-277d,
+ +5.332900939354667E-277d,
+ -3.491241408725929E-276d,
+ -2.1141094074221325E-276d,
+ +1.722049095222509E-275d,
+ +4.0430160253378594E-275d,
+ +1.9888195459082551E-274d,
+ +3.230089643550739E-275d,
+ +5.077824728028163E-274d,
+ -3.526547961682877E-274d,
+ -6.4376298274983765E-273d,
+ -2.5338279333399964E-272d,
+ -3.614847626733713E-272d,
+ +2.510812179067931E-272d,
+ +3.953806005373127E-272d,
+ +7.112596406315374E-272d,
+ -2.850217520533226E-270d,
+ -8.571477929711754E-270d,
+ +1.2902019831221148E-269d,
+ -6.978783784755863E-270d,
+ +9.89845486618531E-269d,
+ -3.538563171970534E-268d,
+ +3.537475449241181E-268d,
+ +3.6924578046381256E-267d,
+ +1.3555502536444713E-266d,
+ -1.1279742372661484E-266d,
+ +5.475072932318336E-266d,
+ -1.1679889049814275E-265d,
+ -8.946297908979776E-266d,
+ +1.0565816011650582E-264d,
+ -3.2161237736296753E-265d,
+ -6.022045553485609E-264d,
+ -2.0332050860436034E-263d,
+ -1.0488538406930105E-262d,
+ +1.6793752843984384E-262d,
+ +3.2558720916543104E-263d,
+ -1.9546569053899882E-262d,
+ +5.082190670014963E-262d,
+ -1.0188117475357564E-260d,
+ +3.7920054509691455E-261d,
+ -8.330969967504819E-260d,
+ -1.1623181434592597E-259d,
+ +9.09665088462258E-259d,
+ -1.56400149127482E-259d,
+ -7.796557225750673E-258d,
+ +6.751460509863465E-258d,
+ +7.243157658226935E-258d,
+ +1.2574668958946027E-256d,
+ +2.2678858131411216E-256d,
+ +5.1079306249351287E-256d,
+ -5.672261759108003E-257d,
+ +3.476539491009769E-256d,
+ -1.3481093992496937E-254d,
+ -3.314051560952014E-254d,
+ +7.408112967339146E-255d,
+ -7.164884605413269E-254d,
+ -6.456588023278983E-253d,
+ -1.4881197370811587E-252d,
+ +1.7534012237555307E-252d,
+ -1.3070101381473173E-251d,
+ +6.081420141954215E-251d,
+ +6.591143677421159E-251d,
+ +2.6917461073773043E-250d,
+ +3.683043641790553E-251d,
+ +1.2195076420741757E-249d,
+ -8.220283439582378E-249d,
+ +1.637852737426943E-248d,
+ -8.332543237340988E-249d,
+ +2.9581193516975647E-248d,
+ -1.7790661150204172E-247d,
+ -1.7809679916043692E-247d,
+ +8.378574405736031E-247d,
+ -2.883847036065813E-246d,
+ +1.3223776943337897E-245d,
+ +3.098547586845664E-245d,
+ -1.1036542789147287E-244d,
+ -5.7187703271582225E-244d,
+ -1.8058492822440396E-244d,
+ +4.4373726292703545E-243d,
+ -3.4631935816990754E-243d,
+ -1.82770041073856E-243d,
+ +3.845535085273936E-242d,
+ +8.446532344375812E-242d,
+ +2.7751016140238277E-242d,
+ +1.3158882241538003E-241d,
+ -3.579433051074272E-240d,
+ -6.151751570213211E-240d,
+ -2.990535475079021E-239d,
+ +2.3396028616528764E-239d,
+ +7.233790684263346E-239d,
+ +1.0847913100494912E-238d,
+ +7.103148400942551E-238d,
+ +3.463600299750966E-237d,
+ -4.873121855093712E-237d,
+ +1.3407295326570417E-236d,
+ +9.390271617387205E-237d,
+ -2.4767709454727603E-235d,
+ +3.205923535388443E-235d,
+ -1.0074984709952582E-234d,
+ +2.4747880175747574E-234d,
+ -5.146939682310558E-234d,
+ -2.827581009333298E-233d,
+ -3.0307641004671077E-233d,
+ +5.92044714050651E-233d,
+ -2.0582596893119236E-232d,
+ -6.58066591313112E-232d,
+ -4.869955151949929E-231d,
+ -5.763495903609913E-231d,
+ -2.3580462372762525E-230d,
+ +1.8559980428862584E-230d,
+ +2.854978560542175E-229d,
+ +5.637945686485334E-229d,
+ +2.1454644909004582E-228d,
+ -1.1918070206953359E-228d,
+ -5.021851606912854E-228d,
+ +3.861525553653117E-227d,
+ +6.533561982617909E-227d,
+ -3.015709444206057E-226d,
+ -5.042005018212734E-227d,
+ +1.5959614205422845E-225d,
+ +2.0402105689098835E-224d,
+ +5.164902728917601E-224d,
+ +9.981031744879876E-224d,
+ +4.0281104210095145E-223d,
+ +1.1158160971176672E-222d,
+ +2.0736172194624895E-222d,
+ +4.983162653734032E-222d,
+ +2.1753390051977871E-221d,
+ +3.969413618002761E-221d,
+ +1.3961255018698695E-220d,
+ +2.1290855095314206E-220d,
+ +1.1927747883417406E-219d,
+ +3.7264401117998796E-219d,
+ +9.318532410862293E-219d,
+ +2.3414841777613345E-218d,
+ +4.3791842770430786E-218d,
+ +1.7173159016511951E-217d,
+ +3.5037536832675478E-217d,
+ +1.4300098613455884E-216d,
+ +2.4189403362149483E-216d,
+ +9.306541421999056E-216d,
+ +3.442100456607687E-215d,
+ +5.94407068841904E-215d,
+ +2.0483260435783403E-214d,
+ +3.8410992889527954E-214d,
+ +1.2038281262953917E-213d,
+ +3.865007795216205E-213d,
+ +9.754659138599756E-213d,
+ +2.7653605770745684E-212d,
+ +5.359568079675375E-212d,
+ +2.61726605666378E-211d,
+ +5.054202073556894E-211d,
+ +8.707092668016246E-211d,
+ +1.4080573899148006E-210d,
+ +1.288124387778789E-209d,
+ +1.8639901642011898E-209d,
+ +6.076014540574561E-209d,
+ +1.798489141298457E-208d,
+ +2.1525406805994896E-208d,
+ +1.1864056832305874E-207d,
+ +2.1077440662171152E-207d,
+ +1.3784853708457332E-206d,
+ +1.6965806532093783E-206d,
+ +7.241626420445137E-206d,
+ +2.575584299085016E-205d,
+ +6.151951078101721E-205d,
+ +2.40652042118887E-204d,
+ +4.022633486003565E-204d,
+ +5.8840879519086286E-204d,
+ +3.2820308007277566E-203d,
+ +4.31880454864738E-203d,
+ +2.427240455243201E-202d,
+ +7.326955749884755E-202d,
+ +1.4310184489676175E-201d,
+ +4.464279133463661E-201d,
+ +4.895131474682867E-201d,
+ +4.48614966943544E-200d,
+ +8.924048768324976E-200d,
+ +2.5035535029701945E-199d,
+ +6.627829836338812E-199d,
+ +2.6066826304502746E-198d,
+ +8.042275310036546E-198d,
+ +2.115062964308555E-197d,
+ +4.413745413236018E-197d,
+ +1.644449394585716E-196d,
+ +3.138217752973845E-196d,
+ +7.48533983136081E-196d,
+ +2.613626422028823E-195d,
+ +3.6741841454219095E-195d,
+ +5.906102862953403E-195d,
+ +4.4940857547850743E-194d,
+ +5.840064709376958E-194d,
+ +3.087661273836024E-193d,
+ +4.995552216100365E-193d,
+ +1.991444798915497E-192d,
+ +7.097454751809522E-192d,
+ +2.0510193986749737E-191d,
+ +5.759440286608551E-191d,
+ +1.7013941257113314E-190d,
+ +2.1383323934483528E-190d,
+ +8.280292810015406E-190d,
+ +3.138655772049104E-189d,
+ +7.961506427685701E-189d,
+ +2.0579001228504997E-188d,
+ +7.530840351477639E-188d,
+ +1.4582863136475673E-187d,
+ +3.149267215638608E-187d,
+ +5.443114553057336E-187d,
+ +3.4672966834277804E-186d,
+ +7.374944406615125E-186d,
+ +2.7318417252599104E-185d,
+ +7.913674211949961E-185d,
+ +2.5217716516462005E-184d,
+ +4.0866585874353075E-184d,
+ +1.2087698972768686E-183d,
+ +3.7072473866919033E-183d,
+ +1.1333588840402273E-182d,
+ +1.61949812578045E-182d,
+ +6.567779607147072E-182d,
+ +2.422974840736314E-181d,
+ +2.551170809294396E-181d,
+ +1.0905890688083124E-180d,
+ +3.221279639653057E-180d,
+ +7.068244813489027E-180d,
+ +1.3752309224575428E-179d,
+ +7.20154303462761E-179d,
+ +1.5391707185581056E-178d,
+ +7.708777608683431E-178d,
+ +5.597398155472547E-178d,
+ +1.8487854656676722E-177d,
+ +1.0577249492414076E-176d,
+ +2.8926683313922764E-176d,
+ +4.090184282164232E-176d,
+ +1.6142943398013813E-175d,
+ +7.873864351702525E-175d,
+ +2.242630017261011E-174d,
+ +3.4637009373878283E-174d,
+ +1.5907089565090164E-173d,
+ +1.6985075903314236E-173d,
+ +1.1552273904608563E-172d,
+ +2.237894048535414E-172d,
+ +5.321990399912051E-172d,
+ +1.4106062639738257E-171d,
+ +2.9850404523368767E-171d,
+ +1.5683802588004895E-170d,
+ +4.880146806045633E-170d,
+ +1.1489352403441815E-169d,
+ +1.6401586605693734E-169d,
+ +8.29169700697816E-169d,
+ +1.0380723705441457E-168d,
+ +7.126414081261746E-168d,
+ +1.253325949455206E-167d,
+ +2.595079206183114E-167d,
+ +1.537490712803659E-166d,
+ +2.6338455225993276E-166d,
+ +7.994936425058567E-166d,
+ +1.5716634677516025E-165d,
+ +3.669404761339415E-165d,
+ +1.9941628263579332E-164d,
+ +4.5012079983352374E-164d,
+ +7.283163019991001E-164d,
+ +2.398038505188316E-163d,
+ +7.868666894503849E-163d,
+ +2.1478649410390003E-162d,
+ +8.306111510463843E-162d,
+ +1.5453160659068463E-161d,
+ -4.590496588813841E-162d,
+ +3.5449293983801232E-161d,
+ -1.0440854056870505E-160d,
+ -2.321064927632431E-160d,
+ +5.707867001443433E-160d,
+ -2.238614484037969E-159d,
+ +2.482282821883242E-159d,
+ -1.1508772192025259E-158d,
+ +1.9903990578876104E-158d,
+ -1.2116165315442256E-158d,
+ -2.9084557554502667E-157d,
+ -1.1211083853006645E-156d,
+ -1.309893394818129E-156d,
+ +4.2269712317468864E-156d,
+ -7.678973146281339E-156d,
+ +3.2021376921211934E-155d,
+ -7.08313012515209E-155d,
+ +1.944398214330544E-154d,
+ +1.1860061363751161E-153d,
+ +1.5234465914578058E-153d,
+ -2.9020908354550263E-153d,
+ +4.980100072851796E-153d,
+ +2.3101551448625578E-152d,
+ -1.1959241322537072E-151d,
+ -9.27398924154364E-153d,
+ +5.999390491704392E-152d,
+ +1.3373196561281372E-150d,
+ -1.0271780540759147E-150d,
+ +2.575620466387945E-150d,
+ -6.56250013356227E-149d,
+ -1.1961357917482867E-148d,
+ +5.5807813570926636E-148d,
+ +9.252840189663807E-148d,
+ -1.830335419852293E-147d,
+ +9.350990339947455E-147d,
+ -1.6072409939877762E-146d,
+ -2.5309995887229526E-146d,
+ -1.6014373376410622E-146d,
+ -3.303297758377758E-145d,
+ +1.5640419864850202E-145d,
+ +9.544642884951585E-145d,
+ -8.64864445321803E-144d,
+ +7.580392204597681E-144d,
+ +2.678334184447568E-143d,
+ -3.7269289985326055E-143d,
+ -2.851965258161176E-142d,
+ +7.243267286265823E-142d,
+ +4.4510805312036926E-141d,
+ +9.008499734799015E-141d,
+ +1.130435759928337E-140d,
+ -3.096539751496479E-140d,
+ -1.497405487919762E-139d,
+ +3.51519845948652E-139d,
+ -4.713790209541894E-139d,
+ +4.740753295616865E-138d,
+ +9.517570994930463E-138d,
+ -1.8842098029339485E-137d,
+ -3.825558165008403E-137d,
+ +1.1817638600528107E-136d,
+ -3.514601201473235E-136d,
+ -6.344612631552417E-136d,
+ -1.6754164785291923E-136d,
+ +4.445372986583078E-135d,
+ -3.89604237755475E-134d,
+ -1.0155552195374609E-134d,
+ +2.1858142063550155E-134d,
+ +3.497714990137842E-133d,
+ -7.635830383612894E-133d,
+ +1.2050744860079718E-132d,
+ -7.683019590615251E-133d,
+ -3.344806129021162E-131d,
+ -1.6737914131474577E-131d,
+ -4.30610076666344E-131d,
+ +5.184023388254773E-130d,
+ +2.6290763595212492E-129d,
+ +7.90041744728452E-130d,
+ -3.204473056113754E-129d,
+ -2.552517201762272E-128d,
+ +7.130134251490065E-128d,
+ -3.2244113258340395E-127d,
+ -1.064920993515727E-127d,
+ +2.7466520735457463E-126d,
+ +4.368312797746065E-126d,
+ +1.8802599072446818E-125d,
+ -4.257625799463564E-125d,
+ +5.491672256552995E-125d,
+ +3.7298611779671127E-124d,
+ +5.724180836308973E-124d,
+ +1.3861841053630075E-123d,
+ +4.2303826056297614E-123d,
+ +3.5335436928899096E-123d,
+ -2.522906629540626E-122d,
+ +1.0147808005267102E-121d,
+ +6.734406065735473E-122d,
+ -4.948973160958133E-121d,
+ +2.4256181927024344E-120d,
+ +4.9056283164780554E-120d,
+ +6.846440394397547E-120d,
+ +3.512747689569002E-119d,
+ -9.020907406701404E-119d,
+ +2.5718749916003624E-118d,
+ +4.3724191002977524E-119d,
+ +1.001455050575191E-117d,
+ -2.4442443105031435E-117d,
+ +2.38873950760028E-116d,
+ -4.831068747037129E-118d,
+ -5.148989321866988E-116d,
+ +1.7875271881514469E-115d,
+ -1.1821586412088555E-114d,
+ +4.43247726423679E-115d,
+ +4.634817120492781E-114d,
+ +1.671311907037975E-113d,
+ -4.595250028278979E-113d,
+ -5.905511605694905E-113d,
+ -1.3657642265608213E-112d,
+ +2.881416869529271E-112d,
+ +2.1253302469985373E-111d,
+ -5.301386276260592E-111d,
+ +1.4198782892306878E-112d,
+ -3.395494928605007E-110d,
+ +9.284633292147283E-110d,
+ -6.054133004896379E-110d,
+ -8.324100783330331E-109d,
+ -2.193190669794277E-108d,
+ +1.3613655394659198E-107d,
+ +6.463452607647978E-108d,
+ +1.0187183636134904E-106d,
+ +1.0705673935006142E-106d,
+ +2.509050608571019E-106d,
+ -1.5096182622106617E-105d,
+ +1.7794190449526737E-106d,
+ +1.2261246749706581E-104d,
+ +2.1377905661197194E-104d,
+ -2.2015877944429946E-104d,
+ +7.873970951802825E-104d,
+ -1.7999197335480384E-103d,
+ +1.0487383011058756E-105d,
+ -2.9988278531841556E-102d,
+ +4.7976477743232285E-102d,
+ +3.452316818502442E-102d,
+ +5.89953246760617E-101d,
+ -4.0785601577267006E-101d,
+ +2.7214076662438963E-100d,
+ +5.237807655758373E-100d,
+ +6.180972117932364E-99d,
+ -1.3019742873005683E-98d,
+ +4.501188264957416E-99d,
+ -2.4075054705261798E-98d,
+ +1.6503086546628772E-97d,
+ -6.878666975101243E-97d,
+ +1.196718116616528E-96d,
+ +2.476190162339265E-96d,
+ -7.1844969234484515E-96d,
+ +5.088386759261555E-95d,
+ +6.749368983223726E-95d,
+ +1.965737856765605E-94d,
+ -5.574080023496771E-94d,
+ +1.2493696959436675E-93d,
+ +8.533262777516794E-94d,
+ -7.225259028588793E-93d,
+ -7.340587186324432E-93d,
+ -3.482412195764625E-92d,
+ +3.4742610108480497E-91d,
+ -7.177274244758699E-91d,
+ +1.2736636153072213E-90d,
+ -5.730160886217076E-90d,
+ -1.545495535488274E-89d,
+ +1.1304179460367007E-89d,
+ +1.249260560756154E-88d,
+ -4.7439719382414206E-88d,
+ +7.164663249266942E-88d,
+ +1.7617425105337704E-87d,
+ +2.4175248449172035E-87d,
+ -1.043079666926483E-86d,
+ -2.8137609614326677E-86d,
+ -1.2091497144395591E-85d,
+ +3.7944631664558904E-85d,
+ -2.8144926807308225E-85d,
+ +3.9782728352520784E-85d,
+ +4.313978872469646E-84d,
+ +5.82190887044604E-84d,
+ +5.883385169571802E-83d,
+ +1.134857098306787E-82d,
+ +3.468049324128309E-82d,
+ +2.625423995658143E-82d,
+ -3.42827917465521E-81d,
+ +5.119461911618321E-81d,
+ -2.134387988350615E-80d,
+ -4.4703076268400615E-80d,
+ +4.806078883451016E-80d,
+ +2.3820250362443495E-79d,
+ -7.258551497833573E-79d,
+ -4.0297835558876335E-78d,
+ +2.1424166787650852E-78d,
+ -3.2117127164185917E-77d,
+ +4.8459153070935316E-77d,
+ -1.766924303914492E-76d,
+ -2.6921749814579492E-76d,
+ -4.1291070428848755E-76d,
+ +2.2086994756104319E-75d,
+ -7.814146377574201E-75d,
+ -1.9589778310104216E-74d,
+ +6.52658129486538E-74d,
+ +1.7804909509998055E-74d,
+ -4.1900132227036916E-73d,
+ +1.5705861683841123E-72d,
+ -1.904714978998808E-72d,
+ -7.81295459930537E-72d,
+ +2.818537910881676E-71d,
+ +5.840507984320445E-71d,
+ +1.7331720051707087E-70d,
+ +1.936946987935961E-70d,
+ -5.86517231340979E-71d,
+ -1.3277440528416646E-69d,
+ +1.9906256185827793E-69d,
+ +8.668714514280051E-69d,
+ +6.643105496829061E-69d,
+ -2.5436254170647032E-67d,
+ -4.8279217213630774E-67d,
+ -1.2640304072937576E-66d,
+ +3.51187258511716E-66d,
+ +1.4199501303738373E-65d,
+ -1.2351697477129173E-65d,
+ +7.0542365522414836E-65d,
+ +1.030593104122615E-64d,
+ -5.452692909894593E-65d,
+ -9.415506349675128E-64d,
+ -3.6206211446779087E-63d,
+ -1.6699188275658641E-62d,
+ +2.287280262665656E-62d,
+ +7.076135457313529E-62d,
+ +2.9019628518165404E-61d,
+ -3.1305705497720186E-61d,
+ +2.2978757040142953E-60d,
+ +1.2424439441817321E-60d,
+ +7.140343013236265E-60d,
+ +8.633726388939636E-60d,
+ +1.3483035574114863E-58d,
+ +1.653701058949654E-58d,
+ -8.939932297357388E-58d,
+ -1.395320103272191E-57d,
+ +6.440430933947252E-58d,
+ -1.681200826841738E-56d,
+ +3.9904382022898837E-56d,
+ -4.870518577546228E-56d,
+ -1.6990896855901115E-55d,
+ -6.751434891261518E-56d,
+ -1.669012123121194E-54d,
+ -4.079585475491198E-54d,
+ -1.3070436427679952E-53d,
+ -3.090028378908628E-53d,
+ +7.468160889798606E-53d,
+ +6.229095980733463E-53d,
+ +1.4794751934479566E-52d,
+ +1.7444373785853918E-51d,
+ -5.3681978363391484E-52d,
+ +2.71853394036182E-51d,
+ -1.3334367969274016E-50d,
+ -1.6958057665854177E-49d,
+ -1.452507231312146E-49d,
+ +3.3855429446520427E-49d,
+ +4.903687986212687E-49d,
+ +2.2185957416622524E-48d,
+ -9.924196700842429E-48d,
+ +4.285128462851149E-47d,
+ +3.076063086193525E-48d,
+ +4.102052341676543E-46d,
+ +1.1745772638457318E-45d,
+ -5.309047216809048E-47d,
+ +2.72972449891179E-45d,
+ -1.1748423022293739E-44d,
+ +6.626052626622228E-44d,
+ +3.0227439688367925E-44d,
+ -4.740494808228372E-43d,
+ +5.926057457356852E-43d,
+ +3.09768273342776E-42d,
+ -5.589493227475577E-42d,
+ -8.84908716783327E-42d,
+ +2.3684740712822874E-41d,
+ +1.4836491430755657E-40d,
+ +4.5878801324451396E-40d,
+ +1.0585156316103144E-39d,
+ +2.3805896467049493E-39d,
+ +1.0285082556185196E-38d,
+ +2.5187968110874885E-38d,
+ -1.4088399542613178E-38d,
+ -3.00901028043488E-38d,
+ +2.0089026801414973E-37d,
+ -1.3324111396289096E-36d,
+ +5.458481186294964E-36d,
+ -4.8402541351522003E-36d,
+ -1.3331969720555312E-35d,
+ -8.248332290732976E-35d,
+ -1.8349670703969982E-34d,
+ +6.403477383195494E-34d,
+ +3.7813691654412385E-34d,
+ +2.4621305031382827E-33d,
+ -5.634051826192439E-33d,
+ +3.817173955083142E-32d,
+ -6.038239639506472E-32d,
+ -2.130447095555397E-31d,
+ -6.824454861992054E-31d,
+ -1.3455801602048414E-30d,
+ -2.518642767561659E-30d,
+ +8.082792416221215E-30d,
+ +4.718103502869148E-29d,
+ -5.607991635038776E-29d,
+ -1.8042191582018579E-28d,
+ +6.989914264479507E-28d,
+ -2.9031739430339586E-28d,
+ +6.076820259849921E-27d,
+ -3.24981577480893E-27d,
+ -2.7648210023059463E-26d,
+ -9.785306155980342E-26d,
+ +1.241529292737115E-25d,
+ +3.0891604448087654E-25d,
+ +2.3451052074796954E-24d,
+ +6.574128018028633E-24d,
+ -1.3345148716925826E-23d,
+ +4.3594621428644293E-23d,
+ -5.678896695157704E-23d,
+ -4.676849004137386E-23d,
+ -2.281578975407609E-22d,
+ -3.144430608076357E-21d,
+ +5.662033727488754E-22d,
+ -4.30293375386492E-21d,
+ +4.985137671479376E-20d,
+ +1.657668502165438E-19d,
+ -3.3878706977811337E-19d,
+ -7.488022803661722E-19d,
+ +1.725039737424264E-18d,
+ -6.0275040161173166E-18d,
+ -8.081007442213538E-19d,
+ +2.9257892371894816E-17d,
+ +1.5231541295722552E-16d,
+ -1.1474026049124666E-17d,
+ +6.890372706231206E-16d,
+ +2.592721454922832E-15d,
+ -1.1253822296423454E-15d,
+ -2.650684279637763E-14d,
+ -4.107226967119929E-15d,
+ -3.130508064738312E-14d,
+ -6.729414275200856E-14d,
+ -1.6166170913368169E-12d,
+ -1.2059301405584488E-12d,
+ -1.2210091619211167E-11d,
+ +3.695372823623631E-12d,
+ +5.119220484478292E-11d,
+ -1.0857572226543142E-10d,
+ -4.6490379071586397E-10d,
+ -4.5810381714280557E-10d,
+ +1.4909756678328582E-9d,
+ -1.3155828104004438E-8d,
+ -9.149755188170102E-9d,
+ +0.0d,
+ +8.254840070411029E-8d,
+ -1.0681886149151956E-7d,
+ -3.359944163407147E-8d,
+ -2.1275002921718894E-6d,
+ +1.2129920353421116E-5d,
+ +2.1520078872608393E-5d,
+ +1.0178783359926372E-4d,
+ -2.077077172525637E-5d,
+ -5.67996159922899E-5d,
+ +9.510567165169581E-4d,
+ +0.0010901978184553272d,
+ +0.010169003920808009d,
+ +0.017008920503326107d,
+ +0.03416477677774927d,
+ -0.1275278893606981d,
+ +0.5205078726367633d,
+ +0.7535752982147762d,
+ +1.1373305111387886d,
+ -3.036812739155085d,
+ +11.409790277969124d,
+ -9.516785302789955d,
+ -49.86840843831867d,
+ -393.7510973999651d,
+ -686.1565277058598d,
+ +4617.385872524165d,
+ -11563.161235730215d,
+ -8230.201383316231d,
+ -34460.52482632287d,
+ +50744.04207438878d,
+ +357908.46214699093d,
+ +1936607.425231087d,
+ +3222936.695160983d,
+ +5477052.0646243105d,
+ -3.517545711859706E7d,
+ -1.2693418527187027E8d,
+ -2.5316384477288628E8d,
+ -1.6436423669122624E8d,
+ +4.0889180422033095E8d,
+ +4.968829330953611E9d,
+ -3.503399598592085E9d,
+ +1.905394922122271E10d,
+ +1.0361722296739479E11d,
+ -5.806792575852521E10d,
+ +2.3454138776381036E11d,
+ -1.718446464587963E12d,
+ -1.0946634815588584E12d,
+ +1.6889383928999305E13d,
+ -3.784600043778247E13d,
+ +7.270965670658928E13d,
+ -4.9202842786896806E14d,
+ +4.597700093952774E14d,
+ +2.6113557852262235E15d,
+ -4.544525556171388E15d,
+ -9.517971970450354E15d,
+ -2.0634857819227416E16d,
+ -9.7143113104549808E16d,
+ -2.2667083759873216E16d,
+ -7.2285665164439578E17d,
+ +4.1215410760803866E18d,
+ +8.5807488300972206E18d,
+ +1.530436781375042E19d,
+ -1.5453111533064765E19d,
+ -1.0633845571643594E20d,
+ -3.512380426745336E20d,
+ +3.7734658676841284E20d,
+ -3.855478664503271E21d,
+ +7.984485303520287E21d,
+ -1.2296934902142301E22d,
+ +1.042139023692827E22d,
+ +1.2167897656061312E23d,
+ +9.22064170155394E22d,
+ +3.965171513035854E23d,
+ -4.135121057126514E24d,
+ -7.944341754299148E24d,
+ +1.4715152230577016E25d,
+ -3.0635272288480756E25d,
+ -9.54468158713835E25d,
+ +1.5411775738825048E25d,
+ -8.274711842374368E26d,
+ -1.0028324930788433E27d,
+ +5.189062091114782E27d,
+ -2.8583500869462184E28d,
+ -5.198295198128238E28d,
+ +2.9758750368256437E29d,
+ +3.216046320616945E29d,
+ -1.7846700158234043E30d,
+ +3.847174961282827E30d,
+ +9.026991921214922E30d,
+ +4.1358029739592175E30d,
+ -6.461509354879894E29d,
+ +9.704297297526684E31d,
+ +2.9731739067444943E32d,
+ +9.97728609663656E32d,
+ +3.1149346370027763E33d,
+ +2.0051635097366476E34d,
+ +2.819272221032373E34d,
+ +1.6266731695798413E34d,
+ +1.998050894021586E35d,
+ -6.1633417615076335E35d,
+ +2.2505716077585116E36d,
+ +1.9299691540987203E36d,
+ +8.006569251375383E36d,
+ -3.785295042408568E37d,
+ -1.1870498357197593E38d,
+ +1.0010529668998112E38d,
+ +1.3240710866573994E38d,
+ +2.6888010385137123E39d,
+ +1.7400655988987023E39d,
+ -6.402740469853475E39d,
+ -3.93114092562274E40d,
+ +1.2363717201084252E41d,
+ -1.9219116633978794E41d,
+ -1.347867098583136E42d,
+ +7.87675118338788E41d,
+ +3.3932984011177642E41d,
+ -1.9872713979884691E43d,
+ +2.220208491349658E43d,
+ -3.466267817480825E43d,
+ +3.19462030745197E44d,
+ -9.841244788104406E44d,
+ -2.2676593395522725E45d,
+ -1.1349246400274207E46d,
+ -1.1700910284427406E46d,
+ -3.6754317105801715E46d,
+ +1.7647101734915075E47d,
+ +2.122358392979746E47d,
+ +3.156243682143956E47d,
+ +5.356668151937413E47d,
+ +2.7668218233914262E48d,
+ +3.5127708120698784E48d,
+ +1.7884841356632925E49d,
+ +1.716531820904728E50d,
+ -2.9114757102866277E50d,
+ +1.0657703081219677E51d,
+ -7.512169809356372E50d,
+ +1.764200470879736E51d,
+ -1.0088898215431471E52d,
+ -3.1085734725176E52d,
+ +4.3529009584292495E52d,
+ -2.467842129213774E53d,
+ -3.9317379627195146E53d,
+ -4.332335454045836E52d,
+ +7.979013724931926E54d,
+ -1.5038413653121357E55d,
+ +9.310799925566843E55d,
+ -2.2042966348036592E55d,
+ -4.518315366841937E55d,
+ -6.971366338144781E56d,
+ -2.0461505570781806E57d,
+ -8.823884392655312E57d,
+ -1.1264032993918548E58d,
+ -7.692065092509875E58d,
+ -1.8472516879728875E59d,
+ +8.72220314694275E58d,
+ +1.6525336989036362E59d,
+ -3.343201925128334E60d,
+ +5.493352163155986E60d,
+ -2.548073509300398E61d,
+ -9.566541624209933E61d,
+ +4.0891054447206644E61d,
+ -7.724182294653349E62d,
+ +1.0143022354947225E63d,
+ -4.952031310451961E63d,
+ -7.877410133454722E63d,
+ +4.505432606253564E64d,
+ -7.330635250808021E64d,
+ -1.642361029990822E65d,
+ +5.982180242124184E65d,
+ +7.120242132370469E65d,
+ +5.908356249789671E66d,
+ -2.8477710945673134E65d,
+ +6.65688196961235E66d,
+ -9.233295580238604E67d,
+ +3.2850043261803593E68d,
+ +7.041681569694413E68d,
+ -1.5652761725518397E69d,
+ +1.5377053215489084E68d,
+ +1.282130763903269E70d,
+ -2.380286345847567E70d,
+ -7.207022875977515E70d,
+ +2.7641662602473095E71d,
+ +7.685235201534525E71d,
+ +4.3239378585884645E70d,
+ -1.6840562544109314E72d,
+ -5.04128025464686E71d,
+ +5.4557485189210095E73d,
+ +7.160277784358221E73d,
+ +7.636179075087608E73d,
+ -8.18804507680012E74d,
+ +2.807397988979441E75d,
+ +2.165163304600171E75d,
+ -1.3208450062862734E76d,
+ -5.1939252391404724E76d,
+ -6.985952908805853E76d,
+ -1.6259920998287064E77d,
+ +6.098975200926637E77d,
+ -5.63383579957466E77d,
+ -1.5876819186852907E78d,
+ +2.1487475413123092E79d,
+ -3.987619123706934E79d,
+ +9.772655251656639E79d,
+ -1.638756156057952E79d,
+ -7.83892088580041E80d,
+ +1.274413296252691E81d,
+ +2.51946651720982E81d,
+ -2.516866097506943E81d,
+ +1.053956282234684E82d,
+ +1.8279051206232177E83d,
+ +1.2250764591564252E82d,
+ -4.0353723442917463E83d,
+ -1.4121324224340735E84d,
+ -5.45287716696021E84d,
+ -1.7514953095665195E85d,
+ -5.0706081370522526E85d,
+ -4.35799392139009E85d,
+ -3.982538093450217E86d,
+ -1.4591838284752642E87d,
+ +2.5313735821872488E87d,
+ -3.718501227185903E86d,
+ -1.3907979640327008E88d,
+ -5.79002114093961E86d,
+ -1.2500675565781447E89d,
+ +4.8182788286170926E89d,
+ -1.7198866036687559E90d,
+ -4.690417668647599E88d,
+ +1.3020631859056421E91d,
+ -1.3850458263351744E91d,
+ +4.87301010703588E91d,
+ -1.695546877943826E92d,
+ -1.6353756659909833E92d,
+ -1.5483926773679628E93d,
+ -1.8921091400297595E93d,
+ -6.183525570536406E93d,
+ -4.987913342551977E93d,
+ +1.0186485886120274E93d,
+ -1.5343120819745468E95d,
+ -5.262123923229857E95d,
+ +1.618327917706804E96d,
+ -4.135185828158998E96d,
+ -8.016793741945299E96d,
+ -3.0399439534134115E97d,
+ -1.2319346292749103E98d,
+ +7.536337311795176E97d,
+ -3.577715974851322E98d,
+ +2.0521614818695524E99d,
+ +1.2627736197958951E98d,
+ -5.206910481915062E99d,
+ +3.0974593993948837E100d,
+ -9.522726334561169E100d,
+ -1.1909272509710985E100d,
+ -5.056512677995137E101d,
+ +2.0902045062932175E102d,
+ +6.243669516810509E102d,
+ -1.7375090618655787E103d,
+ -2.5445477450140954E103d,
+ +3.619891246849381E103d,
+ +8.90737333900943E103d,
+ -2.7897360297480367E104d,
+ +1.3725786770437066E105d,
+ -8.316530604593264E105d,
+ -6.054541568735673E105d,
+ +7.523374196797555E105d,
+ +1.1475955030427985E107d,
+ +1.5260756679495707E107d,
+ +7.370294848920685E107d,
+ +1.3608995799112174E108d,
+ +1.0700758858011432E108d,
+ -4.989318918773146E108d,
+ -1.6629755787634093E108d,
+ +7.635999584053557E109d,
+ +1.892621828736983E109d,
+ -6.793094743406533E110d,
+ -8.160628910742724E110d,
+ -7.724219106106896E111d,
+ -1.6059226011778748E112d,
+ -1.5277127454062126E112d,
+ +3.911086668967361E112d,
+ +3.529920406834134E113d,
+ -4.3991443996021166E113d,
+ -1.2631909085915044E114d,
+ +3.8656278695544835E114d,
+ +1.71845288713123E115d,
+ +3.7660598745907915E115d,
+ -4.048086182363988E115d,
+ +2.3093822298965837E116d,
+ -9.684925795536813E116d,
+ -3.137992585221854E117d,
+ -5.637415935329794E117d,
+ -1.5536658521931418E118d,
+ -6.336314643222911E118d,
+ +8.550658957115427E118d,
+ -5.591880480212007E119d,
+ +2.4137404318673354E119d,
+ -2.631656656397244E120d,
+ -7.653117429165879E119d,
+ -4.073965591445897E121d,
+ +3.634781057940233E121d,
+ +4.537273754534966E121d,
+ -2.5138919966097735E122d,
+ -1.0292817180691822E123d,
+ -1.4265564976097062E122d,
+ +6.000235114895513E123d,
+ +4.186590347846346E124d,
+ -1.8950538406321535E124d,
+ +7.716762345695022E124d,
+ -4.443798187035849E125d,
+ -2.268994961992292E125d,
+ -2.8169291774231604E126d,
+ -2.749127978087685E126d,
+ -2.2929764629585683E126d,
+ -7.369842361872221E127d,
+ +2.81312841469177E128d,
+ +2.7856896414497757E128d,
+ -3.096733638475319E128d,
+ -5.4199510725063615E129d,
+ -7.315860999413894E129d,
+ +3.6424644535156437E130d,
+ -7.886250961456327E130d,
+ +5.289988151341401E130d,
+ +2.7758613753516344E131d,
+ -2.738246981762776E132d,
+ -2.2667181460478093E132d,
+ -3.614672661225457E131d,
+ +2.325337720526947E133d,
+ +4.16603235883392E133d,
+ -6.50348962894948E133d,
+ +3.851445905038431E134d,
+ -5.46060534001412E134d,
+ +5.4679180659102885E135d,
+ -3.037477806841494E135d,
+ -3.0417051809209134E136d,
+ -6.995964550587914E136d,
+ -3.6897084415718804E137d,
+ -6.938000231893302E137d,
+ +2.403806217004454E138d,
+ -3.4552363953199905E138d,
+ +7.3409917428393E138d,
+ -1.7445917446236717E139d,
+ -6.680679913078676E139d,
+ -8.193572619487537E139d,
+ +5.337290292186291E139d,
+ -3.951314467739045E140d,
+ -4.4662073456574476E141d,
+ +6.249381778908997E141d,
+ -2.928362616578011E142d,
+ -1.6661676835672304E143d,
+ -1.974465323891493E143d,
+ +1.3083870531380308E144d,
+ -2.382825271750576E144d,
+ -5.4826958838142734E144d,
+ +1.5340733916570804E145d,
+ -3.1327120557842516E145d,
+ +1.5790297768522832E146d,
+ +1.1518771984292262E146d,
+ -4.789917000227385E145d,
+ -8.689594184775204E146d,
+ +3.0680417869552433E146d,
+ +4.877860620031438E147d,
+ -3.4650891244084597E148d,
+ +1.8702183451052442E149d,
+ -3.5727227900139915E148d,
+ -1.3457821696677932E150d,
+ +3.3212950284273017E149d,
+ +7.316033240396569E150d,
+ -7.187723217018267E150d,
+ -8.537194547485455E150d,
+ -1.4561530066010593E152d,
+ -7.548155147049997E151d,
+ +1.0047353208353007E153d,
+ -1.2489460589853119E153d,
+ +4.426120229279107E153d,
+ -2.5466223330961086E154d,
+ +8.831699889789037E154d,
+ -2.0258084311749475E155d,
+ -5.525009099476396E155d,
+ -1.0235056525096769E156d,
+ -4.117971654572494E154d,
+ -4.7559175309753334E156d,
+ -1.4656240137098836E157d,
+ -7.675790582869644E157d,
+ -1.0126616322947826E158d,
+ +7.084865265284368E158d,
+ -9.374695893307895E158d,
+ +2.05597910889115E159d,
+ -7.368602086210704E159d,
+ -1.6167825196198978E160d,
+ +2.3832096207000712E160d,
+ +1.3166970112139726E161d,
+ -6.432337568761393E161d,
+ +2.9279594746502846E161d,
+ +4.8926595743317624E162d,
+ +1.2704793774453618E163d,
+ -1.1345910784680524E163d,
+ +7.75933511025868E163d,
+ -1.1441115218462356E163d,
+ +5.162248481759758E164d,
+ +6.362563919556132E164d,
+ -2.8362173224732088E165d,
+ -4.342161053332263E165d,
+ +4.388125271425036E166d,
+ -7.049068240916723E166d,
+ +3.8520227881415595E166d,
+ +2.9274120974020826E167d,
+ -7.500936767542933E167d,
+ -6.540181860667302E168d,
+ +4.664436780622191E168d,
+ -1.436111169285268E169d,
+ -1.0407581736224179E170d,
+ -2.7670181051374297E170d,
+ -6.788169932297778E170d,
+ +1.6997387217850427E171d,
+ -1.0965324942770584E171d,
+ +9.841563119484623E171d,
+ +3.175748919314254E172d,
+ +2.9621181706527444E172d,
+ -3.30101656090905E173d,
+ -3.791840683760427E173d,
+ -2.841347842666459E174d,
+ -7.836327226971707E174d,
+ +9.650358667643114E174d,
+ +5.9994277301267294E175d,
+ -6.0490084078440215E175d,
+ -2.8964095485948707E176d,
+ +9.916187343252014E175d,
+ +2.7535627955313556E176d,
+ +3.886891475472745E177d,
+ +3.1962472803616787E178d,
+ -5.50599549115449E178d,
+ +5.672812341879918E178d,
+ -3.295268490032475E179d,
+ +9.761163062156018E179d,
+ +3.107837179570674E180d,
+ +3.3894811576571423E179d,
+ -5.235397688850367E180d,
+ -5.004237248003625E181d,
+ -1.7544995191195304E182d,
+ +2.645622651144787E182d,
+ -3.459885432869825E182d,
+ -4.0361435606199565E183d,
+ -1.8382923511801317E183d,
+ -1.7332235571505177E184d,
+ +2.847653455671381E184d,
+ +1.7991060813894693E185d,
+ -2.0937429891059164E185d,
+ +5.744446753652847E185d,
+ -2.1349396267483754E184d,
+ -1.2542332720182776E186d,
+ +3.3730714236579374E186d,
+ -5.923734606208998E187d,
+ +2.24669039465627E188d,
+ -1.2588742703536392E188d,
+ +1.474522484905093E189d,
+ -2.4006971787803736E189d,
+ -3.52597540499141E189d,
+ +2.6676722922838097E190d,
+ +5.27579825970359E190d,
+ +2.1360492104281465E191d,
+ +1.9442210982008953E191d,
+ -1.4691239161932232E190d,
+ +3.8218180377739526E192d,
+ +1.9722862688653467E192d,
+ +3.047601928063002E193d,
+ +1.6747356805175311E193d,
+ +7.710512446969693E192d,
+ +1.7780021277684035E194d,
+ -1.4015110811648513E195d,
+ +4.0447634595724164E195d,
+ +9.023639664212642E195d,
+ +1.976868146639626E196d,
+ -9.084495133765657E196d,
+ -1.2023077889892748E196d,
+ +5.7455368446308694E197d,
+ -1.7766273910482863E198d,
+ +3.5590470673352285E198d,
+ +1.1304970373249033E199d,
+ +1.6496143246803731E199d,
+ -2.394588390685223E199d,
+ -1.4677321100833294E199d,
+ -1.1843870433971731E201d,
+ -1.8853982316037226E201d,
+ +2.8829871423339434E201d,
+ +5.369687677705385E200d,
+ +1.8356062677502141E202d,
+ -1.5544655377217875E203d,
+ +2.955364187248884E203d,
+ -2.7651059253677425E203d,
+ +9.903174064539538E203d,
+ -3.284204788892967E204d,
+ -1.5843229740595697E205d,
+ +5.333371443528904E204d,
+ +1.2781631468016048E205d,
+ +3.2188292385399854E205d,
+ -6.619064395428225E206d,
+ +1.291561142865928E207d,
+ +1.3142988156905172E207d,
+ -1.3841980097978606E208d,
+ +6.380177790989479E207d,
+ +1.0386032577072378E209d,
+ +2.7681631086098026E209d,
+ -9.053874899534375E209d,
+ +1.2424707839848734E210d,
+ +1.045546633850141E211d,
+ -1.2448938139338362E211d,
+ +7.221902646057552E211d,
+ +6.651345415954053E211d,
+ -5.8180712702152444E212d,
+ +5.275183961165903E212d,
+ +5.092753117288608E212d,
+ -2.437796532151255E213d,
+ +1.3480763914637323E214d,
+ +5.619995933180841E214d,
+ +2.547000388735681E214d,
+ +4.817319356453926E214d,
+ -7.897146442236022E215d,
+ -7.93844120619577E215d,
+ -4.9489938500591624E216d,
+ -2.862720607805682E216d,
+ -2.9275804461593914E217d,
+ -3.411186219855533E217d,
+ -2.0110092718356274E218d,
+ -8.472642266772353E218d,
+ -4.357990742470246E217d,
+ +4.793444363780116E219d,
+ +1.6544084224626834E220d,
+ -6.017988576347111E220d,
+ -3.580397221598409E220d,
+ -4.7208848667217906E221d,
+ -7.724899660259369E221d,
+ -2.4459728627968634E222d,
+ +3.667348665023154E221d,
+ +4.544122762558404E223d,
+ -4.0573420909530794E223d,
+ -3.2552002992257195E223d,
+ -6.488296536838142E224d,
+ +1.7544839352461719E224d,
+ -4.0873400635183405E225d,
+ -8.833499967268279E225d,
+ -1.0953484767704112E226d,
+ -8.56825295972308E226d,
+ -1.8097633115378247E227d,
+ -6.171564449018882E227d,
+ -4.351843341274115E227d,
+ +2.8032429752543687E228d,
+ -1.0065901934522996E229d,
+ +9.863720960170636E228d,
+ -9.481088691357648E229d,
+ -1.6843492713373762E229d,
+ -1.3282890219894906E230d,
+ +6.883577595238845E230d,
+ -1.153577281189635E231d,
+ -8.009548754642203E231d,
+ -4.722612904888278E232d,
+ -4.768909872963015E232d,
+ +3.2542391242036633E233d,
+ +6.513425781583774E233d,
+ -1.8889614379831606E233d,
+ -2.227647301474917E234d,
+ -4.7971208532986115E234d,
+ +6.693500938105557E234d,
+ -6.587776621471115E234d,
+ +3.0099905634916516E236d,
+ -4.6694407626686244E235d,
+ +2.965546585110978E236d,
+ +5.771457643937893E237d,
+ -9.029878114318277E237d,
+ +8.169926810324408E237d,
+ -1.779945804977441E239d,
+ +4.1218749988429474E239d,
+ +7.201319954099161E239d,
+ -1.530845432304069E240d,
+ -3.861762510530086E240d,
+ -2.4090696463777446E241d,
+ -1.8196842273916379E241d,
+ -1.7959243076374794E242d,
+ -3.7257346819782323E242d,
+ +3.413310324247329E242d,
+ -2.0406580894051073E243d,
+ -1.5335923091350053E243d,
+ -1.056727406551016E244d,
+ -4.6753408714233723E244d,
+ -2.0697130057384643E245d,
+ -1.0356006160554071E245d,
+ +1.1339195187304043E246d,
+ +1.792783182582235E246d,
+ +9.599214853681978E245d,
+ +1.5367645598839362E247d,
+ +2.934570385464815E247d,
+ -1.6411525886171892E248d,
+ +2.2638862982382794E248d,
+ -1.2268014119628852E249d,
+ +4.737693450915584E247d,
+ +6.3818993714899675E249d,
+ +1.2639113706171572E250d,
+ -4.011320021817099E249d,
+ -5.2744376732859406E250d,
+ -3.732266217624991E251d,
+ +1.7591819833844019E252d,
+ -3.292458622014749E252d,
+ -9.161340309319204E252d,
+ -1.728610646009749E253d,
+ +1.1698424008604891E254d,
+ -1.8494343291160577E254d,
+ +2.0568656302182574E254d,
+ +1.0537591246531136E255d,
+ +1.803052068234866E254d,
+ -1.053036399720808E256d,
+ +2.1836166619192508E256d,
+ +1.0368403169781264E257d,
+ -2.0648015610276362E257d,
+ +8.426174035728768E257d,
+ -1.3577357192972777E258d,
+ +2.1313950901331177E258d,
+ +8.919141843592823E258d,
+ -1.1800039972549816E259d,
+ -1.1878772398311421E260d,
+ -1.538273497873993E260d,
+ -4.51305093266001E260d,
+ +1.1241179396053055E261d,
+ +6.154786508667658E261d,
+ -1.0626125049032536E262d,
+ -1.8908603201210102E262d,
+ -4.571195152299358E262d,
+ +1.526100002923062E263d,
+ -9.457084582570225E263d,
+ -1.5460500618825853E264d,
+ -5.598276199126451E264d,
+ -1.2074097381167957E265d,
+ -3.015972957475025E265d,
+ +1.4345106852061226E265d,
+ +8.28479585346867E265d,
+ -3.118741081244705E266d,
+ -1.2054747399765794E266d,
+ +3.4454766202661184E267d,
+ +1.1279135096919439E268d,
+ +1.2066382528772518E268d,
+ +1.1984128162292276E269d,
+ +3.685169705587367E268d,
+ +6.570047690198998E269d,
+ +1.8836492887460383E270d,
+ +7.4364594917181125E270d,
+ +1.2773080633674971E271d,
+ +1.8928981707279692E271d,
+ +4.039437286589528E271d,
+ +1.785277385538302E272d,
+ -6.017681359527226E272d,
+ +1.9716943051755635E273d,
+ -8.772048092842086E271d,
+ +1.5645672698520312E274d,
+ -3.7979660725865874E274d,
+ +5.324902289537048E274d,
+ -1.8806716685063293E274d,
+ +9.320900373401115E275d,
+ +1.4615985810260016E275d,
+ +8.321226457219046E276d,
+ -4.608112855795952E276d,
+ -3.476352191116455E277d,
+ +5.266381689434054E277d,
+ -9.622106063561645E277d,
+ +4.1719443712336026E278d,
+ +4.222411269063919E279d,
+ -6.714376022102489E279d,
+ -1.0732735585199074E280d,
+ -2.5866883048437488E280d,
+ -1.1306860837934988E281d,
+ +3.690690354793168E281d,
+ -5.5299180508885456E281d,
+ +2.7006726968568243E282d,
+ +4.135457669031131E282d,
+ +2.8401159516008676E283d,
+ +5.127265762024798E283d,
+ -3.4893601256685762E283d,
+ -1.145160459652136E283d,
+ +2.1742808735341656E284d,
+ +4.656972469326391E285d,
+ +7.672307991205681E285d,
+ +1.5781599575584034E286d,
+ +4.218682431618625E286d,
+ -2.4602260687026867E287d,
+ +2.7211316452521414E287d,
+ -1.8740018211089393E288d,
+ +2.6367639658206183E288d,
+ -3.102678910525039E288d,
+ +1.1992295328636466E289d,
+ +6.8190133180135345E289d,
+ +5.783203879030497E289d,
+ +5.171047077293295E290d,
+ +1.8396930096213817E290d,
+ +1.4977047507315718E290d,
+ +1.0672499803427623E292d,
+ +3.3310942289102464E291d,
+ -7.962256961838823E292d,
+ +1.7396889119023863E293d,
+ +3.8072183820435085E293d,
+ +2.2772059538865722E294d,
+ -2.0549866377878678E294d,
+ -1.2277120342804144E295d,
+ -3.609949022969024E295d,
+ +1.1479863663699871E296d,
+ -1.5314373779304356E296d,
+ -2.2537635160762597E296d,
+ -6.1370690793508674E296d,
+ -4.996854125490041E297d,
+ -6.883499809714189E297d,
+ -2.595456638706416E298d,
+ -1.1892631528580186E299d,
+ -1.4672600326020399E299d,
+ -3.200068509818696E299d,
+ -7.126913872617518E298d,
+ -3.3655587417265094E300d,
+ Double.NaN,
+ Double.NaN,
+ Double.NaN,
+ Double.NaN,
+ Double.NaN,
+ Double.NaN,
+ Double.NaN,
+ Double.NaN,
+ Double.NaN,
+ Double.NaN,
+ Double.NaN,
+ Double.NaN,
+ Double.NaN,
+ Double.NaN,
+ Double.NaN,
+ Double.NaN,
+ Double.NaN,
+ Double.NaN,
+ Double.NaN,
+ Double.NaN,
+ Double.NaN,
+ Double.NaN,
+ Double.NaN,
+ Double.NaN,
+ Double.NaN,
+ Double.NaN,
+ Double.NaN,
+ Double.NaN,
+ Double.NaN,
+ Double.NaN,
+ Double.NaN,
+ Double.NaN,
+ Double.NaN,
+ Double.NaN,
+ Double.NaN,
+ Double.NaN,
+ Double.NaN,
+ Double.NaN,
+ Double.NaN,
+ Double.NaN,
+ };
+
+ /**
+ * Exponential over the range of 0 - 1 in increments of 2^-10 exp(x/1024) = expFracTableA[x] +
+ * expFracTableB[x]. 1024 = 2^10
+ */
+ private static final double[] EXP_FRAC_A =
+ new double[] {
+ +1.0d,
+ +1.0009770393371582d,
+ +1.0019550323486328d,
+ +1.0029339790344238d,
+ +1.0039138793945312d,
+ +1.004894733428955d,
+ +1.0058765411376953d,
+ +1.006859302520752d,
+ +1.007843017578125d,
+ +1.0088276863098145d,
+ +1.0098135471343994d,
+ +1.0108001232147217d,
+ +1.0117876529693604d,
+ +1.0127761363983154d,
+ +1.013765811920166d,
+ +1.014756202697754d,
+ +1.0157477855682373d,
+ +1.016740083694458d,
+ +1.0177335739135742d,
+ +1.0187277793884277d,
+ +1.0197231769561768d,
+ +1.0207195281982422d,
+ +1.021716833114624d,
+ +1.0227150917053223d,
+ +1.023714303970337d,
+ +1.024714469909668d,
+ +1.0257158279418945d,
+ +1.0267179012298584d,
+ +1.0277209281921387d,
+ +1.0287251472473145d,
+ +1.0297303199768066d,
+ +1.0307364463806152d,
+ +1.0317435264587402d,
+ +1.0327515602111816d,
+ +1.0337605476379395d,
+ +1.0347704887390137d,
+ +1.0357816219329834d,
+ +1.0367934703826904d,
+ +1.037806510925293d,
+ +1.038820505142212d,
+ +1.0398354530334473d,
+ +1.040851354598999d,
+ +1.0418684482574463d,
+ +1.0428862571716309d,
+ +1.043905258178711d,
+ +1.0449252128601074d,
+ +1.0459461212158203d,
+ +1.0469679832458496d,
+ +1.0479910373687744d,
+ +1.0490150451660156d,
+ +1.0500397682189941d,
+ +1.0510656833648682d,
+ +1.0520927906036377d,
+ +1.0531206130981445d,
+ +1.0541496276855469d,
+ +1.0551795959472656d,
+ +1.0562105178833008d,
+ +1.0572423934936523d,
+ +1.0582754611968994d,
+ +1.059309482574463d,
+ +1.0603444576263428d,
+ +1.061380386352539d,
+ +1.0624175071716309d,
+ +1.06345534324646d,
+ +1.0644943714141846d,
+ +1.0655345916748047d,
+ +1.066575527191162d,
+ +1.067617654800415d,
+ +1.0686607360839844d,
+ +1.0697050094604492d,
+ +1.0707499980926514d,
+ +1.071796178817749d,
+ +1.072843313217163d,
+ +1.0738916397094727d,
+ +1.0749409198760986d,
+ +1.075991153717041d,
+ +1.0770423412322998d,
+ +1.078094720840454d,
+ +1.0791480541229248d,
+ +1.080202341079712d,
+ +1.0812578201293945d,
+ +1.0823142528533936d,
+ +1.083371639251709d,
+ +1.08443021774292d,
+ +1.0854897499084473d,
+ +1.086550235748291d,
+ +1.0876119136810303d,
+ +1.088674545288086d,
+ +1.089738130569458d,
+ +1.0908029079437256d,
+ +1.0918686389923096d,
+ +1.092935562133789d,
+ +1.094003438949585d,
+ +1.0950722694396973d,
+ +1.096142053604126d,
+ +1.0972130298614502d,
+ +1.09828519821167d,
+ +1.099358320236206d,
+ +1.1004323959350586d,
+ +1.1015074253082275d,
+ +1.102583646774292d,
+ +1.103661060333252d,
+ +1.1047391891479492d,
+ +1.105818748474121d,
+ +1.1068990230560303d,
+ +1.107980489730835d,
+ +1.1090631484985352d,
+ +1.1101467609405518d,
+ +1.1112313270568848d,
+ +1.1123170852661133d,
+ +1.1134037971496582d,
+ +1.1144917011260986d,
+ +1.1155805587768555d,
+ +1.1166706085205078d,
+ +1.1177616119384766d,
+ +1.1188538074493408d,
+ +1.1199469566345215d,
+ +1.1210410594940186d,
+ +1.1221363544464111d,
+ +1.1232328414916992d,
+ +1.1243302822113037d,
+ +1.1254286766052246d,
+ +1.126528263092041d,
+ +1.127629041671753d,
+ +1.1287307739257812d,
+ +1.129833459854126d,
+ +1.1309373378753662d,
+ +1.132042407989502d,
+ +1.133148431777954d,
+ +1.1342556476593018d,
+ +1.1353638172149658d,
+ +1.1364731788635254d,
+ +1.1375834941864014d,
+ +1.1386950016021729d,
+ +1.1398074626922607d,
+ +1.1409211158752441d,
+ +1.142035961151123d,
+ +1.1431517601013184d,
+ +1.14426851272583d,
+ +1.1453864574432373d,
+ +1.14650559425354d,
+ +1.1476259231567383d,
+ +1.148747205734253d,
+ +1.149869441986084d,
+ +1.1509928703308105d,
+ +1.1521174907684326d,
+ +1.153243064880371d,
+ +1.154369831085205d,
+ +1.1554977893829346d,
+ +1.1566267013549805d,
+ +1.1577568054199219d,
+ +1.1588881015777588d,
+ +1.160020351409912d,
+ +1.161153793334961d,
+ +1.1622881889343262d,
+ +1.163423776626587d,
+ +1.1645605564117432d,
+ +1.1656982898712158d,
+ +1.166837215423584d,
+ +1.1679773330688477d,
+ +1.1691184043884277d,
+ +1.1702606678009033d,
+ +1.1714041233062744d,
+ +1.172548532485962d,
+ +1.173694133758545d,
+ +1.1748409271240234d,
+ +1.1759889125823975d,
+ +1.177137851715088d,
+ +1.1782879829406738d,
+ +1.1794393062591553d,
+ +1.1805915832519531d,
+ +1.1817450523376465d,
+ +1.1828997135162354d,
+ +1.1840553283691406d,
+ +1.1852121353149414d,
+ +1.1863701343536377d,
+ +1.1875293254852295d,
+ +1.1886897087097168d,
+ +1.1898510456085205d,
+ +1.1910135746002197d,
+ +1.1921772956848145d,
+ +1.1933419704437256d,
+ +1.1945080757141113d,
+ +1.1956751346588135d,
+ +1.1968433856964111d,
+ +1.1980125904083252d,
+ +1.1991832256317139d,
+ +1.200354814529419d,
+ +1.2015275955200195d,
+ +1.2027015686035156d,
+ +1.2038767337799072d,
+ +1.2050528526306152d,
+ +1.2062301635742188d,
+ +1.2074086666107178d,
+ +1.2085883617401123d,
+ +1.2097692489624023d,
+ +1.210951328277588d,
+ +1.2121343612670898d,
+ +1.2133188247680664d,
+ +1.2145042419433594d,
+ +1.2156908512115479d,
+ +1.2168786525726318d,
+ +1.2180676460266113d,
+ +1.2192575931549072d,
+ +1.2204489707946777d,
+ +1.2216413021087646d,
+ +1.222834825515747d,
+ +1.224029779434204d,
+ +1.2252256870269775d,
+ +1.2264227867126465d,
+ +1.227621078491211d,
+ +1.2288203239440918d,
+ +1.2300209999084473d,
+ +1.2312228679656982d,
+ +1.2324256896972656d,
+ +1.2336299419403076d,
+ +1.234835147857666d,
+ +1.23604154586792d,
+ +1.2372493743896484d,
+ +1.2384581565856934d,
+ +1.2396681308746338d,
+ +1.2408792972564697d,
+ +1.2420918941497803d,
+ +1.2433054447174072d,
+ +1.2445201873779297d,
+ +1.2457361221313477d,
+ +1.2469532489776611d,
+ +1.2481715679168701d,
+ +1.2493910789489746d,
+ +1.2506117820739746d,
+ +1.2518336772918701d,
+ +1.2530567646026611d,
+ +1.2542810440063477d,
+ +1.2555065155029297d,
+ +1.2567331790924072d,
+ +1.2579610347747803d,
+ +1.2591900825500488d,
+ +1.260420322418213d,
+ +1.2616519927978516d,
+ +1.2628846168518066d,
+ +1.2641184329986572d,
+ +1.2653534412384033d,
+ +1.266589879989624d,
+ +1.2678272724151611d,
+ +1.2690660953521729d,
+ +1.27030611038208d,
+ +1.2715470790863037d,
+ +1.272789478302002d,
+ +1.2740330696105957d,
+ +1.275277853012085d,
+ +1.2765238285064697d,
+ +1.27777099609375d,
+ +1.2790195941925049d,
+ +1.2802691459655762d,
+ +1.281519889831543d,
+ +1.2827720642089844d,
+ +1.2840254306793213d,
+ +1.2852799892425537d,
+ +1.2865357398986816d,
+ +1.287792682647705d,
+ +1.2890510559082031d,
+ +1.2903103828430176d,
+ +1.2915711402893066d,
+ +1.2928330898284912d,
+ +1.2940962314605713d,
+ +1.2953605651855469d,
+ +1.296626091003418d,
+ +1.2978930473327637d,
+ +1.2991611957550049d,
+ +1.3004305362701416d,
+ +1.3017010688781738d,
+ +1.3029727935791016d,
+ +1.304245948791504d,
+ +1.3055200576782227d,
+ +1.3067958354949951d,
+ +1.308072566986084d,
+ +1.3093504905700684d,
+ +1.3106298446655273d,
+ +1.3119103908538818d,
+ +1.3131921291351318d,
+ +1.3144752979278564d,
+ +1.3157594203948975d,
+ +1.317044973373413d,
+ +1.3183319568634033d,
+ +1.31961989402771d,
+ +1.3209092617034912d,
+ +1.322199821472168d,
+ +1.3234915733337402d,
+ +1.324784755706787d,
+ +1.3260791301727295d,
+ +1.3273746967315674d,
+ +1.3286716938018799d,
+ +1.329969882965088d,
+ +1.3312692642211914d,
+ +1.3325698375701904d,
+ +1.333871841430664d,
+ +1.3351752758026123d,
+ +1.336479663848877d,
+ +1.3377854824066162d,
+ +1.339092493057251d,
+ +1.3404009342193604d,
+ +1.3417105674743652d,
+ +1.3430213928222656d,
+ +1.3443336486816406d,
+ +1.3456470966339111d,
+ +1.3469617366790771d,
+ +1.3482778072357178d,
+ +1.349595069885254d,
+ +1.3509137630462646d,
+ +1.352233648300171d,
+ +1.3535549640655518d,
+ +1.3548774719238281d,
+ +1.356201171875d,
+ +1.3575263023376465d,
+ +1.3588526248931885d,
+ +1.360180139541626d,
+ +1.361509084701538d,
+ +1.3628394603729248d,
+ +1.364171028137207d,
+ +1.3655037879943848d,
+ +1.366837978363037d,
+ +1.368173360824585d,
+ +1.3695101737976074d,
+ +1.3708481788635254d,
+ +1.372187614440918d,
+ +1.373528242111206d,
+ +1.3748703002929688d,
+ +1.376213550567627d,
+ +1.3775582313537598d,
+ +1.378904104232788d,
+ +1.380251407623291d,
+ +1.3815999031066895d,
+ +1.3829498291015625d,
+ +1.384300947189331d,
+ +1.3856534957885742d,
+ +1.387007236480713d,
+ +1.3883624076843262d,
+ +1.389719009399414d,
+ +1.3910768032073975d,
+ +1.3924360275268555d,
+ +1.393796443939209d,
+ +1.395158290863037d,
+ +1.3965213298797607d,
+ +1.397885799407959d,
+ +1.3992514610290527d,
+ +1.4006187915802002d,
+ +1.401987075805664d,
+ +1.4033570289611816d,
+ +1.4047281742095947d,
+ +1.4061005115509033d,
+ +1.4074742794036865d,
+ +1.4088494777679443d,
+ +1.4102261066436768d,
+ +1.4116039276123047d,
+ +1.4129831790924072d,
+ +1.4143636226654053d,
+ +1.415745496749878d,
+ +1.4171288013458252d,
+ +1.418513298034668d,
+ +1.4198992252349854d,
+ +1.4212865829467773d,
+ +1.4226751327514648d,
+ +1.424065351486206d,
+ +1.4254565238952637d,
+ +1.426849365234375d,
+ +1.4282433986663818d,
+ +1.4296388626098633d,
+ +1.4310357570648193d,
+ +1.432433843612671d,
+ +1.433833360671997d,
+ +1.4352343082427979d,
+ +1.4366366863250732d,
+ +1.4380402565002441d,
+ +1.4394452571868896d,
+ +1.4408516883850098d,
+ +1.4422595500946045d,
+ +1.4436686038970947d,
+ +1.4450790882110596d,
+ +1.446491003036499d,
+ +1.447904348373413d,
+ +1.4493188858032227d,
+ +1.450735092163086d,
+ +1.4521524906158447d,
+ +1.4535713195800781d,
+ +1.454991340637207d,
+ +1.4564130306243896d,
+ +1.4578359127044678d,
+ +1.4592602252960205d,
+ +1.460686206817627d,
+ +1.4621131420135498d,
+ +1.4635417461395264d,
+ +1.4649717807769775d,
+ +1.4664030075073242d,
+ +1.4678359031677246d,
+ +1.4692699909210205d,
+ +1.470705509185791d,
+ +1.4721424579620361d,
+ +1.4735808372497559d,
+ +1.475020408630371d,
+ +1.47646164894104d,
+ +1.4779040813446045d,
+ +1.4793481826782227d,
+ +1.4807934761047363d,
+ +1.4822404384613037d,
+ +1.4836885929107666d,
+ +1.485138177871704d,
+ +1.4865891933441162d,
+ +1.488041639328003d,
+ +1.4894955158233643d,
+ +1.4909508228302002d,
+ +1.4924075603485107d,
+ +1.493865728378296d,
+ +1.4953253269195557d,
+ +1.49678635597229d,
+ +1.49824857711792d,
+ +1.4997124671936035d,
+ +1.5011777877807617d,
+ +1.5026445388793945d,
+ +1.504112720489502d,
+ +1.505582332611084d,
+ +1.5070531368255615d,
+ +1.5085256099700928d,
+ +1.5099995136260986d,
+ +1.511474847793579d,
+ +1.5129516124725342d,
+ +1.5144298076629639d,
+ +1.5159096717834473d,
+ +1.5173907279968262d,
+ +1.5188732147216797d,
+ +1.5203571319580078d,
+ +1.5218427181243896d,
+ +1.523329496383667d,
+ +1.524817943572998d,
+ +1.5263078212738037d,
+ +1.5277988910675049d,
+ +1.5292916297912598d,
+ +1.5307857990264893d,
+ +1.5322813987731934d,
+ +1.5337786674499512d,
+ +1.5352771282196045d,
+ +1.5367772579193115d,
+ +1.538278579711914d,
+ +1.5397815704345703d,
+ +1.5412859916687012d,
+ +1.5427920818328857d,
+ +1.5442993640899658d,
+ +1.5458080768585205d,
+ +1.547318458557129d,
+ +1.548830270767212d,
+ +1.5503435134887695d,
+ +1.5518584251403809d,
+ +1.5533745288848877d,
+ +1.5548923015594482d,
+ +1.5564115047454834d,
+ +1.5579321384429932d,
+ +1.5594542026519775d,
+ +1.5609779357910156d,
+ +1.5625030994415283d,
+ +1.5640296936035156d,
+ +1.5655577182769775d,
+ +1.5670874118804932d,
+ +1.5686185359954834d,
+ +1.5701510906219482d,
+ +1.5716853141784668d,
+ +1.5732207298278809d,
+ +1.5747578144073486d,
+ +1.5762965679168701d,
+ +1.577836513519287d,
+ +1.5793781280517578d,
+ +1.5809214115142822d,
+ +1.5824658870697021d,
+ +1.5840120315551758d,
+ +1.5855598449707031d,
+ +1.587108850479126d,
+ +1.5886595249176025d,
+ +1.5902118682861328d,
+ +1.5917654037475586d,
+ +1.593320608139038d,
+ +1.5948774814605713d,
+ +1.596435785293579d,
+ +1.5979955196380615d,
+ +1.5995566844940186d,
+ +1.6011195182800293d,
+ +1.6026840209960938d,
+ +1.6042497158050537d,
+ +1.6058173179626465d,
+ +1.6073861122131348d,
+ +1.6089565753936768d,
+ +1.6105287075042725d,
+ +1.6121022701263428d,
+ +1.6136772632598877d,
+ +1.6152539253234863d,
+ +1.6168320178985596d,
+ +1.6184117794036865d,
+ +1.619992971420288d,
+ +1.6215758323669434d,
+ +1.6231601238250732d,
+ +1.6247460842132568d,
+ +1.626333475112915d,
+ +1.627922534942627d,
+ +1.6295130252838135d,
+ +1.6311051845550537d,
+ +1.6326987743377686d,
+ +1.634294033050537d,
+ +1.6358907222747803d,
+ +1.6374890804290771d,
+ +1.6390891075134277d,
+ +1.640690565109253d,
+ +1.6422934532165527d,
+ +1.6438980102539062d,
+ +1.6455042362213135d,
+ +1.6471118927001953d,
+ +1.6487212181091309d,
+ +1.6503322124481201d,
+ +1.651944637298584d,
+ +1.6535584926605225d,
+ +1.6551742553710938d,
+ +1.6567914485931396d,
+ +1.6584100723266602d,
+ +1.6600303649902344d,
+ +1.6616523265838623d,
+ +1.663275957107544d,
+ +1.6649010181427002d,
+ +1.666527509689331d,
+ +1.6681559085845947d,
+ +1.669785737991333d,
+ +1.671417236328125d,
+ +1.6730501651763916d,
+ +1.674684762954712d,
+ +1.676321029663086d,
+ +1.6779589653015137d,
+ +1.679598331451416d,
+ +1.681239366531372d,
+ +1.6828820705413818d,
+ +1.6845262050628662d,
+ +1.6861720085144043d,
+ +1.687819480895996d,
+ +1.6894686222076416d,
+ +1.6911191940307617d,
+ +1.6927716732025146d,
+ +1.6944255828857422d,
+ +1.6960809230804443d,
+ +1.6977381706237793d,
+ +1.6993968486785889d,
+ +1.7010571956634521d,
+ +1.7027192115783691d,
+ +1.7043828964233398d,
+ +1.7060482501983643d,
+ +1.7077150344848633d,
+ +1.709383487701416d,
+ +1.7110536098480225d,
+ +1.7127254009246826d,
+ +1.7143988609313965d,
+ +1.716073989868164d,
+ +1.7177505493164062d,
+ +1.7194287776947021d,
+ +1.7211089134216309d,
+ +1.7227904796600342d,
+ +1.7244737148284912d,
+ +1.726158618927002d,
+ +1.7278449535369873d,
+ +1.7295331954956055d,
+ +1.7312231063842773d,
+ +1.7329144477844238d,
+ +1.7346076965332031d,
+ +1.736302375793457d,
+ +1.7379989624023438d,
+ +1.739696979522705d,
+ +1.7413966655731201d,
+ +1.7430980205535889d,
+ +1.7448012828826904d,
+ +1.7465059757232666d,
+ +1.7482123374938965d,
+ +1.74992036819458d,
+ +1.7516300678253174d,
+ +1.7533416748046875d,
+ +1.7550547122955322d,
+ +1.7567694187164307d,
+ +1.7584857940673828d,
+ +1.7602040767669678d,
+ +1.7619237899780273d,
+ +1.7636451721191406d,
+ +1.7653684616088867d,
+ +1.7670931816101074d,
+ +1.768819808959961d,
+ +1.770547866821289d,
+ +1.77227783203125d,
+ +1.7740094661712646d,
+ +1.775742769241333d,
+ +1.777477741241455d,
+ +1.7792143821716309d,
+ +1.7809526920318604d,
+ +1.7826926708221436d,
+ +1.7844345569610596d,
+ +1.7861778736114502d,
+ +1.7879230976104736d,
+ +1.7896699905395508d,
+ +1.7914185523986816d,
+ +1.7931687831878662d,
+ +1.7949209213256836d,
+ +1.7966744899749756d,
+ +1.7984299659729004d,
+ +1.800187110900879d,
+ +1.8019459247589111d,
+ +1.8037066459655762d,
+ +1.8054687976837158d,
+ +1.8072328567504883d,
+ +1.8089985847473145d,
+ +1.8107659816741943d,
+ +1.812535285949707d,
+ +1.8143062591552734d,
+ +1.8160789012908936d,
+ +1.8178532123565674d,
+ +1.819629430770874d,
+ +1.8214070796966553d,
+ +1.8231868743896484d,
+ +1.8249680995941162d,
+ +1.8267512321472168d,
+ +1.828536033630371d,
+ +1.830322504043579d,
+ +1.83211088180542d,
+ +1.8339009284973145d,
+ +1.8356926441192627d,
+ +1.8374862670898438d,
+ +1.8392815589904785d,
+ +1.841078519821167d,
+ +1.8428773880004883d,
+ +1.8446779251098633d,
+ +1.846480131149292d,
+ +1.8482842445373535d,
+ +1.8500902652740479d,
+ +1.8518977165222168d,
+ +1.8537070751190186d,
+ +1.8555183410644531d,
+ +1.8573312759399414d,
+ +1.8591458797454834d,
+ +1.8609623908996582d,
+ +1.8627805709838867d,
+ +1.864600658416748d,
+ +1.866422414779663d,
+ +1.8682458400726318d,
+ +1.8700714111328125d,
+ +1.8718984127044678d,
+ +1.8737273216247559d,
+ +1.8755581378936768d,
+ +1.8773906230926514d,
+ +1.8792247772216797d,
+ +1.8810608386993408d,
+ +1.8828988075256348d,
+ +1.8847384452819824d,
+ +1.886579990386963d,
+ +1.888423204421997d,
+ +1.890268325805664d,
+ +1.8921151161193848d,
+ +1.8939638137817383d,
+ +1.8958141803741455d,
+ +1.8976664543151855d,
+ +1.8995206356048584d,
+ +1.901376485824585d,
+ +1.9032342433929443d,
+ +1.9050939083099365d,
+ +1.9069552421569824d,
+ +1.908818244934082d,
+ +1.9106833934783936d,
+ +1.9125502109527588d,
+ +1.9144186973571777d,
+ +1.9162893295288086d,
+ +1.9181616306304932d,
+ +1.9200356006622314d,
+ +1.9219114780426025d,
+ +1.9237892627716064d,
+ +1.9256689548492432d,
+ +1.9275505542755127d,
+ +1.929433822631836d,
+ +1.931318759918213d,
+ +1.9332058429718018d,
+ +1.9350945949554443d,
+ +1.9369852542877197d,
+ +1.938877820968628d,
+ +1.940772294998169d,
+ +1.9426684379577637d,
+ +1.9445664882659912d,
+ +1.9464664459228516d,
+ +1.9483680725097656d,
+ +1.9502718448638916d,
+ +1.9521772861480713d,
+ +1.9540846347808838d,
+ +1.955993890762329d,
+ +1.9579050540924072d,
+ +1.959817886352539d,
+ +1.9617326259613037d,
+ +1.9636495113372803d,
+ +1.9655680656433105d,
+ +1.9674885272979736d,
+ +1.9694106578826904d,
+ +1.9713349342346191d,
+ +1.9732608795166016d,
+ +1.975188970565796d,
+ +1.977118730545044d,
+ +1.9790503978729248d,
+ +1.9809842109680176d,
+ +1.982919692993164d,
+ +1.9848570823669434d,
+ +1.9867963790893555d,
+ +1.9887375831604004d,
+ +1.990680456161499d,
+ +1.9926254749298096d,
+ +1.994572401046753d,
+ +1.996521234512329d,
+ +1.998471736907959d,
+ +2.000424385070801d,
+ +2.0023789405822754d,
+ +2.004335403442383d,
+ +2.006293773651123d,
+ +2.008254051208496d,
+ +2.010216236114502d,
+ +2.0121798515319824d,
+ +2.014145851135254d,
+ +2.016113758087158d,
+ +2.0180835723876953d,
+ +2.0200552940368652d,
+ +2.022029399871826d,
+ +2.0240049362182617d,
+ +2.02598237991333d,
+ +2.0279617309570312d,
+ +2.0299429893493652d,
+ +2.0319266319274902d,
+ +2.03391170501709d,
+ +2.0358991622924805d,
+ +2.0378880500793457d,
+ +2.039879322052002d,
+ +2.041872501373291d,
+ +2.0438671112060547d,
+ +2.0458641052246094d,
+ +2.047863006591797d,
+ +2.049863815307617d,
+ +2.0518670082092285d,
+ +2.0538716316223145d,
+ +2.055878162384033d,
+ +2.057887077331543d,
+ +2.0598974227905273d,
+ +2.0619101524353027d,
+ +2.063924789428711d,
+ +2.065941333770752d,
+ +2.067959785461426d,
+ +2.0699801445007324d,
+ +2.07200288772583d,
+ +2.0740270614624023d,
+ +2.0760536193847656d,
+ +2.0780820846557617d,
+ +2.0801124572753906d,
+ +2.0821447372436523d,
+ +2.084178924560547d,
+ +2.0862154960632324d,
+ +2.0882534980773926d,
+ +2.0902938842773438d,
+ +2.0923361778259277d,
+ +2.0943803787231445d,
+ +2.0964269638061523d,
+ +2.0984749794006348d,
+ +2.100525379180908d,
+ +2.1025776863098145d,
+ +2.1046319007873535d,
+ +2.1066884994506836d,
+ +2.1087465286254883d,
+ +2.110806941986084d,
+ +2.1128692626953125d,
+ +2.114933490753174d,
+ +2.117000102996826d,
+ +2.1190686225891113d,
+ +2.1211390495300293d,
+ +2.12321138381958d,
+ +2.1252856254577637d,
+ +2.1273622512817383d,
+ +2.1294407844543457d,
+ +2.131521224975586d,
+ +2.133604049682617d,
+ +2.135688304901123d,
+ +2.13777494430542d,
+ +2.139863967895508d,
+ +2.1419544219970703d,
+ +2.144047260284424d,
+ +2.14614200592041d,
+ +2.1482391357421875d,
+ +2.1503376960754395d,
+ +2.1524391174316406d,
+ +2.1545419692993164d,
+ +2.156647205352783d,
+ +2.1587538719177246d,
+ +2.1608633995056152d,
+ +2.1629743576049805d,
+ +2.1650876998901367d,
+ +2.167203426361084d,
+ +2.169320583343506d,
+ +2.1714401245117188d,
+ +2.1735615730285645d,
+ +2.175685405731201d,
+ +2.1778111457824707d,
+ +2.179938793182373d,
+ +2.1820688247680664d,
+ +2.1842007637023926d,
+ +2.1863350868225098d,
+ +2.1884708404541016d,
+ +2.1906094551086426d,
+ +2.192749500274658d,
+ +2.194891929626465d,
+ +2.1970362663269043d,
+ +2.1991829872131348d,
+ +2.201331615447998d,
+ +2.2034826278686523d,
+ +2.2056355476379395d,
+ +2.2077903747558594d,
+ +2.2099475860595703d,
+ +2.212106704711914d,
+ +2.214268207550049d,
+ +2.2164316177368164d,
+ +2.218596935272217d,
+ +2.220764636993408d,
+ +2.2229342460632324d,
+ +2.2251062393188477d,
+ +2.2272801399230957d,
+ +2.2294564247131348d,
+ +2.2316346168518066d,
+ +2.2338151931762695d,
+ +2.2359976768493652d,
+ +2.2381820678710938d,
+ +2.2403693199157715d,
+ +2.242558002471924d,
+ +2.244749069213867d,
+ +2.2469425201416016d,
+ +2.2491378784179688d,
+ +2.2513351440429688d,
+ +2.2535347938537598d,
+ +2.2557363510131836d,
+ +2.2579402923583984d,
+ +2.2601466178894043d,
+ +2.262354850769043d,
+ +2.2645654678344727d,
+ +2.266777992248535d,
+ +2.2689924240112305d,
+ +2.271209716796875d,
+ +2.273428440093994d,
+ +2.2756495475769043d,
+ +2.2778730392456055d,
+ +2.2800989151000977d,
+ +2.2823266983032227d,
+ +2.2845563888549805d,
+ +2.2867884635925293d,
+ +2.289022922515869d,
+ +2.291259288787842d,
+ +2.2934980392456055d,
+ +2.295738697052002d,
+ +2.2979817390441895d,
+ +2.300227165222168d,
+ +2.3024744987487793d,
+ +2.3047242164611816d,
+ +2.306975841522217d,
+ +2.309229850769043d,
+ +2.31148624420166d,
+ +2.31374454498291d,
+ +2.316005229949951d,
+ +2.318267822265625d,
+ +2.32053279876709d,
+ +2.3228001594543457d,
+ +2.3250694274902344d,
+ +2.3273415565490723d,
+ +2.3296151161193848d,
+ +2.3318915367126465d,
+ +2.334169864654541d,
+ +2.3364500999450684d,
+ +2.338733196258545d,
+ +2.3410181999206543d,
+ +2.3433055877685547d,
+ +2.345594882965088d,
+ +2.347886562347412d,
+ +2.3501806259155273d,
+ +2.3524770736694336d,
+ +2.3547754287719727d,
+ +2.3570761680603027d,
+ +2.3593788146972656d,
+ +2.3616843223571777d,
+ +2.3639917373657227d,
+ +2.3663015365600586d,
+ +2.3686132431030273d,
+ +2.370927333831787d,
+ +2.373243808746338d,
+ +2.3755626678466797d,
+ +2.3778839111328125d,
+ +2.380207061767578d,
+ +2.3825325965881348d,
+ +2.3848605155944824d,
+ +2.387190818786621d,
+ +2.3895230293273926d,
+ +2.391857624053955d,
+ +2.3941946029663086d,
+ +2.396533966064453d,
+ +2.3988752365112305d,
+ +2.401218891143799d,
+ +2.4035654067993164d,
+ +2.4059133529663086d,
+ +2.40826416015625d,
+ +2.4106173515319824d,
+ +2.4129724502563477d,
+ +2.415329933166504d,
+ +2.417689800262451d,
+ +2.4200520515441895d,
+ +2.4224166870117188d,
+ +2.424783229827881d,
+ +2.427152633666992d,
+ +2.4295239448547363d,
+ +2.4318976402282715d,
+ +2.4342737197875977d,
+ +2.436652183532715d,
+ +2.439032554626465d,
+ +2.441415786743164d,
+ +2.4438014030456543d,
+ +2.4461889266967773d,
+ +2.4485788345336914d,
+ +2.4509711265563965d,
+ +2.4533658027648926d,
+ +2.4557628631591797d,
+ +2.458162307739258d,
+ +2.460564136505127d,
+ +2.462968349456787d,
+ +2.46537446975708d,
+ +2.4677834510803223d,
+ +2.4701943397521973d,
+ +2.4726080894470215d,
+ +2.4750237464904785d,
+ +2.4774417877197266d,
+ +2.479862689971924d,
+ +2.482285499572754d,
+ +2.484710693359375d,
+ +2.487138271331787d,
+ +2.4895682334899902d,
+ +2.4920010566711426d,
+ +2.4944357872009277d,
+ +2.496872901916504d,
+ +2.499312400817871d,
+ +2.5017542839050293d,
+ +2.5041985511779785d,
+ +2.5066452026367188d,
+ +2.50909423828125d,
+ +2.5115456581115723d,
+ +2.5139999389648438d,
+ +2.516456127166748d,
+ +2.5189146995544434d,
+ +2.5213756561279297d,
+ +2.5238394737243652d,
+ +2.5263051986694336d,
+ +2.528773307800293d,
+ +2.5312442779541016d,
+ +2.533717155456543d,
+ +2.5361928939819336d,
+ +2.538670539855957d,
+ +2.5411510467529297d,
+ +2.5436339378356934d,
+ +2.546119213104248d,
+ +2.5486068725585938d,
+ +2.5510969161987305d,
+ +2.553589344024658d,
+ +2.556084632873535d,
+ +2.558581829071045d,
+ +2.5610814094543457d,
+ +2.5635838508605957d,
+ +2.5660886764526367d,
+ +2.5685958862304688d,
+ +2.571105480194092d,
+ +2.573617458343506d,
+ +2.576131820678711d,
+ +2.5786490440368652d,
+ +2.5811686515808105d,
+ +2.5836901664733887d,
+ +2.586214542388916d,
+ +2.5887417793273926d,
+ +2.591270923614502d,
+ +2.5938024520874023d,
+ +2.596336841583252d,
+ +2.5988736152648926d,
+ +2.601412773132324d,
+ +2.603954315185547d,
+ +2.6064987182617188d,
+ +2.6090455055236816d,
+ +2.6115946769714355d,
+ +2.6141462326049805d,
+ +2.6167001724243164d,
+ +2.6192569732666016d,
+ +2.6218161582946777d,
+ +2.624377727508545d,
+ +2.626941680908203d,
+ +2.6295084953308105d,
+ +2.632077217102051d,
+ +2.6346492767333984d,
+ +2.637223243713379d,
+ +2.6398000717163086d,
+ +2.6423792839050293d,
+ +2.644960880279541d,
+ +2.6475448608398438d,
+ +2.6501317024230957d,
+ +2.6527209281921387d,
+ +2.655313014984131d,
+ +2.657907009124756d,
+ +2.6605043411254883d,
+ +2.6631035804748535d,
+ +2.665705680847168d,
+ +2.6683101654052734d,
+ +2.67091703414917d,
+ +2.6735267639160156d,
+ +2.6761388778686523d,
+ +2.67875337600708d,
+ +2.681370735168457d,
+ +2.683990478515625d,
+ +2.686613082885742d,
+ +2.689237594604492d,
+ +2.6918654441833496d,
+ +2.69449520111084d,
+ +2.6971278190612793d,
+ +2.699763298034668d,
+ +2.7024011611938477d,
+ +2.7050414085388184d,
+ +2.70768404006958d,
+ +2.710329532623291d,
+ +2.712977886199951d,
+ +2.7156286239624023d,
+ +2.7182817459106445d,
+ };
+
+ /**
+ * Exponential over the range of 0 - 1 in increments of 2^-10 exp(x/1024) = expFracTableA[x] +
+ * expFracTableB[x].
+ */
+ private static final double[] EXP_FRAC_B =
+ new double[] {
+ +0.0d,
+ +1.552583321178453E-10d,
+ +1.2423699995465188E-9d,
+ +4.194022929828008E-9d,
+ +9.94381632344361E-9d,
+ +1.9426261544163577E-8d,
+ +3.3576783010266685E-8d,
+ +5.3331719086630523E-8d,
+ +7.962832297769345E-8d,
+ +1.1340476362128895E-7d,
+ -8.281845251820919E-8d,
+ -3.126416414805498E-8d,
+ +3.058997113995161E-8d,
+ +1.0368579417304741E-7d,
+ -4.9452513107409435E-8d,
+ +4.8955889659397494E-8d,
+ -7.698155155722897E-8d,
+ +5.051784853384516E-8d,
+ -4.443661736519001E-8d,
+ +1.1593958457401774E-7d,
+ +5.575759739697068E-8d,
+ +1.4385227981629147E-8d,
+ -7.227368462584163E-9d,
+ -8.129108387083023E-9d,
+ +1.263202100290635E-8d,
+ +5.600896265625552E-8d,
+ -1.154629885168314E-7d,
+ -2.399186832888246E-8d,
+ +9.295948298604103E-8d,
+ -2.070841011504222E-9d,
+ -6.97066538508643E-8d,
+ -1.0898941254272996E-7d,
+ -1.1895963756343625E-7d,
+ -9.865691193993138E-8d,
+ -4.711988033385175E-8d,
+ +3.6613751875298095E-8d,
+ -8.491135959370133E-8d,
+ +6.610611940107793E-8d,
+ +1.3794148633283659E-8d,
+ -2.462631860370667E-9d,
+ +1.830278273495162E-8d,
+ +7.705834203598065E-8d,
+ -6.364563771711373E-8d,
+ +7.39978436695387E-8d,
+ +1.4122417557484554E-8d,
+ -3.881598887298574E-9d,
+ +2.0958481826069642E-8d,
+ +8.96162975425619E-8d,
+ -3.535214171178576E-8d,
+ -1.1455271549574576E-7d,
+ +9.140964977432485E-8d,
+ +1.0667524445105459E-7d,
+ -6.777752790396222E-8d,
+ +4.586785041291296E-8d,
+ -2.8245462428022094E-8d,
+ -5.071761314397018E-8d,
+ -2.0566368810068663E-8d,
+ +6.319146317890346E-8d,
+ -3.687854305539139E-8d,
+ -8.137269363160008E-8d,
+ -6.930491127388755E-8d,
+ +3.1184473002226595E-10d,
+ -1.0995299963140049E-7d,
+ +7.772668425499348E-8d,
+ +8.750367485925089E-8d,
+ -7.963112393823186E-8d,
+ +5.415131809829094E-8d,
+ +1.3006683896462346E-8d,
+ +3.634736373360733E-8d,
+ -1.132504393233074E-7d,
+ +4.2046187038837375E-8d,
+ +2.6396811618001066E-8d,
+ +7.92177143584738E-8d,
+ -3.691100820545433E-8d,
+ -8.257112559083188E-8d,
+ -5.676200971739166E-8d,
+ +4.151794514828518E-8d,
+ -2.5147255753587636E-8d,
+ -1.7335469415174996E-8d,
+ +6.595784859136531E-8d,
+ -1.2680354928109105E-8d,
+ -1.3824992526093461E-8d,
+ +6.353142754175797E-8d,
+ -1.8021197722549054E-8d,
+ -1.9054827792903468E-8d,
+ +6.144098503892116E-8d,
+ -1.3940903373095247E-8d,
+ -5.7694907599522404E-9d,
+ +8.696863522320578E-8d,
+ +2.6869297963554945E-8d,
+ +5.3366470162689076E-8d,
+ -7.094204160127543E-8d,
+ -1.0662027949814858E-7d,
+ -5.26498707801063E-8d,
+ +9.198855229106814E-8d,
+ +8.989677431456647E-8d,
+ -5.790384407322479E-8d,
+ -1.1197236522467887E-7d,
+ -7.12854317090566E-8d,
+ +6.51813137650059E-8d,
+ +6.003465022483798E-8d,
+ -8.569906238528267E-8d,
+ +1.0584469687624562E-7d,
+ -7.956144278281947E-8d,
+ +7.43676272093501E-8d,
+ +9.182512565315022E-8d,
+ -2.6157563728873715E-8d,
+ -4.012947040998503E-8d,
+ +5.094280572218447E-8d,
+ +9.675095351161728E-9d,
+ +7.552139802281006E-8d,
+ +1.1099566726533146E-8d,
+ +5.58656252899437E-8d,
+ -2.756054703800197E-8d,
+ +2.791018095971047E-10d,
+ -9.799351869734466E-8d,
+ -8.291832428736212E-8d,
+ +4.654720780112994E-8d,
+ +5.302803981406403E-8d,
+ -6.243126731995636E-8d,
+ -6.036655299348577E-8d,
+ +6.026878587378257E-8d,
+ +6.210379583313526E-8d,
+ -5.381287389094251E-8d,
+ -4.8012970400697E-8d,
+ +8.055420567281602E-8d,
+ +9.452180117175641E-8d,
+ -5.057430382371206E-9d,
+ +2.1288872215266507E-8d,
+ -6.380305844689076E-8d,
+ -2.0858800984600168E-8d,
+ -8.724006061713588E-8d,
+ -2.3470351753125604E-8d,
+ -6.690931338790221E-8d,
+ +2.192160831263035E-8d,
+ +5.6648446166177225E-9d,
+ -1.1461755745719884E-7d,
+ -9.944393412663547E-8d,
+ +5.2249837964645906E-8d,
+ +1.0311034276196487E-7d,
+ +5.4203784018566126E-8d,
+ -9.340259278913173E-8d,
+ -1.0022192034216903E-7d,
+ +3.481513333662908E-8d,
+ +7.436036590244714E-8d,
+ +1.9485199912395296E-8d,
+ +1.0968068384729757E-7d,
+ +1.0760175582979094E-7d,
+ +1.4322981952798675E-8d,
+ +6.933855730431659E-8d,
+ +3.530656968851287E-8d,
+ -8.669526204279467E-8d,
+ -5.7169586962345785E-8d,
+ -1.1345515834332824E-7d,
+ -1.605251622332555E-8d,
+ -2.298302779758532E-9d,
+ -7.110952399338234E-8d,
+ +1.70164513845372E-8d,
+ +2.4746155561368937E-8d,
+ -4.6834239957353325E-8d,
+ +4.1781076667923185E-8d,
+ +5.326182134294869E-8d,
+ -1.1302647617762544E-8d,
+ +8.759667154796094E-8d,
+ +1.126326877851684E-7d,
+ +6.48979555673987E-8d,
+ -5.451390316294111E-8d,
+ -6.0896188500539086E-9d,
+ -2.7152010585461855E-8d,
+ -1.1660424775832058E-7d,
+ -3.492984900939992E-8d,
+ -1.944841848873016E-8d,
+ -6.905990750285027E-8d,
+ +5.575538653428039E-8d,
+ +1.1768108384670781E-7d,
+ +1.178204606523101E-7d,
+ +5.727787111340131E-8d,
+ -6.284125161007433E-8d,
+ -3.0118152047565877E-9d,
+ -5.448044533034374E-10d,
+ -5.433154287341921E-8d,
+ +7.515630833946181E-8d,
+ -8.780756503572527E-8d,
+ -6.527407547535494E-8d,
+ -9.45487863616303E-8d,
+ +6.390098458668406E-8d,
+ -6.564672913105876E-8d,
+ -5.238488022920792E-9d,
+ +7.824500749252316E-9d,
+ -2.5339299158309795E-8d,
+ -1.036103313062145E-7d,
+ +1.2550633697348567E-8d,
+ +8.584676196065558E-8d,
+ +1.1740089468291563E-7d,
+ +1.0833697012353316E-7d,
+ +5.978002467397905E-8d,
+ -2.7143806069290897E-8d,
+ +8.711129287069315E-8d,
+ -7.316349947981893E-8d,
+ -3.00015852582934E-8d,
+ -2.0691000399732483E-8d,
+ -4.4100097152254264E-8d,
+ -9.909612209943178E-8d,
+ +5.38733640215475E-8d,
+ -6.0893829005035E-8d,
+ +3.457553391989844E-8d,
+ +1.0300006058273187E-7d,
+ -9.290053015365092E-8d,
+ -7.514966995961323E-8d,
+ -8.10254145615142E-8d,
+ -1.0938612624777085E-7d,
+ +7.932952721989251E-8d,
+ +9.428257290008738E-9d,
+ -7.952636967837795E-8d,
+ +5.203033137154554E-8d,
+ -7.159157201731446E-8d,
+ +2.7593424989059015E-8d,
+ +1.1231621190000476E-7d,
+ -5.469119869891027E-8d,
+ +4.560067256086347E-9d,
+ +5.280427179595944E-8d,
+ +9.119538242455128E-8d,
+ -1.1753008498403413E-7d,
+ -9.537874867759656E-8d,
+ -7.96118345325538E-8d,
+ -6.907085854395348E-8d,
+ -6.259620482221904E-8d,
+ -5.902712448725381E-8d,
+ -5.720173456146447E-8d,
+ -5.5957016861703E-8d,
+ -5.412881689012608E-8d,
+ -5.0551842723970724E-8d,
+ -4.405966390424518E-8d,
+ -3.348471032333413E-8d,
+ -1.7658271111516935E-8d,
+ +4.589506477601956E-9d,
+ +3.4429618182751655E-8d,
+ +7.303420385174346E-8d,
+ -1.168420305422519E-7d,
+ -5.718749537552229E-8d,
+ +1.4754809136835937E-8d,
+ +1.001616104682875E-7d,
+ -3.8207793300052055E-8d,
+ +7.766278405014509E-8d,
+ -2.7883635712109803E-8d,
+ -1.1524714043067699E-7d,
+ +5.517333625963128E-8d,
+ +7.724278756071081E-9d,
+ -1.7990934773848504E-8d,
+ -2.0786347668702902E-8d,
+ +5.251554594269693E-10d,
+ +4.7131849857076246E-8d,
+ -1.1819540733893871E-7d,
+ -1.742885956093543E-8d,
+ +1.1220467571570283E-7d,
+ +3.347954541376715E-8d,
+ -1.399157980498908E-8d,
+ -2.9013441705763093E-8d,
+ -1.0389614239253089E-8d,
+ +4.307749759934266E-8d,
+ -1.0583192018912101E-7d,
+ +2.0919226941745448E-8d,
+ -5.2305110482722706E-8d,
+ -8.588407110184028E-8d,
+ -7.861419797923639E-8d,
+ -2.929085835358592E-8d,
+ +6.329175751021792E-8d,
+ -3.807794163054899E-8d,
+ -9.377320954068088E-8d,
+ -1.0258469865953145E-7d,
+ -6.330187984612758E-8d,
+ +2.5286958775281306E-8d,
+ -7.40238661307607E-8d,
+ +1.1681688445204168E-7d,
+ -1.1623125976292733E-7d,
+ -5.6696107089038004E-8d,
+ +5.822140627806124E-8d,
+ -8.678466172071259E-9d,
+ -1.7757121899175995E-8d,
+ +3.220665454652531E-8d,
+ -9.598330731102836E-8d,
+ +7.573375369829243E-8d,
+ +7.174547784678893E-8d,
+ -1.0672213971363184E-7d,
+ +1.8395252217743006E-8d,
+ -2.8511112548600118E-8d,
+ -7.79306270997787E-9d,
+ +8.178019529487065E-8d,
+ +3.0220784595602374E-9d,
+ -4.4156343103298585E-9d,
+ +6.07014616741277E-8d,
+ -3.8809601937571554E-8d,
+ -6.329342805230603E-8d,
+ -1.1511990258493999E-8d,
+ +1.177739474561431E-7d,
+ +8.738625278484571E-8d,
+ -1.0143341551207646E-7d,
+ +2.9394972678456236E-8d,
+ +4.278345398213486E-9d,
+ +6.28805835150457E-8d,
+ -3.197037359731606E-8d,
+ -4.060821046423735E-8d,
+ +3.82160283750664E-8d,
+ -3.2666060441373307E-8d,
+ -1.3584500601329896E-8d,
+ +9.671332777035621E-8d,
+ +6.10626893063691E-8d,
+ +1.1913723189736356E-7d,
+ +3.3774671482641995E-8d,
+ +4.4651109654500895E-8d,
+ -8.539328154875224E-8d,
+ -1.166799420361101E-7d,
+ -4.794765976694151E-8d,
+ -1.1635256954820579E-7d,
+ -8.221241452580445E-8d,
+ +5.5737717715868425E-8d,
+ +6.034539636024073E-8d,
+ -6.712199323081945E-8d,
+ -8.697724830833087E-8d,
+ +2.0494942705297694E-9d,
+ -3.718924074653624E-8d,
+ +3.499747150995707E-8d,
+ -1.8535359161566028E-8d,
+ +4.1905679587096103E-8d,
+ -2.0821912536551675E-8d,
+ +3.297776915751238E-8d,
+ -3.3835280846270374E-8d,
+ +1.8437339356553904E-8d,
+ -4.734187609526424E-8d,
+ +8.527976799299225E-9d,
+ -5.1088103279787804E-8d,
+ +1.3513294656751725E-8d,
+ -3.480032127343472E-8d,
+ +4.367697180842916E-8d,
+ +1.1815196363705356E-8d,
+ +1.0932279207149782E-7d,
+ +9.907230065250944E-8d,
+ -1.764389559496152E-8d,
+ -1.1135725625095859E-9d,
+ -8.846040040259342E-8d,
+ -3.996962588736431E-8d,
+ -9.276238757878814E-8d,
+ -7.12139818505956E-9d,
+ -2.016525972830718E-8d,
+ +1.0782585410141121E-7d,
+ -9.868269632073771E-8d,
+ +7.686861750031585E-8d,
+ -7.947087669425045E-8d,
+ -8.955768055535647E-8d,
+ +4.791582240886607E-8d,
+ +9.583994718167641E-8d,
+ +5.5524866689108584E-8d,
+ -7.171796605211277E-8d,
+ -4.6157237582310713E-8d,
+ -1.0489751005162237E-7d,
+ -8.204903560604627E-9d,
+ +6.818588687884566E-9d,
+ -5.850916105103205E-8d,
+ +3.5549586192569994E-8d,
+ +5.1896700056778354E-8d,
+ -8.146080588190463E-9d,
+ +9.516285362051742E-8d,
+ -1.1368933260611668E-7d,
+ +8.187871486648885E-8d,
+ -3.206182925646474E-8d,
+ +2.265440168347286E-8d,
+ +8.938334752179552E-9d,
+ -7.187922490287331E-8d,
+ +1.9952407216533937E-8d,
+ +4.734805892507655E-8d,
+ +1.1642439930208906E-8d,
+ -8.582843599651953E-8d,
+ -5.3086706437795354E-9d,
+ +1.6121782610217253E-8d,
+ -2.0197142620980974E-8d,
+ -1.129242035557684E-7d,
+ -2.2298267863810133E-8d,
+ +1.4605950309628873E-8d,
+ -8.663710700190489E-10d,
+ -6.736873974532501E-8d,
+ +5.486523121881414E-8d,
+ -1.0965249168570443E-7d,
+ -8.27343074126263E-8d,
+ -1.0144703278439455E-7d,
+ +7.39809943048038E-8d,
+ -3.193297932837415E-8d,
+ +5.900393284617182E-8d,
+ +1.0973020465397083E-7d,
+ -1.1681436418514489E-7d,
+ +9.5985669644661E-8d,
+ +3.423560333632085E-8d,
+ -6.22836197265283E-8d,
+ +4.621027492345726E-8d,
+ -1.1575484316683829E-7d,
+ -6.997545435826076E-8d,
+ -5.3502441327259514E-8d,
+ -6.49667713553005E-8d,
+ -1.029980741248172E-7d,
+ +7.219393868923887E-8d,
+ -1.4854841678687828E-8d,
+ +1.1406713393562271E-7d,
+ -1.650155887561251E-8d,
+ +7.165331603232264E-8d,
+ -9.692697614257269E-8d,
+ -4.402550702194912E-8d,
+ -6.679737442193143E-9d,
+ +1.6492800268960003E-8d,
+ +2.68759245092879E-8d,
+ +2.5854805721793077E-8d,
+ +1.4815967715704613E-8d,
+ -4.852711011229633E-9d,
+ -3.176199594915881E-8d,
+ -6.452129525125173E-8d,
+ -1.01738658407525E-7d,
+ +9.639780418418697E-8d,
+ +5.4445606140746644E-8d,
+ +1.2219361033150988E-8d,
+ -2.8883532688356087E-8d,
+ -6.746431126005811E-8d,
+ -1.0212284427080097E-7d,
+ +1.0696094577483825E-7d,
+ +8.43527683868743E-8d,
+ +6.987544103716777E-8d,
+ +6.493457409236137E-8d,
+ +7.093715125593688E-8d,
+ +8.929153091001965E-8d,
+ -1.1701113164306871E-7d,
+ -6.972256643013266E-8d,
+ -5.848862070736576E-9d,
+ +7.602385197610123E-8d,
+ -6.110775144284437E-8d,
+ +6.101012058093429E-8d,
+ -3.304167134225169E-8d,
+ -1.0342514383702196E-7d,
+ +8.969907328603505E-8d,
+ +7.091600108064668E-8d,
+ +8.006778743052707E-8d,
+ +1.1857939200074815E-7d,
+ -5.0541412403312774E-8d,
+ +5.0970277930552287E-8d,
+ -5.229355472795119E-8d,
+ +1.1793478462381443E-7d,
+ +8.625007227318527E-8d,
+ +9.250422086873268E-8d,
+ -1.0028661472061573E-7d,
+ -1.384914052949463E-8d,
+ +1.1483560326413004E-7d,
+ +4.878798101459259E-8d,
+ +2.7866921183936055E-8d,
+ +5.3514180410849046E-8d,
+ -1.1124565511436785E-7d,
+ +1.186914813275767E-8d,
+ -5.253258132241335E-8d,
+ -6.458486486369316E-8d,
+ -2.2838888809969377E-8d,
+ +7.415557606805398E-8d,
+ -1.0568403170659571E-8d,
+ -3.7139182948393606E-8d,
+ -4.1022790876160215E-9d,
+ +8.999821367768787E-8d,
+ +8.201043988912348E-9d,
+ -9.616457442665051E-9d,
+ +3.8005886250603055E-8d,
+ -8.588890051473289E-8d,
+ +9.699937202692456E-8d,
+ +1.11298006674538E-7d,
+ -4.1527104733570825E-8d,
+ +1.1682852007826251E-7d,
+ +1.1099648061301941E-7d,
+ -5.755303038890997E-8d,
+ +8.948877445235827E-8d,
+ +7.675780395028194E-8d,
+ -9.427143563390596E-8d,
+ +5.471416081500162E-8d,
+ +4.8354824064383506E-8d,
+ -1.118706134478866E-7d,
+ +5.235528379688445E-8d,
+ +6.567708120053687E-8d,
+ -7.042204992948526E-8d,
+ -1.1603891006723397E-7d,
+ -6.968742825553785E-8d,
+ +7.01199184127881E-8d,
+ +6.645352711199266E-8d,
+ -7.919617109348822E-8d,
+ +1.1149986927391714E-7d,
+ -7.522074418324674E-8d,
+ +7.739252980388984E-8d,
+ +9.39987974788905E-8d,
+ -2.390421480210064E-8d,
+ -3.639873824357815E-8d,
+ +5.8015881615938497E-8d,
+ +2.2423186335040668E-8d,
+ +9.674534330665206E-8d,
+ +4.4068830785712375E-8d,
+ +1.0431875573076199E-7d,
+ +4.0584538834428926E-8d,
+ +9.279423236781974E-8d,
+ +2.404020521381534E-8d,
+ +7.425346071427343E-8d,
+ +6.529321706138789E-9d,
+ +6.080174837146273E-8d,
+ +1.6902327633329284E-10d,
+ +6.456806922371733E-8d,
+ +1.7100134295216033E-8d,
+ +9.770510970673519E-8d,
+ +6.94872148530716E-8d,
+ -6.602926393514549E-8d,
+ -6.889997193778161E-8d,
+ +6.240235720677117E-8d,
+ +9.098790295810902E-8d,
+ +1.8386917534879182E-8d,
+ +8.454972737414241E-8d,
+ +5.259099728747365E-8d,
+ -7.595453077213505E-8d,
+ -6.113203624663034E-8d,
+ +9.859622328905143E-8d,
+ -7.206766550807255E-8d,
+ -9.474579567171831E-8d,
+ +3.210408693366267E-8d,
+ +7.160716418525417E-8d,
+ +2.530870537724554E-8d,
+ -1.0524451040704701E-7d,
+ -8.008561371849434E-8d,
+ +1.0233519853128553E-7d,
+ -3.326791455362767E-8d,
+ -8.504961764629757E-9d,
+ -6.024017201863256E-8d,
+ +5.1500902632092514E-8d,
+ +8.98570720774568E-8d,
+ +5.638724693948384E-8d,
+ -4.734813904255994E-8d,
+ +1.8631451577542948E-8d,
+ +1.7470924137873214E-8d,
+ -4.926470933588261E-8d,
+ +5.84096713620797E-8d,
+ +1.0364355880696472E-7d,
+ +8.800655674349468E-8d,
+ +1.3069802481237792E-8d,
+ +1.1882454749452428E-7d,
+ -6.999215748398631E-8d,
+ -7.49674072510849E-8d,
+ +1.054760847603618E-7d,
+ -3.920012014371067E-9d,
+ +7.526183084319617E-8d,
+ +1.0618494853096868E-7d,
+ +9.043280094115832E-8d,
+ +2.9590395068826316E-8d,
+ -7.475571347653619E-8d,
+ +1.7401160143611842E-8d,
+ +6.923209420670962E-8d,
+ +8.232829924979753E-8d,
+ +5.82825404854514E-8d,
+ -1.3108606792380822E-9d,
+ -9.485602512220194E-8d,
+ +1.7663064617118723E-8d,
+ +9.942682855652123E-8d,
+ -8.638275100090915E-8d,
+ -6.132639063569726E-8d,
+ -6.221897889344726E-8d,
+ -8.745525834919404E-8d,
+ +1.029901759234897E-7d,
+ +3.3888561478632076E-8d,
+ -5.47315553588771E-8d,
+ +7.715994473741065E-8d,
+ -4.566098167230033E-8d,
+ +5.5257514455273825E-8d,
+ -9.530545662611411E-8d,
+ -1.889488909834863E-8d,
+ +4.769006625301079E-8d,
+ +1.0607041998938709E-7d,
+ -8.054981263802322E-8d,
+ -3.370929373457322E-8d,
+ +9.799164177397836E-9d,
+ +5.160291611526656E-8d,
+ +9.333090708652975E-8d,
+ -1.0180490545927503E-7d,
+ -5.533523366931846E-8d,
+ -4.044932340334176E-9d,
+ +5.370131904567218E-8d,
+ -1.1887814032213867E-7d,
+ -4.3307634616102625E-8d,
+ +4.363437558318513E-8d,
+ -9.482896784430338E-8d,
+ +1.9782818312325887E-8d,
+ -8.77224935488516E-8d,
+ +6.113879253864931E-8d,
+ -8.822335132515693E-9d,
+ -5.753754066078771E-8d,
+ -8.335545536862392E-8d,
+ -8.462309712606694E-8d,
+ -5.968586877433824E-8d,
+ -6.887556547891059E-9d,
+ +7.542967150507818E-8d,
+ -4.949331199790077E-8d,
+ +9.684172421525468E-8d,
+ +3.9260317944365246E-8d,
+ +1.784536881359796E-8d,
+ +3.426282345243592E-8d,
+ +9.018025618601154E-8d,
+ -5.1151708476133135E-8d,
+ +8.877492215808044E-8d,
+ +3.479545684576179E-8d,
+ +2.7002575714977818E-8d,
+ +6.707201545505014E-8d,
+ -8.173742908533777E-8d,
+ +5.909041310777802E-8d,
+ +1.439903710393587E-8d,
+ +2.4289317341982113E-8d,
+ +9.044519282818302E-8d,
+ -2.3866331257845713E-8d,
+ -7.853944465095286E-8d,
+ -7.188526769607005E-8d,
+ -2.2132706360079843E-9d,
+ -1.0624985110080394E-7d,
+ +9.453598391231829E-8d,
+ -1.134160131581847E-7d,
+ -1.315295870404327E-8d,
+ -7.981320644583728E-8d,
+ -7.327771300038971E-8d,
+ +8.155647334672472E-9d,
+ -7.222791579580787E-8d,
+ -7.430436987497092E-8d,
+ +3.633404807819848E-9d,
+ -7.512438321498593E-8d,
+ -7.044869765481105E-8d,
+ +1.9372589859580955E-8d,
+ -4.2365298585101096E-8d,
+ -1.552830824758035E-8d,
+ +1.0160071259930585E-7d,
+ +7.232201430620959E-8d,
+ -1.0164389431039905E-7d,
+ +5.826233477413577E-8d,
+ +7.6927415825689E-8d,
+ -4.392309439525734E-8d,
+ -6.414337408955734E-8d,
+ +1.799550702470095E-8d,
+ -3.4194410638967946E-8d,
+ +1.9437762419688045E-8d,
+ -5.7792549966531335E-8d,
+ -2.5731071572354522E-8d,
+ +1.173595905705643E-7d,
+ -1.0361863127101014E-7d,
+ +2.8330789837569332E-8d,
+ +3.81131861433539E-8d,
+ -7.252724942149532E-8d,
+ -6.342604067787756E-8d,
+ +6.716441526213986E-8d,
+ +8.257484966196574E-8d,
+ -1.5443717968117592E-8d,
+ +1.3280021798948244E-8d,
+ -6.79180673261558E-8d,
+ -1.8863249269709046E-8d,
+ -7.62162303263991E-8d,
+ +2.011589233663723E-10d,
+ -2.62683511147141E-8d,
+ +8.455684903712996E-8d,
+ +9.602293320384794E-8d,
+ +9.896378545255258E-9d,
+ +6.636396724067746E-8d,
+ +2.8777050870552646E-8d,
+ -1.0109271059094341E-7d,
+ -8.305334708631055E-8d,
+ +8.467026501338835E-8d,
+ -7.29821745001452E-8d,
+ -7.739491336852633E-8d,
+ +7.321238022013781E-8d,
+ -9.621538067089515E-8d,
+ -1.0705722541811197E-7d,
+ +4.247240125405735E-8d,
+ +1.1574222007764044E-7d,
+ +1.145412771487496E-7d,
+ +4.066036653218687E-8d,
+ -1.0410796803072171E-7d,
+ -7.955085231106037E-8d,
+ +1.1612776191572459E-7d,
+ +7.888519481107568E-9d,
+ +7.436813814737735E-8d,
+ +7.894935661289349E-8d,
+ +2.343525263620692E-8d,
+ -9.036933434595339E-8d,
+ -2.2239222395888823E-8d,
+ -8.784622656707742E-9d,
+ -4.819540032304379E-8d,
+ +9.975892708522332E-8d,
+ -3.9945124955316294E-8d,
+ +1.1345047468988893E-8d,
+ +1.702808472925844E-8d,
+ -2.10770182066344E-8d,
+ -1.0114948914089626E-7d,
+ +1.70518021921727E-8d,
+ +9.693260855961159E-8d,
+ -9.809953482725758E-8d,
+ -8.937957126662392E-8d,
+ -1.134963954323427E-7d,
+ +6.980004387880031E-8d,
+ -1.4494150014095534E-8d,
+ +1.122932337832262E-7d,
+ -2.483811732227808E-8d,
+ +5.278759515330048E-8d,
+ +1.0859222881334994E-7d,
+ -9.400056055939758E-8d,
+ -7.630957994128623E-8d,
+ -7.490757191850264E-8d,
+ -8.794689652049879E-8d,
+ -1.1357810855950775E-7d,
+ +8.846862323478745E-8d,
+ +4.32092015744956E-8d,
+ -9.082923009890997E-9d,
+ -6.655106680680314E-8d,
+ +1.1108184705020206E-7d,
+ +4.8838973948592766E-8d,
+ -1.2998975819628988E-8d,
+ -7.25680516883106E-8d,
+ -1.280024819379844E-7d,
+ -1.7743467191652895E-7d,
+ -2.1899520225809197E-7d,
+ +2.2602433110285232E-7d,
+ +2.0582268590356215E-7d,
+ +1.9911192455808124E-7d,
+ +2.0776878313278689E-7d,
+ +2.3367183133931002E-7d,
+ -1.9813568387704588E-7d,
+ -1.320972037315105E-7d,
+ -4.316580502355056E-8d,
+ +7.054443447243064E-8d,
+ +2.109212796025238E-7d,
+ -9.698281856949837E-8d,
+ +1.0239791185239086E-7d,
+ -1.4271754202157014E-7d,
+ +1.232402895636637E-7d,
+ -5.150590480969644E-8d,
+ -1.882201085012735E-7d,
+ +1.918355503889933E-7d,
+ +1.368893262241355E-7d,
+ +1.256828068633383E-7d,
+ +1.601222826656464E-7d,
+ -2.3472125169205568E-7d,
+ -1.032634625827871E-7d,
+ +7.957037517331382E-8d,
+ -1.6114314525832115E-7d,
+ +1.3018591370778052E-7d,
+ +1.8007284821359149E-9d,
+ -6.75421764491544E-8d,
+ -7.592155950645605E-8d,
+ -2.1414301981236817E-8d,
+ +9.79045937979623E-8d,
+ -1.9287515190177685E-7d,
+ +6.184953843236509E-8d,
+ -8.966500602352001E-8d,
+ -1.686490951669855E-7d,
+ -1.7316830893872364E-7d,
+ -1.0128633727463388E-7d,
+ +4.8935021740786486E-8d,
+ -1.9740129448026905E-7d,
+ +1.1532102163380318E-7d,
+ +3.5371542244169364E-8d,
+ +4.153321337726989E-8d,
+ +1.3575372396796738E-7d,
+ -1.5685449228299222E-7d,
+ +1.1933437776279623E-7d,
+ +1.2599421120614435E-8d,
+ +1.7331079674066365E-9d,
+ +8.869266069401045E-8d,
+ -2.013999442282902E-7d,
+ +8.709065843311144E-8d,
+ +2.453117120472083E-9d,
+ +2.3489472779602617E-8d,
+ +1.5216652792122652E-7d,
+ -8.638415150333099E-8d,
+ -2.1335475961524608E-7d,
+ -2.2677272333821516E-7d,
+ -1.246635423141374E-7d,
+ +9.494921297991565E-8d,
+ -4.27932550865546E-8d,
+ -5.907349480138712E-8d,
+ +4.809072216941908E-8d,
+ -1.9615359732789476E-7d,
+ +1.6385396676990034E-7d,
+ +1.7642714221524228E-7d,
+ -1.564440844355254E-7d,
+ +1.2090653407564583E-7d,
+ +5.679855838941285E-8d,
+ +1.3006497185242537E-7d,
+ -1.341336085949317E-7d,
+ +2.1987686050231372E-7d,
+ -2.3641341460419062E-7d,
+ -7.048932272279454E-8d,
+ -2.3401958604540354E-7d,
+ +2.2867766559333004E-7d,
+ -1.1089952719756529E-7d,
+ +1.7977178878541792E-7d,
+ +1.4903074102418675E-7d,
+ -2.011072593789072E-7d,
+ +8.504948422097802E-8d,
+ +5.5846006716348844E-8d,
+ +1.9014079059505456E-7d,
+ +1.3119976852347583E-8d,
+ +3.645999732952202E-9d,
+ +1.6374611405314333E-7d,
+ +1.8612397134087598E-8d,
+ +4.7113225346448296E-8d,
+ -2.2555535676499395E-7d,
+ +1.5631615647329739E-7d,
+ -2.3574653182047758E-7d,
+ +3.08072210937242E-8d,
+ +4.344259288116142E-9d,
+ +1.6374489573868447E-7d,
+ +3.42171232580676E-8d,
+ +9.46452492584643E-8d,
+ -1.297587351085525E-7d,
+ -1.601065201853145E-7d,
+ +5.6550495386976275E-9d,
+ -1.0725602261510391E-7d,
+ -1.9945408945084193E-8d,
+ -2.071910882200156E-7d,
+ -1.900947109027913E-7d,
+ +3.34069282059055E-8d,
+ -1.145810806477298E-8d,
+ +1.5421457732308477E-7d,
+ +5.5657084775121975E-8d,
+ +1.7177785285061278E-7d,
+ +2.7813027425289027E-8d,
+ +1.0267509648109748E-7d,
+ -7.839574072711142E-8d,
+ -3.648293887796095E-8d,
+ +2.3049492079013518E-7d,
+ -2.290530257391564E-7d,
+ +1.747018414872141E-8d,
+ +1.8477759656842807E-8d,
+ -2.2394073401050633E-7d,
+ -2.3085653185818848E-7d,
+ -1.7598351175286083E-10d,
+ -6.640551220774385E-9d,
+ +2.2868466674913266E-7d,
+ +2.3106230530437902E-7d,
+ +2.594209135294356E-9d,
+ +2.2221434720602702E-8d,
+ -1.847872222755186E-7d,
+ -1.3948659218254467E-7d,
+ +1.6023339607737848E-7d,
+ -2.3718944120137026E-7d,
+ +1.0087056692827474E-7d,
+ +2.228553660510707E-7d,
+ +1.3088328582956644E-7d,
+ -1.7292527438195104E-7d,
+ -2.0961068531216087E-7d,
+ +2.2951597845188004E-8d,
+ +5.005103745740068E-8d,
+ -1.2618366811281002E-7d,
+ -2.6784582477238417E-8d,
+ -1.2645600379949252E-7d,
+ +5.3774170051560117E-8d,
+ +3.9205810725333715E-8d,
+ -1.6802196396307013E-7d,
+ -8.893078799284047E-8d,
+ -1.9821451970481713E-7d,
+ -1.689060694498032E-8d,
+ -1.9648717830943396E-8d,
+ -2.0433926409457167E-7d,
+ -9.1973399031975E-8d,
+ -1.5723449006087263E-7d,
+ +7.887051614592191E-8d,
+ +1.4166246290402286E-7d,
+ +3.330146018487787E-8d,
+ +2.3278688667580978E-7d,
+ -2.1139124097042925E-7d,
+ +1.334449995534113E-7d,
+ -1.6104730195920897E-7d,
+ -1.3902314592614197E-7d,
+ +2.0169027167169864E-7d,
+ -9.040643863751471E-8d,
+ -5.946190852360168E-8d,
+ -1.8013411720005014E-7d,
+ +2.6595401669835947E-8d,
+ +8.607292924069425E-8d,
+ +4.84038176769263E-10d,
+ -2.2798356346688802E-7d,
+ -1.203028719549339E-7d,
+ -1.5111906039270745E-7d,
+ +1.5859915617670956E-7d,
+ -1.426262681506497E-7d,
+ -9.892260062323546E-8d,
+ -1.8492643515928268E-7d,
+ +7.840210076743552E-8d,
+ +2.1643071541578027E-7d,
+ +2.313664294893465E-7d,
+ +1.2541842003811723E-7d,
+ -9.920197743470107E-8d,
+ +3.655589133934081E-8d,
+ +5.807052689551411E-8d,
+ -3.244024724169575E-8d,
+ -2.327564406466327E-7d,
+ -6.38187356721971E-8d,
+ -2.3995994000400915E-10d,
+ -3.9793609609721186E-8d,
+ -1.802510054588344E-7d,
+ +5.745586744591196E-8d,
+ +1.987228872666507E-7d,
+ -2.3105188606976847E-7d,
+ +2.0088042407239129E-7d,
+ +6.624793114025702E-8d,
+ -1.5587043044056635E-7d,
+ +1.3606464059428694E-8d,
+ +1.0008761540741556E-7d,
+ +1.058213771597129E-7d,
+ +3.3058299602856804E-8d,
+ -1.1594886810010702E-7d,
+ +1.378919824418909E-7d,
+ -1.5683631181406778E-7d,
+ -4.4200075770425176E-8d,
+ +1.2250985436706623E-9d,
+ -1.8297013058336644E-8d,
+ -1.005004229646318E-7d,
+ +2.337202285991116E-7d,
+ +3.296104292035678E-8d,
+ -2.23668185816307E-7d,
+ -5.7055442971184756E-8d,
+ +5.82391923137467E-8d,
+ +1.244950238958056E-7d,
+ +1.4399358260219398E-7d,
+ +1.1901862840583523E-7d,
+ +5.1856152603337505E-8d,
+ -5.520562000491495E-8d,
+ -1.9987622893254038E-7d,
+ +9.697418238031897E-8d,
+ -1.1603376405901542E-7d,
+ +1.170714288147407E-7d,
+ -1.550851303094034E-7d,
+ +2.3472546699189522E-8d,
+ +1.78211222185955E-7d,
+ -1.6540009048230807E-7d,
+ -5.137865010872577E-8d,
+ +4.57490653163866E-8d,
+ +1.2829599363166098E-7d,
+ +1.985773325073412E-7d,
+ -2.1792661654989742E-7d,
+ -1.652218131743459E-7d,
+ -1.178234251477505E-7d,
+ -7.34071933723896E-8d,
+ -2.9646587857612632E-8d,
+ +1.5787194498912167E-8d,
+ +6.52252321321176E-8d,
+ +1.2100088103262734E-7d,
+ +1.8544977697201776E-7d,
+ -2.159273204728711E-7d,
+ -1.2711589287782304E-7d,
+ -2.2610609958205195E-8d,
+ +9.993330547750349E-8d,
+ -2.33974236642384E-7d,
+ -6.830955860192377E-8d,
+ +1.2244183812423448E-7d,
+ -1.3620325027706252E-7d,
+ +1.1178574689680927E-7d,
+ -8.490693031052439E-8d,
+ +2.2975389535985893E-7d,
+ +1.0445707500867073E-7d,
+ +1.8405243253979117E-8d,
+ -2.6033812325397097E-8d,
+ -2.6489990728664908E-8d,
+ +1.9409124727247465E-8d,
+ +1.1403826867020365E-7d,
+ -2.1706266226554237E-7d,
+ -1.7839974359909697E-8d,
+ +2.3725087624341041E-7d,
+ +7.37567604176979E-8d,
+ -2.9098805266958403E-8d,
+ -6.892713087722722E-8d,
+ -4.333719263537725E-8d,
+ +5.006436936098099E-8d,
+ +2.1367325342138113E-7d,
+ -2.6949659655907758E-8d,
+ -1.9256682968755803E-7d,
+ +1.960616287777496E-7d,
+ +1.876664741413704E-7d,
+ -2.1534486893602122E-7d,
+ -5.688830723853217E-8d,
+ +1.8861113228746644E-7d,
+ +4.6730779443102234E-8d,
+ -3.275360514112964E-9d,
+ +4.1011920825226876E-8d,
+ +1.820141955326842E-7d,
+ -5.468175655175594E-8d,
+ -1.8981247089866317E-7d,
+ -2.209492705846306E-7d,
+ -1.4566110577298295E-7d,
+ +3.848544860465368E-8d,
+ -1.429109630340783E-7d,
+ -2.105749999899302E-7d,
+ -1.6206609756618993E-7d,
+ +5.058693461947143E-9d,
+ -1.8359244902596882E-7d,
+ +2.2810251664891242E-7d,
+ -1.8791776732592608E-7d,
+ +1.3106843166204263E-9d,
+ -1.5543153797220025E-7d,
+ -1.7884997059081524E-7d,
+ -6.648490725635754E-8d,
+ +1.8412576154421806E-7d,
+ +9.860939269906055E-8d,
+ +1.5627006743114285E-7d,
+ -1.17260039161597E-7d,
+ +2.3416513526430908E-7d,
+ -2.1749172296989992E-7d,
+ -3.9242560971295217E-8d,
+ -1.822826971477839E-7d,
+ -1.6729355321895212E-7d,
+ +8.208715337901827E-9d,
+ -1.301267783434537E-7d,
+ -1.029741755377153E-7d,
+ +9.215765583599035E-8d,
+ -1.907487641016455E-8d,
+ +4.2661388254716074E-8d,
+ -1.9697226735187428E-7d,
+ +2.1819935527247946E-7d,
+ -1.398318929248588E-7d,
+ +1.6195123407015624E-7d,
+ +1.723826394935661E-7d,
+ -1.0602700638269148E-7d,
+ -1.9392742205954563E-7d,
+ -8.880302882034106E-8d,
+ +2.1186420987133E-7d,
+ +2.3375763256988976E-7d,
+ -2.0599801342241997E-8d,
+ -7.184550924856607E-8d,
+ +8.254840070367875E-8d,
+ };
+
+ /** Extended precision logarithm table over the range 1 - 2 in increments of 2^-10. */
+ private static final double[][] LN_MANT =
+ new double[][] {
+ {
+ +0.0d, +0.0d,
+ }, // 0
+ {
+ +9.760860120877624E-4d, -3.903230345984362E-11d,
+ }, // 1
+ {
+ +0.0019512202125042677d, -8.124251825289188E-11d,
+ }, // 2
+ {
+ +0.0029254043474793434d, -1.8374207360194882E-11d,
+ }, // 3
+ {
+ +0.0038986406289041042d, -2.1324678121885073E-10d,
+ }, // 4
+ {
+ +0.004870930686593056d, -4.5199654318611534E-10d,
+ }, // 5
+ {
+ +0.00584227591753006d, -2.933016992001806E-10d,
+ }, // 6
+ {
+ +0.006812678650021553d, -2.325147219074669E-10d,
+ }, // 7
+ {
+ +0.007782140746712685d, -3.046577356838847E-10d,
+ }, // 8
+ {
+ +0.008750664070248604d, -5.500631513861575E-10d,
+ }, // 9
+ {
+ +0.00971824862062931d, +8.48292035519895E-10d,
+ }, // 10
+ {
+ +0.010684899985790253d, +1.1422610134013436E-10d,
+ }, // 11
+ {
+ +0.01165061630308628d, +9.168889933128375E-10d,
+ }, // 12
+ {
+ +0.012615403160452843d, -5.303786078838E-10d,
+ }, // 13
+ {
+ +0.013579258695244789d, -5.688639355498786E-10d,
+ }, // 14
+ {
+ +0.01454218477010727d, +7.296670293275653E-10d,
+ }, // 15
+ {
+ +0.015504186972975731d, -4.370104767451421E-10d,
+ }, // 16
+ {
+ +0.016465261578559875d, +1.43695591408832E-9d,
+ }, // 17
+ {
+ +0.01742541790008545d, -1.1862263158849434E-9d,
+ }, // 18
+ {
+ +0.018384650349617004d, -9.482976524690715E-10d,
+ }, // 19
+ {
+ +0.01934296265244484d, +1.9068609515836638E-10d,
+ }, // 20
+ {
+ +0.020300358533859253d, +2.655990315697216E-10d,
+ }, // 21
+ {
+ +0.021256837993860245d, +1.0315548713040775E-9d,
+ }, // 22
+ {
+ +0.022212404757738113d, +5.13345647019085E-10d,
+ }, // 23
+ {
+ +0.02316705882549286d, +4.5604151934208014E-10d,
+ }, // 24
+ {
+ +0.02412080392241478d, -1.1255706987475148E-9d,
+ }, // 25
+ {
+ +0.025073636323213577d, +1.2289023836765196E-9d,
+ }, // 26
+ {
+ +0.02602556347846985d, +1.7990281828096504E-9d,
+ }, // 27
+ {
+ +0.026976589113473892d, -1.4152718164638451E-9d,
+ }, // 28
+ {
+ +0.02792670577764511d, +7.568772963781632E-10d,
+ }, // 29
+ {
+ +0.0288759246468544d, -1.1449998592111558E-9d,
+ }, // 30
+ {
+ +0.029824241995811462d, -1.6850976862319495E-9d,
+ }, // 31
+ {
+ +0.030771657824516296d, +8.422373919843096E-10d,
+ }, // 32
+ {
+ +0.0317181795835495d, +6.872350402175489E-10d,
+ }, // 33
+ {
+ +0.03266380727291107d, -4.541194749189272E-10d,
+ }, // 34
+ {
+ +0.03360854089260101d, -8.9064764856495E-10d,
+ }, // 35
+ {
+ +0.034552380442619324d, +1.0640404096769032E-9d,
+ }, // 36
+ {
+ +0.0354953333735466d, -3.5901655945224663E-10d,
+ }, // 37
+ {
+ +0.03643739968538284d, -3.4829517943661266E-9d,
+ }, // 38
+ {
+ +0.037378571927547455d, +8.149473794244232E-10d,
+ }, // 39
+ {
+ +0.03831886500120163d, -6.990650304449166E-10d,
+ }, // 40
+ {
+ +0.03925827145576477d, +1.0883076226453258E-9d,
+ }, // 41
+ {
+ +0.040196798741817474d, +3.845192807999274E-10d,
+ }, // 42
+ {
+ +0.04113444685935974d, -1.1570594692045927E-9d,
+ }, // 43
+ {
+ +0.04207121580839157d, -1.8877045166697178E-9d,
+ }, // 44
+ {
+ +0.043007105588912964d, -1.6332083257987747E-10d,
+ }, // 45
+ {
+ +0.04394212365150452d, -1.7950057534514933E-9d,
+ }, // 46
+ {
+ +0.04487626254558563d, +2.302710041648838E-9d,
+ }, // 47
+ {
+ +0.045809537172317505d, -1.1410233017161343E-9d,
+ }, // 48
+ {
+ +0.04674194008111954d, -3.0498741599744685E-9d,
+ }, // 49
+ {
+ +0.04767347127199173d, -1.8026348269183678E-9d,
+ }, // 50
+ {
+ +0.04860413819551468d, -3.233204600453039E-9d,
+ }, // 51
+ {
+ +0.04953393340110779d, +1.7211688427961583E-9d,
+ }, // 52
+ {
+ +0.05046287178993225d, -2.329967807055457E-10d,
+ }, // 53
+ {
+ +0.05139094591140747d, -4.191810118556531E-11d,
+ }, // 54
+ {
+ +0.052318163216114044d, -3.5574324788328143E-9d,
+ }, // 55
+ {
+ +0.053244516253471375d, -1.7346590916458485E-9d,
+ }, // 56
+ {
+ +0.05417001247406006d, -4.343048751383674E-10d,
+ }, // 57
+ {
+ +0.055094651877880096d, +1.92909364037955E-9d,
+ }, // 58
+ {
+ +0.056018441915512085d, -5.139745677199588E-10d,
+ }, // 59
+ {
+ +0.05694137513637543d, +1.2637629975129189E-9d,
+ }, // 60
+ {
+ +0.05786345899105072d, +1.3840561112481119E-9d,
+ }, // 61
+ {
+ +0.058784693479537964d, +1.414889689612056E-9d,
+ }, // 62
+ {
+ +0.05970507860183716d, +2.9199191907666474E-9d,
+ }, // 63
+ {
+ +0.0606246218085289d, +7.90594243412116E-12d,
+ }, // 64
+ {
+ +0.06154331564903259d, +1.6844747839686189E-9d,
+ }, // 65
+ {
+ +0.06246116757392883d, +2.0498074572151747E-9d,
+ }, // 66
+ {
+ +0.06337818503379822d, -4.800180493433863E-9d,
+ }, // 67
+ {
+ +0.06429435312747955d, -2.4220822960064277E-9d,
+ }, // 68
+ {
+ +0.06520968675613403d, -4.179048566709334E-9d,
+ }, // 69
+ {
+ +0.06612417101860046d, +6.363872957010456E-9d,
+ }, // 70
+ {
+ +0.06703783571720123d, +9.339468680056365E-10d,
+ }, // 71
+ {
+ +0.06795066595077515d, -4.04226739708981E-9d,
+ }, // 72
+ {
+ +0.0688626617193222d, -7.043545052852817E-9d,
+ }, // 73
+ {
+ +0.06977382302284241d, -6.552819560439773E-9d,
+ }, // 74
+ {
+ +0.07068414986133575d, -1.0571674860370546E-9d,
+ }, // 75
+ {
+ +0.07159365713596344d, -3.948954622015801E-9d,
+ }, // 76
+ {
+ +0.07250232994556427d, +1.1776625988228244E-9d,
+ }, // 77
+ {
+ +0.07341018319129944d, +9.221072639606492E-10d,
+ }, // 78
+ {
+ +0.07431721687316895d, -3.219119568928366E-9d,
+ }, // 79
+ {
+ +0.0752234160900116d, +5.147575929018918E-9d,
+ }, // 80
+ {
+ +0.07612881064414978d, -2.291749683541979E-9d,
+ }, // 81
+ {
+ +0.07703337073326111d, +5.749565906124772E-9d,
+ }, // 82
+ {
+ +0.07793712615966797d, +9.495158151301779E-10d,
+ }, // 83
+ {
+ +0.07884006202220917d, -3.144331429489291E-10d,
+ }, // 84
+ {
+ +0.0797421783208847d, +3.430029236134205E-9d,
+ }, // 85
+ {
+ +0.08064348995685577d, -1.2499290483167703E-9d,
+ }, // 86
+ {
+ +0.08154398202896118d, +2.011215719133196E-9d,
+ }, // 87
+ {
+ +0.08244366943836212d, -2.2728753031387152E-10d,
+ }, // 88
+ {
+ +0.0833425521850586d, -6.508966857277253E-9d,
+ }, // 89
+ {
+ +0.0842406153678894d, -4.801131671405377E-10d,
+ }, // 90
+ {
+ +0.08513787388801575d, +4.406750291994231E-9d,
+ }, // 91
+ {
+ +0.08603434264659882d, -5.304795662536171E-9d,
+ }, // 92
+ {
+ +0.08692999184131622d, +1.6284313912612293E-9d,
+ }, // 93
+ {
+ +0.08782485127449036d, -3.158898981674071E-9d,
+ }, // 94
+ {
+ +0.08871890604496002d, -3.3324878834139977E-9d,
+ }, // 95
+ {
+ +0.08961215615272522d, +2.536961912893389E-9d,
+ }, // 96
+ {
+ +0.09050461649894714d, +9.737596728980696E-10d,
+ }, // 97
+ {
+ +0.0913962870836258d, -6.600437262505396E-9d,
+ }, // 98
+ {
+ +0.09228715300559998d, -3.866609889222889E-9d,
+ }, // 99
+ {
+ +0.09317722916603088d, -4.311847594020281E-9d,
+ }, // 100
+ {
+ +0.09406651556491852d, -6.525851105645959E-9d,
+ }, // 101
+ {
+ +0.09495499730110168d, +5.799080912675435E-9d,
+ }, // 102
+ {
+ +0.09584270417690277d, +4.2634204358490415E-9d,
+ }, // 103
+ {
+ +0.09672962129116058d, +5.167390528799477E-9d,
+ }, // 104
+ {
+ +0.09761576354503632d, -4.994827392841906E-9d,
+ }, // 105
+ {
+ +0.09850110113620758d, +4.970725577861395E-9d,
+ }, // 106
+ {
+ +0.09938566386699677d, +6.6496705953229645E-9d,
+ }, // 107
+ {
+ +0.10026945173740387d, +1.4262712796792241E-9d,
+ }, // 108
+ {
+ +0.1011524498462677d, +5.5822855204629114E-9d,
+ }, // 109
+ {
+ +0.10203467309474945d, +5.593494835247651E-9d,
+ }, // 110
+ {
+ +0.10291612148284912d, +2.8332008343480686E-9d,
+ }, // 111
+ {
+ +0.10379679501056671d, -1.3289231465997192E-9d,
+ }, // 112
+ {
+ +0.10467669367790222d, -5.526819276639527E-9d,
+ }, // 113
+ {
+ +0.10555580258369446d, +6.503128678219282E-9d,
+ }, // 114
+ {
+ +0.10643415153026581d, +6.317463237641817E-9d,
+ }, // 115
+ {
+ +0.10731174051761627d, -4.728528221305482E-9d,
+ }, // 116
+ {
+ +0.10818853974342346d, +4.519199083083901E-9d,
+ }, // 117
+ {
+ +0.10906457901000977d, +5.606492666349878E-9d,
+ }, // 118
+ {
+ +0.10993985831737518d, -1.220176214398581E-10d,
+ }, // 119
+ {
+ +0.11081436276435852d, +3.5759315936869937E-9d,
+ }, // 120
+ {
+ +0.11168810725212097d, +3.1367659571899855E-9d,
+ }, // 121
+ {
+ +0.11256109178066254d, -1.0543075713098835E-10d,
+ }, // 122
+ {
+ +0.11343331634998322d, -4.820065619207094E-9d,
+ }, // 123
+ {
+ +0.11430476605892181d, +5.221136819669415E-9d,
+ }, // 124
+ {
+ +0.11517547070980072d, +1.5395018670011342E-9d,
+ }, // 125
+ {
+ +0.11604541540145874d, +3.5638391501880846E-10d,
+ }, // 126
+ {
+ +0.11691460013389587d, +2.9885336757136527E-9d,
+ }, // 127
+ {
+ +0.11778303980827332d, -4.151889860890893E-9d,
+ }, // 128
+ {
+ +0.11865071952342987d, -4.853823938804204E-9d,
+ }, // 129
+ {
+ +0.11951763927936554d, +2.189226237170704E-9d,
+ }, // 130
+ {
+ +0.12038381397724152d, +3.3791993048776982E-9d,
+ }, // 131
+ {
+ +0.1212492436170578d, +1.5811884868243975E-11d,
+ }, // 132
+ {
+ +0.12211392819881439d, -6.6045909118908625E-9d,
+ }, // 133
+ {
+ +0.1229778528213501d, -2.8786263916116364E-10d,
+ }, // 134
+ {
+ +0.12384103238582611d, +5.354472503748251E-9d,
+ }, // 135
+ {
+ +0.12470348179340363d, -3.2924463896248744E-9d,
+ }, // 136
+ {
+ +0.12556517124176025d, +4.856678149580005E-9d,
+ }, // 137
+ {
+ +0.12642613053321838d, +1.2791850600366742E-9d,
+ }, // 138
+ {
+ +0.12728634476661682d, +2.1525945093362843E-9d,
+ }, // 139
+ {
+ +0.12814581394195557d, +8.749974471767862E-9d,
+ }, // 140
+ {
+ +0.129004567861557d, -7.461209161105275E-9d,
+ }, // 141
+ {
+ +0.12986254692077637d, +1.4390208226263824E-8d,
+ }, // 142
+ {
+ +0.1307198405265808d, -1.3839477920475328E-8d,
+ }, // 143
+ {
+ +0.13157635927200317d, -1.483283901239408E-9d,
+ }, // 144
+ {
+ +0.13243216276168823d, -6.889072914229094E-9d,
+ }, // 145
+ {
+ +0.1332872211933136d, +9.990351100568362E-10d,
+ }, // 146
+ {
+ +0.13414156436920166d, -6.370937412495338E-9d,
+ }, // 147
+ {
+ +0.13499516248703003d, +2.05047480130511E-9d,
+ }, // 148
+ {
+ +0.1358480453491211d, -2.29509872547079E-9d,
+ }, // 149
+ {
+ +0.13670018315315247d, +1.16354361977249E-8d,
+ }, // 150
+ {
+ +0.13755163550376892d, -1.452496267904829E-8d,
+ }, // 151
+ {
+ +0.1384023129940033d, +9.865115839786888E-9d,
+ }, // 152
+ {
+ +0.13925230503082275d, -3.369999130712228E-9d,
+ }, // 153
+ {
+ +0.14010155200958252d, +6.602496401651853E-9d,
+ }, // 154
+ {
+ +0.14095008373260498d, +1.1205312852298845E-8d,
+ }, // 155
+ {
+ +0.14179790019989014d, +1.1660367213160203E-8d,
+ }, // 156
+ {
+ +0.142645001411438d, +9.186471222585239E-9d,
+ }, // 157
+ {
+ +0.14349138736724854d, +4.999341878263704E-9d,
+ }, // 158
+ {
+ +0.14433705806732178d, +3.11611905696257E-10d,
+ }, // 159
+ {
+ +0.14518201351165771d, -3.6671598175618173E-9d,
+ }, // 160
+ {
+ +0.14602625370025635d, -5.730477881659618E-9d,
+ }, // 161
+ {
+ +0.14686977863311768d, -4.674900007989718E-9d,
+ }, // 162
+ {
+ +0.1477125883102417d, +6.999732437141968E-10d,
+ }, // 163
+ {
+ +0.14855468273162842d, +1.159150872494107E-8d,
+ }, // 164
+ {
+ +0.14939609169960022d, -6.082714828488485E-10d,
+ }, // 165
+ {
+ +0.15023678541183472d, -4.905712741596318E-9d,
+ }, // 166
+ {
+ +0.1510767638683319d, -1.124848988733307E-10d,
+ }, // 167
+ {
+ +0.15191605687141418d, -1.484557220949851E-8d,
+ }, // 168
+ {
+ +0.15275460481643677d, +1.1682026251371384E-8d,
+ }, // 169
+ {
+ +0.15359249711036682d, -8.757272519238786E-9d,
+ }, // 170
+ {
+ +0.15442964434623718d, +1.4419920764774415E-8d,
+ }, // 171
+ {
+ +0.15526613593101501d, -7.019891063126053E-9d,
+ }, // 172
+ {
+ +0.15610191226005554d, -1.230153548825964E-8d,
+ }, // 173
+ {
+ +0.15693697333335876d, -2.574172005933276E-10d,
+ }, // 174
+ {
+ +0.15777134895324707d, +4.748140799544371E-10d,
+ }, // 175
+ {
+ +0.15860503911972046d, -8.943081874891003E-9d,
+ }, // 176
+ {
+ +0.15943801403045654d, +2.4500739038517657E-9d,
+ }, // 177
+ {
+ +0.1602703034877777d, +6.007922084557054E-9d,
+ }, // 178
+ {
+ +0.16110190749168396d, +2.8835418231126645E-9d,
+ }, // 179
+ {
+ +0.1619328260421753d, -5.772862039728412E-9d,
+ }, // 180
+ {
+ +0.16276302933692932d, +1.0988372954605789E-8d,
+ }, // 181
+ {
+ +0.16359257698059082d, -5.292913162607026E-9d,
+ }, // 182
+ {
+ +0.16442140936851501d, +6.12956339275823E-9d,
+ }, // 183
+ {
+ +0.16524958610534668d, -1.3210039516811888E-8d,
+ }, // 184
+ {
+ +0.16607704758644104d, -2.5711014608334873E-9d,
+ }, // 185
+ {
+ +0.16690382361412048d, +9.37721319457112E-9d,
+ }, // 186
+ {
+ +0.1677299439907074d, -6.0370682395944045E-9d,
+ }, // 187
+ {
+ +0.168555349111557d, +1.1918249660105651E-8d,
+ }, // 188
+ {
+ +0.1693800985813141d, +4.763282949656017E-9d,
+ }, // 189
+ {
+ +0.17020416259765625d, +3.4223342273948817E-9d,
+ }, // 190
+ {
+ +0.1710275411605835d, +9.014612241310916E-9d,
+ }, // 191
+ {
+ +0.1718502640724182d, -7.145758990550526E-9d,
+ }, // 192
+ {
+ +0.172672301530838d, -1.4142763934081504E-8d,
+ }, // 193
+ {
+ +0.1734936535358429d, -1.0865453656579032E-8d,
+ }, // 194
+ {
+ +0.17431432008743286d, +3.794385569450774E-9d,
+ }, // 195
+ {
+ +0.1751343309879303d, +1.1399188501627291E-9d,
+ }, // 196
+ {
+ +0.17595365643501282d, +1.2076238768270153E-8d,
+ }, // 197
+ {
+ +0.1767723262310028d, +7.901084730502162E-9d,
+ }, // 198
+ {
+ +0.17759034037590027d, -1.0288181007465474E-8d,
+ }, // 199
+ {
+ +0.1784076690673828d, -1.15945645153806E-8d,
+ }, // 200
+ {
+ +0.17922431230545044d, +5.073923825786778E-9d,
+ }, // 201
+ {
+ +0.18004029989242554d, +1.1004278077575267E-8d,
+ }, // 202
+ {
+ +0.1808556318283081d, +7.2831502374676964E-9d,
+ }, // 203
+ {
+ +0.18167030811309814d, -5.0054634662706464E-9d,
+ }, // 204
+ {
+ +0.18248429894447327d, +5.022108460298934E-9d,
+ }, // 205
+ {
+ +0.18329763412475586d, +8.642254225732676E-9d,
+ }, // 206
+ {
+ +0.18411031365394592d, +6.931054493326395E-9d,
+ }, // 207
+ {
+ +0.18492233753204346d, +9.619685356326533E-10d,
+ }, // 208
+ {
+ +0.18573370575904846d, -8.194157257980706E-9d,
+ }, // 209
+ {
+ +0.18654438853263855d, +1.0333241479437797E-8d,
+ }, // 210
+ {
+ +0.1873544454574585d, -1.9948340196027965E-9d,
+ }, // 211
+ {
+ +0.1881638467311859d, -1.4313002926259948E-8d,
+ }, // 212
+ {
+ +0.1889725625514984d, +4.241536392174967E-9d,
+ }, // 213
+ {
+ +0.18978065252304077d, -4.877952454011428E-9d,
+ }, // 214
+ {
+ +0.1905880868434906d, -1.0813801247641613E-8d,
+ }, // 215
+ {
+ +0.1913948655128479d, -1.2513218445781325E-8d,
+ }, // 216
+ {
+ +0.19220098853111267d, -8.925958555729115E-9d,
+ }, // 217
+ {
+ +0.1930064558982849d, +9.956860681280245E-10d,
+ }, // 218
+ {
+ +0.193811297416687d, -1.1505428993246996E-8d,
+ }, // 219
+ {
+ +0.1946154534816742d, +1.4217997464522202E-8d,
+ }, // 220
+ {
+ +0.19541901350021362d, -1.0200858727747717E-8d,
+ }, // 221
+ {
+ +0.19622188806533813d, +5.682607223902455E-9d,
+ }, // 222
+ {
+ +0.1970241367816925d, +3.2988908516009827E-9d,
+ }, // 223
+ {
+ +0.19782572984695435d, +1.3482965534659446E-8d,
+ }, // 224
+ {
+ +0.19862669706344604d, +7.462678536479685E-9d,
+ }, // 225
+ {
+ +0.1994270384311676d, -1.3734273888891115E-8d,
+ }, // 226
+ {
+ +0.20022669434547424d, +1.0521983802642893E-8d,
+ }, // 227
+ {
+ +0.20102575421333313d, -8.152742388541905E-9d,
+ }, // 228
+ {
+ +0.2018241584300995d, -9.133484280193855E-9d,
+ }, // 229
+ {
+ +0.20262190699577332d, +8.59763959528144E-9d,
+ }, // 230
+ {
+ +0.2034190595149994d, -1.3548568223001414E-8d,
+ }, // 231
+ {
+ +0.20421552658081055d, +1.4847880344628818E-8d,
+ }, // 232
+ {
+ +0.20501139760017395d, +5.390620378060543E-9d,
+ }, // 233
+ {
+ +0.2058066427707672d, -1.1109834472051523E-8d,
+ }, // 234
+ {
+ +0.20660123229026794d, -3.845373872038116E-9d,
+ }, // 235
+ {
+ +0.20739519596099854d, -1.6149279479975042E-9d,
+ }, // 236
+ {
+ +0.20818853378295898d, -3.4174925203771133E-9d,
+ }, // 237
+ {
+ +0.2089812457561493d, -8.254443919468538E-9d,
+ }, // 238
+ {
+ +0.20977330207824707d, +1.4672790944499144E-8d,
+ }, // 239
+ {
+ +0.2105647623538971d, +6.753452542942992E-9d,
+ }, // 240
+ {
+ +0.21135559678077698d, -1.218609462241927E-9d,
+ }, // 241
+ {
+ +0.21214580535888672d, -8.254218316367887E-9d,
+ }, // 242
+ {
+ +0.21293538808822632d, -1.3366540360587255E-8d,
+ }, // 243
+ {
+ +0.2137243151664734d, +1.4231244750190031E-8d,
+ }, // 244
+ {
+ +0.2145126760005951d, -1.3885660525939072E-8d,
+ }, // 245
+ {
+ +0.21530038118362427d, -7.3304404046850136E-9d,
+ }, // 246
+ {
+ +0.2160874605178833d, +5.072117654842356E-9d,
+ }, // 247
+ {
+ +0.21687394380569458d, -5.505080220459036E-9d,
+ }, // 248
+ {
+ +0.21765980124473572d, -8.286782292266659E-9d,
+ }, // 249
+ {
+ +0.2184450328350067d, -2.302351152358085E-9d,
+ }, // 250
+ {
+ +0.21922963857650757d, +1.3416565858314603E-8d,
+ }, // 251
+ {
+ +0.22001364827156067d, +1.0033721426962048E-8d,
+ }, // 252
+ {
+ +0.22079706192016602d, -1.1487079818684332E-8d,
+ }, // 253
+ {
+ +0.22157981991767883d, +9.420348186357043E-9d,
+ }, // 254
+ {
+ +0.2223619818687439d, +1.4110645699377834E-8d,
+ }, // 255
+ {
+ +0.2231435477733612d, +3.5408485497116107E-9d,
+ }, // 256
+ {
+ +0.22392448782920837d, +8.468072777056227E-9d,
+ }, // 257
+ {
+ +0.2247048318386078d, +4.255446699237779E-11d,
+ }, // 258
+ {
+ +0.22548454999923706d, +9.016946273084244E-9d,
+ }, // 259
+ {
+ +0.22626367211341858d, +6.537034810260226E-9d,
+ }, // 260
+ {
+ +0.22704219818115234d, -6.451285264969768E-9d,
+ }, // 261
+ {
+ +0.22782009840011597d, +7.979956357126066E-10d,
+ }, // 262
+ {
+ +0.22859740257263184d, -5.759582672039005E-10d,
+ }, // 263
+ {
+ +0.22937411069869995d, -9.633854121180397E-9d,
+ }, // 264
+ {
+ +0.23015019297599792d, +4.363736368635843E-9d,
+ }, // 265
+ {
+ +0.23092567920684814d, +1.2549416560182509E-8d,
+ }, // 266
+ {
+ +0.231700599193573d, -1.3946383592553814E-8d,
+ }, // 267
+ {
+ +0.2324748933315277d, -1.458843364504023E-8d,
+ }, // 268
+ {
+ +0.23324856162071228d, +1.1551692104697154E-8d,
+ }, // 269
+ {
+ +0.23402166366577148d, +5.795621295524984E-9d,
+ }, // 270
+ {
+ +0.23479416966438293d, -1.1301979046684263E-9d,
+ }, // 271
+ {
+ +0.23556607961654663d, -8.303779721781787E-9d,
+ }, // 272
+ {
+ +0.23633739352226257d, -1.4805271785394075E-8d,
+ }, // 273
+ {
+ +0.23710808157920837d, +1.0085373835899469E-8d,
+ }, // 274
+ {
+ +0.2378782033920288d, +7.679117635349454E-9d,
+ }, // 275
+ {
+ +0.2386477291584015d, +8.69177352065934E-9d,
+ }, // 276
+ {
+ +0.23941665887832642d, +1.4034725764547136E-8d,
+ }, // 277
+ {
+ +0.24018502235412598d, -5.185064518887831E-9d,
+ }, // 278
+ {
+ +0.2409527599811554d, +1.1544236628121676E-8d,
+ }, // 279
+ {
+ +0.24171993136405945d, +5.523085719902123E-9d,
+ }, // 280
+ {
+ +0.24248650670051575d, +7.456824943331887E-9d,
+ }, // 281
+ {
+ +0.24325251579284668d, -1.1555923403029638E-8d,
+ }, // 282
+ {
+ +0.24401789903640747d, +8.988361382732908E-9d,
+ }, // 283
+ {
+ +0.2447827160358429d, +1.0381848020926893E-8d,
+ }, // 284
+ {
+ +0.24554696679115295d, -6.480706118857055E-9d,
+ }, // 285
+ {
+ +0.24631062150001526d, -1.0904271124793968E-8d,
+ }, // 286
+ {
+ +0.2470736801624298d, -1.998183061531611E-9d,
+ }, // 287
+ {
+ +0.247836172580719d, -8.676137737360023E-9d,
+ }, // 288
+ {
+ +0.24859806895256042d, -2.4921733203932487E-10d,
+ }, // 289
+ {
+ +0.2493593990802765d, -5.635173762130303E-9d,
+ }, // 290
+ {
+ +0.2501201629638672d, -2.3951455355985637E-8d,
+ }, // 291
+ {
+ +0.25088030099868774d, +5.287121672447825E-9d,
+ }, // 292
+ {
+ +0.2516399025917053d, -6.447877375049486E-9d,
+ }, // 293
+ {
+ +0.25239890813827515d, +1.32472428796441E-9d,
+ }, // 294
+ {
+ +0.2531573176383972d, +2.9479464287605006E-8d,
+ }, // 295
+ {
+ +0.2539151906967163d, +1.9284247135543574E-8d,
+ }, // 296
+ {
+ +0.2546725273132324d, -2.8390360197221716E-8d,
+ }, // 297
+ {
+ +0.255429208278656d, +6.533522495226226E-9d,
+ }, // 298
+ {
+ +0.2561853528022766d, +5.713225978895991E-9d,
+ }, // 299
+ {
+ +0.25694090127944946d, +2.9618050962556135E-8d,
+ }, // 300
+ {
+ +0.25769591331481934d, +1.950605015323617E-8d,
+ }, // 301
+ {
+ +0.25845038890838623d, -2.3762031507525576E-8d,
+ }, // 302
+ {
+ +0.2592042088508606d, +1.98818938195077E-8d,
+ }, // 303
+ {
+ +0.25995755195617676d, -2.751925069084042E-8d,
+ }, // 304
+ {
+ +0.2607102394104004d, +1.3703391844683932E-8d,
+ }, // 305
+ {
+ +0.26146239042282104d, +2.5193525310038174E-8d,
+ }, // 306
+ {
+ +0.2622140049934387d, +7.802219817310385E-9d,
+ }, // 307
+ {
+ +0.26296502351760864d, +2.1983272709242607E-8d,
+ }, // 308
+ {
+ +0.2637155055999756d, +8.979279989292184E-9d,
+ }, // 309
+ {
+ +0.2644653916358948d, +2.9240221157844312E-8d,
+ }, // 310
+ {
+ +0.265214741230011d, +2.4004885823813374E-8d,
+ }, // 311
+ {
+ +0.2659635543823242d, -5.885186277410878E-9d,
+ }, // 312
+ {
+ +0.2667117714881897d, +1.4300386517357162E-11d,
+ }, // 313
+ {
+ +0.2674594521522522d, -1.7063531531989365E-8d,
+ }, // 314
+ {
+ +0.26820653676986694d, +3.3218524692903896E-9d,
+ }, // 315
+ {
+ +0.2689530849456787d, +2.3998252479954764E-9d,
+ }, // 316
+ {
+ +0.2696990966796875d, -1.8997462070389404E-8d,
+ }, // 317
+ {
+ +0.27044451236724854d, -4.350745270980051E-10d,
+ }, // 318
+ {
+ +0.2711893916130066d, -6.892221115467135E-10d,
+ }, // 319
+ {
+ +0.27193373441696167d, -1.89333199110902E-8d,
+ }, // 320
+ {
+ +0.272677481174469d, +5.262017392507765E-9d,
+ }, // 321
+ {
+ +0.27342069149017334d, +1.3115046679980076E-8d,
+ }, // 322
+ {
+ +0.2741633653640747d, +5.4468361834451975E-9d,
+ }, // 323
+ {
+ +0.2749055027961731d, -1.692337384653611E-8d,
+ }, // 324
+ {
+ +0.27564704418182373d, +6.426479056697412E-9d,
+ }, // 325
+ {
+ +0.2763880491256714d, +1.670735065191342E-8d,
+ }, // 326
+ {
+ +0.27712851762771606d, +1.4733029698334834E-8d,
+ }, // 327
+ {
+ +0.27786844968795776d, +1.315498542514467E-9d,
+ }, // 328
+ {
+ +0.2786078453063965d, -2.2735061539223372E-8d,
+ }, // 329
+ {
+ +0.27934664487838745d, +2.994379757313727E-9d,
+ }, // 330
+ {
+ +0.28008490800857544d, +1.970577274107218E-8d,
+ }, // 331
+ {
+ +0.28082263469696045d, +2.820392733542077E-8d,
+ }, // 332
+ {
+ +0.2815598249435425d, +2.929187356678173E-8d,
+ }, // 333
+ {
+ +0.28229647874832153d, +2.377086680926386E-8d,
+ }, // 334
+ {
+ +0.2830325961112976d, +1.2440393009992529E-8d,
+ }, // 335
+ {
+ +0.2837681770324707d, -3.901826104778096E-9d,
+ }, // 336
+ {
+ +0.2845032215118408d, -2.4459827842685974E-8d,
+ }, // 337
+ {
+ +0.2852376699447632d, +1.1165241398059789E-8d,
+ }, // 338
+ {
+ +0.28597164154052734d, -1.54434478239181E-8d,
+ }, // 339
+ {
+ +0.28670501708984375d, +1.5714110564653245E-8d,
+ }, // 340
+ {
+ +0.28743791580200195d, -1.3782394940142479E-8d,
+ }, // 341
+ {
+ +0.2881702184677124d, +1.6063569876284005E-8d,
+ }, // 342
+ {
+ +0.28890204429626465d, -1.317176818216125E-8d,
+ }, // 343
+ {
+ +0.28963327407836914d, +1.8504673536253893E-8d,
+ }, // 344
+ {
+ +0.29036402702331543d, -7.334319635123628E-9d,
+ }, // 345
+ {
+ +0.29109418392181396d, +2.9300903540317107E-8d,
+ }, // 346
+ {
+ +0.2918238639831543d, +9.979706999541057E-9d,
+ }, // 347
+ {
+ +0.29255300760269165d, -4.916314210412424E-9d,
+ }, // 348
+ {
+ +0.293281614780426d, -1.4611908070155308E-8d,
+ }, // 349
+ {
+ +0.2940096855163574d, -1.833351586679361E-8d,
+ }, // 350
+ {
+ +0.29473721981048584d, -1.530926726615185E-8d,
+ }, // 351
+ {
+ +0.2954642176628113d, -4.7689754029101934E-9d,
+ }, // 352
+ {
+ +0.29619067907333374d, +1.4055868011423819E-8d,
+ }, // 353
+ {
+ +0.296916663646698d, -1.7672547212604003E-8d,
+ }, // 354
+ {
+ +0.2976420521736145d, +2.0020234215759705E-8d,
+ }, // 355
+ {
+ +0.2983669638633728d, +8.688424478730524E-9d,
+ }, // 356
+ {
+ +0.2990913391113281d, +8.69851089918337E-9d,
+ }, // 357
+ {
+ +0.29981517791748047d, +2.0810681643102672E-8d,
+ }, // 358
+ {
+ +0.3005385398864746d, -1.3821169493779352E-8d,
+ }, // 359
+ {
+ +0.301261305809021d, +2.4769140784919128E-8d,
+ }, // 360
+ {
+ +0.3019835948944092d, +1.8127576600610336E-8d,
+ }, // 361
+ {
+ +0.3027053475379944d, +2.6612401062437074E-8d,
+ }, // 362
+ {
+ +0.3034266233444214d, -8.629042891789934E-9d,
+ }, // 363
+ {
+ +0.3041473627090454d, -2.724174869314043E-8d,
+ }, // 364
+ {
+ +0.30486756563186646d, -2.8476975783775358E-8d,
+ }, // 365
+ {
+ +0.3055872321128845d, -1.1587600174449919E-8d,
+ }, // 366
+ {
+ +0.3063063621520996d, +2.417189020581056E-8d,
+ }, // 367
+ {
+ +0.3070250153541565d, +1.99407553679345E-8d,
+ }, // 368
+ {
+ +0.3077431917190552d, -2.35387025694381E-8d,
+ }, // 369
+ {
+ +0.3084607720375061d, +1.3683509995845583E-8d,
+ }, // 370
+ {
+ +0.30917787551879883d, +1.3137214081023085E-8d,
+ }, // 371
+ {
+ +0.30989450216293335d, -2.444006866174775E-8d,
+ }, // 372
+ {
+ +0.3106105327606201d, +2.0896888605749563E-8d,
+ }, // 373
+ {
+ +0.31132614612579346d, -2.893149098508887E-8d,
+ }, // 374
+ {
+ +0.31204116344451904d, +5.621509038251498E-9d,
+ }, // 375
+ {
+ +0.3127557039260864d, +6.0778104626050015E-9d,
+ }, // 376
+ {
+ +0.3134697675704956d, -2.6832941696716294E-8d,
+ }, // 377
+ {
+ +0.31418323516845703d, +2.6826625274495256E-8d,
+ }, // 378
+ {
+ +0.31489628553390503d, -1.1030897183911054E-8d,
+ }, // 379
+ {
+ +0.31560879945755005d, -2.047124671392676E-8d,
+ }, // 380
+ {
+ +0.3163207769393921d, -7.709990443086711E-10d,
+ }, // 381
+ {
+ +0.3170322775840759d, -1.0812918808112342E-8d,
+ }, // 382
+ {
+ +0.3177432417869568d, +9.727979174888975E-9d,
+ }, // 383
+ {
+ +0.31845372915267944d, +1.9658551724508715E-9d,
+ }, // 384
+ {
+ +0.3191636800765991d, +2.6222628001695826E-8d,
+ }, // 385
+ {
+ +0.3198731541633606d, +2.3609400272358744E-8d,
+ }, // 386
+ {
+ +0.32058215141296387d, -5.159602957634814E-9d,
+ }, // 387
+ {
+ +0.32129061222076416d, +2.329701319016099E-10d,
+ }, // 388
+ {
+ +0.32199859619140625d, -1.910633190395738E-8d,
+ }, // 389
+ {
+ +0.32270604372024536d, -2.863180390093667E-9d,
+ }, // 390
+ {
+ +0.32341301441192627d, -9.934041364456825E-9d,
+ }, // 391
+ {
+ +0.3241194486618042d, +1.999240777687192E-8d,
+ }, // 392
+ {
+ +0.3248254060745239d, +2.801670341647724E-8d,
+ }, // 393
+ {
+ +0.32553088665008545d, +1.4842534265191358E-8d,
+ }, // 394
+ {
+ +0.32623589038848877d, -1.882789920477354E-8d,
+ }, // 395
+ {
+ +0.3269403576850891d, -1.268923579073577E-8d,
+ }, // 396
+ {
+ +0.32764434814453125d, -2.564688370677835E-8d,
+ }, // 397
+ {
+ +0.3283478021621704d, +2.6015626820520968E-9d,
+ }, // 398
+ {
+ +0.32905077934265137d, +1.3147747907784344E-8d,
+ }, // 399
+ {
+ +0.3297532796859741d, +6.686493860720675E-9d,
+ }, // 400
+ {
+ +0.33045530319213867d, -1.608884086544153E-8d,
+ }, // 401
+ {
+ +0.33115679025650024d, +5.118287907840204E-9d,
+ }, // 402
+ {
+ +0.3318578004837036d, +1.139367970944884E-8d,
+ }, // 403
+ {
+ +0.3325583338737488d, +3.426327822115399E-9d,
+ }, // 404
+ {
+ +0.33325839042663574d, -1.809622142990733E-8d,
+ }, // 405
+ {
+ +0.3339579105377197d, +7.116780143398601E-9d,
+ }, // 406
+ {
+ +0.3346569538116455d, +2.0145352306345386E-8d,
+ }, // 407
+ {
+ +0.3353555202484131d, +2.167272474431968E-8d,
+ }, // 408
+ {
+ +0.33605360984802246d, +1.2380696294966822E-8d,
+ }, // 409
+ {
+ +0.33675122261047363d, -7.050361059209181E-9d,
+ }, // 410
+ {
+ +0.3374482989311218d, +2.366314656322868E-8d,
+ }, // 411
+ {
+ +0.3381449580192566d, -1.4010540194086646E-8d,
+ }, // 412
+ {
+ +0.3388410806655884d, -1.860165465666482E-10d,
+ }, // 413
+ {
+ +0.33953672647476196d, +6.206776940880773E-9d,
+ }, // 414
+ {
+ +0.34023189544677734d, +5.841137379010982E-9d,
+ }, // 415
+ {
+ +0.3409265875816345d, -6.11041311179286E-10d,
+ }, // 416
+ {
+ +0.3416208028793335d, -1.2479264502054702E-8d,
+ }, // 417
+ {
+ +0.34231454133987427d, -2.909443297645926E-8d,
+ }, // 418
+ {
+ +0.34300774335861206d, +9.815805717097634E-9d,
+ }, // 419
+ {
+ +0.3437005281448364d, -1.4291517981101049E-8d,
+ }, // 420
+ {
+ +0.3443927764892578d, +1.8457821628427503E-8d,
+ }, // 421
+ {
+ +0.34508460760116577d, -1.0481908869377813E-8d,
+ }, // 422
+ {
+ +0.34577590227127075d, +1.876076001514746E-8d,
+ }, // 423
+ {
+ +0.3464667797088623d, -1.2362653723769037E-8d,
+ }, // 424
+ {
+ +0.3471571207046509d, +1.6016578405624026E-8d,
+ }, // 425
+ {
+ +0.347847044467926d, -1.4652759033760925E-8d,
+ }, // 426
+ {
+ +0.3485364317893982d, +1.549533655901835E-8d,
+ }, // 427
+ {
+ +0.34922540187835693d, -1.2093068629412478E-8d,
+ }, // 428
+ {
+ +0.3499138355255127d, +2.244531711424792E-8d,
+ }, // 429
+ {
+ +0.35060185194015503d, +5.538565518604807E-10d,
+ }, // 430
+ {
+ +0.35128939151763916d, -1.7511499366215853E-8d,
+ }, // 431
+ {
+ +0.3519763946533203d, +2.850385787215544E-8d,
+ }, // 432
+ {
+ +0.35266298055648804d, +2.003926370146842E-8d,
+ }, // 433
+ {
+ +0.35334908962249756d, +1.734665280502264E-8d,
+ }, // 434
+ {
+ +0.3540347218513489d, +2.1071983674869414E-8d,
+ }, // 435
+ {
+ +0.35471993684768677d, -2.774475773922311E-8d,
+ }, // 436
+ {
+ +0.3554046154022217d, -9.250975291734664E-9d,
+ }, // 437
+ {
+ +0.3560888171195984d, +1.7590672330295415E-8d,
+ }, // 438
+ {
+ +0.35677260160446167d, -6.1837904549178745E-9d,
+ }, // 439
+ {
+ +0.35745590925216675d, -2.0330362973820856E-8d,
+ }, // 440
+ {
+ +0.3581387400627136d, -2.42109990366786E-8d,
+ }, // 441
+ {
+ +0.3588210940361023d, -1.7188958587407816E-8d,
+ }, // 442
+ {
+ +0.35950297117233276d, +1.3711958590112228E-9d,
+ }, // 443
+ {
+ +0.3601844310760498d, -2.7501042008405925E-8d,
+ }, // 444
+ {
+ +0.36086535453796387d, +1.6036460343275798E-8d,
+ }, // 445
+ {
+ +0.3615458607673645d, +1.3405964389498495E-8d,
+ }, // 446
+ {
+ +0.36222589015960693d, +2.484237749027735E-8d,
+ }, // 447
+ {
+ +0.36290550231933594d, -8.629967484362177E-9d,
+ }, // 448
+ {
+ +0.36358463764190674d, -2.6778729562324134E-8d,
+ }, // 449
+ {
+ +0.36426329612731934d, -2.8977490516960565E-8d,
+ }, // 450
+ {
+ +0.36494147777557373d, -1.4601106624823502E-8d,
+ }, // 451
+ {
+ +0.3656191825866699d, +1.69742947894444E-8d,
+ }, // 452
+ {
+ +0.3662964701652527d, +6.7666740211281175E-9d,
+ }, // 453
+ {
+ +0.36697328090667725d, +1.500201674336832E-8d,
+ }, // 454
+ {
+ +0.3676496744155884d, -1.730424167425052E-8d,
+ }, // 455
+ {
+ +0.36832553148269653d, +2.9676011119845104E-8d,
+ }, // 456
+ {
+ +0.36900103092193604d, -2.2253590346826743E-8d,
+ }, // 457
+ {
+ +0.36967599391937256d, +6.3372065441089185E-9d,
+ }, // 458
+ {
+ +0.37035053968429565d, -3.145816653215968E-9d,
+ }, // 459
+ {
+ +0.37102460861206055d, +9.515812117036965E-9d,
+ }, // 460
+ {
+ +0.371698260307312d, -1.4669965113042639E-8d,
+ }, // 461
+ {
+ +0.3723714351654053d, -1.548715389333397E-8d,
+ }, // 462
+ {
+ +0.37304413318634033d, +7.674361647125109E-9d,
+ }, // 463
+ {
+ +0.37371641397476196d, -4.181177882069608E-9d,
+ }, // 464
+ {
+ +0.3743882179260254d, +9.158530500130718E-9d,
+ }, // 465
+ {
+ +0.3750596046447754d, -1.13047236597869E-8d,
+ }, // 466
+ {
+ +0.3757305145263672d, -5.36108186384227E-9d,
+ }, // 467
+ {
+ +0.3764009475708008d, +2.7593452284747873E-8d,
+ }, // 468
+ {
+ +0.37707096338272095d, +2.8557016344085205E-8d,
+ }, // 469
+ {
+ +0.3777405619621277d, -1.868818164036E-9d,
+ }, // 470
+ {
+ +0.3784096837043762d, -3.479042513414447E-9d,
+ }, // 471
+ {
+ +0.37907832860946655d, +2.432550290565648E-8d,
+ }, // 472
+ {
+ +0.37974655628204346d, +2.2538131805476768E-8d,
+ }, // 473
+ {
+ +0.38041436672210693d, -8.244395239939089E-9d,
+ }, // 474
+ {
+ +0.3810817003250122d, -7.821867597227376E-9d,
+ }, // 475
+ {
+ +0.3817485570907593d, +2.4400089062515914E-8d,
+ }, // 476
+ {
+ +0.3824149966239929d, +2.9410015940087773E-8d,
+ }, // 477
+ {
+ +0.38308101892471313d, +7.799913824734797E-9d,
+ }, // 478
+ {
+ +0.38374656438827515d, +1.976524624939355E-8d,
+ }, // 479
+ {
+ +0.38441169261932373d, +6.291008309266035E-9d,
+ }, // 480
+ {
+ +0.3850763440132141d, +2.757030889767851E-8d,
+ }, // 481
+ {
+ +0.38574057817459106d, +2.4585794728405612E-8d,
+ }, // 482
+ {
+ +0.3864043951034546d, -2.0764122246389383E-9d,
+ }, // 483
+ {
+ +0.3870677351951599d, +7.77328837578952E-9d,
+ }, // 484
+ {
+ +0.3877306580543518d, -4.8859560029989374E-9d,
+ }, // 485
+ {
+ +0.3883931040763855d, +2.0133131420595028E-8d,
+ }, // 486
+ {
+ +0.38905513286590576d, +2.380738071335498E-8d,
+ }, // 487
+ {
+ +0.3897167444229126d, +6.7171126157142075E-9d,
+ }, // 488
+ {
+ +0.39037787914276123d, +2.9046141593926277E-8d,
+ }, // 489
+ {
+ +0.3910386562347412d, -2.7836800219410262E-8d,
+ }, // 490
+ {
+ +0.3916988968849182d, +1.545909820981726E-8d,
+ }, // 491
+ {
+ +0.39235877990722656d, -1.930436269002062E-8d,
+ }, // 492
+ {
+ +0.3930181860923767d, -1.2343297554921835E-8d,
+ }, // 493
+ {
+ +0.3936771750450134d, -2.268889128622553E-8d,
+ }, // 494
+ {
+ +0.39433568716049194d, +9.835827818608177E-9d,
+ }, // 495
+ {
+ +0.39499378204345703d, +2.6197411946856397E-8d,
+ }, // 496
+ {
+ +0.3956514596939087d, +2.6965931069318893E-8d,
+ }, // 497
+ {
+ +0.3963087201118469d, +1.2710331127772166E-8d,
+ }, // 498
+ {
+ +0.39696556329727173d, -1.6001563011916016E-8d,
+ }, // 499
+ {
+ +0.39762192964553833d, +1.0016001590267064E-9d,
+ }, // 500
+ {
+ +0.3982778787612915d, +4.680767399874334E-9d,
+ }, // 501
+ {
+ +0.39893341064453125d, -4.399582029272418E-9d,
+ }, // 502
+ {
+ +0.39958852529525757d, -2.5676078228301587E-8d,
+ }, // 503
+ {
+ +0.4002431631088257d, +1.0181870233355787E-9d,
+ }, // 504
+ {
+ +0.40089738368988037d, +1.6639728835984655E-8d,
+ }, // 505
+ {
+ +0.40155118703842163d, +2.174860642202632E-8d,
+ }, // 506
+ {
+ +0.40220457315444946d, +1.6903781197123503E-8d,
+ }, // 507
+ {
+ +0.40285754203796387d, +2.663119647467697E-9d,
+ }, // 508
+ {
+ +0.40351009368896484d, -2.0416603812329616E-8d,
+ }, // 509
+ {
+ +0.4041621685028076d, +7.82494078472695E-9d,
+ }, // 510
+ {
+ +0.40481382608413696d, +2.833770747113627E-8d,
+ }, // 511
+ {
+ +0.40546512603759766d, -1.7929433274271985E-8d,
+ }, // 512
+ {
+ +0.40611594915390015d, -1.1214757379328965E-8d,
+ }, // 513
+ {
+ +0.4067663550376892d, -1.0571553019207106E-8d,
+ }, // 514
+ {
+ +0.40741634368896484d, -1.5449538712332313E-8d,
+ }, // 515
+ {
+ +0.40806591510772705d, -2.529950530235105E-8d,
+ }, // 516
+ {
+ +0.40871500968933105d, +2.0031331601617008E-8d,
+ }, // 517
+ {
+ +0.4093637466430664d, +1.880755298741952E-9d,
+ }, // 518
+ {
+ +0.41001206636428833d, -1.9600580584843318E-8d,
+ }, // 519
+ {
+ +0.41065990924835205d, +1.573691633515306E-8d,
+ }, // 520
+ {
+ +0.4113073945045471d, -1.0772154376548336E-8d,
+ }, // 521
+ {
+ +0.411954402923584d, +2.0624330192486066E-8d,
+ }, // 522
+ {
+ +0.4126010537147522d, -8.741139170029572E-9d,
+ }, // 523
+ {
+ +0.4132472276687622d, +2.0881457123894216E-8d,
+ }, // 524
+ {
+ +0.41389304399490356d, -9.177488027521808E-9d,
+ }, // 525
+ {
+ +0.4145383834838867d, +2.0829952491625585E-8d,
+ }, // 526
+ {
+ +0.4151833653450012d, -7.767915492597301E-9d,
+ }, // 527
+ {
+ +0.4158278703689575d, +2.4774753446082082E-8d,
+ }, // 528
+ {
+ +0.41647201776504517d, -2.1581119071750435E-10d,
+ }, // 529
+ {
+ +0.4171157479286194d, -2.260047972865202E-8d,
+ }, // 530
+ {
+ +0.4177590012550354d, +1.775884601423381E-8d,
+ }, // 531
+ {
+ +0.41840189695358276d, +2.185301053838889E-9d,
+ }, // 532
+ {
+ +0.4190443754196167d, -9.185071463667081E-9d,
+ }, // 533
+ {
+ +0.4196864366531372d, -1.5821896727910552E-8d,
+ }, // 534
+ {
+ +0.4203280806541443d, -1.719582086188318E-8d,
+ }, // 535
+ {
+ +0.42096930742263794d, -1.2778508303324259E-8d,
+ }, // 536
+ {
+ +0.42161011695861816d, -2.042639194493364E-9d,
+ }, // 537
+ {
+ +0.42225050926208496d, +1.5538093219698803E-8d,
+ }, // 538
+ {
+ +0.4228905439376831d, -1.9115659590156936E-8d,
+ }, // 539
+ {
+ +0.42353010177612305d, +1.3729680248843432E-8d,
+ }, // 540
+ {
+ +0.42416930198669434d, -4.611893838830296E-9d,
+ }, // 541
+ {
+ +0.4248080849647522d, -1.4013456880651706E-8d,
+ }, // 542
+ {
+ +0.42544645071029663d, -1.3953728897042917E-8d,
+ }, // 543
+ {
+ +0.42608439922332764d, -3.912427573594197E-9d,
+ }, // 544
+ {
+ +0.4267219305038452d, +1.6629734283189315E-8d,
+ }, // 545
+ {
+ +0.42735910415649414d, -1.1413593493354881E-8d,
+ }, // 546
+ {
+ +0.42799586057662964d, -2.792046157580119E-8d,
+ }, // 547
+ {
+ +0.42863214015960693d, +2.723009182661306E-8d,
+ }, // 548
+ {
+ +0.42926812171936035d, -2.4260535621557444E-8d,
+ }, // 549
+ {
+ +0.42990362644195557d, -3.064060124024764E-9d,
+ }, // 550
+ {
+ +0.43053877353668213d, -2.787640178598121E-8d,
+ }, // 551
+ {
+ +0.4311734437942505d, +2.102412085257792E-8d,
+ }, // 552
+ {
+ +0.4318077564239502d, +2.4939635093999683E-8d,
+ }, // 553
+ {
+ +0.43244171142578125d, -1.5619414792273914E-8d,
+ }, // 554
+ {
+ +0.4330751895904541d, +1.9065734894871523E-8d,
+ }, // 555
+ {
+ +0.4337083101272583d, +1.0294301092654604E-8d,
+ }, // 556
+ {
+ +0.4343410134315491d, +1.8178469851136E-8d,
+ }, // 557
+ {
+ +0.4349733591079712d, -1.6379825102473853E-8d,
+ }, // 558
+ {
+ +0.4356052279472351d, +2.6334323946685834E-8d,
+ }, // 559
+ {
+ +0.43623673915863037d, +2.761628769925529E-8d,
+ }, // 560
+ {
+ +0.436867892742157d, -1.2030229087793677E-8d,
+ }, // 561
+ {
+ +0.4374985694885254d, +2.7106814809424793E-8d,
+ }, // 562
+ {
+ +0.43812888860702515d, +2.631993083235205E-8d,
+ }, // 563
+ {
+ +0.43875885009765625d, -1.3890028312254422E-8d,
+ }, // 564
+ {
+ +0.43938833475112915d, +2.6186133735555794E-8d,
+ }, // 565
+ {
+ +0.4400174617767334d, +2.783809071694788E-8d,
+ }, // 566
+ {
+ +0.440646231174469d, -8.436135220472006E-9d,
+ }, // 567
+ {
+ +0.44127458333969116d, -2.2534815932619883E-8d,
+ }, // 568
+ {
+ +0.4419025182723999d, -1.3961804471714283E-8d,
+ }, // 569
+ {
+ +0.4425300359725952d, +1.7778112039716255E-8d,
+ }, // 570
+ {
+ +0.4431571960449219d, +1.3574569976673652E-8d,
+ }, // 571
+ {
+ +0.4437839984893799d, -2.607907890164073E-8d,
+ }, // 572
+ {
+ +0.4444103240966797d, +1.8518879652136628E-8d,
+ }, // 573
+ {
+ +0.44503629207611084d, +2.865065604247164E-8d,
+ }, // 574
+ {
+ +0.44566190242767334d, +4.806827797299427E-9d,
+ }, // 575
+ {
+ +0.4462870955467224d, +7.0816970994232115E-9d,
+ }, // 576
+ {
+ +0.44691193103790283d, -2.3640641240074437E-8d,
+ }, // 577
+ {
+ +0.4475363492965698d, -2.7267718387865538E-8d,
+ }, // 578
+ {
+ +0.4481603503227234d, -3.3126235292976077E-9d,
+ }, // 579
+ {
+ +0.4487839937210083d, -1.0894001590268427E-8d,
+ }, // 580
+ {
+ +0.4494072198867798d, +1.0077883359971829E-8d,
+ }, // 581
+ {
+ +0.4500300884246826d, +4.825712712114668E-10d,
+ }, // 582
+ {
+ +0.450652539730072d, +2.0407987470746858E-8d,
+ }, // 583
+ {
+ +0.4512746334075928d, +1.073186581170719E-8d,
+ }, // 584
+ {
+ +0.4518963694572449d, -2.8064314757880205E-8d,
+ }, // 585
+ {
+ +0.45251762866973877d, +2.3709316816226527E-8d,
+ }, // 586
+ {
+ +0.4531385898590088d, -1.2281487504266522E-8d,
+ }, // 587
+ {
+ +0.4537591338157654d, -1.634864487421458E-8d,
+ }, // 588
+ {
+ +0.45437926054000854d, +1.1985747222409522E-8d,
+ }, // 589
+ {
+ +0.45499902963638306d, +1.3594057956219485E-8d,
+ }, // 590
+ {
+ +0.4556184411048889d, -1.1047585095328619E-8d,
+ }, // 591
+ {
+ +0.45623743534088135d, -1.8592937532754405E-9d,
+ }, // 592
+ {
+ +0.4568560719490051d, -1.797135137545755E-8d,
+ }, // 593
+ {
+ +0.4574742913246155d, +6.943684261645378E-10d,
+ }, // 594
+ {
+ +0.4580921530723572d, -4.994175141684681E-9d,
+ }, // 595
+ {
+ +0.45870959758758545d, +2.5039391215625133E-8d,
+ }, // 596
+ {
+ +0.45932674407958984d, -2.7943366835352838E-8d,
+ }, // 597
+ {
+ +0.45994341373443604d, +1.534146910128904E-8d,
+ }, // 598
+ {
+ +0.46055978536605835d, -2.3450920230816267E-8d,
+ }, // 599
+ {
+ +0.46117573976516724d, -2.4642997069960124E-8d,
+ }, // 600
+ {
+ +0.4617912769317627d, +1.2232622070370946E-8d,
+ }, // 601
+ {
+ +0.4624064564704895d, +2.80378133047839E-8d,
+ }, // 602
+ {
+ +0.46302127838134766d, +2.3238237048117092E-8d,
+ }, // 603
+ {
+ +0.46363574266433716d, -1.7013046451109475E-9d,
+ }, // 604
+ {
+ +0.46424978971481323d, +1.3287778803035383E-8d,
+ }, // 605
+ {
+ +0.46486347913742065d, +9.06393426961373E-9d,
+ }, // 606
+ {
+ +0.4654768109321594d, -1.3910598647592876E-8d,
+ }, // 607
+ {
+ +0.46608972549438477d, +4.430214458933614E-9d,
+ }, // 608
+ {
+ +0.46670228242874146d, +4.942270562885745E-9d,
+ }, // 609
+ {
+ +0.4673144817352295d, -1.1914734393460718E-8d,
+ }, // 610
+ {
+ +0.4679262638092041d, +1.3922696570638494E-8d,
+ }, // 611
+ {
+ +0.46853768825531006d, +2.3307929211781914E-8d,
+ }, // 612
+ {
+ +0.46914875507354736d, +1.669813444584674E-8d,
+ }, // 613
+ {
+ +0.469759464263916d, -5.450354376430758E-9d,
+ }, // 614
+ {
+ +0.47036975622177124d, +1.6922605350647674E-8d,
+ }, // 615
+ {
+ +0.4709796905517578d, +2.4667033200046904E-8d,
+ }, // 616
+ {
+ +0.47158926725387573d, +1.8236762070433784E-8d,
+ }, // 617
+ {
+ +0.472198486328125d, -1.915204563140137E-9d,
+ }, // 618
+ {
+ +0.47280728816986084d, +2.426795414605756E-8d,
+ }, // 619
+ {
+ +0.4734157919883728d, -2.19717006713618E-8d,
+ }, // 620
+ {
+ +0.47402387857437134d, -2.0974352165535873E-8d,
+ }, // 621
+ {
+ +0.47463154792785645d, +2.770970558184228E-8d,
+ }, // 622
+ {
+ +0.4752389192581177d, +5.32006955298355E-9d,
+ }, // 623
+ {
+ +0.47584593296051025d, -2.809054633964104E-8d,
+ }, // 624
+ {
+ +0.4764525294303894d, -1.2470243596102937E-8d,
+ }, // 625
+ {
+ +0.4770587682723999d, -6.977226702440138E-9d,
+ }, // 626
+ {
+ +0.47766464948654175d, -1.1165866833118273E-8d,
+ }, // 627
+ {
+ +0.47827017307281494d, -2.4591344661022708E-8d,
+ }, // 628
+ {
+ +0.4788752794265747d, +1.2794996377383974E-8d,
+ }, // 629
+ {
+ +0.4794800877571106d, -1.7772927065973874E-8d,
+ }, // 630
+ {
+ +0.48008447885513306d, +3.35657712457243E-9d,
+ }, // 631
+ {
+ +0.48068851232528687d, +1.7020465042442242E-8d,
+ }, // 632
+ {
+ +0.481292188167572d, +2.365953779624783E-8d,
+ }, // 633
+ {
+ +0.4818955063819885d, +2.3713798664443718E-8d,
+ }, // 634
+ {
+ +0.4824984669685364d, +1.7622455019548098E-8d,
+ }, // 635
+ {
+ +0.4831010699272156d, +5.823920246566496E-9d,
+ }, // 636
+ {
+ +0.4837033152580261d, -1.1244184344361017E-8d,
+ }, // 637
+ {
+ +0.48430514335632324d, +2.645961716432205E-8d,
+ }, // 638
+ {
+ +0.4849066734313965d, +1.6207809718247905E-10d,
+ }, // 639
+ {
+ +0.4855077862739563d, +2.9507744508973654E-8d,
+ }, // 640
+ {
+ +0.48610860109329224d, -4.278201128741098E-9d,
+ }, // 641
+ {
+ +0.48670899868011475d, +1.844722015961139E-8d,
+ }, // 642
+ {
+ +0.4873090982437134d, -2.1092372471088425E-8d,
+ }, // 643
+ {
+ +0.4879087805747986d, -3.2555596107382053E-9d,
+ }, // 644
+ {
+ +0.48850810527801514d, +1.2784366845429667E-8d,
+ }, // 645
+ {
+ +0.48910707235336304d, +2.7457984659996047E-8d,
+ }, // 646
+ {
+ +0.48970574140548706d, -1.8409546441412518E-8d,
+ }, // 647
+ {
+ +0.49030399322509766d, -5.179903818099661E-9d,
+ }, // 648
+ {
+ +0.4909018874168396d, +7.97053127828682E-9d,
+ }, // 649
+ {
+ +0.4914994239807129d, +2.146925464473481E-8d,
+ }, // 650
+ {
+ +0.4920966625213623d, -2.3861648589988232E-8d,
+ }, // 651
+ {
+ +0.4926934838294983d, -8.386923035320549E-9d,
+ }, // 652
+ {
+ +0.4932899475097656d, +8.713990131749256E-9d,
+ }, // 653
+ {
+ +0.4938860535621643d, +2.7865534085810115E-8d,
+ }, // 654
+ {
+ +0.4944818615913391d, -1.011325138560159E-8d,
+ }, // 655
+ {
+ +0.4950772523880005d, +1.4409851026316708E-8d,
+ }, // 656
+ {
+ +0.495672345161438d, -1.735227547472004E-8d,
+ }, // 657
+ {
+ +0.49626702070236206d, +1.4231078209064581E-8d,
+ }, // 658
+ {
+ +0.49686139822006226d, -9.628709342929729E-9d,
+ }, // 659
+ {
+ +0.4974554181098938d, -2.8907074856577267E-8d,
+ }, // 660
+ {
+ +0.4980490207672119d, +1.6419797090870802E-8d,
+ }, // 661
+ {
+ +0.49864232540130615d, +7.561041519403049E-9d,
+ }, // 662
+ {
+ +0.49923527240753174d, +4.538983468118194E-9d,
+ }, // 663
+ {
+ +0.49982786178588867d, +7.770560657946324E-9d,
+ }, // 664
+ {
+ +0.500420093536377d, +1.767197002609876E-8d,
+ }, // 665
+ {
+ +0.5010119676589966d, +3.46586694799214E-8d,
+ }, // 666
+ {
+ +0.5016034841537476d, +5.914537964556077E-8d,
+ }, // 667
+ {
+ +0.5021947622299194d, -2.7663203939320167E-8d,
+ }, // 668
+ {
+ +0.5027855634689331d, +1.3064749115929298E-8d,
+ }, // 669
+ {
+ +0.5033761262893677d, -5.667682106730711E-8d,
+ }, // 670
+ {
+ +0.503966212272644d, +1.9424534974370594E-9d,
+ }, // 671
+ {
+ +0.5045560598373413d, -4.908494602153544E-8d,
+ }, // 672
+ {
+ +0.5051454305648804d, +2.906989285008994E-8d,
+ }, // 673
+ {
+ +0.5057345628738403d, -1.602000800745108E-9d,
+ }, // 674
+ {
+ +0.5063233375549316d, -2.148245271118002E-8d,
+ }, // 675
+ {
+ +0.5069117546081543d, -3.016329994276181E-8d,
+ }, // 676
+ {
+ +0.5074998140335083d, -2.7237099632871992E-8d,
+ }, // 677
+ {
+ +0.5080875158309937d, -1.2297127301923986E-8d,
+ }, // 678
+ {
+ +0.5086748600006104d, +1.5062624834468093E-8d,
+ }, // 679
+ {
+ +0.5092618465423584d, +5.524744954836658E-8d,
+ }, // 680
+ {
+ +0.5098485946655273d, -1.054736327333046E-8d,
+ }, // 681
+ {
+ +0.5104348659515381d, +5.650063324725722E-8d,
+ }, // 682
+ {
+ +0.5110208988189697d, +1.8376017791642605E-8d,
+ }, // 683
+ {
+ +0.5116065740585327d, -5.309470636324855E-9d,
+ }, // 684
+ {
+ +0.512191891670227d, -1.4154089255217218E-8d,
+ }, // 685
+ {
+ +0.5127768516540527d, -7.756800301729815E-9d,
+ }, // 686
+ {
+ +0.5133614540100098d, +1.4282730618002001E-8d,
+ }, // 687
+ {
+ +0.5139456987380981d, +5.2364136172269755E-8d,
+ }, // 688
+ {
+ +0.5145297050476074d, -1.2322940607922115E-8d,
+ }, // 689
+ {
+ +0.5151132345199585d, +5.903831350855322E-8d,
+ }, // 690
+ {
+ +0.5156965255737305d, +2.8426856726994483E-8d,
+ }, // 691
+ {
+ +0.5162794589996338d, +1.544882070711032E-8d,
+ }, // 692
+ {
+ +0.5168620347976685d, +2.0500353979930155E-8d,
+ }, // 693
+ {
+ +0.5174442529678345d, +4.397691311390564E-8d,
+ }, // 694
+ {
+ +0.5180262327194214d, -3.2936025225250634E-8d,
+ }, // 695
+ {
+ +0.5186077356338501d, +2.857419553449673E-8d,
+ }, // 696
+ {
+ +0.5191890001296997d, -9.51761338269325E-9d,
+ }, // 697
+ {
+ +0.5197699069976807d, -2.7609457648450225E-8d,
+ }, // 698
+ {
+ +0.520350456237793d, -2.5309316441333305E-8d,
+ }, // 699
+ {
+ +0.5209306478500366d, -2.2258513086839407E-9d,
+ }, // 700
+ {
+ +0.5215104818344116d, +4.203159541613745E-8d,
+ }, // 701
+ {
+ +0.5220900774002075d, -1.1356287358852729E-8d,
+ }, // 702
+ {
+ +0.5226693153381348d, -4.279090925831093E-8d,
+ }, // 703
+ {
+ +0.5232481956481934d, -5.188364552285819E-8d,
+ }, // 704
+ {
+ +0.5238267183303833d, -3.82465458937857E-8d,
+ }, // 705
+ {
+ +0.5244048833847046d, -1.4923330530645769E-9d,
+ }, // 706
+ {
+ +0.5249826908111572d, +5.8765598932137004E-8d,
+ }, // 707
+ {
+ +0.5255602598190308d, +2.3703896609663678E-8d,
+ }, // 708
+ {
+ +0.5261374711990356d, +1.2917117341231647E-8d,
+ }, // 709
+ {
+ +0.5267143249511719d, +2.6789862192139226E-8d,
+ }, // 710
+ {
+ +0.527290940284729d, -5.350322253112414E-8d,
+ }, // 711
+ {
+ +0.5278670787811279d, +1.0839714455426386E-8d,
+ }, // 712
+ {
+ +0.5284429788589478d, -1.821729591343314E-8d,
+ }, // 713
+ {
+ +0.5290185213088989d, -2.1083014672301448E-8d,
+ }, // 714
+ {
+ +0.5295937061309814d, +2.623848491704216E-9d,
+ }, // 715
+ {
+ +0.5301685333251953d, +5.328392630534142E-8d,
+ }, // 716
+ {
+ +0.5307431221008301d, +1.206790586971942E-8d,
+ }, // 717
+ {
+ +0.5313173532485962d, -1.4356011804377797E-9d,
+ }, // 718
+ {
+ +0.5318912267684937d, +1.3152074173459994E-8d,
+ }, // 719
+ {
+ +0.5324647426605225d, +5.6208949382936426E-8d,
+ }, // 720
+ {
+ +0.5330380201339722d, +8.90310227565917E-9d,
+ }, // 721
+ {
+ +0.5336109399795532d, -9.179458802504127E-9d,
+ }, // 722
+ {
+ +0.5341835021972656d, +2.337337845617735E-9d,
+ }, // 723
+ {
+ +0.5347557067871094d, +4.3828918300477925E-8d,
+ }, // 724
+ {
+ +0.535327672958374d, -3.5392250480081715E-9d,
+ }, // 725
+ {
+ +0.53589928150177d, -2.0183663375378704E-8d,
+ }, // 726
+ {
+ +0.5364705324172974d, -5.730898606435436E-9d,
+ }, // 727
+ {
+ +0.537041425704956d, +4.0191927599879235E-8d,
+ }, // 728
+ {
+ +0.5376120805740356d, -1.2522542401353875E-9d,
+ }, // 729
+ {
+ +0.5381823778152466d, -1.0482571326594316E-8d,
+ }, // 730
+ {
+ +0.5387523174285889d, +1.2871924223480165E-8d,
+ }, // 731
+ {
+ +0.539322018623352d, -5.002774317612589E-8d,
+ }, // 732
+ {
+ +0.539891242980957d, +3.960668706590162E-8d,
+ }, // 733
+ {
+ +0.5404602289199829d, +4.372568630242375E-8d,
+ }, // 734
+ {
+ +0.5410289764404297d, -3.730232461206926E-8d,
+ }, // 735
+ {
+ +0.5415972471237183d, +3.5309026109857795E-8d,
+ }, // 736
+ {
+ +0.5421652793884277d, +2.3508325311148225E-8d,
+ }, // 737
+ {
+ +0.5427329540252686d, +4.6871403168921666E-8d,
+ }, // 738
+ {
+ +0.5433003902435303d, -1.3445113140270216E-8d,
+ }, // 739
+ {
+ +0.5438674688339233d, -3.786663982218041E-8d,
+ }, // 740
+ {
+ +0.5444341897964478d, -2.602850370608209E-8d,
+ }, // 741
+ {
+ +0.5450005531311035d, +2.2433348713144506E-8d,
+ }, // 742
+ {
+ +0.5455666780471802d, -1.1326936872620137E-8d,
+ }, // 743
+ {
+ +0.5461324453353882d, -7.737252533211342E-9d,
+ }, // 744
+ {
+ +0.5466978549957275d, +3.3564604642699844E-8d,
+ }, // 745
+ {
+ +0.5472630262374878d, -6.269066061111782E-9d,
+ }, // 746
+ {
+ +0.5478278398513794d, -7.667998948729528E-9d,
+ }, // 747
+ {
+ +0.5483922958374023d, +2.9728170818998143E-8d,
+ }, // 748
+ {
+ +0.5489565134048462d, -1.2930091396008281E-8d,
+ }, // 749
+ {
+ +0.5495203733444214d, -1.607434968107079E-8d,
+ }, // 750
+ {
+ +0.5500838756561279d, +2.0653935146671156E-8d,
+ }, // 751
+ {
+ +0.5506471395492554d, -2.1596593091833788E-8d,
+ }, // 752
+ {
+ +0.5512100458145142d, -2.3259315921149476E-8d,
+ }, // 753
+ {
+ +0.5517725944519043d, +1.6022492496522704E-8d,
+ }, // 754
+ {
+ +0.5523349046707153d, -2.260433328226171E-8d,
+ }, // 755
+ {
+ +0.5528968572616577d, -1.957497997726303E-8d,
+ }, // 756
+ {
+ +0.5534584522247314d, +2.5465477111883854E-8d,
+ }, // 757
+ {
+ +0.5540198087692261d, -6.33792454933092E-9d,
+ }, // 758
+ {
+ +0.554580807685852d, +4.577835263278281E-9d,
+ }, // 759
+ {
+ +0.5551414489746094d, +5.856589221771548E-8d,
+ }, // 760
+ {
+ +0.5557018518447876d, +3.6769498759522324E-8d,
+ }, // 761
+ {
+ +0.5562618970870972d, +5.874989409410614E-8d,
+ }, // 762
+ {
+ +0.5568217039108276d, +5.649147309876989E-9d,
+ }, // 763
+ {
+ +0.5573811531066895d, -2.9726830960751796E-9d,
+ }, // 764
+ {
+ +0.5579402446746826d, +3.323458344853057E-8d,
+ }, // 765
+ {
+ +0.5584990978240967d, -4.588749093664028E-9d,
+ }, // 766
+ {
+ +0.5590575933456421d, +3.115616594184543E-9d,
+ }, // 767
+ {
+ +0.5596157312393188d, +5.6696103838614634E-8d,
+ }, // 768
+ {
+ +0.5601736307144165d, +3.7291263280048303E-8d,
+ }, // 769
+ {
+ +0.5607312917709351d, -5.4751646725093355E-8d,
+ }, // 770
+ {
+ +0.5612884759902954d, +1.9332630743320287E-8d,
+ }, // 771
+ {
+ +0.5618454217910767d, +2.147161515775941E-8d,
+ }, // 772
+ {
+ +0.5624021291732788d, -4.7989172862560625E-8d,
+ }, // 773
+ {
+ +0.5629583597183228d, +4.971378973445109E-8d,
+ }, // 774
+ {
+ +0.5635144710540771d, -4.2702997139152675E-8d,
+ }, // 775
+ {
+ +0.5640701055526733d, +3.273212962622764E-8d,
+ }, // 776
+ {
+ +0.5646255016326904d, +3.79438125545842E-8d,
+ }, // 777
+ {
+ +0.5651806592941284d, -2.6725298288329835E-8d,
+ }, // 778
+ {
+ +0.5657354593276978d, -4.1723833577410244E-8d,
+ }, // 779
+ {
+ +0.5662899017333984d, -6.71028256490915E-9d,
+ }, // 780
+ {
+ +0.56684410572052d, -4.055299181908475E-8d,
+ }, // 781
+ {
+ +0.567397952079773d, -2.3702295314000405E-8d,
+ }, // 782
+ {
+ +0.5679514408111572d, +4.4181618172507453E-8d,
+ }, // 783
+ {
+ +0.5685046911239624d, +4.4228706309734985E-8d,
+ }, // 784
+ {
+ +0.5690577030181885d, -2.3222346436879016E-8d,
+ }, // 785
+ {
+ +0.5696103572845459d, -3.862412756175274E-8d,
+ }, // 786
+ {
+ +0.5701626539230347d, -1.6390743801589046E-9d,
+ }, // 787
+ {
+ +0.5707147121429443d, -3.1139472791083883E-8d,
+ }, // 788
+ {
+ +0.5712664127349854d, -7.579587391156013E-9d,
+ }, // 789
+ {
+ +0.5718178749084473d, -4.983281844744412E-8d,
+ }, // 790
+ {
+ +0.5723689794540405d, -3.835454246739619E-8d,
+ }, // 791
+ {
+ +0.5729197263717651d, +2.7190020372374008E-8d,
+ }, // 792
+ {
+ +0.5734702348709106d, +2.7925807446276126E-8d,
+ }, // 793
+ {
+ +0.574020504951477d, -3.5813506001861646E-8d,
+ }, // 794
+ {
+ +0.5745704174041748d, -4.448550564530588E-8d,
+ }, // 795
+ {
+ +0.5751199722290039d, +2.2423840341717488E-9d,
+ }, // 796
+ {
+ +0.5756692886352539d, -1.450709904687712E-8d,
+ }, // 797
+ {
+ +0.5762182474136353d, +2.4806815282282017E-8d,
+ }, // 798
+ {
+ +0.5767669677734375d, +1.3057724436551892E-9d,
+ }, // 799
+ {
+ +0.5773153305053711d, +3.4529452510568104E-8d,
+ }, // 800
+ {
+ +0.5778634548187256d, +5.598413198183808E-9d,
+ }, // 801
+ {
+ +0.5784112215042114d, +3.405124925700107E-8d,
+ }, // 802
+ {
+ +0.5789587497711182d, +1.0074354568442952E-9d,
+ }, // 803
+ {
+ +0.5795059204101562d, +2.600448597385527E-8d,
+ }, // 804
+ {
+ +0.5800528526306152d, -9.83920263200211E-9d,
+ }, // 805
+ {
+ +0.5805994272232056d, +1.3012807963586057E-8d,
+ }, // 806
+ {
+ +0.5811457633972168d, -2.432215917965441E-8d,
+ }, // 807
+ {
+ +0.5816917419433594d, -2.308736892479391E-9d,
+ }, // 808
+ {
+ +0.5822374820709229d, -3.983067093146514E-8d,
+ }, // 809
+ {
+ +0.5827828645706177d, -1.735366061128156E-8d,
+ }, // 810
+ {
+ +0.5833280086517334d, -5.376251584638963E-8d,
+ }, // 811
+ {
+ +0.5838727951049805d, -2.952399778965259E-8d,
+ }, // 812
+ {
+ +0.5844172239303589d, +5.5685313670430624E-8d,
+ }, // 813
+ {
+ +0.5849615335464478d, -3.6230268489088716E-8d,
+ }, // 814
+ {
+ +0.5855053663253784d, +5.267948957869391E-8d,
+ }, // 815
+ {
+ +0.5860490798950195d, -3.489144132234588E-8d,
+ }, // 816
+ {
+ +0.5865923166275024d, +5.9006122320612716E-8d,
+ }, // 817
+ {
+ +0.5871354341506958d, -2.2934896740542648E-8d,
+ }, // 818
+ {
+ +0.5876781940460205d, -4.1975650319859075E-8d,
+ }, // 819
+ {
+ +0.5882205963134766d, +2.2036094805348692E-9d,
+ }, // 820
+ {
+ +0.5887627601623535d, -9.287179048539306E-9d,
+ }, // 821
+ {
+ +0.5893045663833618d, +4.3079982556221595E-8d,
+ }, // 822
+ {
+ +0.589846134185791d, +4.041399585161321E-8d,
+ }, // 823
+ {
+ +0.5903874635696411d, -1.696746473863933E-8d,
+ }, // 824
+ {
+ +0.5909284353256226d, -9.53795080582038E-9d,
+ }, // 825
+ {
+ +0.5914691686630249d, -5.619010749352923E-8d,
+ }, // 826
+ {
+ +0.5920095443725586d, -3.7398514182529506E-8d,
+ }, // 827
+ {
+ +0.5925495624542236d, +4.71524479659295E-8d,
+ }, // 828
+ {
+ +0.5930894613265991d, -4.0640692434639215E-8d,
+ }, // 829
+ {
+ +0.5936288833618164d, +5.716453096255401E-8d,
+ }, // 830
+ {
+ +0.5941681861877441d, -1.6745661720946737E-8d,
+ }, // 831
+ {
+ +0.5947071313858032d, -2.3639110433141897E-8d,
+ }, // 832
+ {
+ +0.5952457189559937d, +3.67972590471072E-8d,
+ }, // 833
+ {
+ +0.595784068107605d, +4.566672575206695E-8d,
+ }, // 834
+ {
+ +0.5963221788406372d, +3.2813537149653483E-9d,
+ }, // 835
+ {
+ +0.5968599319458008d, +2.916199305533732E-8d,
+ }, // 836
+ {
+ +0.5973974466323853d, +4.410412409109416E-9d,
+ }, // 837
+ {
+ +0.5979346036911011d, +4.85464582112459E-8d,
+ }, // 838
+ {
+ +0.5984715223312378d, +4.267089756924666E-8d,
+ }, // 839
+ {
+ +0.5990082025527954d, -1.2906712010774655E-8d,
+ }, // 840
+ {
+ +0.5995445251464844d, +1.3319784467641742E-9d,
+ }, // 841
+ {
+ +0.6000806093215942d, -3.35137581974451E-8d,
+ }, // 842
+ {
+ +0.6006163358688354d, +2.0734340706476473E-9d,
+ }, // 843
+ {
+ +0.6011518239974976d, -1.0808162722402073E-8d,
+ }, // 844
+ {
+ +0.601686954498291d, +4.735781872502109E-8d,
+ }, // 845
+ {
+ +0.6022218465805054d, +5.76686738430634E-8d,
+ }, // 846
+ {
+ +0.6027565002441406d, +2.043049589651736E-8d,
+ }, // 847
+ {
+ +0.6032907962799072d, +5.515817703577808E-8d,
+ }, // 848
+ {
+ +0.6038248538970947d, +4.2947540692649586E-8d,
+ }, // 849
+ {
+ +0.6043586730957031d, -1.589678872195875E-8d,
+ }, // 850
+ {
+ +0.6048921346664429d, -1.8613847754677912E-9d,
+ }, // 851
+ {
+ +0.6054253578186035d, -3.3851886626187444E-8d,
+ }, // 852
+ {
+ +0.6059582233428955d, +7.64416021682279E-9d,
+ }, // 853
+ {
+ +0.6064908504486084d, +3.7201467248814224E-9d,
+ }, // 854
+ {
+ +0.6070232391357422d, -4.532172996647129E-8d,
+ }, // 855
+ {
+ +0.6075552701950073d, -1.997046552871766E-8d,
+ }, // 856
+ {
+ +0.6080870628356934d, -3.913411606668587E-8d,
+ }, // 857
+ {
+ +0.6086184978485107d, +1.6697361107868944E-8d,
+ }, // 858
+ {
+ +0.609149694442749d, +2.8614950293715483E-8d,
+ }, // 859
+ {
+ +0.6096806526184082d, -3.081552929643174E-9d,
+ }, // 860
+ {
+ +0.6102112531661987d, +4.111645931319645E-8d,
+ }, // 861
+ {
+ +0.6107416152954102d, +4.2298539553668435E-8d,
+ }, // 862
+ {
+ +0.6112717390060425d, +7.630546413718035E-10d,
+ }, // 863
+ {
+ +0.6118015050888062d, +3.601718675118614E-8d,
+ }, // 864
+ {
+ +0.6123310327529907d, +2.914906573537692E-8d,
+ }, // 865
+ {
+ +0.6128603219985962d, -1.9544361222269494E-8d,
+ }, // 866
+ {
+ +0.613389253616333d, +9.442671392695732E-9d,
+ }, // 867
+ {
+ +0.6139179468154907d, -2.8031202304593286E-9d,
+ }, // 868
+ {
+ +0.6144464015960693d, -5.598619958143586E-8d,
+ }, // 869
+ {
+ +0.6149744987487793d, -3.060220883766096E-8d,
+ }, // 870
+ {
+ +0.6155023574829102d, -4.556583652800433E-8d,
+ }, // 871
+ {
+ +0.6160298585891724d, +1.8626341656366314E-8d,
+ }, // 872
+ {
+ +0.6165571212768555d, +4.305870564227991E-8d,
+ }, // 873
+ {
+ +0.6170841455459595d, +2.8024460607734262E-8d,
+ }, // 874
+ {
+ +0.6176109313964844d, -2.6183651590639875E-8d,
+ }, // 875
+ {
+ +0.6181373596191406d, -6.406189112730307E-11d,
+ }, // 876
+ {
+ +0.6186635494232178d, -1.2534241706168776E-8d,
+ }, // 877
+ {
+ +0.6191893815994263d, +5.5906456251308664E-8d,
+ }, // 878
+ {
+ +0.6197150945663452d, -3.286964881802063E-8d,
+ }, // 879
+ {
+ +0.6202404499053955d, -4.0153537978961E-8d,
+ }, // 880
+ {
+ +0.6207654476165771d, +3.434477109643361E-8d,
+ }, // 881
+ {
+ +0.6212903261184692d, -4.750377491075032E-8d,
+ }, // 882
+ {
+ +0.6218148469924927d, -4.699152670372743E-8d,
+ }, // 883
+ {
+ +0.6223390102386475d, +3.617013128065961E-8d,
+ }, // 884
+ {
+ +0.6228630542755127d, -3.6149218175202596E-8d,
+ }, // 885
+ {
+ +0.6233867406845093d, -2.5243286814648133E-8d,
+ }, // 886
+ {
+ +0.6239101886749268d, -5.003410681432538E-8d,
+ }, // 887
+ {
+ +0.6244332790374756d, +8.974417915105033E-9d,
+ }, // 888
+ {
+ +0.6249561309814453d, +3.285935446876949E-8d,
+ }, // 889
+ {
+ +0.6254787445068359d, +2.190661054038537E-8d,
+ }, // 890
+ {
+ +0.6260011196136475d, -2.3598354190515998E-8d,
+ }, // 891
+ {
+ +0.6265231370925903d, +1.5838762427747586E-8d,
+ }, // 892
+ {
+ +0.6270449161529541d, +2.129323729978037E-8d,
+ }, // 893
+ {
+ +0.6275664567947388d, -6.950808333865794E-9d,
+ }, // 894
+ {
+ +0.6280876398086548d, +5.059959203156465E-8d,
+ }, // 895
+ {
+ +0.6286087036132812d, -4.41909071122557E-8d,
+ }, // 896
+ {
+ +0.6291294097900391d, -5.262093550784066E-8d,
+ }, // 897
+ {
+ +0.6296497583389282d, +2.559185648444699E-8d,
+ }, // 898
+ {
+ +0.6301699876785278d, -4.768920119497491E-8d,
+ }, // 899
+ {
+ +0.6306898593902588d, -3.376406008397877E-8d,
+ }, // 900
+ {
+ +0.6312094926834106d, -5.156097914033476E-8d,
+ }, // 901
+ {
+ +0.6317287683486938d, +1.840992392368355E-8d,
+ }, // 902
+ {
+ +0.632247805595398d, +5.721951534729663E-8d,
+ }, // 903
+ {
+ +0.6327667236328125d, -5.406177467045421E-8d,
+ }, // 904
+ {
+ +0.6332851648330688d, +4.247320713683124E-8d,
+ }, // 905
+ {
+ +0.6338034868240356d, -1.0524557502830645E-8d,
+ }, // 906
+ {
+ +0.6343214511871338d, +2.5641927558519502E-8d,
+ }, // 907
+ {
+ +0.6348391771316528d, +3.204135737993823E-8d,
+ }, // 908
+ {
+ +0.6353566646575928d, +8.951285029786536E-9d,
+ }, // 909
+ {
+ +0.6358739137649536d, -4.335116707228395E-8d,
+ }, // 910
+ {
+ +0.6363908052444458d, -5.380016714089483E-9d,
+ }, // 911
+ {
+ +0.6369074583053589d, +3.931710344901743E-9d,
+ }, // 912
+ {
+ +0.6374238729476929d, -1.5140150088220166E-8d,
+ }, // 913
+ {
+ +0.6379399299621582d, +5.688910024377372E-8d,
+ }, // 914
+ {
+ +0.638455867767334d, -1.8124135273572568E-8d,
+ }, // 915
+ {
+ +0.6389714479446411d, -1.486720391901626E-9d,
+ }, // 916
+ {
+ +0.6394867897033691d, -1.2133811978747018E-8d,
+ }, // 917
+ {
+ +0.6400018930435181d, -4.9791700939901716E-8d,
+ }, // 918
+ {
+ +0.6405166387557983d, +5.022188652837274E-9d,
+ }, // 919
+ {
+ +0.6410311460494995d, +3.337143177933685E-8d,
+ }, // 920
+ {
+ +0.6415454149246216d, +3.55284719912458E-8d,
+ }, // 921
+ {
+ +0.6420594453811646d, +1.1765332726757802E-8d,
+ }, // 922
+ {
+ +0.6425732374191284d, -3.7646381826067834E-8d,
+ }, // 923
+ {
+ +0.6430866718292236d, +6.773803682579552E-9d,
+ }, // 924
+ {
+ +0.6435998678207397d, +2.608736797081283E-8d,
+ }, // 925
+ {
+ +0.6441128253936768d, +2.056466263408266E-8d,
+ }, // 926
+ {
+ +0.6446255445480347d, -9.524376551107945E-9d,
+ }, // 927
+ {
+ +0.6451379060745239d, +5.5299060775883977E-8d,
+ }, // 928
+ {
+ +0.6456501483917236d, -2.3114497793159813E-8d,
+ }, // 929
+ {
+ +0.6461620330810547d, -6.077779731902102E-9d,
+ }, // 930
+ {
+ +0.6466736793518066d, -1.2531793589140273E-8d,
+ }, // 931
+ {
+ +0.6471850872039795d, -4.220866994206517E-8d,
+ }, // 932
+ {
+ +0.6476961374282837d, +2.4368339445199057E-8d,
+ }, // 933
+ {
+ +0.6482070684432983d, -5.095229574221907E-8d,
+ }, // 934
+ {
+ +0.6487176418304443d, -2.9485356677301627E-8d,
+ }, // 935
+ {
+ +0.6492279767990112d, -3.0173901411577916E-8d,
+ }, // 936
+ {
+ +0.649738073348999d, -5.275210583909726E-8d,
+ }, // 937
+ {
+ +0.6502478122711182d, +2.2254737134350224E-8d,
+ }, // 938
+ {
+ +0.6507574319839478d, -4.330693978322885E-8d,
+ }, // 939
+ {
+ +0.6512666940689087d, -1.0753950588009912E-8d,
+ }, // 940
+ {
+ +0.6517757177352905d, +9.686179886293545E-10d,
+ }, // 941
+ {
+ +0.6522845029830933d, -7.875434494414498E-9d,
+ }, // 942
+ {
+ +0.6527930498123169d, -3.702271091849158E-8d,
+ }, // 943
+ {
+ +0.6533012390136719d, +3.2999073763758614E-8d,
+ }, // 944
+ {
+ +0.6538093090057373d, -3.5966064858620067E-8d,
+ }, // 945
+ {
+ +0.6543170213699341d, -5.23735298540578E-9d,
+ }, // 946
+ {
+ +0.6548244953155518d, +6.237715351293023E-9d,
+ }, // 947
+ {
+ +0.6553317308425903d, -1.279462699936282E-9d,
+ }, // 948
+ {
+ +0.6558387279510498d, -2.7527887552743672E-8d,
+ }, // 949
+ {
+ +0.6563453674316406d, +4.696233317356646E-8d,
+ }, // 950
+ {
+ +0.6568518877029419d, -1.5967172745329108E-8d,
+ }, // 951
+ {
+ +0.6573580503463745d, +2.2361985518423144E-8d,
+ }, // 952
+ {
+ +0.657863974571228d, +4.2999935789083046E-8d,
+ }, // 953
+ {
+ +0.6583696603775024d, +4.620570188811826E-8d,
+ }, // 954
+ {
+ +0.6588751077651978d, +3.223791487908353E-8d,
+ }, // 955
+ {
+ +0.659380316734314d, +1.3548138612715822E-9d,
+ }, // 956
+ {
+ +0.6598852872848511d, -4.618575323863973E-8d,
+ }, // 957
+ {
+ +0.6603899002075195d, +9.082960673843353E-9d,
+ }, // 958
+ {
+ +0.6608942747116089d, +4.820873399634487E-8d,
+ }, // 959
+ {
+ +0.6613985300064087d, -4.776104368314602E-8d,
+ }, // 960
+ {
+ +0.6619024276733398d, -4.0151502150238136E-8d,
+ }, // 961
+ {
+ +0.6624060869216919d, -4.791602708710648E-8d,
+ }, // 962
+ {
+ +0.6629093885421753d, +4.8410188461165925E-8d,
+ }, // 963
+ {
+ +0.6634125709533691d, +1.0663697110471944E-8d,
+ }, // 964
+ {
+ +0.6639155149459839d, -4.1691464781797555E-8d,
+ }, // 965
+ {
+ +0.66441810131073d, +1.080835500478704E-8d,
+ }, // 966
+ {
+ +0.664920449256897d, +4.920784622407246E-8d,
+ }, // 967
+ {
+ +0.6654226779937744d, -4.544868396511241E-8d,
+ }, // 968
+ {
+ +0.6659245491027832d, -3.448944157854234E-8d,
+ }, // 969
+ {
+ +0.6664261817932129d, -3.6870882345139385E-8d,
+ }, // 970
+ {
+ +0.6669275760650635d, -5.234055273962444E-8d,
+ }, // 971
+ {
+ +0.6674286127090454d, +3.856291077979099E-8d,
+ }, // 972
+ {
+ +0.6679295301437378d, -2.327375671320742E-9d,
+ }, // 973
+ {
+ +0.6684302091598511d, -5.555080534042001E-8d,
+ }, // 974
+ {
+ +0.6689305305480957d, -1.6471487337453832E-9d,
+ }, // 975
+ {
+ +0.6694306135177612d, +4.042486803683015E-8d,
+ }, // 976
+ {
+ +0.6699305772781372d, -4.8293856891818295E-8d,
+ }, // 977
+ {
+ +0.6704301834106445d, -2.9134931730784303E-8d,
+ }, // 978
+ {
+ +0.6709295511245728d, -2.1058207594753368E-8d,
+ }, // 979
+ {
+ +0.6714286804199219d, -2.3814619551682855E-8d,
+ }, // 980
+ {
+ +0.6719275712966919d, -3.7155475428252136E-8d,
+ }, // 981
+ {
+ +0.6724261045455933d, +5.8376834484391746E-8d,
+ }, // 982
+ {
+ +0.6729245185852051d, +2.4611679969129262E-8d,
+ }, // 983
+ {
+ +0.6734226942062378d, -1.899407107267079E-8d,
+ }, // 984
+ {
+ +0.6739205121994019d, +4.7016079464436395E-8d,
+ }, // 985
+ {
+ +0.6744182109832764d, -1.5529608026276525E-8d,
+ }, // 986
+ {
+ +0.6749155521392822d, +3.203391672602453E-8d,
+ }, // 987
+ {
+ +0.6754127740859985d, -4.8465821804075345E-8d,
+ }, // 988
+ {
+ +0.6759096384048462d, -1.8364507801369988E-8d,
+ }, // 989
+ {
+ +0.6764062643051147d, +3.3739397633046517E-9d,
+ }, // 990
+ {
+ +0.6769026517868042d, +1.6994526063192333E-8d,
+ }, // 991
+ {
+ +0.6773988008499146d, +2.2741891590028428E-8d,
+ }, // 992
+ {
+ +0.6778947114944458d, +2.0860312877435047E-8d,
+ }, // 993
+ {
+ +0.678390383720398d, +1.1593703222523284E-8d,
+ }, // 994
+ {
+ +0.678885817527771d, -4.814386594291911E-9d,
+ }, // 995
+ {
+ +0.6793810129165649d, -2.812076759125914E-8d,
+ }, // 996
+ {
+ +0.6798759698867798d, -5.808261186903479E-8d,
+ }, // 997
+ {
+ +0.680370569229126d, +2.4751837654582522E-8d,
+ }, // 998
+ {
+ +0.6808650493621826d, -1.7793890245755405E-8d,
+ }, // 999
+ {
+ +0.6813591718673706d, +5.294053246347931E-8d,
+ }, // 1000
+ {
+ +0.681853175163269d, -1.2220826223585654E-9d,
+ }, // 1001
+ {
+ +0.6823468208312988d, +5.8377876767612725E-8d,
+ }, // 1002
+ {
+ +0.6828403472900391d, -6.437492120743254E-9d,
+ }, // 1003
+ {
+ +0.6833335161209106d, +4.2990710043633113E-8d,
+ }, // 1004
+ {
+ +0.6838265657424927d, -3.1516131027023284E-8d,
+ }, // 1005
+ {
+ +0.684319257736206d, +8.70017386744679E-9d,
+ }, // 1006
+ {
+ +0.6848117113113403d, +4.466959125843237E-8d,
+ }, // 1007
+ {
+ +0.6853040456771851d, -4.25782656420497E-8d,
+ }, // 1008
+ {
+ +0.6857960224151611d, -1.4386267593671393E-8d,
+ }, // 1009
+ {
+ +0.6862877607345581d, +1.0274494061148778E-8d,
+ }, // 1010
+ {
+ +0.686779260635376d, +3.164186629229597E-8d,
+ }, // 1011
+ {
+ +0.6872705221176147d, +4.995334552140326E-8d,
+ }, // 1012
+ {
+ +0.687761664390564d, -5.3763211240398744E-8d,
+ }, // 1013
+ {
+ +0.6882524490356445d, -4.0852427502515625E-8d,
+ }, // 1014
+ {
+ +0.688742995262146d, -3.0287143914420064E-8d,
+ }, // 1015
+ {
+ +0.6892333030700684d, -2.183125937905008E-8d,
+ }, // 1016
+ {
+ +0.6897233724594116d, -1.524901992178814E-8d,
+ }, // 1017
+ {
+ +0.6902132034301758d, -1.0305018010328949E-8d,
+ }, // 1018
+ {
+ +0.6907027959823608d, -6.764191876212205E-9d,
+ }, // 1019
+ {
+ +0.6911921501159668d, -4.391824838015402E-9d,
+ }, // 1020
+ {
+ +0.6916812658309937d, -2.9535446262017846E-9d,
+ }, // 1021
+ {
+ +0.6921701431274414d, -2.2153227096187463E-9d,
+ }, // 1022
+ {
+ +0.6926587820053101d, -1.943473623641502E-9d,
+ }, // 1023
+ };
+
+ /** Class contains only static methods. */
+ private FastMathLiteralArrays() {}
+
+ /**
+ * Load "EXP_INT_A".
+ *
+ * @return a clone of the data array.
+ */
+ static double[] loadExpIntA() {
+ return EXP_INT_A.clone();
+ }
+
+ /**
+ * Load "EXP_INT_B".
+ *
+ * @return a clone of the data array.
+ */
+ static double[] loadExpIntB() {
+ return EXP_INT_B.clone();
+ }
+
+ /**
+ * Load "EXP_FRAC_A".
+ *
+ * @return a clone of the data array.
+ */
+ static double[] loadExpFracA() {
+ return EXP_FRAC_A.clone();
+ }
+
+ /**
+ * Load "EXP_FRAC_B".
+ *
+ * @return a clone of the data array.
+ */
+ static double[] loadExpFracB() {
+ return EXP_FRAC_B.clone();
+ }
+
+ /**
+ * Load "LN_MANT".
+ *
+ * @return a clone of the data array.
+ */
+ static double[][] loadLnMant() {
+ return LN_MANT.clone();
+ }
+}
diff --git a/src/main/java/org/apache/commons/math3/util/Incrementor.java b/src/main/java/org/apache/commons/math3/util/Incrementor.java
new file mode 100644
index 0000000..de9094e
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/util/Incrementor.java
@@ -0,0 +1,218 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math3.util;
+
+import org.apache.commons.math3.exception.MaxCountExceededException;
+import org.apache.commons.math3.exception.NullArgumentException;
+
+/**
+ * Utility that increments a counter until a maximum is reached, at which point, the instance will
+ * by default throw a {@link MaxCountExceededException}. However, the user is able to override this
+ * behaviour by defining a custom {@link MaxCountExceededCallback callback}, in order to e.g. select
+ * which exception must be thrown.
+ *
+ * @since 3.0
+ * @deprecated Use {@link IntegerSequence.Incrementor} instead.
+ */
+@Deprecated
+public class Incrementor {
+ /** Upper limit for the counter. */
+ private int maximalCount;
+
+ /** Current count. */
+ private int count = 0;
+
+ /** Function called at counter exhaustion. */
+ private final MaxCountExceededCallback maxCountCallback;
+
+ /**
+ * Default constructor. For the new instance to be useful, the maximal count must be set by
+ * calling {@link #setMaximalCount(int) setMaximalCount}.
+ */
+ public Incrementor() {
+ this(0);
+ }
+
+ /**
+ * Defines a maximal count.
+ *
+ * @param max Maximal count.
+ */
+ public Incrementor(int max) {
+ this(
+ max,
+ new MaxCountExceededCallback() {
+ /** {@inheritDoc} */
+ public void trigger(int max) throws MaxCountExceededException {
+ throw new MaxCountExceededException(max);
+ }
+ });
+ }
+
+ /**
+ * Defines a maximal count and a callback method to be triggered at counter exhaustion.
+ *
+ * @param max Maximal count.
+ * @param cb Function to be called when the maximal count has been reached.
+ * @throws NullArgumentException if {@code cb} is {@code null}
+ */
+ public Incrementor(int max, MaxCountExceededCallback cb) throws NullArgumentException {
+ if (cb == null) {
+ throw new NullArgumentException();
+ }
+ maximalCount = max;
+ maxCountCallback = cb;
+ }
+
+ /**
+ * Sets the upper limit for the counter. This does not automatically reset the current count to
+ * zero (see {@link #resetCount()}).
+ *
+ * @param max Upper limit of the counter.
+ */
+ public void setMaximalCount(int max) {
+ maximalCount = max;
+ }
+
+ /**
+ * Gets the upper limit of the counter.
+ *
+ * @return the counter upper limit.
+ */
+ public int getMaximalCount() {
+ return maximalCount;
+ }
+
+ /**
+ * Gets the current count.
+ *
+ * @return the current count.
+ */
+ public int getCount() {
+ return count;
+ }
+
+ /**
+ * Checks whether a single increment is allowed.
+ *
+ * @return {@code false} if the next call to {@link #incrementCount(int) incrementCount} will
+ * trigger a {@code MaxCountExceededException}, {@code true} otherwise.
+ */
+ public boolean canIncrement() {
+ return count < maximalCount;
+ }
+
+ /**
+ * Performs multiple increments. See the other {@link #incrementCount() incrementCount} method).
+ *
+ * @param value Number of increments.
+ * @throws MaxCountExceededException at counter exhaustion.
+ */
+ public void incrementCount(int value) throws MaxCountExceededException {
+ for (int i = 0; i < value; i++) {
+ incrementCount();
+ }
+ }
+
+ /**
+ * Adds one to the current iteration count. At counter exhaustion, this method will call the
+ * {@link MaxCountExceededCallback#trigger(int) trigger} method of the callback object passed to
+ * the {@link #Incrementor(int,MaxCountExceededCallback) constructor}. If not explictly set, a
+ * default callback is used that will throw a {@code MaxCountExceededException}.
+ *
+ * @throws MaxCountExceededException at counter exhaustion, unless a custom {@link
+ * MaxCountExceededCallback callback} has been set at construction.
+ */
+ public void incrementCount() throws MaxCountExceededException {
+ if (++count > maximalCount) {
+ maxCountCallback.trigger(maximalCount);
+ }
+ }
+
+ /** Resets the counter to 0. */
+ public void resetCount() {
+ count = 0;
+ }
+
+ /**
+ * Defines a method to be called at counter exhaustion. The {@link #trigger(int) trigger} method
+ * should usually throw an exception.
+ */
+ public interface MaxCountExceededCallback {
+ /**
+ * Function called when the maximal count has been reached.
+ *
+ * @param maximalCount Maximal count.
+ * @throws MaxCountExceededException at counter exhaustion
+ */
+ void trigger(int maximalCount) throws MaxCountExceededException;
+ }
+
+ /**
+ * Create an instance that delegates everything to a {@link IntegerSequence.Incrementor}.
+ *
+ * <p>This factory method is intended only as a temporary hack for internal use in Apache
+ * Commons Math 3.X series, when {@code Incrementor} is required in interface (as a return value
+ * or in protected fields). It should <em>not</em> be used in other cases. The {@link
+ * IntegerSequence.Incrementor} class should be used instead of {@code Incrementor}.
+ *
+ * <p>All methods are mirrored to the underlying {@link IntegerSequence.Incrementor}, as long as
+ * neither {@link #setMaximalCount(int)} nor {@link #resetCount()} are called. If one of these
+ * two methods is called, the created instance becomes independent of the {@link
+ * IntegerSequence.Incrementor} used at creation. The rationale is that {@link
+ * IntegerSequence.Incrementor} cannot change their maximal count and cannot be reset.
+ *
+ * @param incrementor wrapped {@link IntegerSequence.Incrementor}
+ * @return an incrementor wrapping an {@link IntegerSequence.Incrementor}
+ * @since 3.6
+ */
+ public static Incrementor wrap(final IntegerSequence.Incrementor incrementor) {
+ return new Incrementor() {
+
+ /** Underlying incrementor. */
+ private IntegerSequence.Incrementor delegate;
+
+ {
+ // set up matching values at initialization
+ delegate = incrementor;
+ super.setMaximalCount(delegate.getMaximalCount());
+ super.incrementCount(delegate.getCount());
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void setMaximalCount(int max) {
+ super.setMaximalCount(max);
+ delegate = delegate.withMaximalCount(max);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void resetCount() {
+ super.resetCount();
+ delegate = delegate.withStart(0);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void incrementCount() {
+ super.incrementCount();
+ delegate.increment();
+ }
+ };
+ }
+}
diff --git a/src/main/java/org/apache/commons/math3/util/IntegerSequence.java b/src/main/java/org/apache/commons/math3/util/IntegerSequence.java
new file mode 100644
index 0000000..7859ec3
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/util/IntegerSequence.java
@@ -0,0 +1,328 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math3.util;
+
+import org.apache.commons.math3.exception.MathUnsupportedOperationException;
+import org.apache.commons.math3.exception.MaxCountExceededException;
+import org.apache.commons.math3.exception.NotStrictlyPositiveException;
+import org.apache.commons.math3.exception.NullArgumentException;
+import org.apache.commons.math3.exception.ZeroException;
+
+import java.util.Iterator;
+
+/**
+ * Provides a sequence of integers.
+ *
+ * @since 3.6
+ */
+public class IntegerSequence {
+ /** Utility class contains only static methods. */
+ private IntegerSequence() {}
+
+ /**
+ * Creates a sequence {@code [start .. end]}. It calls {@link #range(int,int,int) range(start,
+ * end, 1)}.
+ *
+ * @param start First value of the range.
+ * @param end Last value of the range.
+ * @return a range.
+ */
+ public static Range range(int start, int end) {
+ return range(start, end, 1);
+ }
+
+ /**
+ * Creates a sequence \( a_i, i < 0 <= n \) where \( a_i = start + i * step \) and \( n \) is
+ * such that \( a_n <= max \) and \( a_{n+1} > max \).
+ *
+ * @param start First value of the range.
+ * @param max Last value of the range that satisfies the above construction rule.
+ * @param step Increment.
+ * @return a range.
+ */
+ public static Range range(final int start, final int max, final int step) {
+ return new Range(start, max, step);
+ }
+
+ /** Generates a sequence of integers. */
+ public static class Range implements Iterable<Integer> {
+ /** Number of integers contained in this range. */
+ private final int size;
+
+ /** First value. */
+ private final int start;
+
+ /** Final value. */
+ private final int max;
+
+ /** Increment. */
+ private final int step;
+
+ /**
+ * Creates a sequence \( a_i, i < 0 <= n \) where \( a_i = start + i * step \) and \( n \)
+ * is such that \( a_n <= max \) and \( a_{n+1} > max \).
+ *
+ * @param start First value of the range.
+ * @param max Last value of the range that satisfies the above construction rule.
+ * @param step Increment.
+ */
+ public Range(int start, int max, int step) {
+ this.start = start;
+ this.max = max;
+ this.step = step;
+
+ final int s = (max - start) / step + 1;
+ this.size = s < 0 ? 0 : s;
+ }
+
+ /**
+ * Gets the number of elements contained in the range.
+ *
+ * @return the size of the range.
+ */
+ public int size() {
+ return size;
+ }
+
+ /** {@inheritDoc} */
+ public Iterator<Integer> iterator() {
+ return Incrementor.create()
+ .withStart(start)
+ .withMaximalCount(max + (step > 0 ? 1 : -1))
+ .withIncrement(step);
+ }
+ }
+
+ /**
+ * Utility that increments a counter until a maximum is reached, at which point, the instance
+ * will by default throw a {@link MaxCountExceededException}. However, the user is able to
+ * override this behaviour by defining a custom {@link MaxCountExceededCallback callback}, in
+ * order to e.g. select which exception must be thrown.
+ */
+ public static class Incrementor implements Iterator<Integer> {
+ /** Default callback. */
+ private static final MaxCountExceededCallback CALLBACK =
+ new MaxCountExceededCallback() {
+ /** {@inheritDoc} */
+ public void trigger(int max) throws MaxCountExceededException {
+ throw new MaxCountExceededException(max);
+ }
+ };
+
+ /** Initial value the counter. */
+ private final int init;
+
+ /** Upper limit for the counter. */
+ private final int maximalCount;
+
+ /** Increment. */
+ private final int increment;
+
+ /** Function called at counter exhaustion. */
+ private final MaxCountExceededCallback maxCountCallback;
+
+ /** Current count. */
+ private int count = 0;
+
+ /**
+ * Defines a method to be called at counter exhaustion. The {@link #trigger(int) trigger}
+ * method should usually throw an exception.
+ */
+ public interface MaxCountExceededCallback {
+ /**
+ * Function called when the maximal count has been reached.
+ *
+ * @param maximalCount Maximal count.
+ * @throws MaxCountExceededException at counter exhaustion
+ */
+ void trigger(int maximalCount) throws MaxCountExceededException;
+ }
+
+ /**
+ * Creates an incrementor. The counter will be exhausted either when {@code max} is reached
+ * or when {@code nTimes} increments have been performed.
+ *
+ * @param start Initial value.
+ * @param max Maximal count.
+ * @param step Increment.
+ * @param cb Function to be called when the maximal count has been reached.
+ * @throws NullArgumentException if {@code cb} is {@code null}.
+ */
+ private Incrementor(int start, int max, int step, MaxCountExceededCallback cb)
+ throws NullArgumentException {
+ if (cb == null) {
+ throw new NullArgumentException();
+ }
+ this.init = start;
+ this.maximalCount = max;
+ this.increment = step;
+ this.maxCountCallback = cb;
+ this.count = start;
+ }
+
+ /**
+ * Factory method that creates a default instance. The initial and maximal values are set to
+ * 0. For the new instance to be useful, the maximal count must be set by calling {@link
+ * #withMaximalCount(int) withMaximalCount}.
+ *
+ * @return an new instance.
+ */
+ public static Incrementor create() {
+ return new Incrementor(0, 0, 1, CALLBACK);
+ }
+
+ /**
+ * Creates a new instance with a given initial value. The counter is reset to the initial
+ * value.
+ *
+ * @param start Initial value of the counter.
+ * @return a new instance.
+ */
+ public Incrementor withStart(int start) {
+ return new Incrementor(start, this.maximalCount, this.increment, this.maxCountCallback);
+ }
+
+ /**
+ * Creates a new instance with a given maximal count. The counter is reset to the initial
+ * value.
+ *
+ * @param max Maximal count.
+ * @return a new instance.
+ */
+ public Incrementor withMaximalCount(int max) {
+ return new Incrementor(this.init, max, this.increment, this.maxCountCallback);
+ }
+
+ /**
+ * Creates a new instance with a given increment. The counter is reset to the initial value.
+ *
+ * @param step Increment.
+ * @return a new instance.
+ */
+ public Incrementor withIncrement(int step) {
+ if (step == 0) {
+ throw new ZeroException();
+ }
+ return new Incrementor(this.init, this.maximalCount, step, this.maxCountCallback);
+ }
+
+ /**
+ * Creates a new instance with a given callback. The counter is reset to the initial value.
+ *
+ * @param cb Callback to be called at counter exhaustion.
+ * @return a new instance.
+ */
+ public Incrementor withCallback(MaxCountExceededCallback cb) {
+ return new Incrementor(this.init, this.maximalCount, this.increment, cb);
+ }
+
+ /**
+ * Gets the upper limit of the counter.
+ *
+ * @return the counter upper limit.
+ */
+ public int getMaximalCount() {
+ return maximalCount;
+ }
+
+ /**
+ * Gets the current count.
+ *
+ * @return the current count.
+ */
+ public int getCount() {
+ return count;
+ }
+
+ /**
+ * Checks whether incrementing the counter {@code nTimes} is allowed.
+ *
+ * @return {@code false} if calling {@link #increment()} will trigger a {@code
+ * MaxCountExceededException}, {@code true} otherwise.
+ */
+ public boolean canIncrement() {
+ return canIncrement(1);
+ }
+
+ /**
+ * Checks whether incrementing the counter several times is allowed.
+ *
+ * @param nTimes Number of increments.
+ * @return {@code false} if calling {@link #increment(int) increment(nTimes)} would call the
+ * {@link MaxCountExceededCallback callback} {@code true} otherwise.
+ */
+ public boolean canIncrement(int nTimes) {
+ final int finalCount = count + nTimes * increment;
+ return increment < 0 ? finalCount > maximalCount : finalCount < maximalCount;
+ }
+
+ /**
+ * Performs multiple increments.
+ *
+ * @param nTimes Number of increments.
+ * @throws MaxCountExceededException at counter exhaustion.
+ * @throws NotStrictlyPositiveException if {@code nTimes <= 0}.
+ * @see #increment()
+ */
+ public void increment(int nTimes) throws MaxCountExceededException {
+ if (nTimes <= 0) {
+ throw new NotStrictlyPositiveException(nTimes);
+ }
+
+ if (!canIncrement(0)) {
+ maxCountCallback.trigger(maximalCount);
+ }
+ count += nTimes * increment;
+ }
+
+ /**
+ * Adds the increment value to the current iteration count. At counter exhaustion, this
+ * method will call the {@link MaxCountExceededCallback#trigger(int) trigger} method of the
+ * callback object passed to the {@link #withCallback(MaxCountExceededCallback)} method. If
+ * not explicitly set, a default callback is used that will throw a {@code
+ * MaxCountExceededException}.
+ *
+ * @throws MaxCountExceededException at counter exhaustion, unless a custom {@link
+ * MaxCountExceededCallback callback} has been set.
+ * @see #increment(int)
+ */
+ public void increment() throws MaxCountExceededException {
+ increment(1);
+ }
+
+ /** {@inheritDoc} */
+ public boolean hasNext() {
+ return canIncrement(0);
+ }
+
+ /** {@inheritDoc} */
+ public Integer next() {
+ final int value = count;
+ increment();
+ return value;
+ }
+
+ /**
+ * Not applicable.
+ *
+ * @throws MathUnsupportedOperationException
+ */
+ public void remove() {
+ throw new MathUnsupportedOperationException();
+ }
+ }
+}
diff --git a/src/main/java/org/apache/commons/math3/util/IterationEvent.java b/src/main/java/org/apache/commons/math3/util/IterationEvent.java
new file mode 100644
index 0000000..5cbca66
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/util/IterationEvent.java
@@ -0,0 +1,52 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math3.util;
+
+import java.util.EventObject;
+
+/**
+ * The root class from which all events occurring while running an {@link IterationManager} should
+ * be derived.
+ */
+public class IterationEvent extends EventObject {
+ /** */
+ private static final long serialVersionUID = 20120128L;
+
+ /** The number of iterations performed so far. */
+ private final int iterations;
+
+ /**
+ * Creates a new instance of this class.
+ *
+ * @param source the iterative algorithm on which the event initially occurred
+ * @param iterations the number of iterations performed at the time {@code this} event is
+ * created
+ */
+ public IterationEvent(final Object source, final int iterations) {
+ super(source);
+ this.iterations = iterations;
+ }
+
+ /**
+ * Returns the number of iterations performed at the time {@code this} event is created.
+ *
+ * @return the number of iterations performed
+ */
+ public int getIterations() {
+ return iterations;
+ }
+}
diff --git a/src/main/java/org/apache/commons/math3/util/IterationListener.java b/src/main/java/org/apache/commons/math3/util/IterationListener.java
new file mode 100644
index 0000000..3064acf
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/util/IterationListener.java
@@ -0,0 +1,52 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math3.util;
+
+import java.util.EventListener;
+
+/** The listener interface for receiving events occurring in an iterative algorithm. */
+public interface IterationListener extends EventListener {
+ /**
+ * Invoked after completion of the initial phase of the iterative algorithm (prior to the main
+ * iteration loop).
+ *
+ * @param e The {@link IterationEvent} object.
+ */
+ void initializationPerformed(IterationEvent e);
+
+ /**
+ * Invoked each time an iteration is completed (in the main iteration loop).
+ *
+ * @param e The {@link IterationEvent} object.
+ */
+ void iterationPerformed(IterationEvent e);
+
+ /**
+ * Invoked each time a new iteration is completed (in the main iteration loop).
+ *
+ * @param e The {@link IterationEvent} object.
+ */
+ void iterationStarted(IterationEvent e);
+
+ /**
+ * Invoked after completion of the operations which occur after breaking out of the main
+ * iteration loop.
+ *
+ * @param e The {@link IterationEvent} object.
+ */
+ void terminationPerformed(IterationEvent e);
+}
diff --git a/src/main/java/org/apache/commons/math3/util/IterationManager.java b/src/main/java/org/apache/commons/math3/util/IterationManager.java
new file mode 100644
index 0000000..83f350b
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/util/IterationManager.java
@@ -0,0 +1,191 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math3.util;
+
+import org.apache.commons.math3.exception.MaxCountExceededException;
+
+import java.util.Collection;
+import java.util.concurrent.CopyOnWriteArrayList;
+
+/**
+ * This abstract class provides a general framework for managing iterative algorithms. The maximum
+ * number of iterations can be set, and methods are provided to monitor the current iteration count.
+ * A lightweight event framework is also provided.
+ */
+public class IterationManager {
+
+ /** Keeps a count of the number of iterations. */
+ private IntegerSequence.Incrementor iterations;
+
+ /** The collection of all listeners attached to this iterative algorithm. */
+ private final Collection<IterationListener> listeners;
+
+ /**
+ * Creates a new instance of this class.
+ *
+ * @param maxIterations the maximum number of iterations
+ */
+ public IterationManager(final int maxIterations) {
+ this.iterations = IntegerSequence.Incrementor.create().withMaximalCount(maxIterations);
+ this.listeners = new CopyOnWriteArrayList<IterationListener>();
+ }
+
+ /**
+ * Creates a new instance of this class.
+ *
+ * @param maxIterations the maximum number of iterations
+ * @param callBack the function to be called when the maximum number of iterations has been
+ * reached
+ * @throws org.apache.commons.math3.exception.NullArgumentException if {@code callBack} is
+ * {@code null}
+ * @since 3.1
+ * @deprecated as of 3.6, replaced with {@link #IterationManager(int,
+ * org.apache.commons.math3.util.IntegerSequence.Incrementor.MaxCountExceededCallback)}
+ */
+ @Deprecated
+ public IterationManager(
+ final int maxIterations, final Incrementor.MaxCountExceededCallback callBack) {
+ this(
+ maxIterations,
+ new IntegerSequence.Incrementor.MaxCountExceededCallback() {
+ /** {@inheritDoc} */
+ public void trigger(final int maximalCount) throws MaxCountExceededException {
+ callBack.trigger(maximalCount);
+ }
+ });
+ }
+
+ /**
+ * Creates a new instance of this class.
+ *
+ * @param maxIterations the maximum number of iterations
+ * @param callBack the function to be called when the maximum number of iterations has been
+ * reached
+ * @throws org.apache.commons.math3.exception.NullArgumentException if {@code callBack} is
+ * {@code null}
+ * @since 3.6
+ */
+ public IterationManager(
+ final int maxIterations,
+ final IntegerSequence.Incrementor.MaxCountExceededCallback callBack) {
+ this.iterations =
+ IntegerSequence.Incrementor.create()
+ .withMaximalCount(maxIterations)
+ .withCallback(callBack);
+ this.listeners = new CopyOnWriteArrayList<IterationListener>();
+ }
+
+ /**
+ * Attaches a listener to this manager.
+ *
+ * @param listener A {@code IterationListener} object.
+ */
+ public void addIterationListener(final IterationListener listener) {
+ listeners.add(listener);
+ }
+
+ /**
+ * Informs all registered listeners that the initial phase (prior to the main iteration loop)
+ * has been completed.
+ *
+ * @param e The {@link IterationEvent} object.
+ */
+ public void fireInitializationEvent(final IterationEvent e) {
+ for (IterationListener l : listeners) {
+ l.initializationPerformed(e);
+ }
+ }
+
+ /**
+ * Informs all registered listeners that a new iteration (in the main iteration loop) has been
+ * performed.
+ *
+ * @param e The {@link IterationEvent} object.
+ */
+ public void fireIterationPerformedEvent(final IterationEvent e) {
+ for (IterationListener l : listeners) {
+ l.iterationPerformed(e);
+ }
+ }
+
+ /**
+ * Informs all registered listeners that a new iteration (in the main iteration loop) has been
+ * started.
+ *
+ * @param e The {@link IterationEvent} object.
+ */
+ public void fireIterationStartedEvent(final IterationEvent e) {
+ for (IterationListener l : listeners) {
+ l.iterationStarted(e);
+ }
+ }
+
+ /**
+ * Informs all registered listeners that the final phase (post-iterations) has been completed.
+ *
+ * @param e The {@link IterationEvent} object.
+ */
+ public void fireTerminationEvent(final IterationEvent e) {
+ for (IterationListener l : listeners) {
+ l.terminationPerformed(e);
+ }
+ }
+
+ /**
+ * Returns the number of iterations of this solver, 0 if no iterations has been performed yet.
+ *
+ * @return the number of iterations.
+ */
+ public int getIterations() {
+ return iterations.getCount();
+ }
+
+ /**
+ * Returns the maximum number of iterations.
+ *
+ * @return the maximum number of iterations.
+ */
+ public int getMaxIterations() {
+ return iterations.getMaximalCount();
+ }
+
+ /**
+ * Increments the iteration count by one, and throws an exception if the maximum number of
+ * iterations is reached. This method should be called at the beginning of a new iteration.
+ *
+ * @throws MaxCountExceededException if the maximum number of iterations is reached.
+ */
+ public void incrementIterationCount() throws MaxCountExceededException {
+ iterations.increment();
+ }
+
+ /**
+ * Removes the specified iteration listener from the list of listeners currently attached to
+ * {@code this} object. Attempting to remove a listener which was <em>not</em> previously
+ * registered does not cause any error.
+ *
+ * @param listener The {@link IterationListener} to be removed.
+ */
+ public void removeIterationListener(final IterationListener listener) {
+ listeners.remove(listener);
+ }
+
+ /** Sets the iteration count to 0. This method must be called during the initial phase. */
+ public void resetIterationCount() {
+ iterations = iterations.withStart(0);
+ }
+}
diff --git a/src/main/java/org/apache/commons/math3/util/KthSelector.java b/src/main/java/org/apache/commons/math3/util/KthSelector.java
new file mode 100644
index 0000000..ed0f1d5
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/util/KthSelector.java
@@ -0,0 +1,154 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math3.util;
+
+import org.apache.commons.math3.exception.NullArgumentException;
+
+import java.io.Serializable;
+import java.util.Arrays;
+
+/**
+ * A Simple K<sup>th</sup> selector implementation to pick up the K<sup>th</sup> ordered element
+ * from a work array containing the input numbers.
+ *
+ * @since 3.4
+ */
+public class KthSelector implements Serializable {
+
+ /** Serializable UID. */
+ private static final long serialVersionUID = 20140713L;
+
+ /** Minimum selection size for insertion sort rather than selection. */
+ private static final int MIN_SELECT_SIZE = 15;
+
+ /** A {@link PivotingStrategyInterface} used for pivoting */
+ private final PivotingStrategyInterface pivotingStrategy;
+
+ /** Constructor with default {@link MedianOf3PivotingStrategy median of 3} pivoting strategy */
+ public KthSelector() {
+ this.pivotingStrategy = new MedianOf3PivotingStrategy();
+ }
+
+ /**
+ * Constructor with specified pivoting strategy
+ *
+ * @param pivotingStrategy pivoting strategy to use
+ * @throws NullArgumentException when pivotingStrategy is null
+ * @see MedianOf3PivotingStrategy
+ * @see RandomPivotingStrategy
+ * @see CentralPivotingStrategy
+ */
+ public KthSelector(final PivotingStrategyInterface pivotingStrategy)
+ throws NullArgumentException {
+ MathUtils.checkNotNull(pivotingStrategy);
+ this.pivotingStrategy = pivotingStrategy;
+ }
+
+ /**
+ * Get the pivotin strategy.
+ *
+ * @return pivoting strategy
+ */
+ public PivotingStrategyInterface getPivotingStrategy() {
+ return pivotingStrategy;
+ }
+
+ /**
+ * Select K<sup>th</sup> value in the array.
+ *
+ * @param work work array to use to find out the K<sup>th</sup> value
+ * @param pivotsHeap cached pivots heap that can be used for efficient estimation
+ * @param k the index whose value in the array is of interest
+ * @return K<sup>th</sup> value
+ */
+ public double select(final double[] work, final int[] pivotsHeap, final int k) {
+ int begin = 0;
+ int end = work.length;
+ int node = 0;
+ final boolean usePivotsHeap = pivotsHeap != null;
+ while (end - begin > MIN_SELECT_SIZE) {
+ final int pivot;
+
+ if (usePivotsHeap && node < pivotsHeap.length && pivotsHeap[node] >= 0) {
+ // the pivot has already been found in a previous call
+ // and the array has already been partitioned around it
+ pivot = pivotsHeap[node];
+ } else {
+ // select a pivot and partition work array around it
+ pivot = partition(work, begin, end, pivotingStrategy.pivotIndex(work, begin, end));
+ if (usePivotsHeap && node < pivotsHeap.length) {
+ pivotsHeap[node] = pivot;
+ }
+ }
+
+ if (k == pivot) {
+ // the pivot was exactly the element we wanted
+ return work[k];
+ } else if (k < pivot) {
+ // the element is in the left partition
+ end = pivot;
+ node = FastMath.min(2 * node + 1, usePivotsHeap ? pivotsHeap.length : end);
+ } else {
+ // the element is in the right partition
+ begin = pivot + 1;
+ node = FastMath.min(2 * node + 2, usePivotsHeap ? pivotsHeap.length : end);
+ }
+ }
+ Arrays.sort(work, begin, end);
+ return work[k];
+ }
+
+ /**
+ * Partition an array slice around a pivot.Partitioning exchanges array elements such that all
+ * elements smaller than pivot are before it and all elements larger than pivot are after it.
+ *
+ * @param work work array
+ * @param begin index of the first element of the slice of work array
+ * @param end index after the last element of the slice of work array
+ * @param pivot initial index of the pivot
+ * @return index of the pivot after partition
+ */
+ private int partition(final double[] work, final int begin, final int end, final int pivot) {
+
+ final double value = work[pivot];
+ work[pivot] = work[begin];
+
+ int i = begin + 1;
+ int j = end - 1;
+ while (i < j) {
+ while (i < j && work[j] > value) {
+ --j;
+ }
+ while (i < j && work[i] < value) {
+ ++i;
+ }
+
+ if (i < j) {
+ final double tmp = work[i];
+ work[i++] = work[j];
+ work[j--] = tmp;
+ }
+ }
+
+ if (i >= end || work[i] > value) {
+ --i;
+ }
+ work[begin] = work[i];
+ work[i] = value;
+ return i;
+ }
+}
diff --git a/src/main/java/org/apache/commons/math3/util/MathArrays.java b/src/main/java/org/apache/commons/math3/util/MathArrays.java
new file mode 100644
index 0000000..968e626
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/util/MathArrays.java
@@ -0,0 +1,1912 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math3.util;
+
+import org.apache.commons.math3.Field;
+import org.apache.commons.math3.distribution.UniformIntegerDistribution;
+import org.apache.commons.math3.exception.DimensionMismatchException;
+import org.apache.commons.math3.exception.MathArithmeticException;
+import org.apache.commons.math3.exception.MathIllegalArgumentException;
+import org.apache.commons.math3.exception.MathInternalError;
+import org.apache.commons.math3.exception.NoDataException;
+import org.apache.commons.math3.exception.NonMonotonicSequenceException;
+import org.apache.commons.math3.exception.NotANumberException;
+import org.apache.commons.math3.exception.NotPositiveException;
+import org.apache.commons.math3.exception.NotStrictlyPositiveException;
+import org.apache.commons.math3.exception.NullArgumentException;
+import org.apache.commons.math3.exception.NumberIsTooLargeException;
+import org.apache.commons.math3.exception.util.LocalizedFormats;
+import org.apache.commons.math3.random.RandomGenerator;
+import org.apache.commons.math3.random.Well19937c;
+
+import java.lang.reflect.Array;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.Iterator;
+import java.util.List;
+import java.util.TreeSet;
+
+/**
+ * Arrays utilities.
+ *
+ * @since 3.0
+ */
+public class MathArrays {
+
+ /** Private constructor. */
+ private MathArrays() {}
+
+ /**
+ * Real-valued function that operate on an array or a part of it.
+ *
+ * @since 3.1
+ */
+ public interface Function {
+ /**
+ * Operates on an entire array.
+ *
+ * @param array Array to operate on.
+ * @return the result of the operation.
+ */
+ double evaluate(double[] array);
+
+ /**
+ * @param array Array to operate on.
+ * @param startIndex Index of the first element to take into account.
+ * @param numElements Number of elements to take into account.
+ * @return the result of the operation.
+ */
+ double evaluate(double[] array, int startIndex, int numElements);
+ }
+
+ /**
+ * Create a copy of an array scaled by a value.
+ *
+ * @param arr Array to scale.
+ * @param val Scalar.
+ * @return scaled copy of array with each entry multiplied by val.
+ * @since 3.2
+ */
+ public static double[] scale(double val, final double[] arr) {
+ double[] newArr = new double[arr.length];
+ for (int i = 0; i < arr.length; i++) {
+ newArr[i] = arr[i] * val;
+ }
+ return newArr;
+ }
+
+ /**
+ * Multiply each element of an array by a value.
+ *
+ * <p>The array is modified in place (no copy is created).
+ *
+ * @param arr Array to scale
+ * @param val Scalar
+ * @since 3.2
+ */
+ public static void scaleInPlace(double val, final double[] arr) {
+ for (int i = 0; i < arr.length; i++) {
+ arr[i] *= val;
+ }
+ }
+
+ /**
+ * Creates an array whose contents will be the element-by-element addition of the arguments.
+ *
+ * @param a First term of the addition.
+ * @param b Second term of the addition.
+ * @return a new array {@code r} where {@code r[i] = a[i] + b[i]}.
+ * @throws DimensionMismatchException if the array lengths differ.
+ * @since 3.1
+ */
+ public static double[] ebeAdd(double[] a, double[] b) throws DimensionMismatchException {
+ checkEqualLength(a, b);
+
+ final double[] result = a.clone();
+ for (int i = 0; i < a.length; i++) {
+ result[i] += b[i];
+ }
+ return result;
+ }
+
+ /**
+ * Creates an array whose contents will be the element-by-element subtraction of the second
+ * argument from the first.
+ *
+ * @param a First term.
+ * @param b Element to be subtracted.
+ * @return a new array {@code r} where {@code r[i] = a[i] - b[i]}.
+ * @throws DimensionMismatchException if the array lengths differ.
+ * @since 3.1
+ */
+ public static double[] ebeSubtract(double[] a, double[] b) throws DimensionMismatchException {
+ checkEqualLength(a, b);
+
+ final double[] result = a.clone();
+ for (int i = 0; i < a.length; i++) {
+ result[i] -= b[i];
+ }
+ return result;
+ }
+
+ /**
+ * Creates an array whose contents will be the element-by-element multiplication of the
+ * arguments.
+ *
+ * @param a First factor of the multiplication.
+ * @param b Second factor of the multiplication.
+ * @return a new array {@code r} where {@code r[i] = a[i] * b[i]}.
+ * @throws DimensionMismatchException if the array lengths differ.
+ * @since 3.1
+ */
+ public static double[] ebeMultiply(double[] a, double[] b) throws DimensionMismatchException {
+ checkEqualLength(a, b);
+
+ final double[] result = a.clone();
+ for (int i = 0; i < a.length; i++) {
+ result[i] *= b[i];
+ }
+ return result;
+ }
+
+ /**
+ * Creates an array whose contents will be the element-by-element division of the first argument
+ * by the second.
+ *
+ * @param a Numerator of the division.
+ * @param b Denominator of the division.
+ * @return a new array {@code r} where {@code r[i] = a[i] / b[i]}.
+ * @throws DimensionMismatchException if the array lengths differ.
+ * @since 3.1
+ */
+ public static double[] ebeDivide(double[] a, double[] b) throws DimensionMismatchException {
+ checkEqualLength(a, b);
+
+ final double[] result = a.clone();
+ for (int i = 0; i < a.length; i++) {
+ result[i] /= b[i];
+ }
+ return result;
+ }
+
+ /**
+ * Calculates the L<sub>1</sub> (sum of abs) distance between two points.
+ *
+ * @param p1 the first point
+ * @param p2 the second point
+ * @return the L<sub>1</sub> distance between the two points
+ * @throws DimensionMismatchException if the array lengths differ.
+ */
+ public static double distance1(double[] p1, double[] p2) throws DimensionMismatchException {
+ checkEqualLength(p1, p2);
+ double sum = 0;
+ for (int i = 0; i < p1.length; i++) {
+ sum += FastMath.abs(p1[i] - p2[i]);
+ }
+ return sum;
+ }
+
+ /**
+ * Calculates the L<sub>1</sub> (sum of abs) distance between two points.
+ *
+ * @param p1 the first point
+ * @param p2 the second point
+ * @return the L<sub>1</sub> distance between the two points
+ * @throws DimensionMismatchException if the array lengths differ.
+ */
+ public static int distance1(int[] p1, int[] p2) throws DimensionMismatchException {
+ checkEqualLength(p1, p2);
+ int sum = 0;
+ for (int i = 0; i < p1.length; i++) {
+ sum += FastMath.abs(p1[i] - p2[i]);
+ }
+ return sum;
+ }
+
+ /**
+ * Calculates the L<sub>2</sub> (Euclidean) distance between two points.
+ *
+ * @param p1 the first point
+ * @param p2 the second point
+ * @return the L<sub>2</sub> distance between the two points
+ * @throws DimensionMismatchException if the array lengths differ.
+ */
+ public static double distance(double[] p1, double[] p2) throws DimensionMismatchException {
+ checkEqualLength(p1, p2);
+ double sum = 0;
+ for (int i = 0; i < p1.length; i++) {
+ final double dp = p1[i] - p2[i];
+ sum += dp * dp;
+ }
+ return FastMath.sqrt(sum);
+ }
+
+ /**
+ * Calculates the cosine of the angle between two vectors.
+ *
+ * @param v1 Cartesian coordinates of the first vector.
+ * @param v2 Cartesian coordinates of the second vector.
+ * @return the cosine of the angle between the vectors.
+ * @since 3.6
+ */
+ public static double cosAngle(double[] v1, double[] v2) {
+ return linearCombination(v1, v2) / (safeNorm(v1) * safeNorm(v2));
+ }
+
+ /**
+ * Calculates the L<sub>2</sub> (Euclidean) distance between two points.
+ *
+ * @param p1 the first point
+ * @param p2 the second point
+ * @return the L<sub>2</sub> distance between the two points
+ * @throws DimensionMismatchException if the array lengths differ.
+ */
+ public static double distance(int[] p1, int[] p2) throws DimensionMismatchException {
+ checkEqualLength(p1, p2);
+ double sum = 0;
+ for (int i = 0; i < p1.length; i++) {
+ final double dp = p1[i] - p2[i];
+ sum += dp * dp;
+ }
+ return FastMath.sqrt(sum);
+ }
+
+ /**
+ * Calculates the L<sub>&infin;</sub> (max of abs) distance between two points.
+ *
+ * @param p1 the first point
+ * @param p2 the second point
+ * @return the L<sub>&infin;</sub> distance between the two points
+ * @throws DimensionMismatchException if the array lengths differ.
+ */
+ public static double distanceInf(double[] p1, double[] p2) throws DimensionMismatchException {
+ checkEqualLength(p1, p2);
+ double max = 0;
+ for (int i = 0; i < p1.length; i++) {
+ max = FastMath.max(max, FastMath.abs(p1[i] - p2[i]));
+ }
+ return max;
+ }
+
+ /**
+ * Calculates the L<sub>&infin;</sub> (max of abs) distance between two points.
+ *
+ * @param p1 the first point
+ * @param p2 the second point
+ * @return the L<sub>&infin;</sub> distance between the two points
+ * @throws DimensionMismatchException if the array lengths differ.
+ */
+ public static int distanceInf(int[] p1, int[] p2) throws DimensionMismatchException {
+ checkEqualLength(p1, p2);
+ int max = 0;
+ for (int i = 0; i < p1.length; i++) {
+ max = FastMath.max(max, FastMath.abs(p1[i] - p2[i]));
+ }
+ return max;
+ }
+
+ /** Specification of ordering direction. */
+ public enum OrderDirection {
+ /** Constant for increasing direction. */
+ INCREASING,
+ /** Constant for decreasing direction. */
+ DECREASING
+ }
+
+ /**
+ * Check that an array is monotonically increasing or decreasing.
+ *
+ * @param <T> the type of the elements in the specified array
+ * @param val Values.
+ * @param dir Ordering direction.
+ * @param strict Whether the order should be strict.
+ * @return {@code true} if sorted, {@code false} otherwise.
+ */
+ public static <T extends Comparable<? super T>> boolean isMonotonic(
+ T[] val, OrderDirection dir, boolean strict) {
+ T previous = val[0];
+ final int max = val.length;
+ for (int i = 1; i < max; i++) {
+ final int comp;
+ switch (dir) {
+ case INCREASING:
+ comp = previous.compareTo(val[i]);
+ if (strict) {
+ if (comp >= 0) {
+ return false;
+ }
+ } else {
+ if (comp > 0) {
+ return false;
+ }
+ }
+ break;
+ case DECREASING:
+ comp = val[i].compareTo(previous);
+ if (strict) {
+ if (comp >= 0) {
+ return false;
+ }
+ } else {
+ if (comp > 0) {
+ return false;
+ }
+ }
+ break;
+ default:
+ // Should never happen.
+ throw new MathInternalError();
+ }
+
+ previous = val[i];
+ }
+ return true;
+ }
+
+ /**
+ * Check that an array is monotonically increasing or decreasing.
+ *
+ * @param val Values.
+ * @param dir Ordering direction.
+ * @param strict Whether the order should be strict.
+ * @return {@code true} if sorted, {@code false} otherwise.
+ */
+ public static boolean isMonotonic(double[] val, OrderDirection dir, boolean strict) {
+ return checkOrder(val, dir, strict, false);
+ }
+
+ /**
+ * Check that both arrays have the same length.
+ *
+ * @param a Array.
+ * @param b Array.
+ * @param abort Whether to throw an exception if the check fails.
+ * @return {@code true} if the arrays have the same length.
+ * @throws DimensionMismatchException if the lengths differ and {@code abort} is {@code true}.
+ * @since 3.6
+ */
+ public static boolean checkEqualLength(double[] a, double[] b, boolean abort) {
+ if (a.length == b.length) {
+ return true;
+ } else {
+ if (abort) {
+ throw new DimensionMismatchException(a.length, b.length);
+ }
+ return false;
+ }
+ }
+
+ /**
+ * Check that both arrays have the same length.
+ *
+ * @param a Array.
+ * @param b Array.
+ * @throws DimensionMismatchException if the lengths differ.
+ * @since 3.6
+ */
+ public static void checkEqualLength(double[] a, double[] b) {
+ checkEqualLength(a, b, true);
+ }
+
+ /**
+ * Check that both arrays have the same length.
+ *
+ * @param a Array.
+ * @param b Array.
+ * @param abort Whether to throw an exception if the check fails.
+ * @return {@code true} if the arrays have the same length.
+ * @throws DimensionMismatchException if the lengths differ and {@code abort} is {@code true}.
+ * @since 3.6
+ */
+ public static boolean checkEqualLength(int[] a, int[] b, boolean abort) {
+ if (a.length == b.length) {
+ return true;
+ } else {
+ if (abort) {
+ throw new DimensionMismatchException(a.length, b.length);
+ }
+ return false;
+ }
+ }
+
+ /**
+ * Check that both arrays have the same length.
+ *
+ * @param a Array.
+ * @param b Array.
+ * @throws DimensionMismatchException if the lengths differ.
+ * @since 3.6
+ */
+ public static void checkEqualLength(int[] a, int[] b) {
+ checkEqualLength(a, b, true);
+ }
+
+ /**
+ * Check that the given array is sorted.
+ *
+ * @param val Values.
+ * @param dir Ordering direction.
+ * @param strict Whether the order should be strict.
+ * @param abort Whether to throw an exception if the check fails.
+ * @return {@code true} if the array is sorted.
+ * @throws NonMonotonicSequenceException if the array is not sorted and {@code abort} is {@code
+ * true}.
+ */
+ public static boolean checkOrder(
+ double[] val, OrderDirection dir, boolean strict, boolean abort)
+ throws NonMonotonicSequenceException {
+ double previous = val[0];
+ final int max = val.length;
+
+ int index;
+ ITEM:
+ for (index = 1; index < max; index++) {
+ switch (dir) {
+ case INCREASING:
+ if (strict) {
+ if (val[index] <= previous) {
+ break ITEM;
+ }
+ } else {
+ if (val[index] < previous) {
+ break ITEM;
+ }
+ }
+ break;
+ case DECREASING:
+ if (strict) {
+ if (val[index] >= previous) {
+ break ITEM;
+ }
+ } else {
+ if (val[index] > previous) {
+ break ITEM;
+ }
+ }
+ break;
+ default:
+ // Should never happen.
+ throw new MathInternalError();
+ }
+
+ previous = val[index];
+ }
+
+ if (index == max) {
+ // Loop completed.
+ return true;
+ }
+
+ // Loop early exit means wrong ordering.
+ if (abort) {
+ throw new NonMonotonicSequenceException(val[index], previous, index, dir, strict);
+ } else {
+ return false;
+ }
+ }
+
+ /**
+ * Check that the given array is sorted.
+ *
+ * @param val Values.
+ * @param dir Ordering direction.
+ * @param strict Whether the order should be strict.
+ * @throws NonMonotonicSequenceException if the array is not sorted.
+ * @since 2.2
+ */
+ public static void checkOrder(double[] val, OrderDirection dir, boolean strict)
+ throws NonMonotonicSequenceException {
+ checkOrder(val, dir, strict, true);
+ }
+
+ /**
+ * Check that the given array is sorted in strictly increasing order.
+ *
+ * @param val Values.
+ * @throws NonMonotonicSequenceException if the array is not sorted.
+ * @since 2.2
+ */
+ public static void checkOrder(double[] val) throws NonMonotonicSequenceException {
+ checkOrder(val, OrderDirection.INCREASING, true);
+ }
+
+ /**
+ * Throws DimensionMismatchException if the input array is not rectangular.
+ *
+ * @param in array to be tested
+ * @throws NullArgumentException if input array is null
+ * @throws DimensionMismatchException if input array is not rectangular
+ * @since 3.1
+ */
+ public static void checkRectangular(final long[][] in)
+ throws NullArgumentException, DimensionMismatchException {
+ MathUtils.checkNotNull(in);
+ for (int i = 1; i < in.length; i++) {
+ if (in[i].length != in[0].length) {
+ throw new DimensionMismatchException(
+ LocalizedFormats.DIFFERENT_ROWS_LENGTHS, in[i].length, in[0].length);
+ }
+ }
+ }
+
+ /**
+ * Check that all entries of the input array are strictly positive.
+ *
+ * @param in Array to be tested
+ * @throws NotStrictlyPositiveException if any entries of the array are not strictly positive.
+ * @since 3.1
+ */
+ public static void checkPositive(final double[] in) throws NotStrictlyPositiveException {
+ for (int i = 0; i < in.length; i++) {
+ if (in[i] <= 0) {
+ throw new NotStrictlyPositiveException(in[i]);
+ }
+ }
+ }
+
+ /**
+ * Check that no entry of the input array is {@code NaN}.
+ *
+ * @param in Array to be tested.
+ * @throws NotANumberException if an entry is {@code NaN}.
+ * @since 3.4
+ */
+ public static void checkNotNaN(final double[] in) throws NotANumberException {
+ for (int i = 0; i < in.length; i++) {
+ if (Double.isNaN(in[i])) {
+ throw new NotANumberException();
+ }
+ }
+ }
+
+ /**
+ * Check that all entries of the input array are >= 0.
+ *
+ * @param in Array to be tested
+ * @throws NotPositiveException if any array entries are less than 0.
+ * @since 3.1
+ */
+ public static void checkNonNegative(final long[] in) throws NotPositiveException {
+ for (int i = 0; i < in.length; i++) {
+ if (in[i] < 0) {
+ throw new NotPositiveException(in[i]);
+ }
+ }
+ }
+
+ /**
+ * Check all entries of the input array are >= 0.
+ *
+ * @param in Array to be tested
+ * @throws NotPositiveException if any array entries are less than 0.
+ * @since 3.1
+ */
+ public static void checkNonNegative(final long[][] in) throws NotPositiveException {
+ for (int i = 0; i < in.length; i++) {
+ for (int j = 0; j < in[i].length; j++) {
+ if (in[i][j] < 0) {
+ throw new NotPositiveException(in[i][j]);
+ }
+ }
+ }
+ }
+
+ /**
+ * Returns the Cartesian norm (2-norm), handling both overflow and underflow. Translation of the
+ * minpack enorm subroutine.
+ *
+ * <p>The redistribution policy for MINPACK is available <a
+ * href="http://www.netlib.org/minpack/disclaimer">here</a>, for convenience, it is reproduced
+ * below.
+ *
+ * <table border="0" width="80%" cellpadding="10" align="center" bgcolor="#E0E0E0">
+ * <tr><td>
+ * Minpack Copyright Notice (1999) University of Chicago.
+ * All rights reserved
+ * </td></tr>
+ * <tr><td>
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * <ol>
+ * <li>Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.</li>
+ * <li>Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided
+ * with the distribution.</li>
+ * <li>The end-user documentation included with the redistribution, if any,
+ * must include the following acknowledgment:
+ * {@code This product includes software developed by the University of
+ * Chicago, as Operator of Argonne National Laboratory.}
+ * Alternately, this acknowledgment may appear in the software itself,
+ * if and wherever such third-party acknowledgments normally appear.</li>
+ * <li><strong>WARRANTY DISCLAIMER. THE SOFTWARE IS SUPPLIED "AS IS"
+ * WITHOUT WARRANTY OF ANY KIND. THE COPYRIGHT HOLDER, THE
+ * UNITED STATES, THE UNITED STATES DEPARTMENT OF ENERGY, AND
+ * THEIR EMPLOYEES: (1) DISCLAIM ANY WARRANTIES, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO ANY IMPLIED WARRANTIES
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, TITLE
+ * OR NON-INFRINGEMENT, (2) DO NOT ASSUME ANY LEGAL LIABILITY
+ * OR RESPONSIBILITY FOR THE ACCURACY, COMPLETENESS, OR
+ * USEFULNESS OF THE SOFTWARE, (3) DO NOT REPRESENT THAT USE OF
+ * THE SOFTWARE WOULD NOT INFRINGE PRIVATELY OWNED RIGHTS, (4)
+ * DO NOT WARRANT THAT THE SOFTWARE WILL FUNCTION
+ * UNINTERRUPTED, THAT IT IS ERROR-FREE OR THAT ANY ERRORS WILL
+ * BE CORRECTED.</strong></li>
+ * <li><strong>LIMITATION OF LIABILITY. IN NO EVENT WILL THE COPYRIGHT
+ * HOLDER, THE UNITED STATES, THE UNITED STATES DEPARTMENT OF
+ * ENERGY, OR THEIR EMPLOYEES: BE LIABLE FOR ANY INDIRECT,
+ * INCIDENTAL, CONSEQUENTIAL, SPECIAL OR PUNITIVE DAMAGES OF
+ * ANY KIND OR NATURE, INCLUDING BUT NOT LIMITED TO LOSS OF
+ * PROFITS OR LOSS OF DATA, FOR ANY REASON WHATSOEVER, WHETHER
+ * SUCH LIABILITY IS ASSERTED ON THE BASIS OF CONTRACT, TORT
+ * (INCLUDING NEGLIGENCE OR STRICT LIABILITY), OR OTHERWISE,
+ * EVEN IF ANY OF SAID PARTIES HAS BEEN WARNED OF THE
+ * POSSIBILITY OF SUCH LOSS OR DAMAGES.</strong></li>
+ * <ol></td></tr>
+ * </table>
+ *
+ * @param v Vector of doubles.
+ * @return the 2-norm of the vector.
+ * @since 2.2
+ */
+ public static double safeNorm(double[] v) {
+ double rdwarf = 3.834e-20;
+ double rgiant = 1.304e+19;
+ double s1 = 0;
+ double s2 = 0;
+ double s3 = 0;
+ double x1max = 0;
+ double x3max = 0;
+ double floatn = v.length;
+ double agiant = rgiant / floatn;
+ for (int i = 0; i < v.length; i++) {
+ double xabs = FastMath.abs(v[i]);
+ if (xabs < rdwarf || xabs > agiant) {
+ if (xabs > rdwarf) {
+ if (xabs > x1max) {
+ double r = x1max / xabs;
+ s1 = 1 + s1 * r * r;
+ x1max = xabs;
+ } else {
+ double r = xabs / x1max;
+ s1 += r * r;
+ }
+ } else {
+ if (xabs > x3max) {
+ double r = x3max / xabs;
+ s3 = 1 + s3 * r * r;
+ x3max = xabs;
+ } else {
+ if (xabs != 0) {
+ double r = xabs / x3max;
+ s3 += r * r;
+ }
+ }
+ }
+ } else {
+ s2 += xabs * xabs;
+ }
+ }
+ double norm;
+ if (s1 != 0) {
+ norm = x1max * Math.sqrt(s1 + (s2 / x1max) / x1max);
+ } else {
+ if (s2 == 0) {
+ norm = x3max * Math.sqrt(s3);
+ } else {
+ if (s2 >= x3max) {
+ norm = Math.sqrt(s2 * (1 + (x3max / s2) * (x3max * s3)));
+ } else {
+ norm = Math.sqrt(x3max * ((s2 / x3max) + (x3max * s3)));
+ }
+ }
+ }
+ return norm;
+ }
+
+ /** A helper data structure holding a double and an integer value. */
+ private static class PairDoubleInteger {
+ /** Key */
+ private final double key;
+
+ /** Value */
+ private final int value;
+
+ /**
+ * @param key Key.
+ * @param value Value.
+ */
+ PairDoubleInteger(double key, int value) {
+ this.key = key;
+ this.value = value;
+ }
+
+ /**
+ * @return the key.
+ */
+ public double getKey() {
+ return key;
+ }
+
+ /**
+ * @return the value.
+ */
+ public int getValue() {
+ return value;
+ }
+ }
+
+ /**
+ * Sort an array in ascending order in place and perform the same reordering of entries on other
+ * arrays. For example, if {@code x = [3, 1, 2], y = [1, 2, 3]} and {@code z = [0, 5, 7]}, then
+ * {@code sortInPlace(x, y, z)} will update {@code x} to {@code [1, 2, 3]}, {@code y} to {@code
+ * [2, 3, 1]} and {@code z} to {@code [5, 7, 0]}.
+ *
+ * @param x Array to be sorted and used as a pattern for permutation of the other arrays.
+ * @param yList Set of arrays whose permutations of entries will follow those performed on
+ * {@code x}.
+ * @throws DimensionMismatchException if any {@code y} is not the same size as {@code x}.
+ * @throws NullArgumentException if {@code x} or any {@code y} is null.
+ * @since 3.0
+ */
+ public static void sortInPlace(double[] x, double[]... yList)
+ throws DimensionMismatchException, NullArgumentException {
+ sortInPlace(x, OrderDirection.INCREASING, yList);
+ }
+
+ /**
+ * Sort an array in place and perform the same reordering of entries on other arrays. This
+ * method works the same as the other {@link #sortInPlace(double[], double[][]) sortInPlace}
+ * method, but allows the order of the sort to be provided in the {@code dir} parameter.
+ *
+ * @param x Array to be sorted and used as a pattern for permutation of the other arrays.
+ * @param dir Order direction.
+ * @param yList Set of arrays whose permutations of entries will follow those performed on
+ * {@code x}.
+ * @throws DimensionMismatchException if any {@code y} is not the same size as {@code x}.
+ * @throws NullArgumentException if {@code x} or any {@code y} is null
+ * @since 3.0
+ */
+ public static void sortInPlace(double[] x, final OrderDirection dir, double[]... yList)
+ throws NullArgumentException, DimensionMismatchException {
+
+ // Consistency checks.
+ if (x == null) {
+ throw new NullArgumentException();
+ }
+
+ final int yListLen = yList.length;
+ final int len = x.length;
+
+ for (int j = 0; j < yListLen; j++) {
+ final double[] y = yList[j];
+ if (y == null) {
+ throw new NullArgumentException();
+ }
+ if (y.length != len) {
+ throw new DimensionMismatchException(y.length, len);
+ }
+ }
+
+ // Associate each abscissa "x[i]" with its index "i".
+ final List<PairDoubleInteger> list = new ArrayList<PairDoubleInteger>(len);
+ for (int i = 0; i < len; i++) {
+ list.add(new PairDoubleInteger(x[i], i));
+ }
+
+ // Create comparators for increasing and decreasing orders.
+ final Comparator<PairDoubleInteger> comp =
+ dir == MathArrays.OrderDirection.INCREASING
+ ? new Comparator<PairDoubleInteger>() {
+ /** {@inheritDoc} */
+ public int compare(PairDoubleInteger o1, PairDoubleInteger o2) {
+ return Double.compare(o1.getKey(), o2.getKey());
+ }
+ }
+ : new Comparator<PairDoubleInteger>() {
+ /** {@inheritDoc} */
+ public int compare(PairDoubleInteger o1, PairDoubleInteger o2) {
+ return Double.compare(o2.getKey(), o1.getKey());
+ }
+ };
+
+ // Sort.
+ Collections.sort(list, comp);
+
+ // Modify the original array so that its elements are in
+ // the prescribed order.
+ // Retrieve indices of original locations.
+ final int[] indices = new int[len];
+ for (int i = 0; i < len; i++) {
+ final PairDoubleInteger e = list.get(i);
+ x[i] = e.getKey();
+ indices[i] = e.getValue();
+ }
+
+ // In each of the associated arrays, move the
+ // elements to their new location.
+ for (int j = 0; j < yListLen; j++) {
+ // Input array will be modified in place.
+ final double[] yInPlace = yList[j];
+ final double[] yOrig = yInPlace.clone();
+
+ for (int i = 0; i < len; i++) {
+ yInPlace[i] = yOrig[indices[i]];
+ }
+ }
+ }
+
+ /**
+ * Creates a copy of the {@code source} array.
+ *
+ * @param source Array to be copied.
+ * @return the copied array.
+ */
+ public static int[] copyOf(int[] source) {
+ return copyOf(source, source.length);
+ }
+
+ /**
+ * Creates a copy of the {@code source} array.
+ *
+ * @param source Array to be copied.
+ * @return the copied array.
+ */
+ public static double[] copyOf(double[] source) {
+ return copyOf(source, source.length);
+ }
+
+ /**
+ * Creates a copy of the {@code source} array.
+ *
+ * @param source Array to be copied.
+ * @param len Number of entries to copy. If smaller then the source length, the copy will be
+ * truncated, if larger it will padded with zeroes.
+ * @return the copied array.
+ */
+ public static int[] copyOf(int[] source, int len) {
+ final int[] output = new int[len];
+ System.arraycopy(source, 0, output, 0, FastMath.min(len, source.length));
+ return output;
+ }
+
+ /**
+ * Creates a copy of the {@code source} array.
+ *
+ * @param source Array to be copied.
+ * @param len Number of entries to copy. If smaller then the source length, the copy will be
+ * truncated, if larger it will padded with zeroes.
+ * @return the copied array.
+ */
+ public static double[] copyOf(double[] source, int len) {
+ final double[] output = new double[len];
+ System.arraycopy(source, 0, output, 0, FastMath.min(len, source.length));
+ return output;
+ }
+
+ /**
+ * Creates a copy of the {@code source} array.
+ *
+ * @param source Array to be copied.
+ * @param from Initial index of the range to be copied, inclusive.
+ * @param to Final index of the range to be copied, exclusive. (This index may lie outside the
+ * array.)
+ * @return the copied array.
+ */
+ public static double[] copyOfRange(double[] source, int from, int to) {
+ final int len = to - from;
+ final double[] output = new double[len];
+ System.arraycopy(source, from, output, 0, FastMath.min(len, source.length - from));
+ return output;
+ }
+
+ /**
+ * Compute a linear combination accurately. This method computes the sum of the products <code>
+ * a<sub>i</sub> b<sub>i</sub></code> to high accuracy. It does so by using specific
+ * multiplication and addition algorithms to preserve accuracy and reduce cancellation effects.
+ * <br>
+ * It is based on the 2005 paper <a
+ * href="http://citeseerx.ist.psu.edu/viewdoc/summary?doi=10.1.1.2.1547">Accurate Sum and Dot
+ * Product</a> by Takeshi Ogita, Siegfried M. Rump, and Shin'ichi Oishi published in SIAM J.
+ * Sci. Comput.
+ *
+ * @param a Factors.
+ * @param b Factors.
+ * @return <code>&Sigma;<sub>i</sub> a<sub>i</sub> b<sub>i</sub></code>.
+ * @throws DimensionMismatchException if arrays dimensions don't match
+ */
+ public static double linearCombination(final double[] a, final double[] b)
+ throws DimensionMismatchException {
+ checkEqualLength(a, b);
+ final int len = a.length;
+
+ if (len == 1) {
+ // Revert to scalar multiplication.
+ return a[0] * b[0];
+ }
+
+ final double[] prodHigh = new double[len];
+ double prodLowSum = 0;
+
+ for (int i = 0; i < len; i++) {
+ final double ai = a[i];
+ final double aHigh =
+ Double.longBitsToDouble(Double.doubleToRawLongBits(ai) & ((-1L) << 27));
+ final double aLow = ai - aHigh;
+
+ final double bi = b[i];
+ final double bHigh =
+ Double.longBitsToDouble(Double.doubleToRawLongBits(bi) & ((-1L) << 27));
+ final double bLow = bi - bHigh;
+ prodHigh[i] = ai * bi;
+ final double prodLow =
+ aLow * bLow - (((prodHigh[i] - aHigh * bHigh) - aLow * bHigh) - aHigh * bLow);
+ prodLowSum += prodLow;
+ }
+
+ final double prodHighCur = prodHigh[0];
+ double prodHighNext = prodHigh[1];
+ double sHighPrev = prodHighCur + prodHighNext;
+ double sPrime = sHighPrev - prodHighNext;
+ double sLowSum = (prodHighNext - (sHighPrev - sPrime)) + (prodHighCur - sPrime);
+
+ final int lenMinusOne = len - 1;
+ for (int i = 1; i < lenMinusOne; i++) {
+ prodHighNext = prodHigh[i + 1];
+ final double sHighCur = sHighPrev + prodHighNext;
+ sPrime = sHighCur - prodHighNext;
+ sLowSum += (prodHighNext - (sHighCur - sPrime)) + (sHighPrev - sPrime);
+ sHighPrev = sHighCur;
+ }
+
+ double result = sHighPrev + (prodLowSum + sLowSum);
+
+ if (Double.isNaN(result)) {
+ // either we have split infinite numbers or some coefficients were NaNs,
+ // just rely on the naive implementation and let IEEE754 handle this
+ result = 0;
+ for (int i = 0; i < len; ++i) {
+ result += a[i] * b[i];
+ }
+ }
+
+ return result;
+ }
+
+ /**
+ * Compute a linear combination accurately.
+ *
+ * <p>This method computes a<sub>1</sub>&times;b<sub>1</sub> + a<sub>2</sub>&times;b<sub>2</sub>
+ * to high accuracy. It does so by using specific multiplication and addition algorithms to
+ * preserve accuracy and reduce cancellation effects. It is based on the 2005 paper <a
+ * href="http://citeseerx.ist.psu.edu/viewdoc/summary?doi=10.1.1.2.1547">Accurate Sum and Dot
+ * Product</a> by Takeshi Ogita, Siegfried M. Rump, and Shin'ichi Oishi published in SIAM J.
+ * Sci. Comput.
+ *
+ * @param a1 first factor of the first term
+ * @param b1 second factor of the first term
+ * @param a2 first factor of the second term
+ * @param b2 second factor of the second term
+ * @return a<sub>1</sub>&times;b<sub>1</sub> + a<sub>2</sub>&times;b<sub>2</sub>
+ * @see #linearCombination(double, double, double, double, double, double)
+ * @see #linearCombination(double, double, double, double, double, double, double, double)
+ */
+ public static double linearCombination(
+ final double a1, final double b1, final double a2, final double b2) {
+
+ // the code below is split in many additions/subtractions that may
+ // appear redundant. However, they should NOT be simplified, as they
+ // use IEEE754 floating point arithmetic rounding properties.
+ // The variable naming conventions are that xyzHigh contains the most significant
+ // bits of xyz and xyzLow contains its least significant bits. So theoretically
+ // xyz is the sum xyzHigh + xyzLow, but in many cases below, this sum cannot
+ // be represented in only one double precision number so we preserve two numbers
+ // to hold it as long as we can, combining the high and low order bits together
+ // only at the end, after cancellation may have occurred on high order bits
+
+ // split a1 and b1 as one 26 bits number and one 27 bits number
+ final double a1High =
+ Double.longBitsToDouble(Double.doubleToRawLongBits(a1) & ((-1L) << 27));
+ final double a1Low = a1 - a1High;
+ final double b1High =
+ Double.longBitsToDouble(Double.doubleToRawLongBits(b1) & ((-1L) << 27));
+ final double b1Low = b1 - b1High;
+
+ // accurate multiplication a1 * b1
+ final double prod1High = a1 * b1;
+ final double prod1Low =
+ a1Low * b1Low - (((prod1High - a1High * b1High) - a1Low * b1High) - a1High * b1Low);
+
+ // split a2 and b2 as one 26 bits number and one 27 bits number
+ final double a2High =
+ Double.longBitsToDouble(Double.doubleToRawLongBits(a2) & ((-1L) << 27));
+ final double a2Low = a2 - a2High;
+ final double b2High =
+ Double.longBitsToDouble(Double.doubleToRawLongBits(b2) & ((-1L) << 27));
+ final double b2Low = b2 - b2High;
+
+ // accurate multiplication a2 * b2
+ final double prod2High = a2 * b2;
+ final double prod2Low =
+ a2Low * b2Low - (((prod2High - a2High * b2High) - a2Low * b2High) - a2High * b2Low);
+
+ // accurate addition a1 * b1 + a2 * b2
+ final double s12High = prod1High + prod2High;
+ final double s12Prime = s12High - prod2High;
+ final double s12Low = (prod2High - (s12High - s12Prime)) + (prod1High - s12Prime);
+
+ // final rounding, s12 may have suffered many cancellations, we try
+ // to recover some bits from the extra words we have saved up to now
+ double result = s12High + (prod1Low + prod2Low + s12Low);
+
+ if (Double.isNaN(result)) {
+ // either we have split infinite numbers or some coefficients were NaNs,
+ // just rely on the naive implementation and let IEEE754 handle this
+ result = a1 * b1 + a2 * b2;
+ }
+
+ return result;
+ }
+
+ /**
+ * Compute a linear combination accurately.
+ *
+ * <p>This method computes a<sub>1</sub>&times;b<sub>1</sub> + a<sub>2</sub>&times;b<sub>2</sub>
+ * + a<sub>3</sub>&times;b<sub>3</sub> to high accuracy. It does so by using specific
+ * multiplication and addition algorithms to preserve accuracy and reduce cancellation effects.
+ * It is based on the 2005 paper <a
+ * href="http://citeseerx.ist.psu.edu/viewdoc/summary?doi=10.1.1.2.1547">Accurate Sum and Dot
+ * Product</a> by Takeshi Ogita, Siegfried M. Rump, and Shin'ichi Oishi published in SIAM J.
+ * Sci. Comput.
+ *
+ * @param a1 first factor of the first term
+ * @param b1 second factor of the first term
+ * @param a2 first factor of the second term
+ * @param b2 second factor of the second term
+ * @param a3 first factor of the third term
+ * @param b3 second factor of the third term
+ * @return a<sub>1</sub>&times;b<sub>1</sub> + a<sub>2</sub>&times;b<sub>2</sub> +
+ * a<sub>3</sub>&times;b<sub>3</sub>
+ * @see #linearCombination(double, double, double, double)
+ * @see #linearCombination(double, double, double, double, double, double, double, double)
+ */
+ public static double linearCombination(
+ final double a1,
+ final double b1,
+ final double a2,
+ final double b2,
+ final double a3,
+ final double b3) {
+
+ // the code below is split in many additions/subtractions that may
+ // appear redundant. However, they should NOT be simplified, as they
+ // do use IEEE754 floating point arithmetic rounding properties.
+ // The variables naming conventions are that xyzHigh contains the most significant
+ // bits of xyz and xyzLow contains its least significant bits. So theoretically
+ // xyz is the sum xyzHigh + xyzLow, but in many cases below, this sum cannot
+ // be represented in only one double precision number so we preserve two numbers
+ // to hold it as long as we can, combining the high and low order bits together
+ // only at the end, after cancellation may have occurred on high order bits
+
+ // split a1 and b1 as one 26 bits number and one 27 bits number
+ final double a1High =
+ Double.longBitsToDouble(Double.doubleToRawLongBits(a1) & ((-1L) << 27));
+ final double a1Low = a1 - a1High;
+ final double b1High =
+ Double.longBitsToDouble(Double.doubleToRawLongBits(b1) & ((-1L) << 27));
+ final double b1Low = b1 - b1High;
+
+ // accurate multiplication a1 * b1
+ final double prod1High = a1 * b1;
+ final double prod1Low =
+ a1Low * b1Low - (((prod1High - a1High * b1High) - a1Low * b1High) - a1High * b1Low);
+
+ // split a2 and b2 as one 26 bits number and one 27 bits number
+ final double a2High =
+ Double.longBitsToDouble(Double.doubleToRawLongBits(a2) & ((-1L) << 27));
+ final double a2Low = a2 - a2High;
+ final double b2High =
+ Double.longBitsToDouble(Double.doubleToRawLongBits(b2) & ((-1L) << 27));
+ final double b2Low = b2 - b2High;
+
+ // accurate multiplication a2 * b2
+ final double prod2High = a2 * b2;
+ final double prod2Low =
+ a2Low * b2Low - (((prod2High - a2High * b2High) - a2Low * b2High) - a2High * b2Low);
+
+ // split a3 and b3 as one 26 bits number and one 27 bits number
+ final double a3High =
+ Double.longBitsToDouble(Double.doubleToRawLongBits(a3) & ((-1L) << 27));
+ final double a3Low = a3 - a3High;
+ final double b3High =
+ Double.longBitsToDouble(Double.doubleToRawLongBits(b3) & ((-1L) << 27));
+ final double b3Low = b3 - b3High;
+
+ // accurate multiplication a3 * b3
+ final double prod3High = a3 * b3;
+ final double prod3Low =
+ a3Low * b3Low - (((prod3High - a3High * b3High) - a3Low * b3High) - a3High * b3Low);
+
+ // accurate addition a1 * b1 + a2 * b2
+ final double s12High = prod1High + prod2High;
+ final double s12Prime = s12High - prod2High;
+ final double s12Low = (prod2High - (s12High - s12Prime)) + (prod1High - s12Prime);
+
+ // accurate addition a1 * b1 + a2 * b2 + a3 * b3
+ final double s123High = s12High + prod3High;
+ final double s123Prime = s123High - prod3High;
+ final double s123Low = (prod3High - (s123High - s123Prime)) + (s12High - s123Prime);
+
+ // final rounding, s123 may have suffered many cancellations, we try
+ // to recover some bits from the extra words we have saved up to now
+ double result = s123High + (prod1Low + prod2Low + prod3Low + s12Low + s123Low);
+
+ if (Double.isNaN(result)) {
+ // either we have split infinite numbers or some coefficients were NaNs,
+ // just rely on the naive implementation and let IEEE754 handle this
+ result = a1 * b1 + a2 * b2 + a3 * b3;
+ }
+
+ return result;
+ }
+
+ /**
+ * Compute a linear combination accurately.
+ *
+ * <p>This method computes a<sub>1</sub>&times;b<sub>1</sub> + a<sub>2</sub>&times;b<sub>2</sub>
+ * + a<sub>3</sub>&times;b<sub>3</sub> + a<sub>4</sub>&times;b<sub>4</sub> to high accuracy. It
+ * does so by using specific multiplication and addition algorithms to preserve accuracy and
+ * reduce cancellation effects. It is based on the 2005 paper <a
+ * href="http://citeseerx.ist.psu.edu/viewdoc/summary?doi=10.1.1.2.1547">Accurate Sum and Dot
+ * Product</a> by Takeshi Ogita, Siegfried M. Rump, and Shin'ichi Oishi published in SIAM J.
+ * Sci. Comput.
+ *
+ * @param a1 first factor of the first term
+ * @param b1 second factor of the first term
+ * @param a2 first factor of the second term
+ * @param b2 second factor of the second term
+ * @param a3 first factor of the third term
+ * @param b3 second factor of the third term
+ * @param a4 first factor of the third term
+ * @param b4 second factor of the third term
+ * @return a<sub>1</sub>&times;b<sub>1</sub> + a<sub>2</sub>&times;b<sub>2</sub> +
+ * a<sub>3</sub>&times;b<sub>3</sub> + a<sub>4</sub>&times;b<sub>4</sub>
+ * @see #linearCombination(double, double, double, double)
+ * @see #linearCombination(double, double, double, double, double, double)
+ */
+ public static double linearCombination(
+ final double a1,
+ final double b1,
+ final double a2,
+ final double b2,
+ final double a3,
+ final double b3,
+ final double a4,
+ final double b4) {
+
+ // the code below is split in many additions/subtractions that may
+ // appear redundant. However, they should NOT be simplified, as they
+ // do use IEEE754 floating point arithmetic rounding properties.
+ // The variables naming conventions are that xyzHigh contains the most significant
+ // bits of xyz and xyzLow contains its least significant bits. So theoretically
+ // xyz is the sum xyzHigh + xyzLow, but in many cases below, this sum cannot
+ // be represented in only one double precision number so we preserve two numbers
+ // to hold it as long as we can, combining the high and low order bits together
+ // only at the end, after cancellation may have occurred on high order bits
+
+ // split a1 and b1 as one 26 bits number and one 27 bits number
+ final double a1High =
+ Double.longBitsToDouble(Double.doubleToRawLongBits(a1) & ((-1L) << 27));
+ final double a1Low = a1 - a1High;
+ final double b1High =
+ Double.longBitsToDouble(Double.doubleToRawLongBits(b1) & ((-1L) << 27));
+ final double b1Low = b1 - b1High;
+
+ // accurate multiplication a1 * b1
+ final double prod1High = a1 * b1;
+ final double prod1Low =
+ a1Low * b1Low - (((prod1High - a1High * b1High) - a1Low * b1High) - a1High * b1Low);
+
+ // split a2 and b2 as one 26 bits number and one 27 bits number
+ final double a2High =
+ Double.longBitsToDouble(Double.doubleToRawLongBits(a2) & ((-1L) << 27));
+ final double a2Low = a2 - a2High;
+ final double b2High =
+ Double.longBitsToDouble(Double.doubleToRawLongBits(b2) & ((-1L) << 27));
+ final double b2Low = b2 - b2High;
+
+ // accurate multiplication a2 * b2
+ final double prod2High = a2 * b2;
+ final double prod2Low =
+ a2Low * b2Low - (((prod2High - a2High * b2High) - a2Low * b2High) - a2High * b2Low);
+
+ // split a3 and b3 as one 26 bits number and one 27 bits number
+ final double a3High =
+ Double.longBitsToDouble(Double.doubleToRawLongBits(a3) & ((-1L) << 27));
+ final double a3Low = a3 - a3High;
+ final double b3High =
+ Double.longBitsToDouble(Double.doubleToRawLongBits(b3) & ((-1L) << 27));
+ final double b3Low = b3 - b3High;
+
+ // accurate multiplication a3 * b3
+ final double prod3High = a3 * b3;
+ final double prod3Low =
+ a3Low * b3Low - (((prod3High - a3High * b3High) - a3Low * b3High) - a3High * b3Low);
+
+ // split a4 and b4 as one 26 bits number and one 27 bits number
+ final double a4High =
+ Double.longBitsToDouble(Double.doubleToRawLongBits(a4) & ((-1L) << 27));
+ final double a4Low = a4 - a4High;
+ final double b4High =
+ Double.longBitsToDouble(Double.doubleToRawLongBits(b4) & ((-1L) << 27));
+ final double b4Low = b4 - b4High;
+
+ // accurate multiplication a4 * b4
+ final double prod4High = a4 * b4;
+ final double prod4Low =
+ a4Low * b4Low - (((prod4High - a4High * b4High) - a4Low * b4High) - a4High * b4Low);
+
+ // accurate addition a1 * b1 + a2 * b2
+ final double s12High = prod1High + prod2High;
+ final double s12Prime = s12High - prod2High;
+ final double s12Low = (prod2High - (s12High - s12Prime)) + (prod1High - s12Prime);
+
+ // accurate addition a1 * b1 + a2 * b2 + a3 * b3
+ final double s123High = s12High + prod3High;
+ final double s123Prime = s123High - prod3High;
+ final double s123Low = (prod3High - (s123High - s123Prime)) + (s12High - s123Prime);
+
+ // accurate addition a1 * b1 + a2 * b2 + a3 * b3 + a4 * b4
+ final double s1234High = s123High + prod4High;
+ final double s1234Prime = s1234High - prod4High;
+ final double s1234Low = (prod4High - (s1234High - s1234Prime)) + (s123High - s1234Prime);
+
+ // final rounding, s1234 may have suffered many cancellations, we try
+ // to recover some bits from the extra words we have saved up to now
+ double result =
+ s1234High
+ + (prod1Low + prod2Low + prod3Low + prod4Low + s12Low + s123Low + s1234Low);
+
+ if (Double.isNaN(result)) {
+ // either we have split infinite numbers or some coefficients were NaNs,
+ // just rely on the naive implementation and let IEEE754 handle this
+ result = a1 * b1 + a2 * b2 + a3 * b3 + a4 * b4;
+ }
+
+ return result;
+ }
+
+ /**
+ * Returns true iff both arguments are null or have same dimensions and all their elements are
+ * equal as defined by {@link Precision#equals(float,float)}.
+ *
+ * @param x first array
+ * @param y second array
+ * @return true if the values are both null or have same dimension and equal elements.
+ */
+ public static boolean equals(float[] x, float[] y) {
+ if ((x == null) || (y == null)) {
+ return !((x == null) ^ (y == null));
+ }
+ if (x.length != y.length) {
+ return false;
+ }
+ for (int i = 0; i < x.length; ++i) {
+ if (!Precision.equals(x[i], y[i])) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ /**
+ * Returns true iff both arguments are null or have same dimensions and all their elements are
+ * equal as defined by {@link Precision#equalsIncludingNaN(double,double) this method}.
+ *
+ * @param x first array
+ * @param y second array
+ * @return true if the values are both null or have same dimension and equal elements
+ * @since 2.2
+ */
+ public static boolean equalsIncludingNaN(float[] x, float[] y) {
+ if ((x == null) || (y == null)) {
+ return !((x == null) ^ (y == null));
+ }
+ if (x.length != y.length) {
+ return false;
+ }
+ for (int i = 0; i < x.length; ++i) {
+ if (!Precision.equalsIncludingNaN(x[i], y[i])) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ /**
+ * Returns {@code true} iff both arguments are {@code null} or have same dimensions and all
+ * their elements are equal as defined by {@link Precision#equals(double,double)}.
+ *
+ * @param x First array.
+ * @param y Second array.
+ * @return {@code true} if the values are both {@code null} or have same dimension and equal
+ * elements.
+ */
+ public static boolean equals(double[] x, double[] y) {
+ if ((x == null) || (y == null)) {
+ return !((x == null) ^ (y == null));
+ }
+ if (x.length != y.length) {
+ return false;
+ }
+ for (int i = 0; i < x.length; ++i) {
+ if (!Precision.equals(x[i], y[i])) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ /**
+ * Returns {@code true} iff both arguments are {@code null} or have same dimensions and all
+ * their elements are equal as defined by {@link Precision#equalsIncludingNaN(double,double)
+ * this method}.
+ *
+ * @param x First array.
+ * @param y Second array.
+ * @return {@code true} if the values are both {@code null} or have same dimension and equal
+ * elements.
+ * @since 2.2
+ */
+ public static boolean equalsIncludingNaN(double[] x, double[] y) {
+ if ((x == null) || (y == null)) {
+ return !((x == null) ^ (y == null));
+ }
+ if (x.length != y.length) {
+ return false;
+ }
+ for (int i = 0; i < x.length; ++i) {
+ if (!Precision.equalsIncludingNaN(x[i], y[i])) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ /**
+ * Normalizes an array to make it sum to a specified value. Returns the result of the
+ * transformation
+ *
+ * <pre>
+ * x |-> x * normalizedSum / sum
+ * </pre>
+ *
+ * applied to each non-NaN element x of the input array, where sum is the sum of the non-NaN
+ * entries in the input array.
+ *
+ * <p>Throws IllegalArgumentException if {@code normalizedSum} is infinite or NaN and
+ * ArithmeticException if the input array contains any infinite elements or sums to 0.
+ *
+ * <p>Ignores (i.e., copies unchanged to the output array) NaNs in the input array.
+ *
+ * @param values Input array to be normalized
+ * @param normalizedSum Target sum for the normalized array
+ * @return the normalized array.
+ * @throws MathArithmeticException if the input array contains infinite elements or sums to
+ * zero.
+ * @throws MathIllegalArgumentException if the target sum is infinite or {@code NaN}.
+ * @since 2.1
+ */
+ public static double[] normalizeArray(double[] values, double normalizedSum)
+ throws MathIllegalArgumentException, MathArithmeticException {
+ if (Double.isInfinite(normalizedSum)) {
+ throw new MathIllegalArgumentException(LocalizedFormats.NORMALIZE_INFINITE);
+ }
+ if (Double.isNaN(normalizedSum)) {
+ throw new MathIllegalArgumentException(LocalizedFormats.NORMALIZE_NAN);
+ }
+ double sum = 0d;
+ final int len = values.length;
+ double[] out = new double[len];
+ for (int i = 0; i < len; i++) {
+ if (Double.isInfinite(values[i])) {
+ throw new MathIllegalArgumentException(
+ LocalizedFormats.INFINITE_ARRAY_ELEMENT, values[i], i);
+ }
+ if (!Double.isNaN(values[i])) {
+ sum += values[i];
+ }
+ }
+ if (sum == 0) {
+ throw new MathArithmeticException(LocalizedFormats.ARRAY_SUMS_TO_ZERO);
+ }
+ for (int i = 0; i < len; i++) {
+ if (Double.isNaN(values[i])) {
+ out[i] = Double.NaN;
+ } else {
+ out[i] = values[i] * normalizedSum / sum;
+ }
+ }
+ return out;
+ }
+
+ /**
+ * Build an array of elements.
+ *
+ * <p>Arrays are filled with field.getZero()
+ *
+ * @param <T> the type of the field elements
+ * @param field field to which array elements belong
+ * @param length of the array
+ * @return a new array
+ * @since 3.2
+ */
+ public static <T> T[] buildArray(final Field<T> field, final int length) {
+ @SuppressWarnings("unchecked") // OK because field must be correct class
+ T[] array = (T[]) Array.newInstance(field.getRuntimeClass(), length);
+ Arrays.fill(array, field.getZero());
+ return array;
+ }
+
+ /**
+ * Build a double dimension array of elements.
+ *
+ * <p>Arrays are filled with field.getZero()
+ *
+ * @param <T> the type of the field elements
+ * @param field field to which array elements belong
+ * @param rows number of rows in the array
+ * @param columns number of columns (may be negative to build partial arrays in the same way
+ * <code>new Field[rows][]</code> works)
+ * @return a new array
+ * @since 3.2
+ */
+ @SuppressWarnings("unchecked")
+ public static <T> T[][] buildArray(final Field<T> field, final int rows, final int columns) {
+ final T[][] array;
+ if (columns < 0) {
+ T[] dummyRow = buildArray(field, 0);
+ array = (T[][]) Array.newInstance(dummyRow.getClass(), rows);
+ } else {
+ array = (T[][]) Array.newInstance(field.getRuntimeClass(), new int[] {rows, columns});
+ for (int i = 0; i < rows; ++i) {
+ Arrays.fill(array[i], field.getZero());
+ }
+ }
+ return array;
+ }
+
+ /**
+ * Calculates the <a href="http://en.wikipedia.org/wiki/Convolution">convolution</a> between two
+ * sequences.
+ *
+ * <p>The solution is obtained via straightforward computation of the convolution sum (and not
+ * via FFT). Whenever the computation needs an element that would be located at an index outside
+ * the input arrays, the value is assumed to be zero.
+ *
+ * @param x First sequence. Typically, this sequence will represent an input signal to a system.
+ * @param h Second sequence. Typically, this sequence will represent the impulse response of the
+ * system.
+ * @return the convolution of {@code x} and {@code h}. This array's length will be {@code
+ * x.length + h.length - 1}.
+ * @throws NullArgumentException if either {@code x} or {@code h} is {@code null}.
+ * @throws NoDataException if either {@code x} or {@code h} is empty.
+ * @since 3.3
+ */
+ public static double[] convolve(double[] x, double[] h)
+ throws NullArgumentException, NoDataException {
+ MathUtils.checkNotNull(x);
+ MathUtils.checkNotNull(h);
+
+ final int xLen = x.length;
+ final int hLen = h.length;
+
+ if (xLen == 0 || hLen == 0) {
+ throw new NoDataException();
+ }
+
+ // initialize the output array
+ final int totalLength = xLen + hLen - 1;
+ final double[] y = new double[totalLength];
+
+ // straightforward implementation of the convolution sum
+ for (int n = 0; n < totalLength; n++) {
+ double yn = 0;
+ int k = FastMath.max(0, n + 1 - xLen);
+ int j = n - k;
+ while (k < hLen && j >= 0) {
+ yn += x[j--] * h[k++];
+ }
+ y[n] = yn;
+ }
+
+ return y;
+ }
+
+ /** Specification for indicating that some operation applies before or after a given index. */
+ public enum Position {
+ /** Designates the beginning of the array (near index 0). */
+ HEAD,
+ /** Designates the end of the array. */
+ TAIL
+ }
+
+ /**
+ * Shuffle the entries of the given array. The {@code start} and {@code pos} parameters select
+ * which portion of the array is randomized and which is left untouched.
+ *
+ * @see #shuffle(int[],int,Position,RandomGenerator)
+ * @param list Array whose entries will be shuffled (in-place).
+ * @param start Index at which shuffling begins.
+ * @param pos Shuffling is performed for index positions between {@code start} and either the
+ * end (if {@link Position#TAIL}) or the beginning (if {@link Position#HEAD}) of the array.
+ */
+ public static void shuffle(int[] list, int start, Position pos) {
+ shuffle(list, start, pos, new Well19937c());
+ }
+
+ /**
+ * Shuffle the entries of the given array, using the <a
+ * href="http://en.wikipedia.org/wiki/Fisher–Yates_shuffle#The_modern_algorithm">
+ * Fisher–Yates</a> algorithm. The {@code start} and {@code pos} parameters select which portion
+ * of the array is randomized and which is left untouched.
+ *
+ * @param list Array whose entries will be shuffled (in-place).
+ * @param start Index at which shuffling begins.
+ * @param pos Shuffling is performed for index positions between {@code start} and either the
+ * end (if {@link Position#TAIL}) or the beginning (if {@link Position#HEAD}) of the array.
+ * @param rng Random number generator.
+ */
+ public static void shuffle(int[] list, int start, Position pos, RandomGenerator rng) {
+ switch (pos) {
+ case TAIL:
+ {
+ for (int i = list.length - 1; i >= start; i--) {
+ final int target;
+ if (i == start) {
+ target = start;
+ } else {
+ // NumberIsTooLargeException cannot occur.
+ target = new UniformIntegerDistribution(rng, start, i).sample();
+ }
+ final int temp = list[target];
+ list[target] = list[i];
+ list[i] = temp;
+ }
+ }
+ break;
+ case HEAD:
+ {
+ for (int i = 0; i <= start; i++) {
+ final int target;
+ if (i == start) {
+ target = start;
+ } else {
+ // NumberIsTooLargeException cannot occur.
+ target = new UniformIntegerDistribution(rng, i, start).sample();
+ }
+ final int temp = list[target];
+ list[target] = list[i];
+ list[i] = temp;
+ }
+ }
+ break;
+ default:
+ throw new MathInternalError(); // Should never happen.
+ }
+ }
+
+ /**
+ * Shuffle the entries of the given array.
+ *
+ * @see #shuffle(int[],int,Position,RandomGenerator)
+ * @param list Array whose entries will be shuffled (in-place).
+ * @param rng Random number generator.
+ */
+ public static void shuffle(int[] list, RandomGenerator rng) {
+ shuffle(list, 0, Position.TAIL, rng);
+ }
+
+ /**
+ * Shuffle the entries of the given array.
+ *
+ * @see #shuffle(int[],int,Position,RandomGenerator)
+ * @param list Array whose entries will be shuffled (in-place).
+ */
+ public static void shuffle(int[] list) {
+ shuffle(list, new Well19937c());
+ }
+
+ /**
+ * Returns an array representing the natural number {@code n}.
+ *
+ * @param n Natural number.
+ * @return an array whose entries are the numbers 0, 1, ..., {@code n}-1. If {@code n == 0}, the
+ * returned array is empty.
+ */
+ public static int[] natural(int n) {
+ return sequence(n, 0, 1);
+ }
+
+ /**
+ * Returns an array of {@code size} integers starting at {@code start}, skipping {@code stride}
+ * numbers.
+ *
+ * @param size Natural number.
+ * @param start Natural number.
+ * @param stride Natural number.
+ * @return an array whose entries are the numbers {@code start, start + stride, ..., start +
+ * (size - 1) * stride}. If {@code size == 0}, the returned array is empty.
+ * @since 3.4
+ */
+ public static int[] sequence(int size, int start, int stride) {
+ final int[] a = new int[size];
+ for (int i = 0; i < size; i++) {
+ a[i] = start + i * stride;
+ }
+ return a;
+ }
+
+ /**
+ * This method is used
+ * to verify that the input parameters designate a subarray of positive length.
+ * <p>
+ * <ul>
+ * <li>returns <code>true</code> iff the parameters designate a subarray of
+ * positive length</li>
+ * <li>throws <code>MathIllegalArgumentException</code> if the array is null or
+ * or the indices are invalid</li>
+ * <li>returns <code>false</li> if the array is non-null, but
+ * <code>length</code> is 0.
+ * </ul></p>
+ *
+ * @param values the input array
+ * @param begin index of the first array element to include
+ * @param length the number of elements to include
+ * @return true if the parameters are valid and designate a subarray of positive length
+ * @throws MathIllegalArgumentException if the indices are invalid or the array is null
+ * @since 3.3
+ */
+ public static boolean verifyValues(final double[] values, final int begin, final int length)
+ throws MathIllegalArgumentException {
+ return verifyValues(values, begin, length, false);
+ }
+
+ /**
+ * This method is used
+ * to verify that the input parameters designate a subarray of positive length.
+ * <p>
+ * <ul>
+ * <li>returns <code>true</code> iff the parameters designate a subarray of
+ * non-negative length</li>
+ * <li>throws <code>IllegalArgumentException</code> if the array is null or
+ * or the indices are invalid</li>
+ * <li>returns <code>false</li> if the array is non-null, but
+ * <code>length</code> is 0 unless <code>allowEmpty</code> is <code>true</code>
+ * </ul></p>
+ *
+ * @param values the input array
+ * @param begin index of the first array element to include
+ * @param length the number of elements to include
+ * @param allowEmpty if <code>true</code> then zero length arrays are allowed
+ * @return true if the parameters are valid
+ * @throws MathIllegalArgumentException if the indices are invalid or the array is null
+ * @since 3.3
+ */
+ public static boolean verifyValues(
+ final double[] values, final int begin, final int length, final boolean allowEmpty)
+ throws MathIllegalArgumentException {
+
+ if (values == null) {
+ throw new NullArgumentException(LocalizedFormats.INPUT_ARRAY);
+ }
+
+ if (begin < 0) {
+ throw new NotPositiveException(LocalizedFormats.START_POSITION, Integer.valueOf(begin));
+ }
+
+ if (length < 0) {
+ throw new NotPositiveException(LocalizedFormats.LENGTH, Integer.valueOf(length));
+ }
+
+ if (begin + length > values.length) {
+ throw new NumberIsTooLargeException(
+ LocalizedFormats.SUBARRAY_ENDS_AFTER_ARRAY_END,
+ Integer.valueOf(begin + length),
+ Integer.valueOf(values.length),
+ true);
+ }
+
+ if (length == 0 && !allowEmpty) {
+ return false;
+ }
+
+ return true;
+ }
+
+ /**
+ * This method is used
+ * to verify that the begin and length parameters designate a subarray of positive length
+ * and the weights are all non-negative, non-NaN, finite, and not all zero.
+ * <p>
+ * <ul>
+ * <li>returns <code>true</code> iff the parameters designate a subarray of
+ * positive length and the weights array contains legitimate values.</li>
+ * <li>throws <code>IllegalArgumentException</code> if any of the following are true:
+ * <ul><li>the values array is null</li>
+ * <li>the weights array is null</li>
+ * <li>the weights array does not have the same length as the values array</li>
+ * <li>the weights array contains one or more infinite values</li>
+ * <li>the weights array contains one or more NaN values</li>
+ * <li>the weights array contains negative values</li>
+ * <li>the start and length arguments do not determine a valid array</li></ul>
+ * </li>
+ * <li>returns <code>false</li> if the array is non-null, but
+ * <code>length</code> is 0.
+ * </ul></p>
+ *
+ * @param values the input array
+ * @param weights the weights array
+ * @param begin index of the first array element to include
+ * @param length the number of elements to include
+ * @return true if the parameters are valid and designate a subarray of positive length
+ * @throws MathIllegalArgumentException if the indices are invalid or the array is null
+ * @since 3.3
+ */
+ public static boolean verifyValues(
+ final double[] values, final double[] weights, final int begin, final int length)
+ throws MathIllegalArgumentException {
+ return verifyValues(values, weights, begin, length, false);
+ }
+
+ /**
+ * This method is used
+ * to verify that the begin and length parameters designate a subarray of positive length
+ * and the weights are all non-negative, non-NaN, finite, and not all zero.
+ * <p>
+ * <ul>
+ * <li>returns <code>true</code> iff the parameters designate a subarray of
+ * non-negative length and the weights array contains legitimate values.</li>
+ * <li>throws <code>MathIllegalArgumentException</code> if any of the following are true:
+ * <ul><li>the values array is null</li>
+ * <li>the weights array is null</li>
+ * <li>the weights array does not have the same length as the values array</li>
+ * <li>the weights array contains one or more infinite values</li>
+ * <li>the weights array contains one or more NaN values</li>
+ * <li>the weights array contains negative values</li>
+ * <li>the start and length arguments do not determine a valid array</li></ul>
+ * </li>
+ * <li>returns <code>false</li> if the array is non-null, but
+ * <code>length</code> is 0 unless <code>allowEmpty</code> is <code>true</code>.
+ * </ul></p>
+ *
+ * @param values the input array.
+ * @param weights the weights array.
+ * @param begin index of the first array element to include.
+ * @param length the number of elements to include.
+ * @param allowEmpty if {@code true} than allow zero length arrays to pass.
+ * @return {@code true} if the parameters are valid.
+ * @throws NullArgumentException if either of the arrays are null
+ * @throws MathIllegalArgumentException if the array indices are not valid,
+ * the weights array contains NaN, infinite or negative elements, or there
+ * are no positive weights.
+ * @since 3.3
+ */
+ public static boolean verifyValues(
+ final double[] values,
+ final double[] weights,
+ final int begin,
+ final int length,
+ final boolean allowEmpty)
+ throws MathIllegalArgumentException {
+
+ if (weights == null || values == null) {
+ throw new NullArgumentException(LocalizedFormats.INPUT_ARRAY);
+ }
+
+ checkEqualLength(weights, values);
+
+ boolean containsPositiveWeight = false;
+ for (int i = begin; i < begin + length; i++) {
+ final double weight = weights[i];
+ if (Double.isNaN(weight)) {
+ throw new MathIllegalArgumentException(
+ LocalizedFormats.NAN_ELEMENT_AT_INDEX, Integer.valueOf(i));
+ }
+ if (Double.isInfinite(weight)) {
+ throw new MathIllegalArgumentException(
+ LocalizedFormats.INFINITE_ARRAY_ELEMENT,
+ Double.valueOf(weight),
+ Integer.valueOf(i));
+ }
+ if (weight < 0) {
+ throw new MathIllegalArgumentException(
+ LocalizedFormats.NEGATIVE_ELEMENT_AT_INDEX,
+ Integer.valueOf(i),
+ Double.valueOf(weight));
+ }
+ if (!containsPositiveWeight && weight > 0.0) {
+ containsPositiveWeight = true;
+ }
+ }
+
+ if (!containsPositiveWeight) {
+ throw new MathIllegalArgumentException(LocalizedFormats.WEIGHT_AT_LEAST_ONE_NON_ZERO);
+ }
+
+ return verifyValues(values, begin, length, allowEmpty);
+ }
+
+ /**
+ * Concatenates a sequence of arrays. The return array consists of the entries of the input
+ * arrays concatenated in the order they appear in the argument list. Null arrays cause
+ * NullPointerExceptions; zero length arrays are allowed (contributing nothing to the output
+ * array).
+ *
+ * @param x list of double[] arrays to concatenate
+ * @return a new array consisting of the entries of the argument arrays
+ * @throws NullPointerException if any of the arrays are null
+ * @since 3.6
+ */
+ public static double[] concatenate(double[]... x) {
+ int combinedLength = 0;
+ for (double[] a : x) {
+ combinedLength += a.length;
+ }
+ int offset = 0;
+ int curLength = 0;
+ final double[] combined = new double[combinedLength];
+ for (int i = 0; i < x.length; i++) {
+ curLength = x[i].length;
+ System.arraycopy(x[i], 0, combined, offset, curLength);
+ offset += curLength;
+ }
+ return combined;
+ }
+
+ /**
+ * Returns an array consisting of the unique values in {@code data}. The return array is sorted
+ * in descending order. Empty arrays are allowed, but null arrays result in
+ * NullPointerException. Infinities are allowed. NaN values are allowed with maximum sort order
+ * - i.e., if there are NaN values in {@code data}, {@code Double.NaN} will be the first element
+ * of the output array, even if the array also contains {@code Double.POSITIVE_INFINITY}.
+ *
+ * @param data array to scan
+ * @return descending list of values included in the input array
+ * @throws NullPointerException if data is null
+ * @since 3.6
+ */
+ public static double[] unique(double[] data) {
+ TreeSet<Double> values = new TreeSet<Double>();
+ for (int i = 0; i < data.length; i++) {
+ values.add(data[i]);
+ }
+ final int count = values.size();
+ final double[] out = new double[count];
+ Iterator<Double> iterator = values.iterator();
+ int i = 0;
+ while (iterator.hasNext()) {
+ out[count - ++i] = iterator.next();
+ }
+ return out;
+ }
+}
diff --git a/src/main/java/org/apache/commons/math3/util/MathUtils.java b/src/main/java/org/apache/commons/math3/util/MathUtils.java
new file mode 100644
index 0000000..2f376d7
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/util/MathUtils.java
@@ -0,0 +1,289 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math3.util;
+
+import org.apache.commons.math3.RealFieldElement;
+import org.apache.commons.math3.exception.MathArithmeticException;
+import org.apache.commons.math3.exception.NotFiniteNumberException;
+import org.apache.commons.math3.exception.NullArgumentException;
+import org.apache.commons.math3.exception.util.Localizable;
+import org.apache.commons.math3.exception.util.LocalizedFormats;
+
+import java.util.Arrays;
+
+/**
+ * Miscellaneous utility functions.
+ *
+ * @see ArithmeticUtils
+ * @see Precision
+ * @see MathArrays
+ */
+public final class MathUtils {
+ /**
+ * \(2\pi\)
+ *
+ * @since 2.1
+ */
+ public static final double TWO_PI = 2 * FastMath.PI;
+
+ /**
+ * \(\pi^2\)
+ *
+ * @since 3.4
+ */
+ public static final double PI_SQUARED = FastMath.PI * FastMath.PI;
+
+ /** Class contains only static methods. */
+ private MathUtils() {}
+
+ /**
+ * Returns an integer hash code representing the given double value.
+ *
+ * @param value the value to be hashed
+ * @return the hash code
+ */
+ public static int hash(double value) {
+ return new Double(value).hashCode();
+ }
+
+ /**
+ * Returns {@code true} if the values are equal according to semantics of {@link
+ * Double#equals(Object)}.
+ *
+ * @param x Value
+ * @param y Value
+ * @return {@code new Double(x).equals(new Double(y))}
+ */
+ public static boolean equals(double x, double y) {
+ return new Double(x).equals(new Double(y));
+ }
+
+ /**
+ * Returns an integer hash code representing the given double array.
+ *
+ * @param value the value to be hashed (may be null)
+ * @return the hash code
+ * @since 1.2
+ */
+ public static int hash(double[] value) {
+ return Arrays.hashCode(value);
+ }
+
+ /**
+ * Normalize an angle in a 2&pi; wide interval around a center value.
+ *
+ * <p>This method has three main uses:
+ *
+ * <ul>
+ * <li>normalize an angle between 0 and 2&pi;:<br>
+ * {@code a = MathUtils.normalizeAngle(a, FastMath.PI);}
+ * <li>normalize an angle between -&pi; and +&pi;<br>
+ * {@code a = MathUtils.normalizeAngle(a, 0.0);}
+ * <li>compute the angle between two defining angular positions:<br>
+ * {@code angle = MathUtils.normalizeAngle(end, start) - start;}
+ * </ul>
+ *
+ * <p>Note that due to numerical accuracy and since &pi; cannot be represented exactly, the
+ * result interval is <em>closed</em>, it cannot be half-closed as would be more satisfactory in
+ * a purely mathematical view.
+ *
+ * @param a angle to normalize
+ * @param center center of the desired 2&pi; interval for the result
+ * @return a-2k&pi; with integer k and center-&pi; &lt;= a-2k&pi; &lt;= center+&pi;
+ * @since 1.2
+ */
+ public static double normalizeAngle(double a, double center) {
+ return a - TWO_PI * FastMath.floor((a + FastMath.PI - center) / TWO_PI);
+ }
+
+ /**
+ * Find the maximum of two field elements.
+ *
+ * @param <T> the type of the field elements
+ * @param e1 first element
+ * @param e2 second element
+ * @return max(a1, e2)
+ * @since 3.6
+ */
+ public static <T extends RealFieldElement<T>> T max(final T e1, final T e2) {
+ return e1.subtract(e2).getReal() >= 0 ? e1 : e2;
+ }
+
+ /**
+ * Find the minimum of two field elements.
+ *
+ * @param <T> the type of the field elements
+ * @param e1 first element
+ * @param e2 second element
+ * @return min(a1, e2)
+ * @since 3.6
+ */
+ public static <T extends RealFieldElement<T>> T min(final T e1, final T e2) {
+ return e1.subtract(e2).getReal() >= 0 ? e2 : e1;
+ }
+
+ /**
+ * Reduce {@code |a - offset|} to the primary interval {@code [0, |period|)}.
+ *
+ * <p>Specifically, the value returned is <br>
+ * {@code a - |period| * floor((a - offset) / |period|) - offset}.
+ *
+ * <p>If any of the parameters are {@code NaN} or infinite, the result is {@code NaN}.
+ *
+ * @param a Value to reduce.
+ * @param period Period.
+ * @param offset Value that will be mapped to {@code 0}.
+ * @return the value, within the interval {@code [0 |period|)}, that corresponds to {@code a}.
+ */
+ public static double reduce(double a, double period, double offset) {
+ final double p = FastMath.abs(period);
+ return a - p * FastMath.floor((a - offset) / p) - offset;
+ }
+
+ /**
+ * Returns the first argument with the sign of the second argument.
+ *
+ * @param magnitude Magnitude of the returned value.
+ * @param sign Sign of the returned value.
+ * @return a value with magnitude equal to {@code magnitude} and with the same sign as the
+ * {@code sign} argument.
+ * @throws MathArithmeticException if {@code magnitude == Byte.MIN_VALUE} and {@code sign >= 0}.
+ */
+ public static byte copySign(byte magnitude, byte sign) throws MathArithmeticException {
+ if ((magnitude >= 0 && sign >= 0) || (magnitude < 0 && sign < 0)) { // Sign is OK.
+ return magnitude;
+ } else if (sign >= 0 && magnitude == Byte.MIN_VALUE) {
+ throw new MathArithmeticException(LocalizedFormats.OVERFLOW);
+ } else {
+ return (byte) -magnitude; // Flip sign.
+ }
+ }
+
+ /**
+ * Returns the first argument with the sign of the second argument.
+ *
+ * @param magnitude Magnitude of the returned value.
+ * @param sign Sign of the returned value.
+ * @return a value with magnitude equal to {@code magnitude} and with the same sign as the
+ * {@code sign} argument.
+ * @throws MathArithmeticException if {@code magnitude == Short.MIN_VALUE} and {@code sign >=
+ * 0}.
+ */
+ public static short copySign(short magnitude, short sign) throws MathArithmeticException {
+ if ((magnitude >= 0 && sign >= 0) || (magnitude < 0 && sign < 0)) { // Sign is OK.
+ return magnitude;
+ } else if (sign >= 0 && magnitude == Short.MIN_VALUE) {
+ throw new MathArithmeticException(LocalizedFormats.OVERFLOW);
+ } else {
+ return (short) -magnitude; // Flip sign.
+ }
+ }
+
+ /**
+ * Returns the first argument with the sign of the second argument.
+ *
+ * @param magnitude Magnitude of the returned value.
+ * @param sign Sign of the returned value.
+ * @return a value with magnitude equal to {@code magnitude} and with the same sign as the
+ * {@code sign} argument.
+ * @throws MathArithmeticException if {@code magnitude == Integer.MIN_VALUE} and {@code sign >=
+ * 0}.
+ */
+ public static int copySign(int magnitude, int sign) throws MathArithmeticException {
+ if ((magnitude >= 0 && sign >= 0) || (magnitude < 0 && sign < 0)) { // Sign is OK.
+ return magnitude;
+ } else if (sign >= 0 && magnitude == Integer.MIN_VALUE) {
+ throw new MathArithmeticException(LocalizedFormats.OVERFLOW);
+ } else {
+ return -magnitude; // Flip sign.
+ }
+ }
+
+ /**
+ * Returns the first argument with the sign of the second argument.
+ *
+ * @param magnitude Magnitude of the returned value.
+ * @param sign Sign of the returned value.
+ * @return a value with magnitude equal to {@code magnitude} and with the same sign as the
+ * {@code sign} argument.
+ * @throws MathArithmeticException if {@code magnitude == Long.MIN_VALUE} and {@code sign >= 0}.
+ */
+ public static long copySign(long magnitude, long sign) throws MathArithmeticException {
+ if ((magnitude >= 0 && sign >= 0) || (magnitude < 0 && sign < 0)) { // Sign is OK.
+ return magnitude;
+ } else if (sign >= 0 && magnitude == Long.MIN_VALUE) {
+ throw new MathArithmeticException(LocalizedFormats.OVERFLOW);
+ } else {
+ return -magnitude; // Flip sign.
+ }
+ }
+
+ /**
+ * Check that the argument is a real number.
+ *
+ * @param x Argument.
+ * @throws NotFiniteNumberException if {@code x} is not a finite real number.
+ */
+ public static void checkFinite(final double x) throws NotFiniteNumberException {
+ if (Double.isInfinite(x) || Double.isNaN(x)) {
+ throw new NotFiniteNumberException(x);
+ }
+ }
+
+ /**
+ * Check that all the elements are real numbers.
+ *
+ * @param val Arguments.
+ * @throws NotFiniteNumberException if any values of the array is not a finite real number.
+ */
+ public static void checkFinite(final double[] val) throws NotFiniteNumberException {
+ for (int i = 0; i < val.length; i++) {
+ final double x = val[i];
+ if (Double.isInfinite(x) || Double.isNaN(x)) {
+ throw new NotFiniteNumberException(LocalizedFormats.ARRAY_ELEMENT, x, i);
+ }
+ }
+ }
+
+ /**
+ * Checks that an object is not null.
+ *
+ * @param o Object to be checked.
+ * @param pattern Message pattern.
+ * @param args Arguments to replace the placeholders in {@code pattern}.
+ * @throws NullArgumentException if {@code o} is {@code null}.
+ */
+ public static void checkNotNull(Object o, Localizable pattern, Object... args)
+ throws NullArgumentException {
+ if (o == null) {
+ throw new NullArgumentException(pattern, args);
+ }
+ }
+
+ /**
+ * Checks that an object is not null.
+ *
+ * @param o Object to be checked.
+ * @throws NullArgumentException if {@code o} is {@code null}.
+ */
+ public static void checkNotNull(Object o) throws NullArgumentException {
+ if (o == null) {
+ throw new NullArgumentException();
+ }
+ }
+}
diff --git a/src/main/java/org/apache/commons/math3/util/MedianOf3PivotingStrategy.java b/src/main/java/org/apache/commons/math3/util/MedianOf3PivotingStrategy.java
new file mode 100644
index 0000000..205f114
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/util/MedianOf3PivotingStrategy.java
@@ -0,0 +1,63 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math3.util;
+
+import org.apache.commons.math3.exception.MathIllegalArgumentException;
+
+import java.io.Serializable;
+
+/**
+ * Classic median of 3 strategy given begin and end indices.
+ *
+ * @since 3.4
+ */
+public class MedianOf3PivotingStrategy implements PivotingStrategyInterface, Serializable {
+
+ /** Serializable UID. */
+ private static final long serialVersionUID = 20140713L;
+
+ /**
+ * {@inheritDoc} This in specific makes use of median of 3 pivoting.
+ *
+ * @return The index corresponding to a pivot chosen between the first, middle and the last
+ * indices of the array slice
+ * @throws MathIllegalArgumentException when indices exceeds range
+ */
+ public int pivotIndex(final double[] work, final int begin, final int end)
+ throws MathIllegalArgumentException {
+ MathArrays.verifyValues(work, begin, end - begin);
+ final int inclusiveEnd = end - 1;
+ final int middle = begin + (inclusiveEnd - begin) / 2;
+ final double wBegin = work[begin];
+ final double wMiddle = work[middle];
+ final double wEnd = work[inclusiveEnd];
+
+ if (wBegin < wMiddle) {
+ if (wMiddle < wEnd) {
+ return middle;
+ } else {
+ return wBegin < wEnd ? inclusiveEnd : begin;
+ }
+ } else {
+ if (wBegin < wEnd) {
+ return begin;
+ } else {
+ return wMiddle < wEnd ? inclusiveEnd : middle;
+ }
+ }
+ }
+}
diff --git a/src/main/java/org/apache/commons/math3/util/MultidimensionalCounter.java b/src/main/java/org/apache/commons/math3/util/MultidimensionalCounter.java
new file mode 100644
index 0000000..5e239e9
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/util/MultidimensionalCounter.java
@@ -0,0 +1,283 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math3.util;
+
+import org.apache.commons.math3.exception.DimensionMismatchException;
+import org.apache.commons.math3.exception.NotStrictlyPositiveException;
+import org.apache.commons.math3.exception.OutOfRangeException;
+
+import java.util.NoSuchElementException;
+
+/**
+ * Converter between unidimensional storage structure and multidimensional conceptual structure.
+ * This utility will convert from indices in a multidimensional structure to the corresponding index
+ * in a one-dimensional array. For example, assuming that the ranges (in 3 dimensions) of indices
+ * are 2, 4 and 3, the following correspondences, between 3-tuples indices and unidimensional
+ * indices, will hold:
+ *
+ * <ul>
+ * <li>(0, 0, 0) corresponds to 0
+ * <li>(0, 0, 1) corresponds to 1
+ * <li>(0, 0, 2) corresponds to 2
+ * <li>(0, 1, 0) corresponds to 3
+ * <li>...
+ * <li>(1, 0, 0) corresponds to 12
+ * <li>...
+ * <li>(1, 3, 2) corresponds to 23
+ * </ul>
+ *
+ * @since 2.2
+ */
+public class MultidimensionalCounter implements Iterable<Integer> {
+ /** Number of dimensions. */
+ private final int dimension;
+
+ /** Offset for each dimension. */
+ private final int[] uniCounterOffset;
+
+ /** Counter sizes. */
+ private final int[] size;
+
+ /** Total number of (one-dimensional) slots. */
+ private final int totalSize;
+
+ /** Index of last dimension. */
+ private final int last;
+
+ /** Perform iteration over the multidimensional counter. */
+ public class Iterator implements java.util.Iterator<Integer> {
+ /** Multidimensional counter. */
+ private final int[] counter = new int[dimension];
+
+ /** Unidimensional counter. */
+ private int count = -1;
+
+ /** Maximum value for {@link #count}. */
+ private final int maxCount = totalSize - 1;
+
+ /**
+ * Create an iterator
+ *
+ * @see #iterator()
+ */
+ Iterator() {
+ counter[last] = -1;
+ }
+
+ /** {@inheritDoc} */
+ public boolean hasNext() {
+ return count < maxCount;
+ }
+
+ /**
+ * @return the unidimensional count after the counter has been incremented by {@code 1}.
+ * @throws NoSuchElementException if {@link #hasNext()} would have returned {@code false}.
+ */
+ public Integer next() {
+ if (!hasNext()) {
+ throw new NoSuchElementException();
+ }
+
+ for (int i = last; i >= 0; i--) {
+ if (counter[i] == size[i] - 1) {
+ counter[i] = 0;
+ } else {
+ ++counter[i];
+ break;
+ }
+ }
+
+ return ++count;
+ }
+
+ /**
+ * Get the current unidimensional counter slot.
+ *
+ * @return the index within the unidimensionl counter.
+ */
+ public int getCount() {
+ return count;
+ }
+
+ /**
+ * Get the current multidimensional counter slots.
+ *
+ * @return the indices within the multidimensional counter.
+ */
+ public int[] getCounts() {
+ return MathArrays.copyOf(counter);
+ }
+
+ /**
+ * Get the current count in the selected dimension.
+ *
+ * @param dim Dimension index.
+ * @return the count at the corresponding index for the current state of the iterator.
+ * @throws IndexOutOfBoundsException if {@code index} is not in the correct interval (as
+ * defined by the length of the argument in the {@link
+ * MultidimensionalCounter#MultidimensionalCounter(int[]) constructor of the enclosing
+ * class}).
+ */
+ public int getCount(int dim) {
+ return counter[dim];
+ }
+
+ /**
+ * @throws UnsupportedOperationException
+ */
+ public void remove() {
+ throw new UnsupportedOperationException();
+ }
+ }
+
+ /**
+ * Create a counter.
+ *
+ * @param size Counter sizes (number of slots in each dimension).
+ * @throws NotStrictlyPositiveException if one of the sizes is negative or zero.
+ */
+ public MultidimensionalCounter(int... size) throws NotStrictlyPositiveException {
+ dimension = size.length;
+ this.size = MathArrays.copyOf(size);
+
+ uniCounterOffset = new int[dimension];
+
+ last = dimension - 1;
+ int tS = size[last];
+ for (int i = 0; i < last; i++) {
+ int count = 1;
+ for (int j = i + 1; j < dimension; j++) {
+ count *= size[j];
+ }
+ uniCounterOffset[i] = count;
+ tS *= size[i];
+ }
+ uniCounterOffset[last] = 0;
+
+ if (tS <= 0) {
+ throw new NotStrictlyPositiveException(tS);
+ }
+
+ totalSize = tS;
+ }
+
+ /**
+ * Create an iterator over this counter.
+ *
+ * @return the iterator.
+ */
+ public Iterator iterator() {
+ return new Iterator();
+ }
+
+ /**
+ * Get the number of dimensions of the multidimensional counter.
+ *
+ * @return the number of dimensions.
+ */
+ public int getDimension() {
+ return dimension;
+ }
+
+ /**
+ * Convert to multidimensional counter.
+ *
+ * @param index Index in unidimensional counter.
+ * @return the multidimensional counts.
+ * @throws OutOfRangeException if {@code index} is not between {@code 0} and the value returned
+ * by {@link #getSize()} (excluded).
+ */
+ public int[] getCounts(int index) throws OutOfRangeException {
+ if (index < 0 || index >= totalSize) {
+ throw new OutOfRangeException(index, 0, totalSize);
+ }
+
+ final int[] indices = new int[dimension];
+
+ int count = 0;
+ for (int i = 0; i < last; i++) {
+ int idx = 0;
+ final int offset = uniCounterOffset[i];
+ while (count <= index) {
+ count += offset;
+ ++idx;
+ }
+ --idx;
+ count -= offset;
+ indices[i] = idx;
+ }
+
+ indices[last] = index - count;
+
+ return indices;
+ }
+
+ /**
+ * Convert to unidimensional counter.
+ *
+ * @param c Indices in multidimensional counter.
+ * @return the index within the unidimensionl counter.
+ * @throws DimensionMismatchException if the size of {@code c} does not match the size of the
+ * array given in the constructor.
+ * @throws OutOfRangeException if a value of {@code c} is not in the range of the corresponding
+ * dimension, as defined in the {@link
+ * MultidimensionalCounter#MultidimensionalCounter(int...) constructor}.
+ */
+ public int getCount(int... c) throws OutOfRangeException, DimensionMismatchException {
+ if (c.length != dimension) {
+ throw new DimensionMismatchException(c.length, dimension);
+ }
+ int count = 0;
+ for (int i = 0; i < dimension; i++) {
+ final int index = c[i];
+ if (index < 0 || index >= size[i]) {
+ throw new OutOfRangeException(index, 0, size[i] - 1);
+ }
+ count += uniCounterOffset[i] * c[i];
+ }
+ return count + c[last];
+ }
+
+ /**
+ * Get the total number of elements.
+ *
+ * @return the total size of the unidimensional counter.
+ */
+ public int getSize() {
+ return totalSize;
+ }
+
+ /**
+ * Get the number of multidimensional counter slots in each dimension.
+ *
+ * @return the sizes of the multidimensional counter in each dimension.
+ */
+ public int[] getSizes() {
+ return MathArrays.copyOf(size);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public String toString() {
+ final StringBuilder sb = new StringBuilder();
+ for (int i = 0; i < dimension; i++) {
+ sb.append("[").append(getCount(i)).append("]");
+ }
+ return sb.toString();
+ }
+}
diff --git a/src/main/java/org/apache/commons/math3/util/NumberTransformer.java b/src/main/java/org/apache/commons/math3/util/NumberTransformer.java
new file mode 100644
index 0000000..d25ce14
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/util/NumberTransformer.java
@@ -0,0 +1,36 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math3.util;
+
+import org.apache.commons.math3.exception.MathIllegalArgumentException;
+
+/**
+ * Subclasses implementing this interface can transform Objects to doubles.
+ *
+ * <p>No longer extends Serializable since 2.0
+ */
+public interface NumberTransformer {
+
+ /**
+ * Implementing this interface provides a facility to transform from Object to Double.
+ *
+ * @param o the Object to be transformed.
+ * @return the double value of the Object.
+ * @throws MathIllegalArgumentException if the Object can not be transformed into a Double.
+ */
+ double transform(Object o) throws MathIllegalArgumentException;
+}
diff --git a/src/main/java/org/apache/commons/math3/util/OpenIntToDoubleHashMap.java b/src/main/java/org/apache/commons/math3/util/OpenIntToDoubleHashMap.java
new file mode 100644
index 0000000..cb8369d
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/util/OpenIntToDoubleHashMap.java
@@ -0,0 +1,604 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math3.util;
+
+import java.io.IOException;
+import java.io.ObjectInputStream;
+import java.io.Serializable;
+import java.util.ConcurrentModificationException;
+import java.util.NoSuchElementException;
+
+/**
+ * Open addressed map from int to double.
+ *
+ * <p>This class provides a dedicated map from integers to doubles with a much smaller memory
+ * overhead than standard <code>java.util.Map</code>.
+ *
+ * <p>This class is not synchronized. The specialized iterators returned by {@link #iterator()} are
+ * fail-fast: they throw a <code>ConcurrentModificationException</code> when they detect the map has
+ * been modified during iteration.
+ *
+ * @since 2.0
+ */
+public class OpenIntToDoubleHashMap implements Serializable {
+
+ /** Status indicator for free table entries. */
+ protected static final byte FREE = 0;
+
+ /** Status indicator for full table entries. */
+ protected static final byte FULL = 1;
+
+ /** Status indicator for removed table entries. */
+ protected static final byte REMOVED = 2;
+
+ /** Serializable version identifier */
+ private static final long serialVersionUID = -3646337053166149105L;
+
+ /** Load factor for the map. */
+ private static final float LOAD_FACTOR = 0.5f;
+
+ /**
+ * Default starting size.
+ *
+ * <p>This must be a power of two for bit mask to work properly.
+ */
+ private static final int DEFAULT_EXPECTED_SIZE = 16;
+
+ /**
+ * Multiplier for size growth when map fills up.
+ *
+ * <p>This must be a power of two for bit mask to work properly.
+ */
+ private static final int RESIZE_MULTIPLIER = 2;
+
+ /** Number of bits to perturb the index when probing for collision resolution. */
+ private static final int PERTURB_SHIFT = 5;
+
+ /** Keys table. */
+ private int[] keys;
+
+ /** Values table. */
+ private double[] values;
+
+ /** States table. */
+ private byte[] states;
+
+ /** Return value for missing entries. */
+ private final double missingEntries;
+
+ /** Current size of the map. */
+ private int size;
+
+ /** Bit mask for hash values. */
+ private int mask;
+
+ /** Modifications count. */
+ private transient int count;
+
+ /** Build an empty map with default size and using NaN for missing entries. */
+ public OpenIntToDoubleHashMap() {
+ this(DEFAULT_EXPECTED_SIZE, Double.NaN);
+ }
+
+ /**
+ * Build an empty map with default size
+ *
+ * @param missingEntries value to return when a missing entry is fetched
+ */
+ public OpenIntToDoubleHashMap(final double missingEntries) {
+ this(DEFAULT_EXPECTED_SIZE, missingEntries);
+ }
+
+ /**
+ * Build an empty map with specified size and using NaN for missing entries.
+ *
+ * @param expectedSize expected number of elements in the map
+ */
+ public OpenIntToDoubleHashMap(final int expectedSize) {
+ this(expectedSize, Double.NaN);
+ }
+
+ /**
+ * Build an empty map with specified size.
+ *
+ * @param expectedSize expected number of elements in the map
+ * @param missingEntries value to return when a missing entry is fetched
+ */
+ public OpenIntToDoubleHashMap(final int expectedSize, final double missingEntries) {
+ final int capacity = computeCapacity(expectedSize);
+ keys = new int[capacity];
+ values = new double[capacity];
+ states = new byte[capacity];
+ this.missingEntries = missingEntries;
+ mask = capacity - 1;
+ }
+
+ /**
+ * Copy constructor.
+ *
+ * @param source map to copy
+ */
+ public OpenIntToDoubleHashMap(final OpenIntToDoubleHashMap source) {
+ final int length = source.keys.length;
+ keys = new int[length];
+ System.arraycopy(source.keys, 0, keys, 0, length);
+ values = new double[length];
+ System.arraycopy(source.values, 0, values, 0, length);
+ states = new byte[length];
+ System.arraycopy(source.states, 0, states, 0, length);
+ missingEntries = source.missingEntries;
+ size = source.size;
+ mask = source.mask;
+ count = source.count;
+ }
+
+ /**
+ * Compute the capacity needed for a given size.
+ *
+ * @param expectedSize expected size of the map
+ * @return capacity to use for the specified size
+ */
+ private static int computeCapacity(final int expectedSize) {
+ if (expectedSize == 0) {
+ return 1;
+ }
+ final int capacity = (int) FastMath.ceil(expectedSize / LOAD_FACTOR);
+ final int powerOfTwo = Integer.highestOneBit(capacity);
+ if (powerOfTwo == capacity) {
+ return capacity;
+ }
+ return nextPowerOfTwo(capacity);
+ }
+
+ /**
+ * Find the smallest power of two greater than the input value
+ *
+ * @param i input value
+ * @return smallest power of two greater than the input value
+ */
+ private static int nextPowerOfTwo(final int i) {
+ return Integer.highestOneBit(i) << 1;
+ }
+
+ /**
+ * Get the stored value associated with the given key
+ *
+ * @param key key associated with the data
+ * @return data associated with the key
+ */
+ public double get(final int key) {
+
+ final int hash = hashOf(key);
+ int index = hash & mask;
+ if (containsKey(key, index)) {
+ return values[index];
+ }
+
+ if (states[index] == FREE) {
+ return missingEntries;
+ }
+
+ int j = index;
+ for (int perturb = perturb(hash); states[index] != FREE; perturb >>= PERTURB_SHIFT) {
+ j = probe(perturb, j);
+ index = j & mask;
+ if (containsKey(key, index)) {
+ return values[index];
+ }
+ }
+
+ return missingEntries;
+ }
+
+ /**
+ * Check if a value is associated with a key.
+ *
+ * @param key key to check
+ * @return true if a value is associated with key
+ */
+ public boolean containsKey(final int key) {
+
+ final int hash = hashOf(key);
+ int index = hash & mask;
+ if (containsKey(key, index)) {
+ return true;
+ }
+
+ if (states[index] == FREE) {
+ return false;
+ }
+
+ int j = index;
+ for (int perturb = perturb(hash); states[index] != FREE; perturb >>= PERTURB_SHIFT) {
+ j = probe(perturb, j);
+ index = j & mask;
+ if (containsKey(key, index)) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ /**
+ * Get an iterator over map elements.
+ *
+ * <p>The specialized iterators returned are fail-fast: they throw a <code>
+ * ConcurrentModificationException</code> when they detect the map has been modified during
+ * iteration.
+ *
+ * @return iterator over the map elements
+ */
+ public Iterator iterator() {
+ return new Iterator();
+ }
+
+ /**
+ * Perturb the hash for starting probing.
+ *
+ * @param hash initial hash
+ * @return perturbed hash
+ */
+ private static int perturb(final int hash) {
+ return hash & 0x7fffffff;
+ }
+
+ /**
+ * Find the index at which a key should be inserted
+ *
+ * @param key key to lookup
+ * @return index at which key should be inserted
+ */
+ private int findInsertionIndex(final int key) {
+ return findInsertionIndex(keys, states, key, mask);
+ }
+
+ /**
+ * Find the index at which a key should be inserted
+ *
+ * @param keys keys table
+ * @param states states table
+ * @param key key to lookup
+ * @param mask bit mask for hash values
+ * @return index at which key should be inserted
+ */
+ private static int findInsertionIndex(
+ final int[] keys, final byte[] states, final int key, final int mask) {
+ final int hash = hashOf(key);
+ int index = hash & mask;
+ if (states[index] == FREE) {
+ return index;
+ } else if (states[index] == FULL && keys[index] == key) {
+ return changeIndexSign(index);
+ }
+
+ int perturb = perturb(hash);
+ int j = index;
+ if (states[index] == FULL) {
+ while (true) {
+ j = probe(perturb, j);
+ index = j & mask;
+ perturb >>= PERTURB_SHIFT;
+
+ if (states[index] != FULL || keys[index] == key) {
+ break;
+ }
+ }
+ }
+
+ if (states[index] == FREE) {
+ return index;
+ } else if (states[index] == FULL) {
+ // due to the loop exit condition,
+ // if (states[index] == FULL) then keys[index] == key
+ return changeIndexSign(index);
+ }
+
+ final int firstRemoved = index;
+ while (true) {
+ j = probe(perturb, j);
+ index = j & mask;
+
+ if (states[index] == FREE) {
+ return firstRemoved;
+ } else if (states[index] == FULL && keys[index] == key) {
+ return changeIndexSign(index);
+ }
+
+ perturb >>= PERTURB_SHIFT;
+ }
+ }
+
+ /**
+ * Compute next probe for collision resolution
+ *
+ * @param perturb perturbed hash
+ * @param j previous probe
+ * @return next probe
+ */
+ private static int probe(final int perturb, final int j) {
+ return (j << 2) + j + perturb + 1;
+ }
+
+ /**
+ * Change the index sign
+ *
+ * @param index initial index
+ * @return changed index
+ */
+ private static int changeIndexSign(final int index) {
+ return -index - 1;
+ }
+
+ /**
+ * Get the number of elements stored in the map.
+ *
+ * @return number of elements stored in the map
+ */
+ public int size() {
+ return size;
+ }
+
+ /**
+ * Remove the value associated with a key.
+ *
+ * @param key key to which the value is associated
+ * @return removed value
+ */
+ public double remove(final int key) {
+
+ final int hash = hashOf(key);
+ int index = hash & mask;
+ if (containsKey(key, index)) {
+ return doRemove(index);
+ }
+
+ if (states[index] == FREE) {
+ return missingEntries;
+ }
+
+ int j = index;
+ for (int perturb = perturb(hash); states[index] != FREE; perturb >>= PERTURB_SHIFT) {
+ j = probe(perturb, j);
+ index = j & mask;
+ if (containsKey(key, index)) {
+ return doRemove(index);
+ }
+ }
+
+ return missingEntries;
+ }
+
+ /**
+ * Check if the tables contain an element associated with specified key at specified index.
+ *
+ * @param key key to check
+ * @param index index to check
+ * @return true if an element is associated with key at index
+ */
+ private boolean containsKey(final int key, final int index) {
+ return (key != 0 || states[index] == FULL) && keys[index] == key;
+ }
+
+ /**
+ * Remove an element at specified index.
+ *
+ * @param index index of the element to remove
+ * @return removed value
+ */
+ private double doRemove(int index) {
+ keys[index] = 0;
+ states[index] = REMOVED;
+ final double previous = values[index];
+ values[index] = missingEntries;
+ --size;
+ ++count;
+ return previous;
+ }
+
+ /**
+ * Put a value associated with a key in the map.
+ *
+ * @param key key to which value is associated
+ * @param value value to put in the map
+ * @return previous value associated with the key
+ */
+ public double put(final int key, final double value) {
+ int index = findInsertionIndex(key);
+ double previous = missingEntries;
+ boolean newMapping = true;
+ if (index < 0) {
+ index = changeIndexSign(index);
+ previous = values[index];
+ newMapping = false;
+ }
+ keys[index] = key;
+ states[index] = FULL;
+ values[index] = value;
+ if (newMapping) {
+ ++size;
+ if (shouldGrowTable()) {
+ growTable();
+ }
+ ++count;
+ }
+ return previous;
+ }
+
+ /** Grow the tables. */
+ private void growTable() {
+
+ final int oldLength = states.length;
+ final int[] oldKeys = keys;
+ final double[] oldValues = values;
+ final byte[] oldStates = states;
+
+ final int newLength = RESIZE_MULTIPLIER * oldLength;
+ final int[] newKeys = new int[newLength];
+ final double[] newValues = new double[newLength];
+ final byte[] newStates = new byte[newLength];
+ final int newMask = newLength - 1;
+ for (int i = 0; i < oldLength; ++i) {
+ if (oldStates[i] == FULL) {
+ final int key = oldKeys[i];
+ final int index = findInsertionIndex(newKeys, newStates, key, newMask);
+ newKeys[index] = key;
+ newValues[index] = oldValues[i];
+ newStates[index] = FULL;
+ }
+ }
+
+ mask = newMask;
+ keys = newKeys;
+ values = newValues;
+ states = newStates;
+ }
+
+ /**
+ * Check if tables should grow due to increased size.
+ *
+ * @return true if tables should grow
+ */
+ private boolean shouldGrowTable() {
+ return size > (mask + 1) * LOAD_FACTOR;
+ }
+
+ /**
+ * Compute the hash value of a key
+ *
+ * @param key key to hash
+ * @return hash value of the key
+ */
+ private static int hashOf(final int key) {
+ final int h = key ^ ((key >>> 20) ^ (key >>> 12));
+ return h ^ (h >>> 7) ^ (h >>> 4);
+ }
+
+ /** Iterator class for the map. */
+ public class Iterator {
+
+ /** Reference modification count. */
+ private final int referenceCount;
+
+ /** Index of current element. */
+ private int current;
+
+ /** Index of next element. */
+ private int next;
+
+ /** Simple constructor. */
+ private Iterator() {
+
+ // preserve the modification count of the map to detect concurrent modifications later
+ referenceCount = count;
+
+ // initialize current index
+ next = -1;
+ try {
+ advance();
+ } catch (NoSuchElementException nsee) { // NOPMD
+ // ignored
+ }
+ }
+
+ /**
+ * Check if there is a next element in the map.
+ *
+ * @return true if there is a next element
+ */
+ public boolean hasNext() {
+ return next >= 0;
+ }
+
+ /**
+ * Get the key of current entry.
+ *
+ * @return key of current entry
+ * @exception ConcurrentModificationException if the map is modified during iteration
+ * @exception NoSuchElementException if there is no element left in the map
+ */
+ public int key() throws ConcurrentModificationException, NoSuchElementException {
+ if (referenceCount != count) {
+ throw new ConcurrentModificationException();
+ }
+ if (current < 0) {
+ throw new NoSuchElementException();
+ }
+ return keys[current];
+ }
+
+ /**
+ * Get the value of current entry.
+ *
+ * @return value of current entry
+ * @exception ConcurrentModificationException if the map is modified during iteration
+ * @exception NoSuchElementException if there is no element left in the map
+ */
+ public double value() throws ConcurrentModificationException, NoSuchElementException {
+ if (referenceCount != count) {
+ throw new ConcurrentModificationException();
+ }
+ if (current < 0) {
+ throw new NoSuchElementException();
+ }
+ return values[current];
+ }
+
+ /**
+ * Advance iterator one step further.
+ *
+ * @exception ConcurrentModificationException if the map is modified during iteration
+ * @exception NoSuchElementException if there is no element left in the map
+ */
+ public void advance() throws ConcurrentModificationException, NoSuchElementException {
+
+ if (referenceCount != count) {
+ throw new ConcurrentModificationException();
+ }
+
+ // advance on step
+ current = next;
+
+ // prepare next step
+ try {
+ while (states[++next] != FULL) { // NOPMD
+ // nothing to do
+ }
+ } catch (ArrayIndexOutOfBoundsException e) {
+ next = -2;
+ if (current < 0) {
+ throw new NoSuchElementException();
+ }
+ }
+ }
+ }
+
+ /**
+ * Read a serialized object.
+ *
+ * @param stream input stream
+ * @throws IOException if object cannot be read
+ * @throws ClassNotFoundException if the class corresponding to the serialized object cannot be
+ * found
+ */
+ private void readObject(final ObjectInputStream stream)
+ throws IOException, ClassNotFoundException {
+ stream.defaultReadObject();
+ count = 0;
+ }
+}
diff --git a/src/main/java/org/apache/commons/math3/util/OpenIntToFieldHashMap.java b/src/main/java/org/apache/commons/math3/util/OpenIntToFieldHashMap.java
new file mode 100644
index 0000000..0f4212f
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/util/OpenIntToFieldHashMap.java
@@ -0,0 +1,632 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math3.util;
+
+import org.apache.commons.math3.Field;
+import org.apache.commons.math3.FieldElement;
+
+import java.io.IOException;
+import java.io.ObjectInputStream;
+import java.io.Serializable;
+import java.lang.reflect.Array;
+import java.util.ConcurrentModificationException;
+import java.util.NoSuchElementException;
+
+/**
+ * Open addressed map from int to FieldElement.
+ *
+ * <p>This class provides a dedicated map from integers to FieldElements with a much smaller memory
+ * overhead than standard <code>java.util.Map</code>.
+ *
+ * <p>This class is not synchronized. The specialized iterators returned by {@link #iterator()} are
+ * fail-fast: they throw a <code>ConcurrentModificationException</code> when they detect the map has
+ * been modified during iteration.
+ *
+ * @param <T> the type of the field elements
+ * @since 2.0
+ */
+public class OpenIntToFieldHashMap<T extends FieldElement<T>> implements Serializable {
+
+ /** Status indicator for free table entries. */
+ protected static final byte FREE = 0;
+
+ /** Status indicator for full table entries. */
+ protected static final byte FULL = 1;
+
+ /** Status indicator for removed table entries. */
+ protected static final byte REMOVED = 2;
+
+ /** Serializable version identifier. */
+ private static final long serialVersionUID = -9179080286849120720L;
+
+ /** Load factor for the map. */
+ private static final float LOAD_FACTOR = 0.5f;
+
+ /**
+ * Default starting size.
+ *
+ * <p>This must be a power of two for bit mask to work properly.
+ */
+ private static final int DEFAULT_EXPECTED_SIZE = 16;
+
+ /**
+ * Multiplier for size growth when map fills up.
+ *
+ * <p>This must be a power of two for bit mask to work properly.
+ */
+ private static final int RESIZE_MULTIPLIER = 2;
+
+ /** Number of bits to perturb the index when probing for collision resolution. */
+ private static final int PERTURB_SHIFT = 5;
+
+ /** Field to which the elements belong. */
+ private final Field<T> field;
+
+ /** Keys table. */
+ private int[] keys;
+
+ /** Values table. */
+ private T[] values;
+
+ /** States table. */
+ private byte[] states;
+
+ /** Return value for missing entries. */
+ private final T missingEntries;
+
+ /** Current size of the map. */
+ private int size;
+
+ /** Bit mask for hash values. */
+ private int mask;
+
+ /** Modifications count. */
+ private transient int count;
+
+ /**
+ * Build an empty map with default size and using zero for missing entries.
+ *
+ * @param field field to which the elements belong
+ */
+ public OpenIntToFieldHashMap(final Field<T> field) {
+ this(field, DEFAULT_EXPECTED_SIZE, field.getZero());
+ }
+
+ /**
+ * Build an empty map with default size
+ *
+ * @param field field to which the elements belong
+ * @param missingEntries value to return when a missing entry is fetched
+ */
+ public OpenIntToFieldHashMap(final Field<T> field, final T missingEntries) {
+ this(field, DEFAULT_EXPECTED_SIZE, missingEntries);
+ }
+
+ /**
+ * Build an empty map with specified size and using zero for missing entries.
+ *
+ * @param field field to which the elements belong
+ * @param expectedSize expected number of elements in the map
+ */
+ public OpenIntToFieldHashMap(final Field<T> field, final int expectedSize) {
+ this(field, expectedSize, field.getZero());
+ }
+
+ /**
+ * Build an empty map with specified size.
+ *
+ * @param field field to which the elements belong
+ * @param expectedSize expected number of elements in the map
+ * @param missingEntries value to return when a missing entry is fetched
+ */
+ public OpenIntToFieldHashMap(
+ final Field<T> field, final int expectedSize, final T missingEntries) {
+ this.field = field;
+ final int capacity = computeCapacity(expectedSize);
+ keys = new int[capacity];
+ values = buildArray(capacity);
+ states = new byte[capacity];
+ this.missingEntries = missingEntries;
+ mask = capacity - 1;
+ }
+
+ /**
+ * Copy constructor.
+ *
+ * @param source map to copy
+ */
+ public OpenIntToFieldHashMap(final OpenIntToFieldHashMap<T> source) {
+ field = source.field;
+ final int length = source.keys.length;
+ keys = new int[length];
+ System.arraycopy(source.keys, 0, keys, 0, length);
+ values = buildArray(length);
+ System.arraycopy(source.values, 0, values, 0, length);
+ states = new byte[length];
+ System.arraycopy(source.states, 0, states, 0, length);
+ missingEntries = source.missingEntries;
+ size = source.size;
+ mask = source.mask;
+ count = source.count;
+ }
+
+ /**
+ * Compute the capacity needed for a given size.
+ *
+ * @param expectedSize expected size of the map
+ * @return capacity to use for the specified size
+ */
+ private static int computeCapacity(final int expectedSize) {
+ if (expectedSize == 0) {
+ return 1;
+ }
+ final int capacity = (int) FastMath.ceil(expectedSize / LOAD_FACTOR);
+ final int powerOfTwo = Integer.highestOneBit(capacity);
+ if (powerOfTwo == capacity) {
+ return capacity;
+ }
+ return nextPowerOfTwo(capacity);
+ }
+
+ /**
+ * Find the smallest power of two greater than the input value
+ *
+ * @param i input value
+ * @return smallest power of two greater than the input value
+ */
+ private static int nextPowerOfTwo(final int i) {
+ return Integer.highestOneBit(i) << 1;
+ }
+
+ /**
+ * Get the stored value associated with the given key
+ *
+ * @param key key associated with the data
+ * @return data associated with the key
+ */
+ public T get(final int key) {
+
+ final int hash = hashOf(key);
+ int index = hash & mask;
+ if (containsKey(key, index)) {
+ return values[index];
+ }
+
+ if (states[index] == FREE) {
+ return missingEntries;
+ }
+
+ int j = index;
+ for (int perturb = perturb(hash); states[index] != FREE; perturb >>= PERTURB_SHIFT) {
+ j = probe(perturb, j);
+ index = j & mask;
+ if (containsKey(key, index)) {
+ return values[index];
+ }
+ }
+
+ return missingEntries;
+ }
+
+ /**
+ * Check if a value is associated with a key.
+ *
+ * @param key key to check
+ * @return true if a value is associated with key
+ */
+ public boolean containsKey(final int key) {
+
+ final int hash = hashOf(key);
+ int index = hash & mask;
+ if (containsKey(key, index)) {
+ return true;
+ }
+
+ if (states[index] == FREE) {
+ return false;
+ }
+
+ int j = index;
+ for (int perturb = perturb(hash); states[index] != FREE; perturb >>= PERTURB_SHIFT) {
+ j = probe(perturb, j);
+ index = j & mask;
+ if (containsKey(key, index)) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ /**
+ * Get an iterator over map elements.
+ *
+ * <p>The specialized iterators returned are fail-fast: they throw a <code>
+ * ConcurrentModificationException</code> when they detect the map has been modified during
+ * iteration.
+ *
+ * @return iterator over the map elements
+ */
+ public Iterator iterator() {
+ return new Iterator();
+ }
+
+ /**
+ * Perturb the hash for starting probing.
+ *
+ * @param hash initial hash
+ * @return perturbed hash
+ */
+ private static int perturb(final int hash) {
+ return hash & 0x7fffffff;
+ }
+
+ /**
+ * Find the index at which a key should be inserted
+ *
+ * @param key key to lookup
+ * @return index at which key should be inserted
+ */
+ private int findInsertionIndex(final int key) {
+ return findInsertionIndex(keys, states, key, mask);
+ }
+
+ /**
+ * Find the index at which a key should be inserted
+ *
+ * @param keys keys table
+ * @param states states table
+ * @param key key to lookup
+ * @param mask bit mask for hash values
+ * @return index at which key should be inserted
+ */
+ private static int findInsertionIndex(
+ final int[] keys, final byte[] states, final int key, final int mask) {
+ final int hash = hashOf(key);
+ int index = hash & mask;
+ if (states[index] == FREE) {
+ return index;
+ } else if (states[index] == FULL && keys[index] == key) {
+ return changeIndexSign(index);
+ }
+
+ int perturb = perturb(hash);
+ int j = index;
+ if (states[index] == FULL) {
+ while (true) {
+ j = probe(perturb, j);
+ index = j & mask;
+ perturb >>= PERTURB_SHIFT;
+
+ if (states[index] != FULL || keys[index] == key) {
+ break;
+ }
+ }
+ }
+
+ if (states[index] == FREE) {
+ return index;
+ } else if (states[index] == FULL) {
+ // due to the loop exit condition,
+ // if (states[index] == FULL) then keys[index] == key
+ return changeIndexSign(index);
+ }
+
+ final int firstRemoved = index;
+ while (true) {
+ j = probe(perturb, j);
+ index = j & mask;
+
+ if (states[index] == FREE) {
+ return firstRemoved;
+ } else if (states[index] == FULL && keys[index] == key) {
+ return changeIndexSign(index);
+ }
+
+ perturb >>= PERTURB_SHIFT;
+ }
+ }
+
+ /**
+ * Compute next probe for collision resolution
+ *
+ * @param perturb perturbed hash
+ * @param j previous probe
+ * @return next probe
+ */
+ private static int probe(final int perturb, final int j) {
+ return (j << 2) + j + perturb + 1;
+ }
+
+ /**
+ * Change the index sign
+ *
+ * @param index initial index
+ * @return changed index
+ */
+ private static int changeIndexSign(final int index) {
+ return -index - 1;
+ }
+
+ /**
+ * Get the number of elements stored in the map.
+ *
+ * @return number of elements stored in the map
+ */
+ public int size() {
+ return size;
+ }
+
+ /**
+ * Remove the value associated with a key.
+ *
+ * @param key key to which the value is associated
+ * @return removed value
+ */
+ public T remove(final int key) {
+
+ final int hash = hashOf(key);
+ int index = hash & mask;
+ if (containsKey(key, index)) {
+ return doRemove(index);
+ }
+
+ if (states[index] == FREE) {
+ return missingEntries;
+ }
+
+ int j = index;
+ for (int perturb = perturb(hash); states[index] != FREE; perturb >>= PERTURB_SHIFT) {
+ j = probe(perturb, j);
+ index = j & mask;
+ if (containsKey(key, index)) {
+ return doRemove(index);
+ }
+ }
+
+ return missingEntries;
+ }
+
+ /**
+ * Check if the tables contain an element associated with specified key at specified index.
+ *
+ * @param key key to check
+ * @param index index to check
+ * @return true if an element is associated with key at index
+ */
+ private boolean containsKey(final int key, final int index) {
+ return (key != 0 || states[index] == FULL) && keys[index] == key;
+ }
+
+ /**
+ * Remove an element at specified index.
+ *
+ * @param index index of the element to remove
+ * @return removed value
+ */
+ private T doRemove(int index) {
+ keys[index] = 0;
+ states[index] = REMOVED;
+ final T previous = values[index];
+ values[index] = missingEntries;
+ --size;
+ ++count;
+ return previous;
+ }
+
+ /**
+ * Put a value associated with a key in the map.
+ *
+ * @param key key to which value is associated
+ * @param value value to put in the map
+ * @return previous value associated with the key
+ */
+ public T put(final int key, final T value) {
+ int index = findInsertionIndex(key);
+ T previous = missingEntries;
+ boolean newMapping = true;
+ if (index < 0) {
+ index = changeIndexSign(index);
+ previous = values[index];
+ newMapping = false;
+ }
+ keys[index] = key;
+ states[index] = FULL;
+ values[index] = value;
+ if (newMapping) {
+ ++size;
+ if (shouldGrowTable()) {
+ growTable();
+ }
+ ++count;
+ }
+ return previous;
+ }
+
+ /** Grow the tables. */
+ private void growTable() {
+
+ final int oldLength = states.length;
+ final int[] oldKeys = keys;
+ final T[] oldValues = values;
+ final byte[] oldStates = states;
+
+ final int newLength = RESIZE_MULTIPLIER * oldLength;
+ final int[] newKeys = new int[newLength];
+ final T[] newValues = buildArray(newLength);
+ final byte[] newStates = new byte[newLength];
+ final int newMask = newLength - 1;
+ for (int i = 0; i < oldLength; ++i) {
+ if (oldStates[i] == FULL) {
+ final int key = oldKeys[i];
+ final int index = findInsertionIndex(newKeys, newStates, key, newMask);
+ newKeys[index] = key;
+ newValues[index] = oldValues[i];
+ newStates[index] = FULL;
+ }
+ }
+
+ mask = newMask;
+ keys = newKeys;
+ values = newValues;
+ states = newStates;
+ }
+
+ /**
+ * Check if tables should grow due to increased size.
+ *
+ * @return true if tables should grow
+ */
+ private boolean shouldGrowTable() {
+ return size > (mask + 1) * LOAD_FACTOR;
+ }
+
+ /**
+ * Compute the hash value of a key
+ *
+ * @param key key to hash
+ * @return hash value of the key
+ */
+ private static int hashOf(final int key) {
+ final int h = key ^ ((key >>> 20) ^ (key >>> 12));
+ return h ^ (h >>> 7) ^ (h >>> 4);
+ }
+
+ /** Iterator class for the map. */
+ public class Iterator {
+
+ /** Reference modification count. */
+ private final int referenceCount;
+
+ /** Index of current element. */
+ private int current;
+
+ /** Index of next element. */
+ private int next;
+
+ /** Simple constructor. */
+ private Iterator() {
+
+ // preserve the modification count of the map to detect concurrent modifications later
+ referenceCount = count;
+
+ // initialize current index
+ next = -1;
+ try {
+ advance();
+ } catch (NoSuchElementException nsee) { // NOPMD
+ // ignored
+ }
+ }
+
+ /**
+ * Check if there is a next element in the map.
+ *
+ * @return true if there is a next element
+ */
+ public boolean hasNext() {
+ return next >= 0;
+ }
+
+ /**
+ * Get the key of current entry.
+ *
+ * @return key of current entry
+ * @exception ConcurrentModificationException if the map is modified during iteration
+ * @exception NoSuchElementException if there is no element left in the map
+ */
+ public int key() throws ConcurrentModificationException, NoSuchElementException {
+ if (referenceCount != count) {
+ throw new ConcurrentModificationException();
+ }
+ if (current < 0) {
+ throw new NoSuchElementException();
+ }
+ return keys[current];
+ }
+
+ /**
+ * Get the value of current entry.
+ *
+ * @return value of current entry
+ * @exception ConcurrentModificationException if the map is modified during iteration
+ * @exception NoSuchElementException if there is no element left in the map
+ */
+ public T value() throws ConcurrentModificationException, NoSuchElementException {
+ if (referenceCount != count) {
+ throw new ConcurrentModificationException();
+ }
+ if (current < 0) {
+ throw new NoSuchElementException();
+ }
+ return values[current];
+ }
+
+ /**
+ * Advance iterator one step further.
+ *
+ * @exception ConcurrentModificationException if the map is modified during iteration
+ * @exception NoSuchElementException if there is no element left in the map
+ */
+ public void advance() throws ConcurrentModificationException, NoSuchElementException {
+
+ if (referenceCount != count) {
+ throw new ConcurrentModificationException();
+ }
+
+ // advance on step
+ current = next;
+
+ // prepare next step
+ try {
+ while (states[++next] != FULL) { // NOPMD
+ // nothing to do
+ }
+ } catch (ArrayIndexOutOfBoundsException e) {
+ next = -2;
+ if (current < 0) {
+ throw new NoSuchElementException();
+ }
+ }
+ }
+ }
+
+ /**
+ * Read a serialized object.
+ *
+ * @param stream input stream
+ * @throws IOException if object cannot be read
+ * @throws ClassNotFoundException if the class corresponding to the serialized object cannot be
+ * found
+ */
+ private void readObject(final ObjectInputStream stream)
+ throws IOException, ClassNotFoundException {
+ stream.defaultReadObject();
+ count = 0;
+ }
+
+ /**
+ * Build an array of elements.
+ *
+ * @param length size of the array to build
+ * @return a new array
+ */
+ @SuppressWarnings("unchecked") // field is of type T
+ private T[] buildArray(final int length) {
+ return (T[]) Array.newInstance(field.getRuntimeClass(), length);
+ }
+}
diff --git a/src/main/java/org/apache/commons/math3/util/Pair.java b/src/main/java/org/apache/commons/math3/util/Pair.java
new file mode 100644
index 0000000..95fbb2b
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/util/Pair.java
@@ -0,0 +1,148 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math3.util;
+
+/**
+ * Generic pair. <br>
+ * Although the instances of this class are immutable, it is impossible to ensure that the
+ * references passed to the constructor will not be modified by the caller.
+ *
+ * @param <K> Key type.
+ * @param <V> Value type.
+ * @since 3.0
+ */
+public class Pair<K, V> {
+ /** Key. */
+ private final K key;
+
+ /** Value. */
+ private final V value;
+
+ /**
+ * Create an entry representing a mapping from the specified key to the specified value.
+ *
+ * @param k Key (first element of the pair).
+ * @param v Value (second element of the pair).
+ */
+ public Pair(K k, V v) {
+ key = k;
+ value = v;
+ }
+
+ /**
+ * Create an entry representing the same mapping as the specified entry.
+ *
+ * @param entry Entry to copy.
+ */
+ public Pair(Pair<? extends K, ? extends V> entry) {
+ this(entry.getKey(), entry.getValue());
+ }
+
+ /**
+ * Get the key.
+ *
+ * @return the key (first element of the pair).
+ */
+ public K getKey() {
+ return key;
+ }
+
+ /**
+ * Get the value.
+ *
+ * @return the value (second element of the pair).
+ */
+ public V getValue() {
+ return value;
+ }
+
+ /**
+ * Get the first element of the pair.
+ *
+ * @return the first element of the pair.
+ * @since 3.1
+ */
+ public K getFirst() {
+ return key;
+ }
+
+ /**
+ * Get the second element of the pair.
+ *
+ * @return the second element of the pair.
+ * @since 3.1
+ */
+ public V getSecond() {
+ return value;
+ }
+
+ /**
+ * Compare the specified object with this entry for equality.
+ *
+ * @param o Object.
+ * @return {@code true} if the given object is also a map entry and the two entries represent
+ * the same mapping.
+ */
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (!(o instanceof Pair)) {
+ return false;
+ } else {
+ Pair<?, ?> oP = (Pair<?, ?>) o;
+ return (key == null ? oP.key == null : key.equals(oP.key))
+ && (value == null ? oP.value == null : value.equals(oP.value));
+ }
+ }
+
+ /**
+ * Compute a hash code.
+ *
+ * @return the hash code value.
+ */
+ @Override
+ public int hashCode() {
+ int result = key == null ? 0 : key.hashCode();
+
+ final int h = value == null ? 0 : value.hashCode();
+ result = 37 * result + h ^ (h >>> 16);
+
+ return result;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public String toString() {
+ return "[" + getKey() + ", " + getValue() + "]";
+ }
+
+ /**
+ * Convenience factory method that calls the {@link #Pair(Object, Object) constructor}.
+ *
+ * @param <K> the key type
+ * @param <V> the value type
+ * @param k First element of the pair.
+ * @param v Second element of the pair.
+ * @return a new {@code Pair} containing {@code k} and {@code v}.
+ * @since 3.3
+ */
+ public static <K, V> Pair<K, V> create(K k, V v) {
+ return new Pair<K, V>(k, v);
+ }
+}
diff --git a/src/main/java/org/apache/commons/math3/util/PivotingStrategyInterface.java b/src/main/java/org/apache/commons/math3/util/PivotingStrategyInterface.java
new file mode 100644
index 0000000..0e2b175
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/util/PivotingStrategyInterface.java
@@ -0,0 +1,43 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math3.util;
+
+import org.apache.commons.math3.exception.MathIllegalArgumentException;
+
+/**
+ * A strategy to pick a pivoting index of an array for doing partitioning.
+ *
+ * @see MedianOf3PivotingStrategy
+ * @see RandomPivotingStrategy
+ * @see CentralPivotingStrategy
+ * @since 3.4
+ */
+public interface PivotingStrategyInterface {
+
+ /**
+ * Find pivot index of the array so that partition and K<sup>th</sup> element selection can be
+ * made
+ *
+ * @param work data array
+ * @param begin index of the first element of the slice
+ * @param end index after the last element of the slice
+ * @return the index of the pivot element chosen between the first and the last element of the
+ * array slice
+ * @throws MathIllegalArgumentException when indices exceeds range
+ */
+ int pivotIndex(double[] work, int begin, int end) throws MathIllegalArgumentException;
+}
diff --git a/src/main/java/org/apache/commons/math3/util/Precision.java b/src/main/java/org/apache/commons/math3/util/Precision.java
new file mode 100644
index 0000000..fe93003
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/util/Precision.java
@@ -0,0 +1,613 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math3.util;
+
+import org.apache.commons.math3.exception.MathArithmeticException;
+import org.apache.commons.math3.exception.MathIllegalArgumentException;
+import org.apache.commons.math3.exception.util.LocalizedFormats;
+
+import java.math.BigDecimal;
+
+/**
+ * Utilities for comparing numbers.
+ *
+ * @since 3.0
+ */
+public class Precision {
+ /**
+ * Largest double-precision floating-point number such that {@code 1 + EPSILON} is numerically
+ * equal to 1. This value is an upper bound on the relative error due to rounding real numbers
+ * to double precision floating-point numbers.
+ *
+ * <p>In IEEE 754 arithmetic, this is 2<sup>-53</sup>.
+ *
+ * @see <a href="http://en.wikipedia.org/wiki/Machine_epsilon">Machine epsilon</a>
+ */
+ public static final double EPSILON;
+
+ /**
+ * Safe minimum, such that {@code 1 / SAFE_MIN} does not overflow. <br>
+ * In IEEE 754 arithmetic, this is also the smallest normalized number 2<sup>-1022</sup>.
+ */
+ public static final double SAFE_MIN;
+
+ /** Exponent offset in IEEE754 representation. */
+ private static final long EXPONENT_OFFSET = 1023l;
+
+ /** Offset to order signed double numbers lexicographically. */
+ private static final long SGN_MASK = 0x8000000000000000L;
+
+ /** Offset to order signed double numbers lexicographically. */
+ private static final int SGN_MASK_FLOAT = 0x80000000;
+
+ /** Positive zero. */
+ private static final double POSITIVE_ZERO = 0d;
+
+ /** Positive zero bits. */
+ private static final long POSITIVE_ZERO_DOUBLE_BITS = Double.doubleToRawLongBits(+0.0);
+
+ /** Negative zero bits. */
+ private static final long NEGATIVE_ZERO_DOUBLE_BITS = Double.doubleToRawLongBits(-0.0);
+
+ /** Positive zero bits. */
+ private static final int POSITIVE_ZERO_FLOAT_BITS = Float.floatToRawIntBits(+0.0f);
+
+ /** Negative zero bits. */
+ private static final int NEGATIVE_ZERO_FLOAT_BITS = Float.floatToRawIntBits(-0.0f);
+
+ static {
+ /*
+ * This was previously expressed as = 0x1.0p-53;
+ * However, OpenJDK (Sparc Solaris) cannot handle such small
+ * constants: MATH-721
+ */
+ EPSILON = Double.longBitsToDouble((EXPONENT_OFFSET - 53l) << 52);
+
+ /*
+ * This was previously expressed as = 0x1.0p-1022;
+ * However, OpenJDK (Sparc Solaris) cannot handle such small
+ * constants: MATH-721
+ */
+ SAFE_MIN = Double.longBitsToDouble((EXPONENT_OFFSET - 1022l) << 52);
+ }
+
+ /** Private constructor. */
+ private Precision() {}
+
+ /**
+ * Compares two numbers given some amount of allowed error.
+ *
+ * @param x the first number
+ * @param y the second number
+ * @param eps the amount of error to allow when checking for equality
+ * @return
+ * <ul>
+ * <li>0 if {@link #equals(double, double, double) equals(x, y, eps)}
+ * <li>&lt; 0 if !{@link #equals(double, double, double) equals(x, y, eps)} &amp;&amp; x
+ * &lt; y
+ * <li>> 0 if !{@link #equals(double, double, double) equals(x, y, eps)} &amp;&amp; x > y
+ * or either argument is NaN
+ * </ul>
+ */
+ public static int compareTo(double x, double y, double eps) {
+ if (equals(x, y, eps)) {
+ return 0;
+ } else if (x < y) {
+ return -1;
+ }
+ return 1;
+ }
+
+ /**
+ * Compares two numbers given some amount of allowed error. Two float numbers are considered
+ * equal if there are {@code (maxUlps - 1)} (or fewer) floating point numbers between them, i.e.
+ * two adjacent floating point numbers are considered equal. Adapted from <a
+ * href="http://randomascii.wordpress.com/2012/02/25/comparing-floating-point-numbers-2012-edition/">
+ * Bruce Dawson</a>. Returns {@code false} if either of the arguments is NaN.
+ *
+ * @param x first value
+ * @param y second value
+ * @param maxUlps {@code (maxUlps - 1)} is the number of floating point values between {@code x}
+ * and {@code y}.
+ * @return
+ * <ul>
+ * <li>0 if {@link #equals(double, double, int) equals(x, y, maxUlps)}
+ * <li>&lt; 0 if !{@link #equals(double, double, int) equals(x, y, maxUlps)} &amp;&amp; x
+ * &lt; y
+ * <li>&gt; 0 if !{@link #equals(double, double, int) equals(x, y, maxUlps)} &amp;&amp; x
+ * > y or either argument is NaN
+ * </ul>
+ */
+ public static int compareTo(final double x, final double y, final int maxUlps) {
+ if (equals(x, y, maxUlps)) {
+ return 0;
+ } else if (x < y) {
+ return -1;
+ }
+ return 1;
+ }
+
+ /**
+ * Returns true iff they are equal as defined by {@link #equals(float,float,int) equals(x, y,
+ * 1)}.
+ *
+ * @param x first value
+ * @param y second value
+ * @return {@code true} if the values are equal.
+ */
+ public static boolean equals(float x, float y) {
+ return equals(x, y, 1);
+ }
+
+ /**
+ * Returns true if both arguments are NaN or they are equal as defined by {@link
+ * #equals(float,float) equals(x, y, 1)}.
+ *
+ * @param x first value
+ * @param y second value
+ * @return {@code true} if the values are equal or both are NaN.
+ * @since 2.2
+ */
+ public static boolean equalsIncludingNaN(float x, float y) {
+ return (x != x || y != y) ? !(x != x ^ y != y) : equals(x, y, 1);
+ }
+
+ /**
+ * Returns true if the arguments are equal or within the range of allowed error (inclusive).
+ * Returns {@code false} if either of the arguments is NaN.
+ *
+ * @param x first value
+ * @param y second value
+ * @param eps the amount of absolute error to allow.
+ * @return {@code true} if the values are equal or within range of each other.
+ * @since 2.2
+ */
+ public static boolean equals(float x, float y, float eps) {
+ return equals(x, y, 1) || FastMath.abs(y - x) <= eps;
+ }
+
+ /**
+ * Returns true if the arguments are both NaN, are equal, or are within the range of allowed
+ * error (inclusive).
+ *
+ * @param x first value
+ * @param y second value
+ * @param eps the amount of absolute error to allow.
+ * @return {@code true} if the values are equal or within range of each other, or both are NaN.
+ * @since 2.2
+ */
+ public static boolean equalsIncludingNaN(float x, float y, float eps) {
+ return equalsIncludingNaN(x, y) || (FastMath.abs(y - x) <= eps);
+ }
+
+ /**
+ * Returns true if the arguments are equal or within the range of allowed error (inclusive). Two
+ * float numbers are considered equal if there are {@code (maxUlps - 1)} (or fewer) floating
+ * point numbers between them, i.e. two adjacent floating point numbers are considered equal.
+ * Adapted from <a
+ * href="http://randomascii.wordpress.com/2012/02/25/comparing-floating-point-numbers-2012-edition/">
+ * Bruce Dawson</a>. Returns {@code false} if either of the arguments is NaN.
+ *
+ * @param x first value
+ * @param y second value
+ * @param maxUlps {@code (maxUlps - 1)} is the number of floating point values between {@code x}
+ * and {@code y}.
+ * @return {@code true} if there are fewer than {@code maxUlps} floating point values between
+ * {@code x} and {@code y}.
+ * @since 2.2
+ */
+ public static boolean equals(final float x, final float y, final int maxUlps) {
+
+ final int xInt = Float.floatToRawIntBits(x);
+ final int yInt = Float.floatToRawIntBits(y);
+
+ final boolean isEqual;
+ if (((xInt ^ yInt) & SGN_MASK_FLOAT) == 0) {
+ // number have same sign, there is no risk of overflow
+ isEqual = FastMath.abs(xInt - yInt) <= maxUlps;
+ } else {
+ // number have opposite signs, take care of overflow
+ final int deltaPlus;
+ final int deltaMinus;
+ if (xInt < yInt) {
+ deltaPlus = yInt - POSITIVE_ZERO_FLOAT_BITS;
+ deltaMinus = xInt - NEGATIVE_ZERO_FLOAT_BITS;
+ } else {
+ deltaPlus = xInt - POSITIVE_ZERO_FLOAT_BITS;
+ deltaMinus = yInt - NEGATIVE_ZERO_FLOAT_BITS;
+ }
+
+ if (deltaPlus > maxUlps) {
+ isEqual = false;
+ } else {
+ isEqual = deltaMinus <= (maxUlps - deltaPlus);
+ }
+ }
+
+ return isEqual && !Float.isNaN(x) && !Float.isNaN(y);
+ }
+
+ /**
+ * Returns true if the arguments are both NaN or if they are equal as defined by {@link
+ * #equals(float,float,int) equals(x, y, maxUlps)}.
+ *
+ * @param x first value
+ * @param y second value
+ * @param maxUlps {@code (maxUlps - 1)} is the number of floating point values between {@code x}
+ * and {@code y}.
+ * @return {@code true} if both arguments are NaN or if there are less than {@code maxUlps}
+ * floating point values between {@code x} and {@code y}.
+ * @since 2.2
+ */
+ public static boolean equalsIncludingNaN(float x, float y, int maxUlps) {
+ return (x != x || y != y) ? !(x != x ^ y != y) : equals(x, y, maxUlps);
+ }
+
+ /**
+ * Returns true iff they are equal as defined by {@link #equals(double,double,int) equals(x, y,
+ * 1)}.
+ *
+ * @param x first value
+ * @param y second value
+ * @return {@code true} if the values are equal.
+ */
+ public static boolean equals(double x, double y) {
+ return equals(x, y, 1);
+ }
+
+ /**
+ * Returns true if the arguments are both NaN or they are equal as defined by {@link
+ * #equals(double,double) equals(x, y, 1)}.
+ *
+ * @param x first value
+ * @param y second value
+ * @return {@code true} if the values are equal or both are NaN.
+ * @since 2.2
+ */
+ public static boolean equalsIncludingNaN(double x, double y) {
+ return (x != x || y != y) ? !(x != x ^ y != y) : equals(x, y, 1);
+ }
+
+ /**
+ * Returns {@code true} if there is no double value strictly between the arguments or the
+ * difference between them is within the range of allowed error (inclusive). Returns {@code
+ * false} if either of the arguments is NaN.
+ *
+ * @param x First value.
+ * @param y Second value.
+ * @param eps Amount of allowed absolute error.
+ * @return {@code true} if the values are two adjacent floating point numbers or they are within
+ * range of each other.
+ */
+ public static boolean equals(double x, double y, double eps) {
+ return equals(x, y, 1) || FastMath.abs(y - x) <= eps;
+ }
+
+ /**
+ * Returns {@code true} if there is no double value strictly between the arguments or the
+ * relative difference between them is less than or equal to the given tolerance. Returns {@code
+ * false} if either of the arguments is NaN.
+ *
+ * @param x First value.
+ * @param y Second value.
+ * @param eps Amount of allowed relative error.
+ * @return {@code true} if the values are two adjacent floating point numbers or they are within
+ * range of each other.
+ * @since 3.1
+ */
+ public static boolean equalsWithRelativeTolerance(double x, double y, double eps) {
+ if (equals(x, y, 1)) {
+ return true;
+ }
+
+ final double absoluteMax = FastMath.max(FastMath.abs(x), FastMath.abs(y));
+ final double relativeDifference = FastMath.abs((x - y) / absoluteMax);
+
+ return relativeDifference <= eps;
+ }
+
+ /**
+ * Returns true if the arguments are both NaN, are equal or are within the range of allowed
+ * error (inclusive).
+ *
+ * @param x first value
+ * @param y second value
+ * @param eps the amount of absolute error to allow.
+ * @return {@code true} if the values are equal or within range of each other, or both are NaN.
+ * @since 2.2
+ */
+ public static boolean equalsIncludingNaN(double x, double y, double eps) {
+ return equalsIncludingNaN(x, y) || (FastMath.abs(y - x) <= eps);
+ }
+
+ /**
+ * Returns true if the arguments are equal or within the range of allowed error (inclusive).
+ *
+ * <p>Two float numbers are considered equal if there are {@code (maxUlps - 1)} (or fewer)
+ * floating point numbers between them, i.e. two adjacent floating point numbers are considered
+ * equal.
+ *
+ * <p>Adapted from <a
+ * href="http://randomascii.wordpress.com/2012/02/25/comparing-floating-point-numbers-2012-edition/">
+ * Bruce Dawson</a>. Returns {@code false} if either of the arguments is NaN.
+ *
+ * @param x first value
+ * @param y second value
+ * @param maxUlps {@code (maxUlps - 1)} is the number of floating point values between {@code x}
+ * and {@code y}.
+ * @return {@code true} if there are fewer than {@code maxUlps} floating point values between
+ * {@code x} and {@code y}.
+ */
+ public static boolean equals(final double x, final double y, final int maxUlps) {
+
+ final long xInt = Double.doubleToRawLongBits(x);
+ final long yInt = Double.doubleToRawLongBits(y);
+
+ final boolean isEqual;
+ if (((xInt ^ yInt) & SGN_MASK) == 0l) {
+ // number have same sign, there is no risk of overflow
+ isEqual = FastMath.abs(xInt - yInt) <= maxUlps;
+ } else {
+ // number have opposite signs, take care of overflow
+ final long deltaPlus;
+ final long deltaMinus;
+ if (xInt < yInt) {
+ deltaPlus = yInt - POSITIVE_ZERO_DOUBLE_BITS;
+ deltaMinus = xInt - NEGATIVE_ZERO_DOUBLE_BITS;
+ } else {
+ deltaPlus = xInt - POSITIVE_ZERO_DOUBLE_BITS;
+ deltaMinus = yInt - NEGATIVE_ZERO_DOUBLE_BITS;
+ }
+
+ if (deltaPlus > maxUlps) {
+ isEqual = false;
+ } else {
+ isEqual = deltaMinus <= (maxUlps - deltaPlus);
+ }
+ }
+
+ return isEqual && !Double.isNaN(x) && !Double.isNaN(y);
+ }
+
+ /**
+ * Returns true if both arguments are NaN or if they are equal as defined by {@link
+ * #equals(double,double,int) equals(x, y, maxUlps)}.
+ *
+ * @param x first value
+ * @param y second value
+ * @param maxUlps {@code (maxUlps - 1)} is the number of floating point values between {@code x}
+ * and {@code y}.
+ * @return {@code true} if both arguments are NaN or if there are less than {@code maxUlps}
+ * floating point values between {@code x} and {@code y}.
+ * @since 2.2
+ */
+ public static boolean equalsIncludingNaN(double x, double y, int maxUlps) {
+ return (x != x || y != y) ? !(x != x ^ y != y) : equals(x, y, maxUlps);
+ }
+
+ /**
+ * Rounds the given value to the specified number of decimal places. The value is rounded using
+ * the {@link BigDecimal#ROUND_HALF_UP} method.
+ *
+ * @param x Value to round.
+ * @param scale Number of digits to the right of the decimal point.
+ * @return the rounded value.
+ * @since 1.1 (previously in {@code MathUtils}, moved as of version 3.0)
+ */
+ public static double round(double x, int scale) {
+ return round(x, scale, BigDecimal.ROUND_HALF_UP);
+ }
+
+ /**
+ * Rounds the given value to the specified number of decimal places. The value is rounded using
+ * the given method which is any method defined in {@link BigDecimal}. If {@code x} is infinite
+ * or {@code NaN}, then the value of {@code x} is returned unchanged, regardless of the other
+ * parameters.
+ *
+ * @param x Value to round.
+ * @param scale Number of digits to the right of the decimal point.
+ * @param roundingMethod Rounding method as defined in {@link BigDecimal}.
+ * @return the rounded value.
+ * @throws ArithmeticException if {@code roundingMethod == ROUND_UNNECESSARY} and the specified
+ * scaling operation would require rounding.
+ * @throws IllegalArgumentException if {@code roundingMethod} does not represent a valid
+ * rounding mode.
+ * @since 1.1 (previously in {@code MathUtils}, moved as of version 3.0)
+ */
+ public static double round(double x, int scale, int roundingMethod) {
+ try {
+ final double rounded =
+ (new BigDecimal(Double.toString(x)).setScale(scale, roundingMethod))
+ .doubleValue();
+ // MATH-1089: negative values rounded to zero should result in negative zero
+ return rounded == POSITIVE_ZERO ? POSITIVE_ZERO * x : rounded;
+ } catch (NumberFormatException ex) {
+ if (Double.isInfinite(x)) {
+ return x;
+ } else {
+ return Double.NaN;
+ }
+ }
+ }
+
+ /**
+ * Rounds the given value to the specified number of decimal places. The value is rounded using
+ * the {@link BigDecimal#ROUND_HALF_UP} method.
+ *
+ * @param x Value to round.
+ * @param scale Number of digits to the right of the decimal point.
+ * @return the rounded value.
+ * @since 1.1 (previously in {@code MathUtils}, moved as of version 3.0)
+ */
+ public static float round(float x, int scale) {
+ return round(x, scale, BigDecimal.ROUND_HALF_UP);
+ }
+
+ /**
+ * Rounds the given value to the specified number of decimal places. The value is rounded using
+ * the given method which is any method defined in {@link BigDecimal}.
+ *
+ * @param x Value to round.
+ * @param scale Number of digits to the right of the decimal point.
+ * @param roundingMethod Rounding method as defined in {@link BigDecimal}.
+ * @return the rounded value.
+ * @since 1.1 (previously in {@code MathUtils}, moved as of version 3.0)
+ * @throws MathArithmeticException if an exact operation is required but result is not exact
+ * @throws MathIllegalArgumentException if {@code roundingMethod} is not a valid rounding
+ * method.
+ */
+ public static float round(float x, int scale, int roundingMethod)
+ throws MathArithmeticException, MathIllegalArgumentException {
+ final float sign = FastMath.copySign(1f, x);
+ final float factor = (float) FastMath.pow(10.0f, scale) * sign;
+ return (float) roundUnscaled(x * factor, sign, roundingMethod) / factor;
+ }
+
+ /**
+ * Rounds the given non-negative value to the "nearest" integer. Nearest is determined by the
+ * rounding method specified. Rounding methods are defined in {@link BigDecimal}.
+ *
+ * @param unscaled Value to round.
+ * @param sign Sign of the original, scaled value.
+ * @param roundingMethod Rounding method, as defined in {@link BigDecimal}.
+ * @return the rounded value.
+ * @throws MathArithmeticException if an exact operation is required but result is not exact
+ * @throws MathIllegalArgumentException if {@code roundingMethod} is not a valid rounding
+ * method.
+ * @since 1.1 (previously in {@code MathUtils}, moved as of version 3.0)
+ */
+ private static double roundUnscaled(double unscaled, double sign, int roundingMethod)
+ throws MathArithmeticException, MathIllegalArgumentException {
+ switch (roundingMethod) {
+ case BigDecimal.ROUND_CEILING:
+ if (sign == -1) {
+ unscaled =
+ FastMath.floor(FastMath.nextAfter(unscaled, Double.NEGATIVE_INFINITY));
+ } else {
+ unscaled =
+ FastMath.ceil(FastMath.nextAfter(unscaled, Double.POSITIVE_INFINITY));
+ }
+ break;
+ case BigDecimal.ROUND_DOWN:
+ unscaled = FastMath.floor(FastMath.nextAfter(unscaled, Double.NEGATIVE_INFINITY));
+ break;
+ case BigDecimal.ROUND_FLOOR:
+ if (sign == -1) {
+ unscaled =
+ FastMath.ceil(FastMath.nextAfter(unscaled, Double.POSITIVE_INFINITY));
+ } else {
+ unscaled =
+ FastMath.floor(FastMath.nextAfter(unscaled, Double.NEGATIVE_INFINITY));
+ }
+ break;
+ case BigDecimal.ROUND_HALF_DOWN:
+ {
+ unscaled = FastMath.nextAfter(unscaled, Double.NEGATIVE_INFINITY);
+ double fraction = unscaled - FastMath.floor(unscaled);
+ if (fraction > 0.5) {
+ unscaled = FastMath.ceil(unscaled);
+ } else {
+ unscaled = FastMath.floor(unscaled);
+ }
+ break;
+ }
+ case BigDecimal.ROUND_HALF_EVEN:
+ {
+ double fraction = unscaled - FastMath.floor(unscaled);
+ if (fraction > 0.5) {
+ unscaled = FastMath.ceil(unscaled);
+ } else if (fraction < 0.5) {
+ unscaled = FastMath.floor(unscaled);
+ } else {
+ // The following equality test is intentional and needed for rounding
+ // purposes
+ if (FastMath.floor(unscaled) / 2.0
+ == FastMath.floor(FastMath.floor(unscaled) / 2.0)) { // even
+ unscaled = FastMath.floor(unscaled);
+ } else { // odd
+ unscaled = FastMath.ceil(unscaled);
+ }
+ }
+ break;
+ }
+ case BigDecimal.ROUND_HALF_UP:
+ {
+ unscaled = FastMath.nextAfter(unscaled, Double.POSITIVE_INFINITY);
+ double fraction = unscaled - FastMath.floor(unscaled);
+ if (fraction >= 0.5) {
+ unscaled = FastMath.ceil(unscaled);
+ } else {
+ unscaled = FastMath.floor(unscaled);
+ }
+ break;
+ }
+ case BigDecimal.ROUND_UNNECESSARY:
+ if (unscaled != FastMath.floor(unscaled)) {
+ throw new MathArithmeticException();
+ }
+ break;
+ case BigDecimal.ROUND_UP:
+ // do not round if the discarded fraction is equal to zero
+ if (unscaled != FastMath.floor(unscaled)) {
+ unscaled =
+ FastMath.ceil(FastMath.nextAfter(unscaled, Double.POSITIVE_INFINITY));
+ }
+ break;
+ default:
+ throw new MathIllegalArgumentException(
+ LocalizedFormats.INVALID_ROUNDING_METHOD,
+ roundingMethod,
+ "ROUND_CEILING",
+ BigDecimal.ROUND_CEILING,
+ "ROUND_DOWN",
+ BigDecimal.ROUND_DOWN,
+ "ROUND_FLOOR",
+ BigDecimal.ROUND_FLOOR,
+ "ROUND_HALF_DOWN",
+ BigDecimal.ROUND_HALF_DOWN,
+ "ROUND_HALF_EVEN",
+ BigDecimal.ROUND_HALF_EVEN,
+ "ROUND_HALF_UP",
+ BigDecimal.ROUND_HALF_UP,
+ "ROUND_UNNECESSARY",
+ BigDecimal.ROUND_UNNECESSARY,
+ "ROUND_UP",
+ BigDecimal.ROUND_UP);
+ }
+ return unscaled;
+ }
+
+ /**
+ * Computes a number {@code delta} close to {@code originalDelta} with the property that
+ *
+ * <pre><code>
+ * x + delta - x
+ * </code></pre>
+ *
+ * is exactly machine-representable. This is useful when computing numerical derivatives, in
+ * order to reduce roundoff errors.
+ *
+ * @param x Value.
+ * @param originalDelta Offset value.
+ * @return a number {@code delta} so that {@code x + delta} and {@code x} differ by a
+ * representable floating number.
+ */
+ public static double representableDelta(double x, double originalDelta) {
+ return x + originalDelta - x;
+ }
+}
diff --git a/src/main/java/org/apache/commons/math3/util/RandomPivotingStrategy.java b/src/main/java/org/apache/commons/math3/util/RandomPivotingStrategy.java
new file mode 100644
index 0000000..f2fc28b
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/util/RandomPivotingStrategy.java
@@ -0,0 +1,58 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math3.util;
+
+import org.apache.commons.math3.exception.MathIllegalArgumentException;
+import org.apache.commons.math3.random.RandomGenerator;
+
+import java.io.Serializable;
+
+/**
+ * A strategy of selecting random index between begin and end indices.
+ *
+ * @since 3.4
+ */
+public class RandomPivotingStrategy implements PivotingStrategyInterface, Serializable {
+
+ /** Serializable UID. */
+ private static final long serialVersionUID = 20140713L;
+
+ /** Random generator to use for selecting pivot. */
+ private final RandomGenerator random;
+
+ /**
+ * Simple constructor.
+ *
+ * @param random random generator to use for selecting pivot
+ */
+ public RandomPivotingStrategy(final RandomGenerator random) {
+ this.random = random;
+ }
+
+ /**
+ * {@inheritDoc} A uniform random pivot selection between begin and end indices
+ *
+ * @return The index corresponding to a random uniformly selected value between first and the
+ * last indices of the array slice
+ * @throws MathIllegalArgumentException when indices exceeds range
+ */
+ public int pivotIndex(final double[] work, final int begin, final int end)
+ throws MathIllegalArgumentException {
+ MathArrays.verifyValues(work, begin, end - begin);
+ return begin + random.nextInt(end - begin - 1);
+ }
+}
diff --git a/src/main/java/org/apache/commons/math3/util/ResizableDoubleArray.java b/src/main/java/org/apache/commons/math3/util/ResizableDoubleArray.java
new file mode 100644
index 0000000..f0963ef
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/util/ResizableDoubleArray.java
@@ -0,0 +1,1130 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math3.util;
+
+import org.apache.commons.math3.exception.MathIllegalArgumentException;
+import org.apache.commons.math3.exception.MathIllegalStateException;
+import org.apache.commons.math3.exception.MathInternalError;
+import org.apache.commons.math3.exception.NotStrictlyPositiveException;
+import org.apache.commons.math3.exception.NullArgumentException;
+import org.apache.commons.math3.exception.NumberIsTooSmallException;
+import org.apache.commons.math3.exception.util.LocalizedFormats;
+
+import java.io.Serializable;
+import java.util.Arrays;
+
+/**
+ * A variable length {@link DoubleArray} implementation that automatically handles expanding and
+ * contracting its internal storage array as elements are added and removed.
+ *
+ * <h3>Important note: Usage should not assume that this class is thread-safe even though some of
+ * the methods are {@code synchronized}. This qualifier will be dropped in the next major release
+ * (4.0).</h3>
+ *
+ * <p>The internal storage array starts with capacity determined by the {@code initialCapacity}
+ * property, which can be set by the constructor. The default initial capacity is 16. Adding
+ * elements using {@link #addElement(double)} appends elements to the end of the array. When there
+ * are no open entries at the end of the internal storage array, the array is expanded. The size of
+ * the expanded array depends on the {@code expansionMode} and {@code expansionFactor} properties.
+ * The {@code expansionMode} determines whether the size of the array is multiplied by the {@code
+ * expansionFactor} ({@link ExpansionMode#MULTIPLICATIVE}) or if the expansion is additive ({@link
+ * ExpansionMode#ADDITIVE} -- {@code expansionFactor} storage locations added). The default {@code
+ * expansionMode} is {@code MULTIPLICATIVE} and the default {@code expansionFactor} is 2.
+ *
+ * <p>The {@link #addElementRolling(double)} method adds a new element to the end of the internal
+ * storage array and adjusts the "usable window" of the internal array forward by one position
+ * (effectively making what was the second element the first, and so on). Repeated activations of
+ * this method (or activation of {@link #discardFrontElements(int)}) will effectively orphan the
+ * storage locations at the beginning of the internal storage array. To reclaim this storage, each
+ * time one of these methods is activated, the size of the internal storage array is compared to the
+ * number of addressable elements (the {@code numElements} property) and if the difference is too
+ * large, the internal array is contracted to size {@code numElements + 1}. The determination of
+ * when the internal storage array is "too large" depends on the {@code expansionMode} and {@code
+ * contractionFactor} properties. If the {@code expansionMode} is {@code MULTIPLICATIVE},
+ * contraction is triggered when the ratio between storage array length and {@code numElements}
+ * exceeds {@code contractionFactor.} If the {@code expansionMode} is {@code ADDITIVE}, the number
+ * of excess storage locations is compared to {@code contractionFactor}.
+ *
+ * <p>To avoid cycles of expansions and contractions, the {@code expansionFactor} must not exceed
+ * the {@code contractionFactor}. Constructors and mutators for both of these properties enforce
+ * this requirement, throwing a {@code MathIllegalArgumentException} if it is violated.
+ */
+public class ResizableDoubleArray implements DoubleArray, Serializable {
+ /**
+ * Additive expansion mode.
+ *
+ * @deprecated As of 3.1. Please use {@link ExpansionMode#ADDITIVE} instead.
+ */
+ @Deprecated public static final int ADDITIVE_MODE = 1;
+
+ /**
+ * Multiplicative expansion mode.
+ *
+ * @deprecated As of 3.1. Please use {@link ExpansionMode#MULTIPLICATIVE} instead.
+ */
+ @Deprecated public static final int MULTIPLICATIVE_MODE = 0;
+
+ /** Serializable version identifier. */
+ private static final long serialVersionUID = -3485529955529426875L;
+
+ /** Default value for initial capacity. */
+ private static final int DEFAULT_INITIAL_CAPACITY = 16;
+
+ /** Default value for array size modifier. */
+ private static final double DEFAULT_EXPANSION_FACTOR = 2.0;
+
+ /**
+ * Default value for the difference between {@link #contractionCriterion} and {@link
+ * #expansionFactor}.
+ */
+ private static final double DEFAULT_CONTRACTION_DELTA = 0.5;
+
+ /**
+ * The contraction criteria determines when the internal array will be contracted to fit the
+ * number of elements contained in the element array + 1.
+ */
+ private double contractionCriterion = 2.5;
+
+ /**
+ * The expansion factor of the array. When the array needs to be expanded, the new array size
+ * will be {@code internalArray.length * expansionFactor} if {@code expansionMode} is set to
+ * MULTIPLICATIVE_MODE, or {@code internalArray.length + expansionFactor} if {@code
+ * expansionMode} is set to ADDITIVE_MODE.
+ */
+ private double expansionFactor = 2.0;
+
+ /**
+ * Determines whether array expansion by {@code expansionFactor} is additive or multiplicative.
+ */
+ private ExpansionMode expansionMode = ExpansionMode.MULTIPLICATIVE;
+
+ /** The internal storage array. */
+ private double[] internalArray;
+
+ /**
+ * The number of addressable elements in the array. Note that this has nothing to do with the
+ * length of the internal storage array.
+ */
+ private int numElements = 0;
+
+ /**
+ * The position of the first addressable element in the internal storage array. The addressable
+ * elements in the array are {@code internalArray[startIndex],...,internalArray[startIndex +
+ * numElements - 1]}.
+ */
+ private int startIndex = 0;
+
+ /**
+ * Specification of expansion algorithm.
+ *
+ * @since 3.1
+ */
+ public enum ExpansionMode {
+ /** Multiplicative expansion mode. */
+ MULTIPLICATIVE,
+ /** Additive expansion mode. */
+ ADDITIVE
+ }
+
+ /**
+ * Creates an instance with default properties.
+ *
+ * <ul>
+ * <li>{@code initialCapacity = 16}
+ * <li>{@code expansionMode = MULTIPLICATIVE}
+ * <li>{@code expansionFactor = 2.0}
+ * <li>{@code contractionCriterion = 2.5}
+ * </ul>
+ */
+ public ResizableDoubleArray() {
+ this(DEFAULT_INITIAL_CAPACITY);
+ }
+
+ /**
+ * Creates an instance with the specified initial capacity. Other properties take default
+ * values:
+ *
+ * <ul>
+ * <li>{@code expansionMode = MULTIPLICATIVE}
+ * <li>{@code expansionFactor = 2.0}
+ * <li>{@code contractionCriterion = 2.5}
+ * </ul>
+ *
+ * @param initialCapacity Initial size of the internal storage array.
+ * @throws MathIllegalArgumentException if {@code initialCapacity <= 0}.
+ */
+ public ResizableDoubleArray(int initialCapacity) throws MathIllegalArgumentException {
+ this(initialCapacity, DEFAULT_EXPANSION_FACTOR);
+ }
+
+ /**
+ * Creates an instance from an existing {@code double[]} with the initial capacity and
+ * numElements corresponding to the size of the supplied {@code double[]} array. If the supplied
+ * array is null, a new empty array with the default initial capacity will be created. The input
+ * array is copied, not referenced. Other properties take default values:
+ *
+ * <ul>
+ * <li>{@code initialCapacity = 16}
+ * <li>{@code expansionMode = MULTIPLICATIVE}
+ * <li>{@code expansionFactor = 2.0}
+ * <li>{@code contractionCriterion = 2.5}
+ * </ul>
+ *
+ * @param initialArray initial array
+ * @since 2.2
+ */
+ public ResizableDoubleArray(double[] initialArray) {
+ this(
+ DEFAULT_INITIAL_CAPACITY,
+ DEFAULT_EXPANSION_FACTOR,
+ DEFAULT_CONTRACTION_DELTA + DEFAULT_EXPANSION_FACTOR,
+ ExpansionMode.MULTIPLICATIVE,
+ initialArray);
+ }
+
+ /**
+ * Creates an instance with the specified initial capacity and expansion factor. The remaining
+ * properties take default values:
+ *
+ * <ul>
+ * <li>{@code expansionMode = MULTIPLICATIVE}
+ * <li>{@code contractionCriterion = 0.5 + expansionFactor}
+ * </ul>
+ *
+ * <br>
+ * Throws IllegalArgumentException if the following conditions are not met:
+ *
+ * <ul>
+ * <li>{@code initialCapacity > 0}
+ * <li>{@code expansionFactor > 1}
+ * </ul>
+ *
+ * @param initialCapacity Initial size of the internal storage array.
+ * @param expansionFactor The array will be expanded based on this parameter.
+ * @throws MathIllegalArgumentException if parameters are not valid.
+ * @deprecated As of 3.1. Please use {@link #ResizableDoubleArray(int,double)} instead.
+ */
+ @Deprecated
+ public ResizableDoubleArray(int initialCapacity, float expansionFactor)
+ throws MathIllegalArgumentException {
+ this(initialCapacity, (double) expansionFactor);
+ }
+
+ /**
+ * Creates an instance with the specified initial capacity and expansion factor. The remaining
+ * properties take default values:
+ *
+ * <ul>
+ * <li>{@code expansionMode = MULTIPLICATIVE}
+ * <li>{@code contractionCriterion = 0.5 + expansionFactor}
+ * </ul>
+ *
+ * <br>
+ * Throws IllegalArgumentException if the following conditions are not met:
+ *
+ * <ul>
+ * <li>{@code initialCapacity > 0}
+ * <li>{@code expansionFactor > 1}
+ * </ul>
+ *
+ * @param initialCapacity Initial size of the internal storage array.
+ * @param expansionFactor The array will be expanded based on this parameter.
+ * @throws MathIllegalArgumentException if parameters are not valid.
+ * @since 3.1
+ */
+ public ResizableDoubleArray(int initialCapacity, double expansionFactor)
+ throws MathIllegalArgumentException {
+ this(initialCapacity, expansionFactor, DEFAULT_CONTRACTION_DELTA + expansionFactor);
+ }
+
+ /**
+ * Creates an instance with the specified initialCapacity, expansionFactor, and
+ * contractionCriterion. The expansion mode will default to {@code MULTIPLICATIVE}. <br>
+ * Throws IllegalArgumentException if the following conditions are not met:
+ *
+ * <ul>
+ * <li>{@code initialCapacity > 0}
+ * <li>{@code expansionFactor > 1}
+ * <li>{@code contractionCriterion >= expansionFactor}
+ * </ul>
+ *
+ * @param initialCapacity Initial size of the internal storage array..
+ * @param expansionFactor The array will be expanded based on this parameter.
+ * @param contractionCriteria Contraction criteria.
+ * @throws MathIllegalArgumentException if parameters are not valid.
+ * @deprecated As of 3.1. Please use {@link #ResizableDoubleArray(int,double,double)} instead.
+ */
+ @Deprecated
+ public ResizableDoubleArray(
+ int initialCapacity, float expansionFactor, float contractionCriteria)
+ throws MathIllegalArgumentException {
+ this(initialCapacity, (double) expansionFactor, (double) contractionCriteria);
+ }
+
+ /**
+ * Creates an instance with the specified initial capacity, expansion factor, and contraction
+ * criteria. The expansion mode will default to {@code MULTIPLICATIVE}. <br>
+ * Throws IllegalArgumentException if the following conditions are not met:
+ *
+ * <ul>
+ * <li>{@code initialCapacity > 0}
+ * <li>{@code expansionFactor > 1}
+ * <li>{@code contractionCriterion >= expansionFactor}
+ * </ul>
+ *
+ * @param initialCapacity Initial size of the internal storage array..
+ * @param expansionFactor The array will be expanded based on this parameter.
+ * @param contractionCriterion Contraction criterion.
+ * @throws MathIllegalArgumentException if the parameters are not valid.
+ * @since 3.1
+ */
+ public ResizableDoubleArray(
+ int initialCapacity, double expansionFactor, double contractionCriterion)
+ throws MathIllegalArgumentException {
+ this(
+ initialCapacity,
+ expansionFactor,
+ contractionCriterion,
+ ExpansionMode.MULTIPLICATIVE,
+ null);
+ }
+
+ /**
+ * Create a ResizableArray with the specified properties.
+ *
+ * <p>Throws IllegalArgumentException if the following conditions are not met:
+ *
+ * <ul>
+ * <li><code>initialCapacity > 0</code>
+ * <li><code>expansionFactor > 1</code>
+ * <li><code>contractionFactor >= expansionFactor</code>
+ * <li><code>expansionMode in {MULTIPLICATIVE_MODE, ADDITIVE_MODE}</code>
+ * </ul>
+ *
+ * @param initialCapacity the initial size of the internal storage array
+ * @param expansionFactor the array will be expanded based on this parameter
+ * @param contractionCriteria the contraction Criteria
+ * @param expansionMode the expansion mode
+ * @throws MathIllegalArgumentException if parameters are not valid
+ * @deprecated As of 3.1. Please use {@link
+ * #ResizableDoubleArray(int,double,double,ExpansionMode,double[])} instead.
+ */
+ @Deprecated
+ public ResizableDoubleArray(
+ int initialCapacity,
+ float expansionFactor,
+ float contractionCriteria,
+ int expansionMode)
+ throws MathIllegalArgumentException {
+ this(
+ initialCapacity,
+ expansionFactor,
+ contractionCriteria,
+ expansionMode == ADDITIVE_MODE
+ ? ExpansionMode.ADDITIVE
+ : ExpansionMode.MULTIPLICATIVE,
+ null);
+ // XXX Just ot retain the expected failure in a unit test.
+ // With the new "enum", that test will become obsolete.
+ setExpansionMode(expansionMode);
+ }
+
+ /**
+ * Creates an instance with the specified properties. <br>
+ * Throws MathIllegalArgumentException if the following conditions are not met:
+ *
+ * <ul>
+ * <li>{@code initialCapacity > 0}
+ * <li>{@code expansionFactor > 1}
+ * <li>{@code contractionCriterion >= expansionFactor}
+ * </ul>
+ *
+ * @param initialCapacity Initial size of the internal storage array.
+ * @param expansionFactor The array will be expanded based on this parameter.
+ * @param contractionCriterion Contraction criteria.
+ * @param expansionMode Expansion mode.
+ * @param data Initial contents of the array.
+ * @throws MathIllegalArgumentException if the parameters are not valid.
+ */
+ public ResizableDoubleArray(
+ int initialCapacity,
+ double expansionFactor,
+ double contractionCriterion,
+ ExpansionMode expansionMode,
+ double... data)
+ throws MathIllegalArgumentException {
+ if (initialCapacity <= 0) {
+ throw new NotStrictlyPositiveException(
+ LocalizedFormats.INITIAL_CAPACITY_NOT_POSITIVE, initialCapacity);
+ }
+ checkContractExpand(contractionCriterion, expansionFactor);
+
+ this.expansionFactor = expansionFactor;
+ this.contractionCriterion = contractionCriterion;
+ this.expansionMode = expansionMode;
+ internalArray = new double[initialCapacity];
+ numElements = 0;
+ startIndex = 0;
+
+ if (data != null && data.length > 0) {
+ addElements(data);
+ }
+ }
+
+ /**
+ * Copy constructor. Creates a new ResizableDoubleArray that is a deep, fresh copy of the
+ * original. Needs to acquire synchronization lock on original. Original may not be null;
+ * otherwise a {@link NullArgumentException} is thrown.
+ *
+ * @param original array to copy
+ * @exception NullArgumentException if original is null
+ * @since 2.0
+ */
+ public ResizableDoubleArray(ResizableDoubleArray original) throws NullArgumentException {
+ MathUtils.checkNotNull(original);
+ copy(original, this);
+ }
+
+ /**
+ * Adds an element to the end of this expandable array.
+ *
+ * @param value Value to be added to end of array.
+ */
+ public synchronized void addElement(double value) {
+ if (internalArray.length <= startIndex + numElements) {
+ expand();
+ }
+ internalArray[startIndex + numElements++] = value;
+ }
+
+ /**
+ * Adds several element to the end of this expandable array.
+ *
+ * @param values Values to be added to end of array.
+ * @since 2.2
+ */
+ public synchronized void addElements(double[] values) {
+ final double[] tempArray = new double[numElements + values.length + 1];
+ System.arraycopy(internalArray, startIndex, tempArray, 0, numElements);
+ System.arraycopy(values, 0, tempArray, numElements, values.length);
+ internalArray = tempArray;
+ startIndex = 0;
+ numElements += values.length;
+ }
+
+ /**
+ * Adds an element to the end of the array and removes the first element in the array. Returns
+ * the discarded first element. The effect is similar to a push operation in a FIFO queue.
+ *
+ * <p>Example: If the array contains the elements 1, 2, 3, 4 (in that order) and
+ * addElementRolling(5) is invoked, the result is an array containing the entries 2, 3, 4, 5 and
+ * the value returned is 1.
+ *
+ * @param value Value to be added to the array.
+ * @return the value which has been discarded or "pushed" out of the array by this rolling
+ * insert.
+ */
+ public synchronized double addElementRolling(double value) {
+ double discarded = internalArray[startIndex];
+
+ if ((startIndex + (numElements + 1)) > internalArray.length) {
+ expand();
+ }
+ // Increment the start index
+ startIndex += 1;
+
+ // Add the new value
+ internalArray[startIndex + (numElements - 1)] = value;
+
+ // Check the contraction criterion.
+ if (shouldContract()) {
+ contract();
+ }
+ return discarded;
+ }
+
+ /**
+ * Substitutes <code>value</code> for the most recently added value. Returns the value that has
+ * been replaced. If the array is empty (i.e. if {@link #numElements} is zero), an
+ * IllegalStateException is thrown.
+ *
+ * @param value New value to substitute for the most recently added value
+ * @return the value that has been replaced in the array.
+ * @throws MathIllegalStateException if the array is empty
+ * @since 2.0
+ */
+ public synchronized double substituteMostRecentElement(double value)
+ throws MathIllegalStateException {
+ if (numElements < 1) {
+ throw new MathIllegalStateException(
+ LocalizedFormats.CANNOT_SUBSTITUTE_ELEMENT_FROM_EMPTY_ARRAY);
+ }
+
+ final int substIndex = startIndex + (numElements - 1);
+ final double discarded = internalArray[substIndex];
+
+ internalArray[substIndex] = value;
+
+ return discarded;
+ }
+
+ /**
+ * Checks the expansion factor and the contraction criterion and throws an
+ * IllegalArgumentException if the contractionCriteria is less than the expansionCriteria
+ *
+ * @param expansion factor to be checked
+ * @param contraction criteria to be checked
+ * @throws MathIllegalArgumentException if the contractionCriteria is less than the
+ * expansionCriteria.
+ * @deprecated As of 3.1. Please use {@link #checkContractExpand(double,double)} instead.
+ */
+ @Deprecated
+ protected void checkContractExpand(float contraction, float expansion)
+ throws MathIllegalArgumentException {
+ checkContractExpand((double) contraction, (double) expansion);
+ }
+
+ /**
+ * Checks the expansion factor and the contraction criterion and raises an exception if the
+ * contraction criterion is smaller than the expansion criterion.
+ *
+ * @param contraction Criterion to be checked.
+ * @param expansion Factor to be checked.
+ * @throws NumberIsTooSmallException if {@code contraction < expansion}.
+ * @throws NumberIsTooSmallException if {@code contraction <= 1}.
+ * @throws NumberIsTooSmallException if {@code expansion <= 1 }.
+ * @since 3.1
+ */
+ protected void checkContractExpand(double contraction, double expansion)
+ throws NumberIsTooSmallException {
+ if (contraction < expansion) {
+ final NumberIsTooSmallException e = new NumberIsTooSmallException(contraction, 1, true);
+ e.getContext()
+ .addMessage(
+ LocalizedFormats.CONTRACTION_CRITERIA_SMALLER_THAN_EXPANSION_FACTOR,
+ contraction,
+ expansion);
+ throw e;
+ }
+
+ if (contraction <= 1) {
+ final NumberIsTooSmallException e =
+ new NumberIsTooSmallException(contraction, 1, false);
+ e.getContext()
+ .addMessage(
+ LocalizedFormats.CONTRACTION_CRITERIA_SMALLER_THAN_ONE, contraction);
+ throw e;
+ }
+
+ if (expansion <= 1) {
+ final NumberIsTooSmallException e =
+ new NumberIsTooSmallException(contraction, 1, false);
+ e.getContext()
+ .addMessage(LocalizedFormats.EXPANSION_FACTOR_SMALLER_THAN_ONE, expansion);
+ throw e;
+ }
+ }
+
+ /** Clear the array contents, resetting the number of elements to zero. */
+ public synchronized void clear() {
+ numElements = 0;
+ startIndex = 0;
+ }
+
+ /**
+ * Contracts the storage array to the (size of the element set) + 1 - to avoid a zero length
+ * array. This function also resets the startIndex to zero.
+ */
+ public synchronized void contract() {
+ final double[] tempArray = new double[numElements + 1];
+
+ // Copy and swap - copy only the element array from the src array.
+ System.arraycopy(internalArray, startIndex, tempArray, 0, numElements);
+ internalArray = tempArray;
+
+ // Reset the start index to zero
+ startIndex = 0;
+ }
+
+ /**
+ * Discards the <code>i</code> initial elements of the array. For example, if the array contains
+ * the elements 1,2,3,4, invoking <code>discardFrontElements(2)</code> will cause the first two
+ * elements to be discarded, leaving 3,4 in the array. Throws illegalArgumentException if i
+ * exceeds numElements.
+ *
+ * @param i the number of elements to discard from the front of the array
+ * @throws MathIllegalArgumentException if i is greater than numElements.
+ * @since 2.0
+ */
+ public synchronized void discardFrontElements(int i) throws MathIllegalArgumentException {
+ discardExtremeElements(i, true);
+ }
+
+ /**
+ * Discards the <code>i</code> last elements of the array. For example, if the array contains
+ * the elements 1,2,3,4, invoking <code>discardMostRecentElements(2)</code> will cause the last
+ * two elements to be discarded, leaving 1,2 in the array. Throws illegalArgumentException if i
+ * exceeds numElements.
+ *
+ * @param i the number of elements to discard from the end of the array
+ * @throws MathIllegalArgumentException if i is greater than numElements.
+ * @since 2.0
+ */
+ public synchronized void discardMostRecentElements(int i) throws MathIllegalArgumentException {
+ discardExtremeElements(i, false);
+ }
+
+ /**
+ * Discards the <code>i</code> first or last elements of the array, depending on the value of
+ * <code>front</code>. For example, if the array contains the elements 1,2,3,4, invoking <code>
+ * discardExtremeElements(2,false)</code> will cause the last two elements to be discarded,
+ * leaving 1,2 in the array. For example, if the array contains the elements 1,2,3,4, invoking
+ * <code>discardExtremeElements(2,true)</code> will cause the first two elements to be
+ * discarded, leaving 3,4 in the array. Throws illegalArgumentException if i exceeds
+ * numElements.
+ *
+ * @param i the number of elements to discard from the front/end of the array
+ * @param front true if elements are to be discarded from the front of the array, false if
+ * elements are to be discarded from the end of the array
+ * @throws MathIllegalArgumentException if i is greater than numElements.
+ * @since 2.0
+ */
+ private synchronized void discardExtremeElements(int i, boolean front)
+ throws MathIllegalArgumentException {
+ if (i > numElements) {
+ throw new MathIllegalArgumentException(
+ LocalizedFormats.TOO_MANY_ELEMENTS_TO_DISCARD_FROM_ARRAY, i, numElements);
+ } else if (i < 0) {
+ throw new MathIllegalArgumentException(
+ LocalizedFormats.CANNOT_DISCARD_NEGATIVE_NUMBER_OF_ELEMENTS, i);
+ } else {
+ // "Subtract" this number of discarded from numElements
+ numElements -= i;
+ if (front) {
+ startIndex += i;
+ }
+ }
+ if (shouldContract()) {
+ contract();
+ }
+ }
+
+ /**
+ * Expands the internal storage array using the expansion factor.
+ *
+ * <p>if <code>expansionMode</code> is set to MULTIPLICATIVE_MODE, the new array size will be
+ * <code>internalArray.length * expansionFactor.</code> If <code>expansionMode</code> is set to
+ * ADDITIVE_MODE, the length after expansion will be <code>
+ * internalArray.length + expansionFactor</code>
+ */
+ protected synchronized void expand() {
+ // notice the use of FastMath.ceil(), this guarantees that we will always
+ // have an array of at least currentSize + 1. Assume that the
+ // current initial capacity is 1 and the expansion factor
+ // is 1.000000000000000001. The newly calculated size will be
+ // rounded up to 2 after the multiplication is performed.
+ int newSize = 0;
+ if (expansionMode == ExpansionMode.MULTIPLICATIVE) {
+ newSize = (int) FastMath.ceil(internalArray.length * expansionFactor);
+ } else {
+ newSize = (int) (internalArray.length + FastMath.round(expansionFactor));
+ }
+ final double[] tempArray = new double[newSize];
+
+ // Copy and swap
+ System.arraycopy(internalArray, 0, tempArray, 0, internalArray.length);
+ internalArray = tempArray;
+ }
+
+ /**
+ * Expands the internal storage array to the specified size.
+ *
+ * @param size Size of the new internal storage array.
+ */
+ private synchronized void expandTo(int size) {
+ final double[] tempArray = new double[size];
+ // Copy and swap
+ System.arraycopy(internalArray, 0, tempArray, 0, internalArray.length);
+ internalArray = tempArray;
+ }
+
+ /**
+ * The contraction criteria defines when the internal array will contract to store only the
+ * number of elements in the element array. If the <code>expansionMode</code> is <code>
+ * MULTIPLICATIVE_MODE</code>, contraction is triggered when the ratio between storage array
+ * length and <code>numElements</code> exceeds <code>contractionFactor</code>. If the <code>
+ * expansionMode</code> is <code>ADDITIVE_MODE</code>, the number of excess storage locations is
+ * compared to <code>contractionFactor.</code>
+ *
+ * @return the contraction criteria used to reclaim memory.
+ * @deprecated As of 3.1. Please use {@link #getContractionCriterion()} instead.
+ */
+ @Deprecated
+ public float getContractionCriteria() {
+ return (float) getContractionCriterion();
+ }
+
+ /**
+ * The contraction criterion defines when the internal array will contract to store only the
+ * number of elements in the element array. If the <code>expansionMode</code> is <code>
+ * MULTIPLICATIVE_MODE</code>, contraction is triggered when the ratio between storage array
+ * length and <code>numElements</code> exceeds <code>contractionFactor</code>. If the <code>
+ * expansionMode</code> is <code>ADDITIVE_MODE</code>, the number of excess storage locations is
+ * compared to <code>contractionFactor.</code>
+ *
+ * @return the contraction criterion used to reclaim memory.
+ * @since 3.1
+ */
+ public double getContractionCriterion() {
+ return contractionCriterion;
+ }
+
+ /**
+ * Returns the element at the specified index
+ *
+ * @param index index to fetch a value from
+ * @return value stored at the specified index
+ * @throws ArrayIndexOutOfBoundsException if <code>index</code> is less than zero or is greater
+ * than <code>getNumElements() - 1</code>.
+ */
+ public synchronized double getElement(int index) {
+ if (index >= numElements) {
+ throw new ArrayIndexOutOfBoundsException(index);
+ } else if (index >= 0) {
+ return internalArray[startIndex + index];
+ } else {
+ throw new ArrayIndexOutOfBoundsException(index);
+ }
+ }
+
+ /**
+ * Returns a double array containing the elements of this <code>ResizableArray</code>. This
+ * method returns a copy, not a reference to the underlying array, so that changes made to the
+ * returned array have no effect on this <code>ResizableArray.</code>
+ *
+ * @return the double array.
+ */
+ public synchronized double[] getElements() {
+ final double[] elementArray = new double[numElements];
+ System.arraycopy(internalArray, startIndex, elementArray, 0, numElements);
+ return elementArray;
+ }
+
+ /**
+ * The expansion factor controls the size of a new array when an array needs to be expanded. The
+ * <code>expansionMode</code> determines whether the size of the array is multiplied by the
+ * <code>expansionFactor</code> (MULTIPLICATIVE_MODE) or if the expansion is additive
+ * (ADDITIVE_MODE -- <code>expansionFactor</code> storage locations added). The default <code>
+ * expansionMode</code> is MULTIPLICATIVE_MODE and the default <code>expansionFactor</code> is
+ * 2.0.
+ *
+ * @return the expansion factor of this expandable double array
+ * @deprecated As of 3.1. Return type will be changed to "double" in 4.0.
+ */
+ @Deprecated
+ public float getExpansionFactor() {
+ return (float) expansionFactor;
+ }
+
+ /**
+ * The expansion mode determines whether the internal storage array grows additively or
+ * multiplicatively when it is expanded.
+ *
+ * @return the expansion mode.
+ * @deprecated As of 3.1. Return value to be changed to {@link ExpansionMode} in 4.0.
+ */
+ @Deprecated
+ public int getExpansionMode() {
+ synchronized (this) {
+ switch (expansionMode) {
+ case MULTIPLICATIVE:
+ return MULTIPLICATIVE_MODE;
+ case ADDITIVE:
+ return ADDITIVE_MODE;
+ default:
+ throw new MathInternalError(); // Should never happen.
+ }
+ }
+ }
+
+ /**
+ * Notice the package scope on this method. This method is simply here for the JUnit test, it
+ * allows us check if the expansion is working properly after a number of expansions. This is
+ * not meant to be a part of the public interface of this class.
+ *
+ * @return the length of the internal storage array.
+ * @deprecated As of 3.1. Please use {@link #getCapacity()} instead.
+ */
+ @Deprecated
+ synchronized int getInternalLength() {
+ return internalArray.length;
+ }
+
+ /**
+ * Gets the currently allocated size of the internal data structure used for storing elements.
+ * This is not to be confused with {@link #getNumElements() the number of elements actually
+ * stored}.
+ *
+ * @return the length of the internal array.
+ * @since 3.1
+ */
+ public int getCapacity() {
+ return internalArray.length;
+ }
+
+ /**
+ * Returns the number of elements currently in the array. Please note that this is different
+ * from the length of the internal storage array.
+ *
+ * @return the number of elements.
+ */
+ public synchronized int getNumElements() {
+ return numElements;
+ }
+
+ /**
+ * Returns the internal storage array. Note that this method returns a reference to the internal
+ * storage array, not a copy, and to correctly address elements of the array, the <code>
+ * startIndex</code> is required (available via the {@link #start} method). This method should
+ * only be used in cases where copying the internal array is not practical. The {@link
+ * #getElements} method should be used in all other cases.
+ *
+ * @return the internal storage array used by this object
+ * @since 2.0
+ * @deprecated As of 3.1.
+ */
+ @Deprecated
+ public synchronized double[] getInternalValues() {
+ return internalArray;
+ }
+
+ /**
+ * Provides <em>direct</em> access to the internal storage array. Please note that this method
+ * returns a reference to this object's storage array, not a copy. <br>
+ * To correctly address elements of the array, the "start index" is required (available via the
+ * {@link #getStartIndex() getStartIndex} method. <br>
+ * This method should only be used to avoid copying the internal array. The returned value
+ * <em>must</em> be used for reading only; other uses could lead to this object becoming
+ * inconsistent. <br>
+ * The {@link #getElements} method has no such limitation since it returns a copy of this
+ * array's addressable elements.
+ *
+ * @return the internal storage array used by this object.
+ * @since 3.1
+ */
+ protected double[] getArrayRef() {
+ return internalArray;
+ }
+
+ /**
+ * Returns the "start index" of the internal array. This index is the position of the first
+ * addressable element in the internal storage array. The addressable elements in the array are
+ * at indices contained in the interval [{@link #getStartIndex()}, {@link #getStartIndex()} +
+ * {@link #getNumElements()} - 1].
+ *
+ * @return the start index.
+ * @since 3.1
+ */
+ protected int getStartIndex() {
+ return startIndex;
+ }
+
+ /**
+ * Sets the contraction criteria.
+ *
+ * @param contractionCriteria contraction criteria
+ * @throws MathIllegalArgumentException if the contractionCriteria is less than the
+ * expansionCriteria.
+ * @deprecated As of 3.1 (to be removed in 4.0 as field will become "final").
+ */
+ @Deprecated
+ public void setContractionCriteria(float contractionCriteria)
+ throws MathIllegalArgumentException {
+ checkContractExpand(contractionCriteria, getExpansionFactor());
+ synchronized (this) {
+ this.contractionCriterion = contractionCriteria;
+ }
+ }
+
+ /**
+ * Performs an operation on the addressable elements of the array.
+ *
+ * @param f Function to be applied on this array.
+ * @return the result.
+ * @since 3.1
+ */
+ public double compute(MathArrays.Function f) {
+ final double[] array;
+ final int start;
+ final int num;
+ synchronized (this) {
+ array = internalArray;
+ start = startIndex;
+ num = numElements;
+ }
+ return f.evaluate(array, start, num);
+ }
+
+ /**
+ * Sets the element at the specified index. If the specified index is greater than <code>
+ * getNumElements() - 1</code>, the <code>numElements</code> property is increased to <code>
+ * index +1</code> and additional storage is allocated (if necessary) for the new element and
+ * all (uninitialized) elements between the new element and the previous end of the array).
+ *
+ * @param index index to store a value in
+ * @param value value to store at the specified index
+ * @throws ArrayIndexOutOfBoundsException if {@code index < 0}.
+ */
+ public synchronized void setElement(int index, double value) {
+ if (index < 0) {
+ throw new ArrayIndexOutOfBoundsException(index);
+ }
+ if (index + 1 > numElements) {
+ numElements = index + 1;
+ }
+ if ((startIndex + index) >= internalArray.length) {
+ expandTo(startIndex + (index + 1));
+ }
+ internalArray[startIndex + index] = value;
+ }
+
+ /**
+ * Sets the expansionFactor. Throws IllegalArgumentException if the the following conditions are
+ * not met:
+ *
+ * <ul>
+ * <li><code>expansionFactor > 1</code>
+ * <li><code>contractionFactor >= expansionFactor</code>
+ * </ul>
+ *
+ * @param expansionFactor the new expansion factor value.
+ * @throws MathIllegalArgumentException if expansionFactor is <= 1 or greater than
+ * contractionFactor
+ * @deprecated As of 3.1 (to be removed in 4.0 as field will become "final").
+ */
+ @Deprecated
+ public void setExpansionFactor(float expansionFactor) throws MathIllegalArgumentException {
+ checkContractExpand(getContractionCriterion(), expansionFactor);
+ // The check above verifies that the expansion factor is > 1.0;
+ synchronized (this) {
+ this.expansionFactor = expansionFactor;
+ }
+ }
+
+ /**
+ * Sets the <code>expansionMode</code>. The specified value must be one of ADDITIVE_MODE,
+ * MULTIPLICATIVE_MODE.
+ *
+ * @param expansionMode The expansionMode to set.
+ * @throws MathIllegalArgumentException if the specified mode value is not valid.
+ * @deprecated As of 3.1. Please use {@link #setExpansionMode(ExpansionMode)} instead.
+ */
+ @Deprecated
+ public void setExpansionMode(int expansionMode) throws MathIllegalArgumentException {
+ if (expansionMode != MULTIPLICATIVE_MODE && expansionMode != ADDITIVE_MODE) {
+ throw new MathIllegalArgumentException(
+ LocalizedFormats.UNSUPPORTED_EXPANSION_MODE,
+ expansionMode,
+ MULTIPLICATIVE_MODE,
+ "MULTIPLICATIVE_MODE",
+ ADDITIVE_MODE,
+ "ADDITIVE_MODE");
+ }
+ synchronized (this) {
+ if (expansionMode == MULTIPLICATIVE_MODE) {
+ setExpansionMode(ExpansionMode.MULTIPLICATIVE);
+ } else if (expansionMode == ADDITIVE_MODE) {
+ setExpansionMode(ExpansionMode.ADDITIVE);
+ }
+ }
+ }
+
+ /**
+ * Sets the {@link ExpansionMode expansion mode}.
+ *
+ * @param expansionMode Expansion mode to use for resizing the array.
+ * @deprecated As of 3.1 (to be removed in 4.0 as field will become "final").
+ */
+ @Deprecated
+ public void setExpansionMode(ExpansionMode expansionMode) {
+ synchronized (this) {
+ this.expansionMode = expansionMode;
+ }
+ }
+
+ /**
+ * Sets the initial capacity. Should only be invoked by constructors.
+ *
+ * @param initialCapacity of the array
+ * @throws MathIllegalArgumentException if <code>initialCapacity</code> is not positive.
+ * @deprecated As of 3.1, this is a no-op.
+ */
+ @Deprecated
+ protected void setInitialCapacity(int initialCapacity) throws MathIllegalArgumentException {
+ // Body removed in 3.1.
+ }
+
+ /**
+ * This function allows you to control the number of elements contained in this array, and can
+ * be used to "throw out" the last n values in an array. This function will also expand the
+ * internal array as needed.
+ *
+ * @param i a new number of elements
+ * @throws MathIllegalArgumentException if <code>i</code> is negative.
+ */
+ public synchronized void setNumElements(int i) throws MathIllegalArgumentException {
+ // If index is negative thrown an error.
+ if (i < 0) {
+ throw new MathIllegalArgumentException(LocalizedFormats.INDEX_NOT_POSITIVE, i);
+ }
+
+ // Test the new num elements, check to see if the array needs to be
+ // expanded to accommodate this new number of elements.
+ final int newSize = startIndex + i;
+ if (newSize > internalArray.length) {
+ expandTo(newSize);
+ }
+
+ // Set the new number of elements to new value.
+ numElements = i;
+ }
+
+ /**
+ * Returns true if the internal storage array has too many unused storage positions.
+ *
+ * @return true if array satisfies the contraction criteria
+ */
+ private synchronized boolean shouldContract() {
+ if (expansionMode == ExpansionMode.MULTIPLICATIVE) {
+ return (internalArray.length / ((float) numElements)) > contractionCriterion;
+ } else {
+ return (internalArray.length - numElements) > contractionCriterion;
+ }
+ }
+
+ /**
+ * Returns the starting index of the internal array. The starting index is the position of the
+ * first addressable element in the internal storage array. The addressable elements in the
+ * array are <code>
+ * internalArray[startIndex],...,internalArray[startIndex + numElements -1]
+ * </code>
+ *
+ * @return the starting index.
+ * @deprecated As of 3.1.
+ */
+ @Deprecated
+ public synchronized int start() {
+ return startIndex;
+ }
+
+ /**
+ * Copies source to dest, copying the underlying data, so dest is a new, independent copy of
+ * source. Does not contract before the copy.
+ *
+ * <p>Obtains synchronization locks on both source and dest (in that order) before performing
+ * the copy.
+ *
+ * <p>Neither source nor dest may be null; otherwise a {@link NullArgumentException} is thrown
+ *
+ * @param source ResizableDoubleArray to copy
+ * @param dest ResizableArray to replace with a copy of the source array
+ * @exception NullArgumentException if either source or dest is null
+ * @since 2.0
+ */
+ public static void copy(ResizableDoubleArray source, ResizableDoubleArray dest)
+ throws NullArgumentException {
+ MathUtils.checkNotNull(source);
+ MathUtils.checkNotNull(dest);
+ synchronized (source) {
+ synchronized (dest) {
+ dest.contractionCriterion = source.contractionCriterion;
+ dest.expansionFactor = source.expansionFactor;
+ dest.expansionMode = source.expansionMode;
+ dest.internalArray = new double[source.internalArray.length];
+ System.arraycopy(
+ source.internalArray, 0, dest.internalArray, 0, dest.internalArray.length);
+ dest.numElements = source.numElements;
+ dest.startIndex = source.startIndex;
+ }
+ }
+ }
+
+ /**
+ * Returns a copy of the ResizableDoubleArray. Does not contract before the copy, so the
+ * returned object is an exact copy of this.
+ *
+ * @return a new ResizableDoubleArray with the same data and configuration properties as this
+ * @since 2.0
+ */
+ public synchronized ResizableDoubleArray copy() {
+ final ResizableDoubleArray result = new ResizableDoubleArray();
+ copy(this, result);
+ return result;
+ }
+
+ /**
+ * Returns true iff object is a ResizableDoubleArray with the same properties as this and an
+ * identical internal storage array.
+ *
+ * @param object object to be compared for equality with this
+ * @return true iff object is a ResizableDoubleArray with the same data and properties as this
+ * @since 2.0
+ */
+ @Override
+ public boolean equals(Object object) {
+ if (object == this) {
+ return true;
+ }
+ if (object instanceof ResizableDoubleArray == false) {
+ return false;
+ }
+ synchronized (this) {
+ synchronized (object) {
+ boolean result = true;
+ final ResizableDoubleArray other = (ResizableDoubleArray) object;
+ result = result && (other.contractionCriterion == contractionCriterion);
+ result = result && (other.expansionFactor == expansionFactor);
+ result = result && (other.expansionMode == expansionMode);
+ result = result && (other.numElements == numElements);
+ result = result && (other.startIndex == startIndex);
+ if (!result) {
+ return false;
+ } else {
+ return Arrays.equals(internalArray, other.internalArray);
+ }
+ }
+ }
+ }
+
+ /**
+ * Returns a hash code consistent with equals.
+ *
+ * @return the hash code representing this {@code ResizableDoubleArray}.
+ * @since 2.0
+ */
+ @Override
+ public synchronized int hashCode() {
+ final int[] hashData = new int[6];
+ hashData[0] = Double.valueOf(expansionFactor).hashCode();
+ hashData[1] = Double.valueOf(contractionCriterion).hashCode();
+ hashData[2] = expansionMode.hashCode();
+ hashData[3] = Arrays.hashCode(internalArray);
+ hashData[4] = numElements;
+ hashData[5] = startIndex;
+ return Arrays.hashCode(hashData);
+ }
+}
diff --git a/src/main/java/org/apache/commons/math3/util/TransformerMap.java b/src/main/java/org/apache/commons/math3/util/TransformerMap.java
new file mode 100644
index 0000000..404f7db
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/util/TransformerMap.java
@@ -0,0 +1,182 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math3.util;
+
+import org.apache.commons.math3.exception.MathIllegalArgumentException;
+
+import java.io.Serializable;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * This TansformerMap automates the transformation of mixed object types. It provides a means to set
+ * NumberTransformers that will be selected based on the Class of the object handed to the Maps
+ * <code>double transform(Object o)</code> method.
+ */
+public class TransformerMap implements NumberTransformer, Serializable {
+
+ /** Serializable version identifier */
+ private static final long serialVersionUID = 4605318041528645258L;
+
+ /** A default Number Transformer for Numbers and numeric Strings. */
+ private NumberTransformer defaultTransformer = null;
+
+ /** The internal Map. */
+ private Map<Class<?>, NumberTransformer> map = null;
+
+ /** Build a map containing only the default transformer. */
+ public TransformerMap() {
+ map = new HashMap<Class<?>, NumberTransformer>();
+ defaultTransformer = new DefaultTransformer();
+ }
+
+ /**
+ * Tests if a Class is present in the TransformerMap.
+ *
+ * @param key Class to check
+ * @return true|false
+ */
+ public boolean containsClass(Class<?> key) {
+ return map.containsKey(key);
+ }
+
+ /**
+ * Tests if a NumberTransformer is present in the TransformerMap.
+ *
+ * @param value NumberTransformer to check
+ * @return true|false
+ */
+ public boolean containsTransformer(NumberTransformer value) {
+ return map.containsValue(value);
+ }
+
+ /**
+ * Returns the Transformer that is mapped to a class if mapping is not present, this returns
+ * null.
+ *
+ * @param key The Class of the object
+ * @return the mapped NumberTransformer or null.
+ */
+ public NumberTransformer getTransformer(Class<?> key) {
+ return map.get(key);
+ }
+
+ /**
+ * Sets a Class to Transformer Mapping in the Map. If the Class is already present, this
+ * overwrites that mapping.
+ *
+ * @param key The Class
+ * @param transformer The NumberTransformer
+ * @return the replaced transformer if one is present
+ */
+ public NumberTransformer putTransformer(Class<?> key, NumberTransformer transformer) {
+ return map.put(key, transformer);
+ }
+
+ /**
+ * Removes a Class to Transformer Mapping in the Map.
+ *
+ * @param key The Class
+ * @return the removed transformer if one is present or null if none was present.
+ */
+ public NumberTransformer removeTransformer(Class<?> key) {
+ return map.remove(key);
+ }
+
+ /** Clears all the Class to Transformer mappings. */
+ public void clear() {
+ map.clear();
+ }
+
+ /**
+ * Returns the Set of Classes used as keys in the map.
+ *
+ * @return Set of Classes
+ */
+ public Set<Class<?>> classes() {
+ return map.keySet();
+ }
+
+ /**
+ * Returns the Set of NumberTransformers used as values in the map.
+ *
+ * @return Set of NumberTransformers
+ */
+ public Collection<NumberTransformer> transformers() {
+ return map.values();
+ }
+
+ /**
+ * Attempts to transform the Object against the map of NumberTransformers. Otherwise it returns
+ * Double.NaN.
+ *
+ * @param o the Object to be transformed.
+ * @return the double value of the Object.
+ * @throws MathIllegalArgumentException if the Object can not be transformed into a Double.
+ * @see org.apache.commons.math3.util.NumberTransformer#transform(java.lang.Object)
+ */
+ public double transform(Object o) throws MathIllegalArgumentException {
+ double value = Double.NaN;
+
+ if (o instanceof Number || o instanceof String) {
+ value = defaultTransformer.transform(o);
+ } else {
+ NumberTransformer trans = getTransformer(o.getClass());
+ if (trans != null) {
+ value = trans.transform(o);
+ }
+ }
+
+ return value;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public boolean equals(Object other) {
+ if (this == other) {
+ return true;
+ }
+ if (other instanceof TransformerMap) {
+ TransformerMap rhs = (TransformerMap) other;
+ if (!defaultTransformer.equals(rhs.defaultTransformer)) {
+ return false;
+ }
+ if (map.size() != rhs.map.size()) {
+ return false;
+ }
+ for (Map.Entry<Class<?>, NumberTransformer> entry : map.entrySet()) {
+ if (!entry.getValue().equals(rhs.map.get(entry.getKey()))) {
+ return false;
+ }
+ }
+ return true;
+ }
+ return false;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public int hashCode() {
+ int hash = defaultTransformer.hashCode();
+ for (NumberTransformer t : map.values()) {
+ hash = hash * 31 + t.hashCode();
+ }
+ return hash;
+ }
+}
diff --git a/src/main/java/org/apache/commons/math3/util/package-info.java b/src/main/java/org/apache/commons/math3/util/package-info.java
new file mode 100644
index 0000000..e2e6451
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/util/package-info.java
@@ -0,0 +1,18 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/** Convenience routines and common data structures used throughout the commons-math library. */
+package org.apache.commons.math3.util;